Browse Source

dx8 dxgsg

cxgeorge 24 years ago
parent
commit
f4a3d7d340

+ 23 - 0
panda/metalibs/pandadx8/Sources.pp

@@ -0,0 +1,23 @@
+#define DIRECTORY_IF_DX yes
+
+// DIR_TYPE "metalib" indicates we are building a shared library that
+// consists mostly of references to other shared libraries.  Under
+// Windows, this directly produces a DLL (as opposed to the regular
+// src libraries, which don't produce anything but a pile of OBJ files
+// under Windows).
+
+#define DIR_TYPE metalib
+#define BUILDING_DLL BUILDING_PANDADX
+
+#define COMPONENT_LIBS \
+    dxgsg8 wdxdisplay8
+#define LOCAL_LIBS gsgbase display express gobj
+#define OTHER_LIBS dtoolconfig dtool
+
+#if $[BUILD_DX8]
+#begin metalib_target
+  #define TARGET pandadx8
+
+  #define SOURCES pandadx8.cxx
+#end metalib_target
+#endif

+ 23 - 0
panda/metalibs/pandadx8/pandadx8.cxx

@@ -0,0 +1,23 @@
+// Filename: pandadx.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "pandadx8.h"
+
+#include <config_dxgsg8.h>
+#include <config_wdxdisplay8.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libpandadx
+//  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_libpandadx8() {
+  init_libdxgsg8();
+  init_libwdxdisplay8();
+}

+ 13 - 0
panda/metalibs/pandadx8/pandadx8.h

@@ -0,0 +1,13 @@
+// Filename: pandadx.h
+// Created by:  drose (2Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PANDADX8_H
+#define PANDADX8_H
+
+#include <pandabase.h>
+
+EXPCL_PANDADX void init_libpandadx8();
+
+#endif

+ 31 - 0
panda/src/dxgsg8/Sources.pp

@@ -0,0 +1,31 @@
+#define DIRECTORY_IF_DX yes
+
+#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
+                   dtoolutil:c dtoolbase:c dtool:m
+#define USE_DX yes
+
+#if $[BUILD_DX8]
+#begin lib_target
+
+  #define TARGET dxgsg8
+  #define LOCAL_LIBS \
+    cull gsgmisc gsgbase gobj sgattrib sgraphutil graph display light \
+    putil linmath sgraph mathutil pnmimage event
+    
+  #define COMBINED_SOURCES $[TARGET]_composite1.cxx
+
+  #define SOURCES \
+    config_dxgsg8.h dxGraphicsStateGuardian8.I \
+    dxGraphicsStateGuardian8.cxx dxGraphicsStateGuardian8.h \
+    dxSavedFrameBuffer8.I dxSavedFrameBuffer8.h \
+    dxTextureContext8.h dxGeomNodeContext8.h dxGeomNodeContext8.I
+    
+  #define INCLUDED_SOURCES \
+    config_dxgsg8.cxx dxSavedFrameBuffer8.cxx dxTextureContext8.cxx dxGeomNodeContext8.cxx
+    
+  #define INSTALL_HEADERS \
+    dxGraphicsStateGuardian8.I dxGraphicsStateGuardian8.h \
+    dxSavedFrameBuffer8.I dxSavedFrameBuffer8.h dxTextureContext8.h
+
+#end lib_target
+#endif

+ 138 - 0
panda/src/dxgsg8/config_dxgsg8.cxx

@@ -0,0 +1,138 @@
+// Filename: config_dxgsg.cxx
+// Created by:  drose (06Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_dxgsg8.h"
+#include "dxGraphicsStateGuardian8.h"
+#include "dxSavedFrameBuffer8.h"
+#include "dxTextureContext8.h"
+
+#include <dconfig.h>
+
+Configure(config_dxgsg);
+NotifyCategoryDef(dxgsg, ":display:gsg");
+
+// Configure this variable true to cause the DXGSG to show each
+// transform space it renders by drawing a little unit axis.  This
+// cannot be enabled when the player is compiled in NDEBUG mode.
+bool dx_show_transforms = config_dxgsg.GetBool("dx-show-transforms", false);
+
+//  Configure this to TRUE if you want DirectX to control the entire screen,
+//  If false, it will just blit into a window.
+bool dx_full_screen = config_dxgsg.GetBool("dx-full-screen", false);
+
+//  Configure this true to force the rendering to sync to the video
+//  refresh, or false to let your frame rate go as high as it can,
+//  irrespective of the video refresh.
+bool dx_sync_video = config_dxgsg.GetBool("sync-video", true);
+
+// enable this to turn on full-screen anti-aliasing, if the HW supports it
+// this var is also used in wdxGraphicsWindows.cxx
+bool dx_full_screen_antialiasing = config_dxgsg.GetBool("dx-antialias", false);
+
+// Configure this true to perform a cull traversal over the geometry
+// by default, false otherwise.  The cull traversal provides support
+// for state-sorting, z-sorting, and binning.
+bool dx_cull_traversal = config_dxgsg.GetBool("dx-cull-traversal", true);
+
+// if true, if card only supports per-vertex fog, it will be treated as no-HW fog capability
+bool dx_no_vertex_fog = config_dxgsg.GetBool("dx-no-vertex-fog", false);
+
+// Configure this true to cause all lighting normals to automatically
+// be normalized by the CPU before rendering.  This is
+// necessary if you intend to render things under scale transforms and
+// expect lighting to work correctly.  Maybe one day there will be
+// another way to set this at runtime, instead of only as a configure
+// variable
+bool dx_auto_normalize_lighting = config_dxgsg.GetBool("auto-normalize-lighting", false);
+
+bool dx_show_fps_meter = config_dxgsg.GetBool("show-fps-meter", false);
+float dx_fps_meter_update_interval = max(0.5,config_dxgsg.GetFloat("fps-meter-update-interval", 1.7));
+
+#ifndef NDEBUG
+// debugging flag
+// values are same as D3DCULL enumtype, 0 - no force, 1 - force none, 2 - force CW, 3 - force CCW
+int dx_force_backface_culling = config_dxgsg.GetInt("dx-force-backface-culling", 0);
+#endif
+
+bool dx_mipmap_everything = config_dxgsg.GetBool("dx-mipmap-everything", false);
+bool dx_ignore_mipmaps = config_dxgsg.GetBool("dx-ignore-mipmaps", false);
+
+// if this is set, more accurate but more expensive fog computations are performed
+bool dx_use_rangebased_fog = config_dxgsg.GetBool("dx-use-rangebased-fog", false);
+
+#ifdef _DEBUG
+float dx_global_miplevel_bias = config_dxgsg.GetFloat("dx-global-miplevel-bias", 0.0);
+bool dx_debug_view_mipmaps = config_dxgsg.GetBool("dx-debug-view-mipmaps", false);
+bool dx_force_16bpptextures = config_dxgsg.GetBool("dx-force-16bpptextures", false);
+bool dx_force_anisotropic_filtering = config_dxgsg.GetBool("dx-force-anisotropic-filtering", false);
+//int dx_print_texstats = config_dxgsg.GetBool("dx-print-texstats", 0);
+#endif
+
+// set 'retained-mode #t' and this to have prepare_geom concatenate all tristrips within a geom 
+// together using degenerate tris
+const bool link_tristrips = config_dxgsg.GetBool("link-tristrips", false);
+
+// note:  offset currently disabled since it wasnt working properly
+DXDecalType dx_decal_type = GDT_offset;
+
+static DXDecalType
+parse_decal_type(const string &type) {
+  if (type == "mask") {
+    return GDT_mask;
+  } else if (type == "blend") {
+    return GDT_blend;
+  } else if (type == "offset") {
+    return GDT_offset;
+  }
+  dxgsg_cat.error() << "Invalid dx-decal-type: " << type << "\n";
+  return GDT_offset;
+}
+
+ConfigureFn(config_dxgsg) {
+  init_libdxgsg8();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libdxgsg
+//  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_libdxgsg8() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  string decal_type = config_dxgsg.GetString("dx-decal-type", "");
+  if (!decal_type.empty()) {
+    dx_decal_type = parse_decal_type(decal_type);
+  }
+
+  DXGraphicsStateGuardian::init_type();
+  DXSavedFrameBuffer::init_type();
+  DXTextureContext::init_type();
+  DXGeomNodeContext::init_type();
+
+  GraphicsStateGuardian::get_factory().register_factory
+    (DXGraphicsStateGuardian::get_class_type(),
+     DXGraphicsStateGuardian::make_DXGraphicsStateGuardian);
+}

+ 64 - 0
panda/src/dxgsg8/config_dxgsg8.h

@@ -0,0 +1,64 @@
+// Filename: config_dxgsg.h
+// Created by:  drose (06Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_DXGSG_H
+#define CONFIG_DXGSG_H
+
+#include <pandabase.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(dxgsg, EXPCL_PANDADX, EXPTP_PANDADX);
+
+extern bool dx_full_screen;
+extern bool dx_sync_video;
+extern bool dx_cull_traversal;
+extern bool dx_show_fps_meter;
+extern bool dx_no_vertex_fog;
+extern bool dx_full_screen_antialiasing;
+extern float dx_fps_meter_update_interval;
+extern bool dx_auto_normalize_lighting;
+extern bool dx_use_rangebased_fog;
+extern const bool link_tristrips;
+
+// debug flags
+extern bool dx_ignore_mipmaps;
+extern bool dx_mipmap_everything;
+extern bool dx_show_transforms;
+
+#ifndef NDEBUG
+extern int dx_force_backface_culling;
+#endif
+
+#ifdef _DEBUG
+extern float dx_global_miplevel_bias;
+extern bool dx_debug_view_mipmaps;
+extern bool dx_force_16bpptextures;
+extern bool dx_force_anisotropic_filtering;
+#endif
+
+// Ways to implement decals.
+enum DXDecalType {
+  GDT_mask,   // GL 1.0 style, involving three steps and double-draw of polygon
+  GDT_blend,  // As above, but slower; use blending to disable colorbuffer writes
+  GDT_offset  // The fastest, using GL 1.1 style glPolygonOffset
+};
+extern DXDecalType dx_decal_type;
+
+extern EXPCL_PANDADX void init_libdxgsg8();
+
+#endif

+ 31 - 0
panda/src/dxgsg8/dxGeomNodeContext8.I

@@ -0,0 +1,31 @@
+// Filename: dxGeomNodeContext.I
+// Created by:  drose (12Jun01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: dxGeomNodeContext::Constructor
+//       Access: Public                                 
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXGeomNodeContext::
+DXGeomNodeContext(GeomNode *node) : GeomNodeContext(node)
+{
+  _num_verts = 0;
+  _pVB = NULL;
+  _pXformed_VB = NULL;
+}

+ 33 - 0
panda/src/dxgsg8/dxGeomNodeContext8.cxx

@@ -0,0 +1,33 @@
+// Filename: dxGeomNodeContext.cxx
+// Created by:  drose (12Jun01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dxGeomNodeContext8.h"
+
+TypeHandle DXGeomNodeContext::_type_handle;
+
+DXGeomNodeContext::~DXGeomNodeContext()
+{
+   if(_pXformed_VB!=NULL)
+       _pXformed_VB->Release();
+   _pXformed_VB=NULL;
+
+   if(_pVB!=NULL)
+       _pVB->Release();
+   _pVB=NULL;
+}
+

+ 94 - 0
panda/src/dxgsg8/dxGeomNodeContext8.h

@@ -0,0 +1,94 @@
+// Filename: dxGeomNodeContext.h
+// Created by:  drose (12Jun01)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGEOMNODECONTEXT_H
+#define DXGEOMNODECONTEXT_H
+
+#include <pandabase.h>
+
+#ifdef WIN32_VC
+// Must include windows.h before dx.h on NT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include <geomNodeContext.h>
+#include <geomNode.h>
+#include "pvector.h"
+
+#define D3D_OVERLOADS   //  get D3DVECTOR '+' operator, etc from d3dtypes.h
+#include <d3d.h>
+
+typedef struct {
+     DWORD nVerts;
+     D3DPRIMITIVETYPE primtype;
+} DPInfo;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DXGeomNodeContext
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXGeomNodeContext : public GeomNodeContext {
+public:
+  INLINE DXGeomNodeContext(GeomNode *node);
+  ~DXGeomNodeContext();
+
+  // A list of the dynamic Geoms within the GeomNode; these aren't
+  // part of the above display list and must be drawn separately
+  typedef pvector<PT(dDrawable) > Geoms;
+  Geoms _cached_geoms,_other_geoms;
+
+  // VB's are already reference-counted by D3D, no need to make separate panda object to do that
+  // but we will want a way to know if VB has already been xformed by ProcessVerts this frame
+  // if multiple geomnodes share VBs
+
+  LPDIRECT3DVERTEXBUFFER7 _pVB;
+  LPDIRECT3DVERTEXBUFFER7 _pXformed_VB;
+
+  int _start_index;   // starting offset of this geom's verts within the VB
+  int _num_verts;     // number of verts used by this geomcontext within the VB
+  
+  BYTE *_pEndofVertData;  // ptr to end of current vert data in VB (note: only used/valid during setup)
+
+  // for multiple geoms per VB, either will have to regen lengths based on per* flags, or store
+  // per geom vector of perprim vectors lengths
+  vector<DPInfo> _PrimInfo;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomNodeContext::init_type();
+    register_type(_type_handle, "DXGeomNodeContext",
+                  GeomNodeContext::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 "dxGeomNodeContext8.I"
+
+#endif
+

+ 398 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.I

@@ -0,0 +1,398 @@
+// Filename: dxGraphicsStateGuardian.I
+// Created by:  mike (02Feb99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_dxgsg8.h"
+#include <graphicsWindow.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_line_smooth
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_line_smooth(bool val) {
+  if(_line_smooth_enabled != val) {
+    _line_smooth_enabled = val;
+  #ifdef NDEBUG
+    {
+        if(val && (_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ANTIALIASEDGES))
+           dxgsg_cat.error() << "no HW support for line smoothing!!\n";
+    }
+  #endif
+
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_EDGEANTIALIAS, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_lighting
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_lighting(bool val) {
+  if (_lighting_enabled != val) {
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, (DWORD)val);
+    if(_lighting_enabled = val)
+      _lighting_enabled_this_frame = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_dither
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_dither(bool val) {
+  if (_dither_enabled != val) {
+
+  #ifdef _DEBUG
+    {
+        if(val && !(_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_DITHER))
+           dxgsg_cat.error() << "no HW support for color dithering!!\n";
+        return;
+    }
+  #endif
+
+    _dither_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_stencil_test
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_stencil_test(bool val) {
+  if (_stencil_test_enabled != val) {
+    _stencil_test_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_color_material
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_color_material(bool val) {
+  if (_color_material_enabled != val) {
+    _color_material_enabled = val;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_clip_plane
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_clip_plane(int clip_plane, bool val)
+{
+  if (_clip_plane_enabled[clip_plane] != val)
+    {
+    _clip_plane_enabled[clip_plane] = val;
+    DWORD  ClipPlaneBits;
+    _d3dDevice->GetRenderState(D3DRENDERSTATE_CLIPPLANEENABLE , &ClipPlaneBits);
+    if (val)
+        ClipPlaneBits |= 1 << clip_plane;
+    else
+        ClipPlaneBits &= ~(1 << clip_plane);
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPLANEENABLE , ClipPlaneBits);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_blend
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_blend(bool val) {
+  if (_blend_enabled != val) {
+    _blend_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_blend
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+set_shademode(D3DSHADEMODE val) {
+  if (_CurShadeMode != val) {
+    _CurShadeMode = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, (DWORD)val);
+  }
+}
+
+INLINE void DXGraphicsStateGuardian::
+enable_primitive_clipping(bool val) {
+  if (_clipping_enabled != val) {
+    _clipping_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_fog
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_fog(bool val) {
+  if ((_fog_enabled != val) && (_doFogType!=None)) {
+    _fog_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_alpha_test
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_alpha_test(bool val )
+{
+  if (_alpha_test_enabled != val) {
+    _alpha_test_enabled = val;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::call_dxLightModelAmbient
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+call_dxLightModelAmbient( const Colorf& color)
+{
+  if (_lmodel_ambient != color) {
+    _lmodel_ambient = color;
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "dxLightModel(LIGHT_MODEL_AMBIENT, " << color << ")" << endl;
+#endif
+    _d3dDevice->SetRenderState( D3DRENDERSTATE_AMBIENT,
+                D3DRGBA(color[0], color[1], color[2], color[3]));
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::call_glAlphaFunc
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+call_dxAlphaFunc(D3DCMPFUNC func, DWORD ref)
+{
+  if (_alpha_func != func || _alpha_func_ref != ref) {
+    _alpha_func = func;
+    _alpha_func_ref = ref;
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "dxAlphaFunc(";
+    switch (func) {
+    case D3DCMP_NEVER:
+      dxgsg_cat.debug(false) << "D3DCMP_NEVER, ";
+      break;
+    case D3DCMP_LESS:
+      dxgsg_cat.debug(false) << "D3DCMP_LESS, ";
+      break;
+    case D3DCMP_EQUAL:
+      dxgsg_cat.debug(false) << "D3DCMP_EQUAL, ";
+      break;
+    case D3DCMP_LEQUAL:
+      dxgsg_cat.debug(false) << "D3DCMP_LEQUAL, ";
+      break;
+    case D3DCMP_GREATER:
+      dxgsg_cat.debug(false) << "D3DCMP_GREATER, ";
+      break;
+    case D3DCMP_NOTEQUAL:
+      dxgsg_cat.debug(false) << "D3DCMP_NOTEQUAL, ";
+      break;
+    case D3DCMP_GEQUAL:
+      dxgsg_cat.debug(false) << "D3DCMP_GEQUAL, ";
+      break;
+    case D3DCMP_ALWAYS:
+      dxgsg_cat.debug(false) << "D3DCMP_ALWAYS, ";
+      break;
+    }
+    dxgsg_cat.debug() << ref << ")" << endl;
+#endif
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, func);
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAREF, ref);
+  }
+}
+
+
+INLINE void DXGraphicsStateGuardian::
+call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc )
+{
+  if (_blend_source_func != sfunc)
+    {
+    _blend_source_func = sfunc;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, sfunc);
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "dxSrcBlendFunc(";
+    switch (sfunc)
+        {
+        case D3DBLEND_ZERO:
+          dxgsg_cat.debug(false) << "ZERO, ";
+          break;
+        case D3DBLEND_ONE:
+          dxgsg_cat.debug(false) << "ONE, ";
+          break;
+        case D3DBLEND_DESTCOLOR:
+          dxgsg_cat.debug(false) << "DESTCOLOR, ";
+          break;
+        case D3DBLEND_INVDESTCOLOR:
+          dxgsg_cat.debug(false) << "INVDESTCOLOR, ";
+          break;
+        case D3DBLEND_SRCALPHA:
+          dxgsg_cat.debug(false) << "SRCALPHA, ";
+          break;
+        case D3DBLEND_INVSRCALPHA:
+          dxgsg_cat.debug(false) << "INVSRCALPHA, ";
+          break;
+        case D3DBLEND_DESTALPHA:
+          dxgsg_cat.debug(false) << "DESTALPHA, ";
+          break;
+        case D3DBLEND_INVDESTALPHA:
+          dxgsg_cat.debug(false) << "INVDESTALPHA, ";
+          break;
+        case D3DBLEND_SRCALPHASAT:
+          dxgsg_cat.debug(false) << "SRCALPHASAT, ";
+          break;
+        default:
+          dxgsg_cat.debug(false) << "unknown, ";
+          break;
+        }
+    dxgsg_cat.debug(false) << endl;
+#endif
+    }
+  if ( _blend_dest_func != dfunc)
+    {
+    _blend_dest_func = dfunc;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, dfunc);
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "dxDstBlendFunc(";
+    switch (dfunc)
+        {
+        case D3DBLEND_ZERO:
+          dxgsg_cat.debug(false) << "ZERO, ";
+          break;
+        case D3DBLEND_ONE:
+          dxgsg_cat.debug(false) << "ONE, ";
+          break;
+        case D3DBLEND_DESTCOLOR:
+          dxgsg_cat.debug(false) << "DESTCOLOR, ";
+          break;
+        case D3DBLEND_INVDESTCOLOR:
+          dxgsg_cat.debug(false) << "INVDESTCOLOR, ";
+          break;
+        case D3DBLEND_SRCALPHA:
+          dxgsg_cat.debug(false) << "SRCALPHA, ";
+          break;
+        case D3DBLEND_INVSRCALPHA:
+          dxgsg_cat.debug(false) << "INVSRCALPHA, ";
+          break;
+        case D3DBLEND_DESTALPHA:
+          dxgsg_cat.debug(false) << "DESTALPHA, ";
+          break;
+        case D3DBLEND_INVDESTALPHA:
+          dxgsg_cat.debug(false) << "INVDESTALPHA, ";
+          break;
+        case D3DBLEND_SRCALPHASAT:
+          dxgsg_cat.debug(false) << "SRCALPHASAT, ";
+          break;
+        }
+    dxgsg_cat.debug(false) << endl;
+#endif
+    }
+}
+
+INLINE void DXGraphicsStateGuardian::
+enable_zwritemask(bool val) {
+    if (_depth_write_enabled != val) {
+        _depth_write_enabled = val;
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, val);
+    }
+}
+
+/**  unimplemented
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_multisample
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_multisample(bool val) {
+  _multisample_enabled = val;
+  #ifdef NDEBUG
+    dxgsg_cat.error() << "dx multisample unimplemented!!\n";
+  #endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_multisample_alpha_one
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_multisample_alpha_one(bool val) {
+  if (_multisample_alpha_one_enabled != val) {
+    _multisample_alpha_one_enabled = val;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_multisample_alpha_mask
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_multisample_alpha_mask(bool val) {
+  if (_multisample_alpha_mask_enabled != val) {
+    _multisample_alpha_mask_enabled = val;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_point_smooth
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_point_smooth(bool val) {
+  // _point_smooth_enabled = val;
+
+  #ifdef NDEBUG
+    dxgsg_cat.error() << "dx point smoothing unimplemented!!\n";
+  #endif
+}
+*/
+

+ 7301 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -0,0 +1,7301 @@
+// Filename: dxGraphicsStateGuardian.cxx
+// Created by:  mike (02Feb99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include <pandabase.h>
+#include <directRenderTraverser.h>
+#include <cullTraverser.h>
+#include <displayRegion.h>
+#include <projectionNode.h>
+#include <camera.h>
+#include <renderBuffer.h>
+#include <geom.h>
+#include <geomSphere.h>
+#include <geomIssuer.h>
+#include <graphicsWindow.h>
+#include <graphicsChannel.h>
+#include <projection.h>
+#include <get_rel_pos.h>
+#include <perspectiveProjection.h>
+#include <ambientLight.h>
+#include <directionalLight.h>
+#include <pointLight.h>
+#include <spotlight.h>
+#include <projectionNode.h>
+#include <transformTransition.h>
+#include <colorTransition.h>
+#include <lightTransition.h>
+#include <textureTransition.h>
+#include <renderModeTransition.h>
+#include <materialTransition.h>
+#include <colorBlendTransition.h>
+#include <colorMaskTransition.h>
+#include <texMatrixTransition.h>
+#include <texGenTransition.h>
+#include <textureApplyTransition.h>
+#include <clipPlaneTransition.h>
+#include <transparencyTransition.h>
+#include <fogTransition.h>
+#include <linesmoothTransition.h>
+#include <depthTestTransition.h>
+#include <depthWriteTransition.h>
+#include <cullFaceTransition.h>
+#include <stencilTransition.h>
+
+#ifdef DO_PSTATS
+#include <pStatTimer.h>
+#include <pStatCollector.h>
+#endif
+
+#include "config_dxgsg8.h"
+#include "dxGraphicsStateGuardian8.h"
+
+// disable nameless struct 'warning'
+#pragma warning (disable : 4201)
+
+#include <mmsystem.h>
+
+// print out simple drawprim stats every few secs
+//#define COUNT_DRAWPRIMS
+
+//#define PRINT_TEXSTATS
+// #define MAKE_FPSMETER_TRANSPARENT    
+
+//#define DISABLE_DECALING
+#define DISABLE_POLYGON_OFFSET_DECALING
+// currently doesnt work well enough in toontown models for us to use
+// prob is when viewer gets close to decals, they disappear into wall poly, need to investigate
+
+// test non-optimized general geom pipe for all models
+// apparently DPStrided faults for some color G_OVERALL cases, so comment out for now
+// not clear that it is actually faster in practice, it may even be slightly slower
+#define DONT_USE_DRAWPRIMSTRIDED
+
+//const int VERT_BUFFER_SIZE = (8*1024L);
+// For sparkle particles, we can have 4 vertices per sparkle, and a
+// particle pool size of 1024 particles
+
+// for sprites, 1000 prims, 6 verts/prim, 24 bytes/vert
+const int VERT_BUFFER_SIZE = (32*6*1024L);
+
+// if defined, pandadx only handles 1 panda display region
+// note multiple region code doesnt work now (see prepare_display_region,set_clipper)
+#define NO_MULTIPLE_DISPLAY_REGIONS
+
+TypeHandle DXGraphicsStateGuardian::_type_handle;
+
+// bit masks used for drawing primitives
+// bitmask type: normal=0x1,color=0x2,texcoord=0x4
+typedef enum { NothingSet=0,NormalOnly,ColorOnly,Normal_Color,TexCoordOnly,
+               Normal_TexCoord,Color_TexCoord,Normal_Color_TexCoord
+} DrawLoopFlags;
+
+#define PER_NORMAL   NormalOnly
+#define PER_COLOR    ColorOnly
+#define PER_TEXCOORD TexCoordOnly
+
+// technically DX7's front-end has no limit on the number of lights, but it's simpler for
+// this implementation to set a small GL-like limit to make the light array traversals short
+// and so I dont have to write code that reallocs light arrays
+#define DXGSG_MAX_LIGHTS 8
+
+static D3DMATRIX matIdentity;
+
+#ifdef COUNT_DRAWPRIMS
+// you should just use Intel GPT instead of this stuff
+
+static DWORD cDPcount=0;
+static DWORD cVertcount=0;
+static DWORD cTricount=0;
+static DWORD cGeomcount=0;
+
+static LPDIRECTDRAWSURFACE7 pLastTexture=NULL;
+static DWORD cDP_noTexChangeCount=0;
+static LPDIRECT3DDEVICE7 global_pD3DDevice = NULL;
+
+static void CountDPs(DWORD nVerts,DWORD nTris) {
+    cDPcount++;
+    cVertcount+=nVerts;
+    cTricount+=nTris;
+
+    if(_pCurDeviceTexture==pLastTexture) {  
+        cDP_noTexChangeCount++;
+    } else pLastTexture = _pCurDeviceTexture;
+}
+#else
+#define CountDPs(nv,nt)
+#endif
+
+#define MY_D3DRGBA(r,g,b,a) ((D3DCOLOR) D3DRGBA(r,g,b,a))
+
+#if defined(DO_PSTATS) || defined(PRINT_TEXSTATS)
+static bool bTexStatsRetrievalImpossible=false;
+#endif
+
+//#define Colorf_to_D3DCOLOR(out_color) (MY_D3DRGBA((out_color)[0], (out_color)[1], (out_color)[2], (out_color)[3]))
+
+INLINE DWORD
+Colorf_to_D3DCOLOR(const Colorf &cColorf) {
+// MS VC defines _M_IX86 for x86.  gcc should define _X86_
+#if defined(_M_IX86) || defined(_X86_)
+    DWORD d3dcolor,tempcolorval=255;
+
+    // note the default FPU rounding mode will give 255*0.5f=0x80, not 0x7F as VC would force it to by resetting rounding mode
+    // dont think this makes much difference
+
+    __asm {
+        push ebx   ; want to save this in case this fn is inlined
+        push ecx
+        mov ecx, cColorf
+        fild tempcolorval
+        fld DWORD PTR [ecx]
+        fmul ST(0),ST(1)
+        fistp tempcolorval  ; no way to store directly to int register
+        mov eax, tempcolorval
+        shl eax, 16
+
+        fld DWORD PTR [ecx+4]  ;grn
+        fmul ST(0),ST(1)
+        fistp tempcolorval   
+        mov ebx,tempcolorval
+        shl ebx, 8
+        or eax,ebx
+
+        fld DWORD PTR [ecx+8]  ;blue
+        fmul ST(0),ST(1)
+        fistp tempcolorval
+        or eax,tempcolorval
+
+        fld DWORD PTR [ecx+12] ;alpha
+        fmul ST(0),ST(1)
+        fistp tempcolorval
+        ; simulate pop 255.0 off FP stack w/o store, mark top as empty and increment stk ptr
+        ffree ST(0)
+        fincstp
+        mov ebx,tempcolorval
+        shl ebx, 24
+        or eax,ebx
+        mov d3dcolor,eax
+        pop ecx
+        pop ebx
+    }
+
+   //   dxgsg_cat.debug() << (void*)d3dcolor << endl;
+   return d3dcolor;
+#else //!_X86_
+   return MY_D3DRGBA(cColorf[0], cColorf[1], cColorf[2], cColorf[3]);
+#endif //!_X86_
+}
+
+void DXGraphicsStateGuardian::
+set_color_clear_value(const Colorf& value) {
+  _color_clear_value = value;
+  _d3dcolor_clear_value =  Colorf_to_D3DCOLOR(value);
+}
+
+void DXGraphicsStateGuardian::SetFPSMeterPosition(RECT &view_rect) {
+    if(_fpsmeter_verts==NULL)
+      return;
+
+    DWORD renderWid = view_rect.right - view_rect.left;
+    DWORD renderHt = view_rect.bottom - view_rect.top;
+
+    // adjust these to match fontsize (these are hacks for default font, probably should get char width from win32)
+    #define FPSMETER_NUMFONTLETTERS 11            // need 11 letters [0-9.]
+    #define NUM_FPSMETER_LETTERS 6                // field width used for display
+    #define FPSMETER_LETTER_WIDTH  9
+    #define FPSMETER_LETTER_HEIGHT 12
+    
+    #define FPSMETER_SUFFIX " FPS"
+    #define FPSMETER_SUFFIXLEN 4
+
+    float top_offset,left_offset;
+    float letter_width=FPSMETER_LETTER_WIDTH;
+    float z = 0.1f;  // shouldnt matter since I turn off zfunc and clipping.  these values are written into zbuf, but shouldnt matter since its endofframe
+    float rhw=1.0f;
+
+    top_offset=0.05f * renderHt;
+
+    left_offset = 0.99f*renderWid-letter_width*(NUM_FPSMETER_LETTERS+FPSMETER_SUFFIXLEN);
+    if(left_offset<0.0f)
+        left_offset=0.0f;
+
+    float *fltptr= (float*)_fpsmeter_verts;
+
+    // poly color should be irrelevant since fps texblend throws it away
+    D3DCOLOR fpscolr =  (D3DCOLOR) 0xFFFFFFFF;  //MY_D3DRGBA(1.0f,1.0f,1.0f,1.0f);  
+
+    #define WRITE_FPSMETER_VERT(x,y,z,w,colr,u,v) { *fltptr = x; fltptr++;    \
+                                           *fltptr = y; fltptr++;             \
+                                           *fltptr = z; fltptr++;             \
+                                           *fltptr = w; fltptr++;             \
+                                           (*((DWORD*)fltptr)) = colr; fltptr++; \
+                                           *fltptr = u; fltptr++;             \
+                                           *fltptr = v; fltptr++;             }
+
+    float u_letter_width = _fps_u_usedwidth/(float)FPSMETER_NUMFONTLETTERS;
+    float height = FPSMETER_LETTER_HEIGHT;
+    float cur_xoffset=left_offset;
+
+    // fmt is 3.2, need 12 tris.  cant share verts since texcoords differ
+
+    #define WRITE_FPS_SQUARE(x1,y1,x2,y2,u1,v1,u2,v2)                      \
+        float *firstvertptr = fltptr;                                      \
+        WRITE_FPSMETER_VERT(x1,y1,z,rhw,fpscolr,u1,v1);                    \
+        WRITE_FPSMETER_VERT(x1,y2,z,rhw,fpscolr,u1,v2);                    \
+        float *thirdvertptr=fltptr;                                        \
+        WRITE_FPSMETER_VERT(x2,y2,z,rhw,fpscolr,u2,v2);                    \
+        memcpy(fltptr,thirdvertptr,_fps_vertexsize);                       \
+        fltptr = (float*) (((BYTE*)fltptr) + _fps_vertexsize);             \
+        WRITE_FPSMETER_VERT(x2,y1,z,rhw,fpscolr,u2,v1);                    \
+        memcpy(fltptr,firstvertptr,_fps_vertexsize);                       \
+        fltptr = (float*) (((BYTE*)fltptr) + _fps_vertexsize);  
+
+    for(int i=0;i<NUM_FPSMETER_LETTERS;i++,cur_xoffset+=letter_width) {
+        WRITE_FPS_SQUARE(cur_xoffset,top_offset,cur_xoffset+letter_width,top_offset+height,0.0f,0.0f,u_letter_width,_fps_v_usedheight);
+    }
+
+    // write verts for suffix square
+    WRITE_FPS_SQUARE(cur_xoffset,top_offset,cur_xoffset+FPSMETER_SUFFIXLEN*letter_width,top_offset+height,u_letter_width*FPSMETER_NUMFONTLETTERS,0.0f,u_letter_width*(FPSMETER_NUMFONTLETTERS+FPSMETER_SUFFIXLEN),_fps_v_usedheight);
+}
+
+void DXGraphicsStateGuardian::FillFPSMeterTexture(void) {
+    assert(_fpsmeter_font_surf!=NULL);
+    HRESULT hr;
+
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    _fpsmeter_font_surf->GetSurfaceDesc(&ddsd);
+
+    // init it to transparent black
+    if(FAILED( hr = _fpsmeter_font_surf->Lock( NULL, &ddsd,  DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) {
+        dxgsg_cat.error() << "fps meter creation failed, Lock() failed on texture! hr = " << ConvD3DErrorToString(hr) << "\n";
+        dx_show_fps_meter = false;
+        return;
+    }
+    ZeroMemory(ddsd.lpSurface,ddsd.dwWidth*ddsd.dwHeight*2);
+    _fpsmeter_font_surf->Unlock(NULL);
+
+    // draw FPS text using GDI
+    HDC hDC;
+    if(FAILED( hr = _fpsmeter_font_surf->GetDC(&hDC))) {
+        dxgsg_cat.error() << "fps meter creation failed, GetDC failed on fps font surface! hr = " << ConvD3DErrorToString(hr) << "\n";
+        dx_show_fps_meter = false;
+        return;
+    }
+
+    HFONT hfnt = (HFONT) GetStockObject(ANSI_FIXED_FONT); 
+    (void) SelectObject(hDC, hfnt);
+
+    SetTextColor(hDC, RGB(255,255,128) );
+    SetBkMode(hDC, TRANSPARENT );
+
+    char tstr[2] = {'\0','\0'};
+    RECT Rect;
+    Rect.top = 0; Rect.bottom = FPSMETER_LETTER_HEIGHT-1;
+    Rect.left = 0;  Rect.right = FPSMETER_LETTER_WIDTH;
+
+    for(int i=0;i<FPSMETER_NUMFONTLETTERS;i++) {
+        tstr[0] = '0'+i;
+        if(i==(FPSMETER_NUMFONTLETTERS-1)) {
+            tstr[0] = '.';
+        }
+        DrawText(hDC,tstr,1,&Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
+        Rect.left=Rect.right;
+        Rect.right+=FPSMETER_LETTER_WIDTH;
+    }
+
+    Rect.right=Rect.left+FPSMETER_SUFFIXLEN*FPSMETER_LETTER_WIDTH;
+    DrawText(hDC,FPSMETER_SUFFIX,FPSMETER_SUFFIXLEN,&Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
+
+    _fpsmeter_font_surf->ReleaseDC(hDC);
+
+    // GDI writes 0x00 for alpha, have to make letter pixels opaque again
+    if(FAILED( hr = _fpsmeter_font_surf->Lock( NULL, &ddsd,  DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) {
+        dxgsg_cat.error() << "fps meter creation failed, Lock() failed on texture! hr = " << ConvD3DErrorToString(hr) << "\n";
+        dx_show_fps_meter = false;
+        return;
+    }
+
+    DWORD numpixels=ddsd.dwWidth*ddsd.dwHeight;
+
+    #ifdef MAKE_FPSMETER_TRANSPARENT
+        WORD *pPixel=(WORD*)ddsd.lpSurface;
+        for(int ii=numpixels;ii>0;ii--) {
+            WORD wPixel=*pPixel;
+            if(wPixel & 0x0FFF) {
+                *pPixel |= 0xF000;  //  make written pixels opaque
+            }/* else {
+                *pPixel = 0x700F;  // otherwise background is translucent blue
+            }*/
+            pPixel++;
+        }
+    #endif
+    _fpsmeter_font_surf->Unlock(NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsStateGuardian::
+DXGraphicsStateGuardian(GraphicsWindow *win) : GraphicsStateGuardian(win) {
+    // allocate local buffers used during rendering
+
+    GraphicsStateGuardian::reset();
+
+    _pCurFvfBufPtr = NULL;
+    _pFvfBufBasePtr = new char[VERT_BUFFER_SIZE];  // allocate storage for vertex info.
+    _index_buf = new WORD[D3DMAXNUMVERTICES];  // allocate storage for vertex index info.
+    _fpsmeter_verts=NULL;
+    _fpsmeter_font_surf=NULL;
+    _dx_ready = false;
+
+    _pri = _zbuf = _back = NULL;
+    _pDD = NULL;
+    _d3dDevice = NULL;
+
+    // non-dx obj values inited here should not change if resize is 
+    // called and dx objects need to be recreated (otherwise they
+    // belong in dx_init, with other renderstate
+
+    ZeroMemory(&matIdentity,sizeof(D3DMATRIX));
+    matIdentity._11 = matIdentity._22 = matIdentity._33 = matIdentity._44 = 1.0f;
+
+    // Create a default RenderTraverser.
+    if (dx_cull_traversal) {
+        _render_traverser = new CullTraverser(this, RenderRelation::get_class_type());
+    } else {
+        _render_traverser = new DirectRenderTraverser(this, RenderRelation::get_class_type());
+    }
+
+    // All implementations have the following buffers.
+    _buffer_mask = (RenderBuffer::T_color |
+                    RenderBuffer::T_depth |
+                    RenderBuffer::T_back
+//                  RenderBuffer::T_stencil |
+//                  RenderBuffer::T_accum
+                    );
+
+    //   this is incorrect for general mono displays, need both right and left flags set.
+    //   stereo has not been handled yet for dx
+    //    _buffer_mask &= ~RenderBuffer::T_right;
+
+    _cur_read_pixel_buffer=RenderBuffer::T_front;
+
+    set_color_clear_value(_color_clear_value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsStateGuardian::
+~DXGraphicsStateGuardian() {
+    if (_d3dDevice != NULL)
+        _d3dDevice->SetTexture(0, NULL);  // this frees reference to the old texture
+    _pCurTexContext = NULL;
+
+    free_pointers();
+    delete [] _pFvfBufBasePtr;
+    delete [] _index_buf;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::reset
+//       Access: Public, Virtual
+//  Description: Resets all internal state as if the gsg were newly
+//               created.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+reset(void) {
+    GraphicsStateGuardian::reset();
+    dxgsg_cat.error() << "DXGSG reset() not implemented properly yet!\n";
+    // delete all the objs too, right?
+    //dx_init();
+}
+
+// recreate dx objects without modifying gsg state, other than clearing state cache
+void DXGraphicsStateGuardian::
+free_dxgsg_objects(void) {
+    ULONG refcnt;
+
+    free_pointers();
+
+    // dont want a full reset of gsg, just a state clear      
+    GraphicsStateGuardian::clear_cached_state(); // want gsg to pass all state settings through
+
+    _dx_ready = false;
+
+    if (_d3dDevice!=NULL) {
+        _d3dDevice->SetTexture(0,NULL);  // should release this stuff internally anyway
+        RELEASE(_d3dDevice,dxgsg,"d3dDevice",RELEASE_DOWN_TO_ZERO);
+    }
+
+    DeleteAllVideoSurfaces();
+
+    // Release the DDraw and D3D objects used by the app
+    RELEASE(_zbuf,dxgsg,"zbuffer",false);
+    RELEASE(_back,dxgsg,"backbuffer",false);
+    RELEASE(_pri,dxgsg,"primary surface",false);
+}
+
+HRESULT CALLBACK EnumTexFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param ) {
+    // wont build if its a member fn, so had to do this stuff
+    DXGraphicsStateGuardian *mystate = (DXGraphicsStateGuardian *) param;
+    assert(mystate->_cNumTexPixFmts < MAX_DX_TEXPIXFMTS);
+    memcpy( &(mystate->_pTexPixFmts[mystate->_cNumTexPixFmts]), pddpf, sizeof(DDPIXELFORMAT) );
+    mystate->_cNumTexPixFmts++;
+    return DDENUMRET_OK;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::reset
+//       Access: Public, Virtual
+//  Description: Handles initialization which assumes that DX has already been
+//               set up.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+dx_init(  LPDIRECTDRAW7     context,
+          LPDIRECTDRAWSURFACE7  pri,
+          LPDIRECTDRAWSURFACE7  back,
+          LPDIRECTDRAWSURFACE7  zbuf,
+          LPDIRECT3D7          pD3D,
+          LPDIRECT3DDEVICE7    pDevice,
+          RECT viewrect) {
+    _pDD = context;
+    _pri = pri;
+    _back = back;
+    _zbuf = zbuf;
+    _d3d = pD3D;
+    _d3dDevice = pDevice;
+    _view_rect = viewrect;
+
+    ZeroMemory(&_lmodel_ambient,sizeof(Colorf));
+    _d3dDevice->SetRenderState( D3DRENDERSTATE_AMBIENT, 0x0);
+
+    _light_enabled = (bool *)NULL;
+    _cur_light_enabled = (bool *)NULL;
+    _clip_plane_enabled = (bool *)NULL;
+    _cur_clip_plane_enabled = (bool *)NULL;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPLANEENABLE , 0x0);
+
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, true);
+    _clipping_enabled = true;
+
+    _CurShadeMode =  D3DSHADE_FLAT;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, _CurShadeMode);
+
+    _depth_test_enabled = true; 
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, _depth_test_enabled);
+
+    // need to free these properly
+    _cNumTexPixFmts = 0;
+    _pTexPixFmts = NULL;
+    _pCurTexContext = NULL;
+
+    //Color and alpha transform variables
+    _color_transform_enabled = false;
+    _alpha_transform_enabled = false;
+    _color_transform_required = _color_transform_enabled || _alpha_transform_enabled;
+
+    _current_color_mat = LMatrix4f::ident_mat();
+    _current_alpha_offset = 0;
+    _current_alpha_scale = 1;
+
+    // none of these are implemented
+    //_multisample_enabled = false;
+    //_point_smooth_enabled = false;
+
+    _line_smooth_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_EDGEANTIALIAS, false);
+
+    _color_material_enabled = false;
+    _normals_enabled = false;
+    
+    _depth_test_enabled = D3DZB_FALSE;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+
+    _blend_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, (DWORD)_blend_enabled);
+
+    _d3dDevice->GetRenderState(D3DRENDERSTATE_SRCBLEND, (DWORD*)&_blend_source_func);
+    _d3dDevice->GetRenderState(D3DRENDERSTATE_DESTBLEND, (DWORD*)&_blend_dest_func);
+
+    _fog_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, _fog_enabled);
+
+    _decal_level = 0;
+    _current_projection_mat = LMatrix4f::ident_mat();
+    _projection_mat_stack_count = 0;
+    _issued_color_enabled = false;
+    _enable_all_color = true;
+
+//  GL stuff that hasnt been translated to DX
+//    _scissor_enabled = false;
+//    _multisample_alpha_one_enabled = false;
+//    _multisample_alpha_mask_enabled = false;
+//    _line_width = 1.0f;
+//    _point_size = 1.0f;
+
+    assert(_back!=NULL);  // dxgsg is always double-buffered right now
+
+#ifdef COUNT_DRAWPRIMS
+     global_pD3DDevice = pDevice;
+#endif
+    _pCurrentGeomContext = NULL;
+    _bDrawPrimDoSetupVertexBuffer = false; 
+
+    _last_testcooplevel_result = S_OK;
+
+    // only 1 channel on dx currently
+    _panda_gfx_channel = _win->get_channel(0);
+
+    HRESULT hr;
+
+    _pTexPixFmts = new DDPIXELFORMAT[MAX_DX_TEXPIXFMTS];
+    _cNumTexPixFmts = 0;
+
+    assert(_pTexPixFmts!=NULL);
+
+    if (pDevice->EnumTextureFormats(EnumTexFmtsCallback, this) != S_OK) {
+        dxgsg_cat.error() << "EnumTextureFormats failed!!\n";
+    }
+
+    if (FAILED(hr = pDevice->GetCaps(&_D3DDevDesc))) {
+        dxgsg_cat.fatal() << "GetCaps failed on D3D Device\n";
+        exit(1);
+    }
+
+    DX_DECLARE_CLEAN(DDCAPS,ddCaps);
+    if (FAILED(hr = _pDD->GetCaps(&ddCaps,NULL))) {
+        dxgsg_cat.fatal() << "GetCaps failed on DDraw\n";
+        exit(1);
+    }
+
+    _bIsTNLDevice = (IsEqualGUID(_D3DDevDesc.deviceGUID,IID_IDirect3DTnLHalDevice)!=0);
+
+    if ((dx_decal_type==GDT_offset) && !(_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBIAS)) {
+#ifdef _DEBUG
+        // dx7 doesnt support PLANEMASK renderstate
+#if(DIRECT3D_VERSION < 0x700)
+        dxgsg_cat.debug() << "dx-decal-type 'offset' not supported by hardware, switching to decal masking\n";
+#else
+        dxgsg_cat.debug() << "dx-decal-type 'offset' not supported by hardware, switching to decal double-draw blend-based masking\n";
+#endif
+#endif
+#if(DIRECT3D_VERSION < 0x700)
+        dx_decal_type = GDT_mask;
+#else
+        dx_decal_type = GDT_blend;
+#endif
+    }
+
+#ifdef DISABLE_POLYGON_OFFSET_DECALING
+#ifdef _DEBUG
+    dxgsg_cat.spam() << "polygon-offset decaling disabled in dxgsg, switching to double-draw decaling\n";
+#endif
+
+#if(DIRECT3D_VERSION < 0x700)
+    dx_decal_type = GDT_mask;
+#else
+    dx_decal_type = GDT_blend;
+#endif
+#endif
+
+    if ((dx_decal_type==GDT_mask) && !(_D3DDevDesc.dpcTriCaps.dwMiscCaps & D3DPMISCCAPS_MASKPLANES)) {
+#ifdef _DEBUG
+        dxgsg_cat.debug() << "No hardware support for colorwrite disabling, switching to dx-decal-type 'mask' to 'blend'\n";
+#endif
+        dx_decal_type = GDT_blend;
+    }
+
+    if (((dx_decal_type==GDT_blend)||(dx_decal_type==GDT_mask)) && !(_D3DDevDesc.dpcTriCaps.dwMiscCaps & D3DPMISCCAPS_MASKZ)) {
+        dxgsg_cat.error() << "dx-decal-type mask impossible to implement, no hardware support for Z-masking, decals will not appear correctly!\n";
+    }
+
+//#define REQUIRED_BLENDCAPS (D3DPBLENDCAPS_ZERO|D3DPBLENDCAPS_ONE|D3DPBLENDCAPS_SRCCOLOR|D3DPBLENDCAPS_INVSRCCOLOR| \
+//                            D3DPBLENDCAPS_SRCALPHA|D3DPBLENDCAPS_INVSRCALPHA | D3DPBLENDCAPS_DESTALPHA|D3DPBLENDCAPS_INVDESTALPHA|D3DPBLENDCAPS_DESTCOLOR|D3DPBLENDCAPS_INVDESTCOLOR)
+// voodoo3 doesnt support commented out ones, & we dont need them now
+
+#define REQUIRED_BLENDCAPS (D3DPBLENDCAPS_ZERO|D3DPBLENDCAPS_ONE|  /*D3DPBLENDCAPS_SRCCOLOR|D3DPBLENDCAPS_INVSRCCOLOR| */ \
+                            D3DPBLENDCAPS_SRCALPHA|D3DPBLENDCAPS_INVSRCALPHA /* | D3DPBLENDCAPS_DESTALPHA|D3DPBLENDCAPS_INVDESTALPHA|D3DPBLENDCAPS_DESTCOLOR|D3DPBLENDCAPS_INVDESTCOLOR*/)
+
+    if (((_D3DDevDesc.dpcTriCaps.dwSrcBlendCaps & REQUIRED_BLENDCAPS)!=REQUIRED_BLENDCAPS) ||
+        ((_D3DDevDesc.dpcTriCaps.dwDestBlendCaps & REQUIRED_BLENDCAPS)!=REQUIRED_BLENDCAPS)) {
+        dxgsg_cat.error() << "device is missing alpha blending capabilities, blending may not work correctly: SrcBlendCaps: 0x"<< (void*) _D3DDevDesc.dpcTriCaps.dwSrcBlendCaps << "  DestBlendCaps: "<< (void*) _D3DDevDesc.dpcTriCaps.dwDestBlendCaps << endl;
+    }
+
+    if (!(_D3DDevDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_TRANSPARENCY)) {
+        dxgsg_cat.error() << "device is missing texture transparency capability, transparency may not work correctly!  TextureCaps: 0x"<< (void*) _D3DDevDesc.dpcTriCaps.dwTextureCaps << endl;
+    }
+
+    // just require trilinear.  if it can do that, it can probably do all the lesser point-sampling variations too
+#define REQUIRED_TEXFILTERCAPS (D3DPTFILTERCAPS_MAGFLINEAR |  D3DPTFILTERCAPS_MINFLINEAR | D3DPTFILTERCAPS_LINEAR)
+    if ((_D3DDevDesc.dpcTriCaps.dwTextureFilterCaps & REQUIRED_TEXFILTERCAPS)!=REQUIRED_TEXFILTERCAPS) {
+        dxgsg_cat.error() << "device is missing texture bilinear filtering capability, textures may appear blocky!  TextureFilterCaps: 0x"<< (void*) _D3DDevDesc.dpcTriCaps.dwTextureFilterCaps << endl;
+    }
+#define REQUIRED_MIPMAP_TEXFILTERCAPS (D3DPTFILTERCAPS_MIPFLINEAR | D3DPTFILTERCAPS_LINEARMIPLINEAR)
+
+    if (!(ddCaps.ddsCaps.dwCaps & DDSCAPS_MIPMAP)) {
+        dxgsg_cat.debug() << "device does not have mipmap texturing filtering capability!   TextureFilterCaps: 0x"<< (void*) _D3DDevDesc.dpcTriCaps.dwTextureFilterCaps << endl;
+        dx_ignore_mipmaps = TRUE;
+    } else if ((_D3DDevDesc.dpcTriCaps.dwTextureFilterCaps & REQUIRED_MIPMAP_TEXFILTERCAPS)!=REQUIRED_MIPMAP_TEXFILTERCAPS) {
+        dxgsg_cat.debug() << "device is missing tri-linear mipmap filtering capability, texture mipmaps may not supported! TextureFilterCaps: 0x"<< (void*) _D3DDevDesc.dpcTriCaps.dwTextureFilterCaps << endl;
+    }
+
+#define REQUIRED_TEXBLENDCAPS (D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_SELECTARG1 | D3DTEXOPCAPS_SELECTARG2)
+    if ((_D3DDevDesc.dwTextureOpCaps & REQUIRED_TEXBLENDCAPS)!=REQUIRED_TEXBLENDCAPS) {
+        dxgsg_cat.error() << "device is missing some required texture blending capabilities, texture blending may not work properly! TextureOpCaps: 0x"<< (void*) _D3DDevDesc.dwTextureOpCaps << endl;
+    }
+
+    if(_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE) {
+        // watch out for drivers that emulate per-pixel fog with per-vertex fog (Riva128, Matrox Millen G200)
+        // some of these require gouraud-shading to be set to work, as if you were using vertex fog
+        _doFogType=PerPixelFog;
+    } else {
+        // every card is going to have vertex fog, since it's implemented in d3d runtime
+        assert((_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGVERTEX )!=0);
+
+        // vtx fog may look crappy if you have large polygons in the foreground and they get clipped,
+        // so you may want to disable it
+
+        if(dx_no_vertex_fog) {
+            _doFogType = None;
+        } else {
+            _doFogType = PerVertexFog;
+
+            // range-based fog only works with vertex fog in dx7/8
+            if(dx_use_rangebased_fog && (_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGRANGE))
+                _d3dDevice->SetRenderState(D3DRENDERSTATE_RANGEFOGENABLE, true);
+        }
+    }
+
+    SetRect(&clip_rect, 0,0,0,0);  // no clip rect set
+
+    // Lighting, let's turn it off by default
+    _lighting_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_LIGHTING, _lighting_enabled);
+
+    // turn on dithering if the rendertarget is < 8bits/color channel
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_back);
+    _back->GetSurfaceDesc(&ddsd_back);
+    _dither_enabled = ((ddsd_back.ddpfPixelFormat.dwRGBBitCount < 24) &&
+                       (_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_DITHER));
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE, _dither_enabled);
+
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING,true);
+
+    // Stencil test is off by default
+    _stencil_test_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILENABLE, _stencil_test_enabled);
+
+    // Antialiasing.
+    enable_line_smooth(false);
+//  enable_multisample(true);
+
+    _current_fill_mode = RenderModeProperty::M_filled;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID);
+
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);
+
+    // technically DX7's front-end has no limit on the number of lights, but it's simpler for
+    // this implementation to set a small GL-like limit to make the light array traversals short
+    // and so I dont have to write code that reallocs light arrays
+    assert((_D3DDevDesc.dwMaxActiveLights==0) ||  // 0 means infinite lights
+           (DXGSG_MAX_LIGHTS <= _D3DDevDesc.dwMaxActiveLights));
+
+    _max_lights = DXGSG_MAX_LIGHTS;
+
+    _available_light_ids = PTA(Light*)::empty_array(_max_lights);
+    _light_enabled = new bool[_max_lights];
+    _cur_light_enabled = new bool[_max_lights];
+
+    int i;
+    for (i = 0; i < _max_lights; i++) {
+        _available_light_ids[i] = NULL;
+        _light_enabled[i] = false;
+    }
+
+    if(dx_auto_normalize_lighting)
+         _d3dDevice->SetRenderState(D3DRENDERSTATE_NORMALIZENORMALS, true);
+
+    // Set up the clip plane id map
+    _max_clip_planes = D3DMAXUSERCLIPPLANES;
+    _available_clip_plane_ids = PTA(PlaneNode*)::empty_array(_max_clip_planes);
+    _clip_plane_enabled = new bool[_max_clip_planes];
+    _cur_clip_plane_enabled = new bool[_max_clip_planes];
+    for (i = 0; i < _max_clip_planes; i++) {
+        _available_clip_plane_ids[i] = NULL;
+        _clip_plane_enabled[i] = false;
+    }
+
+    // initial clip rect
+    SetRect(&clip_rect, 0,0,0,0);     // no clip rect set
+
+    // must do SetTSS here because redundant states are filtered out by our code based on current values above, so
+    // initial conditions must be correct
+
+    _CurTexBlendMode = TextureApplyProperty::M_modulate;
+    SetTextureBlendMode(_CurTexBlendMode,FALSE);
+    _texturing_enabled = false;
+    _d3dDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DISABLE);  // disables texturing
+
+    // Init more Texture State
+    _CurTexMagFilter=_CurTexMinFilter=Texture::FT_nearest;
+    _CurTexWrapModeU=_CurTexWrapModeV=Texture::WM_clamp;
+    _CurTexAnisoDegree=1;
+
+    // this code must match apply_texture() code for states above
+    // so DX TSS renderstate matches dxgsg state
+
+    _d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT);
+    _d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFP_POINT);
+    _d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE);
+    _d3dDevice->SetTextureStageState(0, D3DTSS_MAXANISOTROPY,_CurTexAnisoDegree);
+    _d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU));
+    _d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV));
+
+#ifdef _DEBUG
+    if ((_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS) &&
+        (dx_global_miplevel_bias!=0.0f)) {
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MIPMAPLODBIAS, *((LPDWORD) (&dx_global_miplevel_bias)) );
+    }
+#endif
+
+    if(dx_full_screen_antialiasing) {
+      if(_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT) {
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ANTIALIAS,D3DANTIALIAS_SORTINDEPENDENT);
+        if(dxgsg_cat.is_debug()) 
+            dxgsg_cat.debug() << "enabling full-screen anti-aliasing\n";
+      } else {
+        if(dxgsg_cat.is_debug()) 
+            dxgsg_cat.debug() << "device doesnt support full-screen anti-aliasing\n";
+      }
+    }
+
+#ifndef NDEBUG
+    if(dx_force_backface_culling!=0) {
+      if((dx_force_backface_culling > 0) &&
+         (dx_force_backface_culling < D3DCULL_FORCE_DWORD)) {
+             _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, dx_force_backface_culling);
+      } else {
+          dx_force_backface_culling=0;
+          if(dxgsg_cat.is_debug()) 
+              dxgsg_cat.debug() << "error, invalid value for dx-force-backface-culling\n";
+      }
+    }
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, dx_force_backface_culling);
+#else
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
+#endif
+
+    _alpha_func = D3DCMP_ALWAYS;
+    _alpha_func_ref = 0;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, _alpha_func);
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHAREF, _alpha_func_ref);
+    _alpha_test_enabled = false;
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, _alpha_test_enabled);
+
+    // need to release this better, so dx_init can be called multiple times
+
+    if(dx_show_fps_meter) {
+        _start_time = timeGetTime();
+        _current_fps = 0.0f;
+        _start_frame_count = _cur_frame_count = 0;
+
+        // create the fpsmeter font texture
+        DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+        ddsd.dwFlags         =  DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ;
+        ddsd.ddsCaps.dwCaps  = DDSCAPS_TEXTURE;
+        ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE | DDSCAPS2_HINTSTATIC;  
+
+        ddsd.dwTextureStage=0;
+        ddsd.dwFlags |= DDSD_TEXTURESTAGE;
+        ddsd.dwMipMapCount = 1;
+
+        // note GDI cant draw to 4-4-4-4 fmt DDSURF on win9x (GetDC will fail)
+
+        DDPIXELFORMAT *pCurPixFmt;
+        for(i=0,pCurPixFmt=&_pTexPixFmts[_cNumTexPixFmts-1];i<_cNumTexPixFmts;i++,pCurPixFmt--) {
+            if((pCurPixFmt->dwRGBBitCount==16) && 
+        #ifdef MAKE_FPSMETER_TRANSPARENT    
+               (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS) &&
+               (pCurPixFmt->dwRGBAlphaBitMask==0x8000)
+        #else
+               ((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)==0) && 
+               (pCurPixFmt->dwBBitMask==0x001F)
+        #endif
+               )  // emacs gets confused if we don't match parens accurately.
+               break;
+        }
+
+        if(i>=_cNumTexPixFmts) {
+              // if this fails, no 4-4-4-4 fmt, need code to use 5-5-5-1 
+            dxgsg_cat.error() << "couldnt find 4-4-4-4 tex fmt for fpsmeter font!\n";
+            dx_show_fps_meter = false;
+            return;
+        }
+
+        memcpy(&ddsd.ddpfPixelFormat,pCurPixFmt,sizeof(DDPIXELFORMAT));
+
+        DWORD fontareaHeight = FPSMETER_LETTER_HEIGHT;
+        DWORD fontareaWidth = FPSMETER_LETTER_WIDTH * (FPSMETER_NUMFONTLETTERS + FPSMETER_SUFFIXLEN);
+
+        // this is just the area used for the letters, not the suffix
+        DWORD letterfontareaWidth = FPSMETER_LETTER_WIDTH * FPSMETER_NUMFONTLETTERS;
+
+        DWORD texdim_x = fontareaWidth;
+        DWORD texdim_y = fontareaHeight;
+
+        #define ROUND_UP_TO_POW2(val)    if(!ISPOW2(val)) {                    \
+                                            for(int i=31;i>=0;i--) {           \
+                                                if(((1<<i) & val)!=0) {        \
+                                                   val = 1<<(i+1);             \
+                                                   break;                      \
+                                                }}}
+                                                
+        ROUND_UP_TO_POW2(texdim_x);
+        ROUND_UP_TO_POW2(texdim_y);
+
+        // could handle this case less wastefully by creating font texture w/multiple rows, 
+        // so its naturally square
+        if(_D3DDevDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY ) {
+            if(texdim_y> texdim_x)
+              texdim_x = texdim_y;
+            texdim_y = texdim_x;
+        }
+
+        ddsd.dwWidth = texdim_x;
+        ddsd.dwHeight = texdim_y;
+
+        if(_fpsmeter_font_surf!=NULL) {
+            ULONG refcnt;
+            RELEASE(_fpsmeter_font_surf,dxgsg,"fpsmeter fontsurf",false);
+        }
+
+        PRINTREFCNT(_pDD,"pre-fpsmeter-font-create IDirectDraw7");
+
+        // Create a new surface for the texture
+        if(FAILED( hr = _pDD->CreateSurface( &ddsd, &_fpsmeter_font_surf, NULL ) )) {
+            dxgsg_cat.error() << "CreateSurface() failed for fpsmeter font!  hr = " << ConvD3DErrorToString(hr) << "\n";
+            dx_show_fps_meter = false;
+            return;
+        }
+
+        PRINTREFCNT(_pDD,"post-fpsmeter-font-create IDirectDraw7");
+
+        FillFPSMeterTexture();
+
+        // make buffer for fps meter tri data
+
+        _fps_vertexsize = sizeof(D3DVALUE) * 4 + sizeof(D3DCOLOR) + sizeof(float) * 2;
+        _fpsmeter_fvfflags = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+
+        int numverts=(NUM_FPSMETER_LETTERS+1)*2*3;  // +1 for square to hold suffix
+
+        if(_fpsmeter_verts == NULL)
+            _fpsmeter_verts = (DWORD *) new BYTE[_fps_vertexsize*numverts];
+
+        _fps_u_usedwidth = letterfontareaWidth/(float)texdim_x;
+        _fps_v_usedheight = fontareaHeight/(float)texdim_y;
+
+        SetFPSMeterPosition(_view_rect);
+    }
+
+    // Make sure the DX state matches all of our initial attribute states.
+    PT(DepthTestTransition) dta = new DepthTestTransition;
+    PT(DepthWriteTransition) dwa = new DepthWriteTransition;
+    PT(CullFaceTransition) cfa = new CullFaceTransition;
+    PT(LightTransition) la = new LightTransition;
+    PT(TextureTransition) ta = new TextureTransition;
+
+    dta->issue(this);
+    dwa->issue(this);
+    cfa->issue(this);
+    la->issue(this);
+    ta->issue(this); // no curtextcontext, this does nothing.  dx should already be properly inited above anyway
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::clear
+//       Access: Public, Virtual
+//  Description: Clears all of the indicated buffers to their assigned
+//               colors.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+clear(const RenderBuffer &buffer) {
+    DO_PSTATS_STUFF(PStatTimer timer(_win->_clear_pcollector));
+
+    nassertv(buffer._gsg == this);
+    int buffer_type = buffer._buffer_type;
+
+    int flags = 0;
+
+    if (buffer_type & RenderBuffer::T_depth)
+        flags |=  D3DCLEAR_ZBUFFER;
+    if (buffer_type & RenderBuffer::T_back)       //set appropriate flags
+        flags |=  D3DCLEAR_TARGET;
+    if (buffer_type & RenderBuffer::T_stencil)
+        flags |=  D3DCLEAR_STENCIL;
+
+    HRESULT  hr = _d3dDevice->Clear(0, NULL, flags, _d3dcolor_clear_value,
+                                    (D3DVALUE) _depth_clear_value, (DWORD)_stencil_clear_value);
+    if (hr != DD_OK)
+        dxgsg_cat.error() << "clear_buffer failed:  Clear returned " << ConvD3DErrorToString(hr) << endl;
+    /*  The following line will cause the background to always clear to a medium red
+    _color_clear_value[0] = .5;
+    /*  The following lines will cause the background color to cycle from black to red.
+    _color_clear_value[0] += .001;
+     if (_color_clear_value[0] > 1.0f) _color_clear_value[0] = 0.0f;
+     */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::clear
+//       Access: Public, Virtual
+//  Description: Clears all of the indicated buffers to their assigned
+//               colors.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+clear(const RenderBuffer &buffer, const DisplayRegion *region) {
+    DisplayRegionStack old_dr = push_display_region(region);
+    prepare_display_region();
+    clear(buffer);
+    pop_display_region(old_dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_light
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian::
+enable_light(int light, bool val) {
+    if (_light_enabled[light] != val) {
+        _light_enabled[light] = val;
+        HRESULT res = _d3dDevice->LightEnable( light, val  );
+
+#ifdef GSG_VERBOSE
+        dxgsg_cat.debug()
+        << "LightEnable(" << light << "=" << val << ")" << endl;
+#endif
+
+        return(res == DD_OK);
+    }
+    return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::prepare_display_region
+//       Access: Public, Virtual
+//  Description: Prepare a display region for rendering (set up
+//       scissor region and viewport)
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+prepare_display_region() {
+    if (_current_display_region == (DisplayRegion*)0L) {
+        dxgsg_cat.error()
+        << "Invalid NULL display region in prepare_display_region()\n";
+    } else if (_current_display_region != _actual_display_region) {
+        _actual_display_region = _current_display_region;
+
+#ifndef NO_MULTIPLE_DISPLAY_REGIONS
+    int l, b, w, h;
+    _actual_display_region->get_region_pixels(l, b, w, h);
+    GLint x = GLint(l);
+    GLint y = GLint(b);
+    GLsizei width = GLsizei(w);
+    GLsizei height = GLsizei(h);
+#ifdef WBD_GL_MODE
+//    call_glScissor( x, y, width, height );
+//    call_glViewport( x, y, width, height );
+#else
+    if ( _scissor_x != x || _scissor_y != y ||
+            _scissor_width != width || _scissor_height != height )
+        {
+        _scissor_x = x; _scissor_y = y;
+        _scissor_width = width; _scissor_height = height;
+        RECT cliprect;
+        SetRect(&cliprect, x, y, x+width, y+height );
+        set_clipper(cliprect);
+        }
+#endif   //WBD_GL_MODE
+#endif
+    }
+}
+
+#ifndef NO_MULTIPLE_DISPLAY_REGIONS
+////////////////////////////////////////////////////////////////////
+//     Function: set_clipper
+//       Access:
+//  Description: Useless in DX at the present time
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::set_clipper(RECT cliprect) {
+
+    LPDIRECTDRAWCLIPPER Clipper;
+    HRESULT result;
+
+    // For windowed mode, the clip region is associated with the window,
+    // and DirectX does not allow you to create clip regions.
+    if (dx_full_screen) return;
+
+    /* The cliprect we receive is normalized so that (0,0) means the upper left of
+       the client portion of the window.
+        At least, I think that's true, and the following code assumes that.
+        So we must adjust the clip region by offsetting it to the origin of the
+        view rectangle.
+    */
+    clip_rect = cliprect;       // store the normalized clip rect
+    cliprect.left += _view_rect.left;
+    cliprect.right += _view_rect.left;
+    cliprect.top += _view_rect.top;
+    cliprect.bottom += _view_rect.top;
+    RGNDATA *rgn_data = (RGNDATA *)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT));
+    HRGN hrgn = CreateRectRgn(cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
+    GetRegionData(hrgn, sizeof(RGNDATAHEADER) + sizeof(RECT), rgn_data);
+
+    if (_pri->GetClipper(&Clipper) != DD_OK) {
+        result = _pDD->CreateClipper(0, &Clipper, NULL);
+        result = Clipper->SetClipList(rgn_data, 0);
+        result = _pri->SetClipper(Clipper);
+    } else {
+        result = Clipper->SetClipList(rgn_data, 0 );
+        if (result == DDERR_CLIPPERISUSINGHWND) {
+            result = _pri->SetClipper(NULL);
+            result = _pDD->CreateClipper(0, &Clipper, NULL);
+            result = Clipper->SetClipList(rgn_data, 0 ) ;
+            result = _pri->SetClipper(Clipper);
+        }
+    }
+    free(rgn_data);
+    DeleteObject(hrgn);
+}
+#endif
+
+#if defined(_DEBUG) || defined(COUNT_DRAWPRIMS)
+typedef enum {DrawPrim,DrawIndexedPrim,DrawPrimStrided} DP_Type;
+static const char *DP_Type_Strs[3] = {"DrawPrimitive","DrawIndexedPrimitive","DrawPrimitiveStrided"};
+
+void INLINE TestDrawPrimFailure(DP_Type dptype,HRESULT hr,LPDIRECTDRAW7 pDD,DWORD nVerts,DWORD nTris) {
+        if(FAILED(hr)) {
+            // loss of exclusive mode is not a real DrawPrim problem, ignore it
+            HRESULT testcooplvl_hr = pDD->TestCooperativeLevel();
+            if((testcooplvl_hr != DDERR_NOEXCLUSIVEMODE)||(testcooplvl_hr != DDERR_EXCLUSIVEMODEALREADYSET)) {
+                dxgsg_cat.fatal() << DP_Type_Strs[dptype] << "() failed: result = " << ConvD3DErrorToString(hr) << endl;
+                exit(1);
+            }
+        }
+
+        CountDPs(nVerts,nTris);
+}
+#else
+#define TestDrawPrimFailure(a,b,c,nVerts,nTris) CountDPs(nVerts,nTris);
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::render_frame
+//       Access: Public, Virtual
+//  Description: Renders an entire frame, including all display
+//               regions within the frame, and includes any necessary
+//               pre- and post-processing like swapping buffers.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+render_frame() {
+  if (!_dx_ready) 
+    return;
+
+  _win->begin_frame();
+  _lighting_enabled_this_frame = false;
+
+#ifdef DO_PSTATS
+  // For Pstats to track our current texture memory usage, we have to
+  // reset the set of current textures each frame.
+  init_frame_pstats();
+
+  // But since we don't get sent a new issue_texture() unless our
+  // texture state has changed, we have to be sure to clear the
+  // current texture state now.  A bit unfortunate, but probably not
+  // measurably expensive.
+  NodeTransitions state;
+  state.set_transition(new TextureTransition);
+  modify_state(state);
+#endif
+
+  HRESULT hr = _d3dDevice->BeginScene();
+
+  if(FAILED(hr)) {
+    if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) {
+          if(dxgsg_cat.is_debug())
+              dxgsg_cat.debug() << "BeginScene returns " << ConvD3DErrorToString(hr) << endl;
+
+          CheckCooperativeLevel();
+    } else {
+        dxgsg_cat.error() << "BeginScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+    return;
+  }
+
+/* _d3dDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &matIdentity); */
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "begin frame --------------------------------------------" << endl;
+#endif
+
+    _decal_level = 0;
+
+    if (_clear_buffer_type != 0) {
+      // First, clear the entire window.
+      #ifndef NO_MULTIPLE_DISPLAY_REGIONS
+        PT(DisplayRegion) win_dr = _win->make_scratch_display_region(_win->get_width(), _win->get_height());
+        clear(get_render_buffer(_clear_buffer_type), win_dr);
+      #else
+        clear(get_render_buffer(_clear_buffer_type));     
+      #endif
+    }
+
+#if 0
+    int max_channel_index = _win->get_max_channel_index();
+    for (int c = 0; c < max_channel_index; c++) {
+        if (_win->is_channel_defined(c)) {
+#endif
+
+    // only 1 channel on dx currently
+    assert(_win->get_max_channel_index()==1);
+    assert(_win->is_channel_defined(0));
+
+            if(_panda_gfx_channel->is_active()) {
+                // Now render each of our layers in order.
+
+                int num_layers = _panda_gfx_channel->get_num_layers();
+                for (int l = 0; l < num_layers; l++) {
+                    GraphicsLayer *layer = _panda_gfx_channel->get_layer(l);
+                    if (layer->is_active()) {
+                        int num_drs = layer->get_num_drs();
+                        for (int d = 0; d < num_drs; d++) {
+                            DisplayRegion *dr = layer->get_dr(d);
+                            Camera *cam = dr->get_camera();
+
+                            // For each display region, render from the camera's view.
+                            if (dr->is_active() && cam != (Camera *)NULL &&
+                                cam->is_active() && cam->get_scene() != (Node *)NULL) {
+                                DisplayRegionStack old_dr = push_display_region(dr);
+                                prepare_display_region();
+                                render_scene(cam->get_scene(), cam);
+                                pop_display_region(old_dr);
+                            }
+                        }
+                    }       //      if (layer->is_active())
+                }
+            }       //      if (chan->is_active())
+#if 0
+        }
+    }   //  for (int c = 0; c < max_channel_index; c++)
+#endif
+
+    // draw new tri-based FPS meter
+
+  if(dx_show_fps_meter) {             
+        DO_PSTATS_STUFF(PStatTimer timer(_win->_show_fps_pcollector));
+        // compute and write new texture indices here
+    
+        char fps_msg[15];
+        sprintf(fps_msg, "%6.02f fps", _current_fps); // 6 == NUM_FPSMETER_LETTERS
+    
+        #define WRITE_FPS_UV(u,v) {*fltptr=(u); fltptr[1]=(v); fltptr= (float*)(((BYTE*)fltptr)+_fps_vertexsize);}
+        float u_FPSMETER_LETTER_WIDTH = _fps_u_usedwidth/(float)FPSMETER_NUMFONTLETTERS;
+    
+        // write out texcoords
+        float *fltptr = (float*)_fpsmeter_verts;
+        fltptr+=5;   // skip over 1st XYZ,RHW, and colr (5 DWORDs)
+    
+        for(DWORD c=0;c<NUM_FPSMETER_LETTERS;c++) {
+          char ch=fps_msg[c];
+          int charnum=ch-'0';
+          float uval1, uval2;
+          float vval2=_fps_v_usedheight;
+
+          if(ch=='.')
+             charnum=FPSMETER_NUMFONTLETTERS-1;       
+
+          uval1=u_FPSMETER_LETTER_WIDTH*charnum;
+          uval2=uval1+u_FPSMETER_LETTER_WIDTH;
+
+          if((ch!='.') && ((ch<'0') || (ch>'9'))) {
+            uval1=0.0f; uval2=0.0f; vval2=0.0f;
+          }
+
+          WRITE_FPS_UV(uval1,0.0f);
+          WRITE_FPS_UV(uval1,vval2);
+          WRITE_FPS_UV(uval2,vval2);
+    
+          WRITE_FPS_UV(uval2,vval2);
+          WRITE_FPS_UV(uval2,0.0f);
+          WRITE_FPS_UV(uval1,0.0f); 
+        }
+    
+        // is this blending fn expensive?  if so, can just overwrite everything
+    
+        // could a state-block be used here instead?  definitely to set up, but to restore?
+        #ifdef MAKE_FPSMETER_TRANSPARENT    
+           call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
+           if(!_blend_enabled)
+              _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, true);
+        #else
+           if(_blend_enabled)
+               _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, false);
+        #endif
+        
+        if (_CurShadeMode != D3DSHADE_FLAT) {
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT);
+        }
+
+        DWORD saved_zfunc;
+        _d3dDevice->GetRenderState(D3DRENDERSTATE_ZFUNC,&saved_zfunc);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC,D3DCMP_ALWAYS);
+
+        DWORD saved_fill_state;
+        if(_current_fill_mode != RenderModeProperty::M_filled) {
+            _d3dDevice->GetRenderState(D3DRENDERSTATE_FILLMODE, &saved_fill_state);
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID);
+        }
+
+    
+        DWORD saved_clipping_state,saved_cull_state;
+        _d3dDevice->GetRenderState(D3DRENDERSTATE_CULLMODE, &saved_cull_state);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
+    
+        _d3dDevice->GetRenderState(D3DRENDERSTATE_CLIPPING, &saved_clipping_state);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, false);
+        
+        // ignore lighting state since verts are post-xform
+    
+        DWORD saved_magfilter,saved_minfilter,saved_mipfilter;
+        DWORD saved_colorop,saved_alphaop,saved_colorarg1,saved_alphaarg1;
+        LPDIRECTDRAWSURFACE7 saved_tex_surf=NULL;
+
+        _d3dDevice->GetTextureStageState(0, D3DTSS_MAGFILTER, &saved_magfilter);
+        _d3dDevice->GetTextureStageState(0, D3DTSS_MINFILTER, &saved_minfilter);
+        _d3dDevice->GetTextureStageState(0, D3DTSS_MIPFILTER, &saved_mipfilter);
+        _d3dDevice->GetTextureStageState( 0, D3DTSS_COLOROP, &saved_colorop);
+        _d3dDevice->GetTextureStageState( 0, D3DTSS_COLORARG1, &saved_colorarg1);
+        _d3dDevice->GetTextureStageState( 0, D3DTSS_ALPHAOP, &saved_alphaop);
+        _d3dDevice->GetTextureStageState( 0, D3DTSS_ALPHAARG1, &saved_alphaarg1);
+
+        if(saved_mipfilter!= D3DTFP_NONE)
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE);
+
+        #define FPS_TEXFILTER(X) D3D##X##_POINT
+
+        if(saved_minfilter!=FPS_TEXFILTER(TFN))
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, FPS_TEXFILTER(TFN));
+        if(saved_magfilter!= FPS_TEXFILTER(TFG))
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, FPS_TEXFILTER(TFG));
+
+        if(saved_colorop!=D3DTOP_SELECTARG1)
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+        if(saved_colorarg1!=D3DTA_TEXTURE)
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+        if(saved_alphaop!=D3DTOP_SELECTARG1)
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,  D3DTOP_SELECTARG1);
+        if(saved_alphaarg1!=D3DTA_TEXTURE)
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+    
+        hr = _d3dDevice->SetTexture(0, _fpsmeter_font_surf);
+        if(FAILED(hr)) {
+           dxgsg_cat.error() << "SetTexture failed in draw fps meter, result = " << ConvD3DErrorToString(hr) << endl;
+           exit(1);
+        }
+    
+        DWORD nVerts = (NUM_FPSMETER_LETTERS+1)*2*3;   // +1 for suffix square
+        HRESULT hr = _d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, _fpsmeter_fvfflags, _fpsmeter_verts, nVerts, NULL);
+        TestDrawPrimFailure(DrawPrim,hr,_pDD,NUM_FPSMETER_LETTERS*2,0);
+    
+        #ifdef MAKE_FPSMETER_TRANSPARENT    
+          if(!_blend_enabled)
+              _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, false);
+        #else
+          if(_blend_enabled)
+              _d3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, true);
+        #endif
+        
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, saved_zfunc);
+    
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, saved_clipping_state);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, saved_cull_state);
+    
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, saved_magfilter);
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, saved_minfilter);
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, saved_mipfilter);
+        _d3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, saved_colorop);
+        _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, saved_colorarg1);
+        _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, saved_alphaop);
+        _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, saved_alphaarg1);
+
+        if (_CurShadeMode != D3DSHADE_FLAT) {
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, _CurShadeMode);
+        }
+
+        if(_current_fill_mode != RenderModeProperty::M_filled) {
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, saved_fill_state);            
+        }
+
+        _d3dDevice->SetTexture(0, ((_pCurTexContext != NULL) ? _pCurTexContext->_surface : NULL));
+  }
+
+  hr = _d3dDevice->EndScene();  
+
+  // any GDI operations MUST occur after EndScene
+
+  // Now we're done with the frame processing.  Clean up.
+  if (_lighting_enabled_this_frame) {
+        // Let's turn off all the lights we had on, and clear the light
+        // cache--to force the lights to be reissued next frame, in case
+        // their parameters or positions have changed between frames.
+
+        for (int i = 0; i < _max_lights; i++) {
+            enable_light(i, false);
+            _available_light_ids[i] = NULL;
+        }
+
+        // Also force the lighting state to unlit, so that issue_light()
+        // will be guaranteed to be called next frame even if we have the
+        // same set of light pointers we had this frame.
+        NodeTransitions state;
+        state.set_transition(new LightTransition);
+        modify_state(state);
+
+        // All this work to undo the lighting state each frame doesn't seem
+        // ideal--there may be a better way.  Maybe if the lights were just
+        // more aware of whether their parameters or positions have changed
+        // at all?
+   }
+
+  if(FAILED(hr)) {
+    if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) {
+          if(dxgsg_cat.is_debug())
+              dxgsg_cat.debug() << "EndScene returns " << ConvD3DErrorToString(hr) << endl;
+
+          CheckCooperativeLevel();
+    } else {
+       dxgsg_cat.error() << "EndScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl;
+       exit(1);
+    }
+    return;
+  }
+
+   if(dx_show_fps_meter) {
+         DO_PSTATS_STUFF(PStatTimer timer(_win->_show_fps_pcollector));
+
+         DWORD now = timeGetTime();  // this is win32 fn
+
+         float time_delta = (now - _start_time) * 0.001f;
+
+         if(time_delta > dx_fps_meter_update_interval) {
+             // didnt use global clock object, it wasnt working properly when I tried,
+             // its probably slower due to cache faults, and I can easily track all the
+             // info I need in dxgsg
+             DWORD num_frames = _cur_frame_count - _start_frame_count;
+
+             _current_fps = num_frames / time_delta;
+             _start_time = now;
+             _start_frame_count = _cur_frame_count;
+         }
+
+         _cur_frame_count++;  // only used by fps meter right now
+    }
+
+    _win->end_frame();
+
+    show_frame();
+
+#ifdef COUNT_DRAWPRIMS
+    {
+        #define FRAMES_PER_DPINFO 90
+        static DWORD LastDPInfoFrame=0;
+        static DWORD LastTickCount=0;
+
+        if (_cur_frame_count-LastDPInfoFrame > FRAMES_PER_DPINFO) {
+            DWORD CurTickCount=GetTickCount();
+            float delta_secs=(CurTickCount-LastTickCount)/1000.0f;
+
+            float numframes=_cur_frame_count-LastDPInfoFrame;
+            float verts_per_frame = cVertcount/numframes;
+            float tris_per_frame = cTricount/numframes;
+            float DPs_per_frame = cDPcount/numframes;
+            float DPs_notexchange_per_frame = cDP_noTexChangeCount/numframes;
+            float verts_per_DP = cVertcount/(float)cDPcount;
+            float verts_per_sec = cVertcount/delta_secs;
+            float tris_per_sec = cTricount/delta_secs;
+            float Geoms_per_frame = cGeomcount/numframes;
+            float DrawPrims_per_Geom = cDPcount/(float)cGeomcount;
+            float verts_per_Geom = cVertcount/(float)cGeomcount;
+
+            dxgsg_cat.debug() << "==================================="
+                << "\n Avg Verts/sec:\t\t" << verts_per_sec
+                << "\n Avg Tris/sec:\t\t" << tris_per_sec
+                << "\n Avg Verts/frame:\t" << verts_per_frame
+                << "\n Avg Tris/frame:\t" << tris_per_frame
+                << "\n Avg DrawPrims/frm:\t" << DPs_per_frame
+                << "\n Avg Verts/DrawPrim:\t" << verts_per_DP 
+                << "\n Avg DrawPrims w/no Texture Change from prev DrawPrim/frm:\t" << DPs_notexchange_per_frame
+                << "\n Avg Geoms/frm:\t" << Geoms_per_frame
+                << "\n Avg DrawPrims/Geom:\t" << DrawPrims_per_Geom
+                << "\n Avg Verts/Geom:\t" << verts_per_Geom
+                << endl;
+
+            LastDPInfoFrame=_cur_frame_count;
+            cDPcount = cVertcount=cTricount=cDP_noTexChangeCount=cGeomcount=0;
+            LastTickCount=CurTickCount;
+        }
+    }
+#endif
+
+#if defined(DO_PSTATS)||defined(PRINT_TEXSTATS)
+#ifndef PRINT_TEXSTATS
+  if (_texmgrmem_total_pcollector.is_active()) 
+#endif
+  {
+      #define TICKS_PER_GETTEXINFO (2.5*1000)   // 2.5 second interval
+      static DWORD LastTickCount=0;
+      DWORD CurTickCount=GetTickCount();
+
+      if (CurTickCount-LastTickCount > TICKS_PER_GETTEXINFO) {
+          LastTickCount=CurTickCount;
+          report_texmgr_stats();
+      }
+  }
+#endif
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "end frame ----------------------------------------------" << endl;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::report_texmgr_stats
+//       Access: Protected
+//  Description: Reports the DX texture manager's activity to PStats.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+report_texmgr_stats() {
+
+#if defined(DO_PSTATS)||defined(PRINT_TEXSTATS)
+
+  HRESULT hr;
+  DWORD dwTexTotal,dwTexFree,dwVidTotal,dwVidFree;
+
+#ifndef PRINT_TEXSTATS
+  if (_total_texmem_pcollector.is_active())
+#endif
+  {
+      DDSCAPS2 ddsCaps;
+    
+      ZeroMemory(&ddsCaps,sizeof(ddsCaps));
+    
+      ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
+      if(FAILED(  hr = _pDD->GetAvailableVidMem(&ddsCaps,&dwVidTotal,&dwVidFree))) {
+            dxgsg_cat.debug() << "report_texmgr GetAvailableVidMem for VIDMEM failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+      }
+    
+      ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+      if(FAILED(  hr = _pDD->GetAvailableVidMem(&ddsCaps,&dwTexTotal,&dwTexFree))) {
+            dxgsg_cat.debug() << "report_texmgr GetAvailableVidMem for TEXTURE failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+      }
+  }
+
+  D3DDEVINFO_TEXTUREMANAGER tminfo;
+  ZeroMemory(&tminfo,sizeof(D3DDEVINFO_TEXTUREMANAGER));
+
+  if(!bTexStatsRetrievalImpossible) {
+      hr = _d3dDevice->GetInfo(D3DDEVINFOID_TEXTUREMANAGER,&tminfo,sizeof(D3DDEVINFO_TEXTUREMANAGER));
+      if (hr!=D3D_OK) {
+          if (hr==S_FALSE) {
+              static int PrintedMsg=2;
+              if(PrintedMsg>0) {
+                  if(dxgsg_cat.is_debug())
+                    dxgsg_cat.debug() << " ************ texstats GetInfo() requires debug DX DLLs to be installed!!  ***********\n";
+                  ZeroMemory(&tminfo,sizeof(D3DDEVINFO_TEXTUREMANAGER));
+                  bTexStatsRetrievalImpossible=true;
+              }
+          } else {
+              dxgsg_cat.error() << "d3ddev->GetInfo(TEXTUREMANAGER) failed to get tex stats: result = " << ConvD3DErrorToString(hr) << endl;
+              return;
+          }
+      }
+  }
+
+#ifdef PRINT_TEXSTATS
+    char tmpstr1[50],tmpstr2[50],tmpstr3[50],tmpstr4[50];
+    sprintf(tmpstr1,"%.4g",dwVidTotal/1000000.0);
+    sprintf(tmpstr2,"%.4g",dwVidFree/1000000.0);
+    sprintf(tmpstr3,"%.4g",dwTexTotal/1000000.0);
+    sprintf(tmpstr4,"%.4g",dwTexFree/1000000.0);
+    dxgsg_cat.debug() << "\nAvailableVidMem for RenderSurfs: (megs) total: " << tmpstr1 << "  free: " << tmpstr2
+                      << "\nAvailableVidMem for Textures:    (megs) total: " << tmpstr3 << "  free: " << tmpstr4 << endl;
+
+   if(!bTexStatsRetrievalImpossible) {
+            dxgsg_cat.spam()
+                << "\n bThrashing:\t" << tminfo.bThrashing
+                << "\n NumEvicts:\t" << tminfo.dwNumEvicts
+                << "\n NumVidCreates:\t" << tminfo.dwNumVidCreates
+                << "\n NumTexturesUsed:\t" << tminfo.dwNumTexturesUsed
+                << "\n NumUsedTexInVid:\t" << tminfo.dwNumUsedTexInVid
+                << "\n WorkingSet:\t" << tminfo.dwWorkingSet
+                << "\n WorkingSetBytes:\t" << tminfo.dwWorkingSetBytes
+                << "\n TotalManaged:\t" << tminfo.dwTotalManaged
+                << "\n TotalBytes:\t" << tminfo.dwTotalBytes
+                << "\n LastPri:\t" << tminfo.dwLastPri << endl;
+
+            D3DDEVINFO_TEXTURING texappinfo;
+            ZeroMemory(&texappinfo,sizeof(D3DDEVINFO_TEXTURING));
+            hr = _d3dDevice->GetInfo(D3DDEVINFOID_TEXTURING,&texappinfo,sizeof(D3DDEVINFO_TEXTURING));
+            if (hr!=D3D_OK) {
+                dxgsg_cat.error() << "GetInfo(TEXTURING) failed : result = " << ConvD3DErrorToString(hr) << endl;
+                return;
+            } else {
+                dxgsg_cat.spam()
+                << "\n NumTexLoads:\t" << texappinfo.dwNumLoads
+                << "\n ApproxBytesLoaded:\t" << texappinfo.dwApproxBytesLoaded
+                << "\n NumPreLoads:\t" << texappinfo.dwNumPreLoads
+                << "\n NumSet:\t" << texappinfo.dwNumSet
+                << "\n NumCreates:\t" << texappinfo.dwNumCreates
+                << "\n NumDestroys:\t" << texappinfo.dwNumDestroys
+                << "\n NumSetPriorities:\t" << texappinfo.dwNumSetPriorities
+                << "\n NumSetLODs:\t" << texappinfo.dwNumSetLODs
+                << "\n NumLocks:\t" << texappinfo.dwNumLocks
+                << "\n NumGetDCs:\t" << texappinfo.dwNumGetDCs << endl;
+            }
+    }
+#endif
+
+#ifdef DO_PSTATS
+  // Tell PStats about the state of the texture memory.
+
+  if (_texmgrmem_total_pcollector.is_active()) {
+      // report zero if no debug dlls, to signal this info is invalid
+      _texmgrmem_total_pcollector.set_level(tminfo.dwTotalBytes);
+      _texmgrmem_resident_pcollector.set_level(tminfo.dwWorkingSetBytes);
+  }
+    
+  if (_total_texmem_pcollector.is_active()) {
+    _total_texmem_pcollector.set_level(dwTexTotal);
+    _used_texmem_pcollector.set_level(dwTexTotal - dwTexFree);
+  }
+#endif
+
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::render_scene
+//       Access: Public, Virtual
+//  Description: Renders an entire scene, from the root node of the
+//               scene graph, as seen from a particular ProjectionNode
+//               and with a given initial state.  This initial state
+//               may be modified during rendering.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+render_scene(Node *root, ProjectionNode *projnode) {
+#ifdef GSG_VERBOSE
+    _pass_number = 0;
+    dxgsg_cat.debug()
+    << "begin scene - - - - - - - - - - - - - - - - - - - - - - - - -"
+    << endl;
+#endif
+    _current_root_node = root;
+
+    render_subgraph(_render_traverser, root, projnode,
+                    AllTransitionsWrapper());
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "done scene  - - - - - - - - - - - - - - - - - - - - - - - - -"
+    << endl;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::render_subgraph
+//       Access: Public, Virtual
+//  Description: Renders a subgraph of the scene graph as seen from a
+//               given projection node, and with a particular initial
+//               state.  This state may be modified by the render
+//               process.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+render_subgraph(RenderTraverser *traverser,
+                Node *subgraph, ProjectionNode *projnode,
+                const AllTransitionsWrapper &net_trans) {
+    ProjectionNode *old_projection_node = _current_projection_node;
+    _current_projection_node = projnode;
+    LMatrix4f old_projection_mat = _current_projection_mat;
+
+    // d3d is left-handed coord system
+    LMatrix4f projection_mat = projnode->get_projection()->get_projection_mat(CS_yup_left);
+
+#if 0
+    dxgsg_cat.spam() << "cur projection matrix: " << projection_mat <<"\n";
+#endif
+
+    _current_projection_mat = projection_mat;
+    _projection_mat_stack_count++;
+
+#ifdef _DEBUG
+   {
+      static bool bPrintedMsg=false;
+
+      if((!bPrintedMsg) && !IS_NEARLY_EQUAL(projection_mat(2,3),1.0f)) {
+         bPrintedMsg=true;
+         dxgsg_cat.debug() << "non w-compliant render_subgraph projection matrix [2][3] should be 1.0, instead is: " << projection_mat(2,3) << endl;
+         dxgsg_cat.debug() << "cur projection matrix: " << projection_mat << endl;
+      }
+
+      // note: a projection matrix that does not have a [3][4] value of 1.0f is
+      //       not w-compliant and could cause problems with fog
+
+    }
+#endif
+
+    HRESULT hr;
+
+    // We load the projection matrix directly.
+    hr = _d3dDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION,
+                                 (LPD3DMATRIX) _current_projection_mat.get_data());
+
+    // We infer the modelview matrix by doing a wrt on the projection
+    // node.
+    LMatrix4f modelview_mat;
+    get_rel_mat(subgraph, _current_projection_node, modelview_mat);  //needs reversal from glgsg, probably due D3D LH coordsys
+//  get_rel_mat(_current_projection_node, subgraph, modelview_mat);
+
+    if (_coordinate_system != CS_yup_left) {
+        // Now we build the coordinate system conversion into the
+        // modelview matrix (as described in the paragraph above).
+        modelview_mat = modelview_mat *
+                        LMatrix4f::convert_mat(_coordinate_system, CS_yup_left);
+    }
+
+    // The modelview matrix will be loaded as each geometry is
+    // encountered.  So we set the supplied modelview matrix as an
+    // initial value instead of loading it now.
+    AllTransitionsWrapper sub_trans = net_trans;
+    sub_trans.set_transition(new TransformTransition(modelview_mat));
+
+    render_subgraph(traverser, subgraph, sub_trans);
+
+    _current_projection_node = old_projection_node;
+    _current_projection_mat = old_projection_mat;
+    _projection_mat_stack_count--;
+
+
+    // We must now restore the projection matrix from before.  We could
+    // do a push/pop matrix if we were using D3DX
+    if (_projection_mat_stack_count > 0)
+        hr =_d3dDevice->SetTransform(D3DTRANSFORMSTATE_PROJECTION,
+                                    (LPD3DMATRIX) _current_projection_mat.get_data());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::render_subgraph
+//       Access: Public, Virtual
+//  Description: Renders a subgraph of the scene graph as seen from the
+//               current projection node, and with a particular
+//               initial state.  This state may be modified during the
+//               render process.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+render_subgraph(RenderTraverser *traverser, Node *subgraph,
+                const AllTransitionsWrapper &net_trans) {
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "begin subgraph (pass " << ++_pass_number
+    << ") - - - - - - - - - - - - - - - - - - - - - - - - -" << endl;
+#endif
+
+    nassertv(traverser != (RenderTraverser *)NULL);
+    traverser->traverse(subgraph, net_trans);
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "end subgraph (pass " << _pass_number
+    << ") - - - - - - - - - - - - - - - - - - - - - - - - -"
+    << endl;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::add_to_FVFBuf
+//       Access: Private
+//  Description: This adds data to the flexible vertex format
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+add_to_FVFBuf(void *data,  size_t bytes) {
+    memcpy(_pCurFvfBufPtr, data, bytes);
+    _pCurFvfBufPtr += bytes;
+}
+
+// generates slightly fewer instrs
+#define add_DWORD_to_FVFBuf(data) { *((DWORD *)_pCurFvfBufPtr) = (DWORD) data;  _pCurFvfBufPtr += sizeof(DWORD);}
+
+typedef enum {
+    FlatVerts,IndexedVerts,MixedFmtVerts
+} GeomVertFormat;
+
+#define COPYVERTDATA_2_VERTEXBUFFER(PrimType,NumVertices) {                                     \
+            DWORD numVertBytes=_pCurFvfBufPtr-_pFvfBufBasePtr;                                  \
+            memcpy(_pCurrentGeomContext->_pEndofVertData,_pFvfBufBasePtr,numVertBytes);         \
+            DPInfo dpInfo;                                                                      \
+            dpInfo.nVerts=NumVertices;                                                          \
+            dpInfo.primtype=PrimType;                                                           \
+            _pCurrentGeomContext->_PrimInfo.push_back(dpInfo);                                  \
+            _pCurrentGeomContext->_num_verts+=dpInfo.nVerts;                                    \
+            _pCurrentGeomContext->_pEndofVertData+=numVertBytes; }
+            
+
+INLINE void DXGraphicsStateGuardian::
+transform_color(Colorf &InColor,D3DCOLOR &OutRGBAColor) {
+  // To be truly general, we really need a 5x5 matrix to transform a
+  // 4-component color.  Rather than messing with that, we instead
+  // treat the color as a 3-component RGB, which can be transformed by
+  // the ordinary 4x4 matrix, and a separate alpha value, which can be
+  // scaled and offsetted.
+
+    LPoint4f temp_pnt(InColor[0], InColor[1], InColor[2], 1.0f);
+    Colorf out_color = temp_pnt * _current_color_mat;  // maybe expand this out for efficiency
+    out_color[3] = (InColor[3] * _current_alpha_scale) + _current_alpha_offset;
+    OutRGBAColor = Colorf_to_D3DCOLOR(out_color);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::wants_colors
+//       Access: Public, Virtual
+//  Description: Returns true if the GSG should issue geometry color
+//               commands, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool DXGraphicsStateGuardian::
+wants_colors() const {
+    // If we have scene graph color enabled, return false to indicate we
+    // shouldn't bother issuing geometry color commands.
+
+    const ColorTransition *catt;
+    if (!get_attribute_into(catt, this)) {
+        // No scene graph color at all.
+        return true;
+    }
+
+    // We should issue geometry colors only if the scene graph color is off.
+    if (catt->is_off() || (!catt->is_real()))
+        return true;
+
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_prim_setup
+//       Access: Private
+//  Description: This adds data to the flexible vertex format
+////////////////////////////////////////////////////////////////////
+size_t DXGraphicsStateGuardian::
+draw_prim_setup(const Geom *geom) {
+    //  Set the flags for the flexible vertex format and compute the bytes
+    //  required to store a single vertex.
+
+    #ifdef _DEBUG
+      assert(geom->get_binding(G_COORD) != G_OFF);
+    #endif
+
+#define GET_NEXT_VERTEX(NEXTVERT) { NEXTVERT = geom->get_next_vertex(vi); }
+#define GET_NEXT_NORMAL() { p_normal = geom->get_next_normal(ni); }
+#define GET_NEXT_TEXCOORD() { p_texcoord = geom->get_next_texcoord(ti); }
+#define GET_NEXT_COLOR() {                                                           \
+    Colorf tempcolor = geom->get_next_color(ci);                                     \
+    if(!_color_transform_required) {                                                 \
+        _curD3Dcolor = Colorf_to_D3DCOLOR(tempcolor);                                \
+    } else {                                                                         \
+        transform_color(tempcolor,_curD3Dcolor);                                     \
+    }}
+
+////////
+    
+   // this stuff should eventually replace the iterators below
+   PTA_Vertexf coords;
+   PTA_ushort vindexes;
+
+   geom->get_coords(coords,vindexes);
+   if(vindexes!=NULL) {
+      _pCurCoordIndex = _coordindex_array = &vindexes[0];
+   } else {
+      _pCurCoordIndex = _coordindex_array = NULL;
+   }
+   _pCurCoord = _coord_array = &coords[0];
+
+   ///////////////
+
+   vi = geom->make_vertex_iterator();
+   _curFVFflags = D3DFVF_XYZ;
+   size_t vertex_size = sizeof(D3DVALUE) * 3;
+
+   GeomBindType ColorBinding=geom->get_binding(G_COLOR);
+   bool bDoColor=(ColorBinding != G_OFF);
+
+   if (_enable_all_color && (bDoColor || _issued_color_enabled)) {
+        ci = geom->make_color_iterator();
+        _curFVFflags |= D3DFVF_DIFFUSE;
+        vertex_size += sizeof(D3DCOLOR);
+
+        if (_issued_color_enabled) {
+            _curD3Dcolor = _issued_color_D3DCOLOR;  // set primitive color if there is one.
+
+            _perVertex &= ~PER_COLOR;
+            _perPrim &= ~PER_COLOR;
+            _perComp &= ~PER_COLOR;
+         } else if(ColorBinding == G_OVERALL){
+            GET_NEXT_COLOR();
+
+            _perVertex &= ~PER_COLOR;
+            _perPrim &= ~PER_COLOR;
+            _perComp &= ~PER_COLOR;
+        } 
+   }
+
+   if (geom->get_binding(G_NORMAL) != G_OFF) {
+        ni = geom->make_normal_iterator();
+        _curFVFflags |= D3DFVF_NORMAL;
+        vertex_size += sizeof(D3DVALUE) * 3;
+
+        if (geom->get_binding(G_NORMAL) == G_OVERALL)
+            p_normal = geom->get_next_normal(ni);    // set overall normal if there is one
+   }
+
+
+   GeomBindType TexCoordBinding;
+   PTA_TexCoordf texcoords;
+   PTA_ushort tindexes;
+   geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+   if (TexCoordBinding != G_OFF) {
+       // used by faster path
+       if(tindexes!=NULL) {
+          _pCurTexCoordIndex = _texcoordindex_array = &tindexes[0];
+       } else {
+          _pCurTexCoordIndex = _texcoordindex_array = NULL;
+       }
+       _pCurTexCoord = _texcoord_array = &texcoords[0];
+       //////
+
+       ti = geom->make_texcoord_iterator();
+       _curFVFflags |= (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+       vertex_size += sizeof(float) * 2;
+   }
+
+    // If we have per-vertex colors or normals, we need smooth shading.
+    // Otherwise we want flat shading for performance reasons.
+
+   // Note on fogging:
+   // the fogging expression should really be || (_fog_enabled && (_doFogType==PerVertexFog))
+   // instead of just || (_fog_enabled), since GOURAUD shading should not be required for PerPixel 
+   // fog, but the problem is some cards (Riva128,Matrox G200) emulate pixel fog with table fog
+   // but dont force the shading mode to gouraud internally, so you end up with flat-shaded fog colors
+   // (note, TNT does the right thing tho).  So I guess we must do gouraud shading for all fog rendering for now
+   // note that if _doFogType==None, _fog_enabled will always be false
+
+   D3DSHADEMODE needed_shademode = 
+       (((_perVertex & (PER_COLOR | (wants_normals() ? PER_NORMAL : 0))) || _fog_enabled) ?
+        D3DSHADE_GOURAUD : D3DSHADE_FLAT);
+
+   set_shademode(needed_shademode);
+
+   return vertex_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_prim_inner_loop
+//       Access: Private
+//  Description: This adds data to the flexible vertex format with a check
+//               for component normals and color
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_prim_inner_loop(int nVerts, const Geom *geom, ushort perFlags) {
+    Vertexf NextVert;
+
+    for(;nVerts > 0;nVerts--) {
+         // coord info will always be _perVertex
+        GET_NEXT_VERTEX(NextVert);     // need to optimize these 
+        add_to_FVFBuf((void *)&NextVert, sizeof(D3DVECTOR));
+
+        if(perFlags==(ushort)TexCoordOnly) {
+            // break out the common case (for animated chars) 1st
+            GET_NEXT_TEXCOORD();
+        } else {
+            switch (DrawLoopFlags(perFlags)) {
+                case Color_TexCoord:
+                    GET_NEXT_TEXCOORD();
+                case ColorOnly:
+                    GET_NEXT_COLOR();
+                    break;
+                case Normal_Color:
+                    GET_NEXT_COLOR();
+                case NormalOnly:
+                    GET_NEXT_NORMAL();
+                    break;
+                case Normal_Color_TexCoord:
+                    GET_NEXT_COLOR();
+                case Normal_TexCoord:
+                    GET_NEXT_NORMAL();
+                // case TexCoordOnly:
+                    GET_NEXT_TEXCOORD();
+                    break;
+            }
+        }
+
+        if (_curFVFflags & D3DFVF_NORMAL)
+            add_to_FVFBuf((void *)&p_normal, sizeof(D3DVECTOR));
+        if (_curFVFflags & D3DFVF_DIFFUSE)
+            add_DWORD_to_FVFBuf(_curD3Dcolor);
+        if (_curFVFflags & D3DFVF_TEXCOUNT_MASK)
+            add_to_FVFBuf((void *)&p_texcoord, sizeof(TexCoordf));
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_prim_inner_loop_coordtexonly
+//       Access: Private
+//  Description: FastPath loop used by animated character data
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_prim_inner_loop_coordtexonly(int nVerts, const Geom *geom) {
+    // assumes coord and texcoord data is per-vertex, color is not per-vert, and no normal data
+    // this should be common situation for animated character data
+    // inc'ing local ptrs instead of member ones, seems to optimize better
+    // bypass all the slow vertex iterator stuff
+
+    Vertexf *pCurCoord = _pCurCoord;
+    ushort *pCurCoordIndex = _pCurCoordIndex;
+    TexCoordf *pCurTexCoord = _pCurTexCoord;
+    ushort *pCurTexCoordIndex = _pCurTexCoordIndex;
+
+    char *LocalFvfBufPtr = _pCurFvfBufPtr;
+    DWORD cur_color = _curD3Dcolor;
+    bool bDoIndexedTexCoords = (_texcoordindex_array != NULL);
+    bool bDoIndexedCoords = (_coordindex_array != NULL);
+
+    for(;nVerts>0;nVerts--) {
+        if(bDoIndexedCoords) {
+           memcpy(LocalFvfBufPtr,(void*)&_coord_array[*pCurCoordIndex],sizeof(D3DVECTOR));
+           pCurCoordIndex++;           
+        } else {
+           memcpy(LocalFvfBufPtr,(void*)pCurCoord,sizeof(D3DVECTOR));
+           pCurCoord++;
+        }
+
+        LocalFvfBufPtr+=sizeof(D3DVECTOR);
+
+        *((DWORD *)LocalFvfBufPtr) = cur_color;
+        LocalFvfBufPtr += sizeof(DWORD);
+
+        if(bDoIndexedTexCoords) {
+           memcpy(LocalFvfBufPtr,(void*)&_texcoord_array[*pCurTexCoordIndex],sizeof(TexCoordf));
+           pCurTexCoordIndex++;
+        } else {
+           memcpy(LocalFvfBufPtr,(void*)pCurTexCoord,sizeof(TexCoordf));
+           pCurTexCoord++;
+        }
+        LocalFvfBufPtr+=sizeof(TexCoordf);
+    }
+
+    _pCurFvfBufPtr=LocalFvfBufPtr;
+    _pCurCoord = pCurCoord;
+    _pCurCoordIndex = pCurCoordIndex;
+    _pCurTexCoord = pCurTexCoord;
+    _pCurTexCoordIndex = pCurTexCoordIndex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_point
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_point(GeomPoint *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_point()" << endl;
+#endif
+  
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+  
+    // The DX Way
+
+    int nPrims = geom->get_num_prims();
+
+    if (nPrims==0) {
+        dxgsg_cat.warning() << "draw_point() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+#ifdef _DEBUG
+    static BOOL bPrintedMsg=FALSE;
+
+    if (!bPrintedMsg && (geom->get_size()!=1.0f)) {
+        bPrintedMsg=TRUE;
+        dxgsg_cat.warning() << "D3D does not support drawing points of non-unit size, setting point size to 1.0f!\n";
+    }
+#endif
+
+    nassertv(nPrims < D3DMAXNUMVERTICES );
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType bind;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,bind,nindexes);
+    geom->get_colors(colors,bind,cindexes);
+    geom->get_texcoords(texcoords,bind,tindexes);
+
+    GeomVertFormat GeomVrtFmt=FlatVerts;
+
+    // first determine if we're indexed or non-indexed
+
+    if ((vindexes!=NULL)&&(cindexes!=NULL)&&(tindexes!=NULL)&&(nindexes!=NULL)) {
+        GeomVrtFmt=IndexedVerts;
+    } else if (!((vindexes==NULL)&&(cindexes==NULL)&&(tindexes==NULL)&&(nindexes==NULL)))
+        GeomVrtFmt=MixedFmtVerts;
+
+#ifdef DONT_USE_DRAWPRIMSTRIDED
+    GeomVrtFmt=MixedFmtVerts;
+#else
+    if(_bDrawPrimDoSetupVertexBuffer) {
+      GeomVrtFmt=MixedFmtVerts;
+    }
+#endif
+
+    // for Indexed Prims and mixed indexed/non-indexed prims, we will use old pipeline for now
+    // need to add code to handle fully indexed mode (and handle cases with index arrays of different lengths,
+    // values (may only be possible to handle certain cases without reverting to old pipeline)
+    if (GeomVrtFmt!=FlatVerts) {
+
+        _perVertex = 0x0; 
+        _perPrim = 0;
+        if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) _perVertex |= PER_NORMAL;
+        if (geom->get_binding(G_COLOR) == G_PER_VERTEX) _perVertex |= PER_COLOR;
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+        nassertv(nPrims * vertex_size < VERT_BUFFER_SIZE);
+        _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+        // iterate through the point
+        draw_prim_inner_loop(nPrims, geom, _perVertex | _perPrim);
+
+        if(!_bDrawPrimDoSetupVertexBuffer) {
+           HRESULT hr = _d3dDevice->DrawPrimitive(D3DPT_POINTLIST, _curFVFflags, _pFvfBufBasePtr, nPrims, NULL);   
+           TestDrawPrimFailure(DrawPrim,hr,_pDD,nPrims,0);
+        } else {
+            COPYVERTDATA_2_VERTEXBUFFER(D3DPT_POINTLIST,nPrims);
+        }
+    } else {  // setup for strided
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        // new code only handles non-indexed pointlists (is this enough?)
+        nassertv((vindexes==NULL)&&(cindexes==NULL)&&(tindexes==NULL)&&(nindexes==NULL));
+
+        D3DDRAWPRIMITIVESTRIDEDDATA dps_data;
+        memset(&dps_data,0,sizeof(D3DDRAWPRIMITIVESTRIDEDDATA));
+
+        dps_data.position.lpvData = (VOID*)coords;
+        dps_data.position.dwStride = sizeof(D3DVECTOR);
+
+        if (_curFVFflags & D3DFVF_NORMAL) {
+            dps_data.normal.lpvData = (VOID*)norms;
+            dps_data.normal.dwStride = sizeof(D3DVECTOR);
+        }
+
+        if (_curFVFflags & D3DFVF_DIFFUSE) {
+            _pCurFvfBufPtr=_pFvfBufBasePtr;
+
+            dps_data.diffuse.lpvData = (VOID*)_pFvfBufBasePtr;
+            dps_data.diffuse.dwStride = sizeof(D3DCOLOR);
+
+            // Geom nodes store floats for colors, drawprim requires ARGB dwords
+            // BUGBUG: eventually this hack every-frame all-colors conversion needs
+            // to be done only once as part of a vertex buffer
+
+            if(_color_transform_required) {
+                for (int i=0;i<nPrims;i++) {
+                    D3DCOLOR RGBA_color;
+                    transform_color(colors[i],RGBA_color);
+                    add_DWORD_to_FVFBuf(RGBA_color);
+                }
+            } else
+             for (int i=0;i<nPrims;i++) {
+                Colorf out_color=colors[i];
+                add_DWORD_to_FVFBuf(Colorf_to_D3DCOLOR(out_color));
+             }
+        }
+
+        if (_curFVFflags & D3DFVF_TEXCOUNT_MASK) {
+            dps_data.textureCoords[0].lpvData = (VOID*)texcoords;
+            dps_data.textureCoords[0].dwStride = sizeof(TexCoordf);
+        }
+
+        HRESULT hr = _d3dDevice->DrawPrimitiveStrided(D3DPT_POINTLIST, _curFVFflags, &dps_data, nPrims, NULL);
+        TestDrawPrimFailure(DrawPrimStrided,hr,_pDD,nPrims,0);
+    }
+
+    _pCurFvfBufPtr = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_line
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_line(GeomLine* geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_line()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+#ifdef _DEBUG
+    static BOOL bPrintedMsg=FALSE;
+
+    // note: need to implement approximation of non-1.0 width lines with quads
+
+    if (!bPrintedMsg && (geom->get_width()!=1.0f)) {
+        bPrintedMsg=TRUE;
+        dxgsg_cat.warning() << "DX does not support drawing lines with a non-1.0f pixel width, setting width to 1.0f!\n";
+    }
+#endif
+
+    int nPrims = geom->get_num_prims();
+
+    if (nPrims==0) {
+        dxgsg_cat.warning() << "draw_line() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    assert(geom->get_binding(G_COORD) == G_PER_VERTEX);
+    _perVertex = 0x0;
+    _perPrim = _perComp = 0x0;
+
+    switch(geom->get_binding(G_NORMAL)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_NORMAL;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |=  PER_NORMAL;
+            break;
+        default:
+            _perPrim |=  PER_NORMAL;
+    }
+
+    switch(geom->get_binding(G_COLOR)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_COLOR;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |= PER_COLOR;
+            break;
+        default:
+            _perPrim |= PER_COLOR;
+    }
+
+    size_t vertex_size = draw_prim_setup(geom);
+
+    char *_tmp_fvf = NULL;
+    nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+//  nassertv(nPrims * 2 * vertex_size < VERT_BUFFER_SIZE);
+
+    if (nPrims * 2 * vertex_size > VERT_BUFFER_SIZE) {
+        // bugbug: need cleaner way to handle tmp buffer size overruns (malloc/realloc?)
+        _pCurFvfBufPtr = _tmp_fvf = new char[nPrims * 2 * vertex_size];
+    } else  _pCurFvfBufPtr = _pFvfBufBasePtr;            // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+    for (int i = 0; i < nPrims; i++) {
+        if (_perPrim & PER_COLOR) {
+            GET_NEXT_COLOR();
+        }
+        if (_perPrim & PER_NORMAL)
+            p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+        draw_prim_inner_loop(2, geom, _perVertex);
+    }
+
+    HRESULT hr;
+
+    DWORD vertCount = nPrims<<1;
+
+    if(!_bDrawPrimDoSetupVertexBuffer) {
+        if (_tmp_fvf == NULL)
+            hr = _d3dDevice->DrawPrimitive(D3DPT_LINELIST, _curFVFflags, _pFvfBufBasePtr, vertCount, NULL);
+        else {
+            hr = _d3dDevice->DrawPrimitive(D3DPT_LINELIST, _curFVFflags, _tmp_fvf, vertCount, NULL);
+            delete [] _tmp_fvf;
+        }
+        TestDrawPrimFailure(DrawPrim,hr,_pDD,vertCount,0);
+    } else {
+        COPYVERTDATA_2_VERTEXBUFFER(D3DPT_LINELIST,vertCount);
+    }
+
+    _pCurFvfBufPtr = NULL;
+}
+
+void DXGraphicsStateGuardian::
+draw_linestrip(GeomLinestrip* geom, GeomContext *gc) {
+
+#ifdef _DEBUG
+    static BOOL bPrintedMsg=FALSE;
+
+    if (!bPrintedMsg && (geom->get_width()!=1.0f)) {
+        bPrintedMsg=TRUE;
+        dxgsg_cat.warning() << "DX does not support drawing lines with a non-1.0f pixel width, setting width to 1.0f!\n";
+    }
+#endif
+
+  draw_linestrip_base(geom,gc,false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_linestrip
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_linestrip_base(Geom* geom, GeomContext *gc, bool bConnectEnds) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_linestrip()" << endl;
+#endif
+
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+    int nPrims = geom->get_num_prims();
+    const int *plen = geom->get_lengths();
+
+    if (nPrims==0) {
+        dxgsg_cat.warning() << "draw_linestrip() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    assert(geom->get_binding(G_COORD) == G_PER_VERTEX);
+    _perVertex = 0x0;
+
+    _perPrim = _perComp = 0;
+    switch(geom->get_binding(G_NORMAL)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_NORMAL;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |=  PER_NORMAL;
+            break;
+        default:
+            _perPrim |= PER_NORMAL;
+    }
+
+    switch(geom->get_binding(G_COLOR)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_COLOR;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |= PER_COLOR;
+            break;
+        default:
+            _perPrim |= PER_COLOR;
+    }
+
+    size_t vertex_size = draw_prim_setup(geom);
+    ushort perFlags = _perVertex | _perComp;
+
+    for (int i = 0; i < nPrims; i++) {
+        if (_perPrim & PER_COLOR) {
+            GET_NEXT_COLOR();
+        }
+
+        int nVerts;
+
+        if(plen!=NULL) {
+            nVerts= *(plen++);
+            nassertv(nVerts >= 2);
+        } else {
+            nVerts=4;  // we've been called by draw_quad, which has no lengths array
+        }
+
+        nassertv(_pCurFvfBufPtr == NULL);   // make sure the storage pointer is clean.
+        nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE);
+        _pCurFvfBufPtr = _pFvfBufBasePtr;   // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+        draw_prim_inner_loop(nVerts, geom, perFlags);
+
+        if(bConnectEnds) {
+             // append first vertex to end
+             memcpy(_pCurFvfBufPtr,_pFvfBufBasePtr,vertex_size);
+             _pCurFvfBufPtr+=vertex_size;
+             nVerts++;
+        }
+
+        if(!_bDrawPrimDoSetupVertexBuffer) {
+            HRESULT hr = _d3dDevice->DrawPrimitive(D3DPT_LINESTRIP, _curFVFflags, _pFvfBufBasePtr, nVerts, NULL);
+            TestDrawPrimFailure(DrawPrim,hr,_pDD,nVerts,0);
+        } else {
+            COPYVERTDATA_2_VERTEXBUFFER(D3DPT_LINESTRIP,nVerts);
+        }           
+
+        _pCurFvfBufPtr = NULL;
+    }
+}
+
+// this class exists because an alpha sort is necessary for correct
+// sprite rendering, and we can't simply sort the vertex arrays as
+// each vertex may or may not have corresponding information in the
+// x/y texel-world-ratio and rotation arrays.
+typedef struct {
+    Vertexf _v;
+    D3DCOLOR _c;
+    float _x_ratio;
+    float _y_ratio;
+    float _theta;
+} WrappedSprite;
+
+class WrappedSpriteSortPtr {
+public:
+    float z;
+    WrappedSprite *pSpr;
+};
+
+// this struct exists because the STL can sort faster than i can.
+struct draw_sprite_vertex_less {
+    INLINE bool operator ()(const WrappedSpriteSortPtr& v0,
+                            const WrappedSpriteSortPtr& v1) const {
+        return v0.z > v1.z; // reversed from gl due to left-handed coordsys of d3d
+    }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_sprite
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_sprite(GeomSprite *geom, GeomContext *gc) {
+
+    // this is a little bit of a mess, but it's ok.  Here's the deal:
+    // we want to draw, and draw quickly, an arbitrarily large number
+    // of sprites all facing the screen.  Performing the billboard math
+    // for ~1000 sprites is way too slow.  Ideally, we want one
+    // matrix transformation that will handle everything, and this is
+    // just about what ends up happening. We're getting the front-facing
+    // effect by setting up a new frustum (of the same z-depth as the
+    // current one) that is very small in x and y.  This way regularly
+    // rendered triangles that might not be EXACTLY facing the camera
+    // will certainly look close enough.  Then, we transform to camera-space
+    // by hand and apply the inverse frustum to the transformed point.
+    // For some cracked out reason, this actually works.
+
+
+    // Note: for DX8, try to use the PointSprite primitive instead of doing all the stuff below
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_sprite()" << endl;
+#endif
+    // get the array traversal set up.
+    int nprims = geom->get_num_prims();
+
+    if (nprims==0) {
+        return;
+    }
+
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(nprims));
+
+    D3DMATRIX OldD3DWorldMatrix;
+    _d3dDevice->GetTransform(D3DTRANSFORMSTATE_WORLD, &OldD3DWorldMatrix);
+
+    bool bReEnableDither=false;
+
+    _d3dDevice->GetTransform(D3DTRANSFORMSTATE_WORLD, &OldD3DWorldMatrix);
+
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    Geom::ColorIterator ci = geom->make_color_iterator();
+
+    // note although sprite particles technically dont require a texture,
+    // the texture dimensions are used to initialize the size calculations
+    // the code in spriteParticleRenderer.cxx does not handle the no-texture case now
+
+    float tex_xsize = 1.0f;
+    float tex_ysize = 1.0f;
+
+    Texture *tex = geom->get_texture();
+    if(tex !=NULL) {
+
+        // set up the texture-rendering state
+        NodeTransitions state;
+
+        // this sets up texturing.  Could just set the renderstates directly, but this is a little cleaner
+        TextureTransition *ta = new TextureTransition(tex);
+        state.set_transition(ta);
+
+        TextureApplyTransition *taa = new TextureApplyTransition(TextureApplyProperty::M_modulate);
+        state.set_transition(taa);
+
+        modify_state(state);
+
+        tex_xsize = tex->_pbuffer->get_xsize();
+        tex_ysize = tex->_pbuffer->get_ysize();
+    }
+
+    // save the modelview matrix
+    LMatrix4f modelview_mat;
+
+    const TransformTransition *ctatt;
+    if (!get_attribute_into(ctatt, this))
+        modelview_mat = LMatrix4f::ident_mat();
+    else
+        modelview_mat = ctatt->get_matrix();
+
+    // get the camera information
+    float aspect_ratio;
+    aspect_ratio = _actual_display_region->get_camera()->get_aspect();
+
+    // null the world xform, so sprites are orthog to scrn  (but not necessarily camera pnt unless they lie along z-axis)
+    _d3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matIdentity);
+    // only need to change _WORLD xform, _VIEW xform is Identity
+
+    // precomputation stuff
+    float tex_left = geom->get_ll_uv()[0];
+    float tex_right = geom->get_ur_uv()[0];
+    float tex_bottom = geom->get_ll_uv()[1];
+    float tex_top = geom->get_ur_uv()[1];
+    
+    float half_width =  0.5f * tex_xsize * fabs(tex_right - tex_left);
+    float half_height = 0.5f * tex_ysize * fabs(tex_top - tex_bottom);
+    float scaled_width, scaled_height;
+
+    // the user can override alpha sorting if they want
+    bool alpha = false;
+
+    if (!geom->get_alpha_disable()) {
+        // figure out if alpha's enabled (if not, no reason to sort)
+        const TransparencyTransition *ctratt;
+        if (get_attribute_into(ctratt, this))
+          alpha = (ctratt->get_mode() != TransparencyProperty::M_none);
+    }
+
+    // inner loop vars
+    int i;
+    Vertexf source_vert, cameraspace_vert;
+    float *x_walk, *y_walk, *theta_walk;
+    float theta;
+
+    nassertv(geom->get_x_bind_type() != G_PER_VERTEX);
+    nassertv(geom->get_y_bind_type() != G_PER_VERTEX);
+
+    // set up the non-built-in bindings
+    bool x_overall = (geom->get_x_bind_type() == G_OVERALL);
+    bool y_overall = (geom->get_y_bind_type() == G_OVERALL);
+    bool theta_overall = (geom->get_theta_bind_type() == G_OVERALL);
+    bool color_overall = (geom->get_binding(G_COLOR) == G_OVERALL);
+    bool theta_on = !(geom->get_theta_bind_type() == G_OFF);
+
+    // x direction
+    if (x_overall)
+        scaled_width = geom->_x_texel_ratio[0] * half_width;
+    else {
+        nassertv(((int)geom->_x_texel_ratio.size() >= geom->get_num_prims()));
+        x_walk = &geom->_x_texel_ratio[0];
+    }
+
+    // y direction
+    if (y_overall)
+        scaled_height = geom->_y_texel_ratio[0] * half_height * aspect_ratio;
+    else {
+        nassertv(((int)geom->_y_texel_ratio.size() >= geom->get_num_prims()));
+        y_walk = &geom->_y_texel_ratio[0];
+    }
+
+    // theta
+    if (theta_on) {
+        if (theta_overall)
+            theta = geom->_theta[0];
+        else {
+            nassertv(((int)geom->_theta.size() >= geom->get_num_prims()));
+            theta_walk = &geom->_theta[0];
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    // INNER LOOP PART 1 STARTS HERE
+    // Here we transform each point to cameraspace and fill our sort
+    // vector with the final geometric information.
+    /////////////////////////////////////////////////////////////////////
+
+    Colorf v_color;
+
+    // sort container and iterator
+    pvector< WrappedSpriteSortPtr > sorted_sprite_vector;
+    pvector< WrappedSpriteSortPtr >::iterator sorted_vec_iter;
+
+    WrappedSprite *SpriteArray = new WrappedSprite[nprims];
+
+    //BUGBUG: could we use _fvfbuf for this to avoid perframe alloc?
+    // alternately, alloc once when retained mode becomes available
+
+    if (SpriteArray==NULL) {
+        dxgsg_cat.fatal() << "draw_sprite() out of memory!!" << endl;
+        return;
+    }
+
+    // the state is set, start running the prims
+
+    WrappedSprite *pSpr;
+
+    for (pSpr=SpriteArray,i = 0; i < nprims; i++,pSpr++) {
+
+        source_vert = geom->get_next_vertex(vi);
+        cameraspace_vert = source_vert * modelview_mat;
+
+        pSpr->_v.set(cameraspace_vert[0],cameraspace_vert[1],cameraspace_vert[2]);
+
+        if (!color_overall) {
+            GET_NEXT_COLOR();
+            pSpr->_c = _curD3Dcolor;
+        }
+        if (!x_overall)
+            pSpr->_x_ratio = *x_walk++;
+        if (!y_overall)
+            pSpr->_y_ratio = *y_walk++;    // go along array of ratio values stored in geom
+        if (theta_on && (!theta_overall))
+            pSpr->_theta = *theta_walk++;
+    }
+
+    if (alpha) {
+        sorted_sprite_vector.reserve(nprims);   //pre-alloc space for nprims
+
+        for (pSpr=SpriteArray,i = 0; i < nprims; i++,pSpr++) {   // build STL-sortable array
+            WrappedSpriteSortPtr ws_ptr;
+            ws_ptr.z=pSpr->_v[2];
+            ws_ptr.pSpr=pSpr;
+            sorted_sprite_vector.push_back(ws_ptr);
+        }
+
+        // sort the verts properly by alpha (if necessary).  Of course,
+        // the sort is only local, not scene-global, so if you look closely you'll
+        // notice that alphas may be screwy.  It's ok though, because this is fast.
+        // if you want accuracy, use billboards and take the speed hit.
+
+        sort(sorted_sprite_vector.begin(), sorted_sprite_vector.end(), draw_sprite_vertex_less());
+        sorted_vec_iter = sorted_sprite_vector.begin();
+
+        // disabling dither for alpha particle-systems.
+        // ATI sez:  most applications ignore the fact that since alpha blended primitives
+        // combine the data in the frame buffer with the data in the current pixel, pixels 
+        // can be dithered multiple times and accentuate the dither pattern. This is particularly
+        // true in particle systems which rely on the cumulative visual effect of many overlapping
+        // alpha blended primitives.
+
+        if(_dither_enabled) {
+            bReEnableDither=true;
+            enable_dither(false);
+        }
+    }
+
+    Vertexf ul, ur, ll, lr;
+
+    ////////////////////////////////////////////////////////////////////////////
+    // INNER LOOP PART 2 STARTS HERE
+    // Now we run through the cameraspace vector and compute the geometry for each
+    // tristrip.  This includes scaling as per the ratio arrays, as well as
+    // rotating in the z.
+    ////////////////////////////////////////////////////////////////////////////
+
+    D3DCOLOR CurColor;
+
+    #if 0
+        //   not going to attempt this bDoColor optimization to use default white color in flat-shaded
+        //   mode anymore,  it just make the logic confusing below.  from now on, always have color in FVF
+    
+        _curFVFflags = D3DFVF_XYZ | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)) ;
+        DWORD vertex_size = sizeof(float) * 2 + sizeof(D3DVALUE) * 3;
+
+        bool bDoColor=true;
+    
+        if (color_overall) {
+            GET_NEXT_COLOR();
+            CurColor = _curD3Dcolor;
+            bDoColor = (_curD3Dcolor != ~0);  // dont need to add color if it's all white
+        }
+    
+        if (bDoColor) {
+            _curFVFflags |= D3DFVF_DIFFUSE;
+            vertex_size+=sizeof(D3DCOLOR);
+        }
+    #else
+      _curFVFflags = D3DFVF_XYZ | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)) | D3DFVF_DIFFUSE;
+      DWORD vertex_size = sizeof(float) * 2 + sizeof(D3DVALUE) * 3 + sizeof(D3DCOLOR);  
+
+      if (color_overall) {
+        GET_NEXT_COLOR();
+        CurColor = _curD3Dcolor;
+      }
+    #endif
+
+    // see note on fog in draw_prim_setup
+    bool bUseGouraudShadedColor=_fog_enabled;
+
+    set_shademode(!_fog_enabled ? D3DSHADE_FLAT: D3DSHADE_GOURAUD);
+
+    #ifdef _DEBUG
+     nassertv(_pCurFvfBufPtr == NULL);   // make sure the storage pointer is clean.
+     nassertv(nprims * 4 * vertex_size < VERT_BUFFER_SIZE);
+     nassertv(nprims * 6 < D3DMAXNUMVERTICES );
+    #endif
+
+    _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+    const float TexCrdSets[4][2] = {
+      { tex_left, tex_bottom },
+      { tex_right, tex_bottom },
+      { tex_left, tex_top },
+      { tex_right, tex_top }
+    };
+
+#define QUADVERTLISTLEN 6
+
+    DWORD QuadVertIndexList[QUADVERTLISTLEN] = { 0, 1, 2, 3, 2, 1};
+    DWORD CurDPIndexArrLength=0,CurVertCount=0;
+
+    for (pSpr=SpriteArray,i = 0; i < nprims; i++,pSpr++) {   // build STL-sortable array
+
+        if (alpha) {
+            pSpr = sorted_vec_iter->pSpr;
+            sorted_vec_iter++;
+        }
+
+        // if not G_OVERALL, calculate the scale factors    //huh??
+        if (!x_overall)
+            scaled_width = pSpr->_x_ratio * half_width;
+
+        if (!y_overall)
+            scaled_height = pSpr->_y_ratio * half_height * aspect_ratio;
+
+        // if not G_OVERALL, do some trig for this z rotate   //what is the theta angle??
+        if (theta_on) {
+            if (!theta_overall)
+                theta = pSpr->_theta;
+
+            // create the rotated points.  BUGBUG: this matmult will be slow if we dont get inlining
+            // rotate_mat calls sin() on an unbounded val, possible to make it faster with lookup table (modulate to 0-360 range?)
+
+            LMatrix3f xform_mat = LMatrix3f::rotate_mat(theta) *
+                                  LMatrix3f::scale_mat(scaled_width, scaled_height);
+
+            ur = (LVector3f( 1.0f,  1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            ul = (LVector3f(-1.0f,  1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            lr = (LVector3f( 1.0f, -1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            ll = (LVector3f(-1.0f, -1.0f, 0.0f) * xform_mat) + pSpr->_v;
+        } else {
+            // create points for unrotated rect sprites
+            float x,y,negx,negy,z;
+
+            x = pSpr->_v[0] + scaled_width;
+            y = pSpr->_v[1] + scaled_height;
+            negx = pSpr->_v[0] - scaled_width;
+            negy = pSpr->_v[1] - scaled_height;
+            z = pSpr->_v[2];
+
+            ur.set(x, y, z);
+            ul.set(negx, y, z);
+            lr.set(x, negy, z);
+            ll.set(negx, negy, z);
+        }
+
+        // can no longer assume flat-shaded (because of vtx fog), so always copy full color in there
+
+        /*********  LL vertex  **********/
+
+        add_to_FVFBuf((void *)ll.get_data(), sizeof(D3DVECTOR));
+        if (!color_overall)  // otherwise its already been set globally
+           CurColor = pSpr->_c;
+        add_DWORD_to_FVFBuf(CurColor); // only need to cpy color on 1st vert, others are just empty ignored space
+        add_to_FVFBuf((void *)TexCrdSets[0], sizeof(float)*2);
+
+        /*********  LR vertex  **********/
+
+        add_to_FVFBuf((void *)lr.get_data(), sizeof(D3DVECTOR));
+
+        // if flat shading, dont need to write color for middle vtx, just incr ptr
+        if(bUseGouraudShadedColor)
+            *((DWORD *)_pCurFvfBufPtr) = (DWORD) CurColor; 
+        _pCurFvfBufPtr += sizeof(D3DCOLOR);  
+
+        add_to_FVFBuf((void *)TexCrdSets[1], sizeof(float)*2);
+
+        /*********  UL vertex  **********/
+
+        add_to_FVFBuf((void *)ul.get_data(), sizeof(D3DVECTOR));
+        // if flat shading, dont need to write color for middle vtx, just incr ptr
+        if(bUseGouraudShadedColor)
+            *((DWORD *)_pCurFvfBufPtr) = (DWORD) CurColor; 
+        _pCurFvfBufPtr += sizeof(D3DCOLOR);
+        add_to_FVFBuf((void *)TexCrdSets[2], sizeof(float)*2);
+
+        /*********  UR vertex  **********/
+
+        add_to_FVFBuf((void *)ur.get_data(), sizeof(D3DVECTOR));
+        add_DWORD_to_FVFBuf(CurColor);
+        add_to_FVFBuf((void *)TexCrdSets[3], sizeof(float)*2);
+
+        for (int ii=0;ii<QUADVERTLISTLEN;ii++) {
+            _index_buf[CurDPIndexArrLength+ii]=QuadVertIndexList[ii]+CurVertCount;
+        }
+        CurDPIndexArrLength+=QUADVERTLISTLEN;
+        CurVertCount+=4;
+    }
+
+    // cant do tristrip/fan since it would require 1 call want to make 1 call for multiple quads which arent connected
+    // best we can do is indexed primitive, which sends 2 redundant indices instead of sending 2 redundant full verts
+    HRESULT hr = _d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, _curFVFflags, _pFvfBufBasePtr, 4*nprims, _index_buf,QUADVERTLISTLEN*nprims,NULL);
+    TestDrawPrimFailure(DrawIndexedPrim,hr,_pDD,QUADVERTLISTLEN*nprims,nprims);
+
+    _pCurFvfBufPtr = NULL;
+    delete [] SpriteArray;
+
+    // restore the matrices
+    _d3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &OldD3DWorldMatrix);
+
+    if(bReEnableDither)
+        enable_dither(true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_polygon
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_polygon(GeomPolygon *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+   dxgsg_cat.debug() << "draw_polygon()" << endl;
+#endif
+   DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+   DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+   // wireframe polygon will be drawn as linestrip, otherwise draw as multi-tri trifan
+   DWORD rstate;
+   _d3dDevice->GetRenderState(D3DRENDERSTATE_FILLMODE, &rstate);
+   if(rstate==D3DFILL_WIREFRAME) {
+       draw_linestrip_base(geom,gc,true);
+   } else {
+       draw_multitri(geom, D3DPT_TRIANGLEFAN);
+   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_quad
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_quad(GeomQuad *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_quad()" << endl;
+#endif
+   DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+   DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+   // wireframe quad will be drawn as linestrip, otherwise draw as multi-tri trifan
+   DWORD rstate;
+   _d3dDevice->GetRenderState(D3DRENDERSTATE_FILLMODE, &rstate);
+   if(rstate==D3DFILL_WIREFRAME) {
+       draw_linestrip_base(geom,gc,true);
+   } else {
+       draw_multitri(geom, D3DPT_TRIANGLEFAN);
+   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_tri
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_tri(GeomTri *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_tri()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_tri_pcollector.add_level(geom->get_num_vertices()));
+
+#if 0
+    if (_pCurTexContext!=NULL) { 
+        dxgsg_cat.spam() << "Cur active DX texture: " << _pCurTexContext->_tex->get_name() << "\n";
+    }
+#endif
+
+#ifdef COUNT_DRAWPRIMS
+    cGeomcount++;
+#endif
+
+    DWORD nPrims = geom->get_num_prims();
+    HRESULT hr;
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType TexCoordBinding,ColorBinding,NormalBinding;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,NormalBinding,nindexes);
+    geom->get_colors(colors,ColorBinding,cindexes);
+    geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+
+    GeomVertFormat GeomVrtFmt=FlatVerts;
+
+    // first determine if we're indexed or non-indexed
+
+    if ((vindexes!=NULL)&&(cindexes!=NULL)&&(tindexes!=NULL)&&(nindexes!=NULL)) {
+        GeomVrtFmt=IndexedVerts;
+        //make sure array sizes are consistent, we can only pass 1 size to DrawIPrm
+//      nassertv(coords.size==norms.size);      nassertv(coords.size==colors.size);     nassertv(coords.size==texcoords.size);  need to assert only if we have this w/same binding
+        // indexed mode requires all used norms,colors,texcoords,coords array be the same
+        // length, or 0 or 1 (dwStride==0), also requires all elements to use the same index array
+    } else if (!((vindexes==NULL)&&(cindexes==NULL)&&(tindexes==NULL)&&(nindexes==NULL)))
+        GeomVrtFmt=MixedFmtVerts;
+
+#ifdef DONT_USE_DRAWPRIMSTRIDED
+    GeomVrtFmt=MixedFmtVerts;
+#else
+    if(_bDrawPrimDoSetupVertexBuffer) {
+      GeomVrtFmt=MixedFmtVerts;
+    }
+#endif
+
+    // for Indexed Prims and mixed indexed/non-indexed prims, we will use old pipeline for now
+    // need to add code to handle fully indexed mode (and handle cases with index arrays of different lengths,
+    // values (may only be possible to handle certain cases without reverting to old pipeline)
+    if (GeomVrtFmt!=FlatVerts) {
+        // this is the old geom setup, it reformats every vtx into an output array passed to d3d
+
+        _perVertex = 0x0;
+        _perPrim = 0x0;
+
+        if(NormalBinding == G_PER_VERTEX)   
+          _perVertex |= PER_NORMAL;
+         else if(NormalBinding == G_PER_PRIM) 
+                 _perPrim |= PER_NORMAL;
+
+        if(ColorBinding == G_PER_PRIM)  
+           _perPrim |= PER_COLOR;
+          else if(ColorBinding == G_PER_VERTEX)    
+                 _perVertex |= PER_COLOR;
+
+        if (TexCoordBinding == G_PER_VERTEX) 
+           _perVertex |= PER_TEXCOORD;
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+        nassertv(nPrims * 3 * vertex_size < VERT_BUFFER_SIZE);
+        _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+        // iterate through the triangle primitive
+
+        for (uint i = 0; i < nPrims; i++) {
+            if (_perPrim & PER_COLOR) {
+                GET_NEXT_COLOR();
+            }
+
+            if (_perPrim & PER_NORMAL)
+                p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+
+
+            if(_perVertex==TexCoordOnly)
+               draw_prim_inner_loop_coordtexonly(3, geom);   
+             else draw_prim_inner_loop(3, geom, _perVertex);
+        }
+
+        DWORD nVerts=nPrims*3;
+
+        if(!_bDrawPrimDoSetupVertexBuffer) {
+            hr = _d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, _curFVFflags, _pFvfBufBasePtr, nVerts, NULL);
+            TestDrawPrimFailure(DrawPrim,hr,_pDD,nVerts,nPrims);
+        } else {
+            COPYVERTDATA_2_VERTEXBUFFER(D3DPT_TRIANGLELIST,nVerts);
+        }
+
+        _pCurFvfBufPtr = NULL;
+    } else {
+
+        // new geom setup that uses strided DP calls to avoid making an extra pass over the data
+
+        D3DDRAWPRIMITIVESTRIDEDDATA dps_data;
+        memset(&dps_data,0,sizeof(D3DDRAWPRIMITIVESTRIDEDDATA));
+
+#ifdef _DEBUG
+        nassertv(!geom->uses_components());  // code ignores lengths array
+        nassertv(geom->get_binding(G_COORD) == G_PER_VERTEX);
+#endif
+
+        D3DPRIMITIVETYPE primtype=D3DPT_TRIANGLELIST;
+
+        DWORD fvf_flags = D3DFVF_XYZ;
+        dps_data.position.lpvData = (VOID*)coords;
+        dps_data.position.dwStride = sizeof(D3DVECTOR);
+
+        // see fog comment in draw_prim_setup
+        D3DSHADEMODE NeededShadeMode = (_fog_enabled) ? D3DSHADE_GOURAUD : D3DSHADE_FLAT;
+
+        const DWORD dwVertsperPrim=3;
+
+        if ((NormalBinding != G_OFF) && wants_normals()) {
+
+            dps_data.normal.lpvData = (VOID*)norms;
+            dps_data.normal.dwStride = sizeof(D3DVECTOR);
+
+#ifdef _DEBUG
+            nassertv(geom->get_num_vertices_per_prim()==3);
+            nassertv( nPrims*dwVertsperPrim*sizeof(D3DVECTOR) <= D3DMAXNUMVERTICES*sizeof(WORD));
+            if (NormalBinding==G_PER_VERTEX)
+                nassertv(norms.size()>=nPrims*dwVertsperPrim);
+#endif
+
+            fvf_flags |= D3DFVF_NORMAL;
+            NeededShadeMode = D3DSHADE_GOURAUD;
+
+            Normalf *pExpandedNormalArray = (Normalf *)_index_buf;  // BUGBUG:  need to use real permanent buffers for this conversion
+            if (NormalBinding==G_PER_PRIM) {
+                // must use tmp array to duplicate-expand per-prim norms to per-vert norms
+                Normalf *pOutVec = pExpandedNormalArray;
+                Normalf *pInVec=norms;
+
+                nassertv(norms.size()>=nPrims);
+
+                for (uint i=0;i<nPrims;i++,pInVec++,pOutVec+=dwVertsperPrim) {
+                    *pOutVec     = *pInVec;
+                    *(pOutVec+1) = *pInVec;
+                    *(pOutVec+2) = *pInVec;
+                }
+
+                dps_data.normal.lpvData = (VOID*)pExpandedNormalArray;
+
+            } else if (NormalBinding==G_OVERALL) {
+                // copy the one global color in, set stride to 0
+                *pExpandedNormalArray=norms[0];
+                dps_data.normal.lpvData = (VOID*)pExpandedNormalArray;
+                dps_data.normal.dwStride = 0;
+            }
+        }
+
+        ColorTransition *catt=NULL;
+        bool bDoGlobalSceneGraphColor=FALSE,bDoColor=(ColorBinding != G_OFF);
+
+        // We should issue geometry colors only if the scene graph color is off.
+        if (get_attribute_into(catt, this)) {
+            if (!catt->is_real())
+                bDoColor=FALSE;  // this turns off any Geom colors
+            else {
+                ColorBinding=G_OVERALL;
+                bDoGlobalSceneGraphColor=TRUE;
+            }
+        }
+
+        if (bDoColor || bDoGlobalSceneGraphColor) {
+            D3DCOLOR *pOutColor,*pConvertedColorArray;
+            Colorf *pInColor=colors;
+            pOutColor = pConvertedColorArray = (D3DCOLOR *)_pFvfBufBasePtr;
+
+#ifdef _DEBUG
+            nassertv( nPrims*dwVertsperPrim*sizeof(D3DCOLOR) <= VERT_BUFFER_SIZE);
+#endif
+
+            fvf_flags |= D3DFVF_DIFFUSE;
+
+            dps_data.diffuse.lpvData = (VOID*)pConvertedColorArray;
+            dps_data.diffuse.dwStride = sizeof(D3DCOLOR);
+
+            if (ColorBinding==G_PER_PRIM) {
+                // must use tmp array to expand per-prim info to per-vert info
+
+                // Geom nodes store floats for colors, drawprim requires ARGB dwords
+                // BUGBUG: eventually this hack every-frame all-colors conversion needs
+                // to be done only once as part of a vertex buffer
+
+                if (NeededShadeMode!=D3DSHADE_FLAT) {
+                    // but if lighting enabled, we need to color every vert since shading will be GOURAUD
+
+                    if(!_color_transform_required) {
+                        for (uint i=0;i<nPrims;i++,pInColor++,pOutColor+=dwVertsperPrim) {
+                            D3DCOLOR newcolr = Colorf_to_D3DCOLOR(*pInColor);
+                            *pOutColor     = newcolr;
+                            *(pOutColor+1) = newcolr;
+                            *(pOutColor+2) = newcolr;
+                        }
+                     } else {
+                        for (uint i=0;i<nPrims;i++,pInColor++,pOutColor+=dwVertsperPrim) {
+                            D3DCOLOR newcolr;
+                            transform_color(*pInColor,newcolr);
+
+                            *pOutColor     = newcolr;
+                            *(pOutColor+1) = newcolr;
+                            *(pOutColor+2) = newcolr;
+                        }
+                    }
+                } else {
+                    // dont write 2nd,3rd colors in output buffer, these are not used in flat shading
+                    // MAKE SURE ShadeMode never set to GOURAUD after this!
+
+                    if(!_color_transform_required) {
+                        for (uint i=0;i<nPrims;i++,pInColor++,pOutColor+=dwVertsperPrim) {
+                            *pOutColor = Colorf_to_D3DCOLOR(*pInColor);
+                        }
+                     } else {
+                        for (uint i=0;i<nPrims;i++,pInColor++,pOutColor+=dwVertsperPrim) {
+                            transform_color(*pInColor,*pOutColor);
+                        }
+                    }
+                }
+            } else if (ColorBinding==G_PER_VERTEX) {
+                NeededShadeMode = D3DSHADE_GOURAUD;
+
+                // want to do this conversion once in retained mode
+                DWORD cNumColors=nPrims*dwVertsperPrim;
+
+                    if(!_color_transform_required) {
+                        for (uint i=0;i<cNumColors;i++,pInColor++,pOutColor++) {
+                            *pOutColor = Colorf_to_D3DCOLOR(*pInColor);
+                        }
+                     } else {
+                        for (uint i=0;i<cNumColors;i++,pInColor++,pOutColor++) {
+                            transform_color(*pInColor,*pOutColor);
+                        }
+                    }
+            } else {
+#ifdef _DEBUG
+                nassertv(ColorBinding==G_OVERALL);
+#endif
+                // copy the one global color in, set stride to 0
+
+                if(!_color_transform_required) {
+                    if (bDoGlobalSceneGraphColor) {
+                        Colorf colr = catt->get_color();
+                        *pConvertedColorArray = Colorf_to_D3DCOLOR(colr);
+                    } else {
+                        *pConvertedColorArray = Colorf_to_D3DCOLOR(*pInColor);
+                    }
+                } else {
+                    if (bDoGlobalSceneGraphColor) {
+                        Colorf colr = catt->get_color();
+                        transform_color(colr,*pConvertedColorArray);
+                    } else {
+                        transform_color(*pInColor,*pConvertedColorArray);
+                    }
+                }
+
+                dps_data.diffuse.dwStride = 0;
+            }
+        }
+
+        if ((TexCoordBinding != G_OFF) && _texturing_enabled) {
+
+#ifdef _DEBUG
+            nassertv(TexCoordBinding == G_PER_VERTEX);  // only sensible choice for a tri
+#endif
+
+            dps_data.textureCoords[0].lpvData = (VOID*)texcoords;
+            dps_data.textureCoords[0].dwStride = sizeof(TexCoordf);
+            fvf_flags |= (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+        }
+
+        set_shademode(NeededShadeMode);
+
+        DWORD nVerts = nPrims*dwVertsperPrim;
+
+        hr = _d3dDevice->DrawPrimitiveStrided(primtype, fvf_flags, &dps_data, nVerts, NULL);
+        TestDrawPrimFailure(DrawPrimStrided,hr,_pDD,nVerts,nPrims);
+
+        _pCurFvfBufPtr = NULL;
+    }
+
+///////////////////////////
+#if 0
+    // test triangle for me to dbg experiments only
+    float vert_buf[15] = {
+        0.0f, 0.0f, 0.0f,  0.0f, 0.0f,
+        33.0, 0.0f, 0.0f,  0.0f, 2.0,
+        0.0f, 0.0f, 33.0,  2.0, 0.0f
+    };
+
+    _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_BORDER);
+    _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_BORDER);
+    _d3dDevice->SetTextureStageState(0,D3DTSS_BORDERCOLOR,MY_D3DRGBA(0,0,0,0));
+
+    _curFVFflags =  D3DFVF_XYZ | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)) ;
+    HRESULT hr = _d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,  _curFVFflags, vert_buf, nPrims*3, NULL);
+    TestDrawPrimFailure(DrawPrim,hr,_pDD,nPrims*3,nPrims);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_tristrip
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_tristrip(GeomTristrip *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+  dxgsg_cat.debug() << "draw_tristrip()" << endl;
+#endif
+  DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+  DO_PSTATS_STUFF(_vertices_tristrip_pcollector.add_level(geom->get_num_vertices()));
+
+  draw_multitri(geom, D3DPT_TRIANGLESTRIP);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_trifan
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_trifan(GeomTrifan *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_trifan()" << endl;
+#endif
+  DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+  DO_PSTATS_STUFF(_vertices_trifan_pcollector.add_level(geom->get_num_vertices()));
+
+  draw_multitri(geom, D3DPT_TRIANGLEFAN);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_multitri
+//       Access: Public, Virtual
+//  Description: handles trifans and tristrips
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_multitri(Geom *geom, D3DPRIMITIVETYPE trilisttype) {
+
+    DWORD nPrims = geom->get_num_prims();
+    const uint *pLengthArr = (const uint *) ((const int *)geom->get_lengths());
+    HRESULT hr;
+
+    if(nPrims==0) {
+        #ifdef _DEBUG
+          dxgsg_cat.warning() << "draw_multitri() called with ZERO vertices!!" << endl;
+        #endif
+        return;
+    }
+
+#ifdef COUNT_DRAWPRIMS
+    cGeomcount++;
+#endif
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType TexCoordBinding,ColorBinding,NormalBinding;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,NormalBinding,nindexes);
+    geom->get_colors(colors,ColorBinding,cindexes);
+    geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+
+    GeomVertFormat GeomVrtFmt;
+
+#ifdef DONT_USE_DRAWPRIMSTRIDED
+    GeomVrtFmt=MixedFmtVerts;
+#else
+    GeomVrtFmt=FlatVerts;
+
+    if(!geom->uses_components()) {
+       GeomVrtFmt=MixedFmtVerts; // dont need efficiency here, just use simpler codepath
+    } else {
+        // first determine if we're indexed or non-indexed
+        if((vindexes!=NULL)&&(cindexes!=NULL)&&(tindexes!=NULL)&&(nindexes!=NULL)) {
+            GeomVrtFmt=IndexedVerts;
+            //make sure array sizes are consistent, we can only pass 1 size to DrawIPrm
+            //      nassertv(coords.size==norms.size);      nassertv(coords.size==colors.size);     nassertv(coords.size==texcoords.size);  need to assert only if we have this w/same binding
+            // indexed mode requires all used norms,colors,texcoords,coords array be the same
+            // length, or 0 or 1 (dwStride==0), also requires all elements to use the same index array
+        } else if (!((vindexes==NULL)&&(cindexes==NULL)&&(tindexes==NULL)&&(nindexes==NULL)))
+            GeomVrtFmt=MixedFmtVerts;
+    }
+
+    if(_bDrawPrimDoSetupVertexBuffer) {
+      GeomVrtFmt=MixedFmtVerts;
+    }
+#endif
+
+    // for Indexed Prims and mixed indexed/non-indexed prims, we will use old pipeline
+    // cant handle indexed prims because usually have different index arrays for different components,
+    // and DrIdxPrmStrd only accepts 1 index array for all components
+    if (GeomVrtFmt!=FlatVerts) {
+
+        // this is the old geom setup, it reformats every vtx into an output array passed to d3d
+        _perVertex = 0x0;
+        _perPrim = _perComp = 0;
+
+        switch (NormalBinding) {
+            case G_OFF:
+                break;
+            case G_PER_VERTEX:
+                _perVertex |= PER_NORMAL;
+                break;
+            case G_PER_PRIM:
+                _perPrim |= PER_NORMAL;
+                break;
+            case G_PER_COMPONENT:
+                _perComp |= PER_NORMAL;
+                break;
+        }
+
+        switch (ColorBinding) {
+            case G_PER_PRIM:
+                _perPrim |= PER_COLOR;
+                break;
+            case G_PER_COMPONENT:
+                _perComp |= PER_COLOR;
+                break;
+            case G_PER_VERTEX:
+                _perVertex |= PER_COLOR;
+                break;
+        }
+
+        if (TexCoordBinding == G_PER_VERTEX)
+            _perVertex |= PER_TEXCOORD;
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        // iterate through the triangle primitives
+
+        for (uint i = 0; i < nPrims; i++) {
+
+            if (_perPrim & PER_COLOR) {
+                GET_NEXT_COLOR();
+            }
+            if (_perPrim & PER_NORMAL)
+                p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+            
+            int nVerts;
+
+            if(pLengthArr!=NULL) {
+              nVerts = *(pLengthArr++);
+            } else {
+              // we've been called by draw_quad, which has no lengths array
+              nVerts=4;
+            }
+
+#ifdef _DEBUG
+            nassertv(nVerts >= 3);
+            nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+            nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE);
+#endif
+            _pCurFvfBufPtr = _pFvfBufBasePtr;            // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+            if(_perComp==0x0) {
+                 if(_perVertex==TexCoordOnly)
+                    draw_prim_inner_loop_coordtexonly(nVerts, geom);   
+                   else draw_prim_inner_loop(nVerts, geom, _perVertex);
+            } else {
+                    if(trilisttype==D3DPT_TRIANGLESTRIP) {
+                       // in flat shade mode, D3D strips color using the 1st vertex. 
+                       // (note: differs from OGL, which always uses last vtx for strips&fans
+
+                            // Store all but last 2 verts
+                            draw_prim_inner_loop(nVerts-2, geom, _perVertex | _perComp);
+
+                            // _perComp attribs should not be fetched for last 2 verts
+                            draw_prim_inner_loop(2, geom, _perVertex);
+                    } else {
+                       // in flat shade mode, D3D fans color using the 2nd vertex. 
+                       // (note: differs from OGL, which always uses last vtx for strips&fans
+                       // _perComp attribs should not be fetched for first & last verts, they will
+                       // be associated with middle n-2 verts
+
+                            draw_prim_inner_loop(1, geom, _perVertex);
+
+                            draw_prim_inner_loop(nVerts-2, geom, _perVertex | _perComp);
+
+                            draw_prim_inner_loop(1, geom, _perVertex);
+                    }
+            }
+
+            if(!_bDrawPrimDoSetupVertexBuffer) {
+                hr = _d3dDevice->DrawPrimitive(trilisttype,  _curFVFflags, _pFvfBufBasePtr, nVerts, NULL);
+                TestDrawPrimFailure(DrawPrim,hr,_pDD,nVerts,nVerts-2);
+            } else {
+                COPYVERTDATA_2_VERTEXBUFFER(trilisttype,nVerts);
+            }                       
+
+            _pCurFvfBufPtr = NULL;
+        }
+    } else {
+
+        // new geom setup that uses strided DP calls to avoid making an extra pass over the data
+
+        D3DDRAWPRIMITIVESTRIDEDDATA dps_data;
+        memset(&dps_data,0,sizeof(D3DDRAWPRIMITIVESTRIDEDDATA));
+
+#ifdef _DEBUG
+        nassertv(geom->uses_components());
+        nassertv(geom->get_binding(G_COORD) == G_PER_VERTEX);
+#endif
+
+        DWORD fvf_flags = D3DFVF_XYZ;
+        dps_data.position.lpvData = (VOID*)coords;
+        dps_data.position.dwStride = sizeof(D3DVECTOR);
+
+        D3DSHADEMODE NeededShadeMode = D3DSHADE_FLAT;
+
+        DWORD cTotalVerts=0;
+
+        for (uint i=0;i<nPrims;i++) {
+            cTotalVerts+= pLengthArr[i];
+        }
+
+        const DWORD cNumMoreVertsthanTris=2;
+
+        if((NormalBinding != G_OFF) && wants_normals()) {
+
+            dps_data.normal.lpvData = (VOID*)norms;
+            dps_data.normal.dwStride = sizeof(D3DVECTOR);
+
+#ifdef _DEBUG
+            nassertv(geom->get_num_more_vertices_than_components()==2);
+            nassertv(NormalBinding!=G_PER_COMPONENT); // makes no sense, unimplementable for strips since normals always shared across tris
+            nassertv( cTotalVerts*sizeof(D3DVECTOR) <= D3DMAXNUMVERTICES*sizeof(WORD));
+            if(NormalBinding==G_PER_VERTEX)
+                nassertv(norms.size()>=cTotalVerts);
+#endif
+            fvf_flags |= D3DFVF_NORMAL;
+            NeededShadeMode = D3DSHADE_GOURAUD;
+
+            Normalf *pExpandedNormalArray = (Normalf *)_index_buf;  // BUGBUG:  need to use real permanent buffers instead of _indexbuf hack
+
+            if(NormalBinding==G_PER_PRIM) {
+                // we have 1 normal per strip
+                // must use tmp array to duplicate-expand per-prim norms to per-vert norms
+                Normalf *pOutVec = pExpandedNormalArray;
+                Normalf *pInVec=norms;
+                const uint *pLengths=pLengthArr;
+
+                nassertv(norms.size()>=nPrims);
+
+                for (uint i=0;i<nPrims;i++,pInVec++,pLengths++) {
+                    for (uint j=0;j<(*pLengths);j++,pOutVec++) {
+                        *pOutVec = *pInVec;
+                    }
+                }
+
+                dps_data.normal.lpvData = (VOID*)pExpandedNormalArray;
+
+            } else if(NormalBinding==G_OVERALL) {
+                // copy the one global color in, set stride to 0
+                *pExpandedNormalArray=norms[0];
+                dps_data.normal.lpvData = (VOID*)pExpandedNormalArray;
+                dps_data.normal.dwStride = 0;
+            }
+        }
+
+        ColorTransition *catt=NULL;
+        bool bDoGlobalSceneGraphColor=FALSE,bDoColor=(ColorBinding != G_OFF);
+
+        // We should issue geometry colors only if the scene graph color is off.
+        if (get_attribute_into(catt, this)) {
+            if (!catt->is_real())
+                bDoColor=FALSE;  // this turns off any Geom colors
+            else {
+                ColorBinding=G_OVERALL;
+                bDoGlobalSceneGraphColor=TRUE;
+            }
+        }
+
+        if (bDoColor || bDoGlobalSceneGraphColor) {
+            D3DCOLOR *pOutColor,*pConvertedColorArray;
+            Colorf *pInColor=colors;
+            pOutColor = pConvertedColorArray = (D3DCOLOR *)_pFvfBufBasePtr;
+
+#ifdef _DEBUG
+            nassertv( cTotalVerts*sizeof(D3DCOLOR) <= VERT_BUFFER_SIZE);
+#endif
+
+            fvf_flags |= D3DFVF_DIFFUSE;
+
+            dps_data.diffuse.lpvData = (VOID*)pConvertedColorArray;
+            dps_data.diffuse.dwStride = sizeof(D3DCOLOR);
+
+            if (ColorBinding==G_PER_VERTEX) {
+                NeededShadeMode = D3DSHADE_GOURAUD;
+
+                if(!_color_transform_required) {
+                    for (uint i=0;i<cTotalVerts;i++,pInColor++,pOutColor++) {
+                        *pOutColor = Colorf_to_D3DCOLOR(*pInColor);
+                    }
+                } else {
+                    for (uint i=0;i<cTotalVerts;i++,pInColor++,pOutColor++) {
+                        transform_color(*pInColor,*pOutColor);
+                    }
+                }
+            } else if (ColorBinding==G_PER_PRIM) {
+                // must use tmp array to expand per-prim info to per-vert info
+                // eventually want to do this conversion once in retained mode
+                // have one color per strip, need 1 color per vert
+
+                // could save 2 clr writes per strip/fan in flat shade mode but not going to bother here
+
+                if(!_color_transform_required) {
+                    for (uint j=0;j<nPrims;j++,pInColor++) {
+                        D3DCOLOR lastcolr = Colorf_to_D3DCOLOR(*pInColor);
+                        DWORD cStripLength=pLengthArr[j];
+                        for (uint i=0;i<cStripLength;i++,pOutColor++) {
+                            *pOutColor = lastcolr;
+                        }
+                    }
+                } else {
+                    for (uint j=0;j<nPrims;j++,pInColor++) {
+                        D3DCOLOR lastcolr;
+                        transform_color(*pInColor,lastcolr);
+                        DWORD cStripLength=pLengthArr[j];
+                        for (uint i=0;i<cStripLength;i++,pOutColor++) {
+                            *pOutColor = lastcolr;
+                        }
+                    }
+                }
+            } else if (ColorBinding==G_PER_COMPONENT) {
+                // have a color per tri, need a color per vert (2 more than #tris)
+                // want to do this conversion once in retained mode
+                nassertv(colors.size() >= cTotalVerts-nPrims*cNumMoreVertsthanTris);
+
+                #define MULTITRI_COLORCOPY_LOOP                                       \
+                    DWORD cCurStripColorCnt=pLengthArr[j]-cNumMoreVertsthanTris;      \
+                    for (uint i=0;i<cCurStripColorCnt;i++,pInColor++,pOutColor++)
+
+                #define COLOR_CONVERT_COPY_STMT  {*pOutColor = Colorf_to_D3DCOLOR(*pInColor);}
+                #define COLOR_CONVERT_XFORM_STMT {transform_color(*pInColor,*pOutColor);}
+
+                #define COMPONENT_COLOR_COPY_LOOPS(COLOR_COPYSTMT)  {                        \
+                    if (NeededShadeMode == D3DSHADE_FLAT) {                                  \
+                        /* FLAT shade mode.  for tristrips, skip writing last 2 verts.  */   \
+                        /* for trifans, skip first and last verts                       */   \
+                        if (trilisttype==D3DPT_TRIANGLESTRIP) {                              \
+                            for (uint j=0;j<nPrims;j++) {                                    \
+                                MULTITRI_COLORCOPY_LOOP {                                    \
+                                   COLOR_COPYSTMT;                                           \
+                                }                                                            \
+                                pOutColor+=cNumMoreVertsthanTris;                            \
+                            }                                                                \
+                        } else {  /* trifan */                                               \
+                            for (uint j=0;j<nPrims;j++) {                                    \
+                                pOutColor++;                                                 \
+                                MULTITRI_COLORCOPY_LOOP {                                    \
+                                   COLOR_COPYSTMT;                                           \
+                                }                                                            \
+                                pOutColor++;                                                 \
+                            }                                                                \
+                        }                                                                    \
+                    } else {  /* GOURAUD shademode (due to presence of normals) */           \
+                        if (trilisttype==D3DPT_TRIANGLESTRIP) {                              \
+                            for (uint j=0;j<nPrims;j++) {                                    \
+                                MULTITRI_COLORCOPY_LOOP {                                    \
+                                   COLOR_COPYSTMT;                                           \
+                                }                                                            \
+                                DWORD lastcolr = *(pOutColor-1);                             \
+                                *pOutColor++ = lastcolr;                                     \
+                                *pOutColor++ = lastcolr;                                     \
+                            }                                                                \
+                        } else {  /* trifan */                                               \
+                            for (uint j=0;j<nPrims;j++) {                                    \
+                                COLOR_COPYSTMT;                                              \
+                                pOutColor++;                                                 \
+                                MULTITRI_COLORCOPY_LOOP {                                    \
+                                   COLOR_COPYSTMT;                                           \
+                                }                                                            \
+                                *pOutColor++ = *(pOutColor-1);                               \
+                            }                                                                \
+                        }                                                                    \
+                    }                                                                        \
+                  }
+
+                if(!_color_transform_required) {
+                  COMPONENT_COLOR_COPY_LOOPS(COLOR_CONVERT_COPY_STMT);
+                } else {
+                  COMPONENT_COLOR_COPY_LOOPS(COLOR_CONVERT_XFORM_STMT);
+                }
+            } else {
+#ifdef _DEBUG
+                nassertv(ColorBinding==G_OVERALL);
+#endif
+                // copy the one global color in, set stride to 0
+
+                if(!_color_transform_required) {
+                    if (bDoGlobalSceneGraphColor) {
+                        Colorf colr = catt->get_color();
+                        *pConvertedColorArray = Colorf_to_D3DCOLOR(colr);
+                    } else {
+                        *pConvertedColorArray = Colorf_to_D3DCOLOR(*pInColor);
+                    }
+                } else {
+                    if (bDoGlobalSceneGraphColor) {
+                        Colorf colr = catt->get_color();
+                        transform_color(colr,*pConvertedColorArray);
+                    } else {
+                        transform_color(*pInColor,*pConvertedColorArray);
+                    }
+                }
+
+                dps_data.diffuse.dwStride = 0;
+            }
+        }
+
+        if ((TexCoordBinding != G_OFF) && _texturing_enabled) {
+
+#ifdef _DEBUG
+            nassertv(TexCoordBinding == G_PER_VERTEX);  // only sensible choice for a tri
+#endif
+
+            dps_data.textureCoords[0].lpvData = (VOID*)texcoords;
+            dps_data.textureCoords[0].dwStride = sizeof(TexCoordf);
+            fvf_flags |= (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+        }
+
+        set_shademode(NeededShadeMode);
+
+        for (uint j=0;j<nPrims;j++) {
+            const uint cCurNumStripVerts = pLengthArr[j];
+
+            hr = _d3dDevice->DrawPrimitiveStrided(trilisttype, fvf_flags, &dps_data, cCurNumStripVerts, NULL);
+            TestDrawPrimFailure(DrawPrimStrided,hr,_pDD,cCurNumStripVerts,cCurNumStripVerts-2);
+
+            dps_data.position.lpvData = (VOID*)(((char*) dps_data.position.lpvData) + cCurNumStripVerts*dps_data.position.dwStride);
+            dps_data.diffuse.lpvData = (VOID*)(((char*) dps_data.diffuse.lpvData) + cCurNumStripVerts*dps_data.diffuse.dwStride);
+            dps_data.normal.lpvData = (VOID*)(((char*) dps_data.normal.lpvData) + cCurNumStripVerts*dps_data.normal.dwStride);
+            dps_data.textureCoords[0].lpvData = (VOID*)(((char*) dps_data.textureCoords[0].lpvData) + cCurNumStripVerts*dps_data.textureCoords[0].dwStride);
+        }
+
+        nassertv(_pCurFvfBufPtr == NULL);
+    }
+}
+
+//-----------------------------------------------------------------------------
+// Name: GenerateSphere()
+// Desc: Makes vertex and index data for ellipsoid w/scaling factors sx,sy,sz
+//       tries to match gluSphere behavior
+//-----------------------------------------------------------------------------
+
+void DXGraphicsStateGuardian::
+GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize,
+               void *pIndexSpace,DWORD dwIndexSpaceByteSize,
+               D3DVECTOR *pCenter, float fRadius,
+               DWORD wNumRings, DWORD wNumSections, float sx, float sy, float sz,
+               DWORD *pNumVertices,DWORD *pNumIndices,DWORD fvfFlags,DWORD dwVertSize) {
+
+    float x, y, z, rsintheta;
+    D3DVECTOR vPoint;
+
+//#define DBG_GENSPHERE
+#define M_PI 3.1415926f   // probably should get this from mathNumbers.h instead
+
+    nassertv(wNumRings>=2 && wNumSections>=2);
+    wNumRings--;  // wNumRings indicates number of vertex rings (not tri-rings).
+                  // gluSphere 'stacks' arg for 1 vert ring is 2, so convert to our '1'.
+    wNumSections++;  // to make us equiv to gluSphere
+
+    //Figure out needed space for the triangles and vertices.
+    DWORD dwNumVertices,dwNumIndices,dwNumTriangles;
+
+#define DOTEXTURING (fvfFlags & D3DFVF_TEXCOUNT_MASK)
+#define DONORMAL (fvfFlags & D3DFVF_NORMAL)
+#define DOCOLOR (fvfFlags & D3DFVF_DIFFUSE)
+
+    if (DOTEXTURING) {
+        // if texturing, we need full rings of identical position verts at poles to hold diff texture coords
+        wNumRings+=2;
+        dwNumVertices = *pNumVertices = wNumRings * wNumSections;
+        dwNumTriangles = (wNumRings-1) * wNumSections * 2;
+    } else {
+        dwNumVertices = *pNumVertices = wNumRings * wNumSections + 2;
+        dwNumTriangles = wNumRings*wNumSections*2;
+    }
+
+    dwNumIndices = *pNumIndices = dwNumTriangles*3;
+
+    D3DVERTEX* pvVertices = (D3DVERTEX*) pVertexSpace;
+    WORD *pwIndices = (WORD *) pIndexSpace;
+
+    nassertv(dwNumVertices*dwVertSize < VERT_BUFFER_SIZE);
+    nassertv(dwNumIndices < D3DMAXNUMVERTICES );
+
+    // Generate vertex at the top point
+    D3DVECTOR vTopPoint  = *pCenter + D3DVECTOR( 0.0f, +sy*fRadius, 0.0f);
+    D3DVECTOR vBotPoint  = *pCenter + D3DVECTOR( 0.0f, -sy*fRadius, 0.0f);
+    D3DVECTOR vNormal = D3DVECTOR( 0.0f, 1.0f, 0.0f );
+    float texCoords[2];
+
+    nassertv(pVertexSpace==_pCurFvfBufPtr);  // add_to_FVFBuf requires this
+
+#define ADD_GENSPHERE_VERTEX_TO_BUFFER(VERT)                      \
+    add_to_FVFBuf((void *)&(VERT), sizeof(D3DVECTOR));            \
+    if(fvfFlags & D3DFVF_NORMAL)                                  \
+        add_to_FVFBuf((void *)&vNormal, sizeof(D3DVECTOR));       \
+    if(fvfFlags & D3DFVF_DIFFUSE)                                 \
+        add_DWORD_to_FVFBuf(_curD3Dcolor);                              \
+    if(fvfFlags & D3DFVF_TEXCOUNT_MASK)                           \
+        add_to_FVFBuf((void *)texCoords, sizeof(TexCoordf));
+
+#ifdef DBG_GENSPHERE
+    int nvs_written=0;
+    memset(pVertexSpace,0xFF,dwNumVertices*dwVertSize);
+#endif
+
+    if (! DOTEXTURING) {
+        ADD_GENSPHERE_VERTEX_TO_BUFFER(vTopPoint);
+#ifdef DBG_GENSPHERE
+        nvs_written++;
+#endif
+    }
+
+    // Generate vertex points for rings
+    float inv_radius = 1.0f/fRadius;
+    const float reciprocal_PI=1.0f/M_PI;
+    const float reciprocal_2PI=1.0f/(2.0*M_PI);
+    DWORD i;
+    float theta,dtheta;
+
+    if (DOTEXTURING) {
+        // numRings already includes 1st and last rings for this case
+        dtheta = (float)(M_PI / (wNumRings-1));     //Angle between each ring (ignore 2 fake rings)
+        theta = 0.0f;
+    } else {
+        dtheta = (float)(M_PI / (wNumRings + 1));   //Angle between each ring
+        theta = dtheta;
+    }
+    float phi,dphi   = (float)(2*M_PI / (wNumSections-1)); //Angle between each section
+
+    for (i = 0; i < wNumRings; i++) {
+        float costheta,sintheta,cosphi,sinphi;
+        phi =   0.0f;
+
+        if (DOTEXTURING) {
+            texCoords[1] = theta * reciprocal_PI;  // v is the same for each ring
+        }
+
+        // could optimize all this sin/cos stuff w/tables
+        csincos(theta,&sintheta,&costheta);
+        y = fRadius * costheta;     // y is the same for each ring
+
+        rsintheta = fRadius * sintheta;
+
+        for (DWORD j = 0; j < wNumSections; j++) {
+            csincos(phi,&sinphi,&cosphi);
+            x = rsintheta * sinphi;
+            z = rsintheta * cosphi;
+
+#ifdef DBG_GENSPHERE
+            nvs_written++;
+#endif
+            vPoint = *pCenter + D3DVECTOR( sx*x, sy*y, sz*z );
+
+            add_to_FVFBuf((void *)&vPoint, sizeof(D3DVECTOR));
+
+            if (DONORMAL) {
+                // this is wrong normal for the non-spherical case (i think you need to multiply by 1/scale factor per component)
+                vNormal = Normalize(D3DVECTOR( x*inv_radius, y*inv_radius, z*inv_radius ));
+                add_to_FVFBuf((void *)&vNormal, sizeof(D3DVECTOR));
+            }
+
+            if (DOCOLOR)
+                add_DWORD_to_FVFBuf(_curD3Dcolor);
+
+            if (DOTEXTURING) {
+                texCoords[0] = 1.0f - phi*reciprocal_2PI;
+                add_to_FVFBuf((void *)texCoords, sizeof(TexCoordf));
+            }
+
+            phi += dphi;
+        }
+        theta += dtheta;
+    }
+
+    if (! DOTEXTURING) {
+        // Generate bottom vertex
+        vNormal = D3DVECTOR( 0.0f, -1.0f, 0.0f );
+        ADD_GENSPHERE_VERTEX_TO_BUFFER(vBotPoint);
+#ifdef DBG_GENSPHERE
+        nvs_written++;
+#endif
+    }
+
+#ifdef DBG_GENSPHERE
+    assert(nvs_written == dwNumVertices);
+#endif
+
+
+#ifdef DBG_GENSPHERE
+    memset(pwIndices,0xFF,dwNumIndices*sizeof(WORD));
+#endif
+
+    // inited for textured case
+    DWORD cur_vertring_startidx=0;    // first vertex in current ring
+    DWORD CurFinalTriIndex = 0;       // index of next tri to be written
+
+    if (! DOTEXTURING) {
+        // Generate caps using unique the bot/top vert
+        // for non-textured case, could render the caps as indexed trifans,
+        // but should be no perf difference b/w indexed trilists and indexed trifans
+        // and this has advantage of being aggregable into 1 big DPrim call for whole sphere
+
+        for (i = 0; i < wNumSections; i++) {
+            DWORD TopCapTriIndex=3*i;
+            DWORD BotCapTriIndex=3*(dwNumTriangles - wNumSections + i);
+            DWORD i_incd = ((i + 1) % wNumSections);
+
+            pwIndices[TopCapTriIndex++] = 0;
+            pwIndices[TopCapTriIndex++] = i + 1;
+            pwIndices[TopCapTriIndex] =  i_incd + 1;
+
+            pwIndices[BotCapTriIndex++] = (WORD)( dwNumVertices - 1 );
+            pwIndices[BotCapTriIndex++] = (WORD)( dwNumVertices - 2 - i );
+            pwIndices[BotCapTriIndex] = (WORD)( dwNumVertices - 2 - i_incd);
+        }
+
+        cur_vertring_startidx = 1;          // first vertex in current ring (skip top vert)
+        CurFinalTriIndex = wNumSections;    // index of tri to be written, wNumSections to skip the top cap row
+    }
+
+    DWORD j_incd,base_index;
+
+    // technically we could break into a strip for every row (or 1 big strip connected w/degenerate tris)
+    // but indexed trilists should actually be just as fast on HW
+
+    // Generate triangles for the rings
+    for (i = 0; i < wNumRings-1; i++) {
+        for (DWORD j = 0; j < wNumSections; j++) {
+
+            base_index=3*CurFinalTriIndex;  // final vert index is 3*finaltriindex
+            j_incd=(j+1) % wNumSections;
+
+            DWORD v1_row1_idx,v2_row1_idx,v1_row2_idx,v2_row2_idx;
+
+            v1_row1_idx = cur_vertring_startidx + j;
+            v2_row1_idx = cur_vertring_startidx + j_incd;
+            v1_row2_idx = v1_row1_idx + wNumSections;
+            v2_row2_idx = v2_row1_idx + wNumSections;
+
+#ifdef DBG_GENSPHERE
+            assert(v2_row2_idx<dwNumVertices);
+            assert(v1_row2_idx<dwNumVertices);
+            assert(v2_row1_idx<dwNumVertices);
+            assert(v1_row1_idx<dwNumVertices);
+#endif
+
+            pwIndices[base_index++] = v1_row1_idx;
+            pwIndices[base_index++] = v1_row2_idx;
+            pwIndices[base_index++] = v2_row2_idx;
+
+            pwIndices[base_index++] = v1_row1_idx;
+            pwIndices[base_index++] = v2_row2_idx;
+            pwIndices[base_index++] = v2_row1_idx;
+
+            CurFinalTriIndex += 2;  // we wrote 2 tris, add 2 to finaltriindex
+        }
+        cur_vertring_startidx += wNumSections;
+    }
+
+#ifdef DBG_GENSPHERE
+    if (DOTEXTURING) {
+        assert(CurFinalTriIndex == dwNumTriangles);
+        assert(base_index == dwNumIndices);
+    } else {
+        assert(CurFinalTriIndex == dwNumTriangles-wNumSections);
+        assert(base_index == dwNumIndices-wNumSections*3);
+    }
+
+    for (i = 0; i < dwNumIndices; i++)
+        assert(pwIndices[i] <dwNumVertices);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_sphere
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_sphere(GeomSphere *geom, GeomContext *gc) {
+
+#define SPHERE_NUMSLICES 16
+#define SPHERE_NUMSTACKS 10
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug() << "draw_sphere()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+    int nprims = geom->get_num_prims();
+
+    if (nprims==0) {
+        dxgsg_cat.warning() << "draw_sphere() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    Geom::ColorIterator ci;
+    bool bperPrimColor = (geom->get_binding(G_COLOR) == G_PER_PRIM);
+    if (bperPrimColor)
+        ci = geom->make_color_iterator();
+
+    for (int i = 0; i < nprims; i++) {
+
+        DWORD nVerts,nIndices;
+        Vertexf center = geom->get_next_vertex(vi);
+        Vertexf edge = geom->get_next_vertex(vi);
+        LVector3f v = edge - center;
+        float fRadius = sqrt(dot(v, v));
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        _pCurFvfBufPtr = _pFvfBufBasePtr;
+
+        if (bperPrimColor) {
+            GET_NEXT_COLOR();
+        }
+
+        GenerateSphere(_pCurFvfBufPtr, VERT_BUFFER_SIZE,
+                       _index_buf, D3DMAXNUMVERTICES,
+                       (D3DVECTOR *)&center, fRadius,
+                       SPHERE_NUMSTACKS, SPHERE_NUMSLICES,
+                       1.0f, 1.0f, 1.0f,  // no scaling factors, do a sphere not ellipsoid
+                       &nVerts,&nIndices,_curFVFflags,vertex_size);
+
+        // possible optimization: make DP 1 for all spheres call here, since trilist is independent tris.
+        // indexes couldnt start w/0 tho, need to pass offset to gensph
+        HRESULT hr = _d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,  _curFVFflags, _pFvfBufBasePtr, nVerts, _index_buf,nIndices,NULL);
+        TestDrawPrimFailure(DrawIndexedPrim,hr,_pDD,nVerts,(nIndices>>2));
+    }
+
+    _pCurFvfBufPtr = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::prepare_texture
+//       Access: Public, Virtual
+//  Description: Creates a new retained-mode representation of the
+//               given texture, and returns a newly-allocated
+//               TextureContext pointer to reference it.  It is the
+//               responsibility of the calling function to later
+//               call release_texture() with this same pointer (which
+//               will also delete the pointer).
+////////////////////////////////////////////////////////////////////
+TextureContext *DXGraphicsStateGuardian::
+prepare_texture(Texture *tex) {
+
+    DXTextureContext *gtc = new DXTextureContext(tex);
+#ifdef WBD_GL_MODE
+    glGenTextures(1, &gtc->_index);
+
+    bind_texture(gtc);
+    glPrioritizeTextures(1, &gtc->_index, &gtc->_priority);
+    specify_texture(tex);
+    apply_texture_immediate(tex);
+#else
+    if (gtc->CreateTexture(_d3dDevice,_cNumTexPixFmts,_pTexPixFmts) == NULL) {
+        delete gtc;
+        return NULL;
+    }
+#endif              // WBD_GL_MODE
+
+    bool inserted = mark_prepared_texture(gtc);
+
+    // If this assertion fails, the same texture was prepared twice,
+    // which shouldn't be possible, since the texture itself should
+    // detect this.
+    nassertr(inserted, NULL);
+
+    return gtc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_texture
+//       Access: Public, Virtual
+//  Description: Makes the texture the currently available texture for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+apply_texture(TextureContext *tc) {
+    if (tc==NULL) {
+        return;  // use enable_texturing to disable/enable
+    }
+    #ifdef DO_PSTATS
+       add_to_texture_record(tc);
+    #endif
+
+//  bind_texture(tc);
+
+//  specify_texture(tc->_texture);
+    // Note: if this code changes, make sure to change initialization SetTSS code in dx_init as well
+    // so DX TSS renderstate matches dxgsg state
+
+    DXTextureContext *dtc = DCAST(DXTextureContext, tc);
+
+    int dirty = dtc->get_dirty_flags();
+
+    if (dirty) {
+      // If the texture image has changed, or if its use of mipmaps has
+      // changed, we need to re-create the image.  Ignore other types of 
+      // changes, which arent significant for dx
+
+      if((dirty & (Texture::DF_image | Texture::DF_mipmap)) != 0) {
+          // If this is *only* because of a mipmap change, issue a
+          // warning--it is likely that this change is the result of an
+          // error or oversight.
+          if ((dirty & Texture::DF_image) == 0) {
+            dxgsg_cat.warning()
+              << "Texture " << *dtc->_texture << " has changed mipmap state.\n";
+          }
+    
+          dtc->DeleteTexture();
+          if (dtc->CreateTexture(_d3dDevice,_cNumTexPixFmts,_pTexPixFmts) == NULL) {
+            // Oops, we can't re-create the texture for some reason.
+            dxgsg_cat.error()
+              << "Unable to re-create texture " << *dtc->_texture << endl;
+    
+            release_texture(dtc);
+            enable_texturing(false);
+            return;
+          }
+      }
+      dtc->clear_dirty_flags();
+    } else {
+       if(_pCurTexContext == dtc) {
+          return;  // tex already set (and possible problem in state-sorting?)
+       }
+    }
+
+    Texture *tex = tc->_texture;
+    Texture::WrapMode wrapU,wrapV;
+    wrapU=tex->get_wrapu();
+    wrapV=tex->get_wrapv();
+
+    if (wrapU!=_CurTexWrapModeU) {
+        _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSU,get_texture_wrap_mode(wrapU));
+        _CurTexWrapModeU = wrapU;
+    }
+    if (wrapV!=_CurTexWrapModeV) {
+        _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSV,get_texture_wrap_mode(wrapV));
+        _CurTexWrapModeV = wrapV;
+    }
+
+/*
+#ifdef _DEBUG
+    Texture::WrapMode wrapval;
+    _d3dDevice->GetTextureStageState(0,D3DTSS_ADDRESSU,(DWORD*)&wrapval);
+    assert(get_texture_wrap_mode(wrapU) == wrapval);
+    _d3dDevice->GetTextureStageState(0,D3DTSS_ADDRESSV,(DWORD*)&wrapval);
+    assert(get_texture_wrap_mode(wrapV) == wrapval);
+#endif
+*/
+
+    uint aniso_degree=tex->get_anisotropic_degree();
+    Texture::FilterType ft=tex->get_magfilter();
+
+    if (aniso_degree<=1) {
+        if (_CurTexMagFilter!=ft) {
+
+            _CurTexMagFilter = ft;
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER,(ft==Texture::FT_nearest)? D3DTFG_POINT : D3DTFG_LINEAR);
+#ifdef _DEBUG
+            if((ft!=Texture::FT_linear)&&(ft!=Texture::FT_nearest)) {
+                dxgsg_cat.error() << "MipMap filter type setting for texture magfilter makes no sense,  texture: " << tex->get_name() << "\n";
+            }
+#endif
+        }
+    } else {
+        if (aniso_degree!=_CurTexAnisoDegree) {
+            _CurTexAnisoDegree = aniso_degree;
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_ANISOTROPIC );
+            _d3dDevice->SetTextureStageState(0, D3DTSS_MAXANISOTROPY,aniso_degree);
+        }
+    }
+
+#ifdef _DEBUG
+    assert(Texture::FT_linear_mipmap_linear < 8);
+#endif
+/*
+ enum FilterType {
+    FT_nearest,FT_linear,FT_nearest_mipmap_nearest,FT_linear_mipmap_nearest,
+    FT_nearest_mipmap_linear, FT_linear_mipmap_linear, };
+*/
+ static D3DTEXTUREMINFILTER PandaToD3DMinType[8] =
+    {D3DTFN_POINT,D3DTFN_LINEAR,D3DTFN_POINT,D3DTFN_LINEAR,D3DTFN_POINT,D3DTFN_LINEAR};
+ static D3DTEXTUREMIPFILTER PandaToD3DMipType[8] =
+    {D3DTFP_NONE,D3DTFP_NONE,D3DTFP_POINT,D3DTFP_POINT,D3DTFP_LINEAR,D3DTFP_LINEAR};
+
+    ft=tex->get_minfilter();
+
+    if ((ft!=_CurTexMinFilter)||(aniso_degree!=_CurTexAnisoDegree)) {
+
+#ifdef _DEBUG
+        if(ft > Texture::FT_linear_mipmap_linear) {
+                dxgsg_cat.error() << "Unknown tex filter type for tex: " << tex->get_name() << "  filter: "<<(DWORD)ft<<"\n";
+                return;
+        }
+#endif
+
+        D3DTEXTUREMINFILTER minfilter = PandaToD3DMinType[(DWORD)ft];
+        D3DTEXTUREMIPFILTER mipfilter = PandaToD3DMipType[(DWORD)ft];
+
+        #ifndef NDEBUG
+            extern char *PandaFilterNameStrs[];
+            if((!(dtc->_bHasMipMaps))&&(mipfilter!=D3DTFP_NONE)) {
+                dxgsg_cat.error() << "Trying to set mipmap filtering for texture with no generated mipmaps!! texname[" << tex->get_name() << "], filter("<<PandaFilterNameStrs[ft]<<")\n";
+                mipfilter=D3DTFP_NONE;
+            }
+        #endif
+
+        if (aniso_degree>1) {
+            minfilter=D3DTFN_ANISOTROPIC;
+        }
+
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, minfilter);
+        _d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, mipfilter);
+
+        _CurTexMinFilter = ft;
+        _CurTexAnisoDegree = aniso_degree;
+    }
+
+    // bugbug:  does this handle the case of untextured geometry?
+    //          we dont see this bug cause we never mix textured/untextured
+
+    _d3dDevice->SetTexture(0,dtc->_surface);
+
+#if 0
+    if (dtc!=NULL) {
+        dxgsg_cat.spam() << "Setting active DX texture: " << dtc->_tex->get_name() << "\n";
+    }
+#endif
+
+    _pCurTexContext = dtc;   // enable_texturing needs this
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::release_texture
+//       Access: Public, Virtual
+//  Description: Frees the GL resources previously allocated for the
+//               texture.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+release_texture(TextureContext *tc) {
+    DXTextureContext *gtc = DCAST(DXTextureContext, tc);
+    Texture *tex = tc->_texture;
+
+    gtc->DeleteTexture();
+    bool erased = unmark_prepared_texture(gtc);
+
+    // If this assertion fails, a texture was released that hadn't been
+    // prepared (or a texture was released twice).
+    nassertv(erased);
+
+    tex->clear_gsg(this);
+
+    delete gtc;
+}
+
+#if 1
+
+void DXGraphicsStateGuardian::
+copy_texture(TextureContext *tc, const DisplayRegion *dr) {
+    dxgsg_cat.fatal() << "DX copy_texture unimplemented!!!";
+}
+
+#else
+static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
+    4096, 0};
+
+// This function returns the smallest power of two greater than or
+// equal to x.
+static int binary_log_cap(const int x) {
+    int i = 0;
+    for (; (x > logs[i]) && (logs[i] != 0); ++i);
+    if (logs[i] == 0)
+        return 4096;
+    return logs[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::copy_texture
+//       Access: Public, Virtual
+//  Description: Copy the pixel region indicated by the display
+//       region from the framebuffer into texture memory
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+copy_texture(TextureContext *tc, const DisplayRegion *dr) {
+
+    nassertv(tc != NULL && dr != NULL);
+
+    Texture *tex = tc->_texture;
+
+    // Determine the size of the grab from the given display region
+    // If the requested region is not a power of two, grab a region that is
+    // a power of two that contains the requested region
+    int xo, yo, req_w, req_h;
+    dr->get_region_pixels(xo, yo, req_w, req_h);
+    int w = binary_log_cap(req_w);
+    int h = binary_log_cap(req_h);
+    if (w != req_w || h != req_h) {
+        tex->_requested_w = req_w;
+        tex->_requested_h = req_h;
+        tex->_has_requested_size = true;
+    }
+
+    PixelBuffer *pb = tex->_pbuffer;
+
+    pb->set_xorg(xo);
+    pb->set_yorg(yo);
+    pb->set_xsize(w);
+    pb->set_ysize(h);
+
+
+    bind_texture(tc);
+    glCopyTexImage2D( GL_TEXTURE_2D, tex->get_level(),
+                      get_internal_image_format(pb->get_format()),
+                      pb->get_xorg(), pb->get_yorg(),
+                      pb->get_xsize(), pb->get_ysize(), pb->get_border() );
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::copy_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+copy_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) {
+    dxgsg_cat.fatal() << "DX copy_texture unimplemented!!!";
+    return;
+
+    set_read_buffer(rb);
+    copy_texture(tc, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_texture(TextureContext *tc, const DisplayRegion *dr) {
+
+    dxgsg_cat.fatal() << "DXGSG draw_texture unimplemented!!!";
+    return;
+
+    nassertv(tc != NULL && dr != NULL);
+
+#ifdef WBD_GL_MODE
+    Texture *tex = tc->_texture;
+
+    DisplayRegionStack old_dr = push_display_region(dr);
+    prepare_display_region();
+
+    NodeTransitions state;
+    CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_none);
+    DepthTestTransition *dta = new DepthTestTransition(DepthTestProperty::M_none);
+    DepthWriteTransition *dwa = new DepthWriteTransition(DepthWriteTransition::off());
+    TextureTransition *ta = new TextureTransition(tex);
+    TextureApplyTransition *taa = new TextureApplyTransition(TextureApplyProperty::M_decal);
+
+    state.set_transition(new LightTransition);
+    state.set_transition(new ColorMaskTransition);
+    state.set_transition(new RenderModeTransition);
+    state.set_transition(new TexMatrixTransition);
+    state.set_transition(new TransformTransition);
+    state.set_transition(new ColorBlendTransition);
+    state.set_transition(cfa);
+    state.set_transition(dta);
+    state.set_transition(dwa);
+    state.set_transition(ta);
+    state.set_transition(taa);
+    modify_state(state);
+
+    // We set up an orthographic projection that defines our entire
+    // viewport to the range [0..1] in both dimensions.  Then, when we
+    // create a unit square polygon below, it will exactly fill the
+    // viewport (and thus exactly fill the display region).
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    gluOrtho2D(0, 1, 0, 1);
+
+    float txl, txr, tyt, tyb;
+    txl = tyb = 0.;
+    if (tex->_has_requested_size) {
+        txr = ((float)(tex->_requested_w)) / ((float)(tex->_pbuffer->get_xsize()));
+        tyt = ((float)(tex->_requested_h)) / ((float)(tex->_pbuffer->get_ysize()));
+    } else {
+        txr = tyt = 1.;
+    }
+
+    // This two-triangle strip is actually a quad.  But it's usually
+    // better to render quads as tristrips anyway.
+    glBegin(GL_TRIANGLE_STRIP);
+    glTexCoord2f(txl, tyb);   glVertex2i(0, 0);
+    glTexCoord2f(txr, tyb);   glVertex2i(1, 0);
+    glTexCoord2f(txl, tyt);   glVertex2i(0, 1);
+    glTexCoord2f(txr, tyt);   glVertex2i(1, 1);
+    glEnd();
+
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+
+    pop_display_region(old_dr);
+#endif              // WBD_GL_MODE
+
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) {
+    dxgsg_cat.fatal() << "DXGSG draw_texture unimplemented!!!";
+    return;
+
+    set_draw_buffer(rb);
+    draw_texture(tc, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::texture_to_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) {
+    nassertv(tc != NULL && pb != NULL);
+
+    Texture *tex = tc->_texture;
+
+    int w = tex->_pbuffer->get_xsize();
+    int h = tex->_pbuffer->get_ysize();
+
+    PT(DisplayRegion) dr = _win->make_scratch_display_region(w, h);
+
+    FrameBufferStack old_fb = push_frame_buffer
+                              (get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth),
+                               dr);
+
+    texture_to_pixel_buffer(tc, pb, dr);
+
+    pop_frame_buffer(old_fb);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::texture_to_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
+                        const DisplayRegion *dr) {
+    nassertv(tc != NULL && pb != NULL && dr != NULL);
+
+    Texture *tex = tc->_texture;
+
+    // Do a deep copy to initialize the pixel buffer
+    pb->copy(tex->_pbuffer);
+
+    // If the image was empty, we need to render the texture into the frame
+    // buffer and then copy the results into the pixel buffer's image
+    if (pb->_image.empty()) {
+        int w = pb->get_xsize();
+        int h = pb->get_ysize();
+        draw_texture(tc, dr);
+        pb->_image = PTA_uchar::empty_array(w * h * pb->get_num_components());
+        copy_pixel_buffer(pb, dr);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::copy_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) {
+
+    extern HRESULT ConvertDDSurftoPixBuf(PixelBuffer *pixbuf,LPDIRECTDRAWSURFACE7 pDDSurf);
+
+    nassertv(pb != NULL && dr != NULL);
+
+    int xo, yo, w, h;
+    dr->get_region_pixels(xo, yo, w, h);
+
+    // only handled simple case
+    nassertv(xo==0);
+    nassertv(yo==0);
+    nassertv(w==pb->get_xsize());
+    nassertv(h==pb->get_ysize());
+
+/*
+    set_pack_alignment(1);
+    glReadPixels( pb->get_xorg() + xo, pb->get_yorg() + yo,
+                  pb->get_xsize(), pb->get_ysize(),
+                  get_external_image_format(pb->get_format()),
+                  get_image_type(pb->get_image_type()),
+                  pb->_image.p() );
+*/
+
+
+    (void) ConvertDDSurftoPixBuf(pb,((_cur_read_pixel_buffer & RenderBuffer::T_back) ? _back : _pri));
+
+    nassertv(!pb->_image.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::copy_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                  const RenderBuffer &rb) {
+    set_read_buffer(rb);
+    copy_pixel_buffer(pb, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                  const NodeTransitions& na) {
+
+    dxgsg_cat.fatal() << "DXGSG draw_pixel_buffer unimplemented!!!";
+    return;
+
+#ifdef WBD_GL_MODE
+    nassertv(pb != NULL && dr != NULL);
+    nassertv(!pb->_image.empty());
+
+    DisplayRegionStack old_dr = push_display_region(dr);
+    prepare_display_region();
+
+    NodeTransitions state(na);
+    state.set_transition(new LightTransition);
+    state.set_transition(new TextureTransition);
+    state.set_transition(new TransformTransition);
+    state.set_transition(new ColorBlendTransition);
+    state.set_transition(new StencilTransition);
+
+
+    switch (pb->get_format()) {
+        case PixelBuffer::F_depth_component:
+            {
+                ColorMaskTransition *cma = new ColorMaskTransition(0);
+                DepthTestTransition *dta = new DepthTestTransition(DepthTestProperty::M_always);
+                DepthWriteTransition *dwa = new DepthWriteTransition(DepthWriteTransition::off());
+                state.set_transition(cma);
+                state.set_transition(dta);
+                state.set_transition(dwa);
+            }
+            break;
+
+        case PixelBuffer::F_rgb:
+        case PixelBuffer::F_rgb5:
+        case PixelBuffer::F_rgb8:
+        case PixelBuffer::F_rgb12:
+        case PixelBuffer::F_rgba:
+        case PixelBuffer::F_rgba4:
+        case PixelBuffer::F_rgba8:
+        case PixelBuffer::F_rgba12:
+            {
+                ColorMaskTransition *cma = new ColorMaskTransition;
+                DepthTestTransition *dta = new DepthTestTransition(DepthTestProperty::M_none);
+                DepthWriteTransition *dwa = new DepthWriteTransition(DepthWriteTransition::off());
+                state.set_transition(cma);
+                state.set_transition(dta);
+                state.set_transition(dwa);
+            }
+            break;
+        default:
+            dxgsg_cat.error()
+            << "draw_pixel_buffer(): unknown buffer format" << endl;
+            break;
+    }
+
+    modify_state(state);
+
+    enable_color_material(false);
+    set_unpack_alignment(1);
+
+    glMatrixMode( GL_PROJECTION );
+    glPushMatrix();
+    glLoadIdentity();
+    gluOrtho2D(0, _win->get_width(),
+               0, _win->get_height());
+
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "glDrawPixels(" << pb->get_xsize() << ", " << pb->get_ysize()
+    << ", ";
+    switch (get_external_image_format(pb->get_format())) {
+        case GL_DEPTH_COMPONENT:
+            dxgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, ";
+            break;
+        case GL_RGB:
+            dxgsg_cat.debug(false) << "GL_RGB, ";
+            break;
+        case GL_RGBA:
+            dxgsg_cat.debug(false) << "GL_RGBA, ";
+            break;
+        default:
+            dxgsg_cat.debug(false) << "unknown, ";
+            break;
+    }
+    switch (get_image_type(pb->get_image_type())) {
+        case GL_UNSIGNED_BYTE:
+            dxgsg_cat.debug(false) << "GL_UNSIGNED_BYTE, ";
+            break;
+        case GL_float:
+            dxgsg_cat.debug(false) << "GL_float, ";
+            break;
+        default:
+            dxgsg_cat.debug(false) << "unknown, ";
+            break;
+    }
+    dxgsg_cat.debug(false)
+    << (void *)pb->_image.p() << ")" << endl;
+#endif
+
+    glRasterPos2i( pb->get_xorg(), pb->get_yorg() );
+    glDrawPixels( pb->get_xsize(), pb->get_ysize(),
+                  get_external_image_format(pb->get_format()),
+                  get_image_type(pb->get_image_type()),
+                  pb->_image.p() );
+
+    glMatrixMode( GL_PROJECTION );
+    glPopMatrix();
+
+    pop_display_region(old_dr);
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                  const RenderBuffer &rb, const NodeTransitions& na) {
+    set_read_buffer(rb);
+    draw_pixel_buffer(pb, dr, na);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_material
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::apply_material( const Material* material ) {
+    D3DMATERIAL7 cur_material;
+    cur_material.dcvDiffuse = *(D3DCOLORVALUE *)(material->get_diffuse().get_data());
+    cur_material.dcvAmbient = *(D3DCOLORVALUE *)(material->get_ambient().get_data());
+    cur_material.dcvSpecular = *(D3DCOLORVALUE *)(material->get_specular().get_data());
+    cur_material.dcvEmissive = *(D3DCOLORVALUE *)(material->get_emission().get_data());
+    cur_material.dvPower   =  material->get_shininess();
+    _d3dDevice->SetMaterial(&cur_material);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_fog
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+apply_fog(Fog *fog) {
+
+    if(_doFogType==None)
+      return;
+
+    Fog::Mode panda_fogmode = fog->get_mode();
+    D3DFOGMODE d3dfogmode = get_fog_mode_type(panda_fogmode);
+
+
+    // should probably avoid doing redundant SetRenderStates, but whatever
+    _d3dDevice->SetRenderState((D3DRENDERSTATETYPE)_doFogType, d3dfogmode);
+
+    const Colorf &fog_colr = fog->get_color();
+    _d3dDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR,
+                   MY_D3DRGBA(fog_colr[0], fog_colr[1], fog_colr[2], 0.0f));  // Alpha bits are not used
+
+    // do we need to adjust fog start/end values based on D3DPRASTERCAPS_WFOG/D3DPRASTERCAPS_ZFOG ?
+    // if not WFOG, then docs say we need to adjust values to range [0,1]
+
+    switch (panda_fogmode) {
+        case Fog::M_linear:
+            {
+                // Linear fog may be world-relative or
+                // camera-relative.  The fog object knows how to
+                // decode its parameters into camera-relative
+                // properties.
+                float fog_start, fog_end;
+                fog->compute_linear_range(fog_start, fog_end,
+                                          _current_projection_node,
+                                          _coordinate_system);
+
+                _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGSTART,
+                                            *((LPDWORD) (&fog_start)) );
+                _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGEND,
+                                            *((LPDWORD) (&fog_end)) );
+            }
+            break;
+        case Fog::M_exponential:
+        case Fog::M_exponential_squared:
+            {
+                // Exponential fog is always camera-relative.
+                float fog_density = fog->get_exp_density();
+                _d3dDevice->SetRenderState( D3DRENDERSTATE_FOGDENSITY,
+                            *((LPDWORD) (&fog_density)) );
+            }
+            break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::apply_light( PointLight* light ) {
+    // The light position will be relative to the current matrix, so
+    // we have to know what the current matrix is.  Find a better
+    // solution later.
+    D3DCOLORVALUE black;
+    black.r = black.g = black.b = black.a = 0.0f;
+    D3DLIGHT7  alight;
+    alight.dltType =  D3DLIGHT_POINT;
+    alight.dcvDiffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+    alight.dcvAmbient  =  black ;
+    alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data());
+
+    // Position needs to specify x, y, z, and w
+    // w == 1 implies non-infinite position
+    alight.dvPosition = *(D3DVECTOR *)(get_rel_pos( light, _current_root_node ).get_data());
+
+    alight.dvRange =  D3DLIGHT_RANGE_MAX;
+    alight.dvFalloff =  1.0f;
+
+    alight.dvAttenuation0 = (D3DVALUE)light->get_constant_attenuation();
+    alight.dvAttenuation1 = (D3DVALUE)light->get_linear_attenuation();
+    alight.dvAttenuation2 = (D3DVALUE)light->get_quadratic_attenuation();
+    
+    HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::apply_light( DirectionalLight* light ) {
+    // The light position will be relative to the current matrix, so
+    // we have to know what the current matrix is.  Find a better
+    // solution later.
+    D3DCOLORVALUE black;
+    black.r = black.g = black.b = black.a = 0.0f;
+
+    D3DLIGHT7  alight;
+    ZeroMemory(&alight, sizeof(D3DLIGHT7));
+
+    alight.dltType =  D3DLIGHT_DIRECTIONAL;
+    alight.dcvDiffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+    alight.dcvAmbient  =  black ;
+    alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data());
+
+    alight.dvDirection = *(D3DVECTOR *)
+                         (get_rel_forward( light, _current_projection_node, CS_yup_left).get_data());
+
+    alight.dvRange =  D3DLIGHT_RANGE_MAX;
+    alight.dvFalloff =  1.0f;
+
+    alight.dvAttenuation0 = 1.0f;       // constant
+    alight.dvAttenuation1 = 0.0f;       // linear
+    alight.dvAttenuation2 = 0.0f;       // quadratic
+
+    HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight);
+//    _d3dDevice->LightEnable( _cur_light_id, TRUE );
+//    _d3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, TRUE );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::apply_light( Spotlight* light ) {
+    // The light position will be relative to the current matrix, so
+    // we have to know what the current matrix is.  Find a better
+    // solution later.
+#ifdef WBD_GL_MODE
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "glMatrixMode(GL_MODELVIEW)" << endl;
+    dxgsg_cat.debug()
+    << "glPushMatrix()" << endl;
+    dxgsg_cat.debug()
+    << "glLoadIdentity()" << endl;
+#endif
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_left)
+                  .get_data());
+
+    GLenum id = get_light_id( _cur_light_id );
+    Colorf black(0, 0, 0, 1);
+    glLightfv(id, GL_AMBIENT, black.get_data());
+    glLightfv(id, GL_DIFFUSE, light->get_color().get_data());
+    glLightfv(id, GL_SPECULAR, light->get_specular().get_data());
+
+    // Position needs to specify x, y, z, and w
+    // w == 1 implies non-infinite position
+    LPoint3f pos = get_rel_pos( light, _current_projection_node );
+    LPoint4f fpos( pos[0], pos[1], pos[2], 1 );
+    glLightfv( id, GL_POSITION, fpos.get_data() );
+
+    glLightfv( id, GL_SPOT_DIRECTION,
+               get_rel_forward( light, _current_projection_node,
+                                _coordinate_system ).get_data() );
+    glLightf( id, GL_SPOT_EXPONENT, light->get_exponent() );
+    glLightf( id, GL_SPOT_CUTOFF,
+              light->get_cutoff_angle() );
+    glLightf( id, GL_CONSTANT_ATTENUATION,
+              light->get_constant_attenuation() );
+    glLightf( id, GL_LINEAR_ATTENUATION,
+              light->get_linear_attenuation() );
+    glLightf( id, GL_QUADRATIC_ATTENUATION,
+              light->get_quadratic_attenuation() );
+
+    glPopMatrix();
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "glPopMatrix()" << endl;
+#endif
+#else       // DX Spotlight
+    D3DCOLORVALUE black;
+    black.r = black.g = black.b = black.a = 0.0f;
+
+    D3DLIGHT7  alight;
+    ZeroMemory(&alight, sizeof(D3DLIGHT7));
+
+    alight.dltType =  D3DLIGHT_SPOT;
+    alight.dcvAmbient  =  black ;
+    alight.dcvDiffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+    alight.dcvSpecular = *(D3DCOLORVALUE *)(light->get_specular().get_data());
+
+    alight.dvPosition = *(D3DVECTOR *)
+                        (get_rel_pos( light, _current_root_node ).get_data());
+
+    alight.dvDirection = *(D3DVECTOR *)
+                         (get_rel_forward( light, _current_root_node, _coordinate_system).get_data());
+
+    alight.dvRange =  D3DLIGHT_RANGE_MAX;
+    alight.dvFalloff =  1.0f;
+    alight.dvTheta =  0.0f;
+    alight.dvPhi =  light->get_cutoff_angle();
+
+    alight.dvAttenuation0 = (D3DVALUE)light->get_constant_attenuation(); // constant
+    alight.dvAttenuation1 = (D3DVALUE)light->get_linear_attenuation();   // linear
+    alight.dvAttenuation2 = (D3DVALUE)light->get_quadratic_attenuation();// quadratic
+
+    HRESULT res = _d3dDevice->SetLight(_cur_light_id, &alight);
+
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::apply_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::apply_light( AmbientLight* light ) {
+    _cur_ambient_light = _cur_ambient_light + light->get_color();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_transform
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_transform(const TransformTransition *attrib) {
+#ifndef NDEBUG
+    if (dx_show_transforms) {
+
+        bool lighting_was_enabled = _lighting_enabled;
+        bool texturing_was_enabled = _texturing_enabled;
+        enable_lighting(false);
+        enable_texturing(false);
+
+        typedef struct {
+            D3DVALUE x,y,z;   // position
+            D3DVALUE nx,ny,nz; // normal
+            D3DCOLOR  diff;   // diffuse color
+        }        VERTFORMAT;
+
+        VERTFORMAT vert_buf[] = {
+            {0.0f, 0.0f, 0.0f,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(1.0f, 0.0f, 0.0f, 1.0f)},      // red
+            {3.0, 0.0f, 0.0f,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(1.0f, 0.0f, 0.0f, 1.0f)},       // red
+            {0.0f, 0.0f, 0.0f,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(0.0f, 1.0f, 0.0f, 1.0f)},      // grn
+            {0.0f, 3.0, 0.0f,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(0.0f, 1.0f, 0.0f, 1.0f)},       // grn
+            {0.0f, 0.0f, 0.0f,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(0.0f, 0.0f, 1.0f, 1.0f)},      // blu
+            {0.0f, 0.0f, 3.0,  0.0f, -1.0f, 0.0f,  MY_D3DRGBA(0.0f, 0.0f, 1.0f, 1.0f)},       // blu
+        };
+
+        HRESULT hr = _d3dDevice->DrawPrimitive(D3DPT_LINELIST, D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_NORMAL,
+                                  vert_buf, 6, NULL);
+        TestDrawPrimFailure(DrawPrim,hr,_pDD,6,0);
+
+        enable_lighting(lighting_was_enabled);
+        enable_texturing(texturing_was_enabled);
+    }
+#endif
+
+    _d3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD/*VIEW*/,
+                             (LPD3DMATRIX) attrib->get_matrix().get_data());
+    _bTransformIssued = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_matrix
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_tex_matrix(const TexMatrixTransition *attrib) {
+    dxgsg_cat.fatal() << "DXGSG issue_tex_matrix unimplemented!!!";
+    return;
+
+#ifdef WBD_GL_MODE
+#ifdef GSG_VERBOSE
+    dxgsg_cat.debug()
+    << "glLoadMatrix(GL_TEXTURE): " << attrib->get_matrix() << endl;
+#endif
+    glMatrixMode(GL_TEXTURE);
+    glLoadMatrixf(attrib->get_matrix().get_data());
+#else
+    _d3dDevice->SetTransform( D3DTRANSFORMSTATE_TEXTURE0,
+                              (LPD3DMATRIX)attrib->get_matrix().get_data());
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_color
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_color(const ColorTransition *attrib) {
+    
+    bool bAttribOn=attrib->is_on();
+    bool bIsReal = (bAttribOn ? attrib->is_real() : false);
+
+    _issued_color_enabled = bAttribOn;
+
+    // if an active unreal color is set, disable all color
+    _enable_all_color = !(bAttribOn && (!bIsReal));  
+
+    if(bAttribOn && bIsReal) {
+        _issued_color = attrib->get_color();
+        if(_color_transform_required) {
+            transform_color(_issued_color, _issued_color_D3DCOLOR);
+        } else {
+            _issued_color_D3DCOLOR = Colorf_to_D3DCOLOR(_issued_color);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_color_transform
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_color_transform(const ColorMatrixTransition *attrib) {
+    _current_color_mat = attrib->get_matrix();
+
+    // couldnt we compare a single ptr instead of doing full comparison?
+    // bugbug: the ColorMatrixTransition needs to be an On/Off transition 
+    // so we dont have to do this comparison
+    if (_current_color_mat == LMatrix4f::ident_mat()) { 
+        _color_transform_enabled = false;
+        if(_issued_color_enabled) {
+             _issued_color_D3DCOLOR = Colorf_to_D3DCOLOR(_issued_color);
+        }
+    } else {
+        _color_transform_enabled = true;
+        if(_issued_color_enabled) {
+             transform_color(_issued_color, _issued_color_D3DCOLOR);
+        }
+    }
+    _color_transform_required = _color_transform_enabled || _alpha_transform_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_alpha_transform
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_alpha_transform(const AlphaTransformTransition *attrib) {
+    _current_alpha_offset = attrib->get_offset();
+    _current_alpha_scale = attrib->get_scale();
+
+    if ((_current_alpha_offset == 0.0f) && (_current_alpha_scale == 1.0f)) {
+        _alpha_transform_enabled = false;
+        if(_issued_color_enabled) {
+             _issued_color_D3DCOLOR = Colorf_to_D3DCOLOR(_issued_color);
+        }
+    } else {
+        _alpha_transform_enabled = true;
+        if(_issued_color_enabled) {
+             transform_color(_issued_color, _issued_color_D3DCOLOR);
+        }
+    }
+    _color_transform_required = _color_transform_enabled || _alpha_transform_enabled;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_texture(const TextureTransition *attrib) {
+    if (attrib->is_on()) {
+        enable_texturing(true);
+        Texture *tex = attrib->get_texture();
+        nassertv(tex != (Texture *)NULL);
+        tex->apply(this);
+    } else {
+        enable_texturing(false);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_tex_gen
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_tex_gen(const TexGenTransition *attrib) {
+    dxgsg_cat.fatal() << "DXGSG issue_tex_gen unimplemented!!!";
+    return;
+#ifdef WBD_GL_MODE
+    TexGenProperty::Mode mode = attrib->get_mode();
+
+    switch (mode) {
+        case TexGenProperty::M_none:
+            glDisable(GL_TEXTURE_GEN_S);
+            glDisable(GL_TEXTURE_GEN_T);
+            glDisable(GL_TEXTURE_GEN_Q);
+            glDisable(GL_TEXTURE_GEN_R);
+            break;
+
+        case TexGenProperty::M_texture_projector:
+            {
+                glEnable(GL_TEXTURE_GEN_S);
+                glEnable(GL_TEXTURE_GEN_T);
+                glEnable(GL_TEXTURE_GEN_Q);
+                glEnable(GL_TEXTURE_GEN_R);
+                const LMatrix4f &plane = attrib->get_plane();
+                glTexGenfv(GL_S, GL_OBJECT_PLANE, plane.get_row(0).get_data());
+                glTexGenfv(GL_T, GL_OBJECT_PLANE, plane.get_row(1).get_data());
+                glTexGenfv(GL_R, GL_OBJECT_PLANE, plane.get_row(2).get_data());
+                glTexGenfv(GL_Q, GL_OBJECT_PLANE, plane.get_row(3).get_data());
+                glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+                glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+                glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+                glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+            }
+            break;
+
+        case TexGenProperty::M_sphere_map:
+            glEnable(GL_TEXTURE_GEN_S);
+            glEnable(GL_TEXTURE_GEN_T);
+            glDisable(GL_TEXTURE_GEN_Q);
+            glDisable(GL_TEXTURE_GEN_R);
+            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+            break;
+
+        default:
+            dxgsg_cat.error()
+            << "Unknown texgen mode " << (int)mode << endl;
+            break;
+    }
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_material
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_material(const MaterialTransition *attrib) {
+    if (attrib->is_on()) {
+        const Material *material = attrib->get_material();
+        nassertv(material != (const Material *)NULL);
+        apply_material(material);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_fog
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_fog(const FogTransition *attrib) {
+
+    if (attrib->is_on()) {
+        enable_fog(true);
+        Fog *fog = attrib->get_fog();
+        nassertv(fog != (Fog *)NULL);
+        fog->apply(this);
+    } else {
+        enable_fog(false);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_render_mode
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_render_mode(const RenderModeTransition *attrib) {
+
+    RenderModeProperty::Mode mode = attrib->get_mode();
+
+    if(mode==_current_fill_mode) 
+      return;
+
+    switch (mode) {
+        case RenderModeProperty::M_filled:
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID);
+            break;
+
+        case RenderModeProperty::M_wireframe:
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_WIREFRAME);
+            break;
+
+        default:
+            dxgsg_cat.error()
+            << "Unknown render mode " << (int)mode << endl;
+    }
+
+    _current_fill_mode = mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::issue_light(const LightTransition *attrib ) {
+  nassertv(attrib->get_default_dir() != TD_on);
+
+  // Initialize the current ambient light total and currently enabled
+  // light list
+  _cur_ambient_light.set(0.0f, 0.0f, 0.0f, 1.0f);
+  int i;
+  for (i = 0; i < _max_lights; i++)
+    _cur_light_enabled[i] = false;
+
+  int num_enabled = 0;
+  LightTransition::const_iterator li;
+  for (li = attrib->begin(); li != attrib->end(); ++li) {
+    Light *light = (*li).first;
+    nassertv(light != (Light *)NULL);
+    TransitionDirection dir = (*li).second;
+ 
+    if (dir == TD_on) {
+      num_enabled++;
+      enable_lighting(true);
+
+      _cur_light_id = -1;
+            
+      // Ambient lights don't require specific light ids
+      // Simply add in the ambient contribution to the current total
+      if (light->get_light_type() == AmbientLight::get_class_type()) {
+        light->apply(this);
+        // We need to indicate that no light id is necessary because
+        // it's an ambient light
+        _cur_light_id = -2;
+      }
+
+      // Check to see if this light has already been bound to an id
+      for (i = 0; i < _max_lights; i++) {
+        if (_available_light_ids[i] == light) {
+          // Light has already been bound to an id, we only need
+          // to enable the light, not apply it
+          _cur_light_id = -2;
+          if (enable_light(i, true))  _cur_light_enabled[i] = true;
+          break;
+        }
+      }
+
+      // See if there are any unbound light ids
+      if (_cur_light_id == -1) {
+        for (i = 0; i < _max_lights; i++) {
+          if (_available_light_ids[i] == NULL) {
+            _available_light_ids[i] = light;
+            _cur_light_id = i;
+            break;
+          }
+        }
+      }
+
+      // If there were no unbound light ids, see if we can replace
+      // a currently unused but previously bound id
+      if (_cur_light_id == -1) {
+        for (i = 0; i < _max_lights; i++) {
+          if (attrib->is_off(_available_light_ids[i])) {
+            _available_light_ids[i] = light;
+            _cur_light_id = i;
+            break;
+          }
+        }
+      }
+
+      if (_cur_light_id >= 0) {
+        if (enable_light(_cur_light_id, true))  _cur_light_enabled[_cur_light_id] = true;
+
+        // We need to do something different for each type of light
+        light->apply(this);
+      } else if (_cur_light_id == -1) {
+        dxgsg_cat.error()
+          << "issue_light() - failed to bind light to id" << endl;
+      }
+    }
+  }
+
+  // Disable all unused lights
+  for (i = 0; i < _max_lights; i++) {
+    if (!_cur_light_enabled[i])
+      enable_light(i, false);
+  }
+  
+  // If no lights were enabled, disable lighting
+  if (num_enabled == 0) {
+    enable_lighting(false);
+    enable_color_material(false);
+  } else {
+    call_dxLightModelAmbient(_cur_ambient_light);
+    enable_color_material(true);
+  }
+}
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::reset_ambient
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+reset_ambient() {
+    _lmodel_ambient += 2.0f;
+}
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_color_blend       //
+//       Access: Public, Virtual                                  //
+//  Description:                                                  //
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_color_blend(const ColorBlendTransition *attrib) {
+    
+    ColorBlendProperty::Mode mode = attrib->get_mode();
+
+    switch (mode) {
+        case ColorBlendProperty::M_none:
+            enable_blend(false);
+            break;
+        case ColorBlendProperty::M_multiply:
+            enable_blend(true);
+            call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ZERO);
+            break;
+        case ColorBlendProperty::M_add:
+            enable_blend(true);
+            call_dxBlendFunc(D3DBLEND_ONE, D3DBLEND_ONE);
+            break;
+        case ColorBlendProperty::M_multiply_add:
+            enable_blend(true);
+            call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ONE);
+            break;
+        case ColorBlendProperty::M_alpha:
+            enable_blend(true);
+            call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
+            break;
+        default:
+            dxgsg_cat.error()
+            << "Unknown color blend mode " << (int)mode << endl;
+            break;
+    }
+}
+
+void DXGraphicsStateGuardian::SetTextureBlendMode(TextureApplyProperty::Mode TexBlendMode,bool bCanJustEnable) {
+
+/*class TextureApplyProperty {
+  enum Mode {
+    M_modulate,M_decal,M_blend,M_replace,M_add};
+*/
+    static D3DTEXTUREOP TexBlendColorOp1[/* TextureApplyProperty::Mode maxval*/ 10] =
+    {D3DTOP_MODULATE,D3DTOP_BLENDTEXTUREALPHA,D3DTOP_MODULATE,D3DTOP_SELECTARG1,D3DTOP_ADD};
+
+    //if bCanJustEnable, then we only need to make sure ColorOp is turned on and set properly
+    if (bCanJustEnable && (TexBlendMode==_CurTexBlendMode)) {
+        // just reset COLOROP 0 to enable pipeline, rest is already set properly
+        _d3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, TexBlendColorOp1[TexBlendMode] );
+        return;
+    }
+
+    _d3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, TexBlendColorOp1[TexBlendMode] );
+
+    switch (TexBlendMode) {
+
+        case TextureApplyProperty::M_modulate:
+            // emulates GL_MODULATE glTexEnv mode
+            // want to multiply tex-color*pixel color to emulate GL modulate blend (see glTexEnv)
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyProperty::M_decal:
+            // emulates GL_DECAL glTexEnv mode
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyProperty::M_replace:
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            break;
+        case TextureApplyProperty::M_add:
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+            // since I'm making up 'add' mode, use modulate.  "adding" alpha never makes sense right?
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyProperty::M_blend:
+            dxgsg_cat.error()
+            << "Impossible to emulate GL_BLEND in DX exactly " << (int) TexBlendMode << endl;
+/*
+           // emulate GL_BLEND glTexEnv
+
+           GL requires 2 independent operations on 3 input vars for this mode
+           DX texture pipeline requires re-using input of last stage on each new op, so I dont think
+           exact emulation is possible
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT );
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+           _d3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+           need to SetTexture(1,tex) also
+           _d3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE ); wrong
+           _d3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+           _d3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TFACTOR );
+
+           _d3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+           _d3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_CURRENT );
+*/
+
+
+            break;
+        default:
+            dxgsg_cat.error() << "Unknown texture blend mode " << (int) TexBlendMode << endl;
+            break;
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::enable_texturing
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian::
+enable_texturing(bool val) {
+//  if (_texturing_enabled == val) {  // this check is mostly for internal gsg calls, panda already screens out redundant state changes
+//        return;
+//  }
+
+    _texturing_enabled = val;
+
+//  assert(_pCurTexContext!=NULL);  we're definitely called with it NULL for both true and false
+//  I'm going to allow enabling texturing even if no tex has been set yet, seems to cause no probs
+
+    if (val == FALSE) {
+        _d3dDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DISABLE);
+    } else {
+          SetTextureBlendMode(_CurTexBlendMode,TRUE);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_texture_apply
+//       Access: Public, Virtual
+//  Description: handles texture transition (i.e. filter modes, etc) changes
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_texture_apply(const TextureApplyTransition *attrib) {
+
+    _CurTexBlendMode = attrib->get_mode();
+
+    if (!_texturing_enabled) {
+        return;
+    }
+
+    SetTextureBlendMode(_CurTexBlendMode,FALSE);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_color_mask
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_color_mask(const ColorMaskTransition *attrib) {
+    dxgsg_cat.fatal() << "DXGSG issue_color_mask unimplemented (not implementable on DX7, need DX8)!!!";
+    // because PLANEMASK renderstate has inconsistent to poor driver support on dx6 and before, and is
+    // explicitly disabled on dx7
+    return;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_depth_test
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_depth_test(const DepthTestTransition *attrib) {
+    
+
+    DepthTestProperty::Mode mode = attrib->get_mode();
+
+    if (mode == DepthTestProperty::M_none) {
+        _depth_test_enabled = false;
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+    } else {
+        _depth_test_enabled = true;
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_TRUE);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, get_depth_func_type(mode));
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_stencil
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_stencil(const StencilTransition *attrib) {
+
+  StencilProperty::Mode mode = attrib->get_mode();
+
+    if (mode == StencilProperty::M_none) {
+        enable_stencil_test(false);
+    } else {
+        enable_stencil_test(true);
+        D3DSTENCILOP pass_op = get_stencil_action_type(attrib->get_pass_action());
+        D3DSTENCILOP fail_op = get_stencil_action_type(attrib->get_fail_action());
+        D3DSTENCILOP zfail_op = get_stencil_action_type(attrib->get_zfail_action());
+
+        #ifdef _DEBUG
+           if(!(_D3DDevDesc.dwStencilCaps & (1<<(pass_op-1)))) {
+               dxgsg_cat.warning() << "driver doesnt support pass stencil operation: " << pass_op << endl;
+           }
+           if(!(_D3DDevDesc.dwStencilCaps & (1<<(fail_op-1)))) {
+               dxgsg_cat.warning() << "driver doesnt support fail stencil operation: " << fail_op << endl;
+           }
+           if(!(_D3DDevDesc.dwStencilCaps & (1<<(zfail_op-1)))) {
+               dxgsg_cat.warning() << "driver doesnt support zfail stencil operation: " << zfail_op << endl;
+           }
+        #endif
+        // TODO: need to cache all these
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILFUNC, get_stencil_func_type(mode));
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILPASS, pass_op);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILFAIL, fail_op);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILZFAIL, zfail_op);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILREF, attrib->get_reference_value());
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILMASK, attrib->get_func_mask());
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_STENCILWRITEMASK, attrib->get_write_mask());
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_cull_transition
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_cull_face(const CullFaceTransition *attrib) {
+    
+#ifndef NDEBUG
+    if(dx_force_backface_culling!=0) {
+        return;  // leave as initially set
+    }
+#endif
+
+    CullFaceProperty::Mode mode = attrib->get_mode();
+
+    switch (mode) {
+        case CullFaceProperty::M_cull_none:
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
+            break;
+        case CullFaceProperty::M_cull_clockwise:
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CW);
+            break;
+        case CullFaceProperty::M_cull_counter_clockwise:
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW);
+            break;
+        case CullFaceProperty::M_cull_all:
+            dxgsg_cat.warning() << "M_cull_all is invalid for DX GSG renderer, using CULL_CCW \n";
+            _d3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW);
+            break;
+        default:
+            dxgsg_cat.error()
+            << "invalid cull face mode " << (int)mode << endl;
+            break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_clip_plane
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_clip_plane(const ClipPlaneTransition *attrib) {
+  nassertv(attrib->get_default_dir() != TD_on);
+    
+  // Initialize the currently enabled clip plane list
+  int i;
+  for (i = 0; i < _max_clip_planes; i++)
+    _cur_clip_plane_enabled[i] = false;
+  
+  int num_enabled = 0;
+  ClipPlaneTransition::const_iterator pi;
+  for (pi = attrib->begin(); pi != attrib->end(); ++pi) {
+    PlaneNode *plane_node;
+    DCAST_INTO_V(plane_node, (*pi).first);
+    nassertv(plane_node != (PlaneNode *)NULL);
+    TransitionDirection dir = (*pi).second;
+
+    if (dir == TD_on) {
+      _cur_clip_plane_id = -1;
+      num_enabled++;
+      
+      // Check to see if this clip plane has already been bound to an id
+      for (i = 0; i < _max_clip_planes; i++) {
+        if (_available_clip_plane_ids[i] == plane_node) {
+          // Clip plane has already been bound to an id, we only need
+          // to enable the clip plane, not apply it
+          _cur_clip_plane_id = -2;
+          enable_clip_plane(i, true);
+          _cur_clip_plane_enabled[i] = true;
+          break;
+        }
+      }
+      
+      // See if there are any unbound clip plane ids
+      if (_cur_clip_plane_id == -1) {
+        for (i = 0; i < _max_clip_planes; i++) {
+          if (_available_clip_plane_ids[i] == NULL) {
+            _available_clip_plane_ids[i] = plane_node;
+            _cur_clip_plane_id = i;
+            break;
+          }
+        }
+      }
+      
+      // If there were no unbound clip plane ids, see if we can replace
+      // a currently unused but previously bound id
+      if (_cur_clip_plane_id == -1) {
+        for (i = 0; i < _max_clip_planes; i++) {
+          if (attrib->is_off(_available_clip_plane_ids[i])) {
+            _available_clip_plane_ids[i] = plane_node;
+            _cur_clip_plane_id = i;
+            break;
+          }
+        }
+      }
+      
+      if (_cur_clip_plane_id >= 0) {
+        enable_clip_plane(_cur_clip_plane_id, true);
+        _cur_clip_plane_enabled[_cur_clip_plane_id] = true;
+        const Planef clip_plane = plane_node->get_plane();
+        
+        D3DVALUE equation[4];
+        equation[0] = clip_plane._a;
+        equation[1] = clip_plane._b;
+        equation[2] = clip_plane._c;
+        equation[3] = clip_plane._d;
+        _d3dDevice->SetClipPlane(_cur_clip_plane_id, equation);
+        
+      } else if (_cur_clip_plane_id == -1) {
+        dxgsg_cat.error()
+          << "issue_clip_plane() - failed to bind clip plane to id" << endl;
+      }
+    }
+  }
+
+  // Disable all unused clip planes
+  for (i = 0; i < _max_clip_planes; i++) {
+    if (!_cur_clip_plane_enabled[i])
+      enable_clip_plane(i, false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_transparency
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_transparency(const TransparencyTransition *attrib ) {
+
+    TransparencyProperty::Mode mode = attrib->get_mode();
+
+    switch (mode) {
+        case TransparencyProperty::M_none:
+//          enable_multisample_alpha_one(false);
+//          enable_multisample_alpha_mask(false);
+            enable_blend(false);
+            enable_alpha_test(false);
+            break;
+        case TransparencyProperty::M_alpha:
+        case TransparencyProperty::M_alpha_sorted:
+            // Should we really have an "alpha" and an "alpha_sorted" mode,
+            // like Performer does?  (The difference is that "alpha" is with
+            // the write to the depth buffer disabled.)  Or should we just use
+            // the separate depth write transition to control this?  Doing it
+            // implicitly requires a bit more logic here and in the state
+            // management; for now we require the user to explicitly turn off
+            // the depth write.
+//          enable_multisample_alpha_one(false);
+//          enable_multisample_alpha_mask(false);
+            enable_blend(true);
+            enable_alpha_test(false);
+            call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
+            break;
+        case TransparencyProperty::M_multisample:
+//          enable_multisample_alpha_one(true);
+//          enable_multisample_alpha_mask(true);
+            enable_blend(false);
+            enable_alpha_test(false);
+            break;
+        case TransparencyProperty::M_multisample_mask:
+//          enable_multisample_alpha_one(false);
+//          enable_multisample_alpha_mask(true);
+            enable_blend(false);
+            enable_alpha_test(false);
+            break;
+        case TransparencyProperty::M_binary:
+//          enable_multisample_alpha_one(false);
+//          enable_multisample_alpha_mask(false);
+            enable_blend(false);
+            enable_alpha_test(true);
+            call_dxAlphaFunc(D3DCMP_EQUAL, 1);
+            break;
+        default:
+            dxgsg_cat.error()
+            << "invalid transparency mode " << (int)mode << endl;
+            break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_linesmooth
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_linesmooth(const LinesmoothTransition *attrib) {
+    enable_line_smooth(attrib->is_on());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::wants_normals
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool DXGraphicsStateGuardian::
+wants_normals() const {
+    return (_lighting_enabled || _normals_enabled);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::wants_texcoords
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool DXGraphicsStateGuardian::
+wants_texcoords() const {
+    return _texturing_enabled;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::issue_depth_write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+issue_depth_write(const DepthWriteTransition *attrib) {
+    enable_zwritemask(attrib->is_on());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::begin_decal
+//       Access: Public, Virtual
+//  Description: This will be called to initiate decaling mode.  It is
+//               passed the pointer to the GeomNode that will be the
+//               destination of the decals, which it is expected that
+//               the GSG will render normally; subsequent geometry
+//               rendered up until the next call of end_decal() should
+//               be rendered as decals of the base_geom.
+//
+//               The transitions wrapper is the current state as of the
+//               base geometry node.  It may or may not be modified by
+//               the GSG to reflect whatever rendering state is
+//               necessary to render the decals properly.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib) {
+    nassertv(base_geom != (GeomNode *)NULL);
+
+    assert(_decal_level>=0);
+
+    _decal_level++;
+
+#ifndef DISABLE_DECALING
+
+ #ifndef DISABLE_POLYGON_OFFSET_DECALING
+    if (dx_decal_type == GDT_offset) {
+ #define POLYGON_OFFSET_MULTIPLIER 2
+
+        // note: zbias seems inconsitently supported.  may be possible to fake it by
+        //       adding (delta * element[4,3]) to element [3,3] of a regular matrix
+        //       need to test this and see if to offers perf improvement over blend-based decaling
+
+        nassertv(POLYGON_OFFSET_MULTIPLIER*_decal_level < 16);  // 16 is the max allowed zbias
+
+        // Just draw the base geometry normally.
+        base_geom->draw(this);
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, POLYGON_OFFSET_MULTIPLIER * _decal_level); // _decal_level better not be higher than 8!
+    } else
+ #endif
+
+    {
+        if (_decal_level > 1) {
+            base_geom->draw(this);  // If we're already decaling, just draw the geometry.
+        } else {
+            // need to save current xform matrix in case it is changed during subrendering, so subsequent decal draws use same xform
+            _bTransformIssued = false;
+            _d3dDevice->GetTransform( D3DTRANSFORMSTATE_WORLD, &_SavedTransform);
+
+            // First turn off writing the depth buffer to render the base geometry.
+            enable_zwritemask(false);
+
+            // It is also important to update the current state to
+            // indicate the depth buffer write is off, so that future
+            // geometry will render correctly.
+            DepthWriteTransition *dwa = new DepthWriteTransition(DepthWriteTransition::off());
+            attrib.set_transition(dwa);
+
+            // Now render the base geometry.
+            base_geom->draw(this);
+
+            // Render all of the decal geometry, too.  We'll keep the depth
+            // buffer write off during this.
+        }
+    }
+#else
+            base_geom->draw(this);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::end_decal
+//       Access: Public, Virtual
+//  Description: This will be called to terminate decaling mode.  It
+//               is passed the same base_geom that was passed to
+//               begin_decal().
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+end_decal(GeomNode *base_geom) {
+    _decal_level--;
+
+    assert(_decal_level>=0);
+
+#ifdef DISABLE_DECALING
+    return;
+#endif
+
+    nassertv(base_geom != (GeomNode *)NULL);
+//  nassertv(_decal_level >= 1);
+
+#ifndef DISABLE_POLYGON_OFFSET_DECALING
+    if (dx_decal_type == GDT_offset) {
+        // Restore the Zbias offset.
+        _d3dDevice->SetRenderState(D3DRENDERSTATE_ZBIAS, POLYGON_OFFSET_MULTIPLIER * _decal_level); // _decal_level better not be higher than 8!
+    } else
+#endif
+    {  // for GDT_mask
+        if (_decal_level == 0) {
+            // Now we need to re-render the base geometry with the depth write
+            // on and the color mask off, so we update the depth buffer
+            // properly.
+            bool was_textured = _texturing_enabled;
+            bool was_blend = _blend_enabled;
+            D3DBLEND old_blend_source_func = _blend_source_func;
+            D3DBLEND old_blend_dest_func = _blend_dest_func;
+
+            // Enable the writing to the depth buffer.
+            enable_zwritemask(true);
+
+            // Disable the writing to the color buffer, however we have to
+            // do this.  (I don't think this is possible in DX without blending.)
+            if (dx_decal_type == GDT_blend) {
+                // Expensive.
+                enable_blend(true);
+                call_dxBlendFunc(D3DBLEND_ZERO, D3DBLEND_ONE);
+            }
+
+#if(DIRECT3D_VERSION < 0x700)
+            else {  // dx7 doesn't support planemask rstate
+                // note: not saving current planemask val, assumes this is always all 1's.  should be ok
+                _d3dDevice->SetRenderState(D3DRENDERSTATE_PLANEMASK,0x0);  // note PLANEMASK is supposedly obsolete for DX7
+            }
+#endif
+            // Note: For DX8, use D3DRS_COLORWRITEENABLE  (check D3DPMISCCAPS_COLORWRITEENABLE first)
+
+            // No need to have texturing on for this.
+            enable_texturing(false);
+
+            // if current xform has changed, reset to saved xform
+            if(_bTransformIssued) 
+                _d3dDevice->SetTransform( D3DTRANSFORMSTATE_WORLD, &_SavedTransform);
+
+            base_geom->draw(this);
+
+            // Finally, restore the depth write and color mask states to the
+            // way they're supposed to be.
+
+            if (dx_decal_type == GDT_blend) {
+                enable_blend(was_blend);
+                if (was_blend)
+                    call_dxBlendFunc(old_blend_source_func, old_blend_dest_func);
+            }
+#if(DIRECT3D_VERSION < 0x700)
+            else {
+                _d3dDevice->SetRenderState(D3DRENDERSTATE_PLANEMASK,0xFFFFFFFF);  // this is unlikely to work due to poor driver support
+            }
+#endif
+            enable_texturing(was_textured);
+
+            // Finally, restore the depth write state to the
+            // way they're supposed to be.
+
+            // could we do this faster by saving last issued depth writemask?
+            DepthWriteTransition *depth_write;
+            if (get_attribute_into(depth_write, this)) {
+                issue_depth_write(depth_write);
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::compute_distance_to
+//       Access: Public, Virtual
+//  Description: This function may only be called during a render
+//               traversal; it will compute the distance to the
+//               indicated point, assumed to be in modelview
+//               coordinates, from the camera plane.
+////////////////////////////////////////////////////////////////////
+INLINE float DXGraphicsStateGuardian::
+compute_distance_to(const LPoint3f &point) const {
+    // In the case of a DXGraphicsStateGuardian, we know that the
+    // modelview matrix already includes the relative transform from the
+    // camera, as well as a to-y-up conversion.  Thus, the distance to
+    // the camera plane is simply the +z distance.  (negative of gl compute_distance_to,
+    // since d3d uses left-hand coords)
+
+    return point[2];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::set_draw_buffer
+//       Access: Protected
+//  Description: Sets up the glDrawBuffer to render into the buffer
+//               indicated by the RenderBuffer object.  This only sets
+//               up the color bits; it does not affect the depth,
+//               stencil, accum layers.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+set_draw_buffer(const RenderBuffer &rb) {
+    dxgsg_cat.fatal() << "DX set_draw_buffer unimplemented!!!";
+    return;
+
+#ifdef WBD_GL_MODE
+    switch (rb._buffer_type & RenderBuffer::T_color) {
+        case RenderBuffer::T_front:
+            call_glDrawBuffer(GL_FRONT);
+            break;
+
+        case RenderBuffer::T_back:
+            call_glDrawBuffer(GL_BACK);
+            break;
+
+        case RenderBuffer::T_right:
+            call_glDrawBuffer(GL_RIGHT);
+            break;
+
+        case RenderBuffer::T_left:
+            call_glDrawBuffer(GL_LEFT);
+            break;
+
+        case RenderBuffer::T_front_right:
+            call_glDrawBuffer(GL_FRONT_RIGHT);
+            break;
+
+        case RenderBuffer::T_front_left:
+            call_glDrawBuffer(GL_FRONT_LEFT);
+            break;
+
+        case RenderBuffer::T_back_right:
+            call_glDrawBuffer(GL_BACK_RIGHT);
+            break;
+
+        case RenderBuffer::T_back_left:
+            call_glDrawBuffer(GL_BACK_LEFT);
+            break;
+
+        default:
+            call_glDrawBuffer(GL_FRONT_AND_BACK);
+    }
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::set_read_buffer
+//       Access: Protected
+//  Description: Vestigial analog of glReadBuffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+set_read_buffer(const RenderBuffer &rb) {
+
+    if(rb._buffer_type & RenderBuffer::T_front) {
+            _cur_read_pixel_buffer=RenderBuffer::T_front;
+    } else  if(rb._buffer_type & RenderBuffer::T_back) {
+            _cur_read_pixel_buffer=RenderBuffer::T_back;
+    } else {
+            dxgsg_cat.error() << "Invalid or unimplemented Argument to set_read_buffer!\n";
+    }
+    return;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::get_texture_wrap_mode
+//       Access: Protected
+//  Description: Maps from the Texture's internal wrap mode symbols to
+//               GL's.
+////////////////////////////////////////////////////////////////////
+INLINE D3DTEXTUREADDRESS DXGraphicsStateGuardian::
+get_texture_wrap_mode(Texture::WrapMode wm) const {
+
+    if (wm == Texture::WM_clamp)
+        return D3DTADDRESS_CLAMP;
+    else if (wm != Texture::WM_repeat) {
+#ifdef _DEBUG
+        dxgsg_cat.error() << "Invalid or Unimplemented Texture::WrapMode value!\n";
+#endif
+    }
+
+    return D3DTADDRESS_WRAP;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::get_depth_func_type
+//       Access: Protected
+//  Description: Maps from the depth func modes to gl version
+////////////////////////////////////////////////////////////////////
+INLINE D3DCMPFUNC DXGraphicsStateGuardian::
+get_depth_func_type(DepthTestProperty::Mode m) const {
+    switch (m) {
+        case DepthTestProperty::M_never: return D3DCMP_NEVER;
+        case DepthTestProperty::M_less: return D3DCMP_LESS;
+        case DepthTestProperty::M_equal: return D3DCMP_EQUAL;
+        case DepthTestProperty::M_less_equal: return D3DCMP_LESSEQUAL;
+        case DepthTestProperty::M_greater: return D3DCMP_GREATER;
+        case DepthTestProperty::M_not_equal: return D3DCMP_NOTEQUAL;
+        case DepthTestProperty::M_greater_equal: return D3DCMP_GREATEREQUAL;
+        case DepthTestProperty::M_always: return D3DCMP_ALWAYS;
+    }
+    dxgsg_cat.error()
+    << "Invalid DepthTestProperty::Mode value" << endl;
+    return D3DCMP_LESS;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::get_stencil_func_type
+//       Access: Protected
+//  Description: Maps from the stencil func modes to dx version
+////////////////////////////////////////////////////////////////////
+INLINE D3DCMPFUNC DXGraphicsStateGuardian::
+get_stencil_func_type(StencilProperty::Mode m) const {
+    switch (m) {
+        case StencilProperty::M_never: return D3DCMP_NEVER;
+        case StencilProperty::M_less: return D3DCMP_LESS;
+        case StencilProperty::M_equal: return D3DCMP_EQUAL;
+        case StencilProperty::M_less_equal: return D3DCMP_LESSEQUAL;
+        case StencilProperty::M_greater: return D3DCMP_GREATER;
+        case StencilProperty::M_not_equal: return D3DCMP_NOTEQUAL;
+        case StencilProperty::M_greater_equal: return D3DCMP_GREATEREQUAL;
+        case StencilProperty::M_always: return D3DCMP_ALWAYS;
+    }
+    dxgsg_cat.error() << "Invalid StencilProperty::Mode value" << endl;
+    return D3DCMP_LESS;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::get_stencil_action_type
+//       Access: Protected
+//  Description: Maps from the stencil action modes to dx version
+////////////////////////////////////////////////////////////////////
+INLINE D3DSTENCILOP DXGraphicsStateGuardian::
+get_stencil_action_type(StencilProperty::Action a) const {
+    switch (a) {
+        case StencilProperty::A_keep: return D3DSTENCILOP_KEEP;
+        case StencilProperty::A_zero: return D3DSTENCILOP_ZERO;
+        case StencilProperty::A_replace: return D3DSTENCILOP_REPLACE;
+        case StencilProperty::A_increment: return D3DSTENCILOP_INCR;
+        case StencilProperty::A_decrement: return D3DSTENCILOP_DECR;
+        case StencilProperty::A_invert: return D3DSTENCILOP_INVERT;
+    }
+    dxgsg_cat.error() << "Invalid StencilProperty::Action value" << endl;
+    return D3DSTENCILOP_KEEP;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::get_fog_mode_type
+//       Access: Protected
+//  Description: Maps from the fog types to gl version
+////////////////////////////////////////////////////////////////////
+INLINE D3DFOGMODE DXGraphicsStateGuardian::
+get_fog_mode_type(Fog::Mode m) const {
+    switch (m) {
+        case Fog::M_linear: return D3DFOG_LINEAR;
+        case Fog::M_exponential: return D3DFOG_EXP;
+        case Fog::M_exponential_squared: return D3DFOG_EXP2;
+    }
+    dxgsg_cat.error() << "Invalid Fog::Mode value" << endl;
+    return D3DFOG_EXP;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::free_pointers
+//       Access: Public
+//  Description: Frees some memory that was explicitly allocated
+//               within the dxgsg.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+free_pointers() {
+    if (_light_enabled != (bool *)NULL) {
+        delete[] _light_enabled;
+        _light_enabled = (bool *)NULL;
+    }
+    if (_cur_light_enabled != (bool *)NULL) {
+        delete[] _cur_light_enabled;
+        _cur_light_enabled = (bool *)NULL;
+    }
+    if (_clip_plane_enabled != (bool *)NULL) {
+        delete[] _clip_plane_enabled;
+        _clip_plane_enabled = (bool *)NULL;
+    }
+    if (_cur_clip_plane_enabled != (bool *)NULL) {
+        delete[] _cur_clip_plane_enabled;
+        _cur_clip_plane_enabled = (bool *)NULL;
+    }
+
+    if (_pTexPixFmts != NULL) {
+        delete [] _pTexPixFmts;
+        _pTexPixFmts = NULL;
+    }
+
+    if(_fpsmeter_verts!=NULL) {
+        delete [] _fpsmeter_verts;
+        _fpsmeter_verts = NULL;
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::save_frame_buffer
+//       Access: Public
+//  Description: Saves the indicated planes of the frame buffer
+//               (within the indicated display region) and returns it
+//               in some meaningful form that can be restored later
+//               via restore_frame_buffer().  This is a helper
+//               function for push_frame_buffer() and
+//               pop_frame_buffer().
+////////////////////////////////////////////////////////////////////
+PT(SavedFrameBuffer) DXGraphicsStateGuardian::
+save_frame_buffer(const RenderBuffer &buffer,
+                  CPT(DisplayRegion) dr) {
+
+    dxgsg_cat.error() << "save_frame_buffer unimplemented!!\n";
+    return NULL;
+
+#if 0
+    DXSavedFrameBuffer *sfb = new DXSavedFrameBuffer(buffer, dr);
+
+    if (buffer._buffer_type & RenderBuffer::T_depth) {
+        // Save the depth buffer.
+        sfb->_depth =
+        new PixelBuffer(PixelBuffer::depth_buffer(dr->get_pixel_width(),
+                                                  dr->get_pixel_height()));
+        copy_pixel_buffer(sfb->_depth, dr, buffer);
+    }
+
+    if (buffer._buffer_type & RenderBuffer::T_back) {
+        // Save the color buffer.
+        sfb->_back_rgba = new Texture;
+        copy_texture(sfb->_back_rgba->prepare(this), dr, buffer);
+    }
+
+    return sfb;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::restore_frame_buffer
+//       Access: Public
+//  Description: Restores the frame buffer that was previously saved.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+restore_frame_buffer(SavedFrameBuffer *frame_buffer) {
+
+    dxgsg_cat.error() << "restore_frame_buffer unimplemented!!\n";
+    return;
+
+#if 0
+    DXSavedFrameBuffer *sfb = DCAST(DXSavedFrameBuffer, frame_buffer);
+
+    if (sfb->_back_rgba != (Texture *)NULL &&
+        (sfb->_buffer._buffer_type & RenderBuffer::T_back) != 0) {
+        // Restore the color buffer.
+        draw_texture(sfb->_back_rgba->prepare(this),
+                     sfb->_display_region, sfb->_buffer);
+    }
+
+    if (sfb->_depth != (PixelBuffer *)NULL &&
+        (sfb->_buffer._buffer_type & RenderBuffer::T_depth) != 0) {
+        // Restore the depth buffer.
+        draw_pixel_buffer(sfb->_depth, sfb->_display_region, sfb->_buffer);
+    }
+#endif
+}
+
+// factory and type stuff
+
+GraphicsStateGuardian *DXGraphicsStateGuardian::
+make_DXGraphicsStateGuardian(const FactoryParams &params) {
+    GraphicsStateGuardian::GsgWindow *win_param;
+    if (!get_param_into(win_param, params)) {
+        dxgsg_cat.error()
+        << "No window specified for gsg creation!" << endl;
+        return NULL;
+    }
+
+    GraphicsWindow *win = win_param->get_window();
+    return new DXGraphicsStateGuardian(win);
+}
+
+TypeHandle DXGraphicsStateGuardian::get_type(void) const {
+    return get_class_type();
+}
+
+TypeHandle DXGraphicsStateGuardian::get_class_type(void) {
+    return _type_handle;
+}
+
+void DXGraphicsStateGuardian::init_type(void) {
+    GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "DXGraphicsStateGuardian",
+                  GraphicsStateGuardian::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: dx_cleanup
+//  Description: Clean up the DirectX environment.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled) {
+  static bool bAtExitFnEverCalled=false;
+
+    if(dxgsg_cat.is_spam()) {
+        dxgsg_cat.spam() << "dx_cleanup called, bAtExitFnCalled=" << bAtExitFnCalled << ", bAtExitFnEverCalled=" << bAtExitFnEverCalled << endl;
+    }
+    
+    bAtExitFnEverCalled = (bAtExitFnEverCalled || bAtExitFnCalled);
+
+    // for now, I can't trust any of the ddraw/d3d releases during atexit(),
+    // so just return directly.  maybe revisit this later, if have problems
+    // restarting d3d/ddraw after one of these uncleaned-up exits
+    if(bAtExitFnEverCalled)
+      return;
+
+    ULONG refcnt;
+
+    // unsafe to do the D3D releases after exit() called, since DLL_PROCESS_DETACH
+    // msg already delivered to d3d.dll and it's unloaded itself
+    if(!bAtExitFnEverCalled) {
+
+        PRINTREFCNT(_pDD,"exit start IDirectDraw7");
+
+        // these 2 calls release ddraw surfaces and vbuffers.  unsafe unless not on exit
+        release_all_textures();
+        release_all_geoms();
+
+        PRINTREFCNT(_pDD,"after release_all_textures IDirectDraw7");
+
+        // Do a safe check for releasing the D3DDEVICE. RefCount should be zero.
+        // if we're called from exit(), _d3dDevice may already have been released
+        if (_d3dDevice!=NULL) {
+            _d3dDevice->SetTexture(0,NULL);  // should release this stuff internally anyway
+            RELEASE(_d3dDevice,dxgsg,"d3dDevice",RELEASE_DOWN_TO_ZERO);
+        }
+
+        PRINTREFCNT(_pDD,"after d3ddevice release IDirectDraw7");
+
+        RELEASE(_fpsmeter_font_surf,dxgsg,"fpsmeter fontsurf",false);
+
+        PRINTREFCNT(_pDD,"after fpsfont release IDirectDraw7");
+
+        if((_back!=NULL)&&(_zbuf!=NULL))
+            _back->DeleteAttachedSurface(0x0,_zbuf);
+
+        // Release the DDraw and D3D objects used by the app
+        RELEASE(_zbuf,dxgsg,"zbuffer",false);
+
+        PRINTREFCNT(_pDD,"before releasing d3d obj, IDirectDraw7");
+        RELEASE(_d3d,dxgsg,"IDirect3D7 _d3d",false); //RELEASE_DOWN_TO_ZERO);
+        PRINTREFCNT(_pDD,"after releasing d3d obj, IDirectDraw7");
+    
+        RELEASE(_back,dxgsg,"backbuffer",false);
+        RELEASE(_pri,dxgsg,"primary surface",false);
+
+        PRINTREFCNT(_pDD,"after releasing all surfs, IDirectDraw7");
+    }
+
+    // for some reason, DLL_PROCESS_DETACH has not yet been sent to ddraw, so we can still call its fns
+
+    // Do a safe check for releasing DDRAW. RefCount should be zero.
+    if (_pDD!=NULL) {
+        if(bRestoreDisplayMode) {
+          HRESULT hr = _pDD->RestoreDisplayMode(); 
+          if(dxgsg_cat.is_spam())
+                dxgsg_cat.spam() << "dx_cleanup -  Restoring original desktop DisplayMode\n";
+          if(FAILED(hr)) {
+                dxgsg_cat.error() << "dx_cleanup -  RestoreDisplayMode failed, hr = " << ConvD3DErrorToString(hr) << endl;
+          }
+        }
+
+        if(bAtExitFnCalled) {
+           // if exit() called, there is definitely no more need for the IDDraw object,
+           // so we can make sure it's fully released
+           // note currently this is never called 
+           RELEASE(_pDD,dxgsg,"IDirectDraw7 _pDD", RELEASE_DOWN_TO_ZERO);
+        } else {
+           // seems wrong to release to zero, since it might be being used somewhere else?
+
+           RELEASE(_pDD,dxgsg,"IDirectDraw7 _pDD", false);  
+           if(refcnt>0) {
+              if(dxgsg_cat.is_spam())
+                dxgsg_cat.debug() << "dx_cleanup -  warning IDDraw7 refcnt = " << refcnt << ", should be zero!\n";
+           }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: dx_setup_after_resize
+//  Description: Recreate the back buffer and zbuffers at the new size
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+dx_setup_after_resize(RECT viewrect, HWND mwindow) {
+    if (_back == NULL) // nothing created yet
+        return;
+
+    // for safety, need some better error-cleanup here
+    assert((_pri!=NULL) && (_back!=NULL) && (_zbuf!=NULL));
+
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_back);
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_zbuf);
+
+    _back->GetSurfaceDesc(&ddsd_back);
+    _zbuf->GetSurfaceDesc(&ddsd_zbuf);
+
+    ULONG refcnt;
+
+    if((_back!=NULL)&&(_zbuf!=NULL))
+        _back->DeleteAttachedSurface(0x0,_zbuf);
+
+    RELEASE(_zbuf,dxgsg,"zbuffer",false);
+    RELEASE(_back,dxgsg,"backbuffer",false);
+    RELEASE(_pri,dxgsg,"primary surface",false);
+
+    assert((_pri == NULL) && (_back == NULL) && (_zbuf == NULL));
+    _view_rect = viewrect;
+
+    DWORD renderWid = _view_rect.right - _view_rect.left;
+    DWORD renderHt = _view_rect.bottom - _view_rect.top;
+
+    ddsd_back.dwWidth  = ddsd_zbuf.dwWidth = renderWid;
+    ddsd_back.dwHeight = ddsd_zbuf.dwHeight = renderHt;
+
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    ddsd.dwFlags        = DDSD_CAPS;
+
+    PRINTVIDMEM(_pDD,&ddsd.ddsCaps,"resize primary surf");
+    HRESULT hr;
+
+    if (FAILED(hr = _pDD->CreateSurface( &ddsd, &_pri, NULL ))) {
+        dxgsg_cat.fatal() << "resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    if(!dx_full_screen) {
+        // Create a clipper object which handles all our clipping for cases when
+        // our window is partially obscured by other windows.
+        LPDIRECTDRAWCLIPPER Clipper;
+    
+        if (FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) {
+            dxgsg_cat.fatal()
+            << "CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+        // Associate the clipper with our window. Note that, afterwards, the
+        // clipper is internally referenced by the primary surface, so it is safe
+        // to release our local reference to it.
+        Clipper->SetHWnd( 0, mwindow );
+        _pri->SetClipper( Clipper );
+        Clipper->Release();
+    }
+
+    // Recreate the backbuffer. (might want to handle failure due to running out of video memory)
+
+    ddsd_back.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;  // just to make sure
+    ddsd_back.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
+
+    PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize backbuffer surf");
+
+    if (FAILED(hr = _pDD->CreateSurface( &ddsd_back, &_back, NULL ))) {
+        dxgsg_cat.fatal() << "resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize zbuffer surf");
+
+    // Recreate and attach a z-buffer.
+    if (FAILED(hr = _pDD->CreateSurface( &ddsd_zbuf, &_zbuf, NULL ))) {
+        dxgsg_cat.fatal() << "resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    // Attach the z-buffer to the back buffer.
+    if ((hr = _back->AddAttachedSurface( _zbuf ) ) != DD_OK) {
+        dxgsg_cat.fatal() << "resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    if ((hr = _d3dDevice->SetRenderTarget(_back,0x0) ) != DD_OK) {
+        dxgsg_cat.fatal() << "resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    // Create the viewport
+    D3DVIEWPORT7 vp = { 0, 0, renderWid, renderHt, 0.0f, 1.0f};
+    hr = _d3dDevice->SetViewport( &vp );
+    if (hr != DD_OK) {
+        dxgsg_cat.fatal()
+        << "SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    if(dx_show_fps_meter) 
+        SetFPSMeterPosition(_view_rect);
+}
+
+bool refill_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext *dtc = DCAST(DXTextureContext, tc);
+//   DXGraphicsStateGuardian *dxgsg = (DXGraphicsStateGuardian *)void_dxgsg_ptr; not needed?
+
+     // Re-fill the contents of textures and vertex buffers
+     // which just got restored now.
+     HRESULT hr=dtc->FillDDSurfTexturePixels();
+     return hr==S_OK;
+}
+
+bool delete_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext *dtc = DCAST(DXTextureContext, tc);
+
+     // release DDSurf (but not the texture context)
+     dtc->DeleteTexture();
+     return true;
+}
+
+bool recreate_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext *dtc = DCAST(DXTextureContext, tc);
+     DXGraphicsStateGuardian *dxgsg = (DXGraphicsStateGuardian *)void_dxgsg_ptr;
+
+     // Re-fill the contents of textures and vertex buffers
+     // which just got restored now.
+
+     LPDIRECTDRAWSURFACE7 ddtex = dtc->CreateTexture(dxgsg->_d3dDevice,
+                                                     dxgsg->_cNumTexPixFmts,dxgsg->_pTexPixFmts);
+     return ddtex!=NULL;
+}
+
+// release all textures and vertex/index buffers
+HRESULT DXGraphicsStateGuardian::DeleteAllVideoSurfaces(void) {
+  // BUGBUG: need to handle vertexbuffer handling here
+
+  // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
+  traverse_prepared_textures(delete_tex_callback,this);
+
+  ULONG refcnt;
+
+  if(dx_show_fps_meter)
+     RELEASE(_fpsmeter_font_surf,dxgsg,"fpsmeter fontsurf",false);
+
+  if(dxgsg_cat.is_debug())
+      dxgsg_cat.debug() << "release of all textures complete\n";
+  return S_OK;
+}
+
+// recreate all textures and vertex/index buffers
+HRESULT DXGraphicsStateGuardian::RecreateAllVideoSurfaces(void) {
+  // BUGBUG: need to handle vertexbuffer handling here
+
+  // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
+  traverse_prepared_textures(recreate_tex_callback,this);
+
+  if(dxgsg_cat.is_debug())
+      dxgsg_cat.debug() << "recreation of all textures complete\n";
+  return S_OK;
+}
+
+HRESULT DXGraphicsStateGuardian::RestoreAllVideoSurfaces(void) {
+  // BUGBUG: this should also restore vertex buffer contents when they are implemented
+  // You will need to destroy and recreate
+  // optimized vertex buffers however, restoring is not enough.
+
+  HRESULT hr;
+
+  // note: could go through and just restore surfs that return IsLost() true
+  // apparently that isnt as reliable w/some drivers tho
+  if (FAILED(hr = _pDD->RestoreAllSurfaces() )) {
+        dxgsg_cat.fatal() << "RestoreAllSurfs failed : result = " << ConvD3DErrorToString(hr) << endl;
+    exit(1);
+  }
+
+  // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
+  traverse_prepared_textures(refill_tex_callback,this);
+
+  if(dx_show_fps_meter)
+      FillFPSMeterTexture();
+
+  if(dxgsg_cat.is_debug())
+      dxgsg_cat.debug() << "restore and refill of video surfaces complete...\n";
+  return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: show_frame
+//       Access:
+//       Description:   Repaint primary buffer from back buffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::show_frame(void) {
+  if(_pri==NULL)
+    return;
+
+  DO_PSTATS_STUFF(PStatTimer timer(_win->_swap_pcollector));  // this times just the flip, so it must go here in dxgsg, instead of wdxdisplay, which would time the whole frame
+
+  if(dx_full_screen) {
+    show_full_screen_frame();
+  } else {
+    show_windowed_frame();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: show_full_screen_frame
+//       Access:
+//       Description:   Repaint primary buffer from back buffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::show_full_screen_frame(void) {
+  HRESULT hr;
+  
+  // Flip the front and back buffers, to make what we just rendered
+  // visible.
+
+  DWORD dwFlipFlags = DDFLIP_WAIT;
+
+  if (!dx_sync_video) {
+    // If the user indicated via Config that we shouldn't wait for
+    // video sync, then don't wait (if the hardware supports this).
+    // This will introduce visible artifacts like tearing, and may
+    // cause the frame rate to grow excessively (and pointlessly)
+    // high, starving out other processes.
+    dwFlipFlags |= DDFLIP_NOVSYNC;
+//  dwFlipFlags = DDFLIP_DONOTWAIT | DDFLIP_NOVSYNC;
+  }
+
+  // bugbug: dont we want triple buffering instead of wasting time
+  // waiting for vsync?
+  hr = _pri->Flip( NULL, dwFlipFlags);
+
+  if(FAILED(hr)) {
+    if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) {
+      CheckCooperativeLevel();
+    } else {
+      dxgsg_cat.error() << "show_frame() - Flip failed w/unexpected error code: " << ConvD3DErrorToString(hr) << endl;
+      exit(1);
+    }
+  }
+
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: show_windowed_frame
+//       Access: Public
+//  Description: Repaint primary buffer from back buffer (windowed
+//               mode only)
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::show_windowed_frame(void) {
+  HRESULT hr;
+
+  DX_DECLARE_CLEAN(DDBLTFX, bltfx);
+
+  if (dx_sync_video) {
+    // Wait for the video refresh *before* we blt the rendered image
+    // onto the window.  This will (a) prevent the "tearing" of the
+    // image that would occur if only part of the image happened to be
+    // copied into the window before the video refresh occurred, and
+    // (b) prevent our frame rate from going excessively (and
+    // pointlessly) higher than our video refresh rate, starving out
+    // other processes.
+
+    // Unfortunately, when the system is even lightly loaded, this
+    // wait call sometimes appears to wait through multiple frames
+    // before returning, causing our frame rate to be unreasonably low
+    // and erratic.  There doesn't appear to be any way to prevent
+    // this behavior; thus, we allow the user to avoid this wait,
+    // based on the Config settings.
+
+    bltfx.dwDDFX |= DDBLTFX_NOTEARING;  // hmm, does any driver actually recognize this flag?
+  }
+
+  hr = _pri->Blt( &_view_rect, _back,  NULL, DDBLT_DDFX | DDBLT_WAIT, &bltfx );
+
+  if (dx_sync_video) {
+    HRESULT hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
+    if(hr != DD_OK) {
+      dxgsg_cat.error() << "WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl;
+      exit(1);
+    }
+  }
+
+  if(FAILED(hr)) {
+    if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) {
+      CheckCooperativeLevel();
+    } else {
+      dxgsg_cat.error() << "show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl;
+      exit(1);
+    }
+  }
+
+}
+
+bool DXGraphicsStateGuardian::CheckCooperativeLevel(bool bDoReactivateWindow) {
+
+    HRESULT hr = _pDD->TestCooperativeLevel();
+
+    if(SUCCEEDED(_last_testcooplevel_result)) {
+        if(SUCCEEDED(hr))  // this means this was just a safety check, dont need to restore surfs
+            return true;
+
+        // otherwise something just went wrong
+
+        HRESULT hr;
+    
+        // TestCooperativeLevel returns DD_OK: If the current mode is same as the one which the App set.
+        // The following error is returned only for exclusivemode apps.
+        // DDERR_NOEXCLUSIVEMODE: Some other app took exclusive mode.
+
+        hr = _pDD->TestCooperativeLevel();
+    
+        HRESULT expected_error = (dx_full_screen ? DDERR_NOEXCLUSIVEMODE : DDERR_EXCLUSIVEMODEALREADYSET);
+
+        if(hr == expected_error) {
+          // This means that mode changes had taken place, surfaces
+          // were lost but still we are in the original mode, so we
+          // simply restore all surfaces and keep going.
+    
+          if(dxgsg_cat.is_debug()) {
+             if(dx_full_screen)
+                dxgsg_cat.debug() << "Lost access to DDRAW exclusive mode, waiting to regain it...\n";
+              else dxgsg_cat.debug() << "Another app has DDRAW exclusive mode, waiting...\n";
+          }
+
+          if(_dx_ready) {
+             _win->deactivate_window();
+             _dx_ready = FALSE;
+          }
+        } else if(hr==DDERR_WRONGMODE) {
+            // This means that the desktop mode has changed
+            // need to destroy all of dx stuff and recreate everything
+            // back again, which is a big hit
+            dxgsg_cat.error() << "detected display mode change in TestCoopLevel, must recreate all DDraw surfaces, D3D devices, this is not handled yet. hr == " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        } else if(FAILED(hr)) {
+            dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+    } else {
+        // testcooplvl was failing, handle case where it now succeeds
+
+        if(SUCCEEDED(hr)) {
+          if(_last_testcooplevel_result == DDERR_EXCLUSIVEMODEALREADYSET) {
+              if(dxgsg_cat.is_debug())
+                  dxgsg_cat.debug() << "other app relinquished exclusive mode, refilling surfs...\n";
+          } else if(_last_testcooplevel_result == DDERR_NOEXCLUSIVEMODE) {
+                      if(dxgsg_cat.is_debug())
+                          dxgsg_cat.debug() << "regained exclusive mode, refilling surfs...\n";
+          }
+              
+          if(bDoReactivateWindow)
+              _win->reactivate_window();  //must reactivate window before you can restore surfaces (otherwise you are in WRONGVIDEOMODE, and DDraw RestoreAllSurfaces fails)
+
+          RestoreAllVideoSurfaces();  
+
+          _dx_ready = TRUE;
+
+        } else if(hr==DDERR_WRONGMODE) {
+            // This means that the desktop mode has changed
+            // need to destroy all of dx stuff and recreate everything
+            // back again, which is a big hit
+            dxgsg_cat.error() << "detected desktop display mode change in TestCoopLevel, must recreate all DDraw surfaces & D3D devices, this is not handled yet.  " << ConvD3DErrorToString(hr) << endl;
+            _win->close_window();
+            exit(1);
+          } else if((hr!=DDERR_NOEXCLUSIVEMODE) && (hr!=DDERR_EXCLUSIVEMODEALREADYSET)) {
+                      dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl;
+                      exit(1);
+                  }
+    }
+
+    _last_testcooplevel_result = hr;
+    return SUCCEEDED(hr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_window_move
+//       Access:
+//  Description: we receive the new x and y position of the client
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::adjust_view_rect(int x, int y) {
+    if (_view_rect.left != x || _view_rect.top != y) {
+        _view_rect.right = x + _view_rect.right - _view_rect.left;
+        _view_rect.left = x;
+        _view_rect.bottom = y + _view_rect.bottom - _view_rect.top;
+        _view_rect.top = y;
+
+//  set_clipper(clip_rect);
+    }
+}
+
+#if 0
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::save_mipmap_images
+//       Access: Protected
+//  Description: Saves out each mipmap level of the indicated texture
+//               (which must also be the currently active texture in
+//               the GL state) as a separate image file to disk.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::read_mipmap_images(Texture *tex) {
+   Filename filename = tex->get_name();
+   string name;
+   if (filename.empty()) {
+     static index = 0;
+     name = "texture" + format_string(index);
+     index++;
+   } else {
+     name = filename.get_basename_wo_extension();
+   }
+
+   PixelBuffer *pb = tex->get_ram_image();
+   nassertv(pb != (PixelBuffer *)NULL);
+
+   GLenum external_format = get_external_image_format(pb->get_format());
+   GLenum type = get_image_type(pb->get_image_type());
+
+   int xsize = pb->get_xsize();
+   int ysize = pb->get_ysize();
+
+   // Specify byte-alignment for the pixels on output.
+   glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+   int mipmap_level = 0;
+   do {
+     xsize = max(xsize, 1);
+     ysize = max(ysize, 1);
+
+     PT(PixelBuffer) mpb =
+       new PixelBuffer(xsize, ysize, pb->get_num_components(),
+                       pb->get_component_width(), pb->get_image_type(),
+                       pb->get_format());
+     glGetTexImage(GL_TEXTURE_2D, mipmap_level, external_format,
+                   type, mpb->_image);
+     Filename mipmap_filename = name + "_" + format_string(mipmap_level) + ".pnm";
+     nout << "Writing mipmap level " << mipmap_level
+          << " (" << xsize << " by " << ysize << ") "
+          << mipmap_filename << "\n";
+     mpb->write(mipmap_filename);
+
+     xsize >>= 1;
+     ysize >>= 1;
+     mipmap_level++;
+   } while (xsize > 0 && ysize > 0);
+}
+#endif
+
+
+#if 0
+//-----------------------------------------------------------------------------
+// Name: SetViewMatrix()
+// Desc: Given an eye point, a lookat point, and an up vector, this
+//       function builds a 4x4 view matrix.
+//-----------------------------------------------------------------------------
+HRESULT SetViewMatrix( D3DMATRIX& mat, D3DVECTOR& vFrom, D3DVECTOR& vAt,
+                       D3DVECTOR& vWorldUp ) {
+    // Get the z basis vector, which points straight ahead. This is the
+    // difference from the eyepoint to the lookat point.
+    D3DVECTOR vView = vAt - vFrom;
+
+    float fLength = Magnitude( vView );
+    if (fLength < 1e-6f)
+        return E_INVALIDARG;
+
+    // Normalize the z basis vector
+    vView /= fLength;
+
+    // Get the dot product, and calculate the projection of the z basis
+    // vector onto the up vector. The projection is the y basis vector.
+    float fDotProduct = DotProduct( vWorldUp, vView );
+
+    D3DVECTOR vUp = vWorldUp - fDotProduct * vView;
+
+    // If this vector has near-zero length because the input specified a
+    // bogus up vector, let's try a default up vector
+    if (1e-6f > ( fLength = Magnitude( vUp ) )) {
+        vUp = D3DVECTOR( 0.0f, 1.0f, 0.0f ) - vView.y * vView;
+
+        // If we still have near-zero length, resort to a different axis.
+        if (1e-6f > ( fLength = Magnitude( vUp ) )) {
+            vUp = D3DVECTOR( 0.0f, 0.0f, 1.0f ) - vView.z * vView;
+
+            if (1e-6f > ( fLength = Magnitude( vUp ) ))
+                return E_INVALIDARG;
+        }
+    }
+
+    // Normalize the y basis vector
+    vUp /= fLength;
+
+    // The x basis vector is found simply with the cross product of the y
+    // and z basis vectors
+    D3DVECTOR vRight = CrossProduct( vUp, vView );
+
+    // Start building the matrix. The first three rows contains the basis
+    // vectors used to rotate the view to point at the lookat point
+    mat._11 = vRight.x;  mat._12 = vUp.x;  mat._13 = vView.x;  mat._14 = 0.0f;
+    mat._21 = vRight.y;  mat._22 = vUp.y;  mat._23 = vView.y;  mat._24 = 0.0f;
+    mat._31 = vRight.z;  mat._32 = vUp.z;  mat._33 = vView.z;  mat._34 = 0.0f;
+
+    // Do the translation values (rotations are still about the eyepoint)
+    mat._41 = - DotProduct( vFrom, vRight );
+    mat._42 = - DotProduct( vFrom, vUp );
+    mat._43 = - DotProduct( vFrom, vView );
+    mat._44 = 1.0f;
+
+    return S_OK;
+}
+
+#endif
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::prepare_geom_node
+//       Access: Public, Virtual
+//  Description: Prepares the indicated GeomNode for retained-mode
+//               rendering.  If this function returns non-NULL, the
+//               value returned will be passed back to a future call
+//               to draw_geom_node(), which is expected to draw the
+//               contents of the node.
+////////////////////////////////////////////////////////////////////
+GeomNodeContext *DXGraphicsStateGuardian::
+prepare_geom_node(GeomNode *node) {
+
+
+ if(link_tristrips) {
+  for(int iGeom=0;iGeom<node->get_num_geoms();iGeom++) {
+    dDrawable *drawable1 = node->get_geom(iGeom);
+    
+    if(!drawable1->is_of_type(Geom::get_class_type()))
+      continue;
+    
+    Geom *geomptr=DCAST(Geom, drawable1);
+    assert(geomptr!=NULL);
+    
+    if(!geomptr->is_of_type(GeomTristrip::get_class_type()))
+      continue;
+    
+    GeomTristrip *me = DCAST(GeomTristrip, geomptr);
+    assert(me!=NULL);
+    
+    int nPrims=me->get_num_prims();
+    if(nPrims==1)
+      continue;
+    
+    if(dxgsg_cat.is_debug()) {
+       static bool bPrintedMsg=false;
+       if(!bPrintedMsg) {
+           dxgsg_cat.debug() << "linking tristrips together with degenerate tris\n";
+           bPrintedMsg=true;
+       }
+    }
+
+    bool bStripReversalNeeded;
+    unsigned int nOrigTotalVerts=0;
+    PTA_int new_lengths;
+    int p;
+
+    // sum things up so can reserve space for new vecs
+    for(p=0;p<nPrims;p++) {
+      nOrigTotalVerts+=me->get_length(p);
+    }
+
+    // could compute it exactly if I wanted to reproduce all the cTotalVertsOutputSoFar logic in the loop below
+    // might save on memory reallocations. try to overestimate using *3.
+    int cEstimatedTotalVerts=nOrigTotalVerts+nPrims*3-2;
+
+    #define INIT_ATTRVARS(ATTRNAME,PTA_TYPENAME)                                                 \
+       PTA_##PTA_TYPENAME old_##ATTRNAME##s,new_##ATTRNAME##s;                                   \
+       PTA_ushort old_##ATTRNAME##_indices,new_##ATTRNAME##_indices;                             \
+       GeomBindType ATTRNAME##binding;                                                           \
+       me->get_##ATTRNAME##s(old_##ATTRNAME##s, ATTRNAME##binding, old_##ATTRNAME##_indices);    \
+                                                                                                 \
+       PTA_##PTA_TYPENAME::iterator old_##ATTRNAME##_iter=old_##ATTRNAME##s.begin();             \
+       PTA_ushort::iterator old_##ATTRNAME##index_iter;                                          \
+                                                                                                 \
+       if((ATTRNAME##binding!=G_OFF)&&(ATTRNAME##binding!=G_OVERALL)) {                          \
+         if(old_##ATTRNAME##_indices!=NULL) {                                                    \
+             old_##ATTRNAME##index_iter=old_##ATTRNAME##_indices.begin();                        \
+             new_##ATTRNAME##_indices.reserve(cEstimatedTotalVerts);                             \
+         } else {                                                                                \
+             new_##ATTRNAME##s.reserve(cEstimatedTotalVerts);                                    \
+         }                                                                                       \
+       }
+
+    INIT_ATTRVARS(coord,Vertexf);
+    INIT_ATTRVARS(color,Colorf);
+    INIT_ATTRVARS(normal,Normalf);
+    INIT_ATTRVARS(texcoord,TexCoordf);
+
+#define IsOdd(X) (((X) & 0x1)!=0)
+
+    uint cTotalVertsOutputSoFar=0;
+    int nVerts;
+    bool bAddExtraStartVert;
+
+    for(p=0;p<nPrims;p++) {
+      nVerts=me->get_length(p);
+
+         // if bStripStateStartsBackfacing, then if the current strip
+         // ends frontfacing, we can fix the problem by just reversing
+         // the current strip order.  But if the current strip ends
+         // backfacing, this will not work, since last tri is encoded for backfacing slot
+         // so it will be incorrectly backfacing when you put it in a backfacing
+         // slot AND reverse the vtx order.
+      
+         // insert an extra pad vertex at the beginning to force the
+         // strip backface-state parity to change (more expensive, since
+         // we make 1 more degen tri).  We always want the first tri
+         // to start in a front-facing slot (unless it's a reversed end
+
+         bStripReversalNeeded = false;      
+         bAddExtraStartVert=false;
+
+         if(p==0) {
+           cTotalVertsOutputSoFar+=nVerts+1;
+         } else {
+            if(!IsOdd(cTotalVertsOutputSoFar)) {
+               // we're starting on a backfacing slot
+               if(IsOdd(nVerts)) {
+                  bStripReversalNeeded = true;
+               } else {
+                bAddExtraStartVert=true;
+                cTotalVertsOutputSoFar++;
+               }
+            }
+
+            cTotalVertsOutputSoFar+=nVerts+2;
+            if(p==nPrims-1)
+              cTotalVertsOutputSoFar--;
+         }
+                
+#define PERVERTEX_ATTRLOOP(OLDVERT_ITERATOR,NEWVERT_VECTOR,VECLENGTH,NUMSTARTPADDINGATTRS,NUMENDPADDINGATTRS)    \
+          if(bStripReversalNeeded) {                                                        \
+              /* to preserve normal-direction property for backface-cull purposes, */       \
+              /* vtx order must be reversed*/                                               \
+                                                                                            \
+              OLDVERT_ITERATOR+=((VECLENGTH)-1);  /* start at last vert, and go back*/      \
+              /*dxgsg_cat.debug() << "doing reversal on strip " << p << " of length " << nVerts << endl;*/ \
+                                                                                            \
+              if(p!=0) {                                                                    \
+                 /* copy first vert twice to link with last strip*/                         \
+                 for(int i=0;i<NUMSTARTPADDINGATTRS;i++)                                    \
+                      NEWVERT_VECTOR.push_back(*OLDVERT_ITERATOR);                          \
+              }                                                                             \
+                                                                                            \
+              for(int v=0;v<(VECLENGTH);v++,OLDVERT_ITERATOR--) {                           \
+                  NEWVERT_VECTOR.push_back(*OLDVERT_ITERATOR);                              \
+              }                                                                             \
+                                                                                            \
+              OLDVERT_ITERATOR++;                                                           \
+                                                                                            \
+              if(p!=(nPrims-1)) {                                                           \
+                  /* copy last vert twice to link to next strip  */                         \
+                 for(int i=0;i<NUMENDPADDINGATTRS;i++)                                      \
+                    NEWVERT_VECTOR.push_back(*OLDVERT_ITERATOR);                            \
+              }                                                                             \
+                                                                                            \
+              OLDVERT_ITERATOR+=(VECLENGTH);                                                \
+                                                                                            \
+          } else {                                                                          \
+              if(p!=0) {                                                                    \
+                 /* copy first vert twice to link with last strip*/                         \
+                 for(int i=0;i<NUMSTARTPADDINGATTRS;i++)                                    \
+                      NEWVERT_VECTOR.push_back(*OLDVERT_ITERATOR);                          \
+              }                                                                             \
+                                                                                            \
+              for(int v=0;v<(VECLENGTH);v++,OLDVERT_ITERATOR++)                             \
+                  NEWVERT_VECTOR.push_back(*OLDVERT_ITERATOR);                              \
+                                                                                            \
+              if(p!=(nPrims-1)) {                                                           \
+                  /* copy last vert twice to link to next strip  */                         \
+                 for(int i=0;i<NUMENDPADDINGATTRS;i++)                                      \
+                    NEWVERT_VECTOR.push_back(*(OLDVERT_ITERATOR-1));                        \
+              }                                                                             \
+          }
+
+
+#define CONVERT_ATTR_VECTOR(ATTRNAME)                                                          \
+      switch(ATTRNAME##binding) {                                                              \
+            case G_OFF:                                                                        \
+            case G_OVERALL:                                                                    \
+                break;                                                                         \
+                                                                                               \
+            case G_PER_PRIM: {                                                                 \
+              /* must convert to per-component*/                                               \
+              /* nPrims*2+origTotalVerts-2 components  */                                      \
+                int veclength=nVerts+2;                                                        \
+                if((p==0)||(p==(nPrims-1)))                                                    \
+                    veclength--;                                                               \
+                if(bAddExtraStartVert)                                                         \
+                   veclength++;                                                                \
+                                                                                               \
+                if(old_##ATTRNAME##_indices!=NULL) {                                           \
+                    for(int v=0;v<veclength;v++)                                               \
+                       new_##ATTRNAME##_indices.push_back(*old_##ATTRNAME##index_iter);        \
+                                                                                               \
+                    old_##ATTRNAME##index_iter++; /* move on to val for next strip*/           \
+                 } else {                                                                      \
+                      for(int v=0;v<veclength;v++)                                             \
+                         new_##ATTRNAME##s.push_back(*old_##ATTRNAME##_iter);                  \
+                                                                                               \
+                      old_##ATTRNAME##_iter++; /* move on to val for next strip*/              \
+                 }                                                                             \
+                 break;                                                                        \
+             }                                                                                 \
+                                                                                               \
+            case G_PER_COMPONENT:                                                              \
+            case G_PER_VERTEX: {                                                               \
+                int veclength,numstartcopies,numendcopies;                                     \
+                                                                                               \
+                if(ATTRNAME##binding==G_PER_VERTEX) {                                          \
+                    veclength=nVerts;                                                          \
+                    numstartcopies=numendcopies=1;                                             \
+                } else {                                                                       \
+                    veclength=nVerts-2;                                                        \
+                    numstartcopies=numendcopies=2;                                             \
+                }                                                                              \
+                if(bAddExtraStartVert)                                                         \
+                   numstartcopies++;                                                           \
+                                                                                               \
+                if(old_##ATTRNAME##_indices!=NULL) {                                           \
+                    PERVERTEX_ATTRLOOP(old_##ATTRNAME##index_iter,new_##ATTRNAME##_indices,veclength,numstartcopies,numendcopies); \
+                } else {                                                                       \
+                    /* non-indexed case */                                                     \
+                    PERVERTEX_ATTRLOOP(old_##ATTRNAME##_iter,new_##ATTRNAME##s,veclength,numstartcopies,numendcopies); \
+                }                                                                              \
+            }                                                                                  \
+            break;                                                                             \
+      }                                                                                        \
+
+      
+      CONVERT_ATTR_VECTOR(coord);
+
+      #ifdef _DEBUG
+         if(old_coord_indices==NULL)
+           assert(cTotalVertsOutputSoFar==new_coords.size());
+         else 
+           assert(cTotalVertsOutputSoFar==new_coord_indices.size());
+      #endif
+
+      CONVERT_ATTR_VECTOR(color);
+      CONVERT_ATTR_VECTOR(normal);
+      CONVERT_ATTR_VECTOR(texcoord);
+
+    }  // end per-Prim (strip) loop
+
+    if(old_coord_indices!=NULL)  {
+        me->set_coords(old_coords, new_coord_indices);
+        new_lengths.push_back(new_coord_indices.size());
+    } else {
+        me->set_coords(new_coords);
+        new_lengths.push_back(new_coords.size());
+    }
+
+    me->set_lengths(new_lengths);
+    me->set_num_prims(1);
+
+    #define SET_NEW_ATTRIBS(ATTRNAME)                                                                 \
+    if((ATTRNAME##binding!=G_OFF) && (ATTRNAME##binding!=G_OVERALL)) {                                \
+        if(ATTRNAME##binding==G_PER_PRIM)                                                             \
+           ATTRNAME##binding=G_PER_COMPONENT;                                                         \
+        if(old_##ATTRNAME##_indices==NULL) {                                                          \
+           me->set_##ATTRNAME##s(new_##ATTRNAME##s, ATTRNAME##binding);                               \
+        } else {                                                                                      \
+           me->set_##ATTRNAME##s(old_##ATTRNAME##s, ATTRNAME##binding, new_##ATTRNAME##_indices);     \
+        }                                                                                             \
+    }
+    /*    
+    int ii;
+    for( ii=0;ii<old_coords.size();ii++) 
+        dxgsg_cat.debug() << "old coord[" << ii <<"] " << old_coords[ii] << endl;
+    dxgsg_cat.debug() << "=================\n";
+    for(ii=0;ii<new_coords.size();ii++) 
+        dxgsg_cat.debug() << "new coord[" << ii <<"] " << new_coords[ii] << endl;
+    dxgsg_cat.debug() << "=================\n";
+    
+    for( ii=0;ii<old_normals.size();ii++) 
+        dxgsg_cat.debug() << "old norm[" << ii <<"] " << old_normals[ii] << endl;
+    dxgsg_cat.debug() << "=================\n";
+    for(ii=0;ii<new_normals.size();ii++) 
+        dxgsg_cat.debug() << "new norm[" << ii <<"] " << new_normals[ii] << endl;
+    
+    if(old_color_indices!=NULL) {
+    
+    for( ii=0;ii<old_color_indices.size();ii++) 
+        dxgsg_cat.debug() << "old colorindex[" << ii <<"] " << old_color_indices[ii] << endl;
+    dxgsg_cat.debug() << "=================\n";
+    for( ii=0;ii<new_color_indices.size();ii++) 
+        dxgsg_cat.debug() << "new colorindex[" << ii <<"] " << new_color_indices[ii] << endl;
+    }
+    */
+
+    SET_NEW_ATTRIBS(color);
+    SET_NEW_ATTRIBS(normal);
+    SET_NEW_ATTRIBS(texcoord);
+
+    me->make_dirty();
+  }
+ } // if(link_tristrips)
+
+  // for now, only using vertexbufs for static Geom, so
+  // Make sure we have at least some static Geoms in the GeomNode;
+  int cNumVerts=0,i,num_geoms = node->get_num_geoms();
+
+  // need to always put in space for color because we might have some scene-graph color we need to add?
+  // will that even work?  I think we'd have to overwrite all the VB colors dynamically, and then restore
+  // them from somewhere if the global color goes away
+
+  DWORD fvfFlags=D3DFVF_XYZ;// | D3DFVF_DIFFUSE;
+
+  for (i = 0; (i < num_geoms); i++) {
+    dDrawable *drawable1 = node->get_geom(i);
+    if(!drawable1->is_of_type(Geom::get_class_type()))
+      continue;
+    
+    Geom *geom=DCAST(Geom, drawable1);
+    assert(geom!=NULL);
+
+    DWORD new_fvfFlags=D3DFVF_XYZ;
+
+    if(!geom->is_dynamic()) {
+        cNumVerts+=geom->get_num_vertices();
+        if(geom->get_binding(G_COLOR) != G_OFF)
+            new_fvfFlags |= D3DFVF_DIFFUSE;
+        if(geom->get_binding(G_NORMAL) != G_OFF)
+            new_fvfFlags |= D3DFVF_NORMAL;
+        if(geom->get_binding(G_TEXCOORD) != G_OFF) 
+            new_fvfFlags |= (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+    }
+    if(i!=0) {
+       if(fvfFlags!=new_fvfFlags) {
+           // all primitives within a geom must use the same FVF type for the DrawPrim api
+           dxgsg_cat.error() << "error creating vertex buffer, geoms within geomnode require differing vertex data types!!\n";
+           exit(1);
+       }
+    } else fvfFlags=new_fvfFlags;
+  }
+
+  if(cNumVerts==0) {
+    // Never mind.
+    return (GeomNodeContext *)NULL;
+  }
+
+  if(cNumVerts>D3DMAXNUMVERTICES) {
+      dxgsg_cat.error() << "geom node contains more than " << D3DMAXNUMVERTICES << " vertices, cant create 1 vertex buffer\n";
+      exit(1);
+  }
+
+  // Ok, we've got something; use it.
+  DXGeomNodeContext *dx_gnc = new DXGeomNodeContext(node);
+
+  assert(dx_gnc!=NULL);
+
+  // right now there is a 1-1 correspondence b/w vertbufs and geomnodecontexts.
+  // later multiple geomnodecontexts will use the same vertbuf
+
+  HRESULT hr;
+  LPDIRECT3D7 pD3D;
+
+  assert(_d3dDevice!=NULL);
+  hr=_d3dDevice->GetDirect3D(&pD3D);
+  assert(!FAILED(hr));
+  LPDIRECT3DVERTEXBUFFER7 pD3DVertexBuffer;
+  DX_DECLARE_CLEAN(D3DVERTEXBUFFERDESC, VBdesc);
+
+  VBdesc.dwCaps = D3DVBCAPS_WRITEONLY;
+  VBdesc.dwCaps |= _bIsTNLDevice ? 0x0 : D3DVBCAPS_SYSTEMMEMORY;
+  VBdesc.dwFVF=fvfFlags;
+  VBdesc.dwNumVertices=cNumVerts;
+
+  hr=pD3D->CreateVertexBuffer(&VBdesc,&pD3DVertexBuffer,0x0);
+  if(FAILED(hr)) {
+    dxgsg_cat.error() << "error creating vertex buffer: " << ConvD3DErrorToString(hr) << endl;
+    delete dx_gnc;
+    exit(1);
+  }
+
+  dx_gnc->_pVB = pD3DVertexBuffer;
+
+  if(!_bIsTNLDevice) {
+      // create VB for ProcessVerts to xform to
+
+      fvfFlags&=~D3DFVF_XYZ;    // switch to xformed vert type
+      fvfFlags&=~D3DFVF_NORMAL; // xformed verts are also lighted, so no normals allowed
+      fvfFlags|=D3DFVF_XYZRHW; 
+      VBdesc.dwFVF=fvfFlags;
+
+      hr=pD3D->CreateVertexBuffer(&VBdesc,&pD3DVertexBuffer,0x0);
+      if(FAILED(hr)) {
+          dxgsg_cat.error() << "error creating xformed vertex buffer: " << ConvD3DErrorToString(hr) << endl;
+          delete dx_gnc;
+          exit(1);
+      }
+
+      dx_gnc->_pXformed_VB = pD3DVertexBuffer;
+  }
+
+  pD3D->Release();
+
+  dx_gnc->_num_verts=0;
+  dx_gnc->_start_index=0;   
+
+  LPVOID pVertData=NULL;
+  DWORD dwVBFlags = DDLOCK_NOOVERWRITE | DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR |
+                    DDLOCK_WAIT | DDLOCK_DISCARDCONTENTS;
+
+  hr=dx_gnc->_pVB->Lock(dwVBFlags,&pVertData,NULL);
+  if(FAILED(hr)) {
+        dxgsg_cat.error() << "error locking vertex buffer: " << ConvD3DErrorToString(hr) << endl;
+        delete dx_gnc;
+        exit(1);
+  }
+
+  assert(pVertData!=NULL);
+  _pCurrentGeomContext = dx_gnc;
+  _pCurrentGeomContext->_pEndofVertData=(BYTE*)pVertData;
+  _bDrawPrimDoSetupVertexBuffer = true;
+
+
+  for (i = 0; (i < num_geoms); i++) {
+    dDrawable *drawable1 = node->get_geom(i);
+    if(!drawable1->is_of_type(Geom::get_class_type()))
+      continue;
+    
+    Geom *geom=DCAST(Geom, drawable1);
+    assert(geom!=NULL);
+
+    if(geom->is_dynamic()) {
+      dx_gnc->_other_geoms.push_back(geom);
+    } else {
+       dx_gnc->_cached_geoms.push_back(geom);
+       node->get_geom(i)->draw(this);
+    }
+  }
+
+  _bDrawPrimDoSetupVertexBuffer = false;
+  _pCurrentGeomContext->_pEndofVertData=NULL;  
+  _pCurrentGeomContext = NULL;
+
+  hr=dx_gnc->_pVB->Unlock();
+  if(FAILED(hr)) {
+      dxgsg_cat.error() << "error unlocking vertex buffer: " << ConvD3DErrorToString(hr) << endl;
+      delete dx_gnc;
+      exit(1);
+  }
+
+  assert(cNumVerts==dx_gnc->_num_verts);
+
+  hr=dx_gnc->_pVB->Optimize(_d3dDevice,0x0);
+  if(FAILED(hr)) {
+      dxgsg_cat.error() << "error optimizing vertex buffer: " << ConvD3DErrorToString(hr) << endl;
+      delete dx_gnc;
+      exit(1);
+  }
+
+  if(dxgsg_cat.is_spam())
+      dxgsg_cat.spam() << "creating vertex buffer of size: " << cNumVerts << endl;
+
+#if 0  //DO_PSTATS
+  float num_verts_after = 
+    _vertices_tristrip_pcollector.get_level() +
+    _vertices_trifan_pcollector.get_level() +
+    _vertices_tri_pcollector.get_level() +
+    _vertices_other_pcollector.get_level();
+  float num_verts = num_verts_after - num_verts_before;
+  ggnc->_num_verts = (int)(num_verts + 0.5);
+#endif
+
+  bool inserted = mark_prepared_geom_node(dx_gnc);
+
+  // If this assertion fails, the same GeomNode was prepared twice,
+  // which shouldn't be possible, since the GeomNode itself should
+  // detect this.
+  nassertr(inserted, NULL);
+
+  return dx_gnc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::draw_geom_node
+//       Access: Public, Virtual
+//  Description: Draws a GeomNode previously indicated by a call to
+//               prepare_geom_node().
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+draw_geom_node(GeomNode *node, GeomNodeContext *gnc) {
+
+  uint i,num_geoms = node->get_num_geoms();
+
+  if (gnc == (GeomNodeContext *)NULL) {
+
+    // We don't have a saved context; just draw the GeomNode in
+    // immediate mode.
+    for (i = 0; i < num_geoms; i++) {
+        node->get_geom(i)->draw(this);
+    }
+    return;
+  }
+
+  // We do have a saved context; use it.
+  add_to_geom_node_record(gnc);
+  DXGeomNodeContext *dx_gnc = DCAST(DXGeomNodeContext, gnc);
+
+  #ifdef _DEBUG
+     assert(dx_gnc->_pVB!=NULL);
+     assert((!_bIsTNLDevice)==(dx_gnc->_pXformed_VB!=NULL));
+  #endif
+  
+  if(!_bIsTNLDevice) {
+      HRESULT hr;
+    
+      DWORD PVOp=D3DVOP_CLIP | D3DVOP_TRANSFORM | D3DVOP_EXTENTS;
+    
+      D3DVERTEXBUFFERDESC VBdesc;
+      VBdesc.dwSize=sizeof(VBdesc);
+      hr=dx_gnc->_pVB->GetVertexBufferDesc(&VBdesc);   // would be useful to keep fvf in vertbuf struct to avoid having to do this
+      if(FAILED(hr)) {
+        dxgsg_cat.error() << "error in getvbdesc: " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+      }
+    
+      if(_lighting_enabled && (VBdesc.dwFVF & D3DFVF_NORMAL)) {
+          PVOp|=D3DVOP_LIGHT;
+      }
+    
+      hr=dx_gnc->_pXformed_VB->ProcessVertices(PVOp,0,dx_gnc->_num_verts,dx_gnc->_pVB,0,_d3dDevice,0x0);
+      if(FAILED(hr)) {
+        dxgsg_cat.error() << "error in ProcessVertices: " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+      }
+
+      // disable clipping, since VB is already xformed and clipped
+      if(_clipping_enabled)
+          _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, false);
+  }
+
+  // assume we need gouraud for now.  we can make this more complex to select flat conditionally later
+  set_shademode(D3DSHADE_GOURAUD);
+  int cur_startvert=dx_gnc->_start_index;
+
+  for (i = 0; i < dx_gnc->_cached_geoms.size(); i++) {
+    // I think we can just draw them directly? since cant account for issued stuff (since 
+    // its already encoded in the VB), and we can set the renderstates here (e.g. gouraud mode)
+    //      dx_gnc->_cached_geoms[i]->draw(this);
+
+     DPInfo *dpi=&dx_gnc->_PrimInfo[i];
+
+     LPDIRECT3DVERTEXBUFFER7 pVB;
+     if(_bIsTNLDevice) {
+         pVB=dx_gnc->_pVB;
+     } else {
+         pVB=dx_gnc->_pXformed_VB;
+     }
+
+     HRESULT hr = _d3dDevice->DrawPrimitiveVB(dpi->primtype,pVB,cur_startvert,dpi->nVerts,0x0);
+     TestDrawPrimFailure(DrawPrim,hr,_pDD,dpi->nVerts,0);
+
+     cur_startvert+=dpi->nVerts;
+  }
+
+  if((!_bIsTNLDevice) && _clipping_enabled)
+      _d3dDevice->SetRenderState(D3DRENDERSTATE_CLIPPING, true);
+
+  // Also draw all the dynamic Geoms.
+  for (i = 0; i < dx_gnc->_other_geoms.size(); i++) {
+      dx_gnc->_other_geoms[i]->draw(this);
+  }
+
+#if 0 //def DO_PSTATS
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    _vertices_display_list_pcollector.add_level(dx_gnc->_num_verts);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian::release_geom_node
+//       Access: Public, Virtual
+//  Description: Frees the resources previously allocated via a call
+//               to prepare_geom_node(), including deleting the
+//               GeomNodeContext itself, if necessary.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian::
+release_geom_node(GeomNodeContext *gnc) {
+  if (gnc != (GeomNodeContext *)NULL) {
+    DXGeomNodeContext *dx_gnc = DCAST(DXGeomNodeContext, gnc);
+
+    bool erased = unmark_prepared_geom_node(dx_gnc);
+
+    // If this assertion fails, a GeomNode was released that hadn't
+    // been prepared (or a GeomNode was released twice).
+    nassertv(erased);
+    
+    dx_gnc->_node->clear_gsg(this);
+    delete dx_gnc;  // should release vertex buffer
+  }
+}

+ 494 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -0,0 +1,494 @@
+// Filename: dxGraphicsStateGuardian.h
+// Created by:  mike (02Feb99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGRAPHICSSTATEGUARDIAN8_H
+#define DXGRAPHICSSTATEGUARDIAN8_H
+
+//#define GSG_VERBOSE
+
+#include <pandabase.h>
+#include <graphicsStateGuardian.h>
+#include <geomprimitives.h>
+#include <texture.h>
+#include <pixelBuffer.h>
+#include <displayRegion.h>
+#include <material.h>
+#include <textureApplyProperty.h>
+#include <depthTestProperty.h>
+#include <stencilProperty.h>
+#include <fog.h>
+#include <renderModeProperty.h>
+#include <colorMatrixTransition.h>
+#include <alphaTransformTransition.h>
+#include <pointerToArray.h>
+#include <planeNode.h>
+
+#include "dxGeomNodeContext8.h"
+#include "dxTextureContext8.h"
+
+extern char * ConvD3DErrorToString(const HRESULT &error);   // defined in wdxGraphicsPipe.cxx
+
+class PlaneNode;
+class Light;
+
+#ifdef GSG_VERBOSE
+ostream &output_gl_enum(ostream &out, GLenum v);
+INLINE ostream &operator << (ostream &out, GLenum v) {
+  return output_gl_enum(out, v);
+}
+#endif
+
+#ifdef DO_PSTATS
+#define DO_PSTATS_STUFF(XX) XX;
+#else
+#define DO_PSTATS_STUFF(XX)
+#endif
+
+#define DX_DECLARE_CLEAN(type, var) \
+    type var;                       \
+    ZeroMemory(&var, sizeof(type));  \
+    var.dwSize = sizeof(type);
+
+// #define DEBUG_RELEASES
+
+// this is bDoDownToZero argument to RELEASE()
+#define RELEASE_DOWN_TO_ZERO true
+
+#ifdef DEBUG_RELEASES
+#define RELEASE(OBJECT,MODULE,DBGSTR,bDoDownToZero)             \
+   if(((OBJECT)!=NULL)&&(!IsBadWritePtr((OBJECT),4))) {         \
+        refcnt = (OBJECT)->Release();                           \
+        MODULE##_cat.debug() << DBGSTR << " released, refcnt = " << refcnt << endl;  \
+        if((bDoDownToZero) && (refcnt>0)) {                     \
+              MODULE##_cat.warning() << DBGSTR << " released but still has a non-zero refcnt(" << refcnt << "), multi-releasing it down to zero!\n"; \
+              do {                                \
+                refcnt = (OBJECT)->Release();     \
+              } while(refcnt>0);                  \
+        }                                         \
+        (OBJECT) = NULL;                          \
+      } else {                                    \
+        MODULE##_cat.debug() << DBGSTR << " not released, ptr == NULL" << endl;  \
+      } 
+
+#define PRINTREFCNT(OBJECT,STR)  {  (OBJECT)->AddRef();  dxgsg_cat.debug() << STR << " refcnt = " << (OBJECT)->Release() << endl; }
+#else
+#define RELEASE(OBJECT,MODULE,DBGSTR,bDoDownToZero)     \
+   if(((OBJECT)!=NULL)&&(!IsBadWritePtr((OBJECT),4))) { \
+        refcnt=(OBJECT)->Release();                     \
+        if((bDoDownToZero) && (refcnt>0)) {             \
+              MODULE##_cat.warning() << DBGSTR << " released but still has a non-zero refcnt(" << refcnt << "), multi-releasing it down to zero!\n"; \
+              do {                                \
+                refcnt = (OBJECT)->Release();     \
+              } while(refcnt>0);                  \
+        }                                         \
+        (OBJECT) = NULL;                          \
+   }
+
+#define PRINTREFCNT(OBJECT,STR)  
+#endif    
+
+//#if defined(NOTIFY_DEBUG) || defined(DO_PSTATS)
+#ifdef _DEBUG
+// This function now serves both to print a debug message to the
+// console, as well as to notify PStats about the change in texture
+// memory.  Thus, we compile it in if we are building with support for
+// either notify debug messages or PStats; otherwise, we compile it
+// out.
+extern void dbgPrintVidMem(LPDIRECTDRAW7 pDD, LPDDSCAPS2 lpddsCaps,const char *pMsg);
+#define PRINTVIDMEM(pDD,pCaps,pMsg) dbgPrintVidMem(pDD,pCaps,pMsg)
+#else
+#define PRINTVIDMEM(pDD,pCaps,pMsg)
+#endif
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXGraphicsStateGuardian
+// Description : A GraphicsStateGuardian specialized for rendering
+//               into DX.  There should be no DX calls
+//               outside of this object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXGraphicsStateGuardian : public GraphicsStateGuardian {
+  friend class wdxGraphicsWindow;
+  friend class DXTextureContext;
+
+public:
+  DXGraphicsStateGuardian(GraphicsWindow *win);
+  ~DXGraphicsStateGuardian();
+
+  virtual void reset();
+
+  virtual void clear(const RenderBuffer &buffer);
+  virtual void clear(const RenderBuffer &buffer, const DisplayRegion* region);
+
+  virtual void prepare_display_region();
+
+  virtual void render_frame();
+  virtual void render_scene(Node *root, ProjectionNode *projnode);
+  virtual void render_subgraph(RenderTraverser *traverser,
+                   Node *subgraph, ProjectionNode *projnode,
+                   const AllTransitionsWrapper &net_trans);
+  virtual void render_subgraph(RenderTraverser *traverser,
+                   Node *subgraph,
+                   const AllTransitionsWrapper &net_trans);
+
+  virtual void draw_point(GeomPoint *geom, GeomContext *gc);
+  virtual void draw_line(GeomLine *geom, GeomContext *gc);
+  virtual void draw_linestrip(GeomLinestrip *geom, GeomContext *gc);
+  void draw_linestrip_base(Geom *geom, GeomContext *gc, bool bConnectEnds);
+  virtual void draw_sprite(GeomSprite *geom, GeomContext *gc);
+  virtual void draw_polygon(GeomPolygon *geom, GeomContext *gc);
+  virtual void draw_quad(GeomQuad *geom, GeomContext *gc);
+  virtual void draw_tri(GeomTri *geom, GeomContext *gc);
+  virtual void draw_tristrip(GeomTristrip *geom, GeomContext *gc);
+  virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc);
+  virtual void draw_sphere(GeomSphere *geom, GeomContext *gc);
+
+  virtual GeomNodeContext *prepare_geom_node(GeomNode *node);
+  virtual void draw_geom_node(GeomNode *node, GeomNodeContext *gnc);
+  virtual void release_geom_node(GeomNodeContext *gnc);
+
+  virtual TextureContext *prepare_texture(Texture *tex);
+  virtual void apply_texture(TextureContext *tc);
+  virtual void release_texture(TextureContext *tc);
+
+  virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr);
+  virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr,
+                            const RenderBuffer &rb);
+  virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr);
+  virtual void draw_texture(TextureContext *tc, const DisplayRegion *dr,
+                            const RenderBuffer &rb);
+
+  virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb);
+  virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
+                const DisplayRegion *dr);
+
+  virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr);
+  virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                                 const RenderBuffer &rb);
+  virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                 const NodeTransitions& na=NodeTransitions());
+  virtual void draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                                 const RenderBuffer &rb,
+                 const NodeTransitions& na=NodeTransitions());
+
+  virtual void apply_material(const Material *material);
+  virtual void apply_fog(Fog *fog);
+
+  virtual void apply_light(PointLight* light);
+  virtual void apply_light(DirectionalLight* light);
+  virtual void apply_light(Spotlight* light);
+  virtual void apply_light(AmbientLight* light);
+
+  virtual void issue_transform(const TransformTransition *attrib);
+  virtual void issue_tex_matrix(const TexMatrixTransition *attrib);
+  virtual void issue_color(const ColorTransition *attrib);
+  virtual void issue_color_transform(const ColorMatrixTransition *);
+  virtual void issue_alpha_transform(const AlphaTransformTransition *);
+  virtual void issue_texture(const TextureTransition *attrib);
+  virtual void issue_light(const LightTransition *attrib);
+  virtual void issue_material(const MaterialTransition *attrib);
+  virtual void issue_render_mode(const RenderModeTransition *attrib);
+  virtual void issue_color_blend(const ColorBlendTransition *attrib);
+  virtual void issue_texture_apply(const TextureApplyTransition *attrib);
+  virtual void issue_color_mask(const ColorMaskTransition *attrib);
+  virtual void issue_depth_test(const DepthTestTransition *attrib);
+  virtual void issue_depth_write(const DepthWriteTransition *attrib);
+  virtual void issue_tex_gen(const TexGenTransition *attrib);
+  virtual void issue_cull_face(const CullFaceTransition *attrib);
+  virtual void issue_stencil(const StencilTransition *attrib);
+  virtual void issue_clip_plane(const ClipPlaneTransition *attrib);
+  virtual void issue_transparency(const TransparencyTransition *attrib);
+  virtual void issue_fog(const FogTransition *attrib);
+  virtual void issue_linesmooth(const LinesmoothTransition *attrib);
+
+  virtual bool wants_normals(void) const;
+  virtual bool wants_texcoords(void) const;
+  virtual bool wants_colors(void) const;
+
+  virtual void begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib);
+  virtual void end_decal(GeomNode *base_geom);
+
+  INLINE float compute_distance_to(const LPoint3f &point) const;
+  virtual void set_color_clear_value(const Colorf& value);
+
+public:
+  // recreate_tex_callback needs these to be public
+  LPDIRECT3DDEVICE7 _d3dDevice;
+  LPDDPIXELFORMAT   _pTexPixFmts;
+  int               _cNumTexPixFmts;
+
+protected:
+  void free_pointers();            // free local internal buffers
+  void free_dxgsg_objects(void);   // free the DirectX objects we create
+  virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer,
+                         CPT(DisplayRegion) dr);
+  virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer);
+
+  void set_draw_buffer(const RenderBuffer &rb);
+  void set_read_buffer(const RenderBuffer &rb);
+
+  void bind_texture(TextureContext *tc);
+
+  // for storage of the flexible vertex format
+  char *_pCurFvfBufPtr,*_pFvfBufBasePtr;
+  INLINE void add_to_FVFBuf(void *data,  size_t bytes) ;
+  WORD *_index_buf;  // base of malloced array
+
+  bool                  _dx_ready;
+  HRESULT               _last_testcooplevel_result;
+  bool                  _bIsTNLDevice;
+  LPDIRECTDRAWSURFACE7  _back;
+  LPDIRECTDRAWSURFACE7  _zbuf;
+  LPDIRECT3D7           _d3d;
+  LPDIRECTDRAWSURFACE7  _pri;
+  LPDIRECTDRAW7         _pDD;
+
+  RECT                _view_rect;
+  RECT                clip_rect;
+  HDC               _front_hdc;
+  DXTextureContext  *_pCurTexContext;
+
+  bool              _bTransformIssued;  // decaling needs to tell when a transform has been issued
+  D3DMATRIX         _SavedTransform;   
+
+  RenderBuffer::Type _cur_read_pixel_buffer;  // source for copy_pixel_buffer operation
+
+  D3DDEVICEDESC7    _D3DDevDesc;
+
+  void GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize,
+                    void *pIndexSpace,DWORD dwIndexSpaceByteSize,
+                    D3DVECTOR *pCenter, float fRadius,
+                    DWORD wNumRings, DWORD wNumSections, float sx, float sy, float sz,
+                    DWORD *pNumVertices,DWORD *pNumIndices,DWORD fvfFlags,DWORD dwVertSize);
+  HRESULT RestoreAllVideoSurfaces(void);
+  HRESULT RecreateAllVideoSurfaces(void);
+  HRESULT DeleteAllVideoSurfaces(void);
+
+/*
+  INLINE void enable_multisample_alpha_one(bool val);
+  INLINE void enable_multisample_alpha_mask(bool val);
+  INLINE void enable_multisample(bool val);
+*/  
+
+  INLINE void enable_color_material(bool val);
+  INLINE void enable_clip_plane(int clip_plane, bool val);
+  INLINE void enable_fog(bool val);
+  INLINE void enable_zwritemask(bool val);
+  INLINE void set_shademode(D3DSHADEMODE val);
+
+  INLINE D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm) const;
+  INLINE D3DCMPFUNC get_depth_func_type(DepthTestProperty::Mode m) const;
+  INLINE D3DFOGMODE get_fog_mode_type(Fog::Mode m) const;
+
+  INLINE D3DCMPFUNC get_stencil_func_type(StencilProperty::Mode m) const;
+  INLINE D3DSTENCILOP get_stencil_action_type(StencilProperty::Action a) const;
+
+  INLINE void enable_primitive_clipping(bool val);
+  INLINE void enable_alpha_test(bool val);
+  INLINE void enable_line_smooth(bool val);
+  INLINE void enable_blend(bool val);
+  INLINE void enable_point_smooth(bool val);
+  INLINE void enable_texturing(bool val);
+  INLINE void call_dxLightModelAmbient(const Colorf& color);
+  INLINE void call_dxAlphaFunc(D3DCMPFUNC func, DWORD ref);
+  INLINE void call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc);
+  INLINE void enable_lighting(bool val);
+  INLINE void enable_dither(bool val);
+  INLINE void enable_stencil_test(bool val);
+  bool enable_light(int light, bool val);
+  void report_texmgr_stats();
+  void draw_multitri(Geom *geom, D3DPRIMITIVETYPE tri_id);
+
+  void draw_prim_inner_loop(int nVerts, const Geom *geom, ushort perFlags);
+  void draw_prim_inner_loop_coordtexonly(int nVerts, const Geom *geom);
+  size_t draw_prim_setup(const Geom *geom) ;
+
+  //   for drawing primitives
+  Normalf   p_normal;  // still used to hold G_OVERALL, G_PER_PRIM values
+  TexCoordf p_texcoord;
+  D3DCOLOR  _curD3Dcolor;
+  DWORD     _curFVFflags;
+  DWORD     _perPrim,_perVertex,_perComp;   //  these hold DrawLoopFlags bitmask values
+
+  bool  _issued_color_enabled;      // WBD ADDED
+  bool  _enable_all_color;
+  Colorf _issued_color;           // WBD ADDED
+  D3DCOLOR _issued_color_D3DCOLOR;           // WBD ADDED
+  D3DCOLOR _d3dcolor_clear_value;
+
+  D3DSHADEMODE _CurShadeMode;
+
+  bool _bDrawPrimDoSetupVertexBuffer;       // if true, draw methods just copy vertex data into pCurrentGeomContext
+  DXGeomNodeContext *_pCurrentGeomContext;  // used in vertex buffer setup
+
+  // iterators for primitives
+  Geom::VertexIterator vi;
+  Geom::NormalIterator ni;
+  Geom::TexCoordIterator ti;
+  Geom::ColorIterator ci;
+
+  // these are used for fastpaths that bypass the iterators above
+  // pointers to arrays in current geom, used to traverse indexed and non-indexed arrays
+  Vertexf *_coord_array,*_pCurCoord;
+  ushort *_coordindex_array,*_pCurCoordIndex;
+
+  TexCoordf *_texcoord_array,*_pCurTexCoord;
+  ushort *_texcoordindex_array,*_pCurTexCoordIndex;
+
+/*
+  PTA_Normalf _norms;
+  PTA_Colorf _colors;
+  PTA_ushort _cindexes,_nindexes;
+*/  
+
+  Colorf _lmodel_ambient;
+  float _material_ambient;
+  float _material_diffuse;
+  float _material_specular;
+  float _material_shininess;
+  float _material_emission;
+
+  typedef enum {None,
+                PerVertexFog=D3DRENDERSTATE_FOGVERTEXMODE,
+                PerPixelFog=D3DRENDERSTATE_FOGTABLEMODE
+               } DxgsgFogType;
+  DxgsgFogType _doFogType;
+  bool _fog_enabled;
+/*  
+  TODO: cache fog state
+  float _fog_start;
+  float _fog_end;
+  float _fog_density;
+  float _fog_color;
+*/    
+  float      _alpha_func_ref;
+  D3DCMPFUNC _alpha_func;
+
+  D3DBLEND _blend_source_func;
+  D3DBLEND _blend_dest_func;
+
+  bool _line_smooth_enabled;
+  bool* _light_enabled;      // bool[_max_lights]
+  bool _color_material_enabled;
+  bool _lighting_enabled;
+  bool _lighting_enabled_this_frame;
+  bool _texturing_enabled;
+  bool  _clipping_enabled;
+  bool _dither_enabled;
+  bool _stencil_test_enabled;
+  bool* _clip_plane_enabled;      // bool[_max_clip_planes]
+  bool _blend_enabled;
+  bool _depth_test_enabled;
+  bool _depth_write_enabled;
+  bool _alpha_test_enabled;
+  int _decal_level;
+
+  RenderModeProperty::Mode _current_fill_mode;  //poinr/wireframe/solid
+
+  GraphicsChannel *_panda_gfx_channel;  // cache the 1 channel dx supports
+
+  // Cur Texture State
+  TextureApplyProperty::Mode _CurTexBlendMode;
+  Texture::FilterType _CurTexMagFilter,_CurTexMinFilter;
+  DWORD _CurTexAnisoDegree;
+  Texture::WrapMode _CurTexWrapModeU,_CurTexWrapModeV;
+
+  PTA(Light*) _available_light_ids;
+  int _max_lights;
+  bool* _cur_light_enabled;
+  int _cur_light_id;
+  Colorf _cur_ambient_light;
+  LMatrix4f _current_projection_mat;
+  int _projection_mat_stack_count;
+
+  PTA(PlaneNode*) _available_clip_plane_ids;
+  int _max_clip_planes;
+  bool* _cur_clip_plane_enabled;
+  int _cur_clip_plane_id;
+
+  CPT(DisplayRegion) _actual_display_region;
+
+  // Color/Alpha Matrix Transition stuff
+  INLINE void transform_color(Colorf &InColor,D3DCOLOR &OutColor);
+  bool _color_transform_required;  // _color_transform_enabled || _alpha_transform_enabled
+  bool _color_transform_enabled;
+  bool _alpha_transform_enabled;
+  LMatrix4f _current_color_mat;
+  float _current_alpha_offset;
+  float _current_alpha_scale;
+
+  // vars for frames/sec meter
+  DWORD _start_time;
+  DWORD _start_frame_count;
+  DWORD _cur_frame_count;
+  float _current_fps;
+  DWORD *_fpsmeter_verts;
+  DWORD _fpsmeter_fvfflags;
+  LPDIRECTDRAWSURFACE7 _fpsmeter_font_surf;
+  float _fps_u_usedwidth,_fps_v_usedheight;  // fraction of fps font texture actually used
+  DWORD _fps_vertexsize;   // size of verts used to render fps meter
+  void  SetFPSMeterPosition(RECT &view_rect);
+  void  FillFPSMeterTexture(void);
+
+public:
+  static GraphicsStateGuardian*
+  make_DXGraphicsStateGuardian(const FactoryParams &params);
+
+  static TypeHandle get_class_type(void);
+  static void init_type(void);
+  virtual TypeHandle get_type(void) const;
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+  LPDIRECT3DDEVICE7 GetD3DDevice()  {  return _d3dDevice; }
+  LPDIRECTDRAW7 GetDDInterface()  {  return _pDD; }
+  LPDIRECTDRAWSURFACE7 GetBackBuffer()  {  return _back; }
+  LPDIRECTDRAWSURFACE7 GetZBuffer()  {  return _zbuf; }
+  INLINE void  Set_HDC(HDC hdc)  {  _front_hdc = hdc;  }
+  void adjust_view_rect(int x, int y);
+  INLINE void SetDXReady(bool stat)  {  _dx_ready = stat; }
+  INLINE bool GetDXReady(void)  { return _dx_ready;}
+  void DXGraphicsStateGuardian::SetTextureBlendMode(TextureApplyProperty::Mode TexBlendMode,bool bJustEnable);
+
+  void  dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled);
+
+  #define DO_REACTIVATE_WINDOW true
+  bool  CheckCooperativeLevel(bool bDoReactivateWindow = false);
+
+  void  dx_setup_after_resize(RECT viewrect,HWND mwindow) ;
+  void  show_frame();
+  void  show_full_screen_frame();
+  void  show_windowed_frame();
+  void  dx_init(  LPDIRECTDRAW7     context,
+          LPDIRECTDRAWSURFACE7  pri,
+          LPDIRECTDRAWSURFACE7  back,
+          LPDIRECTDRAWSURFACE7  zbuf,
+          LPDIRECT3D7          d3d,
+          LPDIRECT3DDEVICE7    d3dDevice,
+          RECT  viewrect);
+  
+  friend HRESULT CALLBACK EnumTexFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param );
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#define ISPOW2(X) (((X) & ((X)-1))==0)
+
+#include "dxGraphicsStateGuardian8.I"
+
+#endif
+

+ 39 - 0
panda/src/dxgsg8/dxSavedFrameBuffer8.I

@@ -0,0 +1,39 @@
+// Filename: dxSavedFrameBuffer.I
+// Created by:  drose (06Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXSavedFrameBuffer::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXSavedFrameBuffer::
+DXSavedFrameBuffer(const RenderBuffer &buffer, CPT(DisplayRegion) dr) :
+  SavedFrameBuffer(buffer, dr)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXSavedFrameBuffer::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXSavedFrameBuffer::
+~DXSavedFrameBuffer() {
+}
+

+ 21 - 0
panda/src/dxgsg8/dxSavedFrameBuffer8.cxx

@@ -0,0 +1,21 @@
+// Filename: dxSavedFrameBuffer.cxx
+// Created by:  drose (06Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dxSavedFrameBuffer8.h"
+
+TypeHandle DXSavedFrameBuffer::_type_handle;

+ 64 - 0
panda/src/dxgsg8/dxSavedFrameBuffer8.h

@@ -0,0 +1,64 @@
+// Filename: dxSavedFrameBuffer.h
+// Created by:  drose (06Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXSAVEDFRAMEBUFFER8_H
+#define DXSAVEDFRAMEBUFFER8_H
+
+#include <pandabase.h>
+
+#include <savedFrameBuffer.h>
+#include <texture.h>
+#include <textureContext.h>
+#include <pixelBuffer.h>
+
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXSavedFrameBuffer
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXSavedFrameBuffer : public SavedFrameBuffer {
+public:
+  INLINE DXSavedFrameBuffer(const RenderBuffer &buffer,
+                CPT(DisplayRegion) dr);
+  INLINE ~DXSavedFrameBuffer();
+
+  PT(Texture) _back_rgba;
+  PT(PixelBuffer) _depth;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    SavedFrameBuffer::init_type();
+    register_type(_type_handle, "DXSavedFrameBuffer",
+          SavedFrameBuffer::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 "dxSavedFrameBuffer8.I"
+
+#endif
+

+ 1915 - 0
panda/src/dxgsg8/dxTextureContext8.cxx

@@ -0,0 +1,1915 @@
+// Filename: dxTextureContext.cxx
+// Created by:  drose (07Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include <assert.h>
+#include <time.h>
+#include "dxTextureContext8.h"
+#include "config_dxgsg8.h"
+#include "dxGraphicsStateGuardian8.h"
+#include "pnmImage.h"
+
+//#define FORCE_16bpp_1555
+
+typedef enum {
+    None,Conv32to32,Conv32to32_NoAlpha,Conv32to24,Conv32to16_0555,
+    Conv32to16_1555,Conv32to16_0565,Conv32to16_4444,Conv24to32,Conv24to24,
+    Conv24to16_0555,Conv24to16_0565,ConvLum16to16_1555,ConvLum16to16_4444,
+    ConvLum16to32,ConvLum16to16,ConvLum8to8,ConvLum8to24,ConvLum8to32,ConvLum8to16_0555,ConvLum8to16_0565,
+    ConvAlpha8to16_4444,ConvAlpha8to32,ConvAlpha8to8
+} ConversionType;
+
+#ifndef NDEBUG
+char *ConvNameStrs[] = {"None","Conv32to32","Conv32to32_NoAlpha","Conv32to24","Conv32to16_0555",
+    "Conv32to16_1555","Conv32to16_0565","Conv32to16_4444","Conv24to32","Conv24to24",
+    "Conv24to16_0555","Conv24to16_0565","ConvLum16to16_1555","ConvLum16to16_4444",
+    "ConvLum16to32","ConvLum16to16","ConvLum8to8","ConvLum8to24","ConvLum8to32",
+    "ConvLum8to16_0555","ConvLum8to16_0565","ConvAlpha8to16_4444","ConvAlpha8to32","ConvAlpha8to8"
+};
+#endif
+
+char *PandaFilterNameStrs[] = {"FT_nearest","FT_linear","FT_nearest_mipmap_nearest","FT_linear_mipmap_nearest",
+    "FT_nearest_mipmap_linear", "FT_linear_mipmap_linear"
+};
+
+
+TypeHandle DXTextureContext::_type_handle;
+
+#define SWAPDWORDS(X,Y)  { DWORD temp=X;  X=Y; Y=temp; }
+
+#ifdef _DEBUG
+static void DebugPrintPixFmt(DDPIXELFORMAT* pddpf) {
+    static int iddpfnum=0;
+    ostream *dbgout = &dxgsg_cat.debug();
+
+    *dbgout << "DDPF[" << iddpfnum << "]: RGBBitCount:" << pddpf->dwRGBBitCount
+    << " Flags:"  << (void *)pddpf->dwFlags ;
+
+    if(pddpf->dwFlags & DDPF_RGB) {
+        *dbgout << " RGBmask:" << (void *) (pddpf->dwRBitMask | pddpf->dwGBitMask | pddpf->dwBBitMask);
+        *dbgout << " Rmask:" << (void *) (pddpf->dwRBitMask);
+    }
+
+    if(pddpf->dwFlags & DDPF_ALPHAPIXELS) {
+        *dbgout << " Amask:" << (void *) pddpf->dwRGBAlphaBitMask;
+    }
+
+    if(pddpf->dwFlags & DDPF_LUMINANCE) {
+        *dbgout << " Lummask:" << (void *) pddpf->dwLuminanceBitMask;
+    }
+
+    *dbgout << endl;
+
+    iddpfnum++;
+}
+
+void PrintLastError(char *msgbuf) {
+    DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+
+    if(msgbuf==NULL) {
+        LPVOID lpMsgBuf;
+        dwFlags|=FORMAT_MESSAGE_ALLOCATE_BUFFER;
+        FormatMessage( dwFlags,
+                       NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                       (LPTSTR) &lpMsgBuf,0,NULL );
+        MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
+        LocalFree(lpMsgBuf);
+    } else {
+        FormatMessage( dwFlags,
+                       NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                       (LPTSTR) msgbuf,500,NULL );
+    }
+}
+
+#endif
+
+
+/* for reference
+enum Format {
+  F_color_index,
+  F_stencil_index,
+  F_depth_component,
+  F_red,
+  F_green,
+  F_blue,
+  F_alpha,
+  F_rgb,     // any suitable RGB mode, whatever the hardware prefers
+  F_rgb5,    // specifically, 5 bits per R,G,B channel
+  F_rgb8,    // 8 bits per R,G,B channel
+  F_rgb12,   // 12 bits per R,G,B channel
+  F_rgb332,  // 3 bits per R & G, 2 bits for B
+  F_rgba,    // any suitable RGBA mode, whatever the hardware prefers
+  F_rgbm,    // as above, but only requires 1 bit for alpha (i.e. mask)
+  F_rgba4,   // 4 bits per R,G,B,A channel
+  F_rgba5,   // 5 bits per R,G,B channel, 1 bit alpha
+  F_rgba8,   // 8 bits per R,G,B,A channel
+  F_rgba12,  // 12 bits per R,G,B,A channel
+  F_luminance,
+  F_luminance_alpha
+};
+
+  enum Type {
+    T_unsigned_byte,   // 1 byte per channel
+    T_unsigned_short,  // 2 byte per channel
+    T_unsigned_byte_332,  // RGB in 1 byte
+    T_float,             // 1 channel stored as float
+  };
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXTextureContext::get_bits_per_pixel
+//       Access: Protected
+//  Description: Maps from the PixelBuffer's Format symbols
+//               to bpp.  returns # of alpha bits
+//               Note: PixelBuffer's format indicates REQUESTED final format,
+//                     not the stored format, which is indicated by pixelbuffer type
+////////////////////////////////////////////////////////////////////
+
+unsigned int DXTextureContext::
+get_bits_per_pixel(PixelBuffer::Format format, int *alphbits) {
+    *alphbits = 0;      // assume no alpha bits
+    switch(format) {
+        case PixelBuffer::F_alpha:
+            *alphbits = 8;
+            return 8;
+        case PixelBuffer::F_color_index:
+        case PixelBuffer::F_red:
+        case PixelBuffer::F_green:
+        case PixelBuffer::F_blue:
+        case PixelBuffer::F_rgb332:
+        case PixelBuffer::F_luminance_alphamask:
+            *alphbits = 1;
+            return 16;
+        case PixelBuffer::F_luminance_alpha:
+            *alphbits = 8;
+            return 16;
+        case PixelBuffer::F_luminance:
+            return 8;
+        case PixelBuffer::F_rgba4:
+            *alphbits = 4;
+            return 16;
+        case PixelBuffer::F_rgba5:
+            *alphbits = 1;
+            return 16;
+        case PixelBuffer::F_depth_component:
+        case PixelBuffer::F_rgb5:
+            return 16;
+        case PixelBuffer::F_rgb8:
+        case PixelBuffer::F_rgb:
+            return 24;
+        case PixelBuffer::F_rgba8:
+        case PixelBuffer::F_rgba:
+            *alphbits = 8;
+            return 32;
+        case PixelBuffer::F_rgbm:
+            *alphbits = 1;
+            return 32;
+        case PixelBuffer::F_rgb12:
+            return 36;
+        case PixelBuffer::F_rgba12:
+            *alphbits = 12;
+            return 48;
+    }
+    return 8;
+}
+
+HRESULT ConvertPixBuftoDDSurf(ConversionType ConvNeeded,BYTE *pbuf,LPDIRECTDRAWSURFACE7 pDDSurf) {
+    HRESULT hr;
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    if(IsBadWritePtr(pDDSurf,sizeof(DWORD))) {
+        dxgsg_cat.error() << "ConvertPixBuftoDDSurf failed: bad pDDSurf ptr value (" << ((void*)pDDSurf) << ")\n";
+        exit(1);
+    }
+
+    if(FAILED( hr = pDDSurf->Lock( NULL, &ddsd,  DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) {
+        dxgsg_cat.error() << "CreateTexture failed: _surface->Lock() failed on texture! hr = " << ConvD3DErrorToString(hr) << "\n";
+        return hr;
+    }
+    //pbuf contains raw ARGB in PixelBuffer byteorder
+
+    DWORD lPitch = ddsd.lPitch;
+    BYTE* pDDSurfBytes = (BYTE*)ddsd.lpSurface;
+    DWORD dwOrigWidth=ddsd.dwWidth,dwOrigHeight=ddsd.dwHeight;
+
+    switch(ConvNeeded) {
+        case Conv32to32:
+        case Conv32to32_NoAlpha: {
+
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                DWORD *pDstWord;
+                DWORD dwAlphaMaskOff = (ConvNeeded==Conv32to32_NoAlpha) ? 0x00FFFFFF : 0xFFFFFFFF;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (DWORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel = *pSrcWord;
+
+                  // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                  // need to change byte order to ARGB
+#if 0
+                        BYTE r,g,b,a;
+                        a = (BYTE)((dwPixel>>24));   // unsigned >>, no need to &
+                        b = (BYTE)((dwPixel>>16)&0x000000ff);
+                        g = (BYTE)((dwPixel>> 8)&0x000000ff);
+                        r = (BYTE)((dwPixel    )&0x000000ff);
+
+                        *pDstWord = (a << 24) | (r << 16)| (g << 8) | b;
+#else
+                        BYTE r,b;
+                  // simpler: just swap r & b
+                        b = (BYTE)((dwPixel>>16)&0x000000ff);
+                        r = (BYTE)((dwPixel    )&0x000000ff);
+                        *pDstWord = ((dwPixel & 0xff00ff00) | (r<<16) | b) & dwAlphaMaskOff;
+#endif
+                    }
+                }
+                break;
+            }
+
+        case Conv32to16_1555:
+        case Conv32to16_0555: {
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+                DWORD dwAlphaMaskOff = (ConvNeeded==Conv32to16_0555) ? 0x7FFF : 0xFFFF;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+          // for some reason, bits are 'in-order' when converting to 16bit
+
+          // just handle 1/15 alpha/rgb
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b;
+                        DWORD abit,dwPixel = *pSrcWord;
+
+                  // pixel buffer is in ABGR format (orig fmt designed for big-endian RGBA)
+                  // need to change byte order to ARGB
+
+                        abit = ((dwPixel>>16) & 0x00008000);  // just copy high bit
+
+                  // just look at most-signf-bit for alpha.  (alternately, could
+                  // convert any non-zero alpha to full transparent)
+
+                        b = (BYTE)((dwPixel>>16) & 0x000000ff)    >> 3;
+                        g = (BYTE)((dwPixel>> 8) & 0x000000ff)    >> 3;
+                        r = (BYTE)((dwPixel    ) & 0x000000ff)    >> 3;
+
+                  // code truncates 8 bit values to 5 bit (or 1 for alpha)
+
+                        *pDstWord = (abit | (r << 10)| (g << 5) | b) & dwAlphaMaskOff;
+                    }
+                }
+                break;
+            }
+
+        case Conv32to16_0565: {   // could merge this w/above case, but whatever
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+          // for some reason, bits are 'in-order' when converting to 16bit
+
+          // just handle 1/15 alpha/rgb
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+
+                  // pixel buffer is in ABGR format (orig fmt designed for big-endian RGBA)
+                  // need to change byte order to ARGB
+
+                  // just look at most-signf-bit for alpha.  (alternately, could
+                  // convert any non-zero alpha to full transparent)
+
+                        b = (BYTE)((dwPixel>>16) & 0x000000ff)    >> 3;
+                        g = (BYTE)((dwPixel>> 8) & 0x000000ff)    >> 2;
+                        r = (BYTE)((dwPixel    ) & 0x000000ff)    >> 3;
+
+                  // code truncates 8 bit values to 5 bit (or 1 for alpha)
+
+                        *pDstWord = ((r << 11)| (g << 5) | b);
+                    }
+                }
+                break;
+            }
+
+
+        case Conv32to24: {
+
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (BYTE*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord+=3) {
+                  // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                  // need to change byte order to ARGB
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+
+                        b = (BYTE)((dwPixel>>16) & 0x000000ff);
+                        g = (BYTE)((dwPixel>> 8) & 0x000000ff);
+                        r = (BYTE)((dwPixel    ) & 0x000000ff);
+
+                        *pDstWord     = r;
+                        *(pDstWord+1) = g;
+                        *(pDstWord+2) = b;
+                    }
+                }
+                break;
+            }
+
+
+        case Conv32to16_4444: {
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x0f00);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b,a;
+                        DWORD dwPixel = *pSrcWord;
+
+                  // pixel buffer is in ABGR format (orig fmt designed for big-endian RGBA)
+                  // need to change byte order to ARGB
+
+                        a = (BYTE)((dwPixel>>24))                   >> 4;
+                        b = (BYTE)((dwPixel>>16) & 0x000000ff)      >> 4;
+                        g = (BYTE)((dwPixel>> 8) & 0x000000ff)      >> 4;
+                        r = (BYTE)((dwPixel    ) & 0x000000ff)      >> 4;
+
+                        *pDstWord = (a << 12) | (r << 8)| (g << 4) | b;
+                    }
+                }
+                break;
+            }
+
+
+        case Conv24to24: {
+
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (BYTE*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord+=3) {
+                        BYTE r,g,b;
+                        // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                        // need to change byte order to ARGB
+
+                        b = *pSrcWord;
+                        g = *(pSrcWord+1);
+                        r = *(pSrcWord+2);
+
+                        *pDstWord     = r;
+                        *(pDstWord+1) = g;
+                        *(pDstWord+2) = b;
+                    }
+                }
+                break;
+            }
+
+        case Conv24to16_0555: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+             // for some reason, bits are 'in-order' when converting to 16bit
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+
+                     // pixel buffer is in ABGR format (orig fmt designed for big-endian RGBA)
+                     // need to change byte order to ARGB
+                        r = *pSrcWord       >> 3;
+                        g = *(pSrcWord+1)   >> 3;
+                        b = *(pSrcWord+2)   >> 3;
+
+                     // code truncates 8 bit values to 5 bit, leaves high bit as 0
+
+                        *pDstWord = (r << 10)| (g << 5) | b;
+                    }
+                }
+                break;
+            }
+
+        case Conv24to16_0565: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+             // for some reason, bits are 'in-order' when converting to 16bit
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+
+                     // pixel buffer is in ABGR format (orig fmt designed for big-endian RGBA)
+                     // need to change byte order to ARGB
+                        r = *pSrcWord       >> 3;
+                        g = *(pSrcWord+1)   >> 2;
+                        b = *(pSrcWord+2)   >> 3;
+
+                     // code truncates 8 bit values to 5 bit, leaves high bit as 0
+
+                        *pDstWord = (r << 11)| (g << 5) | b;
+                    }
+                }
+                break;
+            }
+
+        case Conv24to32: {
+
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+                  // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                  // need to change byte order to ARGB
+
+                  // bugbug?  swapping doesnt work  I dont understand why must
+                  // swap bytes for 32-bit RGBA (herc) but not here in 24bit,
+                  // where they seem to be in correct order in pixelbuffer
+
+                        r = *pSrcWord;
+                        g = *(pSrcWord+1);
+                        b = *(pSrcWord+2);
+
+                        *pDstWord = 0xFF000000 | (r << 16)| (g << 8) | b;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to32: {
+
+                WORD *pSrcWord = (WORD *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                  // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                  // need to change byte order to ARGB
+                        DWORD dwPixel=*pSrcWord;
+                        BYTE r,a;
+
+                        a = dwPixel >> 8;
+                        r = dwPixel & 0xFF;
+
+                        *pDstWord = (a<<24)| (r << 16)| (r << 8) | r;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16_4444: {
+                WORD *pSrcWord = (WORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+                        BYTE r,a;
+                        dwPixel = *pSrcWord;
+
+                        a = (BYTE)(dwPixel>>8)           >> 4;
+                        r = (BYTE)(dwPixel & 0x000000ff) >> 4;
+
+                        *pDstWord = (a << 12) | (r << 8)| (r << 4) | r;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16_1555: {
+                WORD *pSrcWord = (WORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0x8000);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+                        BYTE r;
+
+                        r = (BYTE)(dwPixel & 0x000000ff) >> 3;
+
+                        *pDstWord = (dwPixel & 0x8000) | (r << 10)| (r << 5) | r;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16: {
+            // AL bytes are in same order
+                CopyMemory(pDDSurfBytes,pbuf,dwOrigWidth*dwOrigHeight*2);
+                break;
+            }
+
+        case ConvLum8to16_0565:
+        case ConvLum8to16_0555: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+                DWORD FarShift,OrVal,MiddleRoundShift;
+
+                if(ConvNeeded==ConvLum8to16_0555) {
+                    FarShift=10;  OrVal=0x8000;  // turn on alpha bit, just in case
+                    MiddleRoundShift = 3;
+                    assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+                } else {
+                    FarShift=11;  OrVal=0x0;
+                    MiddleRoundShift = 2;
+                    assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+                }
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord,GrnVal;
+                        BYTE r;
+
+                        r = (BYTE) dwPixel >> 3;
+                        GrnVal = (BYTE) dwPixel >> MiddleRoundShift;
+
+                  // code truncates 8 bit values to 5 bit  (set alpha=1 for opaque)
+
+                        *pDstWord = ((r << FarShift)| (GrnVal << 5) | r) | OrVal;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum8to8: {
+                CopyMemory(pDDSurfBytes,pbuf,dwOrigWidth*dwOrigHeight);
+                break;
+            }
+
+        case ConvLum8to32: {
+
+          // this is kind of a waste of space, but we trade it for lum resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+
+                        *pDstWord = 0xFF000000 | (dwPixel<<16) | (dwPixel<<8) | dwPixel;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum8to24: {
+
+          // this is kind of a waste of space, but we trade it for lum resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (BYTE *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+
+                        *pDstWord++ = dwPixel;
+                        *pDstWord++ = dwPixel;
+                        *pDstWord   = dwPixel;
+                    }
+                }
+                break;
+            }
+
+        case ConvAlpha8to32: {
+
+              //  waste of space, but this is only place we can get 8bits alpha resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        // OR alpha with full white
+                        *pDstWord = (*pSrcWord << 24) | 0xFFFFFF;
+                    }
+                }
+                break;
+            }
+
+        case ConvAlpha8to16_4444: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB order
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        WORD a = (BYTE)(*pSrcWord>>4);
+                        *pDstWord = (a << 12) | 0x0FFF; // OR alpha with full white
+                    }
+                }
+                break;
+            }
+
+        default:
+            dxgsg_cat.error() << "CreateTexture failed! unhandled texture conversion type: "<< ConvNeeded <<" \n";
+            pDDSurf->Unlock(NULL);
+            return E_INVALIDARG;
+    }
+
+    pDDSurf->Unlock(NULL);
+
+    return S_OK;
+}
+
+HRESULT ConvertDDSurftoPixBuf(PixelBuffer *pixbuf,LPDIRECTDRAWSURFACE7 pDDSurf) {
+
+    HRESULT hr;
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    DWORD dwNumComponents=pixbuf->get_num_components();
+
+    assert(pixbuf->get_component_width()==sizeof(BYTE));   // cant handle anything else now
+    assert(pixbuf->get_image_type()==PixelBuffer::T_unsigned_byte);   // cant handle anything else now
+    assert((dwNumComponents==3) || (dwNumComponents==4));  // cant handle anything else now
+
+    BYTE *pbuf=pixbuf->_image.p();
+
+    if(IsBadWritePtr(pDDSurf,sizeof(DWORD))) {
+        dxgsg_cat.error() << "ConvertDDSurftoPixBuf failed: bad pDDSurf ptr value (" << ((void*)pDDSurf) << ")\n";
+        exit(1);
+    }
+
+    if(FAILED( hr = pDDSurf->Lock( NULL, &ddsd,  DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) {
+        dxgsg_cat.error() << "ConvertDDSurftoPixBuf Lock() failed! hr = " << ConvD3DErrorToString(hr) << "\n";
+        return hr;
+    }
+
+    DWORD dwXWindowOffset=0,dwYWindowOffset=0;
+    DWORD dwCopyWidth=ddsd.dwWidth,dwCopyHeight=ddsd.dwHeight;
+
+   // get window offset so we know where to start grabbing pixels
+    LPDIRECTDRAWCLIPPER pDDClipper;
+    hr = pDDSurf->GetClipper(&pDDClipper);
+
+#ifdef _DEBUG
+    if(FAILED(hr) && !((hr == DDERR_NOCLIPPERATTACHED) && dx_full_screen)) {
+        dxgsg_cat.error() << "ConvertDDSurftoPixBuf GetClipper failed! hr = " << ConvD3DErrorToString(hr) << "\n";
+        return hr;
+    }
+#endif
+
+    if(hr==S_OK) {
+        HWND hWin;
+
+        if(FAILED(hr = pDDClipper->GetHWnd(&hWin))) {
+            dxgsg_cat.error() << "ConvertDDSurftoPixBuf GetHwnd failed! hr = " << ConvD3DErrorToString(hr) << "\n";
+            return hr;
+        }
+
+        RECT view_rect;
+        GetClientRect( hWin, &view_rect );
+        ClientToScreen( hWin, (POINT*)&view_rect.left );
+        ClientToScreen( hWin, (POINT*)&view_rect.right );
+
+        dwXWindowOffset=view_rect.left;
+        dwYWindowOffset=view_rect.top;
+        dwCopyWidth=view_rect.right-view_rect.left;
+        dwCopyHeight=view_rect.bottom-view_rect.top;
+
+        pDDClipper->Release();  // dec ref cnt
+    }
+
+    //make sure there's enough space in the pixbuf, its size must match (especially xsize)
+   // or scanlines will be too long
+
+    if(!((dwCopyWidth==pixbuf->get_xsize()) && (dwCopyHeight<=pixbuf->get_ysize()))) {
+        pDDSurf->Unlock(NULL);
+        assert(0);
+        dxgsg_cat.error() << "ConvertDDSurftoPixBuf, PixBuf incorrect size to hold display surface!\n";
+        return E_FAIL;
+    }
+
+    // others not handled yet
+    assert((ddsd.ddpfPixelFormat.dwRGBBitCount==32)||(ddsd.ddpfPixelFormat.dwRGBBitCount==16)||(ddsd.ddpfPixelFormat.dwRGBBitCount==24));
+
+    //pbuf contains raw ARGB in PixelBuffer byteorder
+
+    DWORD lPitch = ddsd.lPitch;
+    BYTE* pDDSurfBytes = (BYTE*)ddsd.lpSurface;
+
+    if(dwNumComponents==4) {
+        DWORD *pDstWord = (DWORD *) pbuf;
+        switch(ddsd.ddpfPixelFormat.dwRGBBitCount) {
+
+            case 32: {
+                    DWORD *pSrcWord;
+
+                    pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                    for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                        pSrcWord = ((DWORD*)pDDSurfBytes)+dwXWindowOffset;
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++,pDstWord++) {
+                            BYTE r,g,b;
+                            DWORD dwPixel = *pSrcWord;
+
+                            // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                            // need to change byte order from ARGB
+
+                            // simpler: just swap r & b
+                            r = (BYTE)((dwPixel>>16)&0x000000ff);
+                            g = (BYTE)((dwPixel>> 8)&0x000000ff);
+                            b = (BYTE)((dwPixel    )&0x000000ff);
+
+                            *pDstWord = (dwPixel & 0xff00ff00) | (r<<16) | b;
+                        }
+                    }
+                    break;
+                }
+
+            case 24: {
+                    BYTE *pSrcByte;
+
+                    pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                    for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                        pSrcByte = pDDSurfBytes+dwXWindowOffset*3*sizeof(BYTE);
+                        for(DWORD x=0; x<dwCopyWidth; x++,pDstWord++) {
+                            DWORD r,g,b;
+
+                            // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                            // need to change byte order from ARGB
+
+                            b = *pSrcByte++;
+                            g = *pSrcByte++;
+                            r = *pSrcByte++;
+
+                            *pDstWord = 0xFF000000 | (r << 16) | (g << 8) | b;
+                        }
+                    }
+                    break;
+                }
+
+            case 16: {
+
+                    assert((ddsd.ddpfPixelFormat.dwRBitMask==0xF800)  ||  // 565
+                           (ddsd.ddpfPixelFormat.dwRBitMask==0x0F00)  ||  // 4444
+                           (ddsd.ddpfPixelFormat.dwRBitMask==0x7C00));    // 555 or 1555
+
+                    WORD  *pSrcWord;
+                    // handle 0555,1555,0565 in same loop
+                    BYTE redshift,greenshift,blueshift;
+                    DWORD redmask,greenmask,bluemask;
+
+                    if(ddsd.ddpfPixelFormat.dwRBitMask==0xF800) {
+                        redshift=(11-3);
+                        redmask=0xF800;
+                        greenmask=0x07E0;
+                        greenshift=(5-2);
+                        bluemask=0x001F;
+                        blueshift=3;
+                    } else if(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00) {
+                        redmask=0x7C00;
+                        redshift=(10-3);
+                        greenmask=0x03E0;
+                        greenshift=(5-3);
+                        bluemask=0x001F;
+                        blueshift=3;
+                    } else {
+                        assert(ddsd.ddpfPixelFormat.dwRBitMask==0x0F00);
+                        redmask=0x0F00;
+                        redshift=4;
+                        greenmask=0x00F0;
+                        greenshift=0;
+                        bluemask=0x000F;
+                        blueshift=4;
+                    }
+
+                    pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                    for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                        pSrcWord = ((WORD*)pDDSurfBytes)+dwXWindowOffset;
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++,pDstWord++) {
+                            WORD dwPixel = *pSrcWord;
+                            BYTE r,g,b;
+
+                            b = (dwPixel & bluemask) << blueshift;
+                            g = (dwPixel & greenmask) >> greenshift;
+                            r = (dwPixel & redmask) >> redshift;
+
+                            *pDstWord = 0xFF000000 | (b << 16) | (g << 8) | r;
+                        }
+                    }
+                }
+                break;
+        }
+    } else {  // convert to 24bpp pixbuf
+        BYTE *pDstByte = (BYTE *) pbuf;
+        switch(ddsd.ddpfPixelFormat.dwRGBBitCount) {
+
+            case 32: {
+                    DWORD *pSrcWord;
+
+                    pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                    for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                        pSrcWord = ((DWORD*)pDDSurfBytes)+dwXWindowOffset;
+
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++) {
+                            BYTE r,g,b;
+                            DWORD dwPixel = *pSrcWord;
+
+                            // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                            // need to change byte order to ARGB
+
+                            // simpler: just swap r & b
+                            r = (BYTE)((dwPixel>>16)&0x000000ff);
+                            g = (BYTE)((dwPixel>> 8)&0x000000ff);
+                            b = (BYTE)((dwPixel    )&0x000000ff);
+
+                            *pDstByte++ = r;
+                            *pDstByte++ = g;
+                            *pDstByte++ = b;
+                        }
+                    }
+                    break;
+                }
+
+            case 24: {
+                        BYTE *pSrcByte;
+
+                        pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                        for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                            pSrcByte = pDDSurfBytes+dwXWindowOffset*3*sizeof(BYTE);
+                            for(DWORD x=0; x<dwCopyWidth; x++) {
+                                BYTE r,g,b;
+
+                                // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                                // need to change byte order from ARGB
+
+                                b = *pSrcByte++;
+                                g = *pSrcByte++;
+                                r = *pSrcByte++;
+
+                                *pDstByte++ = r;
+                                *pDstByte++ = g;
+                                *pDstByte++ = b;
+                            }
+                        }
+                        break;
+                    }
+
+            case 16: {  // handle 555,1555,565,4444 cases  (not 8-8 yet)
+
+                    assert((ddsd.ddpfPixelFormat.dwRBitMask==0xF800)  ||  // 565
+                           (ddsd.ddpfPixelFormat.dwRBitMask==0x0F00)  ||  // 4444
+                           (ddsd.ddpfPixelFormat.dwRBitMask==0x7C00));    // 555 or 1555
+
+                    WORD  *pSrcWord;
+                        // handle 0555,1555,0565 in same loop
+                    BYTE redshift,greenshift,blueshift;
+                    DWORD redmask,greenmask,bluemask;
+
+                    if(ddsd.ddpfPixelFormat.dwRBitMask==0xF800) {
+                        redshift=(11-3);
+                        redmask=0xF800;
+                        greenmask=0x07E0;
+                        greenshift=(5-2);
+                        bluemask=0x001F;
+                        blueshift=3;
+                    } else if(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00) {
+                        redmask=0x7C00;
+                        redshift=(10-3);
+                        greenmask=0x03E0;
+                        greenshift=(5-3);
+                        bluemask=0x001F;
+                        blueshift=3;
+                    } else {
+                        assert(ddsd.ddpfPixelFormat.dwRBitMask==0x0F00);
+                        redmask=0x0F00;
+                        redshift=4;
+                        greenmask=0x00F0;
+                        greenshift=0;
+                        bluemask=0x000F;
+                        blueshift=4;
+                    }
+
+                    pDDSurfBytes+=ddsd.lPitch*(dwYWindowOffset+dwCopyHeight-1);
+                    for(DWORD y=0; y<dwCopyHeight; y++,pDDSurfBytes-=ddsd.lPitch) {
+                        pSrcWord = ((WORD*)pDDSurfBytes)+dwXWindowOffset;
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++) {
+                            WORD dwPixel = *pSrcWord;
+                            BYTE r,g,b;
+
+                            b = (dwPixel & bluemask) << blueshift;
+                            g = (dwPixel & greenmask) >> greenshift;
+                            r = (dwPixel & redmask) >> redshift;
+
+                            *pDstByte++ = r;
+                            *pDstByte++ = g;
+                            *pDstByte++ = b;
+                        }
+                    }
+                }
+                break;
+        }
+    }
+
+
+    pDDSurf->Unlock(NULL);
+
+    return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Name: CreateTextureFromBitmap()
+// Desc: Use a bitmap to create a texture for the specified device. This code
+//       gets the attributes of the texture from the bitmap, creates the
+//       texture, and then copies the bitmap into the texture.
+//-----------------------------------------------------------------------------
+LPDIRECTDRAWSURFACE7 DXTextureContext::
+CreateTexture(LPDIRECT3DDEVICE7 pd3dDevice, int cNumTexPixFmts, LPDDPIXELFORMAT pTexPixFmts) {
+    HRESULT hr;
+    int i;
+    PixelBuffer *pbuf = _texture->_pbuffer;
+    int cNumAlphaBits;     //  number of alpha bits in texture pixfmt
+
+    DDPIXELFORMAT *pDesiredPixFmt;
+    LPDIRECTDRAWSURFACE7 pddsRender;
+    LPDIRECTDRAW7        pDD = NULL;
+
+    DDPIXELFORMAT TexFmtsArr[MAX_DX_TEXPIXFMTS];
+
+    ConversionType ConvNeeded;
+
+    // make local copy of array so I can muck with it during searches for this texture fmt
+    memcpy(TexFmtsArr,pTexPixFmts,cNumTexPixFmts*sizeof(DDPIXELFORMAT));
+    pTexPixFmts=TexFmtsArr;
+
+    // bpp indicates requested fmt, not pixbuf fmt
+    DWORD bpp = get_bits_per_pixel(pbuf->get_format(), &cNumAlphaBits);
+    PixelBuffer::Type pixbuf_type = pbuf->get_image_type();
+    DWORD cNumColorChannels = pbuf->get_num_components();
+
+    assert(pbuf->get_component_width()==sizeof(BYTE));   // cant handle anything else now
+    assert(pixbuf_type==PixelBuffer::T_unsigned_byte);   // cant handle anything else now
+
+    if((pixbuf_type != PixelBuffer::T_unsigned_byte) || (pbuf->get_component_width()!=1)) {
+        dxgsg_cat.error() << "CreateTexture failed, havent handled non 8-bit channel pixelbuffer types yet! \n";
+        return NULL;
+    }
+
+    DWORD dwOrigWidth  = (DWORD)pbuf->get_xsize();
+    DWORD dwOrigHeight = (DWORD)pbuf->get_ysize();
+
+    // Get the device caps so we can check if the device has any constraints
+    // when using textures
+    D3DDEVICEDESC7 devDesc;
+    if(FAILED( pd3dDevice->GetCaps( &devDesc ) )) {
+        goto error_exit;
+    }
+
+    // Setup the new surface desc for the texture. Note how we are using the
+    // texture manage attribute, so Direct3D does alot of dirty work for us
+    DDSURFACEDESC2 ddsd;
+    ZeroMemory( &ddsd, sizeof(DDSURFACEDESC2) );
+    ddsd.dwSize          = sizeof(DDSURFACEDESC2);
+    ddsd.dwFlags         =  DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH
+                            | DDSD_PIXELFORMAT ;
+
+    ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
+
+    // setup ddpf to match against avail fmts
+
+    ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
+
+    if((pbuf->get_format() == PixelBuffer::F_luminance_alpha)||
+       (pbuf->get_format() == PixelBuffer::F_luminance_alphamask) ||
+       (pbuf->get_format() == PixelBuffer::F_luminance)) {
+        ddsd.ddpfPixelFormat.dwFlags = DDPF_LUMINANCE;
+    }
+
+    ddsd.ddpfPixelFormat.dwRGBBitCount = bpp;
+
+    if(cNumAlphaBits) {
+        if(bpp == 8 && cNumAlphaBits == 8) { // handle special case:  Alpha only buffer
+            ddsd.dwFlags |= DDPF_ALPHA;
+            ddsd.dwAlphaBitDepth = 8;
+            ddsd.ddpfPixelFormat.dwAlphaBitDepth = 8;
+            ddsd.ddpfPixelFormat.dwFlags = DDPF_ALPHA;
+            ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff;
+        } else {
+            ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS;
+            if(cNumAlphaBits == 8)
+                ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
+            else if(cNumAlphaBits == 4)
+                ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xf000;
+        }
+    }
+
+    ddsd.dwWidth         = dwOrigWidth;
+    ddsd.dwHeight        = dwOrigHeight;
+
+    if(!ISPOW2(ddsd.dwWidth) || !ISPOW2(ddsd.dwHeight)) {
+        dxgsg_cat.error() << "ERROR: texture dimensions are not a power of 2 for " << _tex->get_name() << "!!!!! \n";
+#ifdef _DEBUG
+        exit(1);  // want to catch badtexsize errors
+#else
+        goto error_exit;
+#endif
+    }
+
+    bool bShrinkOriginal;
+
+    bShrinkOriginal=false;
+    if((dwOrigWidth>devDesc.dwMaxTextureWidth)||(dwOrigHeight>devDesc.dwMaxTextureHeight)) {
+#ifdef _DEBUG
+        dxgsg_cat.error() << "WARNING: " <<_tex->get_name() << ": Image size exceeds max texture dimensions of (" << devDesc.dwMaxTextureWidth << "," << devDesc.dwMaxTextureHeight << ") !!\n"
+        << "Scaling "<< _tex->get_name() << " ("<< dwOrigWidth<<"," <<dwOrigHeight << ") => ("<<  devDesc.dwMaxTextureWidth << "," << devDesc.dwMaxTextureHeight << ") !\n";
+#endif
+
+        if(dwOrigWidth>devDesc.dwMaxTextureWidth)
+            ddsd.dwWidth=devDesc.dwMaxTextureWidth;
+        if(dwOrigHeight>devDesc.dwMaxTextureHeight)
+            ddsd.dwHeight=devDesc.dwMaxTextureHeight;
+        bShrinkOriginal=true;
+    }
+
+    // checks for SQUARE reqmt (nvidia riva128 needs this)
+    if((ddsd.dwWidth != ddsd.dwHeight) && (devDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY )) {
+
+        // assume pow2 textures.   sum exponents, divide by 2 rounding down to get sq size
+        int i,width_exp,height_exp;
+        for(i=ddsd.dwWidth,width_exp=0;i>1;width_exp++,i>>=1);
+        for(i=ddsd.dwHeight,height_exp=0;i>1;height_exp++,i>>=1);
+        ddsd.dwHeight = ddsd.dwWidth = 1<<((width_exp+height_exp)>>1);
+        bShrinkOriginal=true;
+
+#ifdef _DEBUG
+        dxgsg_cat.debug() << "Scaling "<< _tex->get_name() << " ("<< dwOrigWidth<<"," <<dwOrigHeight << ") => ("<< ddsd.dwWidth<<"," << ddsd.dwHeight << ") to meet HW square texture reqmt\n";
+#endif
+    }
+
+    if(bShrinkOriginal) {
+        // need 2 add checks for errors
+        PNMImage pnmi_src;
+        PNMImage *pnmi = new PNMImage(ddsd.dwWidth, ddsd.dwHeight, cNumColorChannels);
+        pbuf->store(pnmi_src);
+        pnmi->quick_filter_from(pnmi_src,0,0);
+
+        pbuf->load(*pnmi);  // violates device independence of pixbufs
+
+        dwOrigWidth  = (DWORD)pbuf->get_xsize();
+        dwOrigHeight = (DWORD)pbuf->get_ysize();
+        delete pnmi;
+    }
+
+#if 0
+//#ifdef _DEBUG
+// easier to use dxcapsviewer instead of this
+    { static BOOL bPrinted=FALSE;
+        if(!bPrinted) {
+            dxgsg_cat.debug() << "Gfx card supported TexFmts:\n";
+            for(i=0;i<cNumTexPixFmts;i++) {
+                DebugPrintPixFmt(&pTexPixFmts[i]);
+            }
+            bPrinted=TRUE;
+        }
+    }
+#endif
+
+    // first search for an exact match
+    pDesiredPixFmt = &ddsd.ddpfPixelFormat;
+
+    LPDDPIXELFORMAT pCurPixFmt;
+    char *szErrorMsg;
+
+    szErrorMsg = "CreateTexture failed: couldn't find compatible Tex DDPIXELFORMAT!\n";
+
+    dxgsg_cat.spam() << "CreateTexture handling bitdepth: " << bpp << " alphabits: " << cNumAlphaBits << "\n";
+
+    // Mark formats I dont want to deal with
+    for(i=0,pCurPixFmt=pTexPixFmts;i<cNumTexPixFmts;i++,pCurPixFmt++) {
+        if(( pCurPixFmt->dwFlags & (DDPF_BUMPLUMINANCE|DDPF_BUMPDUDV) )  ||
+           ( pCurPixFmt->dwFourCC != 0 ) ||
+           ((cNumAlphaBits==0) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS))) {
+
+            // Make sure to skip any FourCC formats, bump formats
+            // they are not handled by this code yet
+
+            // note: I'm screening out alpha if no alpha requested, so
+            // search fails if 32-rgba avail, but not 32-bit rgb
+            // I could recode for that case too, hopefully this case will not pop up
+
+            pCurPixFmt->dwRGBBitCount+=1;  // incr so it wont be an exact match anymore
+        }
+    }
+
+    // handle each bitdepth separately
+
+    switch(bpp) {  // bpp is REQUESTED bpp, not what exists in the pixbuf array
+
+        case 32:
+
+#ifdef _DEBUG
+            if(!dx_force_16bpptextures)
+#endif
+                for(i=0,pCurPixFmt=pTexPixFmts;i<cNumTexPixFmts;i++,pCurPixFmt++) {
+                    if((pCurPixFmt->dwRGBBitCount==32) &&
+                       (((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)!=0)==(cNumAlphaBits!=0))) {
+                  // we should have a match
+                        assert(pCurPixFmt->dwRGBAlphaBitMask==0xFF000000);
+                        ConvNeeded=((cNumColorChannels==3) ? Conv24to32 : Conv32to32);
+                        goto found_matching_format;
+                        break;
+                    }
+                }
+
+            if(cNumAlphaBits>0) {
+            // no 32-bit fmt, look for 16 bit w/alpha  (1-15)
+
+            // 32 bit RGBA was requested, but only 16 bit alpha fmts are avail
+            // by default, convert to 4-4-4-4 which has 4-bit alpha for blurry edges
+            // if we know tex only needs 1 bit alpha (i.e. for a mask), use 1555 instead
+
+                ConversionType ConvTo1=Conv32to16_4444,ConvTo2=Conv32to16_1555;
+                DWORD dwAlphaMask1=0xF000,dwAlphaMask2=0x8000;
+            // assume ALPHAMASK is x8000 and RGBMASK is x7fff to simplify 32->16 conversion
+            // this should be true on most cards.
+
+#ifndef FORCE_16bpp_1555
+                if(cNumAlphaBits==1)
+#endif
+                {
+                    ConvTo1=Conv32to16_1555;
+                    dwAlphaMask1=0x8000;
+                }
+
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                    if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)
+                       && (pCurPixFmt->dwRGBAlphaBitMask==dwAlphaMask1)) {
+                        ConvNeeded=ConvTo1;
+                        goto found_matching_format;
+                    }
+                }
+
+#ifdef FORCE_16bpp_1555
+                break;
+#endif
+
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                    if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)
+                       && (pCurPixFmt->dwRGBAlphaBitMask==dwAlphaMask2)) {
+                        ConvNeeded=ConvTo2;
+                        goto found_matching_format;
+                    }
+                }
+
+            // at this point, bail.  dont worry about converting to non-alpha formats yet,
+            // I think this will be a very rare case
+                szErrorMsg = "CreateTexture failed: couldn't find compatible Tex DDPIXELFORMAT! no available 16 or 32-bit alpha formats!\n";
+            }
+
+            break;
+
+        case 24:
+
+            assert(cNumAlphaBits==0);  // dont know how to handle non-zero alpha for 24bit total
+
+#ifdef _DEBUG
+            if(!dx_force_16bpptextures)
+#endif
+                for(i=0,pCurPixFmt=pTexPixFmts;i<cNumTexPixFmts;i++,pCurPixFmt++) {
+                    if((pCurPixFmt->dwFlags & DDPF_RGB)&&(pCurPixFmt->dwRGBBitCount==24)) {
+                        ConvNeeded=((cNumColorChannels==3) ? Conv24to24 : Conv32to24);
+                        goto found_matching_format;
+                    }
+                }
+
+#ifdef _DEBUG
+            if(!dx_force_16bpptextures)
+#endif
+
+          // no 24-bit fmt.  look for 32 bit fmt  (note: this is memory-hogging choice
+          // instead I could look for memory-conserving 16-bit fmt).
+          // check mask to ensure ARGB, not RGBA (which I am not handling here)
+                for(i=0,pCurPixFmt=pTexPixFmts;i<cNumTexPixFmts;i++,pCurPixFmt++) {
+                    if((pCurPixFmt->dwRGBBitCount==32) && (pCurPixFmt->dwFlags & DDPF_RGB)
+                       && ((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0xFFFFFF)
+                      ) {
+                    // I'm allowing alpha formats here.  will set alpha to opaque
+                        ConvNeeded=((cNumColorChannels==3) ? Conv24to32 : Conv32to32_NoAlpha);
+                        goto found_matching_format;
+                    }
+                }
+
+          // no 24-bit or 32 fmt.  look for 16 bit fmt
+            for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+              // assume RGBMASK is x7fff to simplify 32->16 conversion, should be true on most cards.
+                if((pCurPixFmt->dwFlags & DDPF_RGB) && (pCurPixFmt->dwRGBBitCount==16)
+                   && !(pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)) {  // no alpha fmts
+                        // if numchan==4,this means user has requested we throw away the input alpha channel
+                    if(pCurPixFmt->dwGBitMask==0x7E0) {
+                            // assumes GBitMask is the biggest one, if we have 16 bit 3-channel
+                        ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0565 : Conv32to16_0565);
+                    } else {
+                        assert((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0x7FFF);
+                        ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0555 : Conv32to16_0555);
+                    }
+                    goto found_matching_format;
+                }
+            }
+
+          // at this point, bail.
+            break;
+
+        case 16:
+
+            if(ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) {
+           // look for native lum fmt
+#ifdef _DEBUG
+                if(!dx_force_16bpptextures)
+#endif
+                {
+
+                    for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS) &&
+                           (pCurPixFmt->dwFlags & DDPF_LUMINANCE)) {
+                            ConvNeeded=ConvLum16to16;
+                            goto found_matching_format;
+                        }
+                    }
+
+           // else look for 32bpp ARGB
+                    for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==32) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS) &&
+                           (pCurPixFmt->dwFlags & DDPF_RGB)) {
+                            ConvNeeded=ConvLum16to32;
+                            goto found_matching_format;
+                        }
+                    }
+                }
+
+           // find compatible 16bpp fmt
+                ConversionType ConvTo1=ConvLum16to16_4444,ConvTo2=ConvLum16to16_1555;
+                DWORD dwAlphaMask1=0xF000,dwAlphaMask2=0x8000;
+            // assume ALPHAMASK is x8000 and RGBMASK is x7fff to simplify 32->16 conversion
+            // this should be true on most cards.
+
+#ifndef FORCE_16bpp_1555
+                if(cNumAlphaBits==1)
+#endif
+                {
+                    ConvTo1=ConvLum16to16_1555;
+                    dwAlphaMask1=0x8000;
+                }
+
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                    if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)
+                       && (pCurPixFmt->dwRGBAlphaBitMask==dwAlphaMask1)) {
+                        ConvNeeded=ConvTo1;
+                        goto found_matching_format;
+                    }
+                }
+
+#ifdef FORCE_16bpp_1555
+                break;
+#endif
+
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                    if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)
+                       && (pCurPixFmt->dwRGBAlphaBitMask==dwAlphaMask2)) {
+                        ConvNeeded=ConvTo2;
+                        goto found_matching_format;
+                    }
+                }
+
+            } else
+
+          // look for compatible 16bit fmts, if none then give up
+          // (dont worry about other bitdepths for 16 bit)
+
+                for(i=0,pCurPixFmt=pTexPixFmts;i<cNumTexPixFmts;i++,pCurPixFmt++) {
+
+                    if((pCurPixFmt->dwRGBBitCount==16)&&(pCurPixFmt->dwFlags & DDPF_RGB)) {
+                        switch(cNumAlphaBits) {
+                            case 0:
+                                if(!(pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)) {
+                            // if numchan==4,this means user has requested we throw away the input alpha channel
+                                    if(pCurPixFmt->dwGBitMask==0x7E0) {
+                                // assumes GBitMask is the biggest one, if we have 16 bit 3-channel
+                                        ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0565 : Conv32to16_0565);
+                                    } else {
+                                        assert((pCurPixFmt->dwRBitMask|pCurPixFmt->dwGBitMask|pCurPixFmt->dwBBitMask)==0x7FFF);
+                                        ConvNeeded=((cNumColorChannels==3) ? Conv24to16_0555 : Conv32to16_0555);
+                                    }
+                                    goto found_matching_format;
+                                }
+                                break;
+                            case 1:
+                                if((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)&&
+                                   (pCurPixFmt->dwRGBAlphaBitMask==0x8000)) {
+                                    ConvNeeded=Conv32to16_1555;
+                                    goto found_matching_format;
+                                }
+                                break;
+                            case 4:
+                                if((pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)&&
+                                   (pCurPixFmt->dwRGBAlphaBitMask==0xF000)) {
+                                    ConvNeeded=Conv32to16_4444;
+                                    goto found_matching_format;
+                                }
+                                break;
+                        }
+                    }
+                }
+
+            break;
+
+        case 8:
+            if(ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) {
+              // look for native lum fmt
+
+                assert(cNumAlphaBits==0);  // dont handle those other 8bit lum fmts like 4-4, since 16 8-8 is usually supported too
+#ifdef _DEBUG
+                if(!dx_force_16bpptextures)
+#endif
+                {
+                    for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==8) && (pCurPixFmt->dwFlags & DDPF_LUMINANCE) &&
+                           (pCurPixFmt->dwLuminanceBitMask==0xFF)) {
+                            ConvNeeded=ConvLum8to8;
+                            goto found_matching_format;
+                        }
+                    }
+
+              // else look for 24bpp RGB
+                    for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==24) && (pCurPixFmt->dwFlags & DDPF_RGB)) {
+                            ConvNeeded=ConvLum8to24;
+                            goto found_matching_format;
+                        }
+                    }
+
+              // else look for 32bpp RGB
+                    for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==32) && (pCurPixFmt->dwFlags & DDPF_RGB)) {
+                            ConvNeeded=ConvLum8to32;
+                            goto found_matching_format;
+                        }
+                    }
+                }
+
+             // find compatible 16bpp fmt, just look for any 565, then 0555
+                DWORD dwMasks[2] = {0xF800, 0x7C00};
+
+                for(DWORD modenum=0;modenum<2;modenum++)
+                    for(i=0,pCurPixFmt=&pTexPixFmts[0];i<cNumTexPixFmts;i++,pCurPixFmt++) {
+                        if((pCurPixFmt->dwRGBBitCount==16) && (pCurPixFmt->dwFlags & DDPF_RGB)
+                           && (!(pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS))
+                           && (pCurPixFmt->dwRBitMask==dwMasks[modenum])) {
+                            ConvNeeded=ConvLum8to16_0565;
+                            goto found_matching_format;
+                        }
+                    }
+            } else if(ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHA) {
+                // look for 32-bit ARGB, else 16-4444.
+                // skip 8bit alpha only, because I think only voodoo supports it
+                // and the voodoo support isn't the kind of blending model we need 
+                // w/color assumed to be white (but need to verify this)
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==32) && (pCurPixFmt->dwFlags & DDPF_RGB) &&
+                           (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)) {
+                            ConvNeeded=ConvAlpha8to32;
+                            goto found_matching_format;
+                        }
+                }
+
+                for(i=0,pCurPixFmt=&pTexPixFmts[cNumTexPixFmts-1];i<cNumTexPixFmts;i++,pCurPixFmt--) {
+                        if((pCurPixFmt->dwRGBBitCount==16) 
+                           && (pCurPixFmt->dwFlags & DDPF_RGB)
+                           && (pCurPixFmt->dwFlags & DDPF_ALPHAPIXELS)
+                           && (pCurPixFmt->dwRGBAlphaBitMask==0xF000)) {
+                            ConvNeeded=ConvAlpha8to16_4444;
+                            goto found_matching_format;
+                        }
+                }
+            }
+            break;
+
+        default:
+            szErrorMsg = "CreateTexture failed: unhandled pixel bitdepth in DX loader";
+    }
+
+    // if we've gotten here, haven't found a match
+
+    dxgsg_cat.error() << szErrorMsg << ";  requested tex bitdepth: " << bpp << "\n";
+    goto error_exit;
+
+    ///////////////////////////////////////////////////////////
+
+    found_matching_format:
+
+    ddsd.ddpfPixelFormat = *pCurPixFmt;
+
+    // Get the device's render target, so we can then use the render target to
+    // get a ptr to a DDraw object. We need the DirectDraw interface for
+    // creating surfaces.
+
+    pd3dDevice->GetRenderTarget( &pddsRender );
+    pddsRender->GetDDInterface( (VOID**)&pDD );
+    pddsRender->Release();
+
+    ddsd.ddsCaps.dwCaps  = DDSCAPS_TEXTURE;
+
+    ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE  // Turn on texture management
+                           | DDSCAPS2_HINTSTATIC;  // BUGBUG:  is this ok for ALL textures?
+
+    // validate magfilter setting
+    // degrade filtering if no HW support
+
+    Texture::FilterType ft;
+
+    ft =_tex->get_magfilter();
+    if((ft!=Texture::FT_linear) && ft!=Texture::FT_nearest) {
+        if(ft==Texture::FT_nearest_mipmap_nearest)
+            ft=Texture::FT_nearest;
+        else ft=Texture::FT_linear;
+    }
+
+    if((ft==Texture::FT_linear) && !(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR))
+        ft=Texture::FT_nearest;
+    _tex->set_magfilter(ft);
+
+    // figure out if we are mipmapping this texture
+    ft =_tex->get_minfilter();
+    _bHasMipMaps=FALSE;
+
+    if(!dx_ignore_mipmaps) {  // set if no HW mipmap capable
+        switch(ft) {
+            case Texture::FT_nearest_mipmap_nearest:
+            case Texture::FT_linear_mipmap_nearest:
+            case Texture::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
+            case Texture::FT_linear_mipmap_linear:
+                _bHasMipMaps=TRUE;
+        }
+
+        if(dx_mipmap_everything) {  // debug toggle, ok to leave in since its just a creation cost
+           _bHasMipMaps=TRUE;
+           if(ft != Texture::FT_linear_mipmap_linear) {
+               dxgsg_cat.spam() << "Forcing mipmap filtering on DX texture [" << _tex->get_name() << "]\n";
+           }
+           ft = Texture::FT_linear_mipmap_linear;
+           _tex->set_minfilter(ft);
+        }
+    } else if((ft==Texture::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
+              (ft==Texture::FT_nearest_mipmap_linear)) {
+        ft=Texture::FT_nearest;
+    } else if((ft==Texture::FT_linear_mipmap_nearest) ||
+              (ft==Texture::FT_linear_mipmap_linear)) {
+        ft=Texture::FT_linear;
+    }
+
+    assert((devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_NEAREST)!=0);
+
+    switch(ft) {
+        case Texture::FT_nearest_mipmap_linear:
+            if(!(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEARMIPNEAREST))
+                ft=Texture::FT_nearest_mipmap_nearest;
+            break;
+        case Texture::FT_linear_mipmap_nearest:
+            if(!(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_MIPLINEAR))
+                ft=Texture::FT_nearest_mipmap_nearest;
+            break;
+        case Texture::FT_linear_mipmap_linear:
+            if(!(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEARMIPLINEAR)) {
+                if(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_MIPLINEAR)
+                    ft=Texture::FT_linear_mipmap_nearest;
+                else ft=Texture::FT_nearest_mipmap_nearest;  // if you cant do linear in a level, you probably cant do linear b/w levels, so just do nearest-all
+            }
+            break;
+        case Texture::FT_linear:
+            if(!(devDesc.dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR))
+                ft=Texture::FT_nearest;
+            break;
+    }
+
+    _tex->set_minfilter(ft);
+
+    uint aniso_degree;
+
+    aniso_degree=1;
+    if(devDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ANISOTROPY) {
+        aniso_degree=_tex->get_anisotropic_degree();
+        if((aniso_degree>devDesc.dwMaxAnisotropy)
+#ifdef _DEBUG
+           || dx_force_anisotropic_filtering
+#endif
+          )
+            aniso_degree=devDesc.dwMaxAnisotropy;
+    }
+    _tex->set_anisotropic_degree(aniso_degree);
+#ifdef _DEBUG
+    dxgsg_cat.spam() << "CreateTexture: setting aniso degree for "<< _tex->get_name() << " to: " << aniso_degree << endl;
+#endif
+
+    if(_bHasMipMaps) {
+       // We dont specify mipmapcount, so CreateSurface will auto-create surfs
+       // for all mipmaps down to 1x1 (if driver supports deep-mipmaps, otherwise Nx1)
+        ddsd.ddsCaps.dwCaps |= (DDSCAPS_MIPMAP | DDSCAPS_COMPLEX);
+        dxgsg_cat.debug() << "CreateTexture: generating mipmaps for "<< _tex->get_name() << endl;
+    }
+
+    if(devDesc.dwDevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES) {
+        // must assign a texture to a specific stage
+        // for now I'm just going to use stage 0 for all
+        ddsd.dwTextureStage=0;
+        ddsd.dwFlags |= DDSD_TEXTURESTAGE;
+    }
+
+    PRINTVIDMEM(pDD,&ddsd.ddsCaps,"texture surf (includes AGP mem)");
+
+    // Create a new surface for the texture
+    if(FAILED( hr = pDD->CreateSurface( &ddsd, &_surface, NULL ) )) {
+        dxgsg_cat.error() << "CreateTexture failed: pDD->CreateSurface() failed!  hr = " << ConvD3DErrorToString(hr) << "\n";
+        goto error_exit;
+    }
+
+
+#ifdef _DEBUG
+    dxgsg_cat.debug() << "CreateTexture: "<< _tex->get_name() <<" converted " << ConvNameStrs[ConvNeeded] << " \n";
+#endif
+
+    _PixBufConversionType=ConvNeeded;
+
+    hr = FillDDSurfTexturePixels();
+    if(FAILED(hr)) {
+        goto error_exit;
+    }
+
+    // Done with DDraw
+    pDD->Release();
+
+    // Return the newly created texture
+    return _surface;
+
+  error_exit:
+
+    if(pDD!=NULL)
+        pDD->Release();
+    if(_surface!=NULL) {
+        _surface->Release();
+        _surface = NULL;
+    }
+
+    return NULL;
+}
+
+HRESULT DXTextureContext::
+FillDDSurfTexturePixels(void) {
+    
+    PixelBuffer *pbuf = _texture->get_ram_image();
+    if (pbuf == (PixelBuffer *)NULL) {
+      dxgsg_cat.fatal() << "CreateTexture: get_ram_image() failed\n";
+      // The texture doesn't have an image to load.
+      return E_FAIL;
+    }
+
+    HRESULT hr = ConvertPixBuftoDDSurf((ConversionType)_PixBufConversionType,pbuf->_image.p(),_surface);
+    if(FAILED(hr)) {
+        return hr;
+    }
+
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    _surface->GetSurfaceDesc(&ddsd);
+
+    if(_bHasMipMaps) {
+        DWORD i,oldcurxsize,oldcurysize,curxsize,curysize,cMipMapCount=ddsd.dwMipMapCount;
+        assert(ddsd.dwMipMapCount<20);
+
+        DWORD cNumColorChannels = pbuf->get_num_components();
+
+        curxsize=ddsd.dwWidth; curysize=ddsd.dwHeight;
+
+        assert(pbuf->get_image_type()==PixelBuffer::T_unsigned_byte);    // cant handle anything else now
+
+        // all mipmap sublevels require 1/3 of total original space. alloc 1/2 for safety
+        BYTE *pMipMapPixBufSpace = new BYTE[((curxsize*curysize*cNumColorChannels)/2)+1024];
+
+        LPDIRECTDRAWSURFACE7 pCurDDSurf=_surface;
+        pCurDDSurf->AddRef();  // so final release doesnt release the surface
+
+        BYTE *pDstWord = pMipMapPixBufSpace;
+        BYTE *pLastMipLevelStart  = (BYTE *) pbuf->_image.p();
+//    clock_t  start1,finish1;
+//    start1=clock();
+        for(i=1;i<ddsd.dwMipMapCount;i++) {
+            oldcurxsize=curxsize; oldcurysize=curysize;
+            curysize = max(curysize>>1,1);
+            curxsize = max(curxsize>>1,1);
+
+            assert(!((oldcurxsize==1)&&(oldcurysize==1)));
+
+            BYTE *pSrcWord;
+            BYTE *pSrcLineStart=pLastMipLevelStart;
+
+            // inc img start to DWORD boundary
+            while(((DWORD)pDstWord) & 0x11)
+                pDstWord++;
+
+            pLastMipLevelStart = pDstWord;
+
+            DWORD x,y,cPixelSize=cNumColorChannels;
+            DWORD src_row_bytelength=oldcurxsize*cPixelSize;
+            DWORD two_src_row_bytelength=2*src_row_bytelength;
+
+        #define GENMIPMAP_DO_INTEGER_DIV    // should be a little faster, but no rounding up
+        #ifdef GENMIPMAP_DO_INTEGER_DIV
+            DWORD DivShift=2;
+            if((oldcurxsize==1)||(oldcurysize==1))
+                DivShift = 1;
+        #else
+            float numpixels_per_filter=4.0f;
+            if((oldcurxsize==1)||(oldcurysize==1))
+                numpixels_per_filter=2.0f;                
+        #endif
+
+            DWORD x_srcptr_inc = ((oldcurxsize==1)? cPixelSize: (2*cPixelSize));
+
+            // box-filter shrink down, avg 4 pixels at a time
+            for(y=0; y<curysize; y++,pSrcLineStart+=two_src_row_bytelength) {
+                pSrcWord=pSrcLineStart;
+                for(x=0; x<curxsize; x++,pSrcWord+=x_srcptr_inc,pDstWord+=cPixelSize) {
+                  // fetches, stores byte at a time.
+                  // inefficient, but works for all channel sizes
+
+                    for(int c=0;c<cPixelSize;c++) {
+                        DWORD colr;
+                        colr =  *(pSrcWord+c);
+                        if(oldcurxsize>1)  // handle 1x[X], [X]x1 cases
+                            colr += *(pSrcWord+cPixelSize+c);
+                        if(oldcurysize>1) {
+                            colr += *(pSrcWord+src_row_bytelength+c);
+                            if(oldcurxsize>1)
+                                colr += *(pSrcWord+src_row_bytelength+cPixelSize+c);
+                        }
+                        #ifdef GENMIPMAP_DO_INTEGER_DIV
+                           colr >>= DivShift;
+                        #else
+                           colr = (DWORD) ((((float)colr)/numpixels_per_filter)+0.5f);
+                        #endif
+
+                        *(pDstWord+c)=(BYTE)colr;
+                    }
+                }
+            }
+
+            // now copy pixbuf to final DD surf
+
+            DDSCAPS2 ddsCaps;
+            LPDIRECTDRAWSURFACE7 pMipLevel_DDSurf;
+            ZeroMemory(&ddsCaps,sizeof(DDSCAPS2));
+            ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
+            ddsCaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL;
+
+            hr = pCurDDSurf->GetAttachedSurface(&ddsCaps, &pMipLevel_DDSurf);
+            if(FAILED(hr)) {
+                dxgsg_cat.error() << "CreateTexture failed creating mipmaps: GetAttachedSurf hr = " << ConvD3DErrorToString(hr) << "\n";
+                delete [] pMipMapPixBufSpace;
+                pCurDDSurf->Release();
+                return hr;
+            }
+
+            hr = ConvertPixBuftoDDSurf((ConversionType)_PixBufConversionType,pLastMipLevelStart,pMipLevel_DDSurf);
+            if(FAILED(hr)) {
+                delete [] pMipMapPixBufSpace;
+                pCurDDSurf->Release();
+                return hr;
+            }
+
+            pCurDDSurf->Release();
+            pCurDDSurf=pMipLevel_DDSurf;
+        }
+
+        //   finish1=clock();
+        //   double elapsed_time  = (double)(finish1 - start1) / CLOCKS_PER_SEC;
+        //   cerr <<  "mipmap gen takes " << elapsed_time << " secs for this texture\n";
+
+        delete [] pMipMapPixBufSpace;
+        pCurDDSurf->Release();
+
+#ifdef _DEBUG
+        if(dx_debug_view_mipmaps) {
+#if 0
+            if(!(ddcaps.dwCaps & DDCAPS_BLTSTRETCH)) {
+                dxgsg_cat.error() << "CreateTexture failed debug-viewing mipmaps, BLT stretching not supported!  ( we need to do SW stretch) \n";
+                return hr;
+            }
+#endif
+
+            // display mipmaps on primary surf
+            HDC hTexDC;
+            LPDIRECTDRAWSURFACE7 pTextureCurrent,pTexturePrev = _surface;
+            int cury,curx;
+            HDC hScreenDC;
+            RECT scrnrect;
+            hScreenDC=GetDC(NULL);
+
+            scrnrect.left=scrnrect.top=0;
+            scrnrect.bottom=GetDeviceCaps(hScreenDC,VERTRES);
+            scrnrect.right=GetDeviceCaps(hScreenDC,HORZRES);
+            char msg[500];
+
+            pTexturePrev->AddRef();
+
+            for(i = 0,curx=scrnrect.left,cury=scrnrect.top; i < ddsd.dwMipMapCount; i++) {
+
+                DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd_cur);
+                pTexturePrev->GetSurfaceDesc(&ddsd_cur);
+
+                hr = pTexturePrev->GetDC(&hTexDC);
+                if(FAILED(hr)) {
+                    dxgsg_cat.error() << "GetDC failed hr = " << ConvD3DErrorToString(hr) << "\n";
+                    break;
+                }
+
+                BOOL res;
+                        // res = BitBlt(_dxgsg->_hdc,0,0,ddsd.dwWidth,ddsd.dwHeight, TexDC,0,0,SRCCOPY);
+                        // loader inverts y, so use StretchBlt to re-invert it
+                        // res = StretchBlt(_dxgsg->hdc,0,ddsd_cur.dwHeight+cury,ddsd_cur.dwWidth,-ddsd_cur.dwHeight, TexDC,0,0,ddsd_cur.dwWidth,ddsd_cur.dwHeight,SRCCOPY);
+#if 0
+                if(cNumAlphaBits>0) {
+                    BLENDFUNCTION  bf;
+                    bf.BlendOp = AC_SRC_OVER;  bf.BlendFlags=0;
+                    bf.SourceConstantAlpha=255; bf.AlphaFormat=AC_SRC_ALPHA;
+                    res = AlphaBlend(hScreenDC,curx,cury,ddsd_cur.dwWidth,ddsd_cur.dwHeight, TexDC,0,0,ddsd_cur.dwWidth,ddsd_cur.dwHeight,bf);
+                    if(!res) {
+                        PrintLastError(msg);
+                        dxgsg_cat.error() << "AlphaBlend BLT failed: "<<msg<<"\n";
+                    }
+
+                } else
+#endif
+                {
+                    res = StretchBlt(hScreenDC,curx,ddsd_cur.dwHeight+cury,ddsd_cur.dwWidth,-((int)ddsd_cur.dwHeight), hTexDC,0,0,ddsd_cur.dwWidth,ddsd_cur.dwHeight,SRCCOPY);
+                    if(!res) {
+                        PrintLastError(msg);
+                        dxgsg_cat.error() << "StretchBLT failed: "<<msg<<"\n";
+
+                    }
+                }
+        //                SetBkMode(hScreenDC, TRANSPARENT);
+                sprintf(msg,"%d",i);
+                TextOut(hScreenDC,curx+(ddsd_cur.dwWidth)/2,5+cury+ddsd_cur.dwHeight,msg,strlen(msg));
+
+                curx+=max(20,ddsd_cur.dwWidth+10);
+
+                if(curx>scrnrect.right) {
+                    curx=0;  cury+=(scrnrect.bottom-scrnrect.top)/2;
+                }
+
+                hr = pTexturePrev->ReleaseDC(hTexDC);
+
+                if(FAILED(hr)) {
+                    dxgsg_cat.error() << "tex ReleaseDC failed for mip "<<i<<" hr = " << ConvD3DErrorToString(hr) << "\n";
+                    break;
+                }
+
+                if(i==ddsd.dwMipMapCount-1) {
+                    pTexturePrev->Release();
+                    continue;
+                }
+
+                DDSCAPS2 ddsCaps;
+                ZeroMemory(&ddsCaps,sizeof(DDSCAPS2));
+                ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
+                ddsCaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL;
+                hr = pTexturePrev->GetAttachedSurface(&ddsCaps, &pTextureCurrent);
+                if(FAILED(hr)) {
+                    dxgsg_cat.error() << " failed displaying mipmaps: GetAttachedSurf hr = " << ConvD3DErrorToString(hr) << "\n";
+                }
+                        // done with the previous texture
+                pTexturePrev->Release();
+                pTexturePrev = pTextureCurrent;
+            }
+
+            ReleaseDC(0,hScreenDC);
+
+            HANDLE hArr[1];
+            MSG winmsg;
+            hArr[0]=GetStdHandle(STD_INPUT_HANDLE);
+            GetMessage(&winmsg,NULL,0,0);
+
+            int val=MsgWaitForMultipleObjects(1,hArr,TRUE,INFINITE,QS_KEY);
+            if(val==-1) {
+                PrintLastError(msg);
+                dxgsg_cat.error() << " MsgWaitForMultipleObjects returns " << val << "  " <<msg << endl;
+            } else {
+                dxgsg_cat.error() << " MsgWaitForMultipleObjects returns " << val << endl;
+            }
+        }
+#endif
+    }
+    return S_OK;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Name: DeleteTexture()
+// Desc: Release the surface used to store the texture
+//-----------------------------------------------------------------------------
+void DXTextureContext::
+DeleteTexture( ) {
+    if(dxgsg_cat.is_spam()) {
+        dxgsg_cat.spam() << "Deleting DX texture for " << _tex->get_name() << "\n";
+    }
+
+    ULONG refcnt;
+
+#ifdef DEBUG_RELEASES
+    if(_surface) {
+        LPDIRECTDRAW7 pDD;
+        _surface->GetDDInterface( (VOID**)&pDD );
+        pDD->Release();
+
+        PRINTREFCNT(pDD,"before DeleteTex, IDDraw7");
+        RELEASE(_surface,dxgsg,"texture",false);
+        PRINTREFCNT(pDD,"after DeleteTex, IDDraw7");
+    }
+#else
+    RELEASE(_surface,dxgsg,"texture",false);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXTextureContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXTextureContext::
+DXTextureContext(Texture *tex) :
+TextureContext(tex) {
+//#ifdef NDEBUG
+    if(dxgsg_cat.is_spam()) {
+       dxgsg_cat.spam() << "Creating DX texture [" << tex->get_name() << "], minfilter(" << PandaFilterNameStrs[tex->get_minfilter()] << "), magfilter("<<PandaFilterNameStrs[tex->get_magfilter()] << "), anisodeg(" << tex->get_anisotropic_degree() << ")\n";
+    }
+//#endif
+    _surface = NULL;
+    _bHasMipMaps = FALSE;
+    _tex = tex;
+}
+
+DXTextureContext::
+~DXTextureContext() {
+    DeleteTexture();
+    TextureContext::~TextureContext();
+    _tex = NULL;
+}
+

+ 89 - 0
panda/src/dxgsg8/dxTextureContext8.h

@@ -0,0 +1,89 @@
+// Filename: dxTextureContext.h
+// Created by:  drose (07Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXTEXTURECONTEXT8_H
+#define DXTEXTURECONTEXT8_H
+
+#include <pandabase.h>
+
+#define WIN32_LEAN_AND_MEAN
+#ifndef STRICT
+// enable strict type checking in windows.h, see msdn
+#define STRICT
+#endif
+
+#include <windows.h>
+
+#include <ddraw.h>
+
+#define D3D_OVERLOADS   //  get D3DVECTOR '+' operator, etc from d3dtypes.h
+#include <d3d.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#include <texture.h>
+#include <textureContext.h>
+
+#define MAX_DX_TEXPIXFMTS 20    // should be enough for any card
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXTextureContext
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXTextureContext : public TextureContext {
+  friend class DXGraphicsStateGuardian;
+  friend class wdxGraphicsWindow;
+
+public:
+  DXTextureContext(Texture *tex);
+  ~DXTextureContext();
+
+  LPDIRECTDRAWSURFACE7  _surface;
+  Texture *_tex;            // ptr to parent, primarily for access to namestr
+
+  LPDIRECTDRAWSURFACE7 CreateTexture(LPDIRECT3DDEVICE7 pd3dDevice, int cNumTexPixFmts, LPDDPIXELFORMAT pTexPixFmts);
+
+  bool _bHasMipMaps;
+  DWORD _PixBufConversionType;  // enum ConversionType
+
+  // must be public since called from global callback fns
+  void DeleteTexture(void);
+  HRESULT FillDDSurfTexturePixels(void);
+
+protected:
+    unsigned int get_bits_per_pixel(PixelBuffer::Format format, int *alphbits);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TextureContext::init_type();
+    register_type(_type_handle, "DXTextureContext",
+          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;
+};
+
+#endif
+

+ 4 - 0
panda/src/dxgsg8/dxgsg8_composite1.cxx

@@ -0,0 +1,4 @@
+#include "config_dxgsg8.cxx"
+#include "dxSavedFrameBuffer8.cxx"
+#include "dxTextureContext8.cxx"
+#include "dxGeomNodeContext8.cxx"

+ 20 - 0
panda/src/wdxdisplay8/Sources.pp

@@ -0,0 +1,20 @@
+#define DIRECTORY_IF_DX yes
+
+#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
+                   dtoolutil:c dtoolbase:c dtool:m
+#define USE_DX yes
+
+#if $[BUILD_DX8]
+#begin lib_target
+  #define TARGET wdxdisplay8
+  #define LOCAL_LIBS dxgsg8
+    
+  #define COMBINED_SOURCES $[TARGET]_composite1.cxx 
+    
+  #define SOURCES \
+    config_wdxdisplay8.h wdxGraphicsPipe8.h wdxGraphicsWindow8.cxx wdxGraphicsWindow8.h
+    
+  #define INCLUDED_SOURCES config_wdxdisplay8.cxx wdxGraphicsPipe8.cxx 
+    
+#end lib_target
+#endif

+ 80 - 0
panda/src/wdxdisplay8/config_wdxdisplay8.cxx

@@ -0,0 +1,80 @@
+// Filename: config_wdxdisplay.cxx
+// Created by:  mike (07Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_wdxdisplay8.h"
+#include "wdxGraphicsPipe8.h"
+#include "wdxGraphicsWindow8.h"
+
+#include <dconfig.h>
+
+Configure(config_wdxdisplay);
+NotifyCategoryDef(wdxdisplay, "display");
+
+ConfigureFn(config_wdxdisplay) {
+  init_libwdxdisplay8();
+}
+
+bool dx_force_16bpp_zbuffer = config_wdxdisplay.GetBool("dx-force-16bpp-zbuffer", false);
+bool bResponsive_minimized_fullscreen_window = config_wdxdisplay.GetBool("responsive-minimized-fullscreen-window",false);
+
+extern void AtExitFn(void);
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libwdxdisplay
+//  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_libwdxdisplay8() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  atexit(AtExitFn);
+
+  wdxGraphicsPipe::init_type();
+  GraphicsPipe::get_factory().register_factory(
+            wdxGraphicsPipe::get_class_type(),
+            wdxGraphicsPipe::make_wdxGraphicsPipe);
+  wdxGraphicsWindow::init_type();
+  GraphicsWindow::get_factory().register_factory(
+            wdxGraphicsWindow::get_class_type(),
+                wdxGraphicsWindow::make_wdxGraphicsWindow);
+
+  set_global_parameters();
+}
+
+// cant use global var cleanly because global var static init executed after init_libwdxdisplay(), incorrectly reiniting var
+Filename get_icon_filename() {
+  string iconname = config_wdxdisplay.GetString("win32-window-icon","");
+  return ExecutionEnvironment::expand_string(iconname);
+}
+
+Filename get_color_cursor_filename() {
+  string cursorname = config_wdxdisplay.GetString("win32-color-cursor","");
+  return ExecutionEnvironment::expand_string(cursorname);
+}
+
+Filename get_mono_cursor_filename() {
+  string cursorname = config_wdxdisplay.GetString("win32-mono-cursor","");
+  return ExecutionEnvironment::expand_string(cursorname);
+}

+ 36 - 0
panda/src/wdxdisplay8/config_wdxdisplay8.h

@@ -0,0 +1,36 @@
+// Filename: config_wdxdisplay.h
+// Created by:  mike (07Oct99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef __CONFIG_WDXDISPLAY8_H__
+#define __CONFIG_WDXDISPLAY8_H__
+
+#include <pandabase.h>
+#include <filename.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(wdxdisplay, EXPCL_PANDADX, EXPTP_PANDADX);
+
+extern bool bResponsive_minimized_fullscreen_window;
+extern bool dx_force_16bpp_zbuffer;
+extern Filename get_icon_filename();
+extern Filename get_mono_cursor_filename();
+extern Filename get_color_cursor_filename();
+
+extern EXPCL_PANDADX void init_libwdxdisplay8();
+
+#endif /* __CONFIG_WDXDISPLAY_H__ */

+ 1603 - 0
panda/src/wdxdisplay8/wdxGraphicsPipe8.cxx

@@ -0,0 +1,1603 @@
+// Filename: wdxGraphicsPipe.cxx
+// Created by:  mike (09Jan97)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "wdxGraphicsPipe8.h"
+#include "config_wdxdisplay8.h"
+#include <mouseButton.h>
+#include <keyboardButton.h>
+#include <dxfile.h>
+
+////////////////////////////////////////////////////////////////////
+// Static variables
+////////////////////////////////////////////////////////////////////
+TypeHandle wdxGraphicsPipe::_type_handle;
+
+//wdxGraphicsPipe *global_pipe;
+
+wdxGraphicsPipe::wdxGraphicsPipe(const PipeSpecifier& spec)
+: InteractiveGraphicsPipe(spec) {
+//    _width = GetSystemMetrics(SM_CXSCREEN);
+//    _height = GetSystemMetrics(SM_CYSCREEN);
+    _shift = false;
+//  global_pipe = this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe::get_window_type
+//       Access: Public, Virtual
+//  Description: Returns the TypeHandle of the kind of window
+//               preferred by this kind of pipe.
+////////////////////////////////////////////////////////////////////
+TypeHandle wdxGraphicsPipe::
+get_window_type() const {
+    return wdxGraphicsWindow::get_class_type();
+}
+
+GraphicsPipe *wdxGraphicsPipe::
+make_wdxGraphicsPipe(const FactoryParams &params) {
+    GraphicsPipe::PipeSpec *pipe_param;
+    if(!get_param_into(pipe_param, params)) {
+        return new wdxGraphicsPipe(PipeSpecifier());
+    } else {
+        return new wdxGraphicsPipe(pipe_param->get_specifier());
+    }
+}
+
+TypeHandle wdxGraphicsPipe::get_class_type(void) {
+    return _type_handle;
+}
+
+const char *pipe_type_name="wdxGraphicsPipe";
+
+void wdxGraphicsPipe::init_type(void) {
+    InteractiveGraphicsPipe::init_type();
+    register_type(_type_handle, pipe_type_name,
+                  InteractiveGraphicsPipe::get_class_type());
+}
+
+TypeHandle wdxGraphicsPipe::get_type(void) const {
+    return get_class_type();
+}
+
+wdxGraphicsPipe::wdxGraphicsPipe(void) {
+    wdxdisplay_cat.error()
+    << pipe_type_name <<"s should not be created with the default constructor" << endl;
+}
+
+wdxGraphicsPipe::wdxGraphicsPipe(const wdxGraphicsPipe&) {
+    wdxdisplay_cat.error()
+    << pipe_type_name << "s should not be copied" << endl;
+}
+
+wdxGraphicsPipe& wdxGraphicsPipe::operator=(const wdxGraphicsPipe&) {
+    wdxdisplay_cat.error()
+    << pipe_type_name << "s should not be assigned" << endl;
+    return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: find_window
+//       Access:
+//  Description: Find the window that has the xwindow "win" in the
+//       window list for the pipe (if it exists)
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow *wdxGraphicsPipe::
+find_window(HWND win) {
+    int num_windows = get_num_windows();
+    for(int w = 0; w < num_windows; w++) {
+        wdxGraphicsWindow *window = DCAST(wdxGraphicsWindow, get_window(w));
+        if(window->_mwindow == win)
+            return window;
+    }
+    return NULL;
+}
+
+extern char * ConvD3DErrorToString(const HRESULT &error) {
+    switch(error) {
+        case E_FAIL:
+            return "Unspecified error E_FAIL";
+
+        case DD_OK:
+            return "No error.\0";
+        case D3DERR_BADMAJORVERSION      : // (700)
+            return "D3DERR_BADMAJORVERSION";//: // (700)
+        case D3DERR_BADMINORVERSION      : // (701)
+            return "D3DERR_BADMINORVERSION";//: // (701)
+        case D3DERR_INVALID_DEVICE   : // (705)
+            return "D3DERR_INVALID_DEVICE";//: // (705)
+        case D3DERR_INITFAILED       : // (706)
+            return "D3DERR_INITFAILED";//: // (706)
+        case D3DERR_DEVICEAGGREGATED : // (707)
+            return "D3DERR_DEVICEAGGREGATED";//: // (707)
+        case D3DERR_EXECUTE_CREATE_FAILED    : // (710)
+            return "D3DERR_EXECUTE_CREATE_FAILED";//: // (710)
+        case D3DERR_EXECUTE_DESTROY_FAILED   : // (711)
+            return "D3DERR_EXECUTE_DESTROY_FAILED";//: // (711)
+        case D3DERR_EXECUTE_LOCK_FAILED  : // (712)
+            return "D3DERR_EXECUTE_LOCK_FAILED";//: // (712)
+        case D3DERR_EXECUTE_UNLOCK_FAILED    : // (713)
+            return "D3DERR_EXECUTE_UNLOCK_FAILED";//: // (713)
+        case D3DERR_EXECUTE_LOCKED       : // (714)
+            return "D3DERR_EXECUTE_LOCKED";//: // (714)
+        case D3DERR_EXECUTE_NOT_LOCKED   : // (715)
+            return "D3DERR_EXECUTE_NOT_LOCKED";//: // (715)
+        case D3DERR_EXECUTE_FAILED       : // (716)
+            return "D3DERR_EXECUTE_FAILED";//: // (716)
+        case D3DERR_EXECUTE_CLIPPED_FAILED   : // (717)
+            return "D3DERR_EXECUTE_CLIPPED_FAILED";//: // (717)
+        case D3DERR_TEXTURE_NO_SUPPORT   : // (720)
+            return "D3DERR_TEXTURE_NO_SUPPORT";//: // (720)
+        case D3DERR_TEXTURE_CREATE_FAILED    : // (721)
+            return "D3DERR_TEXTURE_CREATE_FAILED";//: // (721)
+        case D3DERR_TEXTURE_DESTROY_FAILED   : // (722)
+            return "D3DERR_TEXTURE_DESTROY_FAILED";//: // (722)
+        case D3DERR_TEXTURE_LOCK_FAILED  : // (723)
+            return "D3DERR_TEXTURE_LOCK_FAILED";//: // (723)
+        case D3DERR_TEXTURE_UNLOCK_FAILED    : // (724)
+            return "D3DERR_TEXTURE_UNLOCK_FAILED";//: // (724)
+        case D3DERR_TEXTURE_LOAD_FAILED  : // (725)
+            return "D3DERR_TEXTURE_LOAD_FAILED";//: // (725)
+        case D3DERR_TEXTURE_SWAP_FAILED  : // (726)
+            return "D3DERR_TEXTURE_SWAP_FAILED";//: // (726)
+        case D3DERR_TEXTURE_LOCKED       : // (727)
+            return "D3DERR_TEXTURE_LOCKED";//: // (727)
+        case D3DERR_TEXTURE_NOT_LOCKED   : // (728)
+            return "D3DERR_TEXTURE_NOT_LOCKED";//: // (728)
+        case D3DERR_TEXTURE_GETSURF_FAILED   : // (729)
+            return "D3DERR_TEXTURE_GETSURF_FAILED";//: // (729)
+        case D3DERR_MATRIX_CREATE_FAILED : // (730)
+            return "D3DERR_MATRIX_CREATE_FAILED";//: // (730)
+        case D3DERR_MATRIX_DESTROY_FAILED    : // (731)
+            return "D3DERR_MATRIX_DESTROY_FAILED";//: // (731)
+        case D3DERR_MATRIX_SETDATA_FAILED    : // (732)
+            return "D3DERR_MATRIX_SETDATA_FAILED";//: // (732)
+        case D3DERR_MATRIX_GETDATA_FAILED    : // (733)
+            return "D3DERR_MATRIX_GETDATA_FAILED";//: // (733)
+        case D3DERR_SETVIEWPORTDATA_FAILED   : // (734)
+            return "D3DERR_SETVIEWPORTDATA_FAILED";//: // (734)
+        case D3DERR_INVALIDCURRENTVIEWPORT   : // (735)
+            return "D3DERR_INVALIDCURRENTVIEWPORT";//: // (735)
+        case D3DERR_INVALIDPRIMITIVETYPE     : // (736)
+            return "D3DERR_INVALIDPRIMITIVETYPE";//: // (736)
+        case D3DERR_INVALIDVERTEXTYPE        : // (737)
+            return "D3DERR_INVALIDVERTEXTYPE";//: // (737)
+        case D3DERR_TEXTURE_BADSIZE          : // (738)
+            return "D3DERR_TEXTURE_BADSIZE";//: // (738)
+        case D3DERR_INVALIDRAMPTEXTURE       : // (739)
+            return "D3DERR_INVALIDRAMPTEXTURE";//: // (739)
+        case D3DERR_MATERIAL_CREATE_FAILED   : // (740)
+            return "D3DERR_MATERIAL_CREATE_FAILED";//: // (740)
+        case D3DERR_MATERIAL_DESTROY_FAILED  : // (741)
+            return "D3DERR_MATERIAL_DESTROY_FAILED";//: // (741)
+        case D3DERR_MATERIAL_SETDATA_FAILED  : // (742)
+            return "D3DERR_MATERIAL_SETDATA_FAILED";//: // (742)
+        case D3DERR_MATERIAL_GETDATA_FAILED  : // (743)
+            return "D3DERR_MATERIAL_GETDATA_FAILED";//: // (743)
+        case D3DERR_INVALIDPALETTE           : // (744)
+            return "D3DERR_INVALIDPALETTE";//: // (744)
+        case D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY : // (745)
+            return "D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY";//: // (745)
+        case D3DERR_ZBUFF_NEEDS_VIDEOMEMORY  : // (746)
+            return "D3DERR_ZBUFF_NEEDS_VIDEOMEMORY";//: // (746)
+        case D3DERR_SURFACENOTINVIDMEM       : // (747)
+            return "D3DERR_SURFACENOTINVIDMEM";//: // (747)
+        case D3DERR_LIGHT_SET_FAILED     : // (750)
+            return "D3DERR_LIGHT_SET_FAILED";//: // (750)
+        case D3DERR_LIGHTHASVIEWPORT     : // (751)
+            return "D3DERR_LIGHTHASVIEWPORT";//: // (751)
+        case D3DERR_LIGHTNOTINTHISVIEWPORT           : // (752)
+            return "D3DERR_LIGHTNOTINTHISVIEWPORT";//: // (752)
+        case D3DERR_SCENE_IN_SCENE       : // (760)
+            return "D3DERR_SCENE_IN_SCENE";//: // (760)
+        case D3DERR_SCENE_NOT_IN_SCENE   : // (761)
+            return "D3DERR_SCENE_NOT_IN_SCENE";//: // (761)
+        case D3DERR_SCENE_BEGIN_FAILED   : // (762)
+            return "D3DERR_SCENE_BEGIN_FAILED";//: // (762)
+        case D3DERR_SCENE_END_FAILED     : // (763)
+            return "D3DERR_SCENE_END_FAILED";//: // (763)
+        case D3DERR_INBEGIN                  : // (770)
+            return "D3DERR_INBEGIN";//: // (770)
+        case D3DERR_NOTINBEGIN               : // (771)
+            return "D3DERR_NOTINBEGIN";//: // (771)
+        case D3DERR_NOVIEWPORTS              : // (772)
+            return "D3DERR_NOVIEWPORTS";//: // (772)
+        case D3DERR_VIEWPORTDATANOTSET       : // (773)
+            return "D3DERR_VIEWPORTDATANOTSET";//: // (773)
+        case D3DERR_VIEWPORTHASNODEVICE      : // (774)
+            return "D3DERR_VIEWPORTHASNODEVICE";//: // (774)
+        case D3DERR_NOCURRENTVIEWPORT        : // (775)
+            return "D3DERR_NOCURRENTVIEWPORT";//: // (775)
+        case D3DERR_INVALIDVERTEXFORMAT              : // (2048)
+            return "D3DERR_INVALIDVERTEXFORMAT";//: // (2048)
+        case D3DERR_COLORKEYATTACHED                 : // (2050)
+            return "D3DERR_COLORKEYATTACHED";//: // (2050)
+        case D3DERR_VERTEXBUFFEROPTIMIZED            : // (2060)
+            return "D3DERR_VERTEXBUFFEROPTIMIZED";//: // (2060)
+        case D3DERR_VBUF_CREATE_FAILED               : // (2061)
+            return "D3DERR_VBUF_CREATE_FAILED";//: // (2061)
+        case D3DERR_VERTEXBUFFERLOCKED               : // (2062)
+            return "D3DERR_VERTEXBUFFERLOCKED";//: // (2062)
+        case D3DERR_ZBUFFER_NOTPRESENT               : // (2070)
+            return "D3DERR_ZBUFFER_NOTPRESENT";//: // (2070)
+        case D3DERR_STENCILBUFFER_NOTPRESENT         : // (2071)
+            return "D3DERR_STENCILBUFFER_NOTPRESENT";//: // (2071)
+        case D3DERR_WRONGTEXTUREFORMAT               : // (2072)
+            return "D3DERR_WRONGTEXTUREFORMAT";//: // (2072)
+        case D3DERR_UNSUPPORTEDCOLOROPERATION        : // (2073)
+            return "D3DERR_UNSUPPORTEDCOLOROPERATION";//: // (2073)
+        case D3DERR_UNSUPPORTEDCOLORARG              : // (2074)
+            return "D3DERR_UNSUPPORTEDCOLORARG";//: // (2074)
+        case D3DERR_UNSUPPORTEDALPHAOPERATION        : // (2075)
+            return "D3DERR_UNSUPPORTEDALPHAOPERATION";//: // (2075)
+        case D3DERR_UNSUPPORTEDALPHAARG              : // (2076)
+            return "D3DERR_UNSUPPORTEDALPHAARG";//: // (2076)
+        case D3DERR_TOOMANYOPERATIONS                : // (2077)
+            return "D3DERR_TOOMANYOPERATIONS";//: // (2077)
+        case D3DERR_CONFLICTINGTEXTUREFILTER         : // (2078)
+            return "D3DERR_CONFLICTINGTEXTUREFILTER";//: // (2078)
+        case D3DERR_UNSUPPORTEDFACTORVALUE           : // (2079)
+            return "D3DERR_UNSUPPORTEDFACTORVALUE";//: // (2079)
+        case D3DERR_CONFLICTINGRENDERSTATE           : // (2081)
+            return "D3DERR_CONFLICTINGRENDERSTATE";//: // (2081)
+        case D3DERR_UNSUPPORTEDTEXTUREFILTER         : // (2082)
+            return "D3DERR_UNSUPPORTEDTEXTUREFILTER";//: // (2082)
+        case D3DERR_TOOMANYPRIMITIVES                : // (2083)
+            return "D3DERR_TOOMANYPRIMITIVES";//: // (2083)
+        case D3DERR_INVALIDMATRIX                    : // (2084)
+            return "D3DERR_INVALIDMATRIX";//: // (2084)
+        case D3DERR_TOOMANYVERTICES                  : // (2085)
+            return "D3DERR_TOOMANYVERTICES";//: // (2085)
+        case D3DERR_CONFLICTINGTEXTUREPALETTE        : // (2086)
+            return "D3DERR_CONFLICTINGTEXTUREPALETTE";//: // (2086)
+//#if DX7
+        case D3DERR_VERTEXBUFFERUNLOCKFAILED         : // (2063)
+            return "D3DERR_VERTEXBUFFERUNLOCKFAILED";//: // (2063)
+        case D3DERR_INVALIDSTATEBLOCK        : // (2100)
+            return "D3DERR_INVALIDSTATEBLOCK";//: // (2100)
+        case D3DERR_INBEGINSTATEBLOCK        : // (2101)
+            return "D3DERR_INBEGINSTATEBLOCK";//: // (2101)
+        case D3DERR_NOTINBEGINSTATEBLOCK     : // (2102)
+            return "D3DERR_NOTINBEGINSTATEBLOCK";//: // (2102)
+  //case D3DERR_INOVERLAYSTATEBLOCK      : // (2103)
+  //  return "D3DERR_INOVERLAYSTATEBLOCK";//: // (2103)
+        case DDERR_NOSTEREOHARDWARE       : // ( 181 )
+            return "DDERR_NOSTEREOHARDWARE      ";//: // ( 181 )
+        case DDERR_NOSURFACELEFT              : // ( 182 )
+            return "DDERR_NOSURFACELEFT             ";//: // ( 182 )
+        case DDERR_DDSCAPSCOMPLEXREQUIRED            : // ( 542 )
+            return "DDERR_DDSCAPSCOMPLEXREQUIRED";//: // ( 542 )
+        case DDERR_NOTONMIPMAPSUBLEVEL               : // ( 603 )
+            return "DDERR_NOTONMIPMAPSUBLEVEL";//: // ( 603 )
+        case DDERR_TESTFINISHED                      : // ( 692 )
+            return "DDERR_TESTFINISHED";//: // ( 692 )
+        case DDERR_NEWMODE                           : // ( 693 )
+            return "DDERR_NEWMODE";//: // ( 693 )
+//#endif
+    //case D3DERR_COMMAND_UNPARSED              : // (3000)
+    /// return "case";//D3DERR_COMMAND_UNPARSED              : // (3000)
+
+        case DDERR_ALREADYINITIALIZED     : // ( 5 )
+            return "DDERR_ALREADYINITIALIZED        ";//: // ( 5 )
+        case DDERR_CANNOTATTACHSURFACE        : // ( 10 )
+            return "DDERR_CANNOTATTACHSURFACE       ";//: // ( 10 )
+        case DDERR_CANNOTDETACHSURFACE        : // ( 20 )
+            return "DDERR_CANNOTDETACHSURFACE       ";//: // ( 20 )
+        case DDERR_CURRENTLYNOTAVAIL          : // ( 40 )
+            return "DDERR_CURRENTLYNOTAVAIL         ";//: // ( 40 )
+        case DDERR_EXCEPTION              : // ( 55 )
+            return "DDERR_EXCEPTION             ";//: // ( 55 )
+        case DDERR_HEIGHTALIGN            : // ( 90 )
+            return "DDERR_HEIGHTALIGN           ";//: // ( 90 )
+        case DDERR_INCOMPATIBLEPRIMARY        : // ( 95 )
+            return "DDERR_INCOMPATIBLEPRIMARY       ";//: // ( 95 )
+        case DDERR_INVALIDCAPS            : // ( 100 )
+            return "DDERR_INVALIDCAPS           ";//: // ( 100 )
+        case DDERR_INVALIDCLIPLIST            : // ( 110 )
+            return "DDERR_INVALIDCLIPLIST           ";//: // ( 110 )
+        case DDERR_INVALIDMODE            : // ( 120 )
+            return "DDERR_INVALIDMODE           ";//: // ( 120 )
+        case DDERR_INVALIDOBJECT          : // ( 130 )
+            return "DDERR_INVALIDOBJECT         ";//: // ( 130 )
+        case DDERR_INVALIDPIXELFORMAT     : // ( 145 )
+            return "DDERR_INVALIDPIXELFORMAT        ";//: // ( 145 )
+        case DDERR_INVALIDRECT            : // ( 150 )
+            return "DDERR_INVALIDRECT           ";//: // ( 150 )
+        case DDERR_LOCKEDSURFACES         : // ( 160 )
+            return "DDERR_LOCKEDSURFACES            ";//: // ( 160 )
+        case DDERR_NO3D               : // ( 170 )
+            return "DDERR_NO3D              ";//: // ( 170 )
+        case DDERR_NOALPHAHW              : // ( 180 )
+            return "DDERR_NOALPHAHW             ";//: // ( 180 )
+        case DDERR_NOCLIPLIST         : // ( 205 )
+            return "DDERR_NOCLIPLIST            ";//: // ( 205 )
+        case DDERR_NOCOLORCONVHW          : // ( 210 )
+            return "DDERR_NOCOLORCONVHW         ";//: // ( 210 )
+        case DDERR_NOCOOPERATIVELEVELSET      : // ( 212 )
+            return "DDERR_NOCOOPERATIVELEVELSET     ";//: // ( 212 )
+        case DDERR_NOCOLORKEY         : // ( 215 )
+            return "DDERR_NOCOLORKEY            ";//: // ( 215 )
+        case DDERR_NOCOLORKEYHW           : // ( 220 )
+            return "DDERR_NOCOLORKEYHW          ";//: // ( 220 )
+        case DDERR_NODIRECTDRAWSUPPORT        : // ( 222 )
+            return "DDERR_NODIRECTDRAWSUPPORT       ";//: // ( 222 )
+        case DDERR_NOEXCLUSIVEMODE            : // ( 225 )
+            return "DDERR_NOEXCLUSIVEMODE           ";//: // ( 225 )
+        case DDERR_NOFLIPHW               : // ( 230 )
+            return "DDERR_NOFLIPHW              ";//: // ( 230 )
+        case DDERR_NOGDI              : // ( 240 )
+            return "DDERR_NOGDI             ";//: // ( 240 )
+        case DDERR_NOMIRRORHW         : // ( 250 )
+            return "DDERR_NOMIRRORHW            ";//: // ( 250 )
+        case DDERR_NOTFOUND               : // ( 255 )
+            return "DDERR_NOTFOUND              ";//: // ( 255 )
+        case DDERR_NOOVERLAYHW            : // ( 260 )
+            return "DDERR_NOOVERLAYHW           ";//: // ( 260 )
+        case DDERR_OVERLAPPINGRECTS           : // ( 270 )
+            return "DDERR_OVERLAPPINGRECTS          ";//: // ( 270 )
+        case DDERR_NORASTEROPHW           : // ( 280 )
+            return "DDERR_NORASTEROPHW          ";//: // ( 280 )
+        case DDERR_NOROTATIONHW           : // ( 290 )
+            return "DDERR_NOROTATIONHW          ";//: // ( 290 )
+        case DDERR_NOSTRETCHHW            : // ( 310 )
+            return "DDERR_NOSTRETCHHW           ";//: // ( 310 )
+        case DDERR_NOT4BITCOLOR           : // ( 316 )
+            return "DDERR_NOT4BITCOLOR          ";//: // ( 316 )
+        case DDERR_NOT4BITCOLORINDEX          : // ( 317 )
+            return "DDERR_NOT4BITCOLORINDEX         ";//: // ( 317 )
+        case DDERR_NOT8BITCOLOR           : // ( 320 )
+            return "DDERR_NOT8BITCOLOR          ";//: // ( 320 )
+        case DDERR_NOTEXTUREHW            : // ( 330 )
+            return "DDERR_NOTEXTUREHW           ";//: // ( 330 )
+        case DDERR_NOVSYNCHW              : // ( 335 )
+            return "DDERR_NOVSYNCHW             ";//: // ( 335 )
+        case DDERR_NOZBUFFERHW            : // ( 340 )
+            return "DDERR_NOZBUFFERHW           ";//: // ( 340 )
+        case DDERR_NOZOVERLAYHW           : // ( 350 )
+            return "DDERR_NOZOVERLAYHW          ";//: // ( 350 )
+        case DDERR_OUTOFCAPS              : // ( 360 )
+            return "DDERR_OUTOFCAPS             ";//: // ( 360 )
+        case DDERR_OUTOFVIDEOMEMORY           : // ( 380 )
+            return "DDERR_OUTOFVIDEOMEMORY          ";//: // ( 380 )
+        case DDERR_OVERLAYCANTCLIP            : // ( 382 )
+            return "DDERR_OVERLAYCANTCLIP           ";//: // ( 382 )
+        case DDERR_OVERLAYCOLORKEYONLYONEACTIVE   : // ( 384 )
+            return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE  ";//: // ( 384 )
+        case DDERR_PALETTEBUSY            : // ( 387 )
+            return "DDERR_PALETTEBUSY           ";//: // ( 387 )
+        case DDERR_COLORKEYNOTSET         : // ( 400 )
+            return "DDERR_COLORKEYNOTSET            ";//: // ( 400 )
+        case DDERR_SURFACEALREADYATTACHED     : // ( 410 )
+            return "DDERR_SURFACEALREADYATTACHED        ";//: // ( 410 )
+        case DDERR_SURFACEALREADYDEPENDENT        : // ( 420 )
+            return "DDERR_SURFACEALREADYDEPENDENT       ";//: // ( 420 )
+        case DDERR_SURFACEBUSY            : // ( 430 )
+            return "DDERR_SURFACEBUSY           ";//: // ( 430 )
+        case DDERR_CANTLOCKSURFACE                   : // ( 435 )
+            return "DDERR_CANTLOCKSURFACE";//: // ( 435 )
+        case DDERR_SURFACEISOBSCURED          : // ( 440 )
+            return "DDERR_SURFACEISOBSCURED         ";//: // ( 440 )
+        case DDERR_SURFACELOST            : // ( 450 )
+            return "DDERR_SURFACELOST           ";//: // ( 450 )
+        case DDERR_SURFACENOTATTACHED     : // ( 460 )
+            return "DDERR_SURFACENOTATTACHED        ";//: // ( 460 )
+        case DDERR_TOOBIGHEIGHT           : // ( 470 )
+            return "DDERR_TOOBIGHEIGHT          ";//: // ( 470 )
+        case DDERR_TOOBIGSIZE         : // ( 480 )
+            return "DDERR_TOOBIGSIZE            ";//: // ( 480 )
+        case DDERR_TOOBIGWIDTH            : // ( 490 )
+            return "DDERR_TOOBIGWIDTH           ";//: // ( 490 )
+        case DDERR_UNSUPPORTEDFORMAT          : // ( 510 )
+            return "DDERR_UNSUPPORTEDFORMAT         ";//: // ( 510 )
+        case DDERR_UNSUPPORTEDMASK            : // ( 520 )
+            return "DDERR_UNSUPPORTEDMASK           ";//: // ( 520 )
+        case DDERR_INVALIDSTREAM                     : // ( 521 )
+            return "DDERR_INVALIDSTREAM";//: // ( 521 )
+        case DDERR_VERTICALBLANKINPROGRESS        : // ( 537 )
+            return "DDERR_VERTICALBLANKINPROGRESS       ";//: // ( 537 )
+        case DDERR_WASSTILLDRAWING            : // ( 540 )
+            return "DDERR_WASSTILLDRAWING           ";//: // ( 540 )
+        case DDERR_XALIGN             : // ( 560 )
+            return "DDERR_XALIGN                ";//: // ( 560 )
+        case DDERR_INVALIDDIRECTDRAWGUID      : // ( 561 )
+            return "DDERR_INVALIDDIRECTDRAWGUID     ";//: // ( 561 )
+        case DDERR_DIRECTDRAWALREADYCREATED       : // ( 562 )
+            return "DDERR_DIRECTDRAWALREADYCREATED      ";//: // ( 562 )
+        case DDERR_NODIRECTDRAWHW         : // ( 563 )
+            return "DDERR_NODIRECTDRAWHW            ";//: // ( 563 )
+        case DDERR_PRIMARYSURFACEALREADYEXISTS    : // ( 564 )
+            return "DDERR_PRIMARYSURFACEALREADYEXISTS   ";//: // ( 564 )
+        case DDERR_NOEMULATION            : // ( 565 )
+            return "DDERR_NOEMULATION           ";//: // ( 565 )
+        case DDERR_REGIONTOOSMALL         : // ( 566 )
+            return "DDERR_REGIONTOOSMALL            ";//: // ( 566 )
+        case DDERR_CLIPPERISUSINGHWND     : // ( 567 )
+            return "DDERR_CLIPPERISUSINGHWND        ";//: // ( 567 )
+        case DDERR_NOCLIPPERATTACHED          : // ( 568 )
+            return "DDERR_NOCLIPPERATTACHED         ";//: // ( 568 )
+        case DDERR_NOHWND             : // ( 569 )
+            return "DDERR_NOHWND                ";//: // ( 569 )
+        case DDERR_HWNDSUBCLASSED         : // ( 570 )
+            return "DDERR_HWNDSUBCLASSED            ";//: // ( 570 )
+        case DDERR_HWNDALREADYSET         : // ( 571 )
+            return "DDERR_HWNDALREADYSET            ";//: // ( 571 )
+        case DDERR_NOPALETTEATTACHED          : // ( 572 )
+            return "DDERR_NOPALETTEATTACHED         ";//: // ( 572 )
+        case DDERR_NOPALETTEHW            : // ( 573 )
+            return "DDERR_NOPALETTEHW           ";//: // ( 573 )
+        case DDERR_BLTFASTCANTCLIP            : // ( 574 )
+            return "DDERR_BLTFASTCANTCLIP           ";//: // ( 574 )
+        case DDERR_NOBLTHW                : // ( 575 )
+            return "DDERR_NOBLTHW               ";//: // ( 575 )
+        case DDERR_NODDROPSHW         : // ( 576 )
+            return "DDERR_NODDROPSHW            ";//: // ( 576 )
+        case DDERR_OVERLAYNOTVISIBLE          : // ( 577 )
+            return "DDERR_OVERLAYNOTVISIBLE         ";//: // ( 577 )
+        case DDERR_NOOVERLAYDEST          : // ( 578 )
+            return "DDERR_NOOVERLAYDEST         ";//: // ( 578 )
+        case DDERR_INVALIDPOSITION            : // ( 579 )
+            return "DDERR_INVALIDPOSITION           ";//: // ( 579 )
+        case DDERR_NOTAOVERLAYSURFACE     : // ( 580 )
+            return "DDERR_NOTAOVERLAYSURFACE        ";//: // ( 580 )
+        case DDERR_EXCLUSIVEMODEALREADYSET        : // ( 581 )
+            return "DDERR_EXCLUSIVEMODEALREADYSET       ";//: // ( 581 )
+        case DDERR_NOTFLIPPABLE           : // ( 582 )
+            return "DDERR_NOTFLIPPABLE          ";//: // ( 582 )
+        case DDERR_CANTDUPLICATE          : // ( 583 )
+            return "DDERR_CANTDUPLICATE         ";//: // ( 583 )
+        case DDERR_NOTLOCKED              : // ( 584 )
+            return "DDERR_NOTLOCKED             ";//: // ( 584 )
+        case DDERR_CANTCREATEDC           : // ( 585 )
+            return "DDERR_CANTCREATEDC          ";//: // ( 585 )
+        case DDERR_NODC               : // ( 586 )
+            return "DDERR_NODC              ";//: // ( 586 )
+        case DDERR_WRONGMODE              : // ( 587 )
+            return "DDERR_WRONGMODE             ";//: // ( 587 )
+        case DDERR_IMPLICITLYCREATED          : // ( 588 )
+            return "DDERR_IMPLICITLYCREATED         ";//: // ( 588 )
+        case DDERR_NOTPALETTIZED          : // ( 589 )
+            return "DDERR_NOTPALETTIZED         ";//: // ( 589 )
+        case DDERR_UNSUPPORTEDMODE            : // ( 590 )
+            return "DDERR_UNSUPPORTEDMODE           ";//: // ( 590 )
+        case DDERR_NOMIPMAPHW         : // ( 591 )
+            return "DDERR_NOMIPMAPHW            ";//: // ( 591 )
+        case DDERR_INVALIDSURFACETYPE                : // ( 592 )
+            return "DDERR_INVALIDSURFACETYPE";//: // ( 592 )
+        case DDERR_NOOPTIMIZEHW                      : // ( 600 )
+            return "DDERR_NOOPTIMIZEHW";//: // ( 600 )
+        case DDERR_NOTLOADED                         : // ( 601 )
+            return "DDERR_NOTLOADED";//: // ( 601 )
+        case DDERR_NOFOCUSWINDOW                     : // ( 602 )
+            return "DDERR_NOFOCUSWINDOW";//: // ( 602 )
+        case DDERR_DCALREADYCREATED           : // ( 620 )
+            return "DDERR_DCALREADYCREATED          ";//: // ( 620 )
+        case DDERR_NONONLOCALVIDMEM                  : // ( 630 )
+            return "DDERR_NONONLOCALVIDMEM";//: // ( 630 )
+        case DDERR_CANTPAGELOCK           : // ( 640 )
+            return "DDERR_CANTPAGELOCK          ";//: // ( 640 )
+        case DDERR_CANTPAGEUNLOCK         : // ( 660 )
+            return "DDERR_CANTPAGEUNLOCK            ";//: // ( 660 )
+        case DDERR_NOTPAGELOCKED          : // ( 680 )
+            return "DDERR_NOTPAGELOCKED         ";//: // ( 680 )
+        case DDERR_MOREDATA                   : // ( 690 )
+            return "DDERR_MOREDATA                  ";//: // ( 690 )
+        case DDERR_EXPIRED                           : // ( 691 )
+            return "DDERR_EXPIRED";//: // ( 691 )
+        case DDERR_VIDEONOTACTIVE             : // ( 695 )
+            return "DDERR_VIDEONOTACTIVE            ";//: // ( 695 )
+        case DDERR_DEVICEDOESNTOWNSURFACE         : // ( 699 )
+            return "DDERR_DEVICEDOESNTOWNSURFACE        ";//: // ( 699 )
+        case DXFILEERR_BADOBJECT                 : // (850)
+            return "DXFILEERR_BADOBJECT";//: // (850)
+        case DXFILEERR_BADVALUE                  : // (851)
+            return "DXFILEERR_BADVALUE";//: // (851)
+        case DXFILEERR_BADTYPE                   : // (852)
+            return "DXFILEERR_BADTYPE";//: // (852)
+        case DXFILEERR_BADSTREAMHANDLE           : // (853)
+            return "DXFILEERR_BADSTREAMHANDLE";//: // (853)
+        case DXFILEERR_BADALLOC                  : // (854)
+            return "DXFILEERR_BADALLOC";//: // (854)
+        case DXFILEERR_NOTFOUND                  : // (855)
+            return "DXFILEERR_NOTFOUND";//: // (855)
+        case DXFILEERR_NOTDONEYET                : // (856)
+            return "DXFILEERR_NOTDONEYET";//: // (856)
+        case DXFILEERR_FILENOTFOUND              : // (857)
+            return "DXFILEERR_FILENOTFOUND";//: // (857)
+        case DXFILEERR_RESOURCENOTFOUND          : // (858)
+            return "DXFILEERR_RESOURCENOTFOUND";//: // (858)
+        case DXFILEERR_URLNOTFOUND               : // (859)
+            return "DXFILEERR_URLNOTFOUND";//: // (859)
+        case DXFILEERR_BADRESOURCE               : // (860)
+            return "DXFILEERR_BADRESOURCE";//: // (860)
+        case DXFILEERR_BADFILETYPE               : // (861)
+            return "DXFILEERR_BADFILETYPE";//: // (861)
+        case DXFILEERR_BADFILEVERSION            : // (862)
+            return "DXFILEERR_BADFILEVERSION";//: // (862)
+        case DXFILEERR_BADFILEFLOATSIZE          : // (863)
+            return "DXFILEERR_BADFILEFLOATSIZE";//: // (863)
+        case DXFILEERR_BADFILECOMPRESSIONTYPE    : // (864)
+            return "DXFILEERR_BADFILECOMPRESSIONTYPE";//: // (864)
+        case DXFILEERR_BADFILE                   : // (865)
+            return "DXFILEERR_BADFILE";//: // (865)
+        case DXFILEERR_PARSEERROR                : // (866)
+            return "DXFILEERR_PARSEERROR";//: // (866)
+        case DXFILEERR_NOTEMPLATE                : // (867)
+            return "DXFILEERR_NOTEMPLATE";//: // (867)
+        case DXFILEERR_BADARRAYSIZE              : // (868)
+            return "DXFILEERR_BADARRAYSIZE";//: // (868)
+        case DXFILEERR_BADDATAREFERENCE          : // (869)
+            return "DXFILEERR_BADDATAREFERENCE";//: // (869)
+        case DXFILEERR_INTERNALERROR             : // (870)
+            return "DXFILEERR_INTERNALERROR";//: // (870)
+        case DXFILEERR_NOMOREOBJECTS             : // (871)
+            return "DXFILEERR_NOMOREOBJECTS";//: // (871)
+        case DXFILEERR_BADINTRINSICS             : // (872)
+            return "DXFILEERR_BADINTRINSICS";//: // (872)
+        case DXFILEERR_NOMORESTREAMHANDLES       : // (873)
+            return "DXFILEERR_NOMORESTREAMHANDLES";//: // (873)
+        case DXFILEERR_NOMOREDATA                : // (874)
+            return "DXFILEERR_NOMOREDATA";//: // (874)
+        case DXFILEERR_BADCACHEFILE              : // (875)
+            return "DXFILEERR_BADCACHEFILE";//: // (875)
+        case DXFILEERR_NOINTERNET                : // (876)
+            return "DXFILEERR_NOINTERNET";//: // (876)
+
+        case E_UNEXPECTED                     :
+            return "E_UNEXPECTED                     ";
+        case E_NOTIMPL                        :
+            return "E_NOTIMPL                        ";
+        case E_OUTOFMEMORY                    :
+            return "E_OUTOFMEMORY                    ";
+        case E_INVALIDARG                     :
+            return "E_INVALIDARG or DDERR_INVALIDPARAMS";
+        case E_NOINTERFACE                    :
+            return "E_NOINTERFACE                    ";
+        case E_POINTER                        :
+            return "E_POINTER                        ";
+        case E_HANDLE                         :
+            return "E_HANDLE                         ";
+        case E_ABORT                          :
+            return "E_ABORT                          ";
+//    case E_FAIL                           :
+//    return "E_FAIL                           ";
+        case E_ACCESSDENIED                   :
+            return "E_ACCESSDENIED                   ";
+        case E_PENDING                        :
+            return "E_PENDING                        ";
+        case CO_E_INIT_TLS                    :
+            return "CO_E_INIT_TLS                    ";
+        case CO_E_INIT_SHARED_ALLOCATOR       :
+            return "CO_E_INIT_SHARED_ALLOCATOR       ";
+        case CO_E_INIT_MEMORY_ALLOCATOR       :
+            return "CO_E_INIT_MEMORY_ALLOCATOR       ";
+        case CO_E_INIT_CLASS_CACHE            :
+            return "CO_E_INIT_CLASS_CACHE            ";
+        case CO_E_INIT_RPC_CHANNEL            :
+            return "CO_E_INIT_RPC_CHANNEL            ";
+        case CO_E_INIT_TLS_SET_CHANNEL_CONTROL :
+            return "CO_E_INIT_TLS_SET_CHANNEL_CONTROL ";
+        case CO_E_INIT_TLS_CHANNEL_CONTROL    :
+            return "CO_E_INIT_TLS_CHANNEL_CONTROL    ";
+        case CO_E_INIT_UNACCEPTED_USER_ALLOCATOR :
+            return "CO_E_INIT_UNACCEPTED_USER_ALLOCATOR ";
+        case CO_E_INIT_SCM_MUTEX_EXISTS       :
+            return "CO_E_INIT_SCM_MUTEX_EXISTS       ";
+        case CO_E_INIT_SCM_FILE_MAPPING_EXISTS :
+            return "CO_E_INIT_SCM_FILE_MAPPING_EXISTS ";
+        case CO_E_INIT_SCM_MAP_VIEW_OF_FILE   :
+            return "CO_E_INIT_SCM_MAP_VIEW_OF_FILE   ";
+        case CO_E_INIT_SCM_EXEC_FAILURE       :
+            return "CO_E_INIT_SCM_EXEC_FAILURE       ";
+        case CO_E_INIT_ONLY_SINGLE_THREADED   :
+            return "CO_E_INIT_ONLY_SINGLE_THREADED   ";
+        case CO_E_CANT_REMOTE                 :
+            return "CO_E_CANT_REMOTE                 ";
+        case CO_E_BAD_SERVER_NAME             :
+            return "CO_E_BAD_SERVER_NAME             ";
+        case CO_E_WRONG_SERVER_IDENTITY       :
+            return "CO_E_WRONG_SERVER_IDENTITY       ";
+        case CO_E_OLE1DDE_DISABLED            :
+            return "CO_E_OLE1DDE_DISABLED            ";
+        case CO_E_RUNAS_SYNTAX                :
+            return "CO_E_RUNAS_SYNTAX                ";
+        case CO_E_CREATEPROCESS_FAILURE       :
+            return "CO_E_CREATEPROCESS_FAILURE       ";
+        case CO_E_RUNAS_CREATEPROCESS_FAILURE :
+            return "CO_E_RUNAS_CREATEPROCESS_FAILURE ";
+        case CO_E_RUNAS_LOGON_FAILURE         :
+            return "CO_E_RUNAS_LOGON_FAILURE         ";
+        case CO_E_LAUNCH_PERMSSION_DENIED     :
+            return "CO_E_LAUNCH_PERMSSION_DENIED     ";
+        case CO_E_START_SERVICE_FAILURE       :
+            return "CO_E_START_SERVICE_FAILURE       ";
+        case CO_E_REMOTE_COMMUNICATION_FAILURE :
+            return "CO_E_REMOTE_COMMUNICATION_FAILURE ";
+        case CO_E_SERVER_START_TIMEOUT        :
+            return "CO_E_SERVER_START_TIMEOUT        ";
+        case CO_E_CLSREG_INCONSISTENT         :
+            return "CO_E_CLSREG_INCONSISTENT         ";
+        case CO_E_IIDREG_INCONSISTENT         :
+            return "CO_E_IIDREG_INCONSISTENT         ";
+        case CO_E_NOT_SUPPORTED               :
+            return "CO_E_NOT_SUPPORTED               ";
+        case CO_E_RELOAD_DLL                  :
+            return "CO_E_RELOAD_DLL                  ";
+        case CO_E_MSI_ERROR                   :
+            return "CO_E_MSI_ERROR                   ";
+        case OLE_E_OLEVERB                    :
+            return "OLE_E_OLEVERB                    ";
+        case OLE_E_ADVF                       :
+            return "OLE_E_ADVF                       ";
+        case OLE_E_ENUM_NOMORE                :
+            return "OLE_E_ENUM_NOMORE                ";
+        case OLE_E_ADVISENOTSUPPORTED         :
+            return "OLE_E_ADVISENOTSUPPORTED         ";
+        case OLE_E_NOCONNECTION               :
+            return "OLE_E_NOCONNECTION               ";
+        case OLE_E_NOTRUNNING                 :
+            return "OLE_E_NOTRUNNING                 ";
+        case OLE_E_NOCACHE                    :
+            return "OLE_E_NOCACHE                    ";
+        case OLE_E_BLANK                      :
+            return "OLE_E_BLANK                      ";
+        case OLE_E_CLASSDIFF                  :
+            return "OLE_E_CLASSDIFF                  ";
+        case OLE_E_CANT_GETMONIKER            :
+            return "OLE_E_CANT_GETMONIKER            ";
+        case OLE_E_CANT_BINDTOSOURCE          :
+            return "OLE_E_CANT_BINDTOSOURCE          ";
+        case OLE_E_STATIC                     :
+            return "OLE_E_STATIC                     ";
+        case OLE_E_PROMPTSAVECANCELLED        :
+            return "OLE_E_PROMPTSAVECANCELLED        ";
+        case OLE_E_INVALIDRECT                :
+            return "OLE_E_INVALIDRECT                ";
+        case OLE_E_WRONGCOMPOBJ               :
+            return "OLE_E_WRONGCOMPOBJ               ";
+        case OLE_E_INVALIDHWND                :
+            return "OLE_E_INVALIDHWND                ";
+        case OLE_E_NOT_INPLACEACTIVE          :
+            return "OLE_E_NOT_INPLACEACTIVE          ";
+        case OLE_E_CANTCONVERT                :
+            return "OLE_E_CANTCONVERT                ";
+        case OLE_E_NOSTORAGE                  :
+            return "OLE_E_NOSTORAGE                  ";
+        case DV_E_FORMATETC                   :
+            return "DV_E_FORMATETC                   ";
+        case DV_E_DVTARGETDEVICE              :
+            return "DV_E_DVTARGETDEVICE              ";
+        case DV_E_STGMEDIUM                   :
+            return "DV_E_STGMEDIUM                   ";
+        case DV_E_STATDATA                    :
+            return "DV_E_STATDATA                    ";
+        case DV_E_LINDEX                      :
+            return "DV_E_LINDEX                      ";
+        case DV_E_TYMED                       :
+            return "DV_E_TYMED                       ";
+        case DV_E_CLIPFORMAT                  :
+            return "DV_E_CLIPFORMAT                  ";
+        case DV_E_DVASPECT                    :
+            return "DV_E_DVASPECT                    ";
+        case DV_E_DVTARGETDEVICE_SIZE         :
+            return "DV_E_DVTARGETDEVICE_SIZE         ";
+        case DV_E_NOIVIEWOBJECT               :
+            return "DV_E_NOIVIEWOBJECT               ";
+        case DRAGDROP_E_NOTREGISTERED         :
+            return "DRAGDROP_E_NOTREGISTERED         ";
+        case DRAGDROP_E_ALREADYREGISTERED     :
+            return "DRAGDROP_E_ALREADYREGISTERED     ";
+        case DRAGDROP_E_INVALIDHWND           :
+            return "DRAGDROP_E_INVALIDHWND           ";
+        case CLASS_E_NOAGGREGATION            :
+            return "CLASS_E_NOAGGREGATION            ";
+        case CLASS_E_CLASSNOTAVAILABLE        :
+            return "CLASS_E_CLASSNOTAVAILABLE        ";
+        case CLASS_E_NOTLICENSED              :
+            return "CLASS_E_NOTLICENSED              ";
+        case VIEW_E_DRAW                      :
+            return "VIEW_E_DRAW                      ";
+        case REGDB_E_READREGDB                :
+            return "REGDB_E_READREGDB                ";
+        case REGDB_E_WRITEREGDB               :
+            return "REGDB_E_WRITEREGDB               ";
+        case REGDB_E_KEYMISSING               :
+            return "REGDB_E_KEYMISSING               ";
+        case REGDB_E_INVALIDVALUE             :
+            return "REGDB_E_INVALIDVALUE             ";
+        case REGDB_E_CLASSNOTREG              :
+            return "REGDB_E_CLASSNOTREG              ";
+        case REGDB_E_IIDNOTREG                :
+            return "REGDB_E_IIDNOTREG                ";
+        case CAT_E_CATIDNOEXIST               :
+            return "CAT_E_CATIDNOEXIST               ";
+        case CAT_E_NODESCRIPTION              :
+            return "CAT_E_NODESCRIPTION              ";
+        case CS_E_PACKAGE_NOTFOUND            :
+            return "CS_E_PACKAGE_NOTFOUND            ";
+        case CS_E_NOT_DELETABLE               :
+            return "CS_E_NOT_DELETABLE               ";
+        case CS_E_CLASS_NOTFOUND              :
+            return "CS_E_CLASS_NOTFOUND              ";
+        case CS_E_INVALID_VERSION             :
+            return "CS_E_INVALID_VERSION             ";
+        case CS_E_NO_CLASSSTORE               :
+            return "CS_E_NO_CLASSSTORE               ";
+        case CACHE_E_NOCACHE_UPDATED          :
+            return "CACHE_E_NOCACHE_UPDATED          ";
+        case OLEOBJ_E_NOVERBS                 :
+            return "OLEOBJ_E_NOVERBS                 ";
+        case OLEOBJ_E_INVALIDVERB             :
+            return "OLEOBJ_E_INVALIDVERB             ";
+        case INPLACE_E_NOTUNDOABLE            :
+            return "INPLACE_E_NOTUNDOABLE            ";
+        case INPLACE_E_NOTOOLSPACE            :
+            return "INPLACE_E_NOTOOLSPACE            ";
+        case CONVERT10_E_OLESTREAM_GET        :
+            return "CONVERT10_E_OLESTREAM_GET        ";
+        case CONVERT10_E_OLESTREAM_PUT        :
+            return "CONVERT10_E_OLESTREAM_PUT        ";
+        case CONVERT10_E_OLESTREAM_FMT        :
+            return "CONVERT10_E_OLESTREAM_FMT        ";
+        case CONVERT10_E_OLESTREAM_BITMAP_TO_DIB :
+            return "CONVERT10_E_OLESTREAM_BITMAP_TO_DIB ";
+        case CONVERT10_E_STG_FMT              :
+            return "CONVERT10_E_STG_FMT              ";
+        case CONVERT10_E_STG_NO_STD_STREAM    :
+            return "CONVERT10_E_STG_NO_STD_STREAM    ";
+        case CONVERT10_E_STG_DIB_TO_BITMAP    :
+            return "CONVERT10_E_STG_DIB_TO_BITMAP    ";
+        case CLIPBRD_E_CANT_OPEN              :
+            return "CLIPBRD_E_CANT_OPEN              ";
+        case CLIPBRD_E_CANT_EMPTY             :
+            return "CLIPBRD_E_CANT_EMPTY             ";
+        case CLIPBRD_E_CANT_SET               :
+            return "CLIPBRD_E_CANT_SET               ";
+        case CLIPBRD_E_BAD_DATA               :
+            return "CLIPBRD_E_BAD_DATA               ";
+        case CLIPBRD_E_CANT_CLOSE             :
+            return "CLIPBRD_E_CANT_CLOSE             ";
+        case MK_E_CONNECTMANUALLY             :
+            return "MK_E_CONNECTMANUALLY             ";
+        case MK_E_EXCEEDEDDEADLINE            :
+            return "MK_E_EXCEEDEDDEADLINE            ";
+        case MK_E_NEEDGENERIC                 :
+            return "MK_E_NEEDGENERIC                 ";
+        case MK_E_UNAVAILABLE                 :
+            return "MK_E_UNAVAILABLE                 ";
+        case MK_E_SYNTAX                      :
+            return "MK_E_SYNTAX                      ";
+        case MK_E_NOOBJECT                    :
+            return "MK_E_NOOBJECT                    ";
+        case MK_E_INVALIDEXTENSION            :
+            return "MK_E_INVALIDEXTENSION            ";
+        case MK_E_INTERMEDIATEINTERFACENOTSUPPORTED :
+            return "MK_E_INTERMEDIATEINTERFACENOTSUPPORTED ";
+        case MK_E_NOTBINDABLE                 :
+            return "MK_E_NOTBINDABLE                 ";
+        case MK_E_NOTBOUND                    :
+            return "MK_E_NOTBOUND                    ";
+        case MK_E_CANTOPENFILE                :
+            return "MK_E_CANTOPENFILE                ";
+        case MK_E_MUSTBOTHERUSER              :
+            return "MK_E_MUSTBOTHERUSER              ";
+        case MK_E_NOINVERSE                   :
+            return "MK_E_NOINVERSE                   ";
+        case MK_E_NOSTORAGE                   :
+            return "MK_E_NOSTORAGE                   ";
+        case MK_E_NOPREFIX                    :
+            return "MK_E_NOPREFIX                    ";
+        case MK_E_ENUMERATION_FAILED          :
+            return "MK_E_ENUMERATION_FAILED          ";
+        case CO_E_NOTINITIALIZED              :
+            return "CO_E_NOTINITIALIZED              ";
+        case CO_E_ALREADYINITIALIZED          :
+            return "CO_E_ALREADYINITIALIZED          ";
+        case CO_E_CANTDETERMINECLASS          :
+            return "CO_E_CANTDETERMINECLASS          ";
+        case CO_E_CLASSSTRING                 :
+            return "CO_E_CLASSSTRING                 ";
+        case CO_E_IIDSTRING                   :
+            return "CO_E_IIDSTRING                   ";
+        case CO_E_APPNOTFOUND                 :
+            return "CO_E_APPNOTFOUND                 ";
+        case CO_E_APPSINGLEUSE                :
+            return "CO_E_APPSINGLEUSE                ";
+        case CO_E_ERRORINAPP                  :
+            return "CO_E_ERRORINAPP                  ";
+        case CO_E_DLLNOTFOUND                 :
+            return "CO_E_DLLNOTFOUND                 ";
+        case CO_E_ERRORINDLL                  :
+            return "CO_E_ERRORINDLL                  ";
+        case CO_E_WRONGOSFORAPP               :
+            return "CO_E_WRONGOSFORAPP               ";
+        case CO_E_OBJNOTREG                   :
+            return "CO_E_OBJNOTREG                   ";
+        case CO_E_OBJISREG                    :
+            return "CO_E_OBJISREG                    ";
+        case CO_E_OBJNOTCONNECTED             :
+            return "CO_E_OBJNOTCONNECTED             ";
+        case CO_E_APPDIDNTREG                 :
+            return "CO_E_APPDIDNTREG                 ";
+        case CO_E_RELEASED                    :
+            return "CO_E_RELEASED                    ";
+        case CO_E_FAILEDTOIMPERSONATE         :
+            return "CO_E_FAILEDTOIMPERSONATE         ";
+        case CO_E_FAILEDTOGETSECCTX           :
+            return "CO_E_FAILEDTOGETSECCTX           ";
+        case CO_E_FAILEDTOOPENTHREADTOKEN     :
+            return "CO_E_FAILEDTOOPENTHREADTOKEN     ";
+        case CO_E_FAILEDTOGETTOKENINFO        :
+            return "CO_E_FAILEDTOGETTOKENINFO        ";
+        case CO_E_TRUSTEEDOESNTMATCHCLIENT    :
+            return "CO_E_TRUSTEEDOESNTMATCHCLIENT    ";
+        case CO_E_FAILEDTOQUERYCLIENTBLANKET  :
+            return "CO_E_FAILEDTOQUERYCLIENTBLANKET  ";
+        case CO_E_FAILEDTOSETDACL             :
+            return "CO_E_FAILEDTOSETDACL             ";
+        case CO_E_ACCESSCHECKFAILED           :
+            return "CO_E_ACCESSCHECKFAILED           ";
+        case CO_E_NETACCESSAPIFAILED          :
+            return "CO_E_NETACCESSAPIFAILED          ";
+        case CO_E_WRONGTRUSTEENAMESYNTAX      :
+            return "CO_E_WRONGTRUSTEENAMESYNTAX      ";
+        case CO_E_INVALIDSID                  :
+            return "CO_E_INVALIDSID                  ";
+        case CO_E_CONVERSIONFAILED            :
+            return "CO_E_CONVERSIONFAILED            ";
+        case CO_E_NOMATCHINGSIDFOUND          :
+            return "CO_E_NOMATCHINGSIDFOUND          ";
+        case CO_E_LOOKUPACCSIDFAILED          :
+            return "CO_E_LOOKUPACCSIDFAILED          ";
+        case CO_E_NOMATCHINGNAMEFOUND         :
+            return "CO_E_NOMATCHINGNAMEFOUND         ";
+        case CO_E_LOOKUPACCNAMEFAILED         :
+            return "CO_E_LOOKUPACCNAMEFAILED         ";
+        case CO_E_SETSERLHNDLFAILED           :
+            return "CO_E_SETSERLHNDLFAILED           ";
+        case CO_E_FAILEDTOGETWINDIR           :
+            return "CO_E_FAILEDTOGETWINDIR           ";
+        case CO_E_PATHTOOLONG                 :
+            return "CO_E_PATHTOOLONG                 ";
+        case CO_E_FAILEDTOGENUUID             :
+            return "CO_E_FAILEDTOGENUUID             ";
+        case CO_E_FAILEDTOCREATEFILE          :
+            return "CO_E_FAILEDTOCREATEFILE          ";
+        case CO_E_FAILEDTOCLOSEHANDLE         :
+            return "CO_E_FAILEDTOCLOSEHANDLE         ";
+        case CO_E_EXCEEDSYSACLLIMIT           :
+            return "CO_E_EXCEEDSYSACLLIMIT           ";
+        case CO_E_ACESINWRONGORDER            :
+            return "CO_E_ACESINWRONGORDER            ";
+        case CO_E_INCOMPATIBLESTREAMVERSION   :
+            return "CO_E_INCOMPATIBLESTREAMVERSION   ";
+        case CO_E_FAILEDTOOPENPROCESSTOKEN    :
+            return "CO_E_FAILEDTOOPENPROCESSTOKEN    ";
+        case CO_E_DECODEFAILED                :
+            return "CO_E_DECODEFAILED                ";
+        case CO_E_ACNOTINITIALIZED            :
+            return "CO_E_ACNOTINITIALIZED            ";
+        case OLE_S_USEREG                     :
+            return "OLE_S_USEREG                     ";
+        case OLE_S_STATIC                     :
+            return "OLE_S_STATIC                     ";
+        case OLE_S_MAC_CLIPFORMAT             :
+            return "OLE_S_MAC_CLIPFORMAT             ";
+        case DRAGDROP_S_DROP                  :
+            return "DRAGDROP_S_DROP                  ";
+        case DRAGDROP_S_CANCEL                :
+            return "DRAGDROP_S_CANCEL                ";
+        case DRAGDROP_S_USEDEFAULTCURSORS     :
+            return "DRAGDROP_S_USEDEFAULTCURSORS     ";
+        case DATA_S_SAMEFORMATETC             :
+            return "DATA_S_SAMEFORMATETC             ";
+        case VIEW_S_ALREADY_FROZEN            :
+            return "VIEW_S_ALREADY_FROZEN            ";
+        case CACHE_S_FORMATETC_NOTSUPPORTED   :
+            return "CACHE_S_FORMATETC_NOTSUPPORTED   ";
+        case CACHE_S_SAMECACHE                :
+            return "CACHE_S_SAMECACHE                ";
+        case CACHE_S_SOMECACHES_NOTUPDATED    :
+            return "CACHE_S_SOMECACHES_NOTUPDATED    ";
+        case OLEOBJ_S_INVALIDVERB             :
+            return "OLEOBJ_S_INVALIDVERB             ";
+        case OLEOBJ_S_CANNOT_DOVERB_NOW       :
+            return "OLEOBJ_S_CANNOT_DOVERB_NOW       ";
+        case OLEOBJ_S_INVALIDHWND             :
+            return "OLEOBJ_S_INVALIDHWND             ";
+        case INPLACE_S_TRUNCATED              :
+            return "INPLACE_S_TRUNCATED              ";
+        case CONVERT10_S_NO_PRESENTATION      :
+            return "CONVERT10_S_NO_PRESENTATION      ";
+        case MK_S_REDUCED_TO_SELF             :
+            return "MK_S_REDUCED_TO_SELF             ";
+        case MK_S_ME                          :
+            return "MK_S_ME                          ";
+        case MK_S_HIM                         :
+            return "MK_S_HIM                         ";
+        case MK_S_US                          :
+            return "MK_S_US                          ";
+        case MK_S_MONIKERALREADYREGISTERED    :
+            return "MK_S_MONIKERALREADYREGISTERED    ";
+        case CO_E_CLASS_CREATE_FAILED         :
+            return "CO_E_CLASS_CREATE_FAILED         ";
+        case CO_E_SCM_ERROR                   :
+            return "CO_E_SCM_ERROR                   ";
+        case CO_E_SCM_RPC_FAILURE             :
+            return "CO_E_SCM_RPC_FAILURE             ";
+        case CO_E_BAD_PATH                    :
+            return "CO_E_BAD_PATH                    ";
+        case CO_E_SERVER_EXEC_FAILURE         :
+            return "CO_E_SERVER_EXEC_FAILURE         ";
+        case CO_E_OBJSRV_RPC_FAILURE          :
+            return "CO_E_OBJSRV_RPC_FAILURE          ";
+        case MK_E_NO_NORMALIZED               :
+            return "MK_E_NO_NORMALIZED               ";
+        case CO_E_SERVER_STOPPING             :
+            return "CO_E_SERVER_STOPPING             ";
+        case MEM_E_INVALID_ROOT               :
+            return "MEM_E_INVALID_ROOT               ";
+        case MEM_E_INVALID_LINK               :
+            return "MEM_E_INVALID_LINK               ";
+        case MEM_E_INVALID_SIZE               :
+            return "MEM_E_INVALID_SIZE               ";
+        case CO_S_NOTALLINTERFACES            :
+            return "CO_S_NOTALLINTERFACES            ";
+        case DISP_E_UNKNOWNINTERFACE          :
+            return "DISP_E_UNKNOWNINTERFACE          ";
+        case DISP_E_MEMBERNOTFOUND            :
+            return "DISP_E_MEMBERNOTFOUND            ";
+        case DISP_E_PARAMNOTFOUND             :
+            return "DISP_E_PARAMNOTFOUND             ";
+        case DISP_E_TYPEMISMATCH              :
+            return "DISP_E_TYPEMISMATCH              ";
+        case DISP_E_UNKNOWNNAME               :
+            return "DISP_E_UNKNOWNNAME               ";
+        case DISP_E_NONAMEDARGS               :
+            return "DISP_E_NONAMEDARGS               ";
+        case DISP_E_BADVARTYPE                :
+            return "DISP_E_BADVARTYPE                ";
+        case DISP_E_EXCEPTION                 :
+            return "DISP_E_EXCEPTION                 ";
+        case DISP_E_OVERFLOW                  :
+            return "DISP_E_OVERFLOW                  ";
+        case DISP_E_BADINDEX                  :
+            return "DISP_E_BADINDEX                  ";
+        case DISP_E_UNKNOWNLCID               :
+            return "DISP_E_UNKNOWNLCID               ";
+        case DISP_E_ARRAYISLOCKED             :
+            return "DISP_E_ARRAYISLOCKED             ";
+        case DISP_E_BADPARAMCOUNT             :
+            return "DISP_E_BADPARAMCOUNT             ";
+        case DISP_E_PARAMNOTOPTIONAL          :
+            return "DISP_E_PARAMNOTOPTIONAL          ";
+        case DISP_E_BADCALLEE                 :
+            return "DISP_E_BADCALLEE                 ";
+        case DISP_E_NOTACOLLECTION            :
+            return "DISP_E_NOTACOLLECTION            ";
+        case DISP_E_DIVBYZERO                 :
+            return "DISP_E_DIVBYZERO                 ";
+        case TYPE_E_BUFFERTOOSMALL            :
+            return "TYPE_E_BUFFERTOOSMALL            ";
+        case TYPE_E_FIELDNOTFOUND             :
+            return "TYPE_E_FIELDNOTFOUND             ";
+        case TYPE_E_INVDATAREAD               :
+            return "TYPE_E_INVDATAREAD               ";
+        case TYPE_E_UNSUPFORMAT               :
+            return "TYPE_E_UNSUPFORMAT               ";
+        case TYPE_E_REGISTRYACCESS            :
+            return "TYPE_E_REGISTRYACCESS            ";
+        case TYPE_E_LIBNOTREGISTERED          :
+            return "TYPE_E_LIBNOTREGISTERED          ";
+        case TYPE_E_UNDEFINEDTYPE             :
+            return "TYPE_E_UNDEFINEDTYPE             ";
+        case TYPE_E_QUALIFIEDNAMEDISALLOWED   :
+            return "TYPE_E_QUALIFIEDNAMEDISALLOWED   ";
+        case TYPE_E_INVALIDSTATE              :
+            return "TYPE_E_INVALIDSTATE              ";
+        case TYPE_E_WRONGTYPEKIND             :
+            return "TYPE_E_WRONGTYPEKIND             ";
+        case TYPE_E_ELEMENTNOTFOUND           :
+            return "TYPE_E_ELEMENTNOTFOUND           ";
+        case TYPE_E_AMBIGUOUSNAME             :
+            return "TYPE_E_AMBIGUOUSNAME             ";
+        case TYPE_E_NAMECONFLICT              :
+            return "TYPE_E_NAMECONFLICT              ";
+        case TYPE_E_UNKNOWNLCID               :
+            return "TYPE_E_UNKNOWNLCID               ";
+        case TYPE_E_DLLFUNCTIONNOTFOUND       :
+            return "TYPE_E_DLLFUNCTIONNOTFOUND       ";
+        case TYPE_E_BADMODULEKIND             :
+            return "TYPE_E_BADMODULEKIND             ";
+        case TYPE_E_SIZETOOBIG                :
+            return "TYPE_E_SIZETOOBIG                ";
+        case TYPE_E_DUPLICATEID               :
+            return "TYPE_E_DUPLICATEID               ";
+        case TYPE_E_INVALIDID                 :
+            return "TYPE_E_INVALIDID                 ";
+        case TYPE_E_TYPEMISMATCH              :
+            return "TYPE_E_TYPEMISMATCH              ";
+        case TYPE_E_OUTOFBOUNDS               :
+            return "TYPE_E_OUTOFBOUNDS               ";
+        case TYPE_E_IOERROR                   :
+            return "TYPE_E_IOERROR                   ";
+        case TYPE_E_CANTCREATETMPFILE         :
+            return "TYPE_E_CANTCREATETMPFILE         ";
+        case TYPE_E_CANTLOADLIBRARY           :
+            return "TYPE_E_CANTLOADLIBRARY           ";
+        case TYPE_E_INCONSISTENTPROPFUNCS     :
+            return "TYPE_E_INCONSISTENTPROPFUNCS     ";
+        case TYPE_E_CIRCULARTYPE              :
+            return "TYPE_E_CIRCULARTYPE              ";
+        case STG_E_INVALIDFUNCTION            :
+            return "STG_E_INVALIDFUNCTION            ";
+        case STG_E_FILENOTFOUND               :
+            return "STG_E_FILENOTFOUND               ";
+        case STG_E_PATHNOTFOUND               :
+            return "STG_E_PATHNOTFOUND               ";
+        case STG_E_TOOMANYOPENFILES           :
+            return "STG_E_TOOMANYOPENFILES           ";
+        case STG_E_ACCESSDENIED               :
+            return "STG_E_ACCESSDENIED               ";
+        case STG_E_INVALIDHANDLE              :
+            return "STG_E_INVALIDHANDLE              ";
+        case STG_E_INSUFFICIENTMEMORY         :
+            return "STG_E_INSUFFICIENTMEMORY         ";
+        case STG_E_INVALIDPOINTER             :
+            return "STG_E_INVALIDPOINTER             ";
+        case STG_E_NOMOREFILES                :
+            return "STG_E_NOMOREFILES                ";
+        case STG_E_DISKISWRITEPROTECTED       :
+            return "STG_E_DISKISWRITEPROTECTED       ";
+        case STG_E_SEEKERROR                  :
+            return "STG_E_SEEKERROR                  ";
+        case STG_E_WRITEFAULT                 :
+            return "STG_E_WRITEFAULT                 ";
+        case STG_E_READFAULT                  :
+            return "STG_E_READFAULT                  ";
+        case STG_E_SHAREVIOLATION             :
+            return "STG_E_SHAREVIOLATION             ";
+        case STG_E_LOCKVIOLATION              :
+            return "STG_E_LOCKVIOLATION              ";
+        case STG_E_FILEALREADYEXISTS          :
+            return "STG_E_FILEALREADYEXISTS          ";
+        case STG_E_INVALIDPARAMETER           :
+            return "STG_E_INVALIDPARAMETER           ";
+        case STG_E_MEDIUMFULL                 :
+            return "STG_E_MEDIUMFULL                 ";
+        case STG_E_PROPSETMISMATCHED          :
+            return "STG_E_PROPSETMISMATCHED          ";
+        case STG_E_ABNORMALAPIEXIT            :
+            return "STG_E_ABNORMALAPIEXIT            ";
+        case STG_E_INVALIDHEADER              :
+            return "STG_E_INVALIDHEADER              ";
+        case STG_E_INVALIDNAME                :
+            return "STG_E_INVALIDNAME                ";
+        case STG_E_UNKNOWN                    :
+            return "STG_E_UNKNOWN                    ";
+        case STG_E_UNIMPLEMENTEDFUNCTION      :
+            return "STG_E_UNIMPLEMENTEDFUNCTION      ";
+        case STG_E_INVALIDFLAG                :
+            return "STG_E_INVALIDFLAG                ";
+        case STG_E_INUSE                      :
+            return "STG_E_INUSE                      ";
+        case STG_E_NOTCURRENT                 :
+            return "STG_E_NOTCURRENT                 ";
+        case STG_E_REVERTED                   :
+            return "STG_E_REVERTED                   ";
+        case STG_E_CANTSAVE                   :
+            return "STG_E_CANTSAVE                   ";
+        case STG_E_OLDFORMAT                  :
+            return "STG_E_OLDFORMAT                  ";
+        case STG_E_OLDDLL                     :
+            return "STG_E_OLDDLL                     ";
+        case STG_E_SHAREREQUIRED              :
+            return "STG_E_SHAREREQUIRED              ";
+        case STG_E_NOTFILEBASEDSTORAGE        :
+            return "STG_E_NOTFILEBASEDSTORAGE        ";
+        case STG_E_EXTANTMARSHALLINGS         :
+            return "STG_E_EXTANTMARSHALLINGS         ";
+        case STG_E_DOCFILECORRUPT             :
+            return "STG_E_DOCFILECORRUPT             ";
+        case STG_E_BADBASEADDRESS             :
+            return "STG_E_BADBASEADDRESS             ";
+        case STG_E_INCOMPLETE                 :
+            return "STG_E_INCOMPLETE                 ";
+        case STG_E_TERMINATED                 :
+            return "STG_E_TERMINATED                 ";
+        case STG_S_CONVERTED                  :
+            return "STG_S_CONVERTED                  ";
+        case STG_S_BLOCK                      :
+            return "STG_S_BLOCK                      ";
+        case STG_S_RETRYNOW                   :
+            return "STG_S_RETRYNOW                   ";
+        case STG_S_MONITORING                 :
+            return "STG_S_MONITORING                 ";
+        case STG_S_MULTIPLEOPENS              :
+            return "STG_S_MULTIPLEOPENS              ";
+        case STG_S_CONSOLIDATIONFAILED        :
+            return "STG_S_CONSOLIDATIONFAILED        ";
+        case STG_S_CANNOTCONSOLIDATE          :
+            return "STG_S_CANNOTCONSOLIDATE          ";
+        case RPC_E_CALL_REJECTED              :
+            return "RPC_E_CALL_REJECTED              ";
+        case RPC_E_CALL_CANCELED              :
+            return "RPC_E_CALL_CANCELED              ";
+        case RPC_E_CANTPOST_INSENDCALL        :
+            return "RPC_E_CANTPOST_INSENDCALL        ";
+        case RPC_E_CANTCALLOUT_INASYNCCALL    :
+            return "RPC_E_CANTCALLOUT_INASYNCCALL    ";
+        case RPC_E_CANTCALLOUT_INEXTERNALCALL :
+            return "RPC_E_CANTCALLOUT_INEXTERNALCALL ";
+        case RPC_E_CONNECTION_TERMINATED      :
+            return "RPC_E_CONNECTION_TERMINATED      ";
+        case RPC_E_SERVER_DIED                :
+            return "RPC_E_SERVER_DIED                ";
+        case RPC_E_CLIENT_DIED                :
+            return "RPC_E_CLIENT_DIED                ";
+        case RPC_E_INVALID_DATAPACKET         :
+            return "RPC_E_INVALID_DATAPACKET         ";
+        case RPC_E_CANTTRANSMIT_CALL          :
+            return "RPC_E_CANTTRANSMIT_CALL          ";
+        case RPC_E_CLIENT_CANTMARSHAL_DATA    :
+            return "RPC_E_CLIENT_CANTMARSHAL_DATA    ";
+        case RPC_E_CLIENT_CANTUNMARSHAL_DATA  :
+            return "RPC_E_CLIENT_CANTUNMARSHAL_DATA  ";
+        case RPC_E_SERVER_CANTMARSHAL_DATA    :
+            return "RPC_E_SERVER_CANTMARSHAL_DATA    ";
+        case RPC_E_SERVER_CANTUNMARSHAL_DATA  :
+            return "RPC_E_SERVER_CANTUNMARSHAL_DATA  ";
+        case RPC_E_INVALID_DATA               :
+            return "RPC_E_INVALID_DATA               ";
+        case RPC_E_INVALID_PARAMETER          :
+            return "RPC_E_INVALID_PARAMETER          ";
+        case RPC_E_CANTCALLOUT_AGAIN          :
+            return "RPC_E_CANTCALLOUT_AGAIN          ";
+        case RPC_E_SERVER_DIED_DNE            :
+            return "RPC_E_SERVER_DIED_DNE            ";
+        case RPC_E_SYS_CALL_FAILED            :
+            return "RPC_E_SYS_CALL_FAILED            ";
+        case RPC_E_OUT_OF_RESOURCES           :
+            return "RPC_E_OUT_OF_RESOURCES           ";
+        case RPC_E_ATTEMPTED_MULTITHREAD      :
+            return "RPC_E_ATTEMPTED_MULTITHREAD      ";
+        case RPC_E_NOT_REGISTERED             :
+            return "RPC_E_NOT_REGISTERED             ";
+        case RPC_E_FAULT                      :
+            return "RPC_E_FAULT                      ";
+        case RPC_E_SERVERFAULT                :
+            return "RPC_E_SERVERFAULT                ";
+        case RPC_E_CHANGED_MODE               :
+            return "RPC_E_CHANGED_MODE               ";
+        case RPC_E_INVALIDMETHOD              :
+            return "RPC_E_INVALIDMETHOD              ";
+        case RPC_E_DISCONNECTED               :
+            return "RPC_E_DISCONNECTED               ";
+        case RPC_E_RETRY                      :
+            return "RPC_E_RETRY                      ";
+        case RPC_E_SERVERCALL_RETRYLATER      :
+            return "RPC_E_SERVERCALL_RETRYLATER      ";
+        case RPC_E_SERVERCALL_REJECTED        :
+            return "RPC_E_SERVERCALL_REJECTED        ";
+        case RPC_E_INVALID_CALLDATA           :
+            return "RPC_E_INVALID_CALLDATA           ";
+        case RPC_E_CANTCALLOUT_ININPUTSYNCCALL :
+            return "RPC_E_CANTCALLOUT_ININPUTSYNCCALL ";
+        case RPC_E_WRONG_THREAD               :
+            return "RPC_E_WRONG_THREAD               ";
+        case RPC_E_THREAD_NOT_INIT            :
+            return "RPC_E_THREAD_NOT_INIT            ";
+        case RPC_E_VERSION_MISMATCH           :
+            return "RPC_E_VERSION_MISMATCH           ";
+        case RPC_E_INVALID_HEADER             :
+            return "RPC_E_INVALID_HEADER             ";
+        case RPC_E_INVALID_EXTENSION          :
+            return "RPC_E_INVALID_EXTENSION          ";
+        case RPC_E_INVALID_IPID               :
+            return "RPC_E_INVALID_IPID               ";
+        case RPC_E_INVALID_OBJECT             :
+            return "RPC_E_INVALID_OBJECT             ";
+        case RPC_S_CALLPENDING                :
+            return "RPC_S_CALLPENDING                ";
+        case RPC_S_WAITONTIMER                :
+            return "RPC_S_WAITONTIMER                ";
+        case RPC_E_CALL_COMPLETE              :
+            return "RPC_E_CALL_COMPLETE              ";
+        case RPC_E_UNSECURE_CALL              :
+            return "RPC_E_UNSECURE_CALL              ";
+        case RPC_E_TOO_LATE                   :
+            return "RPC_E_TOO_LATE                   ";
+        case RPC_E_NO_GOOD_SECURITY_PACKAGES  :
+            return "RPC_E_NO_GOOD_SECURITY_PACKAGES  ";
+        case RPC_E_ACCESS_DENIED              :
+            return "RPC_E_ACCESS_DENIED              ";
+        case RPC_E_REMOTE_DISABLED            :
+            return "RPC_E_REMOTE_DISABLED            ";
+        case RPC_E_INVALID_OBJREF             :
+            return "RPC_E_INVALID_OBJREF             ";
+        case RPC_E_NO_CONTEXT                 :
+            return "RPC_E_NO_CONTEXT                 ";
+        case RPC_E_TIMEOUT                    :
+            return "RPC_E_TIMEOUT                    ";
+        case RPC_E_NO_SYNC                    :
+            return "RPC_E_NO_SYNC                    ";
+        case RPC_E_UNEXPECTED                 :
+            return "RPC_E_UNEXPECTED                 ";
+        case NTE_BAD_UID                      :
+            return "NTE_BAD_UID                      ";
+        case NTE_BAD_HASH                     :
+            return "NTE_BAD_HASH                     ";
+    //case NTE_BAD_HASH                     :
+    //return "NTE_BAD_HASH                     ";
+        case NTE_BAD_KEY                      :
+            return "NTE_BAD_KEY                      ";
+        case NTE_BAD_LEN                      :
+            return "NTE_BAD_LEN                      ";
+        case NTE_BAD_DATA                     :
+            return "NTE_BAD_DATA                     ";
+        case NTE_BAD_SIGNATURE                :
+            return "NTE_BAD_SIGNATURE                ";
+        case NTE_BAD_VER                      :
+            return "NTE_BAD_VER                      ";
+        case NTE_BAD_ALGID                    :
+            return "NTE_BAD_ALGID                    ";
+        case NTE_BAD_FLAGS                    :
+            return "NTE_BAD_FLAGS                    ";
+        case NTE_BAD_TYPE                     :
+            return "NTE_BAD_TYPE                     ";
+        case NTE_BAD_KEY_STATE                :
+            return "NTE_BAD_KEY_STATE                ";
+        case NTE_BAD_HASH_STATE               :
+            return "NTE_BAD_HASH_STATE               ";
+        case NTE_NO_KEY                       :
+            return "NTE_NO_KEY                       ";
+        case NTE_NO_MEMORY                    :
+            return "NTE_NO_MEMORY                    ";
+        case NTE_EXISTS                       :
+            return "NTE_EXISTS                       ";
+        case NTE_PERM                         :
+            return "NTE_PERM                         ";
+        case NTE_NOT_FOUND                    :
+            return "NTE_NOT_FOUND                    ";
+        case NTE_DOUBLE_ENCRYPT               :
+            return "NTE_DOUBLE_ENCRYPT               ";
+        case NTE_BAD_PROVIDER                 :
+            return "NTE_BAD_PROVIDER                 ";
+        case NTE_BAD_PROV_TYPE                :
+            return "NTE_BAD_PROV_TYPE                ";
+        case NTE_BAD_PUBLIC_KEY               :
+            return "NTE_BAD_PUBLIC_KEY               ";
+        case NTE_BAD_KEYSET                   :
+            return "NTE_BAD_KEYSET                   ";
+        case NTE_PROV_TYPE_NOT_DEF            :
+            return "NTE_PROV_TYPE_NOT_DEF            ";
+        case NTE_PROV_TYPE_ENTRY_BAD          :
+            return "NTE_PROV_TYPE_ENTRY_BAD          ";
+        case NTE_KEYSET_NOT_DEF               :
+            return "NTE_KEYSET_NOT_DEF               ";
+        case NTE_KEYSET_ENTRY_BAD             :
+            return "NTE_KEYSET_ENTRY_BAD             ";
+        case NTE_PROV_TYPE_NO_MATCH           :
+            return "NTE_PROV_TYPE_NO_MATCH           ";
+        case NTE_SIGNATURE_FILE_BAD           :
+            return "NTE_SIGNATURE_FILE_BAD           ";
+        case NTE_PROVIDER_DLL_FAIL            :
+            return "NTE_PROVIDER_DLL_FAIL            ";
+        case NTE_PROV_DLL_NOT_FOUND           :
+            return "NTE_PROV_DLL_NOT_FOUND           ";
+        case NTE_BAD_KEYSET_PARAM             :
+            return "NTE_BAD_KEYSET_PARAM             ";
+        case NTE_FAIL                         :
+            return "NTE_FAIL                         ";
+        case NTE_SYS_ERR                      :
+            return "NTE_SYS_ERR                      ";
+        case CRYPT_E_MSG_ERROR                :
+            return "CRYPT_E_MSG_ERROR                ";
+        case CRYPT_E_UNKNOWN_ALGO             :
+            return "CRYPT_E_UNKNOWN_ALGO             ";
+        case CRYPT_E_OID_FORMAT               :
+            return "CRYPT_E_OID_FORMAT               ";
+        case CRYPT_E_INVALID_MSG_TYPE         :
+            return "CRYPT_E_INVALID_MSG_TYPE         ";
+        case CRYPT_E_UNEXPECTED_ENCODING      :
+            return "CRYPT_E_UNEXPECTED_ENCODING      ";
+        case CRYPT_E_AUTH_ATTR_MISSING        :
+            return "CRYPT_E_AUTH_ATTR_MISSING        ";
+        case CRYPT_E_HASH_VALUE               :
+            return "CRYPT_E_HASH_VALUE               ";
+        case CRYPT_E_INVALID_INDEX            :
+            return "CRYPT_E_INVALID_INDEX            ";
+        case CRYPT_E_ALREADY_DECRYPTED        :
+            return "CRYPT_E_ALREADY_DECRYPTED        ";
+        case CRYPT_E_NOT_DECRYPTED            :
+            return "CRYPT_E_NOT_DECRYPTED            ";
+        case CRYPT_E_RECIPIENT_NOT_FOUND      :
+            return "CRYPT_E_RECIPIENT_NOT_FOUND      ";
+        case CRYPT_E_CONTROL_TYPE             :
+            return "CRYPT_E_CONTROL_TYPE             ";
+        case CRYPT_E_ISSUER_SERIALNUMBER      :
+            return "CRYPT_E_ISSUER_SERIALNUMBER      ";
+        case CRYPT_E_SIGNER_NOT_FOUND         :
+            return "CRYPT_E_SIGNER_NOT_FOUND         ";
+        case CRYPT_E_ATTRIBUTES_MISSING       :
+            return "CRYPT_E_ATTRIBUTES_MISSING       ";
+        case CRYPT_E_STREAM_MSG_NOT_READY     :
+            return "CRYPT_E_STREAM_MSG_NOT_READY     ";
+        case CRYPT_E_STREAM_INSUFFICIENT_DATA :
+            return "CRYPT_E_STREAM_INSUFFICIENT_DATA ";
+        case CRYPT_E_BAD_LEN                  :
+            return "CRYPT_E_BAD_LEN                  ";
+        case CRYPT_E_BAD_ENCODE               :
+            return "CRYPT_E_BAD_ENCODE               ";
+        case CRYPT_E_FILE_ERROR               :
+            return "CRYPT_E_FILE_ERROR               ";
+        case CRYPT_E_NOT_FOUND                :
+            return "CRYPT_E_NOT_FOUND                ";
+        case CRYPT_E_EXISTS                   :
+            return "CRYPT_E_EXISTS                   ";
+        case CRYPT_E_NO_PROVIDER              :
+            return "CRYPT_E_NO_PROVIDER              ";
+        case CRYPT_E_SELF_SIGNED              :
+            return "CRYPT_E_SELF_SIGNED              ";
+        case CRYPT_E_DELETED_PREV             :
+            return "CRYPT_E_DELETED_PREV             ";
+        case CRYPT_E_NO_MATCH                 :
+            return "CRYPT_E_NO_MATCH                 ";
+        case CRYPT_E_UNEXPECTED_MSG_TYPE      :
+            return "CRYPT_E_UNEXPECTED_MSG_TYPE      ";
+        case CRYPT_E_NO_KEY_PROPERTY          :
+            return "CRYPT_E_NO_KEY_PROPERTY          ";
+        case CRYPT_E_NO_DECRYPT_CERT          :
+            return "CRYPT_E_NO_DECRYPT_CERT          ";
+        case CRYPT_E_BAD_MSG                  :
+            return "CRYPT_E_BAD_MSG                  ";
+        case CRYPT_E_NO_SIGNER                :
+            return "CRYPT_E_NO_SIGNER                ";
+        case CRYPT_E_PENDING_CLOSE            :
+            return "CRYPT_E_PENDING_CLOSE            ";
+        case CRYPT_E_REVOKED                  :
+            return "CRYPT_E_REVOKED                  ";
+        case CRYPT_E_NO_REVOCATION_DLL        :
+            return "CRYPT_E_NO_REVOCATION_DLL        ";
+        case CRYPT_E_NO_REVOCATION_CHECK      :
+            return "CRYPT_E_NO_REVOCATION_CHECK      ";
+        case CRYPT_E_REVOCATION_OFFLINE       :
+            return "CRYPT_E_REVOCATION_OFFLINE       ";
+        case CRYPT_E_NOT_IN_REVOCATION_DATABASE :
+            return "CRYPT_E_NOT_IN_REVOCATION_DATABASE ";
+        case CRYPT_E_INVALID_NUMERIC_STRING   :
+            return "CRYPT_E_INVALID_NUMERIC_STRING   ";
+        case CRYPT_E_INVALID_PRINTABLE_STRING :
+            return "CRYPT_E_INVALID_PRINTABLE_STRING ";
+        case CRYPT_E_INVALID_IA5_STRING       :
+            return "CRYPT_E_INVALID_IA5_STRING       ";
+        case CRYPT_E_INVALID_X500_STRING      :
+            return "CRYPT_E_INVALID_X500_STRING      ";
+        case CRYPT_E_NOT_CHAR_STRING          :
+            return "CRYPT_E_NOT_CHAR_STRING          ";
+        case CRYPT_E_FILERESIZED              :
+            return "CRYPT_E_FILERESIZED              ";
+        case CRYPT_E_SECURITY_SETTINGS        :
+            return "CRYPT_E_SECURITY_SETTINGS        ";
+        case CRYPT_E_NO_VERIFY_USAGE_DLL      :
+            return "CRYPT_E_NO_VERIFY_USAGE_DLL      ";
+        case CRYPT_E_NO_VERIFY_USAGE_CHECK    :
+            return "CRYPT_E_NO_VERIFY_USAGE_CHECK    ";
+        case CRYPT_E_VERIFY_USAGE_OFFLINE     :
+            return "CRYPT_E_VERIFY_USAGE_OFFLINE     ";
+        case CRYPT_E_NOT_IN_CTL               :
+            return "CRYPT_E_NOT_IN_CTL               ";
+        case CRYPT_E_NO_TRUSTED_SIGNER        :
+            return "CRYPT_E_NO_TRUSTED_SIGNER        ";
+        case CRYPT_E_OSS_ERROR                :
+            return "CRYPT_E_OSS_ERROR                ";
+        case CERTSRV_E_BAD_REQUESTSUBJECT     :
+            return "CERTSRV_E_BAD_REQUESTSUBJECT     ";
+        case CERTSRV_E_NO_REQUEST             :
+            return "CERTSRV_E_NO_REQUEST             ";
+        case CERTSRV_E_BAD_REQUESTSTATUS      :
+            return "CERTSRV_E_BAD_REQUESTSTATUS      ";
+        case CERTSRV_E_PROPERTY_EMPTY         :
+            return "CERTSRV_E_PROPERTY_EMPTY         ";
+    //case CERTDB_E_JET_ERROR               :
+    //return "CERTDB_E_JET_ERROR               ";
+        case TRUST_E_SYSTEM_ERROR             :
+            return "TRUST_E_SYSTEM_ERROR             ";
+        case TRUST_E_NO_SIGNER_CERT           :
+            return "TRUST_E_NO_SIGNER_CERT           ";
+        case TRUST_E_COUNTER_SIGNER           :
+            return "TRUST_E_COUNTER_SIGNER           ";
+        case TRUST_E_CERT_SIGNATURE           :
+            return "TRUST_E_CERT_SIGNATURE           ";
+        case TRUST_E_TIME_STAMP               :
+            return "TRUST_E_TIME_STAMP               ";
+        case TRUST_E_BAD_DIGEST               :
+            return "TRUST_E_BAD_DIGEST               ";
+        case TRUST_E_BASIC_CONSTRAINTS        :
+            return "TRUST_E_BASIC_CONSTRAINTS        ";
+        case TRUST_E_FINANCIAL_CRITERIA       :
+            return "TRUST_E_FINANCIAL_CRITERIA       ";
+        case TRUST_E_PROVIDER_UNKNOWN         :
+            return "TRUST_E_PROVIDER_UNKNOWN         ";
+        case TRUST_E_ACTION_UNKNOWN           :
+            return "TRUST_E_ACTION_UNKNOWN           ";
+        case TRUST_E_SUBJECT_FORM_UNKNOWN     :
+            return "TRUST_E_SUBJECT_FORM_UNKNOWN     ";
+        case TRUST_E_SUBJECT_NOT_TRUSTED      :
+            return "TRUST_E_SUBJECT_NOT_TRUSTED      ";
+        case DIGSIG_E_ENCODE                  :
+            return "DIGSIG_E_ENCODE                  ";
+        case DIGSIG_E_DECODE                  :
+            return "DIGSIG_E_DECODE                  ";
+        case DIGSIG_E_EXTENSIBILITY           :
+            return "DIGSIG_E_EXTENSIBILITY           ";
+        case DIGSIG_E_CRYPTO                  :
+            return "DIGSIG_E_CRYPTO                  ";
+        case PERSIST_E_SIZEDEFINITE           :
+            return "PERSIST_E_SIZEDEFINITE           ";
+        case PERSIST_E_SIZEINDEFINITE         :
+            return "PERSIST_E_SIZEINDEFINITE         ";
+        case PERSIST_E_NOTSELFSIZING          :
+            return "PERSIST_E_NOTSELFSIZING          ";
+        case TRUST_E_NOSIGNATURE              :
+            return "TRUST_E_NOSIGNATURE              ";
+        case CERT_E_EXPIRED                   :
+            return "CERT_E_EXPIRED                   ";
+        case CERT_E_VALIDITYPERIODNESTING     :
+            return "CERT_E_VALIDITYPERIODNESTING     ";
+        case CERT_E_ROLE                      :
+            return "CERT_E_ROLE                      ";
+        case CERT_E_PATHLENCONST              :
+            return "CERT_E_PATHLENCONST              ";
+        case CERT_E_CRITICAL                  :
+            return "CERT_E_CRITICAL                  ";
+        case CERT_E_PURPOSE                   :
+            return "CERT_E_PURPOSE                   ";
+        case CERT_E_ISSUERCHAINING            :
+            return "CERT_E_ISSUERCHAINING            ";
+        case CERT_E_MALFORMED                 :
+            return "CERT_E_MALFORMED                 ";
+        case CERT_E_UNTRUSTEDROOT             :
+            return "CERT_E_UNTRUSTEDROOT             ";
+        case CERT_E_CHAINING                  :
+            return "CERT_E_CHAINING                  ";
+        case TRUST_E_FAIL                     :
+            return "TRUST_E_FAIL                     ";
+        case CERT_E_REVOKED                   :
+            return "CERT_E_REVOKED                   ";
+        case CERT_E_UNTRUSTEDTESTROOT         :
+            return "CERT_E_UNTRUSTEDTESTROOT         ";
+        case CERT_E_REVOCATION_FAILURE        :
+            return "CERT_E_REVOCATION_FAILURE        ";
+        case CERT_E_CN_NO_MATCH               :
+            return "CERT_E_CN_NO_MATCH               ";
+        case CERT_E_WRONG_USAGE               :
+            return "CERT_E_WRONG_USAGE               ";
+        case SPAPI_E_EXPECTED_SECTION_NAME    :
+            return "SPAPI_E_EXPECTED_SECTION_NAME    ";
+        case SPAPI_E_BAD_SECTION_NAME_LINE    :
+            return "SPAPI_E_BAD_SECTION_NAME_LINE    ";
+        case SPAPI_E_SECTION_NAME_TOO_LONG    :
+            return "SPAPI_E_SECTION_NAME_TOO_LONG    ";
+        case SPAPI_E_GENERAL_SYNTAX           :
+            return "SPAPI_E_GENERAL_SYNTAX           ";
+        case SPAPI_E_WRONG_INF_STYLE          :
+            return "SPAPI_E_WRONG_INF_STYLE          ";
+        case SPAPI_E_SECTION_NOT_FOUND        :
+            return "SPAPI_E_SECTION_NOT_FOUND        ";
+        case SPAPI_E_LINE_NOT_FOUND           :
+            return "SPAPI_E_LINE_NOT_FOUND           ";
+        case SPAPI_E_NO_ASSOCIATED_CLASS      :
+            return "SPAPI_E_NO_ASSOCIATED_CLASS      ";
+        case SPAPI_E_CLASS_MISMATCH           :
+            return "SPAPI_E_CLASS_MISMATCH           ";
+        case SPAPI_E_DUPLICATE_FOUND          :
+            return "SPAPI_E_DUPLICATE_FOUND          ";
+        case SPAPI_E_NO_DRIVER_SELECTED       :
+            return "SPAPI_E_NO_DRIVER_SELECTED       ";
+        case SPAPI_E_KEY_DOES_NOT_EXIST       :
+            return "SPAPI_E_KEY_DOES_NOT_EXIST       ";
+        case SPAPI_E_INVALID_DEVINST_NAME     :
+            return "SPAPI_E_INVALID_DEVINST_NAME     ";
+        case SPAPI_E_INVALID_CLASS            :
+            return "SPAPI_E_INVALID_CLASS            ";
+        case SPAPI_E_DEVINST_ALREADY_EXISTS   :
+            return "SPAPI_E_DEVINST_ALREADY_EXISTS   ";
+        case SPAPI_E_DEVINFO_NOT_REGISTERED   :
+            return "SPAPI_E_DEVINFO_NOT_REGISTERED   ";
+        case SPAPI_E_INVALID_REG_PROPERTY     :
+            return "SPAPI_E_INVALID_REG_PROPERTY     ";
+        case SPAPI_E_NO_INF                   :
+            return "SPAPI_E_NO_INF                   ";
+        case SPAPI_E_NO_SUCH_DEVINST          :
+            return "SPAPI_E_NO_SUCH_DEVINST          ";
+        case SPAPI_E_CANT_LOAD_CLASS_ICON     :
+            return "SPAPI_E_CANT_LOAD_CLASS_ICON     ";
+        case SPAPI_E_INVALID_CLASS_INSTALLER  :
+            return "SPAPI_E_INVALID_CLASS_INSTALLER  ";
+        case SPAPI_E_DI_DO_DEFAULT            :
+            return "SPAPI_E_DI_DO_DEFAULT            ";
+        case SPAPI_E_DI_NOFILECOPY            :
+            return "SPAPI_E_DI_NOFILECOPY            ";
+        case SPAPI_E_INVALID_HWPROFILE        :
+            return "SPAPI_E_INVALID_HWPROFILE        ";
+        case SPAPI_E_NO_DEVICE_SELECTED       :
+            return "SPAPI_E_NO_DEVICE_SELECTED       ";
+        case SPAPI_E_DEVINFO_LIST_LOCKED      :
+            return "SPAPI_E_DEVINFO_LIST_LOCKED      ";
+        case SPAPI_E_DEVINFO_DATA_LOCKED      :
+            return "SPAPI_E_DEVINFO_DATA_LOCKED      ";
+        case SPAPI_E_DI_BAD_PATH              :
+            return "SPAPI_E_DI_BAD_PATH              ";
+        case SPAPI_E_NO_CLASSINSTALL_PARAMS   :
+            return "SPAPI_E_NO_CLASSINSTALL_PARAMS   ";
+        case SPAPI_E_FILEQUEUE_LOCKED         :
+            return "SPAPI_E_FILEQUEUE_LOCKED         ";
+        case SPAPI_E_BAD_SERVICE_INSTALLSECT  :
+            return "SPAPI_E_BAD_SERVICE_INSTALLSECT  ";
+        case SPAPI_E_NO_CLASS_DRIVER_LIST     :
+            return "SPAPI_E_NO_CLASS_DRIVER_LIST     ";
+        case SPAPI_E_NO_ASSOCIATED_SERVICE    :
+            return "SPAPI_E_NO_ASSOCIATED_SERVICE    ";
+        case SPAPI_E_NO_DEFAULT_DEVICE_INTERFACE :
+            return "SPAPI_E_NO_DEFAULT_DEVICE_INTERFACE ";
+        case SPAPI_E_DEVICE_INTERFACE_ACTIVE  :
+            return "SPAPI_E_DEVICE_INTERFACE_ACTIVE  ";
+        case SPAPI_E_DEVICE_INTERFACE_REMOVED :
+            return "SPAPI_E_DEVICE_INTERFACE_REMOVED ";
+        case SPAPI_E_BAD_INTERFACE_INSTALLSECT :
+            return "SPAPI_E_BAD_INTERFACE_INSTALLSECT ";
+        case SPAPI_E_NO_SUCH_INTERFACE_CLASS  :
+            return "SPAPI_E_NO_SUCH_INTERFACE_CLASS  ";
+        case SPAPI_E_INVALID_REFERENCE_STRING :
+            return "SPAPI_E_INVALID_REFERENCE_STRING ";
+        case SPAPI_E_INVALID_MACHINENAME      :
+            return "SPAPI_E_INVALID_MACHINENAME      ";
+        case SPAPI_E_REMOTE_COMM_FAILURE      :
+            return "SPAPI_E_REMOTE_COMM_FAILURE      ";
+        case SPAPI_E_MACHINE_UNAVAILABLE      :
+            return "SPAPI_E_MACHINE_UNAVAILABLE      ";
+        case SPAPI_E_NO_CONFIGMGR_SERVICES    :
+            return "SPAPI_E_NO_CONFIGMGR_SERVICES    ";
+        case SPAPI_E_INVALID_PROPPAGE_PROVIDER :
+            return "SPAPI_E_INVALID_PROPPAGE_PROVIDER ";
+        case SPAPI_E_NO_SUCH_DEVICE_INTERFACE :
+            return "SPAPI_E_NO_SUCH_DEVICE_INTERFACE ";
+        case SPAPI_E_DI_POSTPROCESSING_REQUIRED :
+            return "SPAPI_E_DI_POSTPROCESSING_REQUIRED ";
+        case SPAPI_E_INVALID_COINSTALLER      :
+            return "SPAPI_E_INVALID_COINSTALLER      ";
+        case SPAPI_E_NO_COMPAT_DRIVERS        :
+            return "SPAPI_E_NO_COMPAT_DRIVERS        ";
+        case SPAPI_E_NO_DEVICE_ICON           :
+            return "SPAPI_E_NO_DEVICE_ICON           ";
+        case SPAPI_E_INVALID_INF_LOGCONFIG    :
+            return "SPAPI_E_INVALID_INF_LOGCONFIG    ";
+        case SPAPI_E_DI_DONT_INSTALL          :
+            return "SPAPI_E_DI_DONT_INSTALL          ";
+        case SPAPI_E_INVALID_FILTER_DRIVER    :
+            return "SPAPI_E_INVALID_FILTER_DRIVER    ";
+        case SPAPI_E_ERROR_NOT_INSTALLED      :
+            return "SPAPI_E_ERROR_NOT_INSTALLED      ";
+
+        default:
+            static char buff[1000];
+            sprintf(buff, "Unrecognized error value: %08X\0", error);
+
+            return buff;
+    }
+}
+

+ 82 - 0
panda/src/wdxdisplay8/wdxGraphicsPipe8.h

@@ -0,0 +1,82 @@
+// Filename: wdxGraphicsPipe.h
+// Created by:  mike (09Jan97)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+#ifndef WDXGRAPHICSPIPE8_H
+#define WDXGRAPHICSPIPE8_H
+//
+////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////
+#include <pandabase.h>
+
+#include <string>
+#include <interactiveGraphicsPipe.h>
+#include "wdxGraphicsWindow8.h"
+
+#ifndef STRICT
+#define STRICT
+#endif
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#undef WINDOWS_LEAN_AND_MEAN
+
+////////////////////////////////////////////////////////////////////
+// Defines
+////////////////////////////////////////////////////////////////////
+class Xclass;
+
+extern char * ConvD3DErrorToString(const HRESULT &error);
+
+////////////////////////////////////////////////////////////////////
+//       Class : wdxGraphicsPipe
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX wdxGraphicsPipe : public InteractiveGraphicsPipe {
+public:
+  wdxGraphicsPipe(const PipeSpecifier&);
+
+  wdxGraphicsWindow* find_window(HWND win);
+//  ButtonHandle lookup_key(WPARAM wparam) const;
+
+  virtual TypeHandle get_window_type() const;
+
+public:
+
+  static GraphicsPipe* make_wdxGraphicsPipe(const FactoryParams &params);
+
+  static TypeHandle get_class_type(void);
+  static void init_type(void);
+  virtual TypeHandle get_type(void) const;
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+
+  static TypeHandle _type_handle;
+
+//  int               _width;
+//  int               _height;
+  bool              _shift;
+
+protected:
+
+  wdxGraphicsPipe(void);
+  wdxGraphicsPipe(const wdxGraphicsPipe&);
+  wdxGraphicsPipe& operator=(const wdxGraphicsPipe&);
+
+};
+
+#endif

+ 2371 - 0
panda/src/wdxdisplay8/wdxGraphicsWindow8.cxx

@@ -0,0 +1,2371 @@
+// Filename: wdxGraphicsWindow.cxx
+// Created by:  mike (09Jan00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+#include <tchar.h>
+#include "wdxGraphicsWindow8.h"
+#include "wdxGraphicsPipe8.h"
+#include "dxGraphicsStateGuardian8.h"
+#include "config_wdxdisplay8.h"
+
+#include <keyboardButton.h>
+#include <mouseButton.h>
+
+#include <throw_event.h>
+
+#ifdef DO_PSTATS
+#include <pStatTimer.h>
+#endif
+
+#define D3D_OVERLOADS
+//#define  INITGUID  dont want this if linking w/dxguid.lib
+#include <d3d.h>
+
+#include <map>
+
+////////////////////////////////////////////////////////////////////
+// Static variables
+////////////////////////////////////////////////////////////////////
+TypeHandle wdxGraphicsWindow::_type_handle;
+
+#define LAST_ERROR 0
+#define ERRORBOX_TITLE "Panda3D Error"
+#define WDX_WINDOWCLASSNAME "wdxDisplay"
+
+typedef map<HWND,wdxGraphicsWindow *> HWND_PANDAWIN_MAP;
+
+HWND_PANDAWIN_MAP hwnd_pandawin_map;
+wdxGraphicsWindow* global_wdxwinptr = NULL;  // need this for temporary windproc
+
+extern bool dx_full_screen_antialiasing;  // defined in dxgsg_config.cxx
+
+#define MOUSE_ENTERED 0
+#define MOUSE_EXITED 1
+#define PAUSED_TIMER_ID  7   // completely arbitrary choice
+#define DXREADY ((_dxgsg!=NULL)&&(_dxgsg->GetDXReady()))
+
+LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam,LPARAM lparam);
+
+// because we dont have access to ModifierButtons, as a hack just synchronize state of these
+// keys on get/lose keybd focus
+#define NUM_MODIFIER_KEYS 16
+unsigned int hardcoded_modifier_buttons[NUM_MODIFIER_KEYS]={VK_SHIFT,VK_MENU,VK_CONTROL,VK_SPACE,VK_TAB,
+                                         VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT,VK_PRIOR,VK_NEXT,VK_HOME,VK_END,
+                                         VK_INSERT,VK_DELETE,VK_ESCAPE};
+
+static DWORD BitDepth_2_DDBDMask(DWORD iBitDepth) {
+    switch(iBitDepth) {
+        case 16: return DDBD_16;
+        case 32: return DDBD_32;
+        case 24: return DDBD_24;
+        case 8: return DDBD_8;
+        case 1: return DDBD_1;
+        case 2: return DDBD_2;
+        case 4: return DDBD_4;
+    }
+    return 0x0;
+}
+/*
+typedef enum {DBGLEV_FATAL,DBGLEV_ERROR,DBGLEV_WARNING,DBGLEV_INFO,DBGLEV_DEBUG,DBGLEV_SPAM
+    } DebugLevels;
+ 
+void PrintDBGStr(DebugLevels level,HRESULT hr,const char *msgstr) {
+    ostream *pstrm;
+    static ostream dbg_strms[DBGLEV_SPAM+1]={wdxdisplay_cat.fatal,wdxdisplay_cat.error,
+        wdxdisplay_cat.warning,wdxdisplay_cat.info,wdxdisplay_cat.debug,wdxdisplay_cat.spam};
+    assert(level<=DBGLEV_SPAM);
+
+    pstrm=dbg_strms[level];
+    (*pstrm)->fatal() << "GetDisplayMode failed, result = " << ConvD3DErrorToString(hr) << endl;
+}
+*/
+#ifdef _DEBUG
+static void DebugPrintPixFmt(DDPIXELFORMAT* pddpf) {
+    static int iddpfnum=0;
+    ostream *dbgout = &dxgsg_cat.debug();
+
+    *dbgout << "ZBuf DDPF[" << iddpfnum << "]: RGBBitCount:" << pddpf->dwRGBBitCount
+    << " Flags:"  << (void *)pddpf->dwFlags ;
+
+    if(pddpf->dwFlags & DDPF_STENCILBUFFER) {
+        *dbgout << " StencilBits:" << (void *) pddpf->dwStencilBitDepth;
+    }
+
+    *dbgout << endl;
+
+    iddpfnum++;
+}
+#endif
+
+// pops up MsgBox w/system error msg
+#define LAST_ERROR 0
+void PrintErrorMessage(DWORD msgID) {
+   LPTSTR pMessageBuffer;
+
+   if(msgID==LAST_ERROR)
+     msgID=GetLastError();
+
+   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                 NULL,msgID,  
+                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
+                 (LPTSTR) &pMessageBuffer,  // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
+                 1024, NULL);
+   MessageBox(GetDesktopWindow(),pMessageBuffer,_T(ERRORBOX_TITLE),MB_OK);
+   wdxdisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
+   LocalFree( pMessageBuffer ); 
+}
+
+//#if defined(NOTIFY_DEBUG) || defined(DO_PSTATS)
+#ifdef _DEBUG
+extern void dbgPrintVidMem(LPDIRECTDRAW7 pDD, LPDDSCAPS2 lpddsCaps,const char *pMsg) {
+    DWORD dwTotal,dwFree;
+    HRESULT hr;
+
+ //  These Caps bits arent allowed to be specified when calling GetAvailVidMem.
+ //  They don't affect surface allocation in a vram heap.
+
+#define AVAILVIDMEM_BADCAPS  (DDSCAPS_BACKBUFFER   | \
+                              DDSCAPS_FRONTBUFFER  | \
+                              DDSCAPS_COMPLEX      | \
+                              DDSCAPS_FLIP         | \
+                              DDSCAPS_OWNDC        | \
+                              DDSCAPS_PALETTE      | \
+                              DDSCAPS_SYSTEMMEMORY | \
+                              DDSCAPS_VISIBLE      | \
+                              DDSCAPS_WRITEONLY)
+
+    DDSCAPS2 ddsCaps = *lpddsCaps;
+    ddsCaps.dwCaps &= ~(AVAILVIDMEM_BADCAPS);  // turn off the bad caps
+//  ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; done internally by DX anyway
+
+    if(FAILED(  hr = pDD->GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) {
+        wdxdisplay_cat.debug() << "GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+  // Write a debug message to the console reporting the texture memory.
+    char tmpstr[100],tmpstr2[100];
+    sprintf(tmpstr,"%.4g",dwTotal/1000000.0);
+    sprintf(tmpstr2,"%.4g",dwFree/1000000.0);
+    if(wdxdisplay_cat.is_debug())
+       wdxdisplay_cat.debug() << "AvailableVidMem before creating "<< pMsg << ",(megs) total: " << tmpstr << "  free:" << tmpstr2 <<endl;
+}
+#endif
+
+#define MAX_DX_ZBUF_FMTS 20
+static int cNumZBufFmts;
+
+HRESULT CALLBACK EnumZBufFmtsCallback( LPDDPIXELFORMAT pddpf, VOID* param )  {
+    DDPIXELFORMAT *ZBufFmtsArr = (DDPIXELFORMAT *) param;
+    assert(cNumZBufFmts < MAX_DX_ZBUF_FMTS);
+    memcpy( &(ZBufFmtsArr[cNumZBufFmts]), pddpf, sizeof(DDPIXELFORMAT) );
+    cNumZBufFmts++;
+    return DDENUMRET_OK;
+}
+
+// fn exists so AtExitFn can call it without refcntr blowing up since its !=0
+void wdxGraphicsWindow::DestroyMe(bool bAtExitFnCalled) {
+  if(wdxdisplay_cat.is_spam())
+      wdxdisplay_cat.spam() << "DestroyMe called, AtExitFnCalled=" << bAtExitFnCalled << endl;
+
+  _exiting_window = true;  // may be needed for DestroyWindow call
+
+  if(_dxgsg!=NULL) {
+      _dxgsg->dx_cleanup(_props._fullscreen, bAtExitFnCalled);
+      _dxgsg=NULL;
+  }
+
+  if(_hdc!=NULL) {
+    ReleaseDC(_mwindow,_hdc);
+    _hdc = NULL;
+  }
+
+  if((_hOldForegroundWindow!=NULL) && (_mwindow==GetForegroundWindow())) {
+      SetForegroundWindow(_hOldForegroundWindow);
+  }
+
+  if(_mwindow!=NULL) {
+      if(_bLoadedCustomCursor && _hMouseCursor!=NULL)
+          DestroyCursor(_hMouseCursor);
+
+    DestroyWindow(_mwindow);
+    hwnd_pandawin_map.erase(_mwindow);
+    _mwindow = NULL;
+  }
+}
+
+void wdxGraphicsWindow::do_close_window() {
+  GraphicsWindow::do_close_window();
+   DestroyMe(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Destructor
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow::~wdxGraphicsWindow(void) {
+   close_window();
+}
+
+void DestroyAllWindows(bool bAtExitFnCalled) {
+   // need to go through all windows in map var and delete them
+   while(!hwnd_pandawin_map.empty()) {
+     // cant use a for loop cause DestroyMe erases things out from under us, so iterator is invalid
+     HWND_PANDAWIN_MAP::iterator pwin = hwnd_pandawin_map.begin();
+     if((*pwin).second != NULL) 
+         (*pwin).second->DestroyMe(bAtExitFnCalled);
+   }
+}
+
+void AtExitFn() {
+#ifdef _DEBUG
+    wdxdisplay_cat.spam() << "AtExitFn called\n";
+#endif
+
+     restore_global_parameters();
+
+     DestroyAllWindows(true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: static_window_proc
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+   HWND_PANDAWIN_MAP::iterator pwin;
+   pwin=hwnd_pandawin_map.find(hwnd);
+
+   if(pwin!=hwnd_pandawin_map.end()) {
+      wdxGraphicsWindow *wdxwinptr=(*pwin).second;
+      return wdxwinptr->window_proc(hwnd, msg, wparam, lparam);
+   } else if(global_wdxwinptr!=NULL){
+       // this stuff should only be used during CreateWindow()
+       return global_wdxwinptr->window_proc(hwnd, msg, wparam, lparam);
+   } else {
+       // should never need this??  (maybe at shutdwn?)
+       return DefWindowProc(hwnd, msg, wparam, lparam);
+   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: window_proc
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+LONG wdxGraphicsWindow::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+    int button = -1;
+    int x, y, width, height;
+
+    switch(msg) {
+
+         case WM_PAINT: {
+            PAINTSTRUCT ps;
+            BeginPaint(hwnd, &ps);
+
+            if(DXREADY)
+                show_frame();
+            EndPaint(hwnd, &ps);
+            return 0;
+        }
+
+        case WM_MOUSEMOVE:
+            if(!DXREADY)
+                break;
+        
+            // Win32 doesn't return the same numbers as X does when the mouse
+            // goes beyond the upper or left side of the window
+            #define SET_MOUSE_COORD(iVal,VAL) { \
+                    iVal = VAL;                   \
+                    if(iVal & 0x8000)             \
+                      iVal -= 0x10000;            \
+            }
+        
+            SET_MOUSE_COORD(x,LOWORD(lparam));
+            SET_MOUSE_COORD(y,HIWORD(lparam));
+
+            if(mouse_motion_enabled()
+               && wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
+                handle_mouse_motion(x, y);
+            } else if(mouse_passive_motion_enabled() &&
+                      ((wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) == 0)) {
+                handle_mouse_motion(x, y);
+            }
+            return 0;
+      #if 0
+        case WM_SYSCHAR:
+        case WM_CHAR:  // shouldnt receive WM_CHAR unless WM_KEYDOWN stops returning 0 and passes on to DefWindProc
+            break;
+      #endif
+
+        case WM_SYSKEYDOWN:
+        case WM_KEYDOWN: {
+
+            POINT point;
+
+            GetCursorPos(&point);
+            ScreenToClient(hwnd, &point);
+
+          #ifdef NDEBUG
+               handle_keypress(lookup_key(wparam), point.x, point.y);
+          #else
+            // handle Cntrl-V paste from clipboard
+            if(!((wparam=='V') && (GetKeyState(VK_CONTROL) < 0))) {
+               handle_keypress(lookup_key(wparam), point.x, point.y);
+            } else {
+                HGLOBAL hglb; 
+                char    *lptstr; 
+            
+                if (!IsClipboardFormatAvailable(CF_TEXT)) 
+                   return 0; 
+            
+                if (!OpenClipboard(NULL)) 
+                   return 0; 
+             
+                hglb = GetClipboardData(CF_TEXT); 
+                if (hglb!=NULL) {
+                    lptstr = (char *) GlobalLock(hglb); 
+                    if(lptstr != NULL)  {
+                        char *pChar;
+                        for(pChar=lptstr;*pChar!=NULL;pChar++) {
+                           handle_keypress(KeyboardButton::ascii_key((uchar)*pChar), point.x, point.y);
+                        }
+                        GlobalUnlock(hglb); 
+                    } 
+                }
+                CloseClipboard(); 
+            }
+          #endif
+            // want to use defwindproc on Alt syskey so Alt-F4 works, etc
+            // but do want to bypass defwindproc F10 behavior (it activates the
+            // main menu, but we have none)
+            if((msg==WM_SYSKEYDOWN)&&(wparam!=VK_F10))
+              break;
+             else return 0;
+        }
+
+        case WM_SYSKEYUP:
+        case WM_KEYUP: {
+            handle_keyrelease(lookup_key(wparam));
+            return 0;
+        }
+
+        case WM_LBUTTONDOWN:
+            button = 0;
+        case WM_MBUTTONDOWN:
+            if(button < 0)
+                button = 1;
+        case WM_RBUTTONDOWN:
+            if(!DXREADY) 
+              break;
+
+            if(button < 0)
+                button = 2;
+            SetCapture(hwnd);
+            SET_MOUSE_COORD(x,LOWORD(lparam));
+            SET_MOUSE_COORD(y,HIWORD(lparam));
+            handle_keypress(MouseButton::button(button), x, y);
+            return 0;
+
+        case WM_LBUTTONUP:
+            button = 0;
+        case WM_MBUTTONUP:
+            if(button < 0)
+                button = 1;
+        case WM_RBUTTONUP:
+            if(!DXREADY) 
+              break;
+
+            if(button < 0)
+                button = 2;
+            ReleaseCapture();
+            #if 0
+               SET_MOUSE_COORD(x,LOWORD(lparam));
+               SET_MOUSE_COORD(y,HIWORD(lparam));           
+            #endif
+            handle_keyrelease(MouseButton::button(button));
+            return 0;
+
+        case WM_MOVE:
+            if(!DXREADY)
+                break;
+            handle_window_move(LOWORD(lparam), HIWORD(lparam) );
+            return 0;
+
+        case WM_EXITSIZEMOVE:
+            #ifdef _DEBUG
+              wdxdisplay_cat.spam()  << "WM_EXITSIZEMOVE received"  << endl;
+            #endif
+            
+            if(_WindowAdjustingType==Resizing) {
+                handle_reshape(true);
+            }
+
+            _WindowAdjustingType = NotAdjusting;
+            return 0;
+
+        case WM_ENTERSIZEMOVE: {
+                if(_dxgsg==NULL)
+                    _dxgsg->SetDXReady(true);   // dont disable here because I want to see pic as I resize
+                _WindowAdjustingType = MovingOrResizing;
+            }
+            break;
+
+        case WM_DISPLAYCHANGE: {
+#ifdef _DEBUG
+            width = LOWORD(lparam);  height = HIWORD(lparam);
+            DWORD newbitdepth=wparam;
+            wdxdisplay_cat.spam() <<"WM_DISPLAYCHANGE received with width:" << width << "  height: " << height << " bpp: " << wparam<< endl;
+#endif
+
+            //    unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't
+            //    seem to think we're back in the original displaymode even after I've received
+            //    the WM_DISPLAYCHANGE msg, and returns WRONGMODE error.  So the only way I can
+            //    think of to make this work is to have the timer periodically check for restored
+            //    coop level
+
+            //    if(_props._fullscreen && _window_inactive) {
+            //          if(_dxgsg!=NULL)
+            //              _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW);
+            //           else reactivate_window();
+            //    }
+
+            // does the windowed case handle displaychange properly?  no. need to recreate all devices
+          }
+          break;
+
+        case WM_SIZE: {
+
+#ifdef _DEBUG
+                {
+                    width = LOWORD(lparam);  height = HIWORD(lparam);
+                    wdxdisplay_cat.spam() << "WM_SIZE received with width:" << width << "  height: " << height << " flags: " <<
+                    ((wparam == SIZE_MAXHIDE)? "SIZE_MAXHIDE " : "") << ((wparam == SIZE_MAXSHOW)? "SIZE_MAXSHOW " : "") <<
+                    ((wparam == SIZE_MINIMIZED)? "SIZE_MINIMIZED " : "") << ((wparam == SIZE_RESTORED)? "SIZE_RESTORED " : "") <<
+                    ((wparam == SIZE_MAXIMIZED)? "SIZE_MAXIMIZED " : "") << endl;
+                }
+#endif
+                // old comment -- added SIZE_RESTORED to handle 3dfx case
+                if((_mwindow==NULL) || dx_full_screen || ((wparam != SIZE_RESTORED) && (wparam != SIZE_MAXIMIZED)))
+                    break;
+
+                width = LOWORD(lparam);  height = HIWORD(lparam);
+
+                if((_props._xsize != width) || (_props._ysize != height)) {
+                    _WindowAdjustingType = Resizing;
+
+                 // for maximized,unmaximize, need to call resize code artificially
+                 // since no WM_EXITSIZEMOVE is generated.
+                 if(wparam==SIZE_MAXIMIZED) {
+                       _bSizeIsMaximized=TRUE;
+                       window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
+                 } else if((wparam==SIZE_RESTORED) && _bSizeIsMaximized) {
+                       _bSizeIsMaximized=FALSE;  // only want to reinit dx if restoring from maximized state
+                       window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
+                 }
+                }
+
+                break;
+            }
+
+        case WM_SETFOCUS: {
+            // wdxdisplay_cat.info() << "got WM_SETFOCUS\n";
+            if(!DXREADY) {
+              break;
+            }
+
+            if(_mouse_entry_enabled)
+                handle_mouse_entry(MOUSE_ENTERED,_hMouseCursor);
+
+            POINT point;
+            GetCursorPos(&point);
+            ScreenToClient(hwnd, &point);
+
+            // this is a hack to make sure common modifier keys have proper state
+            // since at focus loss, app may never receive key-up event corresponding to
+            // a key-down. it would be better to know the exact set of ModifierButtons the
+            // user is using, since we may miss some here
+
+            int i;
+            for(i=0;i<NUM_MODIFIER_KEYS;i++) {
+              if(GetKeyState(hardcoded_modifier_buttons[i]) < 0) 
+                handle_keypress(lookup_key(hardcoded_modifier_buttons[i]),point.x,point.y);
+            }
+            return 0;
+        }
+
+        case WM_KILLFOCUS: {
+            // wdxdisplay_cat.info() << "got WM_KILLFOCUS\n";
+            if(!DXREADY) {
+              break;
+            }
+
+            if(_mouse_entry_enabled)
+                  handle_mouse_entry(MOUSE_EXITED,_hMouseCursor);
+
+            int i;
+            for(i=0;i<NUM_MODIFIER_KEYS;i++) {
+              if(GetKeyState(hardcoded_modifier_buttons[i]) < 0)
+                handle_keyrelease(lookup_key(hardcoded_modifier_buttons[i]));
+            }
+
+            return 0;
+        }
+
+#if 0
+        case WM_WINDOWPOSCHANGING: {
+                LPWINDOWPOS pWindPos = (LPWINDOWPOS) lparam;
+                wdxdisplay_cat.spam() << "WM_WINDOWPOSCHANGING received, flags 0x" << pWindPos->flags <<  endl;
+                break;
+            }
+
+        case WM_GETMINMAXINFO:
+            wdxdisplay_cat.spam() << "WM_GETMINMAXINFO received\n" <<  endl;
+            break;
+#endif
+
+        case WM_ERASEBKGND:
+        // WM_ERASEBKGND will be ignored during resizing, because 
+        // we dont want to redraw as user is manually resizing window
+            if(_WindowAdjustingType)
+                break;
+            return 0;  // dont let GDI waste time redrawing the deflt background
+
+        case WM_TIMER:
+            // 2 cases of app deactivation:
+            //
+            // 1) user has switched out of fullscreen mode
+            //    this is first signalled when ACTIVATEAPP returns false
+            //    for this case, we dont wake up until WM_SIZE returns restore or maximize
+            //    and WM_TIMER just periodically reawakens app for idle processing
+
+            //    unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't
+            //    seem to think we're back in the original displaymode even after I've received
+            //    the WM_DISPLAYCHANGE msg, and returns WRONGMODE error.  So the only way I can
+            //    think of to make this work is to have the timer periodically check for restored
+            //    coop level, as it does in case 2)
+
+            //
+            // 2) windowed app has lost access to dx because another app has taken dx exclusive mode
+            //    here we rely on WM_TIMER to periodically check if it is ok to reawaken app.
+            //    windowed apps currently run regardless of if its window is in the foreground
+            //    so we cannot rely on window messages to reawaken app
+
+            if((wparam==_PandaPausedTimer) && (_window_inactive||_active_minimized_fullscreen)) {
+                assert(_dxgsg!=NULL);
+                _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW);
+
+                // wdxdisplay_cat.spam() << "periodic return of control to app\n";
+                _return_control_to_app = true;
+                // throw_event("PandaPaused");   
+                // do we still need to do this since I return control to app periodically using timer msgs?
+                // does app need to know to avoid major computation?
+            }
+            return 0;
+
+        case WM_CLOSE:
+          close_window();
+
+          // BUGBUG:  right now there is no way to tell the panda app the graphics window is invalid or
+          //          has been closed by the user, to prevent further methods from being called on the window.
+          //          this needs to be added to panda for multiple windows to work.  in the meantime, just
+          //          trigger an exit here if # windows==0, since that is the expected behavior when all 
+          //          windows are closed (should be done by the app though, and it assumes you only make this
+          //          type of panda gfx window)
+    
+          if(hwnd_pandawin_map.size()==0) {
+              exit(0);
+          }
+          return 0;
+
+        //case WM_CREATE:
+        //        break;
+
+        case WM_ACTIVATEAPP: {
+            #ifdef _DEBUG
+              wdxdisplay_cat.spam()  << "WM_ACTIVATEAPP(" << (bool)(wparam!=0) <<") received\n";
+            #endif
+            
+           if((!wparam) && _props._fullscreen) {
+               deactivate_window();
+               return 0;
+           }         // dont want to reactivate until window is actually un-minimized (see WM_SIZE)
+           break;
+        }
+    }
+
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+  
+////////////////////////////////////////////////////////////////////
+//     Function: handle_reshape
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::handle_reshape(bool bDoDxReset) {
+    
+    GdiFlush();
+
+    if(bDoDxReset && _dxgsg!=NULL) {
+        HRESULT hr;
+
+        if(_dxgsg->GetBackBuffer()==NULL) {
+            //assume this is initial creation reshape and ignore this call
+            return;
+        }
+
+        // Clear the back/primary surface to black
+        DX_DECLARE_CLEAN(DDBLTFX, bltfx)
+        bltfx.dwDDFX |= DDBLTFX_NOTEARING;
+        hr = _dxgsg->_pri->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx);
+        if(FAILED( hr )) {
+            wdxdisplay_cat.fatal() << "Blt to Black of Primary Surf failed! : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        if(FAILED(hr = _dxgsg->_pDD->TestCooperativeLevel())) {
+             wdxdisplay_cat.error() << "TestCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+             return;
+        }
+
+        _dxgsg->SetDXReady(false);  // disable rendering whilst we mess with surfs
+
+        _dxgsg->RestoreAllVideoSurfaces();
+
+        // Want to change rendertarget size without destroying d3d device.  To save vid memory
+        // (and make resizing work on memory-starved 4MB cards), we need to construct
+        // a temporary mini-sized render target for the d3d device (it cannot point to a
+        // NULL rendertarget) before creating the fully resized buffers.  The old
+        // rendertargets will be freed when these temp targets are set, and that will give
+        // us the memory to create the resized target
+    
+        LPDIRECTDRAWSURFACE7 pddsDummy = NULL, pddsDummyZ = NULL;
+        ULONG refcnt;
+    
+        DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd );
+
+        _dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd);
+        LPDIRECTDRAW7 pDD = _dxgsg->GetDDInterface();
+    
+        ddsd.dwFlags &= ~DDSD_PITCH;
+        ddsd.dwWidth  = 1; ddsd.dwHeight = 1;
+        ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER);
+    
+        PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf");
+    
+        if(FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) )) {
+            wdxdisplay_cat.fatal() << "Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+    
+        DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ );
+        _dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ);
+        ddsdZ.dwFlags &= ~DDSD_PITCH;
+        ddsdZ.dwWidth  = 1;   ddsdZ.dwHeight = 1;
+    
+        PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf");
+    
+        if(FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) )) {
+            wdxdisplay_cat.fatal() << "Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+    
+        if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) {
+            wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+    
+        if(FAILED( hr = _dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 ))) {
+            wdxdisplay_cat.fatal()
+            << "Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+        RELEASE(pddsDummyZ,wdxdisplay,"dummy resize zbuffer",false);
+        RELEASE(pddsDummy,wdxdisplay,"dummy resize rendertarget buffer",false);
+    }
+    
+    RECT view_rect;
+    GetClientRect( _mwindow, &view_rect );
+    ClientToScreen( _mwindow, (POINT*)&view_rect.left );   // translates top,left pnt
+    ClientToScreen( _mwindow, (POINT*)&view_rect.right );  // translates right,bottom pnt
+    
+    // change _props xsize,ysize
+    resized((view_rect.right - view_rect.left),(view_rect.bottom - view_rect.top));
+    
+    _props._xorg = view_rect.left;  // _props origin should reflect upper left of view rectangle
+    _props._yorg = view_rect.top;
+    
+    if(wdxdisplay_cat.is_spam()) {
+      wdxdisplay_cat.spam() << "reshape to origin: (" << _props._xorg << "," << _props._yorg << "), size: (" << _props._xsize << "," << _props._ysize << ")\n";
+    }
+    
+    if(_dxgsg!=NULL) {
+      if(bDoDxReset)
+          _dxgsg->dx_setup_after_resize(view_rect,_mwindow);  // create the new resized rendertargets
+      _dxgsg->SetDXReady(true);
+    }
+}
+
+void wdxGraphicsWindow::deactivate_window(void) {
+    // current policy is to suspend minimized or deactivated fullscreen windows, but leave
+    // regular windows running normally
+
+   if(_window_inactive || _exiting_window || _active_minimized_fullscreen) {
+       #ifdef _DEBUG
+          if(wdxdisplay_cat.is_spam())
+            wdxdisplay_cat.spam()  << "deactivate_window called, but ignored in current mode\n";
+       #endif
+     return;
+   }
+
+   if(bResponsive_minimized_fullscreen_window) {
+       if(wdxdisplay_cat.is_spam())
+           wdxdisplay_cat.spam() << "WDX fullscreen window switching to active minimized mode...\n";
+       _active_minimized_fullscreen = true;
+   } else {
+
+       if(wdxdisplay_cat.is_spam())
+           wdxdisplay_cat.spam() << "WDX window deactivated, waiting...\n";
+
+       _window_inactive = true;
+   }
+
+   if(_props._fullscreen) {
+       // make sure window is minimized
+    
+       WINDOWPLACEMENT wndpl;
+       wndpl.length=sizeof(WINDOWPLACEMENT);
+       
+       if(!GetWindowPlacement(_mwindow,&wndpl)) {
+           wdxdisplay_cat.error() << "GetWindowPlacement failed!\n";
+           return;
+       }
+       if((wndpl.showCmd!=SW_MINIMIZE)&&(wndpl.showCmd!=SW_SHOWMINIMIZED)) {
+           ShowWindow(_mwindow, SW_MINIMIZE);
+       }
+
+       throw_event("PandaPaused"); // right now this is used to signal python event handler to disable audio
+   }
+
+//   if(!bResponsive_minimized_fullscreen_window) {
+   // need this even in responsive-mode to trigger the dxgsg check of cooplvl, i think?
+       _PandaPausedTimer = SetTimer(_mwindow,PAUSED_TIMER_ID,500,NULL);
+       if(_PandaPausedTimer!=PAUSED_TIMER_ID) {
+           wdxdisplay_cat.error() << "Error in SetTimer!\n";
+       }
+//   }
+}
+
+void wdxGraphicsWindow::reactivate_window(void) {
+    if(_window_inactive) {
+    
+        // first see if dx cooperative level is OK for reactivation
+    //    if(!_dxgsg->CheckCooperativeLevel())
+    //        return;
+    
+        if(wdxdisplay_cat.is_spam())
+            wdxdisplay_cat.spam() << "WDX window re-activated...\n";
+    
+        _window_inactive = false;
+    
+        if(_PandaPausedTimer!=NULL) {
+            KillTimer(_mwindow,_PandaPausedTimer);
+            _PandaPausedTimer = NULL;
+        }
+    
+        // move window to top of zorder
+    //  if(_props._fullscreen)
+    //      SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
+        GdiFlush();
+
+    } else if(_active_minimized_fullscreen) {
+        if(wdxdisplay_cat.is_spam())
+            wdxdisplay_cat.spam() << "WDX window unminimized from active-minimized state...\n";
+    
+        if(_PandaPausedTimer!=NULL) {
+            KillTimer(_mwindow,_PandaPausedTimer);
+            _PandaPausedTimer = NULL;
+        }
+
+        _active_minimized_fullscreen = false;
+    
+        // move window to top of zorder
+    //  if(_props._fullscreen)
+    //      SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
+        GdiFlush();
+    }
+
+    if(_props._fullscreen) {
+        throw_event("PandaRestarted");  // right now this is used to signal python event handler to re-enable audio
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Constructor
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow::
+wdxGraphicsWindow(GraphicsPipe* pipe) : GraphicsWindow(pipe) {
+    config();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Constructor
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow::
+wdxGraphicsWindow(GraphicsPipe* pipe, const
+                  GraphicsWindow::Properties& props) : GraphicsWindow(pipe, props) {
+    config();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: config
+//       Access:
+//  Description:  Set up win32 window
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::config(void) {
+    GraphicsWindow::config();
+
+    global_wdxwinptr = this;  // for use during createwin()
+
+    _hdc = NULL;
+    _mwindow = NULL;
+    _gsg = _dxgsg = NULL;
+    _exiting_window = false;
+    _window_inactive = false;
+    _return_control_to_app = false;
+    _active_minimized_fullscreen = false;
+    _bIsLowVidMemCard = false;
+
+    _hOldForegroundWindow=GetForegroundWindow();
+
+    if(dx_full_screen || _props._fullscreen) {
+        _props._fullscreen = dx_full_screen = true;
+    }
+
+    _WindowAdjustingType = NotAdjusting;
+    _hMouseCursor = NULL;
+    _bSizeIsMaximized=FALSE;
+
+    // Create a GSG to manage the graphics
+    make_gsg();
+    if(_gsg==NULL) {
+        wdxdisplay_cat.error() << "DXGSG creation failed!\n";
+        exit(1);
+    }
+    _dxgsg = DCAST(DXGraphicsStateGuardian, _gsg);
+
+    HINSTANCE hinstance = GetModuleHandle(NULL);
+
+    WNDCLASS wc;
+
+    // Clear before filling in window structure!
+    ZeroMemory(&wc, sizeof(WNDCLASS));
+    wc.style      = CS_HREDRAW | CS_VREDRAW; //CS_OWNDC;
+    wc.lpfnWndProc    = (WNDPROC) static_window_proc;
+    wc.hInstance      = hinstance;
+
+    string windows_icon_filename = get_icon_filename().to_os_specific();
+    string windows_mono_cursor_filename = get_mono_cursor_filename().to_os_specific();
+
+    if(!windows_icon_filename.empty()) {
+        // Note: LoadImage seems to cause win2k internal heap corruption (outputdbgstr warnings)
+        // if icon is more than 8bpp
+
+        // loads a .ico fmt file
+        wc.hIcon = (HICON) LoadImage(NULL, windows_icon_filename.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE );
+
+        if(wc.hIcon==NULL) {
+            wdxdisplay_cat.warning() << "windows icon filename '" << windows_icon_filename << "' not found!!\n";
+        }
+    } else {
+        wc.hIcon = NULL; // use default app icon
+    }
+
+    _bLoadedCustomCursor=false;
+
+    if(!windows_mono_cursor_filename.empty()) {
+        // Note: LoadImage seems to cause win2k internal heap corruption (outputdbgstr warnings)
+        // if icon is more than 8bpp
+
+        DWORD load_flags = LR_LOADFROMFILE;
+
+        if(dx_full_screen) {
+            // I think cursors should use LR_CREATEDIBSECTION since they should not be mapped to the device palette (in the case of 256-color cursors)
+            // since they are not going to be used on the desktop
+            load_flags |= LR_CREATEDIBSECTION;
+        }
+        // loads a .cur fmt file. 
+        _hMouseCursor = (HCURSOR) LoadImage(NULL, windows_mono_cursor_filename.c_str(), IMAGE_CURSOR, 0, 0, load_flags);
+
+        if(_hMouseCursor==NULL) {
+            wdxdisplay_cat.warning() << "windows cursor filename '" << windows_mono_cursor_filename << "' not found!!\n";
+            _hMouseCursor = LoadCursor(NULL, IDC_ARROW);
+        }
+        _bLoadedCustomCursor=true;
+    } else {
+        _hMouseCursor = LoadCursor(NULL, IDC_ARROW);
+    }
+
+    wc.hCursor = _hMouseCursor;
+    wc.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);
+    wc.lpszMenuName   = NULL;
+    wc.lpszClassName  = WDX_WINDOWCLASSNAME;
+
+    if(!RegisterClass(&wc)) {
+        wdxdisplay_cat.fatal() << "could not register window class!" << endl;
+        exit(1);
+    }
+
+    DWORD window_style = WS_POPUP | WS_SYSMENU;  // for CreateWindow
+
+    // rect now contains the coords for the entire window, not the client
+    if(dx_full_screen) {
+
+        _mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(),
+                                window_style, 0, 0, _props._xsize,_props._ysize,
+                                NULL, NULL, hinstance, 0);
+    } else {
+        RECT win_rect;
+        SetRect(&win_rect, _props._xorg,  _props._yorg, _props._xorg + _props._xsize,
+                _props._yorg + _props._ysize);
+
+        if(_props._border)
+            window_style |= WS_OVERLAPPEDWINDOW;  // should we just use WS_THICKFRAME instead?
+
+        AdjustWindowRect(&win_rect, window_style, FALSE);  //compute window size based on desired client area size
+
+        // make sure origin is on screen
+        if(win_rect.left < 0) {
+            win_rect.right -= win_rect.left; win_rect.left = 0;
+        }
+        if(win_rect.top < 0) {
+            win_rect.bottom -= win_rect.top; win_rect.top = 0;
+        }
+
+        _mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(),
+                                window_style, win_rect.left, win_rect.top, win_rect.right-win_rect.left,
+                                win_rect.bottom-win_rect.top,
+                                NULL, NULL, hinstance, 0);
+    }
+
+    if(!_mwindow) {
+        wdxdisplay_cat.fatal() << "config() - failed to create window" << endl;
+        exit(1);
+    }
+
+    hwnd_pandawin_map[_mwindow] = this;
+    global_wdxwinptr = NULL;  // get rid of any reference to this obj
+
+    // move window to top of zorder
+    SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
+
+    _hdc = GetDC(_mwindow);
+
+    dx_setup();
+
+    _mouse_input_enabled = false;
+    _mouse_motion_enabled = false;
+    _mouse_passive_motion_enabled = false;
+    _mouse_entry_enabled = false;
+
+    // Enable detection of mouse input
+    enable_mouse_input(true);
+    enable_mouse_motion(true);
+    enable_mouse_passive_motion(true);
+    //  enable_mouse_entry(true);   re-enable this??
+
+    // Now indicate that we have our keyboard/mouse device ready.
+    GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
+    _input_devices.push_back(device);
+
+    ShowWindow(_mwindow, SW_SHOWNORMAL);
+    ShowWindow(_mwindow, SW_SHOWNORMAL);  // call twice to override STARTUPINFO value, which may be set to hidden initially by emacs
+//  UpdateWindow( _mwindow );
+}
+
+HRESULT CALLBACK EnumDevicesCallback(LPSTR pDeviceDescription, LPSTR pDeviceName,
+                                     LPD3DDEVICEDESC7 pD3DDeviceDesc,LPVOID pContext) {
+    D3DDEVICEDESC7 *pd3ddevs = (D3DDEVICEDESC7 *)pContext;
+#ifdef _DEBUG
+    wdxdisplay_cat.spam() << "Enumerating Device " << pDeviceName << " : " << pDeviceDescription << endl;
+#endif
+
+#define REGHALIDX 0
+#define TNLHALIDX 1
+
+    // only saves hal and tnl devs, not sw rasts
+
+    if(IsEqualGUID(pD3DDeviceDesc->deviceGUID,IID_IDirect3DHALDevice)) {
+        CopyMemory(&pd3ddevs[REGHALIDX],pD3DDeviceDesc,sizeof(D3DDEVICEDESC7));
+    } else if(IsEqualGUID(pD3DDeviceDesc->deviceGUID,IID_IDirect3DTnLHalDevice)) {
+        CopyMemory(&pd3ddevs[TNLHALIDX],pD3DDeviceDesc,sizeof(D3DDEVICEDESC7));
+    }
+    return DDENUMRET_OK;
+}
+
+#define MAX_DISPLAY_MODES 100  // probably dont need this much, since i already screen for width&hgt
+typedef struct {
+  DWORD maxWidth,maxHeight;
+  DWORD supportedBitDepths;    // uses DDBD_* flags
+  LPDDSURFACEDESC2 pDDSD_Arr;
+  DWORD cNumSurfDescs;
+} DisplayModeInfo;
+
+HRESULT WINAPI EnumDisplayModesCallBack(LPDDSURFACEDESC2 lpDDSurfaceDesc,LPVOID lpContext) {
+    DisplayModeInfo *pDMI = (DisplayModeInfo *) lpContext;
+
+    // ddsd_search should assure this is true
+    assert((lpDDSurfaceDesc->dwWidth == pDMI->maxWidth) && (lpDDSurfaceDesc->dwHeight == pDMI->maxHeight));
+
+    // ignore refresh rates under 60Hz (and special values of 0 & 1)
+    if((lpDDSurfaceDesc->dwRefreshRate>1) && (lpDDSurfaceDesc->dwRefreshRate<60))
+      return DDENUMRET_OK;
+
+    assert(pDMI->cNumSurfDescs < MAX_DISPLAY_MODES);
+    memcpy( &(pDMI->pDDSD_Arr[pDMI->cNumSurfDescs]), lpDDSurfaceDesc, sizeof(DDSURFACEDESC2) );
+    pDMI->cNumSurfDescs++;
+    pDMI->supportedBitDepths |= BitDepth_2_DDBDMask(lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount);
+
+    return DDENUMRET_OK;
+}
+
+BOOL WINAPI DriverEnumCallback( GUID* pGUID, TCHAR* strDesc,TCHAR* strName,
+                                VOID *argptr, HMONITOR hm) {
+    if(hm!=NULL)  // skip over non-primary display devices
+        return DDENUMRET_OK;
+
+    // primary display driver will have NULL guid
+    // ignore that and save any non-null value, whic
+    // indicates a secondary driver, which is usually voodoo1/2
+    if(pGUID!=NULL) {
+        memcpy(argptr,pGUID,sizeof(GUID));
+    }
+
+    return DDENUMRET_OK;
+}
+
+void wdxGraphicsWindow::resize(unsigned int xsize,unsigned int ysize) {
+   if(wdxdisplay_cat.is_debug())
+      wdxdisplay_cat.debug() << "resize("<<xsize<<","<<ysize<<") called\n";
+
+   if (!_props._fullscreen) {
+        // is this enough?
+        SetWindowPos(_mwindow, NULL, 0,0, xsize, ysize, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
+        // WM_ERASEBKGND will be ignored, because _WindowAdjustingType!=NotAdjusting because 
+        // we dont want to redraw as user is manually resizing window, so need to force explicit
+        // background clear for the programmatic resize fn call
+        _WindowAdjustingType=NotAdjusting;
+
+         // this doesnt seem to be working in toontown resize, so I put ddraw blackblt in handle_reshape instead
+         //window_proc(_mwindow, WM_ERASEBKGND,(WPARAM)_hdc,0x0);  
+        handle_reshape(true);
+        return;
+   }
+
+   _dxgsg->SetDXReady(false);
+
+   HRESULT hr;
+
+   DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_curmode);
+
+   if(FAILED(hr = _dxgsg->_pDD->GetDisplayMode(&ddsd_curmode))) {
+       wdxdisplay_cat.fatal() << "resize() - GetDisplayMode failed, result = " << ConvD3DErrorToString(hr) << endl;
+       exit(1);
+   }
+
+   DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_search);
+
+   ddsd_search.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
+   ddsd_search.dwWidth=xsize;  ddsd_search.dwHeight=ysize;
+
+   // not requesting same refresh rate since changing res might not support same refresh rate
+
+   DDSURFACEDESC2 DDSD_Arr[MAX_DISPLAY_MODES];
+   DisplayModeInfo DMI;
+   ZeroMemory(&DDSD_Arr,sizeof(DDSD_Arr));
+   ZeroMemory(&DMI,sizeof(DMI));
+   DMI.maxWidth=xsize;  DMI.maxHeight=ysize;
+   DMI.pDDSD_Arr=DDSD_Arr;
+
+   if(FAILED(hr = _dxgsg->_pDD->EnumDisplayModes(DDEDM_REFRESHRATES,&ddsd_search,&DMI,EnumDisplayModesCallBack))) {
+       wdxdisplay_cat.fatal() << "resize() - EnumDisplayModes failed, result = " << ConvD3DErrorToString(hr) << endl;
+       return;
+   }
+
+   DMI.supportedBitDepths &= _dxgsg->_D3DDevDesc.dwDeviceRenderBitDepth;
+
+   DWORD dwFullScreenBitDepth;
+   DWORD requested_bpp=ddsd_curmode.ddpfPixelFormat.dwRGBBitCount;
+
+   // would like to match current bpp first.  if that is not possible, try 16bpp, then 32
+   DWORD requested_bpp_DDBD = BitDepth_2_DDBDMask(requested_bpp);
+
+   if(DMI.supportedBitDepths & requested_bpp_DDBD) {
+       dwFullScreenBitDepth=requested_bpp;
+   } else if(DMI.supportedBitDepths & DDBD_16) {
+       dwFullScreenBitDepth=16;
+   } else if(DMI.supportedBitDepths & DDBD_32) {
+       dwFullScreenBitDepth=32;
+   } else {
+       wdxdisplay_cat.error()
+          << "resize failed, no fullScreen resolutions at " << xsize << "x" << ysize << endl;
+       return;
+   }
+
+   if(FAILED(hr = _dxgsg->_pDD->TestCooperativeLevel())) {
+        wdxdisplay_cat.error() << "TestCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+        wdxdisplay_cat.error() << "Full screen app failed to get exclusive mode on resize, exiting..\n";
+        return;
+   }
+
+   _dxgsg->free_dxgsg_objects();
+
+   // let driver choose default refresh rate (hopefully its >=60Hz)   
+   if(FAILED( hr = _dxgsg->_pDD->SetDisplayMode( xsize,ysize,dwFullScreenBitDepth, 0L, 0L ))) {
+        wdxdisplay_cat.error() << "resize failed to reset display mode to (" << xsize <<"x"<<ysize<<"x"<<dwFullScreenBitDepth<<"): result = " << ConvD3DErrorToString(hr) << endl;
+   }
+
+   if(wdxdisplay_cat.is_debug()) {
+      DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd34); 
+      _dxgsg->_pDD->GetDisplayMode(&ddsd34);
+      wdxdisplay_cat.debug() << "set displaymode to " << ddsd34.dwWidth << "x" << ddsd34.dwHeight << " at "<< ddsd34.ddpfPixelFormat.dwRGBBitCount << "bpp, " << ddsd34.dwRefreshRate<< "Hz\n";
+   }
+
+   CreateScreenBuffersAndDevice(xsize,ysize,_dxgsg->_pDD,_dxgsg->_d3d,NULL);
+   _dxgsg->RecreateAllVideoSurfaces();
+   _dxgsg->SetDXReady(true);
+}
+
+unsigned int wdxGraphicsWindow::
+verify_window_sizes(unsigned int numsizes,unsigned int *dimen) {
+   DWORD num_valid_modes=0;
+   HRESULT hr;
+
+   // not requesting same refresh rate since changing res might not support same refresh rate at new size
+
+   DDSURFACEDESC2 DDSD_Arr[MAX_DISPLAY_MODES];
+   DisplayModeInfo DMI;
+   DWORD i,*pCurDim=(DWORD *)dimen;
+
+
+   for(i=0;i<numsizes;i++,pCurDim+=2) {
+      DWORD xsize=pCurDim[0];
+      DWORD ysize=pCurDim[1];
+
+       DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_search);
+       ddsd_search.dwFlags = DDSD_HEIGHT | DDSD_WIDTH; //| DDSD_PIXELFORMAT;
+       ddsd_search.dwWidth=xsize;  ddsd_search.dwHeight=ysize;
+    
+       ZeroMemory(&DDSD_Arr,sizeof(DDSD_Arr));
+       ZeroMemory(&DMI,sizeof(DMI));
+       DMI.maxWidth=xsize;  DMI.maxHeight=ysize;
+       DMI.pDDSD_Arr=DDSD_Arr;
+    
+       if(FAILED(hr = _dxgsg->_pDD->EnumDisplayModes(DDEDM_REFRESHRATES,&ddsd_search,&DMI,EnumDisplayModesCallBack))) {
+           wdxdisplay_cat.fatal() << "resize() - EnumDisplayModes failed, result = " << ConvD3DErrorToString(hr) << endl;
+           return 0;
+       }
+    
+       // get rid of bpp's we cant render at
+       DMI.supportedBitDepths &= _dxgsg->_D3DDevDesc.dwDeviceRenderBitDepth;
+
+       bool bIsGoodMode=false;
+
+       if(_bIsLowVidMemCard) 
+           bIsGoodMode=(((float)xsize*(float)ysize)<=(float)(640*480));
+         else bIsGoodMode=((DMI.supportedBitDepths & (DDBD_16 | DDBD_24 | DDBD_32))!=0);
+
+       if(bIsGoodMode)
+         num_valid_modes++;
+        else {
+            pCurDim[0] = 0;
+            pCurDim[1] = 0;
+        }
+   }
+
+   return num_valid_modes;
+}
+
+// imperfect method to ID NVid? could also scan desc str, but that isnt fullproof either
+#define IS_NVIDIA(DDDEVICEID) ((DDDEVICEID.dwVendorId==0x10DE) || (DDDEVICEID.dwVendorId==0x12D2))
+#define IS_ATI(DDDEVICEID) (DDDEVICEID.dwVendorId==0x1002) 
+#define IS_MATROX(DDDEVICEID) (DDDEVICEID.dwVendorId==0x102B)
+
+void wdxGraphicsWindow::
+check_for_color_cursor_support(void) {
+    // card support for non-black/white GDI cursors varies greatly.  if the cursor is not supported,
+    // it is rendered in software by GDI, which causes a flickering effect (because it's not synced 
+    // with flip?).  GDI transparently masks what's happening so there is no easy way for app to detect
+    // if HW cursor support exists.  alternatives are to tie cursor motion to frame rate using DDraw blts
+    // or overlays, or to have separate thread draw cursor (sync issues?).  instead we do mono cursor 
+    // unless card is known to support 256 color cursors
+
+    string windows_color_cursor_filename = get_color_cursor_filename().to_os_specific();
+    if(windows_color_cursor_filename.empty())
+       return;
+
+    bool bSupportsColorCursor=false;
+
+    if(IS_NVIDIA(_DXDeviceID)) {    
+        // all nvidia seem to support 256 color
+        bSupportsColorCursor=true;
+    } else if(IS_ATI(_DXDeviceID)) {
+        // radeons seem to be in the 5100 range and support color, assume anything in 6000 or above 
+        // is newer than radeon and supports 256 color
+        if(((_DXDeviceID.dwDeviceId>=0x5100) && (_DXDeviceID.dwDeviceId<=0x5200)) ||
+           (_DXDeviceID.dwDeviceId>=0x6000))
+            bSupportsColorCursor=true;
+    } else if IS_MATROX(_DXDeviceID) {
+        if(_DXDeviceID.dwDeviceId==0x0525)   // G400 seems to support color cursors, havent tested other matrox
+            bSupportsColorCursor=true;
+    }
+
+    // TODO: add more cards as more testing is done
+
+    if(bSupportsColorCursor) {
+        // Note: LoadImage seems to cause win2k internal heap corruption (outputdbgstr warnings)
+        // if icon is more than 8bpp
+
+        DWORD load_flags = LR_LOADFROMFILE;
+
+        if(dx_full_screen) {
+            // I think cursors should use LR_CREATEDIBSECTION since they should not be mapped to the device palette (in the case of 256-color cursors)
+            // since they are not going to be used on the desktop
+            load_flags |= LR_CREATEDIBSECTION;
+        }
+
+        // loads a .cur fmt file. 
+        HCURSOR hNewMouseCursor = (HCURSOR) LoadImage(NULL, windows_color_cursor_filename.c_str(), IMAGE_CURSOR, 0, 0, load_flags );
+
+        if(hNewMouseCursor==NULL) {
+            wdxdisplay_cat.warning() << "windows color cursor filename '" << windows_color_cursor_filename << "' not found!!\n";
+            return;
+        }
+
+        SetClassLongPtr(_mwindow, GCLP_HCURSOR, (LONG_PTR) hNewMouseCursor);
+        SetCursor(hNewMouseCursor);
+
+        if(_bLoadedCustomCursor)
+           DestroyCursor(_hMouseCursor);
+        _hMouseCursor = hNewMouseCursor;
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: dx_setup
+//  Description: Set up the DirectX environment.  The size of the
+//               rendered area will be computed from the Client area
+//               of the window (if in windowed mode) and the _props
+//               structure will be set accordingly.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::
+dx_setup() {
+    LPDIRECT3D7   pD3DI;
+    LPDIRECTDRAW7 pDD;
+    HRESULT hr;
+    DX_DECLARE_CLEAN( DDSURFACEDESC2, SurfaceDesc );
+
+    // Check for DirectX 7 by looking for DirectDrawCreateEx
+
+    HINSTANCE DDHinst = LoadLibrary( "ddraw.dll" );
+    if(DDHinst == 0) {
+        wdxdisplay_cat.fatal() << "can't locate DDRAW.DLL!" << endl;
+        exit(1);
+    }
+
+    if(NULL == GetProcAddress( DDHinst, "DirectDrawCreateEx" )) {
+        wdxdisplay_cat.fatal() << "Panda currently requires at least DirectX 7.0!" << endl;
+        exit(1);
+    }
+
+    GUID DriverGUID;
+    ZeroMemory(&DriverGUID,sizeof(GUID));
+
+      // search for early voodoo-type non-primary display drivers
+      // if they exist, use them for 3D  (could examine 3D devices on all
+      // drivers and pick the best one, but I'll assume the computer setuper knows what he's doing)
+    if(hr = DirectDrawEnumerateEx( DriverEnumCallback, &DriverGUID, DDENUM_NONDISPLAYDEVICES )) {
+        wdxdisplay_cat.fatal()   << "config() - DirectDrawEnumerateEx failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    GUID *pOurDriverGUID=NULL;
+    if(DriverGUID.Data1 != 0x0) {    // assumes no driver guid ever starts with 0, so 0 means Enum found no voodoo-type device
+        pOurDriverGUID=&DriverGUID;
+    }
+
+      // Create the Direct Draw Object
+    hr = DirectDrawCreateEx(pOurDriverGUID, (void **)&pDD, IID_IDirectDraw7, NULL);
+    if(hr != DD_OK) {
+        wdxdisplay_cat.fatal()
+        << "config() - DirectDrawCreateEx failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    FreeLibrary(DDHinst);    //undo LoadLib above, decrement ddrawl.dll refcnt (after DDrawCreate, since dont want to unload/reload)
+
+    pDD->GetDeviceIdentifier(&_DXDeviceID,0x0);
+
+#ifdef _DEBUG
+    wdxdisplay_cat.debug() << " GfxCard: " << _DXDeviceID.szDescription <<  "; DriverFile: '" << _DXDeviceID.szDriver  << "'; VendorID: " <<_DXDeviceID.dwVendorId <<"; DriverVer: " << HIWORD(_DXDeviceID.liDriverVersion.HighPart) << "." << LOWORD(_DXDeviceID.liDriverVersion.HighPart) << "." << HIWORD(_DXDeviceID.liDriverVersion.LowPart) << "." << LOWORD(_DXDeviceID.liDriverVersion.LowPart) << endl;
+#endif
+    
+    check_for_color_cursor_support();
+    
+    // Query DirectDraw for access to Direct3D
+
+    hr = pDD->QueryInterface( IID_IDirect3D7, (VOID**)&pD3DI );
+    if(hr != DD_OK) {
+        wdxdisplay_cat.fatal() << "QI for D3D failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    D3DDEVICEDESC7 d3ddevs[2];  // put HAL in 0, TnLHAL in 1
+
+
+    // just look for HAL and TnL devices right now.  I dont think
+    // we have any interest in the sw rasts at this point
+
+    ZeroMemory(d3ddevs,2*sizeof(D3DDEVICEDESC7));
+
+    hr = pD3DI->EnumDevices(EnumDevicesCallback,d3ddevs);
+    if(hr != DD_OK) {
+        wdxdisplay_cat.fatal() << "EnumDevices failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    WORD DeviceIdx=REGHALIDX;
+
+    if(!(d3ddevs[DeviceIdx].dwDevCaps & D3DDEVCAPS_HWRASTERIZATION )) {
+        wdxdisplay_cat.fatal() << "No 3D HW present, exiting..." << endl;
+        exit(1);
+    }
+
+    // select TNL if present
+    if(d3ddevs[TNLHALIDX].dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) {
+        DeviceIdx=TNLHALIDX;
+    }
+
+    D3DDEVICEDESC7 *pD3DDevDesc=&d3ddevs[DeviceIdx];
+
+    DWORD dwRenderWidth=0, dwRenderHeight=0;
+
+    if(dx_full_screen) {
+           dwRenderWidth  = _props._xsize;
+           dwRenderHeight = _props._ysize;
+           _props._xorg = _props._yorg = 0;
+
+           // CREATE FULL SCREEN BUFFERS
+           // Store the rectangle which contains the renderer
+
+           DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_search);
+           ddsd_search.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
+           ddsd_search.dwWidth=dwRenderWidth;  ddsd_search.dwHeight=dwRenderHeight;
+
+           DDSURFACEDESC2 DDSD_Arr[MAX_DISPLAY_MODES];
+           DisplayModeInfo DMI;
+           ZeroMemory(&DDSD_Arr,sizeof(DDSD_Arr));
+           ZeroMemory(&DMI,sizeof(DMI));
+           DMI.maxWidth=dwRenderWidth;  DMI.maxHeight=dwRenderHeight;
+           DMI.pDDSD_Arr=DDSD_Arr;
+
+           if(FAILED(hr= pDD->EnumDisplayModes(DDEDM_REFRESHRATES,&ddsd_search,&DMI,EnumDisplayModesCallBack))) {
+               wdxdisplay_cat.fatal() << "EnumDisplayModes failed, result = " << ConvD3DErrorToString(hr) << endl;
+               exit(1);
+           }
+
+           DWORD dwFullScreenBitDepth;
+
+           // Now we try to figure out if we can use requested screen resolution and best
+           // rendertarget bpp and still have at least 2 meg of texture vidmem
+
+           // Get Current VidMem avail.  Note this is only an estimate, when we switch to fullscreen
+           // mode from desktop, more vidmem will be available (typically 1.2 meg).  I dont want
+           // to switch to fullscreen more than once due to the annoying monitor flicker, so try
+           // to figure out optimal mode using this estimate
+           DDSCAPS2 ddsCaps;
+           DWORD dwTotal,dwFree;
+           ZeroMemory(&ddsCaps,sizeof(DDSCAPS2));
+           ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; //set internally by DX anyway, dont think this any different than 0x0
+
+           if(FAILED(  hr = pDD->GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) {
+               wdxdisplay_cat.error() << "GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl;
+               exit(1);
+           }
+
+   #ifdef _DEBUG
+           wdxdisplay_cat.debug() << "before FullScreen switch: GetAvailableVidMem returns Total: " << dwTotal/1000000.0 << "  Free: " << dwFree/1000000.0 << endl;
+   #endif
+
+           DMI.supportedBitDepths &= pD3DDevDesc->dwDeviceRenderBitDepth;
+
+           // note: this chooses 32bpp, which may not be preferred over 16 for memory & speed reasons
+           if(DMI.supportedBitDepths & DDBD_32) {
+               dwFullScreenBitDepth=32;              // go for 32bpp if its avail
+           } else if(DMI.supportedBitDepths & DDBD_24) {
+               dwFullScreenBitDepth=24;              // go for 24bpp if its avail
+           } else if(DMI.supportedBitDepths & DDBD_16) {
+               dwFullScreenBitDepth=16;              // do 16bpp
+           } else {
+               wdxdisplay_cat.fatal()
+               << "No Supported FullScreen resolutions at " << dwRenderWidth << "x" << dwRenderHeight << endl;
+               exit(1);
+           }
+
+           if(dwFree>0) {  // assume buggy drivers (this means you, FireGL2) may return zero for dwTotal, so ignore value if its 0
+
+               // hack: figuring out exactly what res to use is tricky, instead I will
+               // just use 640x480 if we have < 3 meg avail
+
+       #define LOWVIDMEMTHRESHOLD 3500000
+               if(dwFree< LOWVIDMEMTHRESHOLD) {
+                   // we're going to need 800x600 or 640x480 at 16 bit to save enough tex vidmem
+                   dwFullScreenBitDepth=16;              // do 16bpp
+                   dwRenderWidth=640;
+                   dwRenderHeight=480;
+                   _bIsLowVidMemCard = true;
+                   wdxdisplay_cat.debug() << " "<<dwFree <<" Available VidMem is under "<< LOWVIDMEMTHRESHOLD <<", using 640x480 16bpp rendertargets to save tex vidmem.\n";
+               }
+
+       #if 0
+       // cant do this without more accurate way to estimate mem used before actually switching
+       // to that fullscrn mode.  simply computing memsize based on GetDisplayMode doesnt seem
+       // to be accurate within more than 1 meg
+
+               // we think we need to reserve at least 2 megs of vidmem for textures.
+               // to do this, reduce buffer bitdepth if possible
+       #define RESERVEDTEXVIDMEM 2000000
+
+               int rendertargetmem=dwRenderWidth*dwRenderHeight*(dwFullScreenBitDepth>>3);
+               int memleft = dwFree-rendertargetmem*2;   //*2 to handle backbuf/zbuf
+
+               if(memleft < RESERVEDTEXVIDMEM) {
+                   dwFullScreenBitDepth=16;
+                   wdxdisplay_cat.debug() << "using 16bpp rendertargets to save tex vidmem\n";
+                   assert((DMI.supportedBitDepths & DDBD_16) && (pD3DDevDesc->dwDeviceRenderBitDepth & DDBD_16));   // probably a safe assumption
+                   rendertargetmem=dwRenderWidth*dwRenderHeight*(dwFullScreenBitDepth>>3);
+                   memleft = dwFree-rendertargetmem*2;
+
+                    // BUGBUG:  if we still cant reserve 2 megs of vidmem, need to auto-reduce the scrn res
+                   if(memleft < RESERVEDTEXVIDMEM)
+                       wdxdisplay_cat.debug() << " XXXXXX WARNING: cant reserve 2MB of tex vidmem. only " << memleft << " bytes available. Need to rewrite wdxdisplay to try lower resolutions  XXXXXXXXXXXXXXXXXXXX\n";
+               }
+       #endif
+           }
+
+   //      extern bool dx_preserve_fpu_state;
+           DWORD SCL_FPUFlag = DDSCL_FPUSETUP;
+
+   //      if(dx_preserve_fpu_state)
+   //         SCL_FPUFlag = DDSCL_FPUPRESERVE;  // tell d3d to preserve the fpu state across calls.  this hurts perf, but is good for dbgging
+
+           DWORD SCL_FLAGS = SCL_FPUFlag | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT;
+
+           if(FAILED(hr = pDD->SetCooperativeLevel(_mwindow, SCL_FLAGS))) {
+               wdxdisplay_cat.fatal() << "SetCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+               exit(1);
+           }
+
+           // s3 savage2000 on w95 seems to set EXCLUSIVE_MODE only if you call SetCoopLevel twice.
+           // so we do it, it really shouldnt be necessary if drivers werent buggy
+           if(FAILED(hr = pDD->SetCooperativeLevel(_mwindow, SCL_FLAGS))) {
+               wdxdisplay_cat.fatal()
+               << "SetCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+               exit(1);
+           }
+
+           if(FAILED(hr = pDD->TestCooperativeLevel())) {
+               wdxdisplay_cat.fatal() << "TestCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+               wdxdisplay_cat.fatal() << "Full screen app failed to get exclusive mode on init, exiting..\n";
+               exit(1);
+           }
+
+           // let driver choose default refresh rate (hopefully its >=60Hz)
+           if(FAILED( hr = pDD->SetDisplayMode( dwRenderWidth, dwRenderHeight,
+                                                dwFullScreenBitDepth, 0L, 0L ))) {
+               wdxdisplay_cat.fatal() << "failed to reset display mode to ("<<dwRenderWidth<<"x"<<dwRenderHeight<<"x"<<dwFullScreenBitDepth<<"): result = " << ConvD3DErrorToString(hr) << endl;
+               exit(1);
+           }
+
+          if(wdxdisplay_cat.is_debug()) {
+              DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd34); 
+              pDD->GetDisplayMode(&ddsd34);
+              wdxdisplay_cat.debug() << "set displaymode to " << ddsd34.dwWidth << "x" << ddsd34.dwHeight << " at "<< ddsd34.ddpfPixelFormat.dwRGBBitCount << "bpp, " << ddsd34.dwRefreshRate<< "Hz\n";
+
+           #ifdef _DEBUG
+              if(FAILED(  hr = pDD->GetAvailableVidMem(&ddsCaps,&dwTotal,&dwFree))) {
+                  wdxdisplay_cat.debug() << "GetAvailableVidMem failed : result = " << ConvD3DErrorToString(hr) << endl;
+                  exit(1);
+              }
+              wdxdisplay_cat.debug() << "after FullScreen switch: GetAvailableVidMem returns Total: " << dwTotal/1000000.0 << "  Free: " << dwFree/1000000.0 << endl;
+           #endif
+           }
+    }
+
+    CreateScreenBuffersAndDevice(dwRenderWidth,dwRenderHeight,pDD,pD3DI,pD3DDevDesc);
+
+    _dxgsg->SetDXReady(true);
+}
+
+
+void wdxGraphicsWindow::
+CreateScreenBuffersAndDevice(DWORD dwRenderWidth, DWORD dwRenderHeight,LPDIRECTDRAW7 pDD,
+                             LPDIRECT3D7 pD3DI,D3DDEVICEDESC7 *pD3DDevDesc) {
+    LPDIRECTDRAWSURFACE7  pPrimaryDDSurf,pBackDDSurf,pZDDSurf;
+    LPDIRECT3DDEVICE7     pD3DDevice;
+    RECT view_rect;
+    int i;
+    HRESULT hr;
+    DX_DECLARE_CLEAN( DDSURFACEDESC2, SurfaceDesc );
+    D3DDEVICEDESC7 d3ddevs[2];  // put HAL in 0, TnLHAL in 1
+
+    assert(pDD!=NULL);
+    assert(pD3DI!=NULL);
+
+    // select the best device if the caller does not provide one
+    if(pD3DDevDesc==NULL) {
+    
+        // just look for HAL and TnL devices right now.  I dont think
+        // we have any interest in the sw rasts at this point
+
+        ZeroMemory(d3ddevs,2*sizeof(D3DDEVICEDESC7));
+    
+        hr = pD3DI->EnumDevices(EnumDevicesCallback,d3ddevs);
+        if(hr != DD_OK) {
+            wdxdisplay_cat.fatal() << "EnumDevices failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        WORD DeviceIdx=REGHALIDX;
+
+        if(!(d3ddevs[DeviceIdx].dwDevCaps & D3DDEVCAPS_HWRASTERIZATION )) {
+            wdxdisplay_cat.fatal() << "No 3D HW present, exiting..." << endl;
+            exit(1);
+        }
+
+        // select TNL if present
+        if(d3ddevs[TNLHALIDX].dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) {
+            DeviceIdx=TNLHALIDX;
+        }
+
+        pD3DDevDesc=&d3ddevs[DeviceIdx];
+    }
+
+    DX_DECLARE_CLEAN(DDCAPS,DDCaps);
+    pDD->GetCaps(&DDCaps,NULL);
+
+   if(dx_full_screen) {
+        // Setup to create the primary surface w/backbuffer
+        DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd)
+        ddsd.dwFlags           = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;
+        ddsd.ddsCaps.dwCaps    = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE |
+                                 DDSCAPS_FLIP | DDSCAPS_COMPLEX;
+        ddsd.dwBackBufferCount = 1;
+
+        if(dx_full_screen_antialiasing) {
+            // cant check that d3ddevice has this capability yet, so got to set it anyway.
+            // hope this is OK.
+            ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_HINTANTIALIASING; 
+        }
+
+        PRINTVIDMEM(pDD,&ddsd.ddsCaps,"initial primary & backbuf");
+
+        // Create the primary surface
+        if(FAILED( hr = pDD->CreateSurface( &ddsd, &pPrimaryDDSurf, NULL ) )) {
+            wdxdisplay_cat.fatal() << "CreateSurface failed for primary surface: result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        // Clear the primary surface to black
+
+        DX_DECLARE_CLEAN(DDBLTFX, bltfx)
+        bltfx.dwDDFX |= DDBLTFX_NOTEARING;
+        hr = pPrimaryDDSurf->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx);
+
+        if(FAILED( hr )) {
+            wdxdisplay_cat.fatal() << "Blt to Black of Primary Surf failed! : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        // Get the backbuffer, which was created along with the primary.
+        DDSCAPS2 ddscaps = { DDSCAPS_BACKBUFFER, 0, 0, 0};
+        if(FAILED( hr = pPrimaryDDSurf->GetAttachedSurface( &ddscaps, &pBackDDSurf ) )) {
+            wdxdisplay_cat.fatal() << "Can't get the backbuffer: result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        if(FAILED( hr = pBackDDSurf->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx))) {
+            wdxdisplay_cat.fatal() << "Blt to Black of Back Surf failed! : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        SetRect(&view_rect, 0, 0, dwRenderWidth, dwRenderHeight);
+    }   // end create full screen buffers
+
+    else {          // CREATE WINDOWED BUFFERS
+        assert(dwRenderWidth==0 && dwRenderHeight==0);  // these params are ignored for windowed mode
+
+        if(!(DDCaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED)) {
+            wdxdisplay_cat.fatal() << "the 3D HW cannot render windowed, exiting..." << endl;
+            exit(1);
+        }
+
+        if(FAILED(hr = pDD->GetDisplayMode( &SurfaceDesc ))) {
+            wdxdisplay_cat.fatal()
+            << "GetDisplayMode failed result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+        if(SurfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8) {
+            wdxdisplay_cat.fatal() << "Can't run windowed in an 8-bit or less display mode" << endl;
+            exit(1);
+        }
+
+        if(!(BitDepth_2_DDBDMask(SurfaceDesc.ddpfPixelFormat.dwRGBBitCount) & pD3DDevDesc->dwDeviceRenderBitDepth)) {
+            wdxdisplay_cat.fatal() << "3D Device doesnt support rendering at " << SurfaceDesc.ddpfPixelFormat.dwRGBBitCount << "bpp (current desktop bitdepth)" << endl;
+            exit(1);
+        }
+
+//      extern bool dx_preserve_fpu_state;
+        DWORD SCL_FPUFlag = DDSCL_FPUSETUP;
+
+//      if(dx_preserve_fpu_state)
+//         SCL_FPUFlag = DDSCL_FPUPRESERVE;  // tell d3d to preserve the fpu state across calls.  this hurts perf, but is good for dbgging
+
+        if(FAILED(hr = pDD->SetCooperativeLevel(_mwindow, SCL_FPUFlag | DDSCL_NORMAL))) {
+            wdxdisplay_cat.fatal() << "SetCooperativeLevel failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+        // Get the dimensions of the viewport and screen bounds
+
+        GetClientRect( _mwindow, &view_rect );
+        POINT ul,lr;
+        ul.x=view_rect.left;  ul.y=view_rect.top;
+        lr.x=view_rect.right;  lr.y=view_rect.bottom;
+        ClientToScreen( _mwindow, &ul );
+        ClientToScreen( _mwindow, &lr );
+        view_rect.left=ul.x; view_rect.top=ul.y;
+        view_rect.right=lr.x; view_rect.bottom=lr.y;
+
+        dwRenderWidth  = view_rect.right - view_rect.left;
+        dwRenderHeight = view_rect.bottom - view_rect.top;
+        _props._xorg = view_rect.left;  // _props should reflect view rectangle
+        _props._yorg = view_rect.top;
+        _props._xsize = dwRenderWidth;
+        _props._ysize = dwRenderHeight;
+
+        // Initialize the description of the primary surface
+        ZeroMemory( &SurfaceDesc, sizeof(DDSURFACEDESC2) );
+        SurfaceDesc.dwSize         = sizeof(DDSURFACEDESC2);
+        SurfaceDesc.dwFlags        = DDSD_CAPS ;
+        SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+        PRINTVIDMEM(pDD,&SurfaceDesc.ddsCaps,"initial primary surface");
+
+        // Create the primary surface.  This includes all of the visible
+        // window, so no need to specify height/width
+        if(FAILED(hr = pDD->CreateSurface( &SurfaceDesc, &pPrimaryDDSurf, NULL ))) {
+            wdxdisplay_cat.fatal()
+            << "CreateSurface failed for primary surface: result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        // Create a clipper object which handles all our clipping for cases when
+        // our window is partially obscured by other windows.
+        LPDIRECTDRAWCLIPPER Clipper;
+        if(FAILED(hr = pDD->CreateClipper( 0, &Clipper, NULL ))) {
+            wdxdisplay_cat.fatal()
+            << "CreateClipper failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        // Associate the clipper with our window. Note that, afterwards, the
+        // clipper is internally referenced by the primary surface, so it is safe
+        // to release our local reference to it.
+        Clipper->SetHWnd( 0, _mwindow );
+        pPrimaryDDSurf->SetClipper( Clipper );
+        Clipper->Release();
+   
+        // Clear the primary surface to black
+        DX_DECLARE_CLEAN(DDBLTFX, bltfx)
+
+        if(FAILED( hr = pPrimaryDDSurf->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx))) {
+            wdxdisplay_cat.fatal()
+            << "Blt to Black of Primary Surf failed! : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        // Setup a surface description to create a backbuffer.
+        SurfaceDesc.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
+        SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY;
+        SurfaceDesc.dwWidth  = dwRenderWidth;
+        SurfaceDesc.dwHeight = dwRenderHeight;
+
+        if(dx_full_screen_antialiasing) {
+            // cant check that d3ddevice has this capability yet, so got to set it anyway.
+            // hope this is OK.
+            SurfaceDesc.ddsCaps.dwCaps2 |= DDSCAPS2_HINTANTIALIASING; 
+        }
+
+        PRINTVIDMEM(pDD,&SurfaceDesc.ddsCaps,"initial backbuf");
+
+        // Create the backbuffer. (might want to handle failure due to running out of video memory.
+        if(FAILED(hr = pDD->CreateSurface( &SurfaceDesc, &pBackDDSurf, NULL ))) {
+            wdxdisplay_cat.fatal()
+            << "CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        if(FAILED( hr = pBackDDSurf->Blt(NULL,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&bltfx))) {
+            wdxdisplay_cat.fatal()
+            << "Blt to Black of Back Surf failed! : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+    }  // end create windowed buffers
+
+//  ========================================================
+
+    resized(dwRenderWidth,dwRenderHeight);  // update panda channel/display rgn info
+
+    #ifndef NDEBUG
+      if(!(_props._mask & W_DEPTH)) {
+        wdxdisplay_cat.info() << "no zbuffer requested, skipping zbuffer creation\n";
+      }
+    #endif
+
+    // Check if the device supports z-bufferless hidden surface removal. If so,
+    // we don't really need a z-buffer
+    if((!(pD3DDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR )) &&
+       (_props._mask & W_DEPTH)) {
+
+        // Get z-buffer dimensions from the render target
+        DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd);
+        pBackDDSurf->GetSurfaceDesc( &ddsd );
+
+        // Setup the surface desc for the z-buffer.
+        ddsd.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT;
+        ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY;
+
+        DDPIXELFORMAT ZBufPixFmts[MAX_DX_ZBUF_FMTS];
+        cNumZBufFmts=0;
+
+        // Get an appropiate pixel format from enumeration of the formats. On the
+        // first pass, we look for a zbuffer dpeth which is equal to the frame
+        // buffer depth (as some cards unfornately require this).
+        if(FAILED(pD3DI->EnumZBufferFormats(  IID_IDirect3DHALDevice, EnumZBufFmtsCallback,
+                                              (VOID*)&ZBufPixFmts ))) {
+            wdxdisplay_cat.fatal() << "EnumZBufferFormats failed " << endl;
+            exit(1);
+        }
+
+#ifdef _DEBUG
+        { static BOOL bPrinted=FALSE;
+            if(!bPrinted) {
+                for(i=0;i<cNumZBufFmts;i++) {
+                    DebugPrintPixFmt(&ZBufPixFmts[i]);
+                }
+                bPrinted=TRUE;
+            }
+        }
+#endif
+
+        // int want_depth_bits = _props._want_depth_bits;  should we pay attn to these at some point?
+        // int want_color_bits = _props._want_color_bits;
+        bool bWantStencil = ((_props._mask & W_STENCIL)!=0);
+
+        LPDDPIXELFORMAT pCurPixFmt,pz16=NULL,pz24=NULL,pz32=NULL;
+        for(i=0,pCurPixFmt=ZBufPixFmts;i<cNumZBufFmts;i++,pCurPixFmt++) {
+          if(bWantStencil==((pCurPixFmt->dwFlags & DDPF_STENCILBUFFER)!=0)) {
+            switch(pCurPixFmt->dwRGBBitCount) {
+                case 16:
+                    pz16=pCurPixFmt;
+                    break;
+                case 24:
+                    pz24=pCurPixFmt;
+                    break;
+                case 32:
+                    pz32=pCurPixFmt;
+                    break;
+            }
+          }
+        }
+
+        if((pz16==NULL)&&(pz24==NULL)&&(pz32==NULL)) {
+            if(bWantStencil) 
+                wdxdisplay_cat.fatal() << "stencil buffer requested, device has no stencil capability\n";
+              else wdxdisplay_cat.fatal() << "failed to find adequate zbuffer capability\n";
+            exit(1);
+        }
+
+        #define SET_ZBUF_DEPTH(DEPTH) { assert(pz##DEPTH != NULL); _depth_buffer_bpp=DEPTH; ddsd.ddpfPixelFormat = *pz##DEPTH;}
+
+        if(IS_NVIDIA(_DXDeviceID)) {
+           DX_DECLARE_CLEAN(DDSURFACEDESC2,ddsd_pri)
+            pPrimaryDDSurf->GetSurfaceDesc(&ddsd_pri);
+
+            // must pick zbuf depth to match primary surface depth for nvidia
+            if(ddsd_pri.ddpfPixelFormat.dwRGBBitCount==16) {
+                SET_ZBUF_DEPTH(16);
+            } else {
+                if(dx_force_16bpp_zbuffer) {
+                    wdxdisplay_cat.fatal() << "'dx-force-16bpp-zbuffer #t' requires a 16bpp desktop on nvidia cards\n";
+                    exit(1);
+                }
+                // take the smaller of 24 or 32.  (already assured to match stencil capability)
+                if(pz24!=NULL) {
+                    SET_ZBUF_DEPTH(24);
+                } else SET_ZBUF_DEPTH(32);
+            }
+        } else {
+            if(dx_force_16bpp_zbuffer) {
+                if(pz16==NULL) {
+                    wdxdisplay_cat.fatal() << "'dx-force-16bpp-zbuffer #t', but no 16bpp zbuf fmts available on this card\n";
+                    exit(1);
+                }
+
+                if(wdxdisplay_cat.is_debug())
+                   wdxdisplay_cat.debug() << "forcing use of 16bpp Z-Buffer\n";
+                SET_ZBUF_DEPTH(16);
+                ddsd.ddpfPixelFormat = *pz16;
+            } else {
+                // pick the highest res zbuffer format avail.  Note: this is choosing to waste vid-memory
+                // and possibly perf for more accuracy, less z-fighting at long distance (std 16bpp would 
+                // be smaller// maybe faster)
+                // order of preference 24: (should be enough), 32: probably means 24 of Z, then 16
+
+                if(bWantStencil && (pz32!=NULL)) {
+                    // dont want to select 16/8 z/stencil over 24/8 z/stenc
+                    SET_ZBUF_DEPTH(32);
+                } else {
+                    if(pz24!=NULL) {
+                        SET_ZBUF_DEPTH(24);
+                    } else if(pz32!=NULL) {
+                        SET_ZBUF_DEPTH(32);
+                    } else {
+                        SET_ZBUF_DEPTH(16);
+                    }
+                }
+            }
+        }
+
+        PRINTVIDMEM(pDD,&ddsd.ddsCaps,"initial zbuf");
+
+#ifdef _DEBUG
+        wdxdisplay_cat.info() << "Creating " << ddsd.ddpfPixelFormat.dwRGBBitCount << "bpp zbuffer\n";
+#endif
+
+        // Create and attach a z-buffer
+        if(FAILED( hr = pDD->CreateSurface( &ddsd, &pZDDSurf, NULL ) )) {
+            wdxdisplay_cat.fatal() << "CreateSurface failed for Z buffer: result = " <<  ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+
+        if(FAILED( hr = pBackDDSurf->AddAttachedSurface( pZDDSurf ) )) {
+            wdxdisplay_cat.fatal() << "AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl;
+            exit(1);
+        }
+    }
+
+    // Create the device. The device is created off of our back buffer, which
+    // becomes the render target for the newly created device.
+    hr = pD3DI->CreateDevice(pD3DDevDesc->deviceGUID, pBackDDSurf, &pD3DDevice );
+    if(hr != DD_OK) {
+        wdxdisplay_cat.fatal() << "CreateDevice failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    // Create the viewport
+    D3DVIEWPORT7 vp = { 0, 0, _props._xsize, _props._ysize, 0.0f, 1.0f};
+    hr = pD3DDevice->SetViewport( &vp );
+    if(hr != DD_OK) {
+        wdxdisplay_cat.fatal() << "SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl;
+        exit(1);
+    }
+
+    _dxgsg->Set_HDC(_hdc);
+    _dxgsg->dx_init(pDD, pPrimaryDDSurf, pBackDDSurf, pZDDSurf, pD3DI, pD3DDevice, view_rect);
+    // do not SetDXReady() yet since caller may want to do more work before letting rendering proceed
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: setup_colormap
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::setup_colormap(void) {
+    PIXELFORMATDESCRIPTOR pfd;
+    LOGPALETTE *logical;
+    int n;
+
+  // should probably do this some other way than through opengl PixelFormat calls
+
+  /* grab the pixel format */
+    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+    DescribePixelFormat(_hdc, GetPixelFormat(_hdc),
+                        sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+
+    if(!(pfd.dwFlags & PFD_NEED_PALETTE ||
+         pfd.iPixelType == PFD_TYPE_COLORINDEX))
+        return;
+
+    n = 1 << pfd.cColorBits;
+
+  /* allocate a bunch of memory for the logical palette (assume 256
+     colors in a Win32 palette */
+    logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
+                                  sizeof(PALETTEENTRY) * n);
+    memset(logical, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n);
+
+  /* set the entries in the logical palette */
+    logical->palVersion = 0x300;
+    logical->palNumEntries = n;
+
+  /* start with a copy of the current system palette */
+    GetSystemPaletteEntries(_hdc, 0, 256, &logical->palPalEntry[0]);
+
+    if(pfd.iPixelType == PFD_TYPE_RGBA) {
+        int redMask = (1 << pfd.cRedBits) - 1;
+        int greenMask = (1 << pfd.cGreenBits) - 1;
+        int blueMask = (1 << pfd.cBlueBits) - 1;
+        int i;
+
+    /* fill in an RGBA color palette */
+        for(i = 0; i < n; ++i) {
+            logical->palPalEntry[i].peRed =
+            (((i >> pfd.cRedShift)   & redMask)   * 255) / redMask;
+            logical->palPalEntry[i].peGreen =
+            (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
+            logical->palPalEntry[i].peBlue =
+            (((i >> pfd.cBlueShift)  & blueMask)  * 255) / blueMask;
+            logical->palPalEntry[i].peFlags = 0;
+        }
+    }
+
+    _colormap = CreatePalette(logical);
+    free(logical);
+
+    SelectPalette(_hdc, _colormap, FALSE);
+    RealizePalette(_hdc);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: begin_frame
+//       Access:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::begin_frame(void) {
+    GraphicsWindow::begin_frame();
+}
+
+void wdxGraphicsWindow::show_frame(void) {
+    _dxgsg->show_frame();
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: end_frame
+//       Access:
+//  Description:  timer info, incs frame #
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::end_frame(void) {
+    GraphicsWindow::end_frame();
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_window_move
+//       Access:
+//  Description: we receive the new x and y position of the client
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::handle_window_move(int x, int y) {
+    _dxgsg->adjust_view_rect(x,y);
+    _props._xorg = x;
+    _props._yorg = y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_mouse_motion
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::handle_mouse_motion(int x, int y) {
+    _input_devices[0].set_pointer_in_window(x, y);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_mouse_entry
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::handle_mouse_entry(int state,HCURSOR hCursor) {
+    if(state == MOUSE_EXITED) {
+        _input_devices[0].set_pointer_out_of_window();
+    } else {
+//        SetCursor(hCursor);  believe this is not necessary, handled by windows
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_keypress
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::
+handle_keypress(ButtonHandle key, int x, int y) {
+/*
+    if(key.has_ascii_equivalent()) {
+        wdxdisplay_cat.spam() << key.get_ascii_equivalent() << endl;
+
+        short d1 = GetKeyState(VK_CONTROL);
+        short d2 = GetKeyState(VK_SHIFT);
+        wdxdisplay_cat.spam().flags(ios::hex | ios::uppercase );
+        wdxdisplay_cat.spam()  << " Control: " << ((d1 < 0)? "down" : "up") << "  Shift: " << ((d2 < 0)? "down" : "up") << endl;
+    }
+*/
+    _input_devices[0].set_pointer_in_window(x, y);
+    if(key != ButtonHandle::none()) {
+        _input_devices[0].button_down(key);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: handle_keyrelease
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::
+handle_keyrelease(ButtonHandle key) {
+    if(key != ButtonHandle::none()) {
+        _input_devices[0].button_up(key);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow::supports_update
+//       Access: Public, Virtual
+//  Description: Returns true if this particular kind of
+//               GraphicsWindow supports use of the update() function
+//               to update the graphics one frame at a time, so that
+//               the window does not need to be the program's main
+//               loop.  Returns false if the only way to update the
+//               window is to call main_loop().
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow::
+supports_update() const {
+    return true;
+}
+
+void INLINE process_1_event(void) {
+  MSG msg;
+
+  if(!GetMessage(&msg, NULL, 0, 0)) {
+      // WM_QUIT received
+      DestroyAllWindows(false);
+      exit(msg.wParam);  // this will invoke AtExitFn
+  }
+
+  // Translate virtual key messages
+  TranslateMessage(&msg);
+  // Call window_proc
+  DispatchMessage(&msg);
+}
+
+void INLINE wdxGraphicsWindow::process_events(void) {
+  if(_window_inactive) {
+      // Get 1 msg at a time until no more are left and we block and sleep,
+      // or message changes _return_control_to_app or _window_inactive status
+
+      while(_window_inactive && (!_return_control_to_app)) {
+          process_1_event();
+      }
+      _return_control_to_app = false;
+
+  } else {
+      MSG msg;
+
+      // handle all msgs on queue in a row
+      while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+          process_1_event();
+      }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: update
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::update(void) {
+#ifdef DO_PSTATS
+  _show_code_pcollector.stop();
+
+  if(!_window_inactive) {
+      PStatClient::main_tick();
+  }
+#endif
+
+  process_events();
+
+  if(_window_inactive) {
+      // note _window_inactive must be checked after process_events is called, to avoid draw_callback being called
+      if(_idle_callback)
+          call_idle_callback();
+      return;
+  }
+
+  call_draw_callback(true);
+
+  if(_idle_callback)
+    call_idle_callback();
+
+#ifdef DO_PSTATS
+  _show_code_pcollector.start();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: enable_mouse_input
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::enable_mouse_input(bool val) {
+    _mouse_input_enabled = val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: enable_mouse_motion
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::enable_mouse_motion(bool val) {
+    _mouse_motion_enabled = val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: enable_mouse_passive_motion
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow::enable_mouse_passive_motion(bool val) {
+    _mouse_passive_motion_enabled = val;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow::get_gsg_type
+//       Access: Public, Virtual
+//  Description: Returns the TypeHandle of the kind of GSG preferred
+//               by this kind of window.
+////////////////////////////////////////////////////////////////////
+TypeHandle wdxGraphicsWindow::
+get_gsg_type() const {
+    return DXGraphicsStateGuardian::get_class_type();
+}
+
+GraphicsWindow *wdxGraphicsWindow::
+make_wdxGraphicsWindow(const FactoryParams &params) {
+    GraphicsWindow::WindowPipe *pipe_param;
+    if(!get_param_into(pipe_param, params)) {
+        wdxdisplay_cat.error()
+        << "No pipe specified for window creation!" << endl;
+        return NULL;
+    }
+
+    GraphicsPipe *pipe = pipe_param->get_pipe();
+
+    GraphicsWindow::WindowProps *props_param;
+    if(!get_param_into(props_param, params)) {
+        return new wdxGraphicsWindow(pipe);
+    } else {
+        return new wdxGraphicsWindow(pipe, props_param->get_properties());
+    }
+}
+
+TypeHandle wdxGraphicsWindow::get_class_type(void) {
+    return _type_handle;
+}
+
+void wdxGraphicsWindow::init_type(void) {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "wdxGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+}
+
+TypeHandle wdxGraphicsWindow::get_type(void) const {
+    return get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: lookup_key
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+ButtonHandle wdxGraphicsWindow::
+lookup_key(WPARAM wparam) const {
+    switch(wparam) {
+        case VK_BACK: return KeyboardButton::backspace();
+        case VK_TAB: return KeyboardButton::tab();
+        case VK_ESCAPE: return KeyboardButton::escape();
+        case VK_SPACE: return KeyboardButton::space();
+        case VK_UP: return KeyboardButton::up();
+        case VK_DOWN: return KeyboardButton::down();
+        case VK_LEFT: return KeyboardButton::left();
+        case VK_RIGHT: return KeyboardButton::right();
+        case VK_PRIOR: return KeyboardButton::page_up();
+        case VK_NEXT: return KeyboardButton::page_down();
+        case VK_HOME: return KeyboardButton::home();
+        case VK_END: return KeyboardButton::end();
+        case VK_F1: return KeyboardButton::f1();
+        case VK_F2: return KeyboardButton::f2();
+        case VK_F3: return KeyboardButton::f3();
+        case VK_F4: return KeyboardButton::f4();
+        case VK_F5: return KeyboardButton::f5();
+        case VK_F6: return KeyboardButton::f6();
+        case VK_F7: return KeyboardButton::f7();
+        case VK_F8: return KeyboardButton::f8();
+        case VK_F9: return KeyboardButton::f9();
+        case VK_F10: return KeyboardButton::f10();
+        case VK_F11: return KeyboardButton::f11();
+        case VK_F12: return KeyboardButton::f12();
+        case VK_INSERT: return KeyboardButton::insert();
+        case VK_DELETE: return KeyboardButton::del();
+        case VK_CAPITAL: return KeyboardButton::caps_lock();
+        case VK_NUMLOCK: return KeyboardButton::num_lock();
+        case VK_SCROLL: return KeyboardButton::scroll_lock();
+        case VK_SNAPSHOT: return KeyboardButton::print_screen();
+
+        case VK_SHIFT:
+        case VK_LSHIFT:
+        case VK_RSHIFT:
+            return KeyboardButton::shift();
+
+        case VK_CONTROL:
+        case VK_LCONTROL:
+        case VK_RCONTROL:
+            return KeyboardButton::control();
+
+        case VK_MENU:
+        case VK_LMENU:
+        case VK_RMENU:
+            return KeyboardButton::alt();
+
+        default:
+            int key = MapVirtualKey(wparam, 2);
+            if(isascii(key) && key != 0) {
+                bool bCapsLockDown=((GetKeyState(VK_CAPITAL) & 0x1)!=0);
+                bool bShiftUp = (GetKeyState(VK_SHIFT) >= 0);
+                if(bShiftUp) {
+                    if(bCapsLockDown) 
+                        key = toupper(key);
+                    else key = tolower(key);
+                } else {
+                    switch(key) {
+                        // these keys are unaffected by capslock
+                        case '1': key = '!'; break;
+                        case '2': key = '@'; break;
+                        case '3': key = '#'; break;
+                        case '4': key = '$'; break;
+                        case '5': key = '%'; break;
+                        case '6': key = '^'; break;
+                        case '7': key = '&'; break;
+                        case '8': key = '*'; break;
+                        case '9': key = '('; break;
+                        case '0': key = ')'; break;
+                        case '-': key = '_'; break;
+                        case '=': key = '+'; break;
+                        case ',': key = '<'; break;
+                        case '.': key = '>'; break;
+                        case '/': key = '?'; break;
+                        case ';': key = ':'; break;
+                        case '\'': key = '"'; break;
+                        case '[': key = '{'; break;
+                        case ']': key = '}'; break;
+                        case '\\': key = '|'; break;
+                        case '`': key = '~'; break;
+                        default:
+                            if(bCapsLockDown) 
+                                key = tolower(key);
+                            else key = toupper(key);
+                    }
+                }
+                return KeyboardButton::ascii_key((uchar)key);
+            }
+            break;
+    }
+    return ButtonHandle::none();
+}
+
+int wdxGraphicsWindow::
+get_depth_bitwidth(void) {
+    if((_dxgsg==NULL) || (_dxgsg->_zbuf==NULL))
+      return -1;
+
+    return _depth_buffer_bpp;
+
+// this is not reliable, on GF2, GetSurfDesc returns 32bpp when you created a 24bpp zbuf
+//    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+//    _dxgsg->_zbuf->GetSurfaceDesc(&ddsd); 
+//  return ddsd.ddpfPixelFormat.dwRGBBitCount;
+}
+
+// Global system parameters we want to modify during our run
+static int iMouseTrails;
+static bool bCursorShadowOn,bMouseVanish;
+
+#ifndef SPI_GETMOUSEVANISH
+// get of this when we upgrade to winxp winuser.h
+#define SPI_GETMOUSEVANISH                  0x1020
+#define SPI_SETMOUSEVANISH                  0x1021
+#endif
+
+void set_global_parameters(void) {
+  // turn off mousetrails and cursor shadow and mouse cursor vanish
+  // cursor shadow causes cursor blink and reduced frame rate due to lack of driver support for 
+  // cursor alpha blending
+
+  // this is a win2k/xp only param, could use GetVersionEx to do it just for win2k,
+  // but since it is just a param it will just cause a silent error on other OS's
+  // that should be ok
+  SystemParametersInfo(SPI_GETCURSORSHADOW,NULL,&bCursorShadowOn,NULL);
+  SystemParametersInfo(SPI_SETCURSORSHADOW,NULL,(PVOID)false,NULL);
+
+  SystemParametersInfo(SPI_GETMOUSETRAILS,NULL,&iMouseTrails,NULL);
+  SystemParametersInfo(SPI_SETMOUSETRAILS,NULL,(PVOID)0,NULL);
+
+  // this is ME/XP only feature
+  SystemParametersInfo(SPI_GETMOUSEVANISH,NULL,&bMouseVanish,NULL);
+  SystemParametersInfo(SPI_SETMOUSEVANISH,NULL,(PVOID)false,NULL);
+}
+
+void restore_global_parameters(void) {
+  SystemParametersInfo(SPI_SETCURSORSHADOW,NULL,(PVOID)bCursorShadowOn,NULL);
+  SystemParametersInfo(SPI_SETMOUSETRAILS,NULL,(PVOID)iMouseTrails,NULL);
+  SystemParametersInfo(SPI_SETMOUSEVANISH,NULL,(PVOID)bMouseVanish,NULL);
+}
+

+ 144 - 0
panda/src/wdxdisplay8/wdxGraphicsWindow8.h

@@ -0,0 +1,144 @@
+// Filename: wdxGraphicsWindow.h
+// Created by:  mike (09Jan97)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+#ifndef WDXGRAPHICSWINDOW8_H
+#define WDXGRAPHICSWINDOW8_H
+//#define WBD_GL_MODE 1    // if setting this, do it in dxGraphicsStateGuardian.h too
+//
+////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////
+#include <pandabase.h>
+
+#include <graphicsWindow.h>
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#undef WINDOWS_LEAN_AND_MEAN
+#include <d3d.h>
+
+////////////////////////////////////////////////////////////////////
+// Defines
+////////////////////////////////////////////////////////////////////
+class wdxGraphicsPipe;
+
+const int WDXWIN_CONFIGURE =    4;
+const int WDXWIN_EVENT =    8;
+
+////////////////////////////////////////////////////////////////////
+//       Class : wdxGraphicsWindow
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX wdxGraphicsWindow : public GraphicsWindow {
+ friend class DXGraphicsStateGuardian;
+ friend class DXTextureContext;
+
+public:
+  wdxGraphicsWindow(GraphicsPipe* pipe);
+  wdxGraphicsWindow(GraphicsPipe* pipe,
+             const GraphicsWindow::Properties& props);
+  virtual ~wdxGraphicsWindow(void);
+
+  virtual bool supports_update() const;
+  virtual void update(void);
+  virtual void end_frame( void );
+
+  virtual TypeHandle get_gsg_type() const;
+  static GraphicsWindow* make_wdxGraphicsWindow(const FactoryParams &params);
+  void CreateScreenBuffersAndDevice(DWORD dwRenderWidth, DWORD dwRenderHeight,
+                                    LPDIRECTDRAW7 pDD,LPDIRECT3D7 pD3DI,
+                                    D3DDEVICEDESC7 *pD3DDevDesc);
+
+  LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  void process_events(void);
+
+  INLINE bool mouse_entry_enabled(void) { return _mouse_entry_enabled; }
+  INLINE bool mouse_motion_enabled(void) { return _mouse_motion_enabled; }
+  INLINE bool mouse_passive_motion_enabled(void) {
+    return _mouse_passive_motion_enabled;
+  }
+  void handle_window_move( int x, int y );
+  void handle_mouse_motion( int x, int y );
+  void handle_mouse_entry( int state, HCURSOR hMouseCursor );
+  void handle_keypress( ButtonHandle key, int x, int y );
+  void handle_keyrelease( ButtonHandle key);
+  void dx_setup();
+  virtual void begin_frame( void );
+  void show_frame();
+  DXGraphicsStateGuardian *_dxgsg;
+  
+  virtual void resize(unsigned int xsize,unsigned int ysize);
+  virtual unsigned int verify_window_sizes(unsigned int numsizes,unsigned int *dimen);
+  virtual int get_depth_bitwidth(void);
+
+protected:
+  void CreateScreenBuffersAndDevice(LPDIRECTDRAW7 pDD,LPDIRECT3D7 pD3DI);
+  ButtonHandle lookup_key(WPARAM wparam) const;
+  virtual void config( void );
+  void setup_colormap(void);
+
+  void enable_mouse_input(bool val);
+  void enable_mouse_motion(bool val);
+  void enable_mouse_passive_motion(bool val);
+  void enable_mouse_entry(bool val);
+  void check_for_color_cursor_support(void);
+  DDDEVICEIDENTIFIER2 _DXDeviceID;
+
+public:
+  HWND              _mwindow;
+  HWND              _hOldForegroundWindow;  
+  UINT_PTR          _PandaPausedTimer;
+
+private:
+  HDC               _hdc;
+  HPALETTE          _colormap;
+  typedef enum { NotAdjusting,MovingOrResizing,Resizing } WindowAdjustType;
+  WindowAdjustType _WindowAdjustingType;
+  bool              _bIsLowVidMemCard;
+  bool    _bLoadedCustomCursor;
+  HCURSOR _hMouseCursor;
+  bool    _bSizeIsMaximized;
+  bool              _mouse_input_enabled;
+  bool              _mouse_motion_enabled;
+  bool              _mouse_passive_motion_enabled;
+  bool              _mouse_entry_enabled;
+  bool              _exiting_window;
+  bool              _window_inactive;
+  bool              _active_minimized_fullscreen;
+  bool              _return_control_to_app;
+  int               _depth_buffer_bpp;
+
+public:
+  static TypeHandle get_class_type(void);
+  static void init_type(void);
+  virtual TypeHandle get_type(void) const;
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+  void DestroyMe(bool bAtExitFnCalled);
+  virtual void do_close_window();
+  void deactivate_window(void);
+  void reactivate_window(void);
+  void handle_reshape(bool bDoDXReset);
+
+private:
+  static TypeHandle _type_handle;
+};
+
+extern void set_global_parameters(void);
+extern void restore_global_parameters(void);
+
+
+#endif

+ 4 - 0
panda/src/wdxdisplay8/wdxdisplay8_composite1.cxx

@@ -0,0 +1,4 @@
+
+#include "config_wdxdisplay8.cxx"
+#include "wdxGraphicsPipe8.cxx"
+