Browse Source

Initial revision

David Rose 25 years ago
parent
commit
2fd714e52f
100 changed files with 11145 additions and 0 deletions
  1. 49 0
      panda/Config.Irix.pp
  2. 49 0
      panda/Config.Linux.pp
  3. 49 0
      panda/Config.Win32.pp
  4. 167 0
      panda/Config.pp
  5. 211 0
      panda/LocalSetup.pp
  6. 11 0
      panda/Package.pp
  7. 10 0
      panda/Sources.pp
  8. 933 0
      panda/acinclude.m4
  9. 7 0
      panda/metalibs/Sources.pp
  10. 25 0
      panda/metalibs/panda/Sources.pp
  11. 9 0
      panda/metalibs/panda/panda.cxx
  12. 21 0
      panda/metalibs/pandadx/Sources.pp
  13. 9 0
      panda/metalibs/pandadx/pandadx.cxx
  14. 19 0
      panda/metalibs/pandaegg/Sources.pp
  15. 9 0
      panda/metalibs/pandaegg/pandaegg.cxx
  16. 17 0
      panda/metalibs/pandaexpress/Sources.pp
  17. 9 0
      panda/metalibs/pandaexpress/pandaexpress.cxx
  18. 21 0
      panda/metalibs/pandagl/Sources.pp
  19. 9 0
      panda/metalibs/pandagl/pandagl.cxx
  20. 21 0
      panda/metalibs/pandaglut/Sources.pp
  21. 9 0
      panda/metalibs/pandaglut/pandaglut.cxx
  22. 19 0
      panda/metalibs/pandaphysics/Sources.pp
  23. 9 0
      panda/metalibs/pandaphysics/pandaphysics.cxx
  24. 21 0
      panda/metalibs/pandarib/Sources.pp
  25. 9 0
      panda/metalibs/pandarib/pandarib.cxx
  26. 212 0
      panda/src/PACKAGE-DESC
  27. 4 0
      panda/src/Sources.pp
  28. 49 0
      panda/src/audio/Sources.pp
  29. 15 0
      panda/src/audio/audio.h
  30. 75 0
      panda/src/audio/audio_load_midi.cxx
  31. 71 0
      panda/src/audio/audio_load_wav.cxx
  32. 69 0
      panda/src/audio/audio_manager.I
  33. 95 0
      panda/src/audio/audio_manager.cxx
  34. 47 0
      panda/src/audio/audio_manager.h
  35. 233 0
      panda/src/audio/audio_midi.cxx
  36. 26 0
      panda/src/audio/audio_midi.h
  37. 305 0
      panda/src/audio/audio_mikmod_traits.cxx
  38. 102 0
      panda/src/audio/audio_mikmod_traits.h
  39. 67 0
      panda/src/audio/audio_music.I
  40. 36 0
      panda/src/audio/audio_music.cxx
  41. 62 0
      panda/src/audio/audio_music.h
  42. 13 0
      panda/src/audio/audio_null_traits.I
  43. 70 0
      panda/src/audio/audio_null_traits.cxx
  44. 41 0
      panda/src/audio/audio_null_traits.h
  45. 141 0
      panda/src/audio/audio_pool.I
  46. 243 0
      panda/src/audio/audio_pool.cxx
  47. 63 0
      panda/src/audio/audio_pool.h
  48. 67 0
      panda/src/audio/audio_sample.I
  49. 45 0
      panda/src/audio/audio_sample.cxx
  50. 63 0
      panda/src/audio/audio_sample.h
  51. 48 0
      panda/src/audio/audio_trait.cxx
  52. 49 0
      panda/src/audio/audio_trait.h
  53. 28 0
      panda/src/audio/audio_win_traits.I
  54. 660 0
      panda/src/audio/audio_win_traits.cxx
  55. 78 0
      panda/src/audio/audio_win_traits.h
  56. 41 0
      panda/src/audio/config_audio.cxx
  57. 20 0
      panda/src/audio/config_audio.h
  58. 41 0
      panda/src/audio/test_audio.cxx
  59. 47 0
      panda/src/builder/Sources.pp
  60. 69 0
      panda/src/builder/builder.I
  61. 229 0
      panda/src/builder/builder.cxx
  62. 198 0
      panda/src/builder/builder.h
  63. 69 0
      panda/src/builder/builderAttrib.I
  64. 12 0
      panda/src/builder/builderAttrib.cxx
  65. 59 0
      panda/src/builder/builderAttrib.h
  66. 244 0
      panda/src/builder/builderAttribTempl.I
  67. 70 0
      panda/src/builder/builderAttribTempl.h
  68. 117 0
      panda/src/builder/builderBucket.I
  69. 240 0
      panda/src/builder/builderBucket.cxx
  70. 104 0
      panda/src/builder/builderBucket.h
  71. 92 0
      panda/src/builder/builderBucketNode.I
  72. 87 0
      panda/src/builder/builderBucketNode.cxx
  73. 58 0
      panda/src/builder/builderBucketNode.h
  74. 908 0
      panda/src/builder/builderFuncs.I
  75. 66 0
      panda/src/builder/builderFuncs.h
  76. 37 0
      panda/src/builder/builderMisc.cxx
  77. 18 0
      panda/src/builder/builderMisc.h
  78. 11 0
      panda/src/builder/builderNormalVisualizer.I
  79. 65 0
      panda/src/builder/builderNormalVisualizer.cxx
  80. 51 0
      panda/src/builder/builderNormalVisualizer.h
  81. 194 0
      panda/src/builder/builderPrim.cxx
  82. 119 0
      panda/src/builder/builderPrim.h
  83. 1025 0
      panda/src/builder/builderPrimTempl.I
  84. 143 0
      panda/src/builder/builderPrimTempl.h
  85. 116 0
      panda/src/builder/builderProperties.cxx
  86. 115 0
      panda/src/builder/builderProperties.h
  87. 102 0
      panda/src/builder/builderTypes.cxx
  88. 221 0
      panda/src/builder/builderTypes.h
  89. 265 0
      panda/src/builder/builderVertex.I
  90. 12 0
      panda/src/builder/builderVertex.cxx
  91. 119 0
      panda/src/builder/builderVertex.h
  92. 291 0
      panda/src/builder/builderVertexTempl.I
  93. 73 0
      panda/src/builder/builderVertexTempl.h
  94. 14 0
      panda/src/builder/config_builder.cxx
  95. 14 0
      panda/src/builder/config_builder.h
  96. 11 0
      panda/src/builder/mesher.cxx
  97. 42 0
      panda/src/builder/mesher.h
  98. 143 0
      panda/src/builder/mesherEdge.I
  99. 62 0
      panda/src/builder/mesherEdge.h
  100. 312 0
      panda/src/builder/mesherFanMaker.I

+ 49 - 0
panda/Config.Irix.pp

@@ -0,0 +1,49 @@
+//
+// Config.Irix.pp
+//
+// This file defines some custom config variables for the SGI/Irix
+// platform.  It makes some initial guesses about compiler features,
+// etc.
+//
+
+// Is the platform big-endian (like an SGI workstation) or
+// little-endian (like a PC)?  Define this to the empty string to
+// indicate little-endian, or nonempty to indicate big-endian.
+#define WORDS_BIGENDIAN 1
+
+// Does the C++ compiler support namespaces?
+#define HAVE_NAMESPACE 1
+
+// Does the C++ compiler support ios::binary?
+#define HAVE_IOS_BINARY
+
+// Do we have a gettimeofday() function?
+#define HAVE_GETTIMEOFDAY 1
+
+// Does gettimeofday() take only one parameter?
+#define GETTIMEOFDAY_ONE_PARAM
+
+// Do we have getopt() and/or getopt_long_only() built into the
+// system?
+#define HAVE_GETOPT 1
+#define HAVE_GETOPT_LONG_ONLY
+
+// Should we include <iostream> or <iostream.h>?  Define HAVE_IOSTREAM
+// to nonempty if we should use <iostream>, or empty if we should use
+// <iostream.h>.
+#define HAVE_IOSTREAM
+
+// Do we have a true stringstream class defined in <sstream>?
+#define HAVE_SSTREAM
+
+// Do we have <malloc.h>?
+#define HAVE_MALLOC_H 1
+
+// Do we have <alloca.h>?
+#define HAVE_ALLOCA_H 1
+
+// Do we have <sys/types.h>?
+#define HAVE_SYS_TYPES_H 1
+
+// Do we have <unistd.h>?
+#define HAVE_UNISTD_H 1

+ 49 - 0
panda/Config.Linux.pp

@@ -0,0 +1,49 @@
+//
+// Config.Linux.pp
+//
+// This file defines some custom config variables for the Linux
+// platform.  It makes some initial guesses about compiler features,
+// etc.
+//
+
+// Is the platform big-endian (like an SGI workstation) or
+// little-endian (like a PC)?  Define this to the empty string to
+// indicate little-endian, or nonempty to indicate big-endian.
+#define WORDS_BIGENDIAN
+
+// Does the C++ compiler support namespaces?
+#define HAVE_NAMESPACE 1
+
+// Does the C++ compiler support ios::binary?
+#define HAVE_IOS_BINARY 1
+
+// Do we have a gettimeofday() function?
+#define HAVE_GETTIMEOFDAY 1
+
+// Does gettimeofday() take only one parameter?
+#define GETTIMEOFDAY_ONE_PARAM
+
+// Do we have getopt() and/or getopt_long_only() built into the
+// system?
+#define HAVE_GETOPT 1
+#define HAVE_GETOPT_LONG_ONLY 1
+
+// Should we include <iostream> or <iostream.h>?  Define HAVE_IOSTREAM
+// to nonempty if we should use <iostream>, or empty if we should use
+// <iostream.h>.
+#define HAVE_IOSTREAM
+
+// Do we have a true stringstream class defined in <sstream>?
+#define HAVE_SSTREAM
+
+// Do we have <malloc.h>?
+#define HAVE_MALLOC_H 1
+
+// Do we have <alloca.h>?
+#define HAVE_ALLOCA_H 1
+
+// Do we have <sys/types.h>?
+#define HAVE_SYS_TYPES_H 1
+
+// Do we have <unistd.h>?
+#define HAVE_UNISTD_H 1

+ 49 - 0
panda/Config.Win32.pp

@@ -0,0 +1,49 @@
+//
+// Config.Win32.pp
+//
+// This file defines some custom config variables for the Windows
+// platform.  It makes some initial guesses about compiler features,
+// etc.
+//
+
+// Is the platform big-endian (like an SGI workstation) or
+// little-endian (like a PC)?  Define this to the empty string to
+// indicate little-endian, or nonempty to indicate big-endian.
+#define WORDS_BIGENDIAN
+
+// Does the C++ compiler support namespaces?
+#define HAVE_NAMESPACE 1
+
+// Does the C++ compiler support ios::binary?
+#define HAVE_IOS_BINARY 1
+
+// Do we have a gettimeofday() function?
+#define HAVE_GETTIMEOFDAY
+
+// Does gettimeofday() take only one parameter?
+#define GETTIMEOFDAY_ONE_PARAM
+
+// Do we have getopt() and/or getopt_long_only() built into the
+// system?
+#define HAVE_GETOPT
+#define HAVE_GETOPT_LONG_ONLY
+
+// Should we include <iostream> or <iostream.h>?  Define HAVE_IOSTREAM
+// to nonempty if we should use <iostream>, or empty if we should use
+// <iostream.h>.
+#define HAVE_IOSTREAM 1
+
+// Do we have a true stringstream class defined in <sstream>?
+#define HAVE_SSTREAM 1
+
+// Do we have <malloc.h>?
+#define HAVE_MALLOC_H 1
+
+// Do we have <alloca.h>?
+#define HAVE_ALLOCA_H
+
+// Do we have <sys/types.h>?
+#define HAVE_SYS_TYPES_H 1
+
+// Do we have <unistd.h>?
+#define HAVE_UNISTD_H

+ 167 - 0
panda/Config.pp

@@ -0,0 +1,167 @@
+//
+// Config.pp
+//
+// This file defines certain configuration variables that are written
+// into the various make scripts.  It is processed by ppremake (along
+// with the Sources.pp files in each of the various directories) to
+// generate build scripts appropriate to each environment.
+//
+// ppremake is capable of generating generic Unix autoconf/automake
+// style build scripts, as well as makefiles customized for SGI's
+// MipsPRO compiler or for Microsoft's Visual C++.  It can also
+// generate Microsoft Developer's Studio project files directly.  In
+// principle, it can be extended to generate suitable build script
+// files for any number of different build environments.
+//
+// All of these build scripts can be tuned for a particular
+// environment via this file.  This is the place for the user to
+// specify which external packages are installed and where, or to
+// enable or disable certain optional features.  However, it is
+// suggested that rather than modify this file directly, you create a
+// custom file in your home directory and there redefine whatever
+// variables are appropriate, and set the environment variable
+// PPREMAKE_CONFIG to refer to it.  In this way, you can easily get an
+// updated source tree (including a new Config.pp) without risking
+// accidentally losing your customizations.  This also avoids having
+// to redefine the same variables in different packages (for instance,
+// in dtool and in panda).
+//
+// If you *do* decide to make changes directly to this file, you
+// should also comment out the line near the bottom that includes the
+// file $[TOPDIRPREFIX]Config.$[PLATFORM].pp, to avoid stomping on the
+// changes you make.
+//
+// The syntax in this file resembles some hybrid between C++
+// preprocessor declarations and GNU make variables.  This is the same
+// syntax used in the various ppremake system configure files; it's
+// designed to be easy to use as a macro language to generate
+// makefiles and their ilk.
+// 
+
+// What kind of build scripts are we generating?  This selects a
+// suitable template file from the ppremake system files.  The
+// allowable choices, at present, are:
+//
+//  autoconf  - Generate configure.in and a series of Makefile.am files,
+//              suitable for using with autoconf/automake.  Not quite
+//              there yet.
+//  stopgap   - Generate original Cary-style Makefile/Makefile.install/etc.
+//              files, to ease transition to the new system.
+//
+#define BUILD_TYPE stopgap
+
+// Define the directory in which the system ppremake files are
+// installed.
+#define PPREMAKE_DIR /usr/local/panda/share
+
+
+// In which directory should this package be installed when you type
+// "make install"?  This has no meaning when BUILD_TYPE is "stopgap".
+#define INSTALL_DIR /usr/local/panda
+
+
+// What level of compiler optimization/debug symbols should we build?
+// The various optimize levels are defined as follows:
+//
+//   1 - No compiler optimizations, full debug symbols
+//   2 - Full compiler optimizations, full debug symbols
+//         (if the compiler supports this)
+//   3 - Full compiler optimizations, no debug symbols
+//   4 - Full optimizations, no debug symbols, and asserts removed
+//
+// Setting this has no effect when BUILD_TYPE is "stopgap".  In this
+// case, the compiler optimizations are selected by setting the
+// environment variable OPTIMIZE accordingly at compile time.
+#define OPTIMIZE 1
+
+
+
+////////////////////////////////////////////////////////////////////
+// The remaining variables are considered only if BUILD_TYPE is not
+// "autoconf".  (Autoconf can determine these directly.)
+////////////////////////////////////////////////////////////////////
+
+// NOTE: In the following, to indicate "yes" to a yes/no question,
+// define the variable to be a nonempty string.  To indicate "no",
+// define the variable to be an empty string.
+
+// Is Python installed, and should Python interfaces be generated?  If
+// Python is installed, which directory is it in?  (If the directory
+// is someplace standard like /usr/include, you may leave it blank.)
+#define HAVE_PYTHON 1
+#define PYTHON_INCLUDE /usr/local/include/python1.6
+#define PYTHON_LIB
+
+// Is NSPR installed, and where?
+#define HAVE_NSPR 1
+#define NSPR_INCLUDE /usr/local/mozilla/dist/*/include
+#define NSPR_LIB
+
+// Is VRPN installed, and where?
+#define HAVE_VRPN
+#define VRPN_INCLUDE
+#define VRPN_LIB
+
+// Is ZLIB installed, and where?
+#define HAVE_ZLIB 1
+#define ZLIB_INCLUDE
+#define ZLIB_LIB
+
+// Is OpenGL installed, and where?
+#define HAVE_GL 1
+#define GL_INCLUDE
+#define GL_LIB
+#define GLU_INCLUDE
+#define GLU_LIB
+
+// How about GLX?
+#define HAVE_GLX 1
+#define GLX_INCLUDE
+#define GLX_LIB
+
+// Glut?
+#define HAVE_GLUT
+#define GLUT_INCLUDE
+#define GLUT_LIB
+
+// Should we try to build the WGL interface?
+#define HAVE_WGL
+
+// Should we try to build the DirectX interface?
+#define HAVE_DX
+
+// Do you want to build the Renderman interface?
+#define HAVE_RIB
+
+// Is Mikmod installed?
+#define HAVE_MIKMOD
+#define MIKMOD_CFLAGS
+#define MIKMOD_INCLUDE
+#define MIKMOD_LIB
+
+
+//////////////////////////////////////////////////////////////////////
+// There are also some additional variables that control specific
+// compiler/platform features or characteristics, defined in the
+// platform specific file Config.platform.pp.  Be sure to inspect
+// these variables for correctness too.  As above, these are
+// unnecessary when BUILD_TYPE is "autoconf".
+//////////////////////////////////////////////////////////////////////
+#include $[TOPDIRPREFIX]Config.$[PLATFORM].pp
+
+
+// Also pull in whatever package-specific variables there may be.
+#include $[TOPDIRPREFIX]Package.pp
+
+
+// If the environment variable PPREMAKE_CONFIG is set, it points to a
+// user-customized Config.pp file, for instance in the user's home
+// directory.  This file might redefine any of the variables defined
+// above.
+#if $[ne $[PPREMAKE_CONFIG],]
+  #include $[PPREMAKE_CONFIG]
+#endif
+
+
+// Finally, include the system configure file.
+#include $[PPREMAKE_DIR]/System.pp

+ 211 - 0
panda/LocalSetup.pp

@@ -0,0 +1,211 @@
+//
+// LocalSetup.pp
+//
+// This file contains further instructions to set up the DTOOL package
+// when using ppremake.  In particular, it creates the dtool_config.h
+// file based on the user's selected configure variables.  This script
+// need not execute when BUILD_TYPE is "autoconf"; in this case, the
+// dtool_config.h file will automatically be correctly generated by
+// configure.
+//
+
+#if $[ne $[BUILD_TYPE],autoconf]
+
+// A couple of variables to output the C #define and #undef
+// directives, since we can't type them literally.
+#define define #define
+#define undef #undef
+
+#output dtool_config.h
+#format straight
+/* dtool_config.h.  Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE]. */
+
+/* Define if you have the ANSI C header files.  */
+$[define] STDC_HEADERS 1
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#if $[WORDS_BIGENDIAN]
+$[define] WORDS_BIGENDIAN 1
+#else
+$[undef] WORDS_BIGENDIAN
+#endif
+
+/* Define if the X Window System is missing or not being used.  */
+$[undef] X_DISPLAY_MISSING
+
+/* Define if lex declares yytext as a char * by default, not a char[].  */
+$[undef] YYTEXT_POINTER
+
+/* Define if the C++ compiler uses namespaces.  */
+#if $[HAVE_NAMESPACE]
+$[define] HAVE_NAMESPACE 1
+#else
+$[undef] HAVE_NAMESPACE
+#endif
+
+/* Define if the C++ iostream library supports ios::binary.  */
+#if $[HAVE_IOS_BINARY]
+$[define] HAVE_IOS_BINARY 1
+#else
+$[undef] HAVE_IOS_BINARY
+#endif
+
+/* Define if we have Python installed.  */
+#if $[HAVE_PYTHON]
+$[define] HAVE_PYTHON 1
+#else
+$[undef] HAVE_PYTHON
+#endif
+
+/* Define if we have NSPR installed.  */
+#if $[HAVE_NSPR]
+$[define] HAVE_NSPR 1
+#else
+$[undef] HAVE_NSPR
+#endif
+
+/* Define if we have VRPN installed.  */
+#if $[HAVE_VRPN]
+$[define] HAVE_VRPN 1
+#else
+$[undef] HAVE_VRPN
+#endif
+
+/* Define if we have zlib installed.  */
+#if $[HAVE_ZLIB]
+$[define] HAVE_ZLIB 1
+#else
+$[undef] HAVE_ZLIB
+#endif
+
+/* Define if we have OpenGL installed and want to build for GL.  */
+#if $[HAVE_GL]
+$[define] HAVE_GL 1
+#else
+$[undef] HAVE_GL
+#endif
+
+/* Define if we have GLU installed.  */
+#if $[HAVE_GLU]
+$[define] HAVE_GLU 1
+#else
+$[undef] HAVE_GLU
+#endif
+
+/* Define if we have GLX installed and want to build for GLX.  */
+#if $[HAVE_GLX]
+$[define] HAVE_GLX 1
+#else
+$[undef] HAVE_GLX
+#endif
+
+/* Define if we have Glut installed and want to build for Glut.  */
+#if $[HAVE_GLUT]
+$[define] HAVE_GLUT 1
+#else
+$[undef] HAVE_GLUT
+#endif
+
+/* Define if we want to build the Renderman interface.  */
+#if $[HAVE_RIB]
+$[define] HAVE_RIB 1
+#else
+$[undef] HAVE_RIB
+#endif
+
+/* Define if we want to use mikmod for audio.  */
+#if $[HAVE_MIKMOD]
+$[define] HAVE_MIKMOD 1
+#else
+$[undef] HAVE_MIKMOD
+#endif
+
+/* Define if we have a gettimeofday() function. */
+#if $[HAVE_GETTIMEOFDAY]
+$[define] HAVE_GETTIMEOFDAY 1
+#else
+$[undef] HAVE_GETTIMEOFDAY
+#endif
+
+/* Define if gettimeofday() takes only one parameter. */
+#if $[GETTIMEOFDAY_ONE_PARAM]
+$[define] GETTIMEOFDAY_ONE_PARAM 1
+#else
+$[undef] GETTIMEOFDAY_ONE_PARAM
+#endif
+
+/* Define if you have the getopt function.  */
+#if $[HAVE_GETOPT]
+$[define] HAVE_GETOPT 1
+#else
+$[undef] HAVE_GETOPT
+#endif
+
+/* Define if you have the getopt_long_only function.  */
+#if $[HAVE_GETOPT_LONG_ONLY]
+$[define] HAVE_GETOPT_LONG_ONLY 1
+#else
+$[undef] HAVE_GETOPT_LONG_ONLY
+#endif
+
+/* Define if you have the <alloca.h> header file.  */
+$[define] HAVE_ALLOCA_H 1
+
+/* Define if you have the <io.h> header file.  */
+$[undef] HAVE_IO_H
+
+/* Define if you have the <iostream> header file.  */
+#if $[HAVE_IOSTREAM]
+$[define] HAVE_IOSTREAM 1
+#else
+$[undef] HAVE_IOSTREAM
+#endif
+
+/* Define if you have the <malloc.h> header file.  */
+#if $[HAVE_MALLOC_H]
+$[define] HAVE_MALLOC_H 1
+#else
+$[undef] HAVE_MALLOC_H
+#endif
+
+/* Define if you have the <alloca.h> header file.  */
+#if $[HAVE_ALLOCA_H]
+$[define] HAVE_ALLOCA_H 1
+#else
+$[undef] HAVE_ALLOCA_H
+#endif
+
+/* Define if you have the <minmax.h> header file.  */
+$[undef] HAVE_MINMAX_H
+
+/* Define if you have the <sstream> header file.  */
+#if $[HAVE_SSTREAM]
+$[define] HAVE_SSTREAM 1
+#else
+$[undef] HAVE_SSTREAM
+#endif
+
+/* Define if you have the <sys/types.h> header file.  */
+#if $[HAVE_SYS_TYPES]
+$[define] HAVE_SYS_TYPES 1
+#else
+$[undef] HAVE_SYS_TYPES
+#endif
+
+/* Define if you have the <unistd.h> header file.  */
+#if $[HAVE_UNISTD_H]
+$[define] HAVE_UNISTD_H 1
+#else
+$[undef] HAVE_UNISTD_H
+#endif
+
+/* Name of package */
+$[define] PACKAGE $[PACKAGE]
+
+/* Version number of package */
+$[define] VERSION $[VERSION]
+
+#end dtool_config.h
+
+#endif   // BUILD_TYPE

+ 11 - 0
panda/Package.pp

@@ -0,0 +1,11 @@
+//
+// Package.pp
+//
+// This file defines a few more configuration variables that are
+// inconvenient to store in Config.pp, simply because they are more
+// specific to a particular package and not likely to be edited by a
+// configuring user.
+
+// What is the name and version of this source tree?
+#define PACKAGE panda
+#define VERSION 0.80

+ 10 - 0
panda/Sources.pp

@@ -0,0 +1,10 @@
+// This is the toplevel directory.  It contains configure.in and other
+// stuff.
+
+#define DIR_TYPE toplevel
+
+#define SAMPLE_SOURCE_FILE src/pandabase/pandabase.cxx
+#define REQUIRED_TREES dtool
+
+#define EXTRA_DIST \
+    Config.Irix.pp Config.Linux.pp Config.Win32.pp LocalSetup.pp Package.pp

+ 933 - 0
panda/acinclude.m4

@@ -0,0 +1,933 @@
+AC_DEFUN(AC_GETTIMEOFDAY,
+[AC_CACHE_CHECK([for gettimeofday()],
+  ac_cv_gettimeofday,
+[
+  AC_TRY_COMPILE([
+#include <sys/time.h>
+  ],[
+    gettimeofday((struct timeval *)NULL, (struct timezone *)NULL);
+  ], ac_cv_gettimeofday="2 params", ac_cv_gettimeofday=no)
+  if test "$ac_cv_gettimeofday" = no; then
+    AC_TRY_COMPILE([
+#include <sys/time.h>
+    ],[
+      gettimeofday((struct timeval *)NULL, (struct timezone *)NULL);
+    ], ac_cv_gettimeofday="1 param", ac_cv_gettimeofday=no)
+  fi
+])
+if test "$ac_cv_gettimeofday" = "1 param"; then
+  AC_DEFINE(GETTIMEOFDAY_ONE_PARAM)
+  AC_DEFINE(HAVE_GETTIMEOFDAY)
+elif test "$ac_cv_gettimeofday" = "2 params"; then
+  AC_DEFINE(HAVE_GETTIMEOFDAY)
+fi
+])
+
+
+AC_DEFUN(AC_HEADER_IOSTREAM,
+[AC_CHECK_HEADERS(iostream,[have_iostream=yes],[have_iostream=no])])
+
+AC_DEFUN(AC_IOS_BINARY,
+[AC_CACHE_CHECK([for ios::binary],
+  ac_cv_ios_binary,
+[
+if test $have_iostream = yes; then
+  AC_TRY_COMPILE([
+#include <iostream>
+  ],[
+  int x; x = ios::binary;
+  ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no)
+else
+  AC_TRY_COMPILE([
+#include <iostream.h>
+  ],[
+  int x; x = ios::binary;
+  ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no)
+fi
+
+])
+if test $ac_cv_ios_binary = yes; then
+  AC_DEFINE(HAVE_IOS_BINARY)
+fi
+])
+
+
+AC_DEFUN(AC_NAMESPACE,
+[AC_CACHE_CHECK([for compiler namespace support],
+  ac_cv_namespace,
+[AC_TRY_COMPILE(
+[namespace std { };
+using namespace std;],
+[],
+  ac_cv_namespace=yes, ac_cv_namespace=no)])
+if test $ac_cv_namespace = yes; then
+  AC_DEFINE(HAVE_NAMESPACE)
+fi
+])
+
+
+dnl A handy function to see if a library is in a particular directory.
+dnl AC_CHECK_LIB_LOC(directory, library, function, action-if-found, action-if-not-found, other-libraries)
+dnl
+AC_DEFUN(AC_CHECK_LIB_LOC,
+[AC_MSG_CHECKING([for lib$2 in $1])
+ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
+AC_CACHE_VAL(ac_cv_lib_loc_$ac_lib_var,
+[ac_save_LIBS="$LIBS"
+LIBS="-L$1 -l$2 $6 $LIBS"
+AC_TRY_LINK(dnl
+ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus
+extern "C"
+#endif
+])dnl
+[/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $3();
+],
+            [$3()],
+            eval "ac_cv_lib_loc_$ac_lib_var=yes",
+            eval "ac_cv_lib_loc_$ac_lib_var=no")
+LIBS="$ac_save_LIBS"
+])dnl
+if eval "test \"`echo '$ac_cv_lib_loc_'$ac_lib_var`\" = yes"; then
+  AC_MSG_RESULT(yes)
+  ifelse([$4], ,
+[LIBS="-L$1 -l$2 $LIBS"
+], [$4])
+else
+  AC_MSG_RESULT(no)
+ifelse([$5], , , [$5
+])dnl
+fi
+])
+
+dnl A handy function to search a number of possible locations for a library.
+dnl AC_SEARCH_LIB(search-dirs, library, function, package, other-libraries)
+dnl 
+dnl Sets $package_LIB to the directory containing the library, or to the
+dnl string "no" if the library cannot be found.
+AC_DEFUN(AC_SEARCH_LIB, [
+ac_found_lib="no"
+if test "$1" = ""; then
+  AC_CHECK_LIB($2, $3, [ ac_found_lib=""; ],, $5)
+else
+  for ac_check_dir in $1; do
+    if test "$ac_found_lib" = "no"; then
+      AC_CHECK_LIB_LOC($ac_check_dir, $2, $3, [ ac_found_lib="$ac_check_dir"; ],, $5)
+    fi
+  done
+fi
+$4_LIB="$ac_found_lib"
+])
+
+dnl A handy function to see if a header file is in a particular directory.
+dnl AC_CHECK_HEADER_LOC(directory, header, action-if-found, action-if-not-found)
+dnl
+AC_DEFUN(AC_CHECK_HEADER_LOC, [
+  AC_MSG_CHECKING([for $2 in $1])
+  ac_include_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
+  AC_CACHE_VAL(ac_cv_include_loc_$ac_include_var, [
+    ac_save_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="-I$1 $CPPFLAGS"
+    AC_TRY_CPP([#include <$2>], ac_ch_found_it="yes", ac_ch_found_it="no")
+    if test "$ac_ch_found_it" = "yes"; then
+      AC_MSG_RESULT(yes)
+      ifelse([$3], , :, [$3])
+    else
+      AC_MSG_RESULT(no)
+      ifelse([$4], , , [$4])
+    fi
+    CPPFLAGS="$ac_save_CPPFLAGS"
+  ])
+])
+
+
+dnl A handy function to search a number of possible locations for a header
+dnl file.
+dnl
+dnl AC_SEARCH_HEADER(search-dirs, header, package)
+dnl
+dnl Sets $package_INCLUDE to the directory containing the header, or to
+dnl the string "no" if the header cannot be found.
+AC_DEFUN(AC_SEARCH_HEADER, [
+ac_found_header="no"
+if test "$1" = ""; then
+  AC_CHECK_HEADER($2, [ ac_found_header="";])
+else
+  for ac_check_dir in $1; do
+    if test "$ac_found_header" = "no"; then
+      AC_CHECK_HEADER_LOC($ac_check_dir, $2, [ ac_found_header="$ac_check_dir";])
+    fi
+  done
+fi
+$3_INCLUDE="$ac_found_header"
+])
+
+
+dnl A handy function to scan for a third-party package, consisting of at
+dnl least a library and an include file.  A few assumptions are made about
+dnl the relationships between lib and include directories.
+dnl
+dnl AC_SEARCH_PACKAGE(search-dirs, package-names, header, library, function, package, other-libraries)
+dnl
+dnl search-dirs is the whitespace-separated list of directory prefixes to
+dnl   check.
+dnl package-names is a whitespace-separated list of possible names the
+dnl   package may have been installed under.
+dnl
+dnl For each combination of ${search-dir} and ${package-name}, the following
+dnl directories are searched:
+dnl
+dnl   ${search-dir}
+dnl   ${search-dir}/lib
+dnl   ${search-dir}/${package-name}
+dnl   ${search-dir}/${package-name}/lib
+dnl   ${search-dir}/lib/${package-name}
+dnl
+dnl And similarly for include.
+dnl
+dnl Sets the variables $package_INCLUDE and $package_LIB to the directories
+dnl containing the header file and library, respectively.  If both pieces
+dnl are located, also sets the variable $package_PKG to "yes"; otherwise,
+dnl sets the variable $package_PKG to "no".
+dnl
+AC_DEFUN(AC_SEARCH_PACKAGE, [
+$6_LIB="no"
+$6_INCLUDE="no"
+$6_PKG="no"
+
+dnl Look for the library.
+AC_SEARCH_LIB("", $4, $5, $6, $7)
+for ac_sp_dir in $1; do
+  if test "[$]$6_LIB" = "no"; then
+    AC_SEARCH_LIB("$ac_sp_dir" "$ac_sp_dir/lib", $4, $5, $6, $7)
+    for ac_sp_pkg in $2; do
+      if test "[$]$6_LIB" = "no"; then
+        AC_SEARCH_LIB("$ac_sp_dir/$ac_sp_pkg" "$ac_sp_dir/$ac_sp_pkg/lib" \
+                      "$ac_sp_dir/lib/$ac_sp_pkg", $4, $5, $6, $7)
+      fi
+    done
+  fi
+done
+
+dnl Now look for the header file.  Don't bother looking if the library
+dnl wasn't found.
+if test "[$]$6_LIB" != "no"; then
+  dnl First look in the obvious directory corresponding to the lib dir.
+  ac_sp_testinc=`echo [$]$6_LIB | sed 's:/lib:/include:'`
+  AC_SEARCH_HEADER("$ac_sp_testinc", $3, $6)
+
+  dnl If it wasn't found there, cast about.
+  if test "[$]$6_INCLUDE" = "no"; then
+    for ac_sp_dir in $1; do
+      if test "[$]$6_INCLUDE" = "no"; then
+        AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $6)
+        for ac_sp_pkg in $2; do
+          if test "[$]$6_INCLUDE" = "no"; then
+            AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \
+                             "$ac_sp_dir/$ac_sp_pkg/include" \
+                             "$ac_sp_dir/include/$ac_sp_pkg", $3, $6)
+          fi
+        done
+      fi
+    done
+  fi
+
+  dnl If we got both a header and a library, set the PKG variable.
+  if test "[$]$6_INCLUDE" != "no"; then
+    $6_PKG="yes"
+  fi
+fi
+])  
+
+dnl A handy function, similar to AC_SEARCH_PACKAGE, above, that looks for a
+dnl package that only consists of header files, no libraries.
+dnl
+dnl AC_SEARCH_HPACKAGE(search-dirs, package-names, header, package)
+dnl
+dnl search-dirs is the whitespace-separated list of directory prefixes to
+dnl   check.
+dnl package-names is a whitespace-separated list of possible names the
+dnl   package may have been installed under.
+dnl
+dnl For each combination of ${search-dir} and ${package-name}, the following
+dnl directories are searched:
+dnl
+dnl   ${search-dir}
+dnl   ${search-dir}/include
+dnl   ${search-dir}/${package-name}
+dnl   ${search-dir}/${package-name}/include
+dnl   ${search-dir}/include/${package-name}
+dnl
+dnl Sets the variable $package_INCLUDE to the directory containing the
+dnl header file, and sets the variable $package_PKG to "yes"; if the
+dnl directory is not found, sets the variable $package_PKG to "no".
+dnl
+AC_DEFUN(AC_SEARCH_HPACKAGE, [
+$4_INCLUDE="no"
+$4_PKG="no"
+
+dnl Look for the header.
+AC_SEARCH_HEADER("", $3, $4)
+for ac_sp_dir in $1; do
+  if test "[$]$4_INCLUDE" = "no"; then
+    AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $4)
+    for ac_sp_pkg in $2; do
+      if test "[$]$4_INCLUDE" = "no"; then
+        AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \
+                         "$ac_sp_dir/$ac_sp_pkg/include" \
+                         "$ac_sp_dir/include/$ac_sp_pkg", $3, $4)
+      fi
+    done
+  fi
+done
+
+dnl If we got both a header file, set the PKG variable.
+if test "[$]$4_INCLUDE" != "no"; then
+  $4_PKG="yes"
+fi
+])  
+
+# Define a conditional.
+
+AC_DEFUN(AM_CONDITIONAL,
+[AC_SUBST($1_TRUE)
+AC_SUBST($1_FALSE)
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi])
+
+
+
+
+# Configure paths for GTK--
+# Erik Andersen	30 May 1998
+# Modified by Tero Pulkkinen (added the compiler checks... I hope they work..)
+
+dnl Test for GTKMM, and define GTKMM_CFLAGS and GTKMM_LIBS
+dnl   to be used as follows:
+dnl AM_PATH_GTKMM([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl
+AC_DEFUN(AM_PATH_GTKMM,
+[dnl 
+dnl Get the cflags and libraries from the gtkmm-config script
+dnl
+AC_ARG_WITH(gtkmm-prefix,[  --with-gtkmm-prefix=PREFIX
+                          Prefix where GTK-- is installed (optional)],
+            gtkmm_config_prefix="$withval", gtkmm_config_prefix="")
+AC_ARG_WITH(gtkmm-exec-prefix,[  --with-gtkmm-exec-prefix=PREFIX
+                          Exec prefix where GTK-- is installed (optional)],
+            gtkmm_config_exec_prefix="$withval", gtkmm_config_exec_prefix="")
+AC_ARG_ENABLE(gtkmmtest, [  --disable-gtkmmtest     Do not try to compile and run a test GTK-- program],
+		    , enable_gtkmmtest=yes)
+
+  if test x$gtkmm_config_exec_prefix != x ; then
+     gtkmm_config_args="$gtkmm_config_args --exec-prefix=$gtkmm_config_exec_prefix"
+     if test x${GTKMM_CONFIG+set} != xset ; then
+        GTKMM_CONFIG=$gtkmm_config_exec_prefix/bin/gtkmm-config
+     fi
+  fi
+  if test x$gtkmm_config_prefix != x ; then
+     gtkmm_config_args="$gtkmm_config_args --prefix=$gtkmm_config_prefix"
+     if test x${GTKMM_CONFIG+set} != xset ; then
+        GTKMM_CONFIG=$gtkmm_config_prefix/bin/gtkmm-config
+     fi
+  fi
+
+  AC_PATH_PROG(GTKMM_CONFIG, gtkmm-config, no)
+  min_gtkmm_version=ifelse([$1], ,0.10.0,$1)
+
+  AC_MSG_CHECKING(for GTK-- - version >= $min_gtkmm_version)
+  no_gtkmm=""
+  if test "$GTKMM_CONFIG" = "no" ; then
+    no_gtkmm=yes
+  else
+    AC_LANG_SAVE
+    AC_LANG_CPLUSPLUS
+
+    GTKMM_CFLAGS=`$GTKMM_CONFIG $gtkmm_config_args --cflags`
+    GTKMM_LIBS=`$GTKMM_CONFIG $gtkmm_config_args --libs`
+    gtkmm_config_major_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    gtkmm_config_minor_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    gtkmm_config_micro_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_gtkmmtest" = "xyes" ; then
+      ac_save_CXXFLAGS="$CXXFLAGS"
+      ac_save_LIBS="$LIBS"
+      CXXFLAGS="$CXXFLAGS $GTKMM_CFLAGS"
+      LIBS="$LIBS $GTKMM_LIBS"
+dnl
+dnl Now check if the installed GTK-- is sufficiently new. (Also sanity
+dnl checks the results of gtkmm-config to some extent
+dnl
+      rm -f conf.gtkmmtest
+      AC_TRY_RUN([
+#include <gtk--.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int 
+main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.gtkmmtest");
+
+  /* HP/UX 0 (%@#!) writes to sscanf strings */
+  tmp_version = g_strdup("$min_gtkmm_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_gtkmm_version");
+     exit(1);
+   }
+
+  if ((gtkmm_major_version != $gtkmm_config_major_version) ||
+      (gtkmm_minor_version != $gtkmm_config_minor_version) ||
+      (gtkmm_micro_version != $gtkmm_config_micro_version))
+    {
+      printf("\n*** 'gtkmm-config --version' returned %d.%d.%d, but GTK-- (%d.%d.%d)\n", 
+             $gtkmm_config_major_version, $gtkmm_config_minor_version, $gtkmm_config_micro_version,
+             gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version);
+      printf ("*** was found! If gtkmm-config was correct, then it is best\n");
+      printf ("*** to remove the old version of GTK--. You may also be able to fix the error\n");
+      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+      printf("*** required on your system.\n");
+      printf("*** If gtkmm-config was wrong, set the environment variable GTKMM_CONFIG\n");
+      printf("*** to point to the correct copy of gtkmm-config, and remove the file config.cache\n");
+      printf("*** before re-running configure\n");
+    } 
+/* GTK-- does not have the GTKMM_*_VERSION constants */
+/* 
+  else if ((gtkmm_major_version != GTKMM_MAJOR_VERSION) ||
+	   (gtkmm_minor_version != GTKMM_MINOR_VERSION) ||
+           (gtkmm_micro_version != GTKMM_MICRO_VERSION))
+    {
+      printf("*** GTK-- header files (version %d.%d.%d) do not match\n",
+	     GTKMM_MAJOR_VERSION, GTKMM_MINOR_VERSION, GTKMM_MICRO_VERSION);
+      printf("*** library (version %d.%d.%d)\n",
+	     gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version);
+    }
+*/
+  else
+    {
+      if ((gtkmm_major_version > major) ||
+        ((gtkmm_major_version == major) && (gtkmm_minor_version > minor)) ||
+        ((gtkmm_major_version == major) && (gtkmm_minor_version == minor) && (gtkmm_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of GTK-- (%d.%d.%d) was found.\n",
+               gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version);
+        printf("*** You need a version of GTK-- newer than %d.%d.%d. The latest version of\n",
+	       major, minor, micro);
+        printf("*** GTK-- is always available from ftp://ftp.gtk.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the gtkmm-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of GTK--, but you can also set the GTKMM_CONFIG environment to point to the\n");
+        printf("*** correct copy of gtkmm-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+      }
+    }
+  return 1;
+}
+],, no_gtkmm=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CXXFLAGS="$ac_save_CXXFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_gtkmm" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$GTKMM_CONFIG" = "no" ; then
+       echo "*** The gtkmm-config script installed by GTK-- could not be found"
+       echo "*** If GTK-- was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GTKMM_CONFIG environment variable to the"
+       echo "*** full path to gtkmm-config."
+       echo "*** The gtkmm-config script was not available in GTK-- versions"
+       echo "*** prior to 0.9.12. Perhaps you need to update your installed"
+       echo "*** version to 0.9.12 or later"
+     else
+       if test -f conf.gtkmmtest ; then
+        :
+       else
+          echo "*** Could not run GTK-- test program, checking why..."
+          CXXFLAGS="$CFLAGS $GTKMM_CXXFLAGS"
+          LIBS="$LIBS $GTKMM_LIBS"
+          AC_TRY_LINK([
+#include <gtk--.h>
+#include <stdio.h>
+],      [ return ((gtkmm_major_version) || (gtkmm_minor_version) || (gtkmm_micro_version)); ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding GTK-- or finding the wrong"
+          echo "*** version of GTK--. If it is not finding GTK--, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+	  echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means GTK-- was incorrectly installed"
+          echo "*** or that you have moved GTK-- since it was installed. In the latter case, you"
+          echo "*** may want to edit the gtkmm-config script: $GTKMM_CONFIG" ])
+          CXXFLAGS="$ac_save_CXXFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     GTKMM_CFLAGS=""
+     GTKMM_LIBS=""
+     ifelse([$3], , :, [$3])
+     AC_LANG_RESTORE
+  fi
+  AC_SUBST(GTKMM_CFLAGS)
+  AC_SUBST(GTKMM_LIBS)
+  rm -f conf.gtkmmtest
+])
+
+
+## libtool.m4 - Configure libtool for the target system. -*-Shell-script-*-
+## Copyright (C) 1996-1999 Free Software Foundation, Inc.
+## Originally by Gordon Matzigkeit <[email protected]>, 1996
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+##
+## As a special exception to the GNU General Public License, if you
+## distribute this file as part of a program that contains a
+## configuration script generated by Autoconf, you may include it under
+## the same distribution terms that you use for the rest of that program.
+
+# serial 40 AC_PROG_LIBTOOL
+AC_DEFUN(AC_PROG_LIBTOOL,
+[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl
+
+# Save cache, so that ltconfig can load it
+AC_CACHE_SAVE
+
+# Actually configure libtool.  ac_aux_dir is where install-sh is found.
+CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" \
+LD="$LD" LDFLAGS="$LDFLAGS" LIBS="$LIBS" \
+LN_S="$LN_S" NM="$NM" RANLIB="$RANLIB" \
+DLLTOOL="$DLLTOOL" AS="$AS" OBJDUMP="$OBJDUMP" \
+${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \
+$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \
+|| AC_MSG_ERROR([libtool configure failed])
+
+# Reload cache, that may have been modified by ltconfig
+AC_CACHE_LOAD
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ac_aux_dir/ltconfig $ac_aux_dir/ltmain.sh"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+# Redirect the config.log output again, so that the ltconfig log is not
+# clobbered by the next message.
+exec 5>>./config.log
+])
+
+AC_DEFUN(AC_LIBTOOL_SETUP,
+[AC_PREREQ(2.13)dnl
+AC_REQUIRE([AC_ENABLE_SHARED])dnl
+AC_REQUIRE([AC_ENABLE_STATIC])dnl
+AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([AC_PROG_RANLIB])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_LD])dnl
+AC_REQUIRE([AC_PROG_NM])dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+dnl
+
+# Check for any special flags to pass to ltconfig.
+#
+# the following will cause an existing older ltconfig to fail, so
+# we ignore this at the expense of the cache file... Checking this 
+# will just take longer ... bummer!
+#libtool_flags="--cache-file=$cache_file"
+#
+test "$enable_shared" = no && libtool_flags="$libtool_flags --disable-shared"
+test "$enable_static" = no && libtool_flags="$libtool_flags --disable-static"
+test "$enable_fast_install" = no && libtool_flags="$libtool_flags --disable-fast-install"
+test "$ac_cv_prog_gcc" = yes && libtool_flags="$libtool_flags --with-gcc"
+test "$ac_cv_prog_gnu_ld" = yes && libtool_flags="$libtool_flags --with-gnu-ld"
+ifdef([AC_PROVIDE_AC_LIBTOOL_DLOPEN],
+[libtool_flags="$libtool_flags --enable-dlopen"])
+ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL],
+[libtool_flags="$libtool_flags --enable-win32-dll"])
+AC_ARG_ENABLE(libtool-lock,
+  [  --disable-libtool-lock  avoid locking (might break parallel builds)])
+test "x$enable_libtool_lock" = xno && libtool_flags="$libtool_flags --disable-lock"
+test x"$silent" = xyes && libtool_flags="$libtool_flags --silent"
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case "$host" in
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case "`/usr/bin/file conftest.o`" in
+    *32-bit*)
+      LD="${LD-ld} -32"
+      ;;
+    *N32*)
+      LD="${LD-ld} -n32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -64"
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+
+ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL],
+[*-*-cygwin* | *-*-mingw*)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+])
+esac
+])
+
+# AC_LIBTOOL_DLOPEN - enable checks for dlopen support
+AC_DEFUN(AC_LIBTOOL_DLOPEN, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])])
+
+# AC_LIBTOOL_WIN32_DLL - declare package support for building win32 dll's
+AC_DEFUN(AC_LIBTOOL_WIN32_DLL, [AC_BEFORE([$0], [AC_LIBTOOL_SETUP])])
+
+# AC_ENABLE_SHARED - implement the --enable-shared flag
+# Usage: AC_ENABLE_SHARED[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN(AC_ENABLE_SHARED, [dnl
+define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(shared,
+changequote(<<, >>)dnl
+<<  --enable-shared[=PKGS]  build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case "$enableval" in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+  enable_shared=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_shared=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl
+])
+
+# AC_DISABLE_SHARED - set the default shared flag to --disable-shared
+AC_DEFUN(AC_DISABLE_SHARED, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_SHARED(no)])
+
+# AC_ENABLE_STATIC - implement the --enable-static flag
+# Usage: AC_ENABLE_STATIC[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN(AC_ENABLE_STATIC, [dnl
+define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(static,
+changequote(<<, >>)dnl
+<<  --enable-static[=PKGS]  build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case "$enableval" in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+  enable_static=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_static=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_static=AC_ENABLE_STATIC_DEFAULT)dnl
+])
+
+# AC_DISABLE_STATIC - set the default static flag to --disable-static
+AC_DEFUN(AC_DISABLE_STATIC, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)])
+
+
+# AC_ENABLE_FAST_INSTALL - implement the --enable-fast-install flag
+# Usage: AC_ENABLE_FAST_INSTALL[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN(AC_ENABLE_FAST_INSTALL, [dnl
+define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(fast-install,
+changequote(<<, >>)dnl
+<<  --enable-fast-install[=PKGS]  optimize for fast installation [default=>>AC_ENABLE_FAST_INSTALL_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case "$enableval" in
+yes) enable_fast_install=yes ;;
+no) enable_fast_install=no ;;
+*)
+  enable_fast_install=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_fast_install=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_fast_install=AC_ENABLE_FAST_INSTALL_DEFAULT)dnl
+])
+
+# AC_ENABLE_FAST_INSTALL - set the default to --disable-fast-install
+AC_DEFUN(AC_DISABLE_FAST_INSTALL, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_FAST_INSTALL(no)])
+
+# AC_PROG_LD - find the path to the GNU or non-GNU linker
+AC_DEFUN(AC_PROG_LD,
+[AC_ARG_WITH(gnu-ld,
+[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+ac_prog=ld
+if test "$ac_cv_prog_gcc" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by GCC])
+  ac_prog=`($CC -print-prog-name=ld) 2>&5`
+  case "$ac_prog" in
+    # Accept absolute paths.
+changequote(,)dnl
+    [\\/]* | [A-Za-z]:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+changequote([,])dnl
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(ac_cv_path_LD,
+[if test -z "$LD"; then
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      ac_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      if "$ac_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+	test "$with_gnu_ld" != no && break
+      else
+	test "$with_gnu_ld" != yes && break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  ac_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$ac_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_SUBST(LD)
+AC_PROG_LD_GNU
+])
+
+AC_DEFUN(AC_PROG_LD_GNU,
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  ac_cv_prog_gnu_ld=yes
+else
+  ac_cv_prog_gnu_ld=no
+fi])
+])
+
+# AC_PROG_NM - find the path to a BSD-compatible name lister
+AC_DEFUN(AC_PROG_NM,
+[AC_MSG_CHECKING([for BSD-compatible nm])
+AC_CACHE_VAL(ac_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  ac_cv_path_NM="$NM"
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext ; then
+      # Check to see if the nm accepts a BSD-compat flag.
+      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+      #   nm: unknown option "B" ignored
+      if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+	ac_cv_path_NM="$ac_dir/nm -B"
+	break
+      elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+	ac_cv_path_NM="$ac_dir/nm -p"
+	break
+      else
+	ac_cv_path_NM=${ac_cv_path_NM="$ac_dir/nm"} # keep the first match, but
+	continue # so that we can try to find one that supports BSD flags
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_path_NM" && ac_cv_path_NM=nm
+fi])
+NM="$ac_cv_path_NM"
+AC_MSG_RESULT([$NM])
+AC_SUBST(NM)
+])
+
+# AC_CHECK_LIBM - check for math library
+AC_DEFUN(AC_CHECK_LIBM,
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case "$host" in
+*-*-beos* | *-*-cygwin*)
+  # These system don't have libm
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, main, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, main, LIBM="-lm")
+  ;;
+esac
+])
+
+# AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for
+# the libltdl convenience library, adds --enable-ltdl-convenience to
+# the configure arguments.  Note that LIBLTDL is not AC_SUBSTed, nor
+# is AC_CONFIG_SUBDIRS called.  If DIR is not provided, it is assumed
+# to be `${top_builddir}/libltdl'.  Make sure you start DIR with
+# '${top_builddir}/' (note the single quotes!) if your package is not
+# flat, and, if you're not using automake, define top_builddir as
+# appropriate in the Makefiles.
+AC_DEFUN(AC_LIBLTDL_CONVENIENCE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  case "$enable_ltdl_convenience" in
+  no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;;
+  "") enable_ltdl_convenience=yes
+      ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;;
+  esac
+  LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdlc.la
+  INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl'])
+])
+
+# AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for
+# the libltdl installable library, and adds --enable-ltdl-install to
+# the configure arguments.  Note that LIBLTDL is not AC_SUBSTed, nor
+# is AC_CONFIG_SUBDIRS called.  If DIR is not provided, it is assumed
+# to be `${top_builddir}/libltdl'.  Make sure you start DIR with
+# '${top_builddir}/' (note the single quotes!) if your package is not
+# flat, and, if you're not using automake, define top_builddir as
+# appropriate in the Makefiles.
+# In the future, this macro may have to be called after AC_PROG_LIBTOOL.
+AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  AC_CHECK_LIB(ltdl, main,
+  [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no],
+  [if test x"$enable_ltdl_install" = xno; then
+     AC_MSG_WARN([libltdl not installed, but installation disabled])
+   else
+     enable_ltdl_install=yes
+   fi
+  ])
+  if test x"$enable_ltdl_install" = x"yes"; then
+    ac_configure_args="$ac_configure_args --enable-ltdl-install"
+    LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdl.la
+    INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl'])
+  else
+    ac_configure_args="$ac_configure_args --enable-ltdl-install=no"
+    LIBLTDL="-lltdl"
+    INCLTDL=
+  fi
+])
+
+dnl old names
+AC_DEFUN(AM_PROG_LIBTOOL, [indir([AC_PROG_LIBTOOL])])dnl
+AC_DEFUN(AM_ENABLE_SHARED, [indir([AC_ENABLE_SHARED], $@)])dnl
+AC_DEFUN(AM_ENABLE_STATIC, [indir([AC_ENABLE_STATIC], $@)])dnl
+AC_DEFUN(AM_DISABLE_SHARED, [indir([AC_DISABLE_SHARED], $@)])dnl
+AC_DEFUN(AM_DISABLE_STATIC, [indir([AC_DISABLE_STATIC], $@)])dnl
+AC_DEFUN(AM_PROG_LD, [indir([AC_PROG_LD])])dnl
+AC_DEFUN(AM_PROG_NM, [indir([AC_PROG_NM])])dnl
+
+dnl This is just to silence aclocal about the macro not being used
+ifelse([AC_DISABLE_FAST_INSTALL])dnl

+ 7 - 0
panda/metalibs/Sources.pp

@@ -0,0 +1,7 @@
+// This is a group directory: a directory level above a number of
+// source subdirectories.
+
+#define DIR_TYPE group
+
+// The metalibs directory always depends on the src directory.
+#define DEPENDS src

+ 25 - 0
panda/metalibs/panda/Sources.pp

@@ -0,0 +1,25 @@
+// 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_PANDA
+
+#define LOCAL_LIBS \
+    pstatclient grutil chan chancfg \
+    char chat collide cull device \
+    dgraph display gobj graph gsgbase \
+    gsgmisc light linmath mathutil net pnm \
+    pnmimagetypes pnmimage sgattrib sgmanip sgraph sgraphutil \
+    switchnode text tform lerp loader putil effects \
+    audio pandabase
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET panda
+
+  #define SOURCES panda.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/panda/panda.cxx

@@ -0,0 +1,9 @@
+// Filename: panda.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpanda.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpanda.

+ 21 - 0
panda/metalibs/pandadx/Sources.pp

@@ -0,0 +1,21 @@
+#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 LOCAL_LIBS \
+    dxgsg wdxdisplay
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandadx
+
+  #define SOURCES pandadx.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandadx/pandadx.cxx

@@ -0,0 +1,9 @@
+// Filename: pandadx.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandadx.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandadx.

+ 19 - 0
panda/metalibs/pandaegg/Sources.pp

@@ -0,0 +1,19 @@
+// 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_PANDAEGG
+
+#define LOCAL_LIBS \
+    egg2sg egg builder
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandaegg
+
+  #define SOURCES pandaegg.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandaegg/pandaegg.cxx

@@ -0,0 +1,9 @@
+// Filename: pandaegg.cxx
+// Created by:  drose (16May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandaegg.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandaegg.

+ 17 - 0
panda/metalibs/pandaexpress/Sources.pp

@@ -0,0 +1,17 @@
+// 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_PANDAEXPRESS
+
+#define LOCAL_LIBS downloader event ipc express pandabase
+#define OTHER_LIBS interrogatedb dconfig dtoolutil
+
+#begin metalib_target
+  #define TARGET pandaexpress
+
+  #define SOURCES pandaexpress.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandaexpress/pandaexpress.cxx

@@ -0,0 +1,9 @@
+// Filename: pandaexpress.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpanda.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpanda.

+ 21 - 0
panda/metalibs/pandagl/Sources.pp

@@ -0,0 +1,21 @@
+#define DIRECTORY_IF_GL 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_PANDAGL
+
+#define LOCAL_LIBS \
+    glgsg glxdisplay // sgiglxdisplay
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandagl
+
+  #define SOURCES pandagl.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandagl/pandagl.cxx

@@ -0,0 +1,9 @@
+// Filename: pandagl.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandagl.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandagl.

+ 21 - 0
panda/metalibs/pandaglut/Sources.pp

@@ -0,0 +1,21 @@
+#define DIRECTORY_IF_GLUT 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_PANDAGLUT
+
+#define LOCAL_LIBS \
+    glutdisplay // sgiglutdisplay
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandaglut
+
+  #define SOURCES pandaglut.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandaglut/pandaglut.cxx

@@ -0,0 +1,9 @@
+// Filename: pandaglut.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandaglut.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandaglut.

+ 19 - 0
panda/metalibs/pandaphysics/Sources.pp

@@ -0,0 +1,19 @@
+// 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_PANDAPHYSICS
+
+#define LOCAL_LIBS \
+    physics particlesystem
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandaphysics
+
+  #define SOURCES pandaphysics.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandaphysics/pandaphysics.cxx

@@ -0,0 +1,9 @@
+// Filename: pandaphysics.cxx
+// Created by:  drose (16May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandaphysics.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandaphysics.

+ 21 - 0
panda/metalibs/pandarib/Sources.pp

@@ -0,0 +1,21 @@
+#define DIRECTORY_IF_RIB 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_PANDARIB
+
+#define LOCAL_LIBS \
+    ribgsg ribdisplay
+
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin metalib_target
+  #define TARGET pandarib
+
+  #define SOURCES pandarib.cxx
+#end metalib_target

+ 9 - 0
panda/metalibs/pandarib/pandarib.cxx

@@ -0,0 +1,9 @@
+// Filename: pandarib.cxx
+// Created by:  drose (15May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libpandarib.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libpandarib.

+ 212 - 0
panda/src/PACKAGE-DESC

@@ -0,0 +1,212 @@
+Quick descriptions of the packages:
+
+audio -
+    Audio API for panda
+builder - 
+    A tool to create Geoms and GeomNodes from a pile of unconnected
+    triangles or polygons.  It separates geometry according to state
+    (texturing, normals, etc.), discovers connections where they exist
+    and tries to build nearly-optimal tristrips.
+chan -
+    Animation channels.  This defines the various kinds of
+    AnimChannels that may be defined, as well as the MovingPart class
+    which binds to the channels and plays the animation.  This is a
+    support library for char, as well as any other libraries that want
+    to define objects whose values change over time.
+chancfg - 
+    This package contains the code for the 'channel configuration' system.
+    This is responsible for asking for a window to be opened, possibly binding
+    'pipe video' channels to it, and dividing it up into Display Regions.
+chat -
+    Implementation of a text-entry system along the bottom edge of the
+    window, along with any other support functions for an online chat
+    system that might be required.
+collide -
+    This package contains the classes that control and detect collisions
+configfiles -
+    This package contains the housekeeping and configuration files needed by
+    things like attach, and emacs.
+cull -
+    This package contains the Cull Traverser.  The cull traversal collects all
+    state changes specified, and removes unneccesary state change requests.  Also
+    does all the depth sorting for proper alphaing.
+device -
+    Device drivers, such as mouse and keyboard, trackers, etc...  The 
+    base class for using various device APIs is here.
+dgraph -
+    Defines and manages the data graph, which is the hierarchy of
+    devices, tforms, and any other things which might have an input or
+    an output and need to execute every frame.
+display -
+    Abstract display classes, including pipes, windows, channels, and
+    display regions
+doc -
+    Documentation that doesn't fit in any of the packages
+downloader -
+    Tool to allow automatic download of files in the background
+dxgsg - 
+    Handles all communication with the DirectX backend, and manages state
+    to minimize redundant state changes.
+effects -
+    Various graphics effects that aren't shaders.  I.E Lens Flares
+egg -
+    A.k.a. the "egg library", this reads, writes, and manipulates egg
+    files.  It knows nothing about the scene graph structure in the
+    rest of the player; it lives in its own little egg world.
+egg2sg -
+    A.k.a. the "egg loader", this converts the egg structure read from
+    the egg library, above, to a scene graph structure, suitable for
+    rendering.
+event -
+    Tools for throwing, handling and receiving events
+framework -
+    A simple, stupid framework around which to write a simple, stupid
+    demo program.  Handy for quickly writing programs that open a
+    window and display the OmniTriangle.
+glgsg -
+    Handles all communication with the GL backend, and manages state
+    to minimize redundant state changes.
+glidedisplay - (defunct)
+    This package contains the code to manage pipes/windows/channels on glide
+    hardware.
+glidegsg - (defunct)
+    This package contains the GSG for the glide backend.  All glide calls
+    should live in here.
+glutdisplay -
+    Glut specific display classes.  This uses the Glut library to open
+    a window and manage keyboard and mouse events.  Glut has the
+    advantage of being widely ported, but unfortunately it needs to
+    control the main loop and so limits the kind of programs you can
+    write.
+glxdisplay -
+    X windows display classes that replace Glut functionality.
+gobj -
+    Graphical non-scene-graph objects, such as textures and geometry primitives
+graph -
+    Defines the basic graph operations that are not specific to scene
+    graphs.  This includes nodes, relations, transitions, and
+    attributes.
+gsg -
+    The base definition for the GraphicsStateGuardian.  This is the
+    abstract base class for all backend specifications.
+gsgbase -
+    Base GSG class defined to avoid cyclical dependency build
+gsgmisc -
+    Some utility functions for gsg that could not live in the same
+    directory for circular dependency reasons.
+ipc -
+    This package contains the abstraction/adaptation layer for: mutexes,
+    semiphores, condition variables, threads, and files.
+light -
+    Lights needed to be their own package to avoid multiple inheritance
+    problems.  All concrete light types are both lights and nodes.
+linmath -
+    Linear algebra library.
+loader -
+    Tool for loading various kinds of files into Graph structures.
+    Can be done asynchronously
+mathutil -
+    Math utility functions, such as frustum and plane
+net -
+    Net connection classes
+panda -
+    Builds the libpanda shared library.  This is a single library that
+    encapsulates most of the packages in Panda, especially those that
+    are considered essential to Panda's basic functionality.  On
+    Windows platforms, the individual packages are not themselves
+    built into shared libraries; the single LIBPANDA.DLL is the only
+    library file.  On Unix platforms, the individual packages are each
+    built into their own shared library files, and a trivial
+    libpanda.so is built that unifies all of them.
+pandadx -
+    As above, for the DirectX libraries.  This includes all of the
+    packages, in addition to those in libpanda, that are required for
+    rendering on a DirectX platform.  This also includes
+    Windows-specific libraries.
+pandaegg -
+    As above, for the egg reader.  This includes all of the packages,
+    in addition to those in libpanda, that are required for reading
+    egg files into the scene graph.
+pandagl -
+    As above, for the OpenGL libraries.  This includes all of the
+    packages, in addition to those in libpanda, that are required for
+    rendering on an OpenGL platform, with the exception of Glut.
+pandaglut -
+    As above, for the OpenGL Glut libraries.
+pandaphysics - 
+    As above, for the physics/particle systems libraries.
+pandarib -
+    As above, for the Renderman non-realtime renderer.
+particlesystem -
+    Tool for doing particle systems.  Contains various kinds of particles,
+    emiters, factories and renderers.
+physics -
+    Base classes for physical objects and forces.  Also contains the
+    physics manager class
+pnm -
+    A more-or-less intact version of the NetPBM package, compiled as a
+    single library.  This is a support library for pnmimage.
+pnmimage -
+    Reads and writes image files in various formats, by using the pnm
+    and tiff libraries.
+pstats -
+    Package for gathering performance statistics
+ps2display - (defunct)
+    Playstation 2 display classes.
+p2gsg - (defunct)
+    Play station 2 specific rendering backend.
+ribdisplay -
+    RIB-specific "display" classes.  These don't actually open
+    windows, but actually specify filenames.
+ribgsg -
+    The RIB-specific rendering backend.  Writing a frame to this
+    backend causes a RIB file to be produced, which can be used to
+    render the scene offline.
+sgattrib -
+    Scene graph attributes and transitions (state information)
+sgidisplay - 
+    This package contains the code to manage pipes/windows/channels on SGI
+    hardware.
+sgiglutdisplay - 
+    This package contains the code to manage pipes/windows/channels on SGI
+    hardware for GLUT
+sgiglxdisplay -
+    SGI specific X windows display classes that replace Glut functionality.
+sgmanip -
+    High-level tools for manipulation of scene graphs.  This primarily
+    defines NodePath, the principle interface for high-level scripting
+    languages into the scene graph.
+sgraph -
+    Graph operations and nodes that are specific to render-type scene
+    graphs.  This includes GeomNode, Camera, etc.
+sgraphutil -
+    Handy utility functions for working with scene graphs.
+shader -
+    Shaders that generate special effects by modifying the render
+    traversal and computing multiple passes.
+stats - (defunct)
+    Package for gathering performance stats
+statsdisplay - (defunct)
+    Package for remotely displaying the perfomance stats on a running program
+switchnode -
+    Package of nodes that switch out geometry underneath them based on
+    various conditions.
+testbed -
+    C test programs, that primarily link with framework.
+text - 
+    Package for generating renderable text using textured polygons.
+tform -
+    Data transforming objects that live in the data graph and convert
+    raw data (as read from an input device, for instance) to something
+    more useful.
+tiff -
+    Another support library for pnmimage.
+util -
+    This package contains all the little fiddley things that are atomic to
+    just about all the code.  Things like: config, ref count, notify, etc.
+vrpn - 
+    Defines the specific client code for interfacing to the VRPN API.
+wdxdisplay -
+    Windows DirectX specific display classes
+wgldisplay -
+    Windows OpenGL specific display classes

+ 4 - 0
panda/src/Sources.pp

@@ -0,0 +1,4 @@
+// This is a group directory: a directory level above a number of
+// source subdirectories.
+
+#define DIR_TYPE group

+ 49 - 0
panda/src/audio/Sources.pp

@@ -0,0 +1,49 @@
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin lib_target
+  #define TARGET audio
+  #define LOCAL_LIBS putil ipc
+
+  #define SOURCES \
+    audio_manager.I audio_manager.cxx audio_manager.h audio_midi.cxx \
+    audio_midi.h audio_music.I audio_music.cxx audio_music.h \
+    audio_pool.I audio_pool.cxx audio_pool.h audio_sample.I \
+    audio_sample.cxx audio_sample.h audio_trait.cxx audio_trait.h \
+    config_audio.cxx config_audio.h
+
+  #define INSTALL_HEADERS \
+    audio.h audio_manager.h audio_music.h audio_pool.I audio_pool.h \
+    audio_sample.I audio_sample.h audio_trait.h
+
+  #define IGATESCAN audio.h
+
+#end lib_target
+
+#begin lib_target
+  #define TARGET audio_load_midi
+  #define LOCAL_LIBS \
+    audio
+
+  #define SOURCES \
+    audio_load_midi.cxx
+
+#end lib_target
+
+#begin lib_target
+  #define TARGET audio_load_wav
+  #define LOCAL_LIBS \
+    audio
+
+  #define SOURCES \
+    audio_load_wav.cxx
+
+#end lib_target
+
+#begin test_bin_target
+  #define TARGET test_audio
+
+  #define SOURCES \
+    test_audio.cxx
+
+#end test_bin_target
+

+ 15 - 0
panda/src/audio/audio.h

@@ -0,0 +1,15 @@
+// Filename: audio.h
+// Created by:  frang (06Jul00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_H__
+#define __AUDIO_H__
+
+#include "audio_trait.h"
+#include "audio_sample.h"
+#include "audio_music.h"
+#include "audio_manager.h"
+#include "audio_pool.h"
+
+#endif /* __AUDIO_H__ */

+ 75 - 0
panda/src/audio/audio_load_midi.cxx

@@ -0,0 +1,75 @@
+// Filename: audio_load_midi.cxx
+// Created by:  cary (26Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <dconfig.h>
+#include "audio_pool.h"
+#include "config_audio.h"
+
+Configure(audio_load_midi);
+
+#ifdef USE_MIKMOD
+
+#include "audio_mikmod_traits.h"
+
+void AudioDestroyMidi(AudioTraits::MusicClass* music) {
+  MikModMidi::destroy(music);
+}
+
+void AudioLoadMidi(AudioTraits::MusicClass** music,
+		   AudioTraits::PlayerClass** player,
+		   AudioTraits::DeleteMusicFunc** destroy, Filename filename) {
+  *music = MikModMidi::load_midi(filename);
+  if (*music == (AudioTraits::MusicClass*)0L)
+    return;
+  *player = MikModMidiPlayer::get_instance();
+  *destroy = AudioDestroyMidi;
+}
+
+#else
+
+#ifdef PENV_WIN32
+
+#include "audio_win_traits.h"
+
+void AudioDestroyMidi(AudioTraits::MusicClass* music) {
+  WinMusic::destroy(music);
+}
+
+void AudioLoadMidi(AudioTraits::MusicClass** music,
+		   AudioTraits::PlayerClass** player,
+		   AudioTraits::DeleteMusicFunc** destroy, Filename filename) {
+  *music = WinMusic::load_midi(filename);
+  if (*music == (AudioTraits::MusicClass*)0L)
+    return;
+  *player = WinPlayer::get_instance();
+  *destroy = AudioDestroyMidi;
+  audio_cat->debug() << "sanity check: music = " << (void*)*music
+		     << "  player = " << (void*)*player << "  destroy = "
+		     << (void*)*destroy << endl;
+}
+#else
+
+// Null driver
+#include "audio_null_traits.h"
+
+void AudioDestroyMidi(AudioTraits::MusicClass* music) {
+  delete music;
+}
+
+void AudioLoadMidi(AudioTraits::MusicClass** music,
+		   AudioTraits::PlayerClass** player,
+		   AudioTraits::DeleteMusicFunc** destroy, Filename) {
+  *music = new NullMusic();
+  *player = new NullPlayer();
+  *destroy = AudioDestroyMidi;
+}
+
+#endif /* win32 */
+#endif /* mikmod */
+
+ConfigureFn(audio_load_midi) {
+  AudioPool::register_music_loader("midi", AudioLoadMidi);
+  AudioPool::register_music_loader("mid", AudioLoadMidi);
+}

+ 71 - 0
panda/src/audio/audio_load_wav.cxx

@@ -0,0 +1,71 @@
+// Filename: audio_load_wav.cxx
+// Created by:  cary (23Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <dconfig.h>
+#include "audio_pool.h"
+
+Configure(audio_load_wav);
+
+#ifdef USE_MIKMOD
+
+#include "audio_mikmod_traits.h"
+
+void AudioDestroyWav(AudioTraits::SampleClass* sample) {
+  MikModSample::destroy(sample);
+}
+
+void AudioLoadWav(AudioTraits::SampleClass** sample,
+		  AudioTraits::PlayerClass** player,
+		  AudioTraits::DeleteSampleFunc** destroy, Filename filename) {
+  *sample = MikModSample::load_wav(filename);
+  if (*sample == (AudioTraits::SampleClass*)0L)
+    return;
+  *player = MikModSamplePlayer::get_instance();
+  *destroy = AudioDestroyWav;
+}
+
+#else /* no MikMod */
+
+#ifdef PENV_WIN32
+
+#include "audio_win_traits.h"
+
+void AudioDestroyWav(AudioTraits::SampleClass* sample) {
+  WinSample::destroy(sample);
+}
+
+void AudioLoadWav(AudioTraits::SampleClass** sample,
+		  AudioTraits::PlayerClass** player,
+		  AudioTraits::DeleteSampleFunc** destroy, Filename filename) {
+  *sample = WinSample::load_wav(filename);
+  if (*sample == (AudioTraits::SampleClass*)0L)
+    return;
+  *player = WinPlayer::get_instance();
+  *destroy = AudioDestroyWav;
+}
+
+#else /* no win32 */
+
+// Null driver
+#include "audio_null_traits.h"
+
+void AudioDestroyWav(AudioTraits::SampleClass* sample) {
+  delete sample;
+}
+
+void AudioLoadWav(AudioTraits::SampleClass** sample,
+		  AudioTraits::PlayerClass** player,
+		  AudioTraits::DeleteSampleFunc** destroy, Filename) {
+  *sample = new NullSample();
+  *player = new NullPlayer();
+  *destroy = AudioDestroyWav;
+}
+
+#endif /* win32 */
+#endif /* MikMod */
+
+ConfigureFn(audio_load_wav) {
+  AudioPool::register_sample_loader("wav", AudioLoadWav);
+}

+ 69 - 0
panda/src/audio/audio_manager.I

@@ -0,0 +1,69 @@
+// Filename: audio_manager.I
+// Created by:  cary (24Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::play (AudioSample)
+//       Access: Public, Static
+//  Description: Play an audio sample
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::play(AudioSample* sample) {
+  get_ptr()->ns_play(sample);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::play (AudioMusic)
+//       Access: Public, Static
+//  Description: Play an audio music instance
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::play(AudioMusic* music) {
+  get_ptr()->ns_play(music);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::update
+//       Access: Public, Static
+//  Description: make sure buffers are full
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::update(void) {
+  mutex_lock l(_manager_mutex);
+  if (_update_func != (UpdateFunc*)0L)
+    (*_update_func)();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::spawn_update
+//       Access: Public, Static
+//  Description: spawn a thread to call update
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::spawn_update(void) {
+  get_ptr()->ns_spawn_update();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::set_volume (sample)
+//       Access: Public, Static
+//  Description: set the volume on a sample
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::set_volume(AudioSample* sample, int v) {
+  get_ptr()->ns_set_volume(sample, v);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::set_volume (music)
+//       Access: Public, Static
+//  Description: set the volume on music
+////////////////////////////////////////////////////////////////////
+INLINE void AudioManager::set_volume(AudioMusic* music, int v) {
+  get_ptr()->ns_set_volume(music, v);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::Constructor
+//       Access: Private
+//  Description: The constructor is not intended to be called
+//               directly; there's only supposed to be one AudioManager
+//               in the universe and it constructs itself.
+////////////////////////////////////////////////////////////////////
+INLINE AudioManager::AudioManager(void) : _spawned((thread*)0L) {}

+ 95 - 0
panda/src/audio/audio_manager.cxx

@@ -0,0 +1,95 @@
+// Filename: audio_manager.cxx
+// Created by:  cary (24Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_manager.h"
+#include "config_audio.h"
+
+AudioManager* AudioManager::_global_ptr = (AudioManager*)0L;
+AudioManager::UpdateFunc* AudioManager::_update_func =
+    (AudioManager::UpdateFunc*)0L;
+mutex AudioManager::_manager_mutex;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::set_update_func
+//       Access: Public, Static
+//  Description: register a function that will maintain the buffers
+//               for audio output
+////////////////////////////////////////////////////////////////////
+void AudioManager::set_update_func(AudioManager::UpdateFunc* func) {
+  if (_update_func != (AudioManager::UpdateFunc*)0L)
+    audio_cat->error() << "There maybe be more then one audio driver installed"
+		       << endl;
+  _update_func = func;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::get_ptr
+//       Access: Private, Static
+//  Description: Initializes and/or returns the global pointer to the
+//               one AudioManager object in the system.
+////////////////////////////////////////////////////////////////////
+AudioManager* AudioManager::get_ptr(void) {
+  if (_global_ptr == (AudioManager*)0L)
+    _global_ptr = new AudioManager;
+  return _global_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::ns_play (AudioSample)
+//       Access: Private
+//  Description: get the player off the sample, and start it playing
+////////////////////////////////////////////////////////////////////
+void AudioManager::ns_play(AudioSample* sample) {
+  sample->get_player()->play_sample(sample->get_sample());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::ns_play (AudioMusic)
+//       Access: Private
+//  Description: get the player off the music, and start it playing
+////////////////////////////////////////////////////////////////////
+void AudioManager::ns_play(AudioMusic* music) {
+  music->get_player()->play_music(music->get_music());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::spawned_update
+//       Access: static
+//  Description: the thread function to call update forever.
+////////////////////////////////////////////////////////////////////
+void AudioManager::spawned_update(void*) {
+  while (1) {
+    AudioManager::update();
+    ipc_traits::sleep(0, 1000000);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::ns_set_volume (AudioSample)
+//       Access: Private
+//  Description: get the player off the sample, and set volume on it
+////////////////////////////////////////////////////////////////////
+void AudioManager::ns_set_volume(AudioSample* sample, int v) {
+  sample->get_player()->set_volume(sample->get_sample(), v);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::ns_set_volume (AudioMusic)
+//       Access: Private
+//  Description: get the player off the music, and set volume on it
+////////////////////////////////////////////////////////////////////
+void AudioManager::ns_set_volume(AudioMusic* music, int v) {
+  music->get_player()->set_volume(music->get_music(), v);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::ns_spawn_update
+//       Access: Private
+//  Description: spawn a thread that calls update every so often
+////////////////////////////////////////////////////////////////////
+void AudioManager::ns_spawn_update(void) {
+  _spawned = thread::create(spawned_update, (void*)0L,
+			    thread::PRIORITY_NORMAL);
+}

+ 47 - 0
panda/src/audio/audio_manager.h

@@ -0,0 +1,47 @@
+// Filename: audio_manager.h
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_MANAGER_H__
+#define __AUDIO_MANAGER_H__
+
+#include "audio_trait.h"
+#include "audio_sample.h"
+#include "audio_music.h"
+
+#include <ipc_mutex.h>
+#include <ipc_thread.h>
+
+class EXPCL_PANDA AudioManager {
+private:
+  INLINE AudioManager(void);
+
+  void ns_play(AudioSample*);
+  void ns_play(AudioMusic*);
+  void ns_spawn_update(void);
+  void ns_set_volume(AudioSample*, int);
+  void ns_set_volume(AudioMusic*, int);
+
+  static AudioManager* get_ptr(void);
+  static void spawned_update(void*);
+
+  typedef void UpdateFunc(void);
+  static AudioManager* _global_ptr;
+  static UpdateFunc* _update_func;
+  static mutex _manager_mutex;
+  thread* _spawned;
+public:
+  static void set_update_func(UpdateFunc*);
+
+  INLINE static void play(AudioSample*);
+  INLINE static void play(AudioMusic*);
+  INLINE static void update(void);
+  INLINE static void spawn_update(void);
+  INLINE static void set_volume(AudioSample*, int);
+  INLINE static void set_volume(AudioMusic*, int);
+};
+
+#include "audio_manager.I"
+
+#endif /* __AUDIO_MANAGER_H__ */

+ 233 - 0
panda/src/audio/audio_midi.cxx

@@ -0,0 +1,233 @@
+// Filename: audio_midi.cxx
+// Created by:  cary (26Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_midi.h"
+#include "config_audio.h"
+#include <pandabase.h>    // get iostream and fstream
+
+#define SHORT short
+#define LONG long
+
+#define RIFF 0x52494646
+#define CTMF 0x43544d46
+#define MThd 0x4d546864
+#define MTrk 0x4d54726b
+
+// take data off the supplimental stream until it's empty
+inline static unsigned char read8(istream& is, istream& supp) {
+  unsigned char b;
+  if (supp.eof()) {
+    is >> b;
+  } else {
+    supp >> b;
+  }
+  return b;
+}
+
+inline static unsigned SHORT read16(istream& is, istream& supp) {
+  unsigned char b1, b2;
+  b1 = read8(is, supp);
+  b2 = read8(is, supp);
+  unsigned SHORT ret = (b1 << 8) | (b2);
+  return ret;
+}
+
+inline static unsigned LONG read32(istream& is, istream& supp) {
+  unsigned char b1, b2, b3, b4;
+  b1 = read8(is, supp);
+  b2 = read8(is, supp);
+  b3 = read8(is, supp);
+  b4 = read8(is, supp);
+  unsigned int LONG ret = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
+  return ret;
+}
+
+inline static void scroll8(unsigned LONG& prev, unsigned LONG& curr,
+			   istream& is, istream& supp) {
+  unsigned char b1 = ((curr >> 24) & 0xff);
+  prev = ((prev << 8) & 0xffffff00) | (b1);
+  b1 = read8(is, supp);
+  curr = ((curr << 8) & 0xffffff00) | (b1);
+}
+
+AudioMidi::AudioMidi(Filename filename, int header_idx) {
+  filename.set_binary();
+  ifstream in;
+  if (!filename.open_read(in)) {
+    cerr << "ACK, cannot read '" << filename << "'" << endl;
+    return;
+  }
+
+  istringstream dummy("");
+  unsigned LONG prev = 0x0;
+  unsigned LONG curr = read32(in, dummy);
+  int count = 0;
+  bool done = false;
+  do {
+    if (curr == MThd) {
+      ++count;
+      if (count == header_idx)
+	done = true;
+      else {
+	scroll8(prev, curr, in, dummy);
+	scroll8(prev, curr, in, dummy);
+	scroll8(prev, curr, in, dummy);
+	scroll8(prev, curr, in, dummy);
+      }
+    } else {
+      scroll8(prev, curr, in, dummy);
+    }
+    if (in.eof())
+      done = true;
+  } while (!done);
+  if (in.eof()) {
+    cerr << "fewer then " << header_idx << " headers in file (" << count
+	 << ")" << endl;
+    return;
+  }
+  if (prev == RIFF) {
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "it's a RIFF file" << endl;
+    curr = read32(in, dummy);
+    curr = read32(in, dummy);
+    curr = read32(in, dummy);
+    curr = read32(in, dummy);
+  }
+  unsigned LONG tracklen;
+  unsigned SHORT format;
+  unsigned SHORT numtracks;
+  unsigned SHORT division;
+  if (curr == MThd) {
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "easy header" << endl;
+    tracklen = read32(in, dummy);
+    format = read16(in, dummy);
+    numtracks = read16(in, dummy);
+    division = read16(in, dummy);
+  } else if (curr == CTMF) {
+    // Creative Labs CMF file.  We're not supporting this yet
+    cerr << "don't support Creative Labs CMF files yet" << endl;
+    return;
+  } else {
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "hard header" << endl;
+    done = false;
+    do {
+      if (curr == MThd)
+	done = true;
+      else
+	scroll8(prev, curr, in, dummy);
+      if (in.eof())
+	done = true;
+    } while (!done);
+    if (in.eof()) {
+      cerr << "truncated file!" << endl;
+      return;
+    }
+    tracklen = read32(in, dummy);
+    format = read16(in, dummy);
+    numtracks = read16(in, dummy);
+    division = read16(in, dummy);
+  }
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "Read header.  tracklen = " << tracklen
+		       << "  format = " << format << "  numtracks = "
+		       << numtracks << "  division = " << division << endl;
+  for (int currtrack = 0; currtrack < numtracks; ++currtrack) {
+    string fudge;
+    curr = read32(in, dummy);
+    if (curr != MTrk) {
+      if (audio_cat->is_debug())
+	audio_cat->debug() << "having to seach for track #" << currtrack
+			   << endl;
+      if (curr == MThd) {
+	if (audio_cat->is_debug())
+	  audio_cat->debug() << "hit a header instead, skipping track" << endl;
+	continue;
+      } else {
+	done = false;
+	if (currtrack > 0) {
+	  string stmp = (*(_seq.rbegin()));
+	  int i = stmp.rfind("MTrk");
+	  if (i != string::npos) {
+	    fudge = stmp.substr(i+4, string::npos);
+	    unsigned char b;
+	    b = ((curr >> 24) & 0xff);
+	    fudge += b;
+	    b = ((curr >> 16) & 0xff);
+	    fudge += b;
+	    b = ((curr >> 8) & 0xff);
+	    fudge += b;
+	    b = (curr & 0xff);
+	    fudge += b;
+	    done = true;
+	  }
+	}
+	if (!done) {
+	  do {
+	    if (curr == MTrk)
+	      done = true;
+	    else
+	      scroll8(prev, curr, in, dummy);
+	    if (in.eof())
+	      done = true;
+	  } while (!done);
+	  if (in.eof()) {
+	    cerr << "truncated file" << endl;
+	    return;
+	  }
+	}
+      }
+    }
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "fudge = '" << fudge << "'" << endl;
+    istringstream fudges(fudge);
+    if (fudge.empty()) {
+      // force EOF
+      unsigned char b;
+      fudges >> b;
+    }
+    unsigned LONG thislen = read32(in, fudges);
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "found track #" << currtrack << " with length = "
+			 << thislen << endl;
+    {
+      ostringstream os;
+      int i;
+      for (i=0; i<thislen; ++i) {
+	unsigned char b;
+	b = read8(in, fudges);
+	os << b;
+	if (in.eof() && ((i+1) < thislen))
+	  break;
+      }
+      if (in.eof() && (i != thislen)) {
+	cerr << "truncated file" << endl;
+      }
+      string s = os.str();
+      _seq.push_back(s);
+      if (audio_cat->is_debug())
+	audio_cat->debug() << "track data (" << s.length() << "): '" << s
+			   << "'" << endl;
+    }
+  }
+  if ((_seq.size() != numtracks) && audio_cat->is_debug())
+    audio_cat->debug()
+      << "actual number of tracks read does not match header. ("
+      << _seq.size() << " != " << numtracks << ")" << endl;
+}
+
+AudioMidi::AudioMidi(const AudioMidi& c) : _seq(c._seq) {}
+
+AudioMidi::~AudioMidi(void) {
+}
+
+AudioMidi& AudioMidi::operator=(const AudioMidi&) {
+  return *this;
+}
+
+bool AudioMidi::operator==(const AudioMidi&) const {
+  return false;
+}

+ 26 - 0
panda/src/audio/audio_midi.h

@@ -0,0 +1,26 @@
+// Filename: audio_midi.h
+// Created by:  cary (26Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_MIDI_H__
+#define __AUDIO_MIDI_H__
+
+#include <filename.h>
+#include <list>
+
+// define an internal representation for a midi file
+class AudioMidi {
+private:
+  typedef list<string> StrList;
+  StrList _seq;
+public:
+  AudioMidi(Filename, int = 1);
+  AudioMidi(const AudioMidi&);
+  ~AudioMidi(void);
+
+  AudioMidi& operator=(const AudioMidi&);
+  bool operator==(const AudioMidi&) const;
+};
+
+#endif /* __AUDIO_MIDI_H__ */

+ 305 - 0
panda/src/audio/audio_mikmod_traits.cxx

@@ -0,0 +1,305 @@
+// Filename: audio_mikmod_traits.cxx
+// Created by:  cary (23Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_mikmod_traits.h"
+#include "audio_manager.h"
+#include "config_audio.h"
+#include <list>
+#include <serialization.h>
+
+static bool have_initialized = false;
+static bool initialization_error = false;
+
+static void update_mikmod(void) {
+  MikMod_Update();
+}
+
+static void initialize(void) {
+  if (have_initialized)
+    return;
+  if (initialization_error)
+    return;
+  /* register the drivers */
+  MikMod_RegisterAllDrivers();
+  /* initialize the mikmod library */
+  md_mixfreq = audio_mix_freq;
+  {
+    // I think this is defined elsewhere
+    typedef list<string> StrList;
+    typedef Serialize::Deserializer<StrList> OptBuster;
+    StrList opts;
+    OptBuster buster(*audio_mode_flags, " ");
+    opts = buster();
+    for (StrList::iterator i=opts.begin(); i!=opts.end(); ++i) {
+      if ((*i) == "DMODE_INTERP") {
+	md_mode |= DMODE_INTERP;
+      } else if ((*i) == "DMODE_REVERSE") {
+	md_mode |= DMODE_REVERSE;
+      } else if ((*i) == "DMODE_SURROUND") {
+	md_mode |= DMODE_SURROUND;
+      } else if ((*i) == "DMODE_16BITS") {
+	md_mode |= DMODE_16BITS;
+      } else if ((*i) == "DMODE_HQMIXER") {
+	md_mode |= DMODE_HQMIXER;
+      } else if ((*i) == "DMODE_SOFT_MUSIC") {
+	md_mode |= DMODE_SOFT_MUSIC;
+      } else if ((*i) == "DMODE_SOFT_SNDFX") {
+	md_mode |= DMODE_SOFT_SNDFX;
+      } else if ((*i) == "DMODE_STEREO") {
+	md_mode |= DMODE_STEREO;
+      } else {
+	audio_cat->error() << "unknown audio driver flag '" << *i << "'"
+			   << endl;
+      }
+    }
+    if (audio_cat->is_debug()) {
+      audio_cat->debug() << "final driver mode is (";
+      bool any_out = false;
+      if (md_mode & DMODE_INTERP) {
+	audio_cat->debug(false) << "DMODE_INTERP";
+	any_out = true;
+      }
+      if (md_mode & DMODE_REVERSE) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_REVERSE";
+	any_out = true;
+      }
+      if (md_mode & DMODE_SURROUND) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_SURROUND";
+	any_out = true;
+      }
+      if (md_mode & DMODE_16BITS) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_16BITS";
+	any_out = true;
+      }
+      if (md_mode & DMODE_HQMIXER) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_HQMIXER";
+	any_out = true;
+      }
+      if (md_mode & DMODE_SOFT_MUSIC) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_SOFT_MUSIC";
+	any_out = true;
+      }
+      if (md_mode & DMODE_SOFT_SNDFX) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_SOFT_SNDFX";
+	any_out = true;
+      }
+      if (md_mode & DMODE_STEREO) {
+	if (any_out)
+	  audio_cat->debug(false) << ", ";
+	audio_cat->debug(false) << "DMODE_STEREO";
+	any_out = true;
+      }
+      audio_cat->debug(false) << ")" << endl;
+    }
+  }
+  md_device = audio_driver_select;
+  if (MikMod_Init((char*)(audio_driver_params->c_str()))) {
+    audio_cat->error() << "Could not initialize the audio drivers.  '"
+		       << MikMod_strerror(MikMod_errno) << "'" << endl;
+    initialization_error = true;
+    return;
+  }
+  if (audio_cat->is_debug()) {
+    audio_cat->debug() << "driver info" << endl << MikMod_InfoDriver() << endl;
+  }
+  MikMod_SetNumVoices(-1, audio_sample_voices);
+  AudioManager::set_update_func(update_mikmod);
+  have_initialized = true;
+}
+
+MikModSample::MikModSample(SAMPLE* sample) : _sample(sample), _voice(-1) {
+}
+
+MikModSample::~MikModSample(void) {
+  Sample_Free(_sample);
+}
+
+float MikModSample::length(void) {
+  float len = _sample->length;
+  float speed = _sample->speed;
+  return len / speed;
+}
+
+AudioTraits::SampleClass::SampleStatus MikModSample::status(void) {
+  if (_voice == -1)
+    return READY;
+  if (Voice_Stopped(_voice))
+    return PLAYING;
+  return READY;
+}
+
+MikModSample* MikModSample::load_wav(Filename filename) {
+  initialize();
+  SAMPLE* sample = Sample_Load((char*)(filename.c_str()));
+  if (sample == (SAMPLE*)0L) {
+    audio_cat->error() << "error loading sample '" << filename << "' because '"
+		       << MikMod_strerror(MikMod_errno) << "'" << endl;
+    return (MikModSample*)0L;
+  }
+  return new MikModSample(sample);
+}
+
+void MikModSample::destroy(AudioTraits::SampleClass* sample) {
+  delete sample;
+}
+
+void MikModSample::set_voice(int v) {
+  _voice = v;
+}
+
+int MikModSample::get_voice(void) {
+  return _voice;
+}
+
+SAMPLE* MikModSample::get_sample(void) {
+  return _sample;
+}
+
+int MikModSample::get_freq(void) {
+  return _sample->speed;
+}
+
+MikModMusic::MikModMusic(void) {
+}
+
+MikModMusic::~MikModMusic(void) {
+}
+
+AudioTraits::MusicClass::MusicStatus MikModMusic::status(void) {
+  return READY;
+}
+
+MikModMidi::MikModMidi(void) {
+}
+
+MikModMidi::~MikModMidi(void) {
+}
+
+MikModMidi* MikModMidi::load_midi(Filename) {
+  initialize();
+  return new MikModMidi();
+}
+
+void MikModMidi::destroy(AudioTraits::MusicClass* music) {
+  delete music;
+}
+
+AudioTraits::MusicClass::MusicStatus MikModMidi::status(void) {
+  return READY;
+}
+
+MikModSamplePlayer* MikModSamplePlayer::_global_instance =
+    (MikModSamplePlayer*)0L;
+
+MikModSamplePlayer::MikModSamplePlayer(void) : AudioTraits::PlayerClass() {
+}
+
+MikModSamplePlayer::~MikModSamplePlayer(void) {
+}
+
+void MikModSamplePlayer::play_sample(AudioTraits::SampleClass* sample) {
+  if (!have_initialized)
+    initialize();
+  if (!MikMod_Active()) {
+    if (MikMod_EnableOutput()) {
+      audio_cat->error() << "could not enable sample output '"
+			 << MikMod_strerror(MikMod_errno) << "'" << endl;
+    }
+  }
+  // cast to the correct type
+  MikModSample* msample = (MikModSample*)sample;
+  // fire it off
+  msample->set_voice(Sample_Play(msample->get_sample(), 0, 0));
+  Voice_SetFrequency(msample->get_voice(), msample->get_freq());
+  if (Voice_GetFrequency(msample->get_voice()) != msample->get_freq())
+    audio_cat->error() << "setting freq did not stick!" << endl;
+  Voice_SetPanning(msample->get_voice(), 127);
+}
+
+void MikModSamplePlayer::play_music(AudioTraits::MusicClass*) {
+  audio_cat->error() << "trying to play music with a MikModSamplePlayer"
+		     << endl;
+}
+
+void MikModSamplePlayer::set_volume(AudioTraits::SampleClass* sample, int v) {
+  initialize();
+  MikModSample* msample = (MikModSample*)sample;
+  Voice_SetVolume(msample->get_voice(), v);
+}
+
+void MikModSamplePlayer::set_volume(AudioTraits::MusicClass*, int) {
+  audio_cat->error()
+    << "trying to set volume on music withe a MikModSamplePlayer" << endl;
+}
+
+MikModSamplePlayer* MikModSamplePlayer::get_instance(void) {
+  if (_global_instance == (MikModSamplePlayer*)0L)
+    _global_instance = new MikModSamplePlayer();
+  return _global_instance;
+}
+
+MikModFmsynthPlayer::MikModFmsynthPlayer(void) {
+}
+
+MikModFmsynthPlayer::~MikModFmsynthPlayer(void) {
+}
+
+void MikModFmsynthPlayer::play_sample(AudioTraits::SampleClass*) {
+  audio_cat->error() << "trying to play a sample with a MikModFmsynthPlayer"
+		     << endl;
+}
+
+void MikModFmsynthPlayer::play_music(AudioTraits::MusicClass* music) {
+}
+
+void MikModFmsynthPlayer::set_volume(AudioTraits::SampleClass*, int) {
+  audio_cat->error()
+    << "trying to set volume on a sample with a MikModFmsynthPlayer" << endl;
+}
+
+void MikModFmsynthPlayer::set_volume(AudioTraits::MusicClass*, int) {
+}
+
+MikModMidiPlayer* MikModMidiPlayer::_global_instance = (MikModMidiPlayer*)0L;
+
+MikModMidiPlayer::MikModMidiPlayer(void) {
+}
+
+MikModMidiPlayer::~MikModMidiPlayer(void) {
+}
+
+void MikModMidiPlayer::play_sample(AudioTraits::SampleClass*) {
+  audio_cat->error() << "trying to play a sample with a MikModMidiPlayer"
+		     << endl;
+}
+
+void MikModMidiPlayer::play_music(AudioTraits::MusicClass* music) {
+}
+
+void MikModMidiPlayer::set_volume(AudioTraits::SampleClass*, int) {
+  audio_cat->error()
+    << "trying to set volume on a sample with a MikModMidiPlayer" << endl;
+}
+
+void MikModMidiPlayer::set_volume(AudioTraits::MusicClass*, int) {
+}
+
+MikModMidiPlayer* MikModMidiPlayer::get_instance(void) {
+  if (_global_instance == (MikModMidiPlayer*)0L)
+    _global_instance = new MikModMidiPlayer();
+  return _global_instance;
+}

+ 102 - 0
panda/src/audio/audio_mikmod_traits.h

@@ -0,0 +1,102 @@
+// Filename: audio_mikmod_traits.h
+// Created by:  frang (06Jul00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_MIKMOD_TRAITS_H__
+#define __AUDIO_MIKMOD_TRAITS_H__
+
+#include "audio_trait.h"
+#include <pandabase.h>
+#include <filename.h>
+#include <mikmod.h>
+
+class EXPCL_PANDA MikModSample : public AudioTraits::SampleClass {
+private:
+  SAMPLE* _sample;
+  int _voice;
+
+  MikModSample(SAMPLE*);
+public:
+  virtual ~MikModSample(void);
+
+  virtual float length(void);
+  virtual AudioTraits::SampleClass::SampleStatus status(void);
+public:
+  // used by the readers
+  static MikModSample* load_wav(Filename);
+  static void destroy(AudioTraits::SampleClass*);
+  // used by the players
+  virtual void set_voice(int);
+  virtual int get_voice(void);
+  virtual SAMPLE* get_sample(void);
+  virtual int get_freq(void);
+};
+
+class EXPCL_PANDA MikModMusic : public AudioTraits::MusicClass {
+private:
+  MODULE* _music;
+public:
+  MikModMusic(void);
+  virtual ~MikModMusic(void);
+
+  virtual AudioTraits::MusicClass::MusicStatus status(void);
+};
+
+class EXPCL_PANDA MikModMidi : public AudioTraits::MusicClass {
+private:
+public:
+  MikModMidi(void);
+  virtual ~MikModMidi(void);
+
+  virtual AudioTraits::MusicClass::MusicStatus status(void);
+public:
+  // used by the readers
+  static MikModMidi* load_midi(Filename);
+  static void destroy(AudioTraits::MusicClass*);
+};
+
+class EXPCL_PANDA MikModSamplePlayer : public AudioTraits::PlayerClass {
+public:
+  MikModSamplePlayer(void);
+  virtual ~MikModSamplePlayer(void);
+
+  virtual void play_sample(AudioTraits::SampleClass*);
+  virtual void play_music(AudioTraits::MusicClass*);
+  virtual void set_volume(AudioTraits::SampleClass*, int);
+  virtual void set_volume(AudioTraits::MusicClass*, int);
+public:
+  // used by the readers
+  static MikModSamplePlayer* get_instance(void);
+private:
+  static MikModSamplePlayer* _global_instance;
+};
+
+class EXPCL_PANDA MikModFmsynthPlayer : public AudioTraits::PlayerClass {
+public:
+  MikModFmsynthPlayer(void);
+  virtual ~MikModFmsynthPlayer(void);
+
+  virtual void play_sample(AudioTraits::SampleClass*);
+  virtual void play_music(AudioTraits::MusicClass*);
+  virtual void set_volume(AudioTraits::SampleClass*, int);
+  virtual void set_volume(AudioTraits::MusicClass*, int);
+};
+
+class EXPCL_PANDA MikModMidiPlayer : public AudioTraits::PlayerClass {
+public:
+  MikModMidiPlayer(void);
+  virtual ~MikModMidiPlayer(void);
+
+  virtual void play_sample(AudioTraits::SampleClass*);
+  virtual void play_music(AudioTraits::MusicClass*);
+  virtual void set_volume(AudioTraits::SampleClass*, int);
+  virtual void set_volume(AudioTraits::MusicClass*, int);
+public:
+  // used by the readers
+  static MikModMidiPlayer* get_instance(void);
+private:
+  static MikModMidiPlayer* _global_instance;
+};
+
+#endif /* __AUDIO_MIKMOD_TRAITS_H__ */

+ 67 - 0
panda/src/audio/audio_music.I

@@ -0,0 +1,67 @@
+// Filename: audio_music.I
+// Created by:  cary (26Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::constructor
+//       Access: Protected
+//  Description: initialize a new music
+////////////////////////////////////////////////////////////////////
+INLINE AudioMusic::AudioMusic(AudioTraits::MusicClass* music,
+				AudioTraits::PlayerClass* player,
+				AudioTraits::DeleteMusicFunc* destroy,
+				const string& filename) : Namable(filename),
+							  _music(music),
+							  _player(player),
+							  _destroy(destroy) {}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::copy constructor
+//       Access: Protected
+//  Description: copy a music, but we don't really want to allow this
+////////////////////////////////////////////////////////////////////
+INLINE AudioMusic::AudioMusic(const AudioMusic& c) : Namable(c.get_name()),
+							_music(c._music),
+							_player(c._player),
+							_destroy(c._destroy) {}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::assignment operator
+//       Access: Protected
+//  Description: copy a music, but we don't really want to allow this
+////////////////////////////////////////////////////////////////////
+INLINE AudioMusic& AudioMusic::operator=(const AudioMusic& c) {
+  this->set_name(c.get_name());
+  this->_music = c._music;
+  this->_player = c._player;
+  this->_destroy = c._destroy;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::get_player
+//       Access: Protected
+//  Description: return the player for this music
+////////////////////////////////////////////////////////////////////
+INLINE AudioTraits::PlayerClass* AudioMusic::get_player(void) {
+  return _player;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::get_music
+//       Access: Protected
+//  Description: return the trait music class for this music
+////////////////////////////////////////////////////////////////////
+INLINE AudioTraits::MusicClass* AudioMusic::get_music(void) {
+  return _music;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::equality operator
+//       Access: Public
+//  Description: test to see if two musics are the same
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioMusic::operator==(const AudioMusic& c) const {
+  return (_music == c._music);
+}

+ 36 - 0
panda/src/audio/audio_music.cxx

@@ -0,0 +1,36 @@
+// Filename: audio_music.cxx
+// Created by:  cary (26Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_music.h"
+#include "config_audio.h"
+
+TypeHandle AudioMusic::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::destructor
+//       Access: Public
+//  Description: deletes the music data and then lets the system
+//               destroy this structure
+////////////////////////////////////////////////////////////////////
+AudioMusic::~AudioMusic(void) {
+  (*_destroy)(_music);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioMusic::status
+//       Access: Public
+//  Description: return the current play status of this music
+////////////////////////////////////////////////////////////////////
+AudioMusic::MusicStatus AudioMusic::status(void) {
+  AudioTraits::MusicClass::MusicStatus stat = _music->status();
+  switch (stat) {
+  case AudioTraits::MusicClass::READY:
+    return READY;
+  case AudioTraits::MusicClass::PLAYING:
+    return PLAYING;
+  }
+  audio_cat->error() << "unknown status for music" << endl;
+  return READY;
+}

+ 62 - 0
panda/src/audio/audio_music.h

@@ -0,0 +1,62 @@
+// Filename: audio_music.h
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_MUSIC_H__
+#define __AUDIO_MUSIC_H__
+
+#include "audio_trait.h"
+#include "typedReferenceCount.h"
+#include "namable.h"
+
+class AudioPool;
+class AudioManager;
+
+class EXPCL_PANDA AudioMusic : public TypedReferenceCount, public Namable {
+private:
+  AudioTraits::MusicClass *_music;
+  AudioTraits::PlayerClass *_player;
+  AudioTraits::DeleteMusicFunc *_destroy;
+protected:
+  INLINE AudioMusic(AudioTraits::MusicClass*, AudioTraits::PlayerClass*,
+		    AudioTraits::DeleteMusicFunc*, const string&);
+  INLINE AudioMusic(const AudioMusic&);
+  INLINE AudioMusic& operator=(const AudioMusic&);
+
+  INLINE AudioTraits::PlayerClass* get_player(void);
+  INLINE AudioTraits::MusicClass* get_music(void);
+
+  friend class AudioPool;
+  friend class AudioManager;
+public:
+  virtual ~AudioMusic(void);
+  INLINE bool operator==(const AudioMusic&) const;
+
+  enum MusicStatus { READY, PLAYING };
+
+  MusicStatus status(void);
+public:
+  // type stuff
+  static TypeHandle get_class_type(void) {
+    return _type_handle;
+  }
+  static void init_type(void) {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "AudioMusic",
+		  TypedReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type(void) const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type(void) {
+    init_type();
+    return get_class_type();
+  }
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "audio_music.I"
+
+#endif /* __AUDIO_MUSIC_H__ */

+ 13 - 0
panda/src/audio/audio_null_traits.I

@@ -0,0 +1,13 @@
+// Filename: audio_null_traits.I
+// Created by:  cary (25Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE NullSample::NullSample(void) {
+}
+
+INLINE NullMusic::NullMusic(void) {
+}
+
+INLINE NullPlayer::NullPlayer(void) {
+}

+ 70 - 0
panda/src/audio/audio_null_traits.cxx

@@ -0,0 +1,70 @@
+// Filename: audio_null_traits.cxx
+// Created by:  cary (25Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_null_traits.h"
+#include "audio_manager.h"
+#include "config_audio.h"
+
+static bool have_initialized = false;
+
+static void update_null(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "Update in Null audio driver" << endl;
+}
+
+static void initialize(void) {
+  if (have_initialized)
+    return;
+  AudioManager::set_update_func(update_null);
+  have_initialized = true;
+}
+
+NullSample::~NullSample(void) {
+}
+
+float NullSample::length(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in sample length in Null audio driver" << endl;
+  return 0.;
+}
+
+AudioTraits::SampleClass::SampleStatus NullSample::status(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in sample status in Null audio driver" << endl;
+  return AudioTraits::SampleClass::READY;
+}
+
+NullMusic::~NullMusic(void) {
+}
+
+AudioTraits::MusicClass::MusicStatus NullMusic::status(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in music status in Null audio driver" << endl;
+  return READY;
+}
+
+NullPlayer::~NullPlayer(void) {
+}
+
+void NullPlayer::play_sample(AudioTraits::SampleClass*) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in play sample in Null audio driver" << endl;
+}
+
+void NullPlayer::play_music(AudioTraits::MusicClass*) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in play music in Null audio driver" << endl;
+}
+
+void NullPlayer::set_volume(AudioTraits::SampleClass*, int) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in set volume (sample) in Null audio driver"
+		       << endl;
+}
+
+void NullPlayer::set_volume(AudioTraits::MusicClass*, int) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in set volume (music) in Null audio driver" << endl;
+}

+ 41 - 0
panda/src/audio/audio_null_traits.h

@@ -0,0 +1,41 @@
+// Filename: audio_null_traits.h
+// Created by:  cary (25Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_NULL_TRAITS_H__
+#define __AUDIO_NULL_TRAITS_H__
+
+#include "audio_trait.h"
+
+class EXPCL_PANDA NullSample : public AudioTraits::SampleClass {
+public:
+  INLINE NullSample(void);
+  virtual ~NullSample(void);
+
+  virtual float length(void);
+  virtual AudioTraits::SampleClass::SampleStatus status(void);
+};
+
+class EXPCL_PANDA NullMusic : public AudioTraits::MusicClass {
+public:
+  INLINE NullMusic(void);
+  virtual ~NullMusic(void);
+
+  virtual AudioTraits::MusicClass::MusicStatus status(void);
+};
+
+class EXPCL_PANDA NullPlayer : public AudioTraits::PlayerClass {
+public:
+  INLINE NullPlayer(void);
+  virtual ~NullPlayer(void);
+
+  virtual void play_sample(AudioTraits::SampleClass*);
+  virtual void play_music(AudioTraits::MusicClass*);
+  virtual void set_volume(AudioTraits::SampleClass*, int);
+  virtual void set_volume(AudioTraits::MusicClass*, int);
+};
+
+#include "audio_null_traits.I"
+
+#endif /* __AUDIO_NULL_TRAITS_H__ */

+ 141 - 0
panda/src/audio/audio_pool.I

@@ -0,0 +1,141 @@
+// Filename: audio_pool.I
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::has_sample
+//       Access: Public, Static
+//  Description: Returns true if the sample has ever been loaded,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioPool::has_sample(const string& filename) {
+  return get_ptr()->ns_has_sample(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::verify_sample
+//       Access: Public, Static
+//  Description: Loads the given filename up into a sample, if it has
+//               not already been loaded, and returns true to indicate
+//               success, or false to indicate failure.  If this
+//               returns true, it is guaranteed that a subsequent call
+//               to load_sample() with the same sample name will
+//               return a valid AudioSample pointer.
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioPool::verify_sample(const string& filename) {
+  return load_sample(filename) != (AudioSample*)0L;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::load_sample
+//       Access: Public, Static
+//  Description: Loads the given filename up into a sample, if it has
+//               not already been loaded, and returns the new sample.
+//               If a sample with the same filename was previously
+//               loaded, returns that one instead.  If the sample
+//               file cannot be found, returns NULL.
+////////////////////////////////////////////////////////////////////
+INLINE AudioSample* AudioPool::load_sample(const string& filename) {
+  return get_ptr()->ns_load_sample(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::release_sample
+//       Access: Public, Static
+//  Description: Removes the indicated sample from the pool,
+//               indicating it will never be loaded again; the sample
+//               may then be freed.  If this function is never called,
+//               a reference count will be maintained on every sample
+//               ever loaded, and samples will never be freed.
+//
+//               The sample's name should not have been changed
+//               during its lifetime, or this function may fail to
+//               locate it in the pool.
+////////////////////////////////////////////////////////////////////
+INLINE void AudioPool::release_sample(AudioSample* sample) {
+  get_ptr()->ns_release_sample(sample);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::release_all_samples
+//       Access: Public, Static
+//  Description: Releases all samples in the pool and restores the
+//               pool to the empty state.
+////////////////////////////////////////////////////////////////////
+INLINE void AudioPool::release_all_samples(void) {
+  get_ptr()->ns_release_all_samples();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::has_music
+//       Access: Public, Static
+//  Description: Returns true if the music has ever been loaded,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioPool::has_music(const string& filename) {
+  return get_ptr()->ns_has_music(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::verify_music
+//       Access: Public, Static
+//  Description: Loads the given filename up into a music, if it has
+//               not already been loaded, and returns true to indicate
+//               success, or false to indicate failure.  If this
+//               returns true, it is guaranteed that a subsequent call
+//               to load_music() with the same music name will
+//               return a valid AudioMusic pointer.
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioPool::verify_music(const string& filename) {
+  return load_music(filename) != (AudioMusic*)0L;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::load_music
+//       Access: Public, Static
+//  Description: Loads the given filename up into a music, if it has
+//               not already been loaded, and returns the new music.
+//               If a music with the same filename was previously
+//               loaded, returns that one instead.  If the music
+//               file cannot be found, returns NULL.
+////////////////////////////////////////////////////////////////////
+INLINE AudioMusic* AudioPool::load_music(const string& filename) {
+  return get_ptr()->ns_load_music(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::release_music
+//       Access: Public, Static
+//  Description: Removes the indicated music from the pool,
+//               indicating it will never be loaded again; the music
+//               may then be freed.  If this function is never called,
+//               a reference count will be maintained on every music
+//               ever loaded, and music will never be freed.
+//
+//               The music's name should not have been changed
+//               during its lifetime, or this function may fail to
+//               locate it in the pool.
+////////////////////////////////////////////////////////////////////
+INLINE void AudioPool::release_music(AudioMusic* music) {
+  get_ptr()->ns_release_music(music);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::release_all_music
+//       Access: Public, Static
+//  Description: Releases all music in the pool and restores the
+//               pool to the empty state.
+////////////////////////////////////////////////////////////////////
+INLINE void AudioPool::release_all_music(void) {
+  get_ptr()->ns_release_all_music();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::Constructor
+//       Access: Private
+//  Description: The constructor is not intended to be called
+//               directly; there's only supposed to be one AudioPool
+//               in the universe and it constructs itself.
+////////////////////////////////////////////////////////////////////
+INLINE AudioPool::AudioPool(void) {}

+ 243 - 0
panda/src/audio/audio_pool.cxx

@@ -0,0 +1,243 @@
+// Filename: audio_pool.cxx
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_pool.h"
+#include "config_audio.h"
+#include <config_util.h>
+
+AudioPool* AudioPool::_global_ptr = (AudioPool*)0L;
+typedef map<string, AudioPool::SampleLoadFunc*> SampleLoaders;
+SampleLoaders* _sample_loaders = (SampleLoaders*)0L;
+typedef map<string, AudioPool::MusicLoadFunc*> MusicLoaders;
+MusicLoaders* _music_loaders = (MusicLoaders*)0L;
+
+////////////////////////////////////////////////////////////////////
+//     Function: check_sample_loaders
+//       Access: Static
+//  Description: ensure that the sample loaders map has been initialized
+////////////////////////////////////////////////////////////////////
+static void check_sample_loaders(void) {
+  if (_sample_loaders == (SampleLoaders*)0L)
+    _sample_loaders = new SampleLoaders;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: check_music_loaders
+//       Access: Static
+//  Description: ensure that the music loaders map has been initialized
+////////////////////////////////////////////////////////////////////
+static void check_music_loaders(void) {
+  if (_music_loaders == (MusicLoaders*)0L)
+    _music_loaders = new MusicLoaders;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::get_ptr
+//       Access: Private, Static
+//  Description: Initializes and/or returns the global pointer to the
+//               one AudioPool object in the system.
+////////////////////////////////////////////////////////////////////
+AudioPool* AudioPool::get_ptr(void) {
+  if (_global_ptr == (AudioPool*)0L)
+    _global_ptr = new AudioPool;
+  return _global_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_has_sample
+//       Access: Private
+//  Description: The nonstatic implementation of has_sample().
+////////////////////////////////////////////////////////////////////
+bool AudioPool::ns_has_sample(Filename filename) {
+  filename.resolve_filename(get_sound_path());
+
+  SampleMap::const_iterator si;
+  si = _samples.find(filename);
+  if (si != _samples.end()) {
+    // this sample was previously loaded
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_load_sample
+//       Access: Private
+//  Description: The nonstatic implementation of load_sample().
+////////////////////////////////////////////////////////////////////
+AudioSample* AudioPool::ns_load_sample(Filename filename) {
+  filename.resolve_filename(get_sound_path());
+
+  SampleMap::const_iterator si;
+  si = _samples.find(filename);
+  if (si != _samples.end()) {
+    // this sample was previously loaded
+    return (*si).second;
+  }
+  audio_cat.info() << "Loading sample " << filename << "\n";
+  AudioTraits::SampleClass* sample = (AudioTraits::SampleClass*)0L;
+  AudioTraits::PlayerClass* player = (AudioTraits::PlayerClass*)0L;
+  AudioTraits::DeleteSampleFunc* destroy = (AudioTraits::DeleteSampleFunc*)0L;
+  string ext = filename.get_extension();
+  SampleLoaders::const_iterator sli;
+  check_sample_loaders();
+  sli = _sample_loaders->find(ext);
+  if (sli == _sample_loaders->end()) {
+    audio_cat->error() << "no loader available for audio type '" << ext
+		       << "'" << endl;
+    return (AudioSample*)0L;
+  }
+  (*((*sli).second))(&sample, &player, &destroy, filename);
+  if ((sample == (AudioTraits::SampleClass*)0L) ||
+      (player == (AudioTraits::PlayerClass*)0L) ||
+      (destroy == (AudioTraits::DeleteSampleFunc*)0L)) {
+    audio_cat->error() << "could not load '" << filename << "'" << endl;
+    return (AudioSample*)0L;
+  }
+  PT(AudioSample) the_sample = new AudioSample(sample, player, destroy,
+					       filename);
+  _samples[filename] = the_sample;
+  return the_sample;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_release_sample
+//       Access: Private
+//  Description: The nonstatic implementation of release_sample().
+////////////////////////////////////////////////////////////////////
+void AudioPool::ns_release_sample(AudioSample* sample) {
+  string filename = sample->get_name();
+  SampleMap::iterator si;
+  si = _samples.find(filename);
+  if (si != _samples.end() && (*si).second == sample) {
+    _samples.erase(si);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_release_all_samples
+//       Access: Private
+//  Description: The nonstatic implementation of release_all_samples().
+////////////////////////////////////////////////////////////////////
+void AudioPool::ns_release_all_samples(void) {
+  _samples.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::register_sample_loader
+//       Access: Public, static
+//  Description: A static function to register a function for loading
+//               audio samples.
+////////////////////////////////////////////////////////////////////
+void AudioPool::register_sample_loader(const string& ext,
+				       AudioPool::SampleLoadFunc* func) {
+  SampleLoaders::const_iterator sli;
+  check_sample_loaders();
+  sli = _sample_loaders->find(ext);
+  if (sli != _sample_loaders->end()) {
+    audio_cat->warning() << "attempted to register a loader for audio type '"
+			 << ext << "' more then once." << endl;
+    return;
+  }
+  (*_sample_loaders)[ext] = func;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_has_music
+//       Access: Private
+//  Description: The nonstatic implementation of has_music().
+////////////////////////////////////////////////////////////////////
+bool AudioPool::ns_has_music(Filename filename) {
+  filename.resolve_filename(get_sound_path());
+
+  MusicMap::const_iterator si;
+  si = _music.find(filename);
+  if (si != _music.end()) {
+    // this music was previously loaded
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_load_music
+//       Access: Private
+//  Description: The nonstatic implementation of load_music().
+////////////////////////////////////////////////////////////////////
+AudioMusic* AudioPool::ns_load_music(Filename filename) {
+  filename.resolve_filename(get_sound_path());
+
+  MusicMap::const_iterator si;
+  si = _music.find(filename);
+  if (si != _music.end()) {
+    // this sample was previously loaded
+    return (*si).second;
+  }
+  audio_cat.info() << "Loading music " << filename << "\n";
+  AudioTraits::MusicClass* music = (AudioTraits::MusicClass*)0L;
+  AudioTraits::PlayerClass* player = (AudioTraits::PlayerClass*)0L;
+  AudioTraits::DeleteMusicFunc* destroy = (AudioTraits::DeleteMusicFunc*)0L;
+  string ext = filename.get_extension();
+  MusicLoaders::const_iterator sli;
+  check_music_loaders();
+  sli = _music_loaders->find(ext);
+  if (sli == _music_loaders->end()) {
+    audio_cat->error() << "no loader available for audio type '" << ext
+		       << "'" << endl;
+    return (AudioMusic*)0L;
+  }
+  (*((*sli).second))(&music, &player, &destroy, filename);
+  if ((music == (AudioTraits::MusicClass*)0L) ||
+      (player == (AudioTraits::PlayerClass*)0L) ||
+      (destroy == (AudioTraits::DeleteMusicFunc*)0L)) {
+    audio_cat->error() << "could not load '" << filename << "'" << endl;
+    return (AudioMusic*)0L;
+  }
+  PT(AudioMusic) the_music = new AudioMusic(music, player, destroy, filename);
+  _music[filename] = the_music;
+  return the_music;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_release_music
+//       Access: Private
+//  Description: The nonstatic implementation of release_music().
+////////////////////////////////////////////////////////////////////
+void AudioPool::ns_release_music(AudioMusic* music) {
+  string filename = music->get_name();
+  MusicMap::iterator si;
+  si = _music.find(filename);
+  if (si != _music.end() && (*si).second == music) {
+    _music.erase(si);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::ns_release_all_music
+//       Access: Private
+//  Description: The nonstatic implementation of release_all_music().
+////////////////////////////////////////////////////////////////////
+void AudioPool::ns_release_all_music(void) {
+  _music.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioPool::register_music_loader
+//       Access: Public, static
+//  Description: A static function to register a function for loading
+//               audio music.
+////////////////////////////////////////////////////////////////////
+void AudioPool::register_music_loader(const string& ext,
+				      AudioPool::MusicLoadFunc* func) {
+  MusicLoaders::const_iterator sli;
+  check_music_loaders();
+  sli = _music_loaders->find(ext);
+  if (sli != _music_loaders->end()) {
+    audio_cat->warning() << "attempted to register a loader for audio type '"
+			 << ext << "' more then once." << endl;
+    return;
+  }
+  (*_music_loaders)[ext] = func;
+}

+ 63 - 0
panda/src/audio/audio_pool.h

@@ -0,0 +1,63 @@
+// Filename: audio_pool.h
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_POOL_H__
+#define __AUDIO_POOL_H__
+
+#include "audio_sample.h"
+#include "audio_music.h"
+#include <map>
+#include <pandabase.h>
+#include <filename.h>
+#include <pointerTo.h>
+
+class EXPCL_PANDA AudioPool {
+private:
+  INLINE AudioPool(void);
+
+  bool ns_has_sample(Filename filename);
+  AudioSample* ns_load_sample(Filename filename);
+  void ns_release_sample(AudioSample* sample);
+  void ns_release_all_samples(void);
+
+  bool ns_has_music(Filename filename);
+  AudioMusic* ns_load_music(Filename filename);
+  void ns_release_music(AudioMusic* music);
+  void ns_release_all_music(void);
+
+  static AudioPool* get_ptr(void);
+
+  static AudioPool *_global_ptr;
+  typedef map<string, PT(AudioSample) > SampleMap;
+  SampleMap _samples;
+  typedef map<string, PT(AudioMusic) > MusicMap;
+  MusicMap _music;
+public:
+  typedef void SampleLoadFunc(AudioTraits::SampleClass**,
+			      AudioTraits::PlayerClass**,
+			      AudioTraits::DeleteSampleFunc**, Filename);
+
+  INLINE static bool has_sample(const string& filename);
+  INLINE static bool verify_sample(const string& filename);
+  INLINE static AudioSample* load_sample(const string& filename);
+  INLINE static void release_sample(AudioSample* sample);
+  INLINE static void release_all_samples(void);
+  static void register_sample_loader(const string&, SampleLoadFunc*);
+
+  typedef void MusicLoadFunc(AudioTraits::MusicClass**,
+			     AudioTraits::PlayerClass**,
+			     AudioTraits::DeleteMusicFunc**, Filename);
+
+  INLINE static bool has_music(const string& filename);
+  INLINE static bool verify_music(const string& filename);
+  INLINE static AudioMusic* load_music(const string& filename);
+  INLINE static void release_music(AudioMusic* music);
+  INLINE static void release_all_music(void);
+  static void register_music_loader(const string&, MusicLoadFunc*);
+};
+
+#include "audio_pool.I"
+
+#endif /* __AUDIO_POOL_H__ */

+ 67 - 0
panda/src/audio/audio_sample.I

@@ -0,0 +1,67 @@
+// Filename: audio_sample.I
+// Created by:  cary (23Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::constructor
+//       Access: Protected
+//  Description: initialize a new sample
+////////////////////////////////////////////////////////////////////
+INLINE AudioSample::AudioSample(AudioTraits::SampleClass* sample,
+				AudioTraits::PlayerClass* player,
+				AudioTraits::DeleteSampleFunc* destroy,
+				const string& filename) : Namable(filename),
+							  _sample(sample),
+							  _player(player),
+							  _destroy(destroy) {}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::copy constructor
+//       Access: Protected
+//  Description: copy a sample, but we don't really want to allow this
+////////////////////////////////////////////////////////////////////
+INLINE AudioSample::AudioSample(const AudioSample& c) : Namable(c.get_name()),
+							_sample(c._sample),
+							_player(c._player),
+							_destroy(c._destroy) {}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::assignment operator
+//       Access: Protected
+//  Description: copy a sample, but we don't really want to allow this
+////////////////////////////////////////////////////////////////////
+INLINE AudioSample& AudioSample::operator=(const AudioSample& c) {
+  this->set_name(c.get_name());
+  this->_sample = c._sample;
+  this->_player = c._player;
+  this->_destroy = c._destroy;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::get_player
+//       Access: Protected
+//  Description: return the player for this sample
+////////////////////////////////////////////////////////////////////
+INLINE AudioTraits::PlayerClass* AudioSample::get_player(void) {
+  return _player;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::get_sample
+//       Access: Protected
+//  Description: return the trait sample class for this sample
+////////////////////////////////////////////////////////////////////
+INLINE AudioTraits::SampleClass* AudioSample::get_sample(void) {
+  return _sample;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::equality operator
+//       Access: Public
+//  Description: test to see if two samples are the same
+////////////////////////////////////////////////////////////////////
+INLINE bool AudioSample::operator==(const AudioSample& c) const {
+  return (_sample == c._sample);
+}

+ 45 - 0
panda/src/audio/audio_sample.cxx

@@ -0,0 +1,45 @@
+// Filename: audio_sample.cxx
+// Created by:  cary (23Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_sample.h"
+#include "config_audio.h"
+
+TypeHandle AudioSample::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::destructor
+//       Access: Public
+//  Description: deletes the sample data and then lets the system
+//               destroy this structure
+////////////////////////////////////////////////////////////////////
+AudioSample::~AudioSample(void) {
+  (*_destroy)(_sample);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::length
+//       Access: Public
+//  Description: return the length (in seconds) of the sample
+////////////////////////////////////////////////////////////////////
+float AudioSample::length(void) {
+  return _sample->length();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioSample::status
+//       Access: Public
+//  Description: return the current play status of this sample
+////////////////////////////////////////////////////////////////////
+AudioSample::SampleStatus AudioSample::status(void) {
+  AudioTraits::SampleClass::SampleStatus stat = _sample->status();
+  switch (stat) {
+  case AudioTraits::SampleClass::READY:
+    return READY;
+  case AudioTraits::SampleClass::PLAYING:
+    return PLAYING;
+  }
+  audio_cat->error() << "unknown status for sample" << endl;
+  return READY;
+}

+ 63 - 0
panda/src/audio/audio_sample.h

@@ -0,0 +1,63 @@
+// Filename: audio_sample.h
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_SAMPLE_H__
+#define __AUDIO_SAMPLE_H__
+
+#include "audio_trait.h"
+#include "typedReferenceCount.h"
+#include "namable.h"
+
+class AudioPool;
+class AudioManager;
+
+class EXPCL_PANDA AudioSample : public TypedReferenceCount, public Namable {
+private:
+  AudioTraits::SampleClass *_sample;
+  AudioTraits::PlayerClass *_player;
+  AudioTraits::DeleteSampleFunc *_destroy;
+protected:
+  INLINE AudioSample(AudioTraits::SampleClass*, AudioTraits::PlayerClass*,
+		     AudioTraits::DeleteSampleFunc*, const string&);
+  INLINE AudioSample(const AudioSample&);
+  INLINE AudioSample& operator=(const AudioSample&);
+
+  INLINE AudioTraits::PlayerClass* get_player(void);
+  INLINE AudioTraits::SampleClass* get_sample(void);
+
+  friend class AudioPool;
+  friend class AudioManager;
+public:
+  virtual ~AudioSample(void);
+  INLINE bool operator==(const AudioSample&) const;
+
+  enum SampleStatus { READY, PLAYING } ;
+  
+  float length(void);
+  SampleStatus status(void);
+public:
+  // type stuff
+  static TypeHandle get_class_type(void) {
+    return _type_handle;
+  }
+  static void init_type(void) {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "AudioSample",
+		  TypedReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type(void) const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type(void) {
+    init_type();
+    return get_class_type();
+  }
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "audio_sample.I"
+
+#endif /* __AUDIO_SAMPLE_H__ */

+ 48 - 0
panda/src/audio/audio_trait.cxx

@@ -0,0 +1,48 @@
+// Filename: audio_trait.cxx
+// Created by:  cary (23Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_trait.h"
+#include "config_audio.h"
+
+AudioTraits::SampleClass::~SampleClass(void) {
+}
+
+float AudioTraits::SampleClass::length(void) {
+  audio_cat->error() << "In abstract SampleClass::length!" << endl;
+  return 0.;
+}
+
+AudioTraits::SampleClass::SampleStatus AudioTraits::SampleClass::status(void) {
+  audio_cat->error() << "In abstract SampleClass::status!" << endl;
+  return READY;
+}
+
+AudioTraits::MusicClass::~MusicClass(void) {
+}
+
+AudioTraits::MusicClass::MusicStatus AudioTraits::MusicClass::status(void) {
+  audio_cat->error() << "In abstract MusicClass::status!" << endl;
+  return READY;
+}
+
+AudioTraits::PlayerClass::~PlayerClass(void) {
+}
+
+void AudioTraits::PlayerClass::play_sample(AudioTraits::SampleClass*) {
+  audio_cat->error() << "In abstract PlayerClass::play_sample!" << endl;
+}
+
+void AudioTraits::PlayerClass::play_music(AudioTraits::MusicClass*) {
+  audio_cat->error() << "In abstract PlayerClass::play_music!" << endl;
+}
+
+void AudioTraits::PlayerClass::set_volume(AudioTraits::SampleClass*, int) {
+  audio_cat->error() << "In abstract PlayerClass::set_volume (sample)!"
+		     << endl;
+}
+
+void AudioTraits::PlayerClass::set_volume(AudioTraits::MusicClass*, int) {
+  audio_cat->error() << "In abstract PlayerClass::set_volume (music)!" << endl;
+}

+ 49 - 0
panda/src/audio/audio_trait.h

@@ -0,0 +1,49 @@
+// Filename: audio_trait.h
+// Created by:  frang (06Jul00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_TRAIT_H__
+#define __AUDIO_TRAIT_H__
+
+#include <pandabase.h>
+
+class EXPCL_PANDA AudioTraits {
+public:
+  class SampleClass;
+  class MusicClass;
+
+  typedef void DeleteSampleFunc(SampleClass*);
+  typedef void DeleteMusicFunc(MusicClass*);
+
+  class EXPCL_PANDA SampleClass {
+  public:
+    SampleClass(void) {}
+    virtual ~SampleClass(void);
+
+    enum SampleStatus { READY, PLAYING } ;
+
+    virtual float length(void) = 0;
+    virtual SampleStatus status(void) = 0;
+  };
+  class EXPCL_PANDA MusicClass {
+  public:
+    MusicClass(void) {}
+    virtual ~MusicClass(void);
+
+    enum MusicStatus { READY, PLAYING };
+    virtual MusicStatus status(void) = 0;
+  };
+  class EXPCL_PANDA PlayerClass {
+  public:
+    PlayerClass(void) {}
+    virtual ~PlayerClass(void);
+
+    virtual void play_sample(SampleClass*) = 0;
+    virtual void play_music(MusicClass*) = 0;
+    virtual void set_volume(SampleClass*, int) = 0;
+    virtual void set_volume(MusicClass*, int) = 0;
+  };
+};
+
+#endif /* __AUDIO_TRAIT_H__ */

+ 28 - 0
panda/src/audio/audio_win_traits.I

@@ -0,0 +1,28 @@
+// Filename: audio_win_traits.I
+// Created by:  cary (27Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE WinSample::WinSample(void) : _channel(NULL), _data(NULL), _len(0) {
+}
+
+INLINE LPDIRECTSOUNDBUFFER WinSample::get_channel(void) {
+  return _channel;
+}
+
+INLINE WinMusic::WinMusic(void) : _performance(NULL), _music(NULL),
+				  _buffer(NULL), _synth(NULL), _data(NULL),
+				  _len(0) {
+  init();
+}
+
+INLINE IDirectMusicPerformance* WinMusic::get_performance(void) {
+  return _performance;
+}
+
+INLINE IDirectMusicSegment* WinMusic::get_music(void) {
+  return _music;
+}
+
+INLINE WinPlayer::WinPlayer(void) {
+}

+ 660 - 0
panda/src/audio/audio_win_traits.cxx

@@ -0,0 +1,660 @@
+// Filename: audio_win_traits.cxx
+// Created by:  cary (27Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "audio_win_traits.h"
+#include "audio_manager.h"
+#include "config_audio.h"
+
+#include <direct.h>
+
+static bool have_initialized = false;
+static HWND global_hwnd;
+
+// these are used by the direct sound playing stuff
+static LPDIRECTSOUNDBUFFER soundPrimaryBuffer = NULL;
+static LPDIRECTSOUND soundDirectSound = NULL;
+
+// these are used by the direct music playing stuff
+static IDirectMusic* musicDirectMusic = NULL;
+static IDirectSound* musicDirectSound = NULL;
+
+#define CHECK_RESULT(_result, _msg) \
+  if (FAILED(_result)) { \
+    audio_cat->error() << _msg << " at " << __FILE__ << ":" << __LINE__ \
+                       << endl; \
+    return; \
+  }
+
+// #define MULTI_TO_WIDE(_in, _out) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, _in, -1, _out, DMUS_MAX_FILENAME)
+#define MULTI_TO_WIDE(x,y) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, y, -1, x, _MAX_PATH);
+
+static void update_win(void) {
+}
+
+static void initialize(void) {
+  if (have_initialized)
+    return;
+
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in winAudio initialize" << endl;
+
+  // rumor has it this will work, if it doesn't we need to create an invisible
+  // application window for this kind of thing
+  global_hwnd = GetDesktopWindow();
+
+  // initialize COM
+  HRESULT result = CoInitialize(NULL);
+  CHECK_RESULT(result, "CoInitialize failed");
+
+  //
+  // initialize the globals for the direct sound drivers
+  //
+
+  // create a direct sound object
+  result = DirectSoundCreate(NULL, &soundDirectSound, NULL);
+  CHECK_RESULT(result, "could not create a Direct Sound (tm) object (c)");
+
+  // set the cooperative level
+  result = soundDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_PRIORITY);
+  if (FAILED(result)) {
+    audio_cat->warning() << "could not set Direct Sound co-op level to "
+			 << "DSSCL_PRIORITY, trying DSSCL_NORMAL" << endl;
+    result = soundDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_NORMAL);
+    CHECK_RESULT(result, "failed setting to DSSCL_NORMAL");
+  }
+
+  // Move any unused portions of onboard sound memory to a contiguous block
+  // so that the largest portion of free memory will be available.
+  soundDirectSound->Compact();
+
+  // create the primary buffer
+  DSBUFFERDESC dsbd;
+  ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
+  dsbd.dwSize  =  sizeof(DSBUFFERDESC);
+  dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+  result = soundDirectSound->CreateSoundBuffer(&dsbd, &soundPrimaryBuffer,
+					       NULL);
+  CHECK_RESULT(result, "could not create primary buffer");
+
+  // set primary buffer format to 22kHz and 16-bit output
+  // COME BACK LATER TO MAKE THIS CONFIG
+  WAVEFORMATEX wfx;
+  ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
+  wfx.wFormatTag      = WAVE_FORMAT_PCM;
+  wfx.nChannels       = 2;
+  wfx.nSamplesPerSec  = 22050;
+  wfx.wBitsPerSample  = 16;
+  wfx.nBlockAlign     = wfx.wBitsPerSample / 8 * wfx.nChannels;
+  wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
+  result = soundPrimaryBuffer->SetFormat(&wfx);
+  // SetFormat requires at least DSSCL_PRIORITY, which we may not have
+  if (result == DSERR_PRIOLEVELNEEDED)
+    audio_cat->warning() << "could not set format of Primary Buffer because "
+			 << "we didn't get DSSCL_PRIORITY" << endl;
+
+/*
+  //
+  // initialize the globals for the direct music drivers
+  //
+
+  // create the direct sound object
+  result = DirectSoundCreate(NULL, &musicDirectSound, NULL);
+  CHECK_RESULT(result,
+	       "could not create a second Direct Sound (tm) object (c)");
+
+  // set the cooperative level
+  result = musicDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_PRIORITY);
+  if (FAILED(result)) {
+    audio_cat->warning() << "could not set Direct Sound (2) co-op level to "
+			 << "DSSCL_PRIORITY, trying DSSCL_NORMAL" << endl;
+    result = musicDirectSound->SetCooperativeLevel(global_hwnd, DSSCL_NORMAL);
+    CHECK_RESULT(result, "failed setting to DSSCL_NORMAL");
+  }
+
+  // create the direct music object
+  result = CoCreateInstance(CLSID_DirectMusic, NULL, CLSCTX_INPROC,
+			    IID_IDirectMusic, (void**)&musicDirectMusic);
+  CHECK_RESULT(result, "could not create Direct Music (tm) object (c)");
+
+  // set direct sound for direct music
+  result = musicDirectMusic->SetDirectSound(musicDirectSound, NULL);
+  CHECK_RESULT(result, "could not add Direct Sound (tm) to Direct Music (tm)");
+*/
+
+  //
+  // finish out with our stuff
+  //
+
+  AudioManager::set_update_func(update_win);
+  have_initialized = true;
+}
+
+static void shutdown(void) {
+  // release the primary sound buffer
+  if (soundPrimaryBuffer) {
+    soundPrimaryBuffer->Release();
+    soundPrimaryBuffer = NULL;
+  }
+
+  // release direct sound object
+  if (soundDirectSound) {
+    soundDirectSound->Release();
+    soundDirectSound = NULL;
+  }
+
+  // release direct music object
+  if (musicDirectMusic) {
+    musicDirectMusic->Release();
+    musicDirectMusic = NULL;
+  }
+
+  if (musicDirectSound) {
+    musicDirectSound->Release();
+    musicDirectSound = NULL;
+  }
+
+  // shutdown COM
+  CoUninitialize();
+}
+
+WinSample::~WinSample(void) {
+  // unload any data we have
+  if (_channel) {
+    _channel->Release();
+    _channel = NULL;
+  }
+  // we may or may not be leaking the _data
+}
+
+float WinSample::length(void) {
+  // DO THIS
+  return 0.;
+}
+
+AudioTraits::SampleClass::SampleStatus WinSample::status(void) {
+  if (_channel) {
+    DWORD dwStatus;
+    _channel->GetStatus(&dwStatus);
+    if (dwStatus & DSBSTATUS_PLAYING)
+      return AudioTraits::SampleClass::PLAYING;
+  }
+  return AudioTraits::SampleClass::READY;
+}
+
+BYTE* WinSample::lock(void) {
+  HRESULT result = _channel->Lock(0, 0, (void**)&_data, &_len, NULL, 0,
+				  DSBLOCK_ENTIREBUFFER);
+  if (FAILED(result)) {
+    audio_cat->error() << "failed to lock buffer" << endl;
+    return NULL;
+  }
+  return _data;
+}
+
+void WinSample::unlock(void) {
+  HRESULT result = _channel->Unlock(_data, _len, NULL, 0);
+  CHECK_RESULT(result, "failed to unlock buffer");
+}
+
+// these are used by the wav loader
+WAVEFORMATEX* pwfx;
+HMMIO hmmioIn;
+MMCKINFO ckIn;
+MMCKINFO ckInRiff;
+
+HRESULT readMMIO(HMMIO hmmio, MMCKINFO* pckInRIFF, WAVEFORMATEX** ppwfxInfo) {
+  MMCKINFO ckin;
+  PCMWAVEFORMAT pcmWaveFormat;
+
+  *ppwfxInfo = NULL;
+  if (mmioDescend(hmmio, pckInRIFF, NULL, 0) != 0)
+    return E_FAIL;
+  if ((pckInRIFF->ckid != FOURCC_RIFF) ||
+      (mmioFOURCC('W', 'A', 'V', 'E') != pckInRIFF->fccType))
+    return E_FAIL;
+  ckin.ckid = mmioFOURCC('f', 'm', 't', ' ');
+  if (mmioDescend(hmmio, &ckin, pckInRIFF, MMIO_FINDCHUNK) != 0)
+    return E_FAIL;
+  if (ckin.cksize < (LONG)sizeof(PCMWAVEFORMAT))
+    return E_FAIL;
+  if (mmioRead(hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat)) !=
+      sizeof(pcmWaveFormat))
+    return E_FAIL;
+  if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM) {
+    if ((*ppwfxInfo = new WAVEFORMATEX) == NULL)
+      return E_FAIL;
+    memcpy(*ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat));
+    (*ppwfxInfo)->cbSize = 0;
+  } else {
+    WORD cbExtraBytes = 0L;
+    if (mmioRead(hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
+      return E_FAIL;
+    *ppwfxInfo = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)+cbExtraBytes];
+    if (*ppwfxInfo == NULL)
+      return E_FAIL;
+    memcpy(*ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat));
+    (*ppwfxInfo)->cbSize = cbExtraBytes;
+    if (mmioRead(hmmio, (CHAR*)(((BYTE*)&((*ppwfxInfo)->cbSize))+sizeof(WORD)),
+		 cbExtraBytes) != cbExtraBytes) {
+      delete *ppwfxInfo;
+      *ppwfxInfo = NULL;
+      return E_FAIL;
+    }
+  }
+  if (mmioAscend(hmmio, &ckin, 0) != 0) {
+    delete *ppwfxInfo;
+    *ppwfxInfo = NULL;
+    return E_FAIL;
+  }
+  return S_OK;
+}
+
+HRESULT wave_open_file(const CHAR* strFileName, HMMIO* phmmioIn,
+		       WAVEFORMATEX** ppwfxInfo, MMCKINFO* pckInRIFF) {
+  HMMIO hmmio = NULL;
+  if ((hmmio = mmioOpen(const_cast<CHAR*>(strFileName), NULL,
+			MMIO_ALLOCBUF | MMIO_READ)) == NULL)
+    return E_FAIL;
+  HRESULT hr;
+  if (FAILED(hr = readMMIO(hmmio, pckInRIFF, ppwfxInfo))) {
+    mmioClose(hmmio, 0);
+    return hr;
+  }
+  *phmmioIn = hmmio;
+  return S_OK;
+}
+
+HRESULT wave_start_data_read(HMMIO* phmmioIn, MMCKINFO* pckIn,
+			     MMCKINFO* pckInRIFF) {
+  // seek to the data
+  if (mmioSeek(*phmmioIn, pckInRIFF->dwDataOffset + sizeof(FOURCC),
+	       SEEK_SET) == -1)
+    return E_FAIL;
+  //search the input file for the 'data' chunk
+  pckIn->ckid = mmioFOURCC('d', 'a', 't', 'a');
+  if (mmioDescend(*phmmioIn, pckIn, pckInRIFF, MMIO_FINDCHUNK) != 0)
+    return E_FAIL;
+  return S_OK;
+}
+
+HRESULT wave_read_file(HMMIO hmmio, UINT cbRead, BYTE* pbDest, MMCKINFO* pckIn,
+		       UINT* cbActualRead) {
+  MMIOINFO mmioinfoIn;
+  *cbActualRead = 0;
+  if (mmioGetInfo(hmmio, &mmioinfoIn, 0) != 0)
+    return E_FAIL;
+  UINT cbDataIn = cbRead;
+  if (cbDataIn > pckIn->cksize)
+    cbDataIn = pckIn->cksize;
+  for (DWORD cT=0; cT<cbDataIn; ++cT) {
+    // copy bytes from the io to the buffer
+    if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead) {
+      if (mmioAdvance(hmmio, &mmioinfoIn, MMIO_READ) != 0)
+	return E_FAIL;
+      if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
+	return E_FAIL;
+    }
+    // actual copy
+    *((BYTE*)pbDest+cT) = *((BYTE*)mmioinfoIn.pchNext);
+    mmioinfoIn.pchNext++;
+  }
+  if (mmioSetInfo(hmmio, &mmioinfoIn, 0) != 0)
+    return E_FAIL;
+  *cbActualRead = cbDataIn;
+  return S_OK;
+}
+
+HRESULT wave_load_internal(const CHAR* filename, WAVEFORMATEX& wavInfo,
+			   BYTE*& wavData, UINT& wavSize) {
+  wavData = NULL;
+  wavSize = 0;
+  HRESULT result = wave_open_file(filename, &hmmioIn, &pwfx, &ckInRiff);
+  if (FAILED(result))
+    return result;
+  result = wave_start_data_read(&hmmioIn, &ckIn, &ckInRiff);
+  if (SUCCEEDED(result)) {
+    memcpy(&wavInfo, pwfx, sizeof(WAVEFORMATEX));
+    DWORD size = ckIn.cksize + ckIn.dwDataOffset;
+    wavData = new BYTE[size];
+    result = wave_read_file(hmmioIn, size, wavData, &ckIn, &wavSize);
+  }
+  mmioClose(hmmioIn, 0);
+  return result;
+}
+
+HRESULT wave_load(const CHAR* filename, WAVEFORMATEX& wavInfo, BYTE*& wavData,
+		  UINT& wavSize) {
+  pwfx = NULL;
+  HRESULT result = wave_load_internal(filename, wavInfo, wavData, wavSize);
+  if (pwfx) {
+    delete pwfx;
+    pwfx = NULL;
+  }
+  return result;
+}
+
+WinSample* WinSample::load_wav(Filename filename) {
+  WinSample* ret = (WinSample*)0L;
+
+  initialize();
+  WAVEFORMATEX wavInfo;
+  UINT wavSize = 0;
+  BYTE* wavData = NULL;
+
+  HRESULT result = wave_load(filename.c_str(), wavInfo, wavData, wavSize);
+  if (FAILED(result)) {
+    if (wavData)
+      delete [] wavData;
+    return ret;
+  }
+
+  // create direct sound channel
+  ret = new WinSample();
+  DSBUFFERDESC dsbdDesc;
+  ZeroMemory(&dsbdDesc, sizeof(DSBUFFERDESC));
+  dsbdDesc.dwSize = sizeof(DSBUFFERDESC);
+/*
+  dsbdDesc.dwFlags = DSBCAPS_CTRLDEFAULT | DSBCAPS_STATIC |
+                     DSBCAPS_GLOBALFOCUS;
+*/
+  dsbdDesc.dwFlags = DSBCAPS_STATIC |
+                     DSBCAPS_GLOBALFOCUS;
+  dsbdDesc.dwBufferBytes = wavSize;
+  dsbdDesc.lpwfxFormat = &wavInfo;
+  dsbdDesc.lpwfxFormat->cbSize = sizeof(wavInfo);
+  result = soundDirectSound->CreateSoundBuffer(&dsbdDesc, &(ret->_channel), NULL);
+  if (FAILED(result)) {
+    delete ret;
+    ret = (WinSample*)0L;
+  }
+
+  if (ret) {
+    BYTE* dst = NULL;
+    dst = ret->lock();
+    try {
+      memcpy(dst, wavData, wavSize);
+    }
+    catch(...) {
+      delete ret;
+      ret = (WinSample*)0L;
+    }
+    if (ret)
+      ret->unlock();
+  }
+
+  if (wavData)
+    delete [] wavData;
+
+  return ret;
+}
+
+void WinSample::destroy(AudioTraits::SampleClass* sample) {
+  delete sample;
+}
+
+WinMusic::~WinMusic(void) {
+  // AudioManager::stop(this);
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinMusic::~WinMusic()" << endl;
+
+  if (_music) {
+    _music->Release();
+    _music = NULL;
+  }
+
+  if (_synth) {
+    _synth->Release();
+    _synth = NULL;
+  }
+
+  if (_performance) {
+    _performance->Release();
+    _performance = NULL;
+  }
+
+  if (_buffer) {
+    _buffer->Release();
+    _buffer = NULL;
+  }
+
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "out of WinMusic::~WinMusic()" << endl;
+}
+
+void WinMusic::init(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinMusic::init()" << endl;
+
+  initialize();
+  // create the direct sound performance object
+  HRESULT result = CoCreateInstance(CLSID_DirectMusicPerformance, NULL,
+				    CLSCTX_INPROC,
+				    IID_IDirectMusicPerformance2,
+				    (void**)&_performance);
+  if (FAILED(result)) {
+    audio_cat->error() << "could not create performance object" << endl;
+    _performance = NULL;
+    return;
+  }
+
+  // initialize performance object
+  // result = _performance->Init(&musicDirectMusic, NULL, NULL);
+  result = _performance->Init(NULL, NULL, NULL);
+  CHECK_RESULT(result, "could not initialize performance object");
+
+/*
+  // create the output synth object
+  DMUS_PORTPARAMS params;
+  ZeroMemory(&params, sizeof(DMUS_PORTPARAMS));
+  params.dwSize = sizeof(DMUS_PORTPARAMS);
+  result = musicDirectMusic->CreatePort(GUID_NULL, &params, &_synth, NULL);
+  CHECK_RESULT(result, "could not create synth");
+
+  // create sound buffer
+  WAVEFORMATEX format;
+  DWORD formatExSize;
+  DWORD bufferSize;
+  ZeroMemory(&format, sizeof(WAVEFORMATEX));
+  formatExSize = format.cbSize = sizeof(WAVEFORMATEX);
+  result = _synth->GetFormat(&format, &formatExSize, &bufferSize);
+  CHECK_RESULT(result, "failed to get format from synth");
+  DSBUFFERDESC bufferDesc;
+  ZeroMemory(&bufferDesc, sizeof(DSBUFFERDESC));
+  bufferDesc.dwSize = sizeof(DSBUFFERDESC);
+*
+  bufferDesc.dwFlags = DSBCAPS_CTRLDEFAULT | DSBCAPS_STICKYFOCUS;
+*
+  bufferDesc.dwFlags = DSBCAPS_STICKYFOCUS;
+  bufferDesc.dwBufferBytes = bufferSize;
+  bufferDesc.lpwfxFormat = &format;
+  bufferDesc.lpwfxFormat->cbSize = sizeof(WAVEFORMATEX);
+  result = musicDirectSound->CreateSoundBuffer(&bufferDesc, &_buffer, NULL);
+  CHECK_RESULT(result, "could not create buffer for music");
+
+  // initialize synth
+  result = _synth->SetDirectSound(musicDirectSound, _buffer);
+  CHECK_RESULT(result, "failed to initialize synth");
+
+  // activate synth
+  result = _synth->Activate(TRUE);
+  CHECK_RESULT(result, "failed to activate synth");
+*/
+
+  // add the synth to the performance
+  // result = _performance->AddPort(_synth);
+  result = _performance->AddPort(NULL);
+  CHECK_RESULT(result, "failed to add synth to performance");
+
+/*
+  // allocate performance channels
+  result = _performance->AssignPChannelBlock(0, _synth, 1);
+  CHECK_RESULT(result, "failed to assign performance channels");
+*/
+
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "out of WinMusic::init()  _performance = "
+                       << (void*)_performance << "  _synth = "
+                       << (void*)_synth << "  _buffer = " << (void*)_buffer
+                       << endl;
+}
+
+AudioTraits::MusicClass::MusicStatus WinMusic::status(void) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinMusic::status()" << endl;
+
+  if (_performance && _music) {
+    if (_performance->IsPlaying(_music, NULL) == S_OK) {
+      if (audio_cat->is_debug())
+	audio_cat->debug() << "returning PLAYING" << endl;
+      return PLAYING;
+    }
+  } else
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "MusicStatus no performance or music!" << endl;
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "returning READY" << endl;
+  return READY;
+}
+
+WinMusic* WinMusic::load_midi(Filename filename) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinMusic::load_midi()" << endl;
+  initialize();
+  // WinMusic* ret = (WinMusic*)0L;
+  WinMusic* ret = new WinMusic();
+  if (ret->_performance && ret->_music) {
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "for some reason, have to stop" << endl;
+    ret->_performance->Stop(NULL, NULL, 0, 0);
+  }
+  ret->_music = NULL;
+  IDirectMusicLoader* loader;
+  HRESULT result = CoCreateInstance(CLSID_DirectMusicLoader, NULL,
+				    CLSCTX_INPROC, IID_IDirectMusicLoader,
+				    (void**)&loader);
+  if (FAILED(result)) {
+    audio_cat->error() << "could not create music loader" << endl;
+    delete ret;
+    ret = (WinMusic*)0L;
+    return ret;
+  }
+
+  char szDir[_MAX_PATH];
+  WCHAR wszDir[_MAX_PATH];
+  if (_getcwd(szDir, _MAX_PATH)==NULL) {
+    audio_cat->error() << "could not getcwd" << endl;
+    delete ret;
+    ret = (WinMusic*)0L;
+    return ret;
+  }
+  MULTI_TO_WIDE(wszDir, szDir);
+  result = loader->SetSearchDirectory(GUID_DirectMusicAllTypes, wszDir, FALSE);
+  if (FAILED(result)) {
+    audio_cat->error() << "could not set search directory" << endl;
+    delete ret;
+    ret = (WinMusic*)0L;
+    return ret;
+  }
+
+  DMUS_OBJECTDESC fdesc;
+  fdesc.guidClass = CLSID_DirectMusicSegment;
+  fdesc.dwSize = sizeof(DMUS_OBJECTDESC);
+  // MULTI_TO_WIDE(filename.c_str(), fdesc.wszFileName);
+  MULTI_TO_WIDE(fdesc.wszFileName, filename.c_str());
+  // fdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
+  fdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
+  result = loader->GetObject(&fdesc, IID_IDirectMusicSegment2,
+			     (void**)&(ret->_music));
+  if (FAILED(result)) {
+    audio_cat->error() << "failed to load file" << endl;
+    loader->Release();
+    delete ret;
+    ret = (WinMusic*)0L;
+    return ret;
+  }
+  ret->_music->SetParam(GUID_StandardMIDIFile, -1, 0, 0,
+			(void*)(ret->_performance));
+  ret->_music->SetParam(GUID_Download, -1, 0, 0, (void*)(ret->_performance));
+  // ret->_buffer->SetVolume(0);
+  // ret->_buffer->SetPan(0);
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "out of WinMusic::load_midi()  _music = "
+                       << (void*)ret->_music << endl;
+  return ret;
+}
+
+void WinMusic::destroy(AudioTraits::MusicClass* music) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinMusic::destroy()" << endl;
+  delete music;
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "out of WinMusic::destroy()" << endl;
+}
+
+WinPlayer* WinPlayer::_global_instance = (WinPlayer*)0L;
+
+WinPlayer::~WinPlayer(void) {
+}
+
+void WinPlayer::play_sample(AudioTraits::SampleClass* sample) {
+  initialize();
+  WinSample* wsample = (WinSample*)sample;
+  LPDIRECTSOUNDBUFFER chan = wsample->get_channel();
+  if (chan) {
+    chan->Stop();
+    HRESULT result = chan->Play(0, 0, 0);
+    if (FAILED(result))
+      audio_cat->error() << "sample play failed" << endl;
+  }
+}
+
+void WinPlayer::play_music(AudioTraits::MusicClass* music) {
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "in WinPlayer::play_music()" << endl;
+  initialize();
+  WinMusic* wmusic = (WinMusic*)music;
+  IDirectMusicPerformance* _perf = wmusic->get_performance();
+  IDirectMusicSegment* _msc = wmusic->get_music();
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "about to jump in: _perf = " << (void*)_perf
+                       << "  _msc = " << (void*)_msc << endl;
+  if (_perf && _msc) {
+    if (audio_cat->is_debug())
+      audio_cat->debug() << "made it inside" << endl;
+    // _msc->SetRepeats(0);
+    IDirectMusicSegmentState* segState;
+    // HRESULT result = _perf->PlaySegment(_msc, 0, 0, NULL);
+    HRESULT result = _perf->PlaySegment(_msc, 0, 0, &segState);
+    if (result != S_OK) {
+      audio_cat->error() << "music play failed" << endl;
+      switch (result) {
+      case E_OUTOFMEMORY: audio_cat->error() << "reports out of memory" << endl;
+        break;
+      case E_POINTER: audio_cat->error() << "reports invalid pointer" << endl;
+        break;
+      case DMUS_E_NO_MASTER_CLOCK: audio_cat->error() << "reports no master clock" << endl;
+        break;
+      case DMUS_E_SEGMENT_INIT_FAILED: audio_cat->error() << "reports segment init failed" << endl;
+        break;
+      case DMUS_E_TIME_PAST: audio_cat->error() << "reports time past" << endl;
+        break;
+      };
+    }
+  }
+  if (audio_cat->is_debug())
+    audio_cat->debug() << "out of WinPlayer::play_music()" << endl;
+}
+
+void WinPlayer::set_volume(AudioTraits::SampleClass*, int) {
+}
+
+void WinPlayer::set_volume(AudioTraits::MusicClass*, int) {
+}
+
+WinPlayer* WinPlayer::get_instance(void) {
+  if (_global_instance == (WinPlayer*)0L)
+    _global_instance = new WinPlayer();
+  return _global_instance;
+}

+ 78 - 0
panda/src/audio/audio_win_traits.h

@@ -0,0 +1,78 @@
+// Filename: audio_win_traits.h
+// Created by:  cary (27Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __AUDIO_WIN_TRAITS_H__
+#define __AUDIO_WIN_TRAITS_H__
+
+#include "audio_trait.h"
+#include <filename.h>
+
+#include <windows.h>
+#include <dsound.h>
+#include <dmusici.h>
+
+class EXPCL_PANDA WinSample : public AudioTraits::SampleClass {
+private:
+  LPDIRECTSOUNDBUFFER _channel;
+  BYTE* _data;
+  DWORD _len;
+public:
+  INLINE WinSample(void);
+  virtual ~WinSample(void);
+
+  virtual float length(void);
+  virtual AudioTraits::SampleClass::SampleStatus status(void);
+public:
+  // these are used by the laoders
+  BYTE* lock(void);
+  void  unlock(void);
+  static WinSample* load_wav(Filename);
+  static void destroy(AudioTraits::SampleClass*);
+  // these are used by the player
+  INLINE LPDIRECTSOUNDBUFFER get_channel(void);
+};
+
+class EXPCL_PANDA WinMusic : public AudioTraits::MusicClass {
+private:
+  IDirectMusicPerformance* _performance;
+  IDirectMusicSegment* _music;
+  IDirectSoundBuffer* _buffer;
+  IDirectMusicPort* _synth;
+  BYTE* _data;
+  DWORD _len;
+
+  void init(void);
+public:
+  INLINE WinMusic(void);
+  virtual ~WinMusic(void);
+
+  virtual AudioTraits::MusicClass::MusicStatus status(void);
+  // these are used by the loaders
+  static WinMusic* load_midi(Filename);
+  static void destroy(AudioTraits::MusicClass*);
+  // these are used by the players
+  INLINE IDirectMusicPerformance* get_performance(void);
+  INLINE IDirectMusicSegment* get_music(void);
+};
+
+class EXPCL_PANDA WinPlayer : public AudioTraits::PlayerClass {
+public:
+  INLINE WinPlayer(void);
+  virtual ~WinPlayer(void);
+
+  virtual void play_sample(AudioTraits::SampleClass*);
+  virtual void play_music(AudioTraits::MusicClass*);
+  virtual void set_volume(AudioTraits::SampleClass*, int);
+  virtual void set_volume(AudioTraits::MusicClass*, int);
+public:
+  // used by the readers
+  static WinPlayer* get_instance(void);
+private:
+  static WinPlayer* _global_instance;
+};
+
+#include "audio_win_traits.I"
+
+#endif /* __AUDIO_WIN_TRAITS_H__ */

+ 41 - 0
panda/src/audio/config_audio.cxx

@@ -0,0 +1,41 @@
+// Filename: config_audio.cxx
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_audio.h"
+#include "audio_sample.h"
+#include "audio_music.h"
+#include <dconfig.h>
+
+Configure(config_audio);
+NotifyCategoryDef(audio, "");
+
+int audio_sample_voices = config_audio.GetInt("audio-sample-voices", 8);
+int audio_mix_freq = config_audio.GetInt("audio-mix-freq", 11025);
+string* audio_mode_flags;
+int audio_driver_select = config_audio.GetInt("audio-driver-select", 0);
+string* audio_driver_params;
+
+ConfigureFn(config_audio) {
+  AudioSample::init_type();
+  AudioMusic::init_type();
+
+  Config::ConfigTable::Symbol mode;
+  config_audio.GetAll("audio-mode-flag", mode);
+  Config::ConfigTable::Symbol::iterator i;
+  audio_mode_flags = new string;
+  for (i=mode.begin(); i!=mode.end(); ++i) {
+    if (!audio_mode_flags->empty())
+      *audio_mode_flags += " ";
+    *audio_mode_flags += (*i).Val();
+  }
+  Config::ConfigTable::Symbol parms;
+  config_audio.GetAll("audio-driver-param", parms);
+  audio_driver_params = new string;
+  for (i=parms.begin(); i!=parms.end(); ++i) {
+    if (!audio_driver_params->empty())
+      *audio_driver_params += " ";
+    *audio_driver_params += (*i).Val();
+  }
+}

+ 20 - 0
panda/src/audio/config_audio.h

@@ -0,0 +1,20 @@
+// Filename: config_audio.h
+// Created by:  cary (22Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef __CONFIG_AUDIO_H__
+#define __CONFIG_AUDIO_H__
+
+#include <pandabase.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(audio, EXPCL_PANDA, EXPTP_PANDA);
+
+extern int audio_sample_voices;
+extern int audio_mix_freq;
+extern string* audio_mode_flags;
+extern int audio_driver_select;
+extern string* audio_driver_params;
+
+#endif /* __CONFIG_AUDIO_H__ */

+ 41 - 0
panda/src/audio/test_audio.cxx

@@ -0,0 +1,41 @@
+// Filename: test_audio.cxx
+// Created by:  cary (24Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <pandabase.h>
+#include "audio.h"
+
+#include "config_audio.h"
+#include <ipc_traits.h>
+
+int
+main(int argc, char* argv[]) {
+  if (! AudioPool::verify_sample("test.wav")) {
+    audio_cat->fatal() << "could not locate 'test.wav'" << endl;
+    exit(-1);
+  }
+  AudioSample* sample = AudioPool::load_sample("test.wav");
+  audio_cat->info() << "test.wav is " << sample->length() << "sec long"
+		     << endl;
+  audio_cat->info() << "Playing test.wav" << endl;
+  AudioManager::play(sample);
+  while (sample->status() == AudioSample::PLAYING) {
+    AudioManager::update();
+    ipc_traits::sleep(0, 1000000);
+  }
+
+  //   AudioMidi foo("test.midi");
+  if (! AudioPool::verify_music("test.midi")) {
+    audio_cat->fatal() << "could not locate 'test.midi'" << endl;
+    exit(-1);
+  }
+  AudioMusic* music = AudioPool::load_music("test.midi");
+  audio_cat->info() << "Playing test.midi" << endl;
+  AudioManager::play(music);
+  while (music->status() == AudioMusic::PLAYING) {
+    AudioManager::update();
+    ipc_traits::sleep(0, 1000000);
+  }
+  return 0;
+}

+ 47 - 0
panda/src/builder/Sources.pp

@@ -0,0 +1,47 @@
+#define OTHER_LIBS interrogatedb dconfig dtoolutil dtoolbase
+
+#begin lib_target
+  #define TARGET builder
+  #define LOCAL_LIBS \
+    linmath gobj sgraph sgattrib graph putil gsgbase mathutil pnmimage \
+    pandabase
+
+  #define SOURCES \
+    builder.I builder.cxx builder.h builderAttrib.I builderAttrib.cxx \
+    builderAttrib.h builderBucket.I builderBucket.cxx builderBucket.h \
+    builderBucketNode.I builderBucketNode.cxx builderBucketNode.h \
+    builderMisc.cxx builderMisc.h builderNormalVisualizer.I \
+    builderNormalVisualizer.cxx builderNormalVisualizer.h \
+    builderPrim.cxx builderPrim.h builderProperties.cxx \
+    builderProperties.h builderTypes.cxx builderTypes.h builderVertex.I \
+    builderVertex.cxx builderVertex.h config_builder.cxx \
+    config_builder.h mesher.cxx mesher.h pta_BuilderC.cxx \
+    pta_BuilderC.h pta_BuilderN.cxx pta_BuilderN.h pta_BuilderTC.cxx \
+    pta_BuilderTC.h pta_BuilderV.cxx pta_BuilderV.h vector_BuilderC.cxx \
+    vector_BuilderC.h vector_BuilderN.cxx vector_BuilderN.h \
+    vector_BuilderTC.cxx vector_BuilderTC.h vector_BuilderV.cxx \
+    vector_BuilderV.h
+
+  #define INSTALL_HEADERS \
+    builder.I builder.h builderAttrib.I builderAttrib.h \
+    builderAttribTempl.I builderAttribTempl.h builderBucket.I \
+    builderBucket.h builderBucketNode.I builderBucketNode.h \
+    builderNormalVisualizer.I builderNormalVisualizer.h builderPrim.h \
+    builderPrimTempl.I builderPrimTempl.h builderProperties.h \
+    builderTypes.h builderVertex.I builderVertex.h builderVertexTempl.I \
+    builderVertexTempl.h config_builder.h pta_BuilderC.h pta_BuilderN.h \
+    pta_BuilderTC.h pta_BuilderV.h vector_BuilderC.h vector_BuilderN.h \
+    vector_BuilderTC.h vector_BuilderV.h
+
+#end lib_target
+
+#begin test_bin_target
+  #define TARGET test_builder
+  #define LOCAL_LIBS \
+    builder
+
+  #define SOURCES \
+    test_builder.cxx test_builder_data.cxx
+
+#end test_bin_target
+

+ 69 - 0
panda/src/builder/builder.I

@@ -0,0 +1,69 @@
+// Filename: builder.I
+// Created by:  drose (09Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::add_prim
+//       Access: Public
+//  Description: Adds the indicated nonindexed primitive, along with
+//               its associated bucket, to the builder's internal
+//               structures.  This copies all relevant information
+//               into the builder.
+//
+//               Returns true if the primitive was successfully added,
+//               false if it was invalid for some reason (for
+//               instance, a polygon with two vertices).
+////////////////////////////////////////////////////////////////////
+INLINE bool Builder::
+add_prim(const BuilderBucket &bucket, 
+	 const BuilderPrim &prim) {
+  add_bucket(bucket);
+  return ((BuilderBucketNode &)(*_bi)).add_prim(prim);
+}
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::add_prim
+//       Access: Public
+//  Description: Adds the indicated indexed primitive, along with
+//               its associated bucket, to the builder's internal
+//               structures.  This copies all relevant information
+//               into the builder.
+//
+//               Returns true if the primitive was successfully added,
+//               false if it was invalid for some reason (for
+//               instance, a polygon with two vertices).
+////////////////////////////////////////////////////////////////////
+INLINE bool Builder::
+add_prim(const BuilderBucket &bucket, 
+	 const BuilderPrimI &prim) {
+  add_bucket(bucket);
+  return ((BuilderBucketNode &)(*_bi)).add_prim(prim);
+}
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::add_prim_nonindexed
+//       Access: Public
+//  Description: Adds the specified indexed primitive as if it were
+//               nonindexed.  This simply looks up each coordinate
+//               value on the prim in its associated array and stores
+//               a nonindexed primitive with the actual coordinate
+//               values, instead of the index numbers.
+//
+//               This is handy for having code that calls the builder
+//               and might want to build either indexed or nonindexed
+//               geometry, as selected by the user at run-time.
+//               Simply build indexed geometry in all cases, then call
+//               either add_prim or add_prim_nonindexed, according to
+//               the user's selection.
+////////////////////////////////////////////////////////////////////
+INLINE bool Builder::
+add_prim_nonindexed(const BuilderBucket &bucket, 
+		    const BuilderPrimI &prim) {
+  add_bucket(bucket);
+  return ((BuilderBucketNode &)(*_bi)).add_prim_nonindexed(prim);
+}

+ 229 - 0
panda/src/builder/builder.cxx

@@ -0,0 +1,229 @@
+// Filename: builder.cxx
+// Created by:  drose (09Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builder.h"
+#include "builderFuncs.h"
+#include "builderMisc.h"
+
+#include <notify.h>
+#include <namedNode.h>
+#include <geomNode.h>
+#include <renderRelation.h>
+
+#include <map>
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Builder::
+Builder() {
+  _bi = _buckets.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Builder::
+~Builder() {
+  // Free all the buckets we allocated.  We allocated 'em, we free
+  // 'em.
+  Buckets::iterator bi;
+  for (bi = _buckets.begin();
+       bi != _buckets.end();
+       ++bi) {
+    BuilderBucket *bucket = (*bi).get_bucket();
+    delete bucket;
+  }
+}
+
+
+// We use the NodeMap class to build up a map of Nodes to GeomNodes.
+// There may be several buckets that point to the same Node; these
+// should all be given the same GeomNode, when possible.
+
+// However, if two buckets have different sets of scene graph
+// properties--that is, the _trans member is different--they must be
+// given separate GeomNodes.
+
+// Furthermore, it's possible to name each bucket.  If two buckets
+// with the same Node pointer have different names, then they should
+// be given two different GeomNodes.
+
+class NodeMap : public Namable {
+public:
+  NodeMap(NamedNode *node, const BuilderBucket *bucket) 
+    : _node(node), _bucket(bucket) { }
+  
+  bool operator < (const NodeMap &other) const {
+    if (_node != other._node) {
+      return _node < other._node;
+    }
+    if (_bucket->get_name() != other._bucket->get_name()) {
+      return _bucket->get_name() < other._bucket->get_name();
+    }
+    return (_bucket->_trans.compare_to(other._bucket->_trans) < 0);
+  }
+
+  NamedNode *_node;
+
+  // Although a bucket pointer is stored here in the NodeMap class,
+  // you should not use it except to extract the name and/or the
+  // _trans member.  Remember, this bucket pointer stands for any of
+  // possibly several bucket pointers, all different, except that they
+  // share the same name and _trans.
+  const BuilderBucket *_bucket;
+};
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::build
+//       Access: Public
+//  Description: Creates Geoms for all the primitives added to all
+//               buckets, and adds them where appropriate to place
+//               them in the scene graph under their respective
+//               parents, and/or returns a single GeomNode that
+//               contains all geometry whose bucket did not reference
+//               a particular scene graph node to parent them to.
+//
+//               If a bucket's _node pointer was a GeomNode, the
+//               geometry will be added directly to that node.  If the
+//               _node pointer was any other kind of node, a GeomNode
+//               will be created and parented to that node, and its
+//               name will be the name of the bucket.  In this case,
+//               the name of the bucket can also be used to different
+//               nodes: if two buckets reference the same node, but
+//               have different names, then two different GeomNodes
+//               are created, one with each name.
+////////////////////////////////////////////////////////////////////
+GeomNode *Builder::
+build(const string &default_name) {
+  typedef map<NodeMap, GeomNode *, less<NodeMap> > GeomNodeMap;
+  GeomNodeMap geom_nodes;
+
+  // First, build all the Geoms and create GeomNodes for them.  Each
+  // unique Node gets its own GeomNode.  If the Node is itself a
+  // GeomNode, that GeomNode is used directly.
+  Buckets::iterator i;
+  for (i = _buckets.begin();
+       i != _buckets.end();
+       ++i) {
+    BuilderBucket *bucket = (*i).get_bucket();
+    NamedNode *node = bucket->_node;
+    const string &name = bucket->get_name();
+    GeomNode *geom_node = NULL;
+
+    if (node!=NULL && node->is_of_type(GeomNode::get_class_type())) {
+      // The node is a GeomNode.  In this case, we simply use that
+      // node.  We can't separate them out by name in this case; we'll
+      // just assign to it the first nonempty name we encounter.
+      geom_node = (GeomNode *)node;
+
+      // Since the caller already created this GeomNode and passed it
+      // in, we'll leave it up to the caller to name the node and set
+      // up the state transitions leading into it.
+      
+    } else {
+      // The node is not a GeomNode, so look it up in the map.
+      GeomNodeMap::iterator f = geom_nodes.find(NodeMap(node, bucket));
+      if (f != geom_nodes.end()) {
+	geom_node = (*f).second;
+	
+      } else {
+	// No such node/name combination.  Create a new one.
+	geom_node = bucket->make_geom_node();
+	if (geom_node != NULL) {
+	  geom_nodes[NodeMap(node, bucket)] = geom_node;
+	}
+      }
+    }
+
+    if (geom_node != NULL) {
+      (*i).build(geom_node);
+    }
+  }
+
+  // Now go through and parent the geom_nodes under their respective
+  // group nodes.  Save out the geom_node associated with a NULL Node;
+  // this one is returned from this function.
+
+  GeomNode *base_geom_node = NULL;
+
+  GeomNodeMap::iterator gi;
+
+  for (gi = geom_nodes.begin();
+       gi != geom_nodes.end();
+       ++gi) {
+    const NodeMap &nm = (*gi).first;
+    GeomNode *geom_node = (*gi).second;
+
+    NamedNode *node = nm._node;
+    const string &name = nm._bucket->get_name();
+    const NodeTransitions &trans = nm._bucket->_trans;
+
+    // Assign the name to the geom, if it doesn't have one already.
+    if (!geom_node->has_name()) {
+      if (!name.empty()) {
+	geom_node->set_name(name);
+	
+      } else if (!default_name.empty()) {
+	geom_node->set_name(default_name);
+      }
+    }
+
+    // Only reparent the geom_node if it has no parent already.
+    if (geom_node->_parents.empty()) {
+      if (geom_node->get_num_geoms() == 0) {
+	// If there was nothing added, never mind.
+	delete geom_node;
+	
+      } else if (node==NULL) {
+	nassertr(base_geom_node == NULL, NULL);
+	base_geom_node = geom_node;
+	
+      } else {
+	RenderRelation *arc = new RenderRelation(node, geom_node);
+	// Now, this is our only opportunity to apply the scene-graph
+	// state specified in the bucket to the node: we have created
+	// our own geom_node for these buckets, and we have parented
+	// it to the scene graph.
+	arc->copy_transitions_from(trans);
+      }
+    }
+  }
+
+  return base_geom_node;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Builder::add_bucket
+//       Access: Protected
+//  Description: Adds a new BuilderBucket just like the given one to
+//               the set of all used BuilderBuckets, and makes it the
+//               current bucket.  Future primitives will be added to
+//               this bucket.
+////////////////////////////////////////////////////////////////////
+void Builder::
+add_bucket(const BuilderBucket &bucket) {
+  // Optimization: maybe it's the same bucket we used last time.
+  if (_bi != _buckets.end() &&
+      (*_bi) == BuilderBucketNode((BuilderBucket *)&bucket)) {
+    return;
+  }
+
+  // Nope.  Look again.
+  _bi = _buckets.find((BuilderBucket *)&bucket);
+  if (_bi == _buckets.end()) {
+    BuilderBucket *new_bucket = bucket.make_copy();
+    _bi = _buckets.insert(new_bucket).first;
+  }
+}
+

+ 198 - 0
panda/src/builder/builder.h

@@ -0,0 +1,198 @@
+// Filename: builder.h
+// Created by:  drose (09Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDER_H
+#define BUILDER_H
+
+////////////////////////////////////////////////////////////////////
+//
+// Builder
+//
+// The builder accepts as input a loose collection of polygons with
+// various attributes, sizes, and shapes, and does all the work of
+// grouping relating polygons and creating triangle strips, etc.,
+// ultimately storing the resulting optimized geometry into one or
+// more GeomNodes.
+//
+// It is intended that the builder should be the single entry point
+// for all code wishing to create geometry in the scene graph.  The
+// builder can know about the kinds of geometry that are optimal for a
+// particular platform, or even about the kinds of geometry that are
+// available for a given platform.  (For instance, perhaps on some
+// bizarre platform, triangle strips do not exist, but quadstrips are
+// really fast.  User code should not create triangle strips
+// directly.)
+//
+// Actually, there are two fairly separate pieces in this package.
+// The first is the builder itself, which handles the interface to
+// user code, and is responsible for collecting polygons from the
+// caller, sorting them according to their attributes, and creating
+// Geoms that represent the resulting geometry.  The second piece is
+// the mesher, which receives geometry from the builder and tries to
+// create optimal triangle strips (or whichever kind of higher-level
+// structure is most appropriate) from them, which it hands back to
+// the builder.
+//
+// It is possible to use the builder without invoking the mesher, in
+// which case the builder will create Geoms with the individual prims
+// exactly as the user passed them in.  It is not possible to use the
+// mesher without first going through the builder.
+//
+//
+// The general system of using the builder is as follows:
+//
+//  (a) Create a Builder object.
+//
+//  (b) Iterate through the polygons.  For each polygon:
+//
+//      (c) Create a BuilderBucket object and assign to it the
+//          scene-graph level attributes, such as texture, lighting,
+//          etc. for your polygon.  If several polygons share the same
+//          attributes, they can of course use the same bucket.  But
+//          there's no reason to be afraid of creating a new bucket
+//          object each time, if that's more convenient.
+//
+//      (d) Create a BuilderPrim object to describe the polygon.  If
+//          the polygon is to have a polygon color or polygon normal,
+//          set these on the BuilderPrim.
+//
+//      (e) Iterate through the polygon vertices, in counterclockwise
+//          order when viewed from the front of the polygon.  For each
+//          vertex:
+//
+//          (f) Create a BuilderVertex object.  If the vertex has a
+//              texture coordinate, normal, or color, set this on the
+//              BuilderVertex.
+//
+//          (g) Add the BuilderVertex to the BuilderPrim.
+//
+//      (h) Add the BuilderPrim to the Builder.
+//
+//  (i) Call Builder::build() and receive your new geometry!
+//
+// All of these objects--BuilderBucket, BuilderPrim, and
+// BuilderVertex--can, and probably should, be ordinary local
+// variables.  When they are added into their respective data
+// structures they are copied, not referenced, so there's no need to
+// try to keep them around after that.
+//
+// The BuilderBucket is the builder's system for grouping polygons
+// that share similar characteristings.  Polygons that were added to
+// the builder with equivalent (though not necessarily identical)
+// buckets may be candidates for joining together into triangle strips
+// when possible.
+//
+//
+// That's the basic operation.  There are lots of fancy features on
+// top of that.
+//
+//  * Other kinds of geometry than polygons are supported.  Presently,
+//    these are light points and line segments.  To add these kinds of
+//    geometry, call set_type() on your BuilderPrim with either
+//    BPT_point or BPT_line.
+//
+//  * Indexed geometry is supported as well as nonindexed.  Indexed
+//    geometry means that the vertices, UV's, etc. are referenced
+//    indirectly; an index number into a table is stored instead of
+//    the actual coordinate values.  Indexed geometry may be freely
+//    mixed in with nonindexed geometry; the builder will sort them
+//    out (although each polygon must be either entirely indexed or
+//    entirely nonindexed).  To create indexed geometry, use a
+//    BuilderPrimI object, and assign to it a number of BuilderVertexI
+//    vertices.  The coordinate values you will assign are ushort
+//    array index numbers.  Store the array pointers these refer to in
+//    the BuilderBucket, via set_coords(), set_normals(), etc.
+//
+//  * The builder is to a certain extent scene-graph aware.  In the
+//    normal usage, you give it a bunch of polygons which are all
+//    lumped together, and when you call build() it allocates and
+//    returns a GeomNode which has all of your geometry in it.
+//    However, you can also ask it to distribute the geometry
+//    throughout a pre-existing scene graph.  To do this, assign the
+//    _node pointer of your BuilderBucket to point to the node each
+//    polygon belongs under, as you create the polygons.  Now when you
+//    call build(), the builder will create all the polygons under the
+//    nodes you indicated, creating new GeomNodes whenever necessary.
+//    The advantage to this method is that you don't have to process
+//    your polygons in scene-graph order; the builder can sort them
+//    out for you.  Another advantage is it allows the builder to set
+//    up the state for you, see the next point:
+//
+//  * It is only when you are taking advantage of the scene-graph
+//    awareness of the builder that the builder can assign the state
+//    transitions (like texturing, etc.) you specify to the geometry
+//    it builds.  This is because the state transitions are stored on
+//    the arcs of the scene graph, and in non-scene graph mode there
+//    are no arcs!
+//
+//  * You can fine-tune the mesher behavior via a number of parameters
+//    on the BuilderBucket.  Look in builderProperties.h for these
+//    parameters (BuilderBucket inherits from BuilderProperties).
+//    This is also where you turn the mesher off if you don't want it.
+//
+//  * You can set global properties on all buckets easily either by
+//    creating your own default BuilderBucket that you use to
+//    initialize each individual BuilderBucket you create, or by
+//    changing the parameters stored in the bucket pointed to by
+//    BuilderBucket::get_default_bucket(), which is what is used to
+//    initialize any BuilderBucket created with a default constructor.
+//    It is suggested that the get_default_bucket() pointer be used to
+//    define global defaults at applications start-up, while a local
+//    default BuilderBucket should be used for local defaults.
+//
+//  * You can also control the binning behavior, if you have some
+//    particular user-specific parameters you want your geometry to be
+//    grouped on.  To do this, subclass from BuilderBucket and
+//    redefine the comparison operator (it's a virtual function), as
+//    well as the make_copy() function.
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+#include <pandabase.h>
+
+#include "builderAttrib.h"
+#include "builderBucketNode.h"
+
+#include <pointerTo.h>
+
+#include <set>
+
+
+class GeomNode;
+
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : Builder
+// Description : The main driver class to the builder package.  See
+//               the comments above.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG Builder {
+public:
+  Builder();
+  ~Builder();
+
+  INLINE bool add_prim(const BuilderBucket &bucket,
+		       const BuilderPrim &prim);
+  INLINE bool add_prim(const BuilderBucket &bucket,
+		       const BuilderPrimI &prim);
+  INLINE bool add_prim_nonindexed(const BuilderBucket &bucket,
+				  const BuilderPrimI &prim);
+
+  GeomNode *build(const string &default_name = "");
+
+protected:
+  void add_bucket(const BuilderBucket &bucket);
+
+  typedef set<BuilderBucketNode, less<BuilderBucketNode> > Buckets;
+ 
+  Buckets _buckets;
+  Buckets::iterator _bi;
+};
+
+#include "builder.I"
+
+#endif

+ 69 - 0
panda/src/builder/builderAttrib.I

@@ -0,0 +1,69 @@
+// Filename: builderAttrib.I
+// Created by:  drose (22Jan99)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttrib::set_normal_value
+//       Access: Public
+//  Description: Reassigns the normal, without knowing whether the
+//               attribute is indexed or nonindexed.  A nonindexed
+//               attribute will look up the index in the array and
+//               store the resulting value, while an indexed attribute
+//               will just store the index number (which assumes the
+//               array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderAttrib::
+set_normal_value(const BuilderN *array, ushort index) {
+  set_normal(array[index]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttrib::set_color_value
+//       Access: Public
+//  Description: Reassigns the color, without knowing whether the
+//               attribute is indexed or nonindexed.  A nonindexed
+//               attribute will look up the index in the array and
+//               store the resulting value, while an indexed attribute
+//               will just store the index number (which assumes the
+//               array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderAttrib::
+set_color_value(const BuilderC *array, ushort index) {
+  set_color(array[index]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribI::set_normal_value
+//       Access: Public
+//  Description: Reassigns the normal, without knowing whether the
+//               attribute is indexed or nonindexed.  A nonindexed
+//               attribute will look up the index in the array and
+//               store the resulting value, while an indexed attribute
+//               will just store the index number (which assumes the
+//               array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderAttribI::
+set_normal_value(const BuilderN *, ushort index) {
+  set_normal(index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribI::set_color_value
+//       Access: Public
+//  Description: Reassigns the color, without knowing whether the
+//               attribute is indexed or nonindexed.  A nonindexed
+//               attribute will look up the index in the array and
+//               store the resulting value, while an indexed attribute
+//               will just store the index number (which assumes the
+//               array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderAttribI::
+set_color_value(const BuilderC *, ushort index) {
+  set_color(index);
+}

+ 12 - 0
panda/src/builder/builderAttrib.cxx

@@ -0,0 +1,12 @@
+// Filename: builderAttrib.cxx
+// Created by:  drose (11May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderAttrib.h"
+
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma implementation
+#endif

+ 59 - 0
panda/src/builder/builderAttrib.h

@@ -0,0 +1,59 @@
+// Filename: builderAttrib.h
+// Created by:  drose (22Jan99)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERATTRIB_H
+#define BUILDERATTRIB_H
+
+///////////////////////////////////////////////////////////////////
+//
+// BuilderAttrib, BuilderAttribI
+//
+// This is the parent class of both BuilderVertex and BuilderPrim, and
+// contains the attribute values which may be set on either of them:
+// specifically, normal, color, and pixel size. (Pixel size is the
+// thickness of the lines, for a polygon or a line, or the size of the
+// point, in pixels.)
+//
+// Like BuilderPrim and BuilderVertex, the two classes BuilderAttrib
+// and BuilderAttribI are actually both instantiations of the same
+// template class, BuilderAttribTempl.
+//
+///////////////////////////////////////////////////////////////////
+
+#include <pandabase.h>
+
+#include "builderAttribTempl.h"
+
+#define BUILDERATTRIBTEMPL_BUILDERV BuilderAttribTempl<BuilderV, BuilderN, BuilderTC, BuilderC>
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERATTRIBTEMPL_BUILDERV);
+#define BUILDERATTRIBTEMPL_USHORT BuilderAttribTempl<ushort, ushort, ushort, ushort>
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERATTRIBTEMPL_USHORT);
+
+class EXPCL_PANDAEGG BuilderAttrib
+  : public BuilderAttribTempl<BuilderV, BuilderN, BuilderTC, BuilderC> {
+public:
+  BuilderAttrib() {}
+
+  INLINE void set_normal_value(const BuilderN *array, ushort index);
+  INLINE void set_color_value(const BuilderC *array, ushort index);
+};
+
+class EXPCL_PANDAEGG BuilderAttribI
+  : public BuilderAttribTempl<ushort, ushort, ushort, ushort> {
+public:
+  BuilderAttribI() {}
+
+  INLINE void set_normal_value(const BuilderN *array, ushort index);
+  INLINE void set_color_value(const BuilderC *array, ushort index);
+};
+
+#include "builderAttrib.I"
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#endif

+ 244 - 0
panda/src/builder/builderAttribTempl.I

@@ -0,0 +1,244 @@
+// Filename: builderAttribTempl.I
+// Created by:  drose (17Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT>::
+BuilderAttribTempl() {
+  _flags = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::Copy constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT>::
+BuilderAttribTempl(const BuilderAttribTempl &copy) {
+  (*this) = copy;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::Copy assignment operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT> &BuilderAttribTempl<VT, NT, TT, CT>::
+operator = (const BuilderAttribTempl<VT, NT, TT, CT> &copy) {
+  _normal = copy._normal;
+  _color = copy._color;
+  _pixel_size = copy._pixel_size;
+  _flags = copy._flags;
+
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::clear
+//       Access: Public
+//  Description: Resets the attribute flags to their original, empty
+//               state--where no attributes have been applied.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT> &BuilderAttribTempl<VT, NT, TT, CT>::
+clear() {
+  _flags = 0;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::has_normal
+//       Access: Public
+//  Description: Returns true if the attribute has a normal.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderAttribTempl<VT, NT, TT, CT>::
+has_normal() const {
+  return (_flags & BAF_normal)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::get_normal
+//       Access: Public
+//  Description: Returns the attribute's normal.  It is an error to
+//               call this without first verifying that has_normal() is
+//               true.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT>::NType BuilderAttribTempl<VT, NT, TT, CT>::
+get_normal() const {
+  nassertr(has_normal(), _normal);
+  return _normal;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::set_normal
+//       Access: Public
+//  Description: Resets the attribute's normal.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT> &BuilderAttribTempl<VT, NT, TT, CT>::
+set_normal(const NType &n) {
+  _flags |= BAF_normal;
+  _normal = n;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::has_color
+//       Access: Public
+//  Description: Returns true if the attribute has a color.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderAttribTempl<VT, NT, TT, CT>::
+has_color() const {
+  return (_flags & BAF_color)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::get_color
+//       Access: Public
+//  Description: Returns the attribute's color.  It is an error to
+//               call this without first verifying that has_color() is
+//               true.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT>::CType BuilderAttribTempl<VT, NT, TT, CT>::
+get_color() const {
+  nassertr(has_color(), _color);
+  return _color;
+}
+
+template <class VT, class NT, class TT, class CT>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::set_color
+//       Access: Public
+//  Description: Resets the attribute's color.
+////////////////////////////////////////////////////////////////////
+INLINE BuilderAttribTempl<VT, NT, TT, CT> &BuilderAttribTempl<VT, NT, TT, CT>::
+set_color(const CType &c) {
+  _flags |= BAF_color;
+  _color = c;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::has_pixel_size
+//       Access: Public
+//  Description: Returns true if the attribute has a pixel_size.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderAttribTempl<VT, NT, TT, CT>::
+has_pixel_size() const {
+  return (_flags & BAF_pixel_size)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::get_pixel_size
+//       Access: Public
+//  Description: Returns the attribute's pixel_size.  It is an error to
+//               call this without first verifying that has_pixel_size() is
+//               true.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE float BuilderAttribTempl<VT, NT, TT, CT>::
+get_pixel_size() const {
+  nassertr(has_pixel_size(), _pixel_size);
+  return _pixel_size;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::set_pixel_size
+//       Access: Public
+//  Description: Resets the attribute's pixel_size.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderAttribTempl<VT, NT, TT, CT> &BuilderAttribTempl<VT, NT, TT, CT>::
+set_pixel_size(float s) {
+  _flags |= BAF_pixel_size;
+  _pixel_size = s;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::operator ==
+//       Access: Public
+//  Description: Assigns an ordering to the vertices.  This is used by
+//               the Mesher to group identical vertices.  This assumes
+//               that all vertices in the locus of consideration will
+//               share the same state: with or without normals,
+//               texcoords, etc.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderAttribTempl<VT, NT, TT, CT>::
+operator == (const BuilderAttribTempl<VT, NT, TT, CT> &other) const {
+  if (has_normal() && !(_normal == other._normal))
+    return false;
+
+  if (has_color() && !(_color == other._color))
+    return false;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::operator <
+//       Access: Public
+//  Description: Assigns an ordering to the vertices.  This is used by
+//               the Mesher to group identical vertices.  This assumes
+//               that all vertices in the locus of consideration will
+//               share the same state: with or without normals,
+//               texcoords, etc.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderAttribTempl<VT, NT, TT, CT>::
+operator < (const BuilderAttribTempl<VT, NT, TT, CT> &other) const {
+  if (has_normal() && !(_normal == other._normal))
+    return _normal < other._normal;
+
+  if (has_color() && !(_color == other._color))
+    return _color < other._color;
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderAttribTempl::output
+//       Access: Public
+//  Description: Formats the attribs for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE ostream &BuilderAttribTempl<VT, NT, TT, CT>::
+output(ostream &out) const {
+  if (this!=NULL) {
+    if (has_normal()) {
+      out << " normal " << get_normal();
+    }
+    
+    if (has_color()) {
+      out << " color " << get_color();
+    }
+  }
+  return out;
+}

+ 70 - 0
panda/src/builder/builderAttribTempl.h

@@ -0,0 +1,70 @@
+// Filename: builderAttribTempl.h
+// Created by:  drose (17Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef BUILDERATTRIBTEMPL_H
+#define BUILDERATTRIBTEMPL_H
+
+#include <pandabase.h>
+
+#include "builderTypes.h"
+
+#include <vector>
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : BuilderAttribTempl
+// Description : The main body of BuilderAttrib and BuilderAttribI,
+//               and the base class for BuilderVertexTempl and
+//               BuilderPrimTempl, this class defines the attributes
+//               that may be specified for either vertices or
+//               primitives.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+class BuilderAttribTempl {
+public:
+  typedef VT VType;
+  typedef NT NType;
+  typedef TT TType;
+  typedef CT CType;
+
+  INLINE BuilderAttribTempl();
+  INLINE BuilderAttribTempl(const BuilderAttribTempl &copy);
+  INLINE BuilderAttribTempl &operator = (const BuilderAttribTempl &copy);
+
+  INLINE BuilderAttribTempl &clear();
+
+  INLINE bool has_normal() const;
+  INLINE NType get_normal() const;
+  INLINE BuilderAttribTempl &set_normal(const NType &n);
+
+  INLINE bool has_color() const;
+  INLINE CType get_color() const;
+  INLINE BuilderAttribTempl &set_color(const CType &c);
+
+  INLINE bool has_pixel_size() const;
+  INLINE float get_pixel_size() const;
+  INLINE BuilderAttribTempl &set_pixel_size(float s);
+
+  INLINE bool operator == (const BuilderAttribTempl &other) const;
+  INLINE bool operator < (const BuilderAttribTempl &other) const;
+
+  INLINE ostream &output(ostream &out) const;
+
+protected:
+  NType _normal;
+  CType _color;
+  float _pixel_size;
+  int _flags;
+};
+
+template <class VT, class NT, class TT, class CT>
+INLINE ostream &operator << (ostream &out,
+			     const BuilderAttribTempl<VT, NT, TT, CT> &attrib) {
+  return attrib.output(out);
+}
+
+#include "builderAttribTempl.I"
+
+#endif
+

+ 117 - 0
panda/src/builder/builderBucket.I

@@ -0,0 +1,117 @@
+// Filename: builderBucket.I
+// Created by:  drose (09Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::set_coords
+//       Access: Public
+//  Description: Sets the array that will be used to define the
+//               vertices for any indexed geometry that is associated
+//               with this bucket.
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderBucket::
+set_coords(const PTA_Vertexf &coords) {
+  _coords = coords;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::get_coords
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PTA_Vertexf BuilderBucket::
+get_coords() const {
+  return _coords;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::set_normals
+//       Access: Public
+//  Description: Sets the array that will be used to define the
+//               normals for any indexed geometry that is associated
+//               with this bucket.
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderBucket::
+set_normals(const PTA_Normalf &normals) {
+  _normals = normals;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::get_normals
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PTA_Normalf BuilderBucket::
+get_normals() const {
+  return _normals;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::set_texcoords
+//       Access: Public
+//  Description: Sets the array that will be used to define the
+//               texture coordinates for any indexed geometry that is
+//               associated with this bucket.
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderBucket::
+set_texcoords(const PTA_TexCoordf &texcoords) {
+  _texcoords = texcoords;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::get_texcoords
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PTA_TexCoordf BuilderBucket::
+get_texcoords() const {
+  return _texcoords;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::set_colors
+//       Access: Public
+//  Description: Sets the array that will be used to define the
+//               colors for any indexed geometry that is associated
+//               with this bucket.
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderBucket::
+set_colors(const PTA_Colorf &colors) {
+  _colors = colors;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::get_colors
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PTA_Colorf BuilderBucket::
+get_colors() const {
+  return _colors;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::get_default_bucket
+//       Access: Public, Static
+//  Description: Returns a pointer to the BuilderBucket that is used
+//               to initialize any BuilderBuckets created with a
+//               default constructor.  This is just a convenient way
+//               to set some global parameters.
+////////////////////////////////////////////////////////////////////
+INLINE BuilderBucket *BuilderBucket::
+get_default_bucket() {
+  if (_default_bucket==NULL) {
+    _default_bucket = new BuilderBucket(true);
+  }
+  return _default_bucket;
+}

+ 240 - 0
panda/src/builder/builderBucket.cxx

@@ -0,0 +1,240 @@
+// Filename: builderBucket.cxx
+// Created by:  drose (10Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderAttrib.h"
+#include "builderBucket.h"
+#include "builderFuncs.h"
+#include "builderMisc.h"
+
+#include <namedNode.h>
+#include <geomNode.h>
+
+BuilderBucket *BuilderBucket::_default_bucket = NULL;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BuilderBucket::
+BuilderBucket() {
+  _node = NULL;
+  (*this) = (*get_default_bucket());
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::Copy constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BuilderBucket::
+BuilderBucket(const BuilderBucket &copy) {
+  _node = NULL;
+  (*this) = copy;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::Copy assignment operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BuilderBucket &BuilderBucket::
+operator = (const BuilderBucket &copy) {
+  ((BuilderProperties &)*this) = (BuilderProperties &)copy;
+
+  //  setGState(copy._state);
+  set_name(copy.get_name());
+  set_coords(copy._coords);
+  set_normals(copy._normals);
+  set_texcoords(copy._texcoords);
+  set_colors(copy._colors);
+
+  _node = copy._node;
+  _drawBin = copy._drawBin;
+  _drawOrder = copy._drawOrder;
+
+  _trans = copy._trans;
+
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BuilderBucket::
+~BuilderBucket() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::make_copy
+//       Access: Public, Virtual
+//  Description: Allocates and returns a new copy of this object.  If
+//               you are subclassing from BuilderBucket, you must
+//               redefine this to return an instance of your new
+//               subclass, because the Builder will call this function
+//               to get its own copy.
+////////////////////////////////////////////////////////////////////
+BuilderBucket *BuilderBucket::
+make_copy() const {
+  return new BuilderBucket(*this);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::make_geom_node
+//       Access: Public, Virtual
+//  Description: Called by the builder when it is time to create a new
+//               GeomNode.  This function should allocate and return a
+//               new GeomNode suitable for adding geometry to.  You
+//               may redefine it to return a subclass of GeomNode, or
+//               to do some initialization to the node.
+////////////////////////////////////////////////////////////////////
+GeomNode *BuilderBucket::
+make_geom_node() {
+  return new GeomNode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::done_geom
+//       Access: Public, Virtual
+//  Description: Called after all the geometry has been added to the
+//               Geom.  This is just a hook for the user to redefine
+//               to do any post-processing that may be desired on the
+//               geometry.  It may deallocate it and return a new
+//               copy.  If it returns NULL, the geom is discarded.
+////////////////////////////////////////////////////////////////////
+Geom *BuilderBucket::
+done_geom(Geom *geom) {
+  return geom;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::Ordering operator
+//       Access: Public, Virtual
+//  Description: Defines an arbitrary ordering among different
+//               buckets, and groups identical buckets together.
+//               (Buckets a and b are identical if !(a < b) and !(b <
+//               a).)
+//
+//               The actual order between different buckets is
+//               arbitrary and largely irrelevant, so long as it is
+//               consistent.  That is, if (a < b) and (b < c), it must
+//               also be true that (a < c).  Also, if (a < b), it
+//               cannot be true that (b < a).
+////////////////////////////////////////////////////////////////////
+bool BuilderBucket::
+operator < (const BuilderBucket &other) const {
+  if (get_name() != other.get_name()) {
+    return get_name() < other.get_name();
+  }
+
+  if (_node != other._node)
+    return _node < other._node;
+  
+  if (_coords != other._coords)      
+    return _coords < other._coords;
+  if (_normals != other._normals)        
+    return _normals < other._normals;
+  if (_texcoords != other._texcoords)
+    return _texcoords < other._texcoords;
+  if (_colors != other._colors)
+    return _colors < other._colors;
+  
+  if (_drawBin != other._drawBin)
+    return _drawBin < other._drawBin;
+  if (_drawOrder != other._drawOrder)
+    return _drawOrder < other._drawOrder;
+
+  int compare = _trans.compare_to(other._trans);
+  if (compare != 0) {
+    return (compare < 0);
+  }
+
+  return BuilderProperties::operator < (other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::output
+//       Access: Public, Virtual
+//  Description: Formats the bucket for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+void BuilderBucket::
+output(ostream &out) const {
+  out << "Bucket \"" << get_name() << "\"";
+
+  if (_node != (NamedNode *)NULL) {
+    out << " attached to " << *_node << "\n";
+  }
+  out << "\n";
+
+
+  if (_coords != (Vertexf *)NULL) {
+    out << "_coords = " << (void *)_coords << "\n";
+  }
+
+  if (_normals != (Normalf *)NULL) {
+    out << "_normals = " << (void *)_normals << "\n";
+  }
+
+  if (_texcoords != (TexCoordf *)NULL) {
+    out << "_texcoords = " << (void *)_texcoords << "\n";
+  }
+
+  if (_colors != (Colorf *)NULL) {
+    out << "_colors = " << (void *)_colors << "\n";
+  }
+
+  if (_drawBin != -1) {
+    out << "_drawBin = " << _drawBin << "\n";
+  }
+ 
+  if (_drawOrder != 0) {
+    out << "_drawOrder = " << _drawOrder << "\n";
+  }
+  
+  BuilderProperties::output(out);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucket::private Constructor
+//       Access: Private
+//  Description: This special constructor is used only to initialize
+//               the _default_bucket pointer.  It sets up the initial
+//               defaults.  The normal constructor copies from this
+//               instance.
+////////////////////////////////////////////////////////////////////
+BuilderBucket::
+BuilderBucket(int) {
+  _node = NULL;
+
+  _drawBin = -1;
+  _drawOrder = 0;
+
+  // From BuilderProperties
+  _mesh = true;
+  _retesselate_coplanar = true;
+  _show_tstrips = false;
+  _show_qsheets = false;
+  _show_quads = false;
+  _show_normals = false;
+  _normal_color._v.set(1.0, 0.0, 0.0, 1.0);
+  _normal_scale = 1.0;
+  _subdivide_polys = true;
+  _coplanar_threshold = 0.01;
+
+  _unroll_fans = true;
+  _consider_fans = true;
+  _max_tfan_angle = 40.0;
+  _min_tfan_tris = 0;
+}

+ 104 - 0
panda/src/builder/builderBucket.h

@@ -0,0 +1,104 @@
+// Filename: builderBucket.h
+// Created by:  drose (09Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERBUCKET_H
+#define BUILDERBUCKET_H
+
+#include <pandabase.h>
+
+#include "builderProperties.h"
+
+#include <namable.h>
+#include <pointerToArray.h>
+#include <luse.h>
+#include <nodeTransitions.h>
+#include <pta_Vertexf.h>
+#include <pta_Normalf.h>
+#include <pta_Colorf.h>
+#include <pta_TexCoordf.h>
+
+#include <stdlib.h>
+
+
+class NamedNode;
+class Geom;
+class GeomNode;
+
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : BuilderBucket
+// Description : The main grouping tool for BuilderPrims.  See the
+//               comments at the beginning of builder.h.
+//
+//               As each primitive is added to the builder, it is
+//               associated with a bucket.  The bucket stores the
+//               scene-graph properties of the primitive, and is used
+//               to identify primitives that have the same properties
+//               and thus may be joined into a common triangle strip.
+//
+//               This grouping is done via the ordering operator, <,
+//               which defines an arbitrary ordering for buckets and
+//               identifies those buckets which are equivalent to each
+//               other.  By subclassing off of BuilderBucket and
+//               redefining this operator, you can change the grouping
+//               behavior to suit your needs, if necessary.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderBucket : public BuilderProperties, public Namable {
+public:
+  BuilderBucket();
+  BuilderBucket(const BuilderBucket &copy);
+  BuilderBucket &operator = (const BuilderBucket &copy);
+  virtual ~BuilderBucket();
+
+  virtual BuilderBucket *make_copy() const;
+  virtual GeomNode *make_geom_node();
+  virtual Geom *done_geom(Geom *geom);
+
+  virtual bool operator < (const BuilderBucket &other) const;
+
+  INLINE void set_coords(const PTA_Vertexf &coords);
+  INLINE PTA_Vertexf get_coords() const;
+
+  INLINE void set_normals(const PTA_Normalf &normals);
+  INLINE PTA_Normalf get_normals() const;
+
+  INLINE void set_texcoords(const PTA_TexCoordf &texcoords);
+  INLINE PTA_TexCoordf get_texcoords() const;
+
+  INLINE void set_colors(const PTA_Colorf &colors);
+  INLINE PTA_Colorf get_colors() const;
+
+  INLINE static BuilderBucket *get_default_bucket();
+
+  virtual void output(ostream &out) const;
+
+  NamedNode *_node;
+
+  short _drawBin;
+  unsigned int _drawOrder;
+
+  NodeTransitions _trans;
+
+protected:
+  PTA_Vertexf _coords;
+  PTA_Normalf _normals;
+  PTA_TexCoordf _texcoords;
+  PTA_Colorf _colors;
+
+  static BuilderBucket *_default_bucket;
+
+private:
+  BuilderBucket(int);
+};
+
+INLINE ostream &operator << (ostream &out, const BuilderBucket &bucket) {
+  bucket.output(out);
+  return out;
+}
+
+#include "builderBucket.I"
+
+#endif
+

+ 92 - 0
panda/src/builder/builderBucketNode.I

@@ -0,0 +1,92 @@
+// Filename: builderBucketNode.I
+// Created by:  drose (10Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE BuilderBucketNode::
+BuilderBucketNode(BuilderBucket *bucket) : _bucket(bucket) {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::Copy constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE BuilderBucketNode::
+BuilderBucketNode(const BuilderBucketNode &copy) {
+  (*this) = copy;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::Copy assignment operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderBucketNode::
+operator = (const BuilderBucketNode &copy) {
+  _bucket = copy._bucket;
+  _prims = copy._prims;
+  _iprims = copy._iprims;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::add_prim_nonindexed
+//       Access: Public
+//  Description: Adds the indicated indexed primitive to the bucket as
+//               if it were nonindexed, and returns true if the
+//               primitive was valid.  Intended to be called from
+//               Builder::add_prim_nonindexed().
+////////////////////////////////////////////////////////////////////
+INLINE bool BuilderBucketNode::
+add_prim_nonindexed(const BuilderPrimI &prim) {
+  BuilderPrim nonindexed;
+  nonindexed.nonindexed_copy(prim, *_bucket);
+  return add_prim(nonindexed);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::get_bucket
+//       Access: Public
+//  Description: Returns the BuilderBucket object associated with this
+//               node.
+////////////////////////////////////////////////////////////////////
+INLINE BuilderBucket *BuilderBucketNode::
+get_bucket() const {
+  return _bucket;
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::Ordering operator
+//       Access: Public
+//  Description: Returns true if this bucket precedes the indicated
+//               one in ordering.  This function is used by the STL
+//               set in the Builder object to sort buckets into order,
+//               and to collect similar buckets together.
+////////////////////////////////////////////////////////////////////
+INLINE bool BuilderBucketNode::
+operator < (const BuilderBucketNode &other) const {
+  return (*_bucket) < (*other._bucket);
+}
+  
+  
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::Equivalence operator
+//       Access: Public
+//  Description: Returns true if the two buckets are equivalent, based
+//               on the ordering operator, above.
+////////////////////////////////////////////////////////////////////
+INLINE bool BuilderBucketNode::
+operator == (const BuilderBucketNode &other) const {
+  return !((*_bucket) < (*other._bucket) || 
+	   (*other._bucket) < (*_bucket));
+}	

+ 87 - 0
panda/src/builder/builderBucketNode.cxx

@@ -0,0 +1,87 @@
+// Filename: builderBucketNode.cxx
+// Created by:  drose (10Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderFuncs.h"
+#include "builderBucketNode.h"
+
+#include <geomNode.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::add_prim
+//       Access: Public
+//  Description: Adds the indicated indexed primitive to the
+//               bucket, and returns true if the primitive was valid.
+//               Intended to be called from Builder::add_prim().
+////////////////////////////////////////////////////////////////////
+bool BuilderBucketNode::
+add_prim(const BuilderPrim &prim) {
+  bool result = expand(prim, *_bucket, inserter(_prims, _prims.begin()));
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::add_prim
+//       Access: Public
+//  Description: Adds the indicated nonindexed primitive to the
+//               bucket, and returns true if the primitive was valid.
+//               Intended to be called from Builder::add_prim().
+////////////////////////////////////////////////////////////////////
+bool BuilderBucketNode::
+add_prim(const BuilderPrimI &prim) {
+  bool result = expand(prim, *_bucket, inserter(_iprims, _iprims.begin()));
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderBucketNode::build
+//       Access: Public
+//  Description: Builds all the geometry assigned to this particular
+//               bucket, and assigns it to the indicated GeomNode.
+//               Returns the number of Geoms created.
+////////////////////////////////////////////////////////////////////
+int BuilderBucketNode::
+build(GeomNode *geom_node) const {
+  int count = 0;
+
+  {
+    // First, the nonindexed.
+    Prims::const_iterator pi, last_pi;
+    last_pi = _prims.begin();
+    
+    for (pi = _prims.begin();
+	 pi != _prims.end();
+	 ++pi) {
+      if ((*last_pi) < (*pi)) {
+	count += mesh_and_build(last_pi, pi, *_bucket, geom_node, 
+			        (BuilderPrim *)0);
+	last_pi = pi;
+      }
+    }
+    count += mesh_and_build(last_pi, pi, *_bucket, geom_node,
+                            (BuilderPrim *)0);
+  }
+
+  {
+    // Then, the indexed.
+    IPrims::const_iterator pi, last_pi;
+    last_pi = _iprims.begin();
+    
+    for (pi = _iprims.begin();
+	 pi != _iprims.end();
+	 ++pi) {
+      if ((*last_pi) < (*pi)) {
+	count += mesh_and_build(last_pi, pi, *_bucket, geom_node,
+				(BuilderPrimI *)0);
+	last_pi = pi;
+      }
+    }
+    count += mesh_and_build(last_pi, pi, *_bucket, geom_node,
+                            (BuilderPrimI *)0);
+  }
+
+  return count;
+}
+

+ 58 - 0
panda/src/builder/builderBucketNode.h

@@ -0,0 +1,58 @@
+// Filename: builderBucketNode.h
+// Created by:  drose (10Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERBUCKETNODE_H
+#define BUILDERBUCKETNODE_H
+
+#include <pandabase.h>
+
+#include "builderPrim.h"
+#include "builderBucket.h"
+
+#include <set>
+
+class GeomNode;
+
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : BuilderBucketNode
+// Description : This is a wrapper class around BuilderBucket, used by
+//               the Builder class.  It stores a pointer to a
+//               BuilderBucket object, as well as lists of the
+//               primitives that have been added to it.
+//
+//               There are no functions in this class that are
+//               intended to be called directly by user code; instead,
+//               use the interface provided by Builder.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderBucketNode {
+public:
+  INLINE BuilderBucketNode(BuilderBucket *bucket);
+  INLINE BuilderBucketNode(const BuilderBucketNode &copy);
+  INLINE void operator = (const BuilderBucketNode &copy);
+
+  bool add_prim(const BuilderPrim &prim);
+  bool add_prim(const BuilderPrimI &prim);
+  INLINE bool add_prim_nonindexed(const BuilderPrimI &prim);
+
+  INLINE BuilderBucket *get_bucket() const;
+ 
+  INLINE bool operator < (const BuilderBucketNode &other) const;
+  INLINE bool operator == (const BuilderBucketNode &other) const;
+
+  int build(GeomNode *geom_node) const;
+ 
+protected:
+  typedef multiset<BuilderPrim, less<BuilderPrim> > Prims;
+  typedef multiset<BuilderPrimI, less<BuilderPrimI> > IPrims;
+
+  BuilderBucket *_bucket;
+  Prims _prims;
+  IPrims _iprims;
+};
+
+#include "builderBucketNode.I"
+
+#endif

+ 908 - 0
panda/src/builder/builderFuncs.I

@@ -0,0 +1,908 @@
+// Filename: builderFuncs.cxx
+// Created by:  drose (09Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderPrim.h"
+#include "mesherTempl.h"
+#include "builderNormalVisualizer.h"
+#include "config_builder.h"
+
+#include <geom.h>
+#include <geomprimitives.h>
+#include <geomNode.h>
+
+#include <algorithm>
+#ifndef PENV_WIN32
+#include <alloca.h>
+#endif
+
+#ifdef WIN32_VC
+struct Vtx {
+  int index;
+  BuilderV coord;
+  struct Vtx *next;
+};
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: decomp_concave
+//  Description: Decomposes a concave polygon into triangles.  Returns
+//               true if successful, false if the polygon is
+//               self-intersecting.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+static bool
+decomp_concave(const PrimType &prim, BuilderBucket &bucket, 
+	       OutputIterator result,
+	       int asum, int x, int y) {
+#ifndef WIN32_VC
+  struct Vtx {
+    int index;
+    BuilderV coord;
+    struct Vtx *next;
+  };
+#endif
+  
+#define VX(p, c)    p->coord[c]
+
+  vector<PrimType> output_prims;
+
+  Vtx *p0, *p1, *p2, *t0, *vert;
+  Vtx *m[3];
+  float	xmin, xmax, ymin, ymax;
+  int i, init, csum, chek;
+  float a[3], b[3], c[3], s[3];
+
+  int num_verts = prim.get_num_verts();
+  nassertr(num_verts >= 3, false);
+  
+  /* Make linked list of verts */
+  vert = (Vtx *) alloca(sizeof(Vtx));
+  vert->index = 0;
+  vert->coord = prim.get_vertex(0).get_coord_value(bucket);
+  p1 = vert;
+
+  for (i = 1; i < num_verts; i++) {
+    p0 = (Vtx *) alloca(sizeof(Vtx));
+    p0->index = i;
+    p0->coord = prim.get_vertex(i).get_coord_value(bucket);
+    // There shouldn't be two consecutive identical vertices.  If
+    // there are, skip one.
+    if (!(p0->coord == p1->coord)) {
+      p1->next = p0;
+      p1 = p0;
+    }
+  }
+  p1->next = vert;
+
+  p0 = vert;
+  p1 = p0->next;
+  p2 = p1->next;
+  m[0] = p0;
+  m[1] = p1;
+  m[2] = p2;
+  chek = 0;
+
+  while (p0 != p2->next) {
+    /* Polygon is self-intersecting so punt */
+    if (chek && 
+	m[0] == p0 && 
+	m[1] == p1 && 
+	m[2] == p2) {
+
+      //      builder_cat.info() << "Could not decompose concave polygon!";
+      return false;
+    }
+
+    chek = 1;
+
+    a[0] = VX(p1, y) - VX(p2, y);
+    b[0] = VX(p2, x) - VX(p1, x);
+    a[2] = VX(p0, y) - VX(p1, y);
+    b[2] = VX(p1, x) - VX(p0, x);
+    
+    csum = ((b[0] * a[2] - b[2] * a[0] >= 0.0) ? 1 : 0);
+    
+    if (csum ^ asum) {
+      /* current angle is concave */
+      p0 = p1;
+      p1 = p2;
+      p2 = p2->next;
+
+    } else {
+      /* current angle is convex */
+      xmin = (VX(p0, x) < VX(p1, x)) ? VX(p0, x) : VX(p1, x);
+      if (xmin > VX(p2, x))
+	xmin = VX(p2, x);
+      
+      xmax = (VX(p0, x) > VX(p1, x)) ? VX(p0, x) : VX(p1, x);
+      if (xmax < VX(p2, x))
+	xmax = VX(p2, x);
+      
+      ymin = (VX(p0, y) < VX(p1, y)) ? VX(p0, y) : VX(p1, y);
+      if (ymin > VX(p2, y))
+	ymin = VX(p2, y);
+      
+      ymax = (VX(p0, y) > VX(p1, y)) ? VX(p0, y) : VX(p1, y);
+      if (ymax < VX(p2, y))
+	ymax = VX(p2, y);
+      
+      for (init = 1, t0 = p2->next; t0 != p0; t0 = t0->next) {
+	if (VX(t0, x) >= xmin && VX(t0, x) <= xmax &&
+	    VX(t0, y) >= ymin && VX(t0, y) <= ymax) {
+	  if (init) {
+	    a[1] = VX(p2, y) - VX(p0, y);
+	    b[1] = VX(p0, x) - VX(p2, x);
+	    init = 0;
+	    c[0] = VX(p1, x) * VX(p2, y) - VX(p2, x) * VX(p1, y);
+	    c[1] = VX(p2, x) * VX(p0, y) - VX(p0, x) * VX(p2, y);
+	    c[2] = VX(p0, x) * VX(p1, y) - VX(p1, x) * VX(p0, y);
+	  }
+	  
+	  s[0] = a[0] * VX(t0, x) + b[0] * VX(t0, y) + c[0];
+	  s[1] = a[1] * VX(t0, x) + b[1] * VX(t0, y) + c[1];
+	  s[2] = a[2] * VX(t0, x) + b[2] * VX(t0, y) + c[2];
+	  
+	  if (asum) {
+	    if (s[0] >= 0.0 && s[1] >= 0.0 && s[2] >= 0.0)
+	      break;
+	  } else {
+	    if (s[0] <= 0.0 && s[1] <= 0.0 && s[2] <= 0.0)
+	      break;
+	  }
+	}
+      }
+      
+      if (t0 != p0) {
+	p0 = p1;
+	p1 = p2;
+	p2 = p2->next;
+      } else {
+	PrimType new_prim(prim);
+	new_prim.set_type(BPT_tri);
+	new_prim.clear_vertices();
+	new_prim.add_vertex(prim.get_vertex(p0->index));
+	new_prim.add_vertex(prim.get_vertex(p1->index));
+	new_prim.add_vertex(prim.get_vertex(p2->index));
+	output_prims.push_back(new_prim);
+	
+	p0->next = p1->next;
+	p1 = p2;
+	p2 = p2->next;
+	
+	m[0] = p0;
+	m[1] = p1;
+	m[2] = p2;
+	chek = 0;
+      }
+    }
+  }
+
+  PrimType new_prim(prim);
+  new_prim.set_type(BPT_tri);
+  new_prim.clear_vertices();
+  new_prim.add_vertex(prim.get_vertex(p0->index));
+  new_prim.add_vertex(prim.get_vertex(p1->index));
+  new_prim.add_vertex(prim.get_vertex(p2->index));
+  output_prims.push_back(new_prim);
+
+  copy(output_prims.begin(), output_prims.end(), result);
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: triangulate_poly
+//  Description: Breaks a (possibly concave) higher-order polygon into
+//               a series of constituent triangles.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+static bool
+triangulate_poly(const PrimType &prim, BuilderBucket &bucket, 
+		 OutputIterator result) {
+  BuilderV p0, p1, as;
+  float dx1, dy1, dx2, dy2, max;
+  int i, flag, asum, csum, index, x, y, v0, v1, v, even;
+  
+  // First see if the polygon is just a triangle
+  int num_verts = prim.get_num_verts();
+  if (num_verts == 3) {
+    PrimType new_prim(prim);
+    new_prim.set_type(BPT_tri);
+    *result++ = new_prim;
+
+    return true;
+
+  } else if (num_verts < 3) {
+    // Or if it's a degenerate polygon.
+    return false;
+  }
+
+  // calculate signed areas
+  as[0] = 0.0;
+  as[1] = 0.0;
+  as[2] = 0.0;
+
+  for (i = 0; i < num_verts; i++) {
+    p0 = prim.get_vertex(i).get_coord_value(bucket);
+    p1 = prim.get_vertex((i + 1) % num_verts).get_coord_value(bucket);
+    as[0] += p0[0] * p1[1] - p0[1] * p1[0];
+    as[1] += p0[0] * p1[2] - p0[2] * p1[0];
+    as[2] += p0[1] * p1[2] - p0[2] * p1[1];
+  }
+
+  /* select largest signed area */
+  max = 0.0;
+  index = 0;
+  for (i = 0; i < 3; i++) {
+    if (as[i] >= 0.0) {
+      if (as[i] > max) {
+	max = as[i];
+	index = i;
+	flag = 1;
+      }
+    } else {
+      as[i] = -as[i];
+      if (as[i] > max) {
+	max = as[i];
+	index = i;
+	flag = 0;
+      }
+    }
+  }
+
+  /* pointer offsets */
+  switch (index) {
+  case 0:
+    x = 0;
+    y = 1;
+    break;
+    
+  case 1:
+    x = 0;
+    y = 2;
+    break;
+    
+  case 2:
+    x = 1;
+    y = 2;
+    break;
+  }
+
+  /* concave check */
+  p0 = prim.get_vertex(0).get_coord_value(bucket); 
+  p1 = prim.get_vertex(1).get_coord_value(bucket);
+  dx1 = p1[x] - p0[x];
+  dy1 = p1[y] - p0[y];
+  p0 = p1;
+  p1 = prim.get_vertex(2).get_coord_value(bucket);
+
+  dx2 = p1[x] - p0[x];
+  dy2 = p1[y] - p0[y];
+  asum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0);
+
+  for (i = 0; i < num_verts - 1; i++) {
+    p0 = p1;
+    p1 = prim.get_vertex((i+3) % num_verts).get_coord_value(bucket);
+
+    dx1 = dx2;
+    dy1 = dy2;
+    dx2 = p1[x] - p0[x];
+    dy2 = p1[y] - p0[y];
+    csum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0);
+
+    if (csum ^ asum) {
+      return decomp_concave(prim, bucket, result, flag, x, y);
+    }
+  }
+
+  v0 = 0;
+  v1 = 1;
+  v = num_verts - 1;
+  
+  even = 1;
+  
+  /* 
+   * Convert to triangles only. Do not fan out from a single vertex
+   * but zigzag into triangle strip.
+   */
+  for (i = 0; i < num_verts - 2; i++) {
+    if (even) {
+      PrimType new_prim(prim);
+      new_prim.set_type(BPT_tri);
+      new_prim.clear_vertices();
+      new_prim.add_vertex(prim.get_vertex(v0));
+      new_prim.add_vertex(prim.get_vertex(v1));
+      new_prim.add_vertex(prim.get_vertex(v));
+      *result++ = new_prim;
+      v0 = v1;
+      v1 = v;
+      v = v0 + 1;
+    } else {
+      PrimType new_prim(prim);
+      new_prim.set_type(BPT_tri);
+      new_prim.clear_vertices();
+      new_prim.add_vertex(prim.get_vertex(v1));
+      new_prim.add_vertex(prim.get_vertex(v0));
+      new_prim.add_vertex(prim.get_vertex(v));
+      *result++ = new_prim;
+      v0 = v1;
+      v1 = v;
+      v = v0 - 1;
+    }
+    
+    even = !even;
+  }
+  
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: expand_polys
+//  Description: Identifies a single polygon as a triangle, quad, or
+//               higher-order polygon, and writes it into the result
+//               list.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+static bool
+expand_polys(PrimType &prim, BuilderBucket &, 
+	     OutputIterator result) {
+  switch (prim.get_num_verts()) {
+  case 0:
+  case 1:
+  case 2:
+    return false;
+
+  case 3:
+    prim.set_type(BPT_tri);
+    break;
+
+  case 4:
+    prim.set_type(BPT_quad);
+    break;
+
+  default:
+    prim.set_type(BPT_poly);
+  }
+  
+  *result++ = prim;
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: expand_points
+//  Description: Expands a light points primitive into its individual
+//               component points, with one point per primitive.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+static bool
+expand_points(const PrimType &prim, BuilderBucket &, 
+	      OutputIterator result) {
+  // Each vertex goes in its own primitive.
+
+  int num_verts = prim.get_num_verts();
+  for (int i = 0; i < num_verts; i++) {
+    PrimType new_prim(prim);
+    new_prim.clear_vertices();
+    new_prim.add_vertex(prim.get_vertex(i));
+    *result++ = new_prim;
+  }
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: expand_lines
+//  Description: Expands a linestrip primitive into its component line
+//               primitives.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+static bool
+expand_lines(const PrimType &prim, BuilderBucket &, 
+	     OutputIterator result) {
+  // Each line segment goes in its own primitive.  This breaks up the
+  // linestrips already defined; we'll re-strip them later if the
+  // generate-tstrips flag is enabled.
+
+  int num_verts = prim.get_num_verts();
+  for (int i = 1; i < num_verts; i++) {
+    PrimType new_prim(prim);
+    new_prim.clear_vertices();
+    new_prim.add_vertex(prim.get_vertex(i-1));
+    new_prim.add_vertex(prim.get_vertex(i));
+    *result++ = new_prim;
+  }
+  return true;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: expand
+//  Description: Receives a single primitive as a BuilderPrim or
+//               BuilderPrimI object, as input by the user.  Does some
+//               initial processing on the primitive to verify
+//               internal consistency (for instance, that a quad has
+//               four vertices), and returns a new BuilderPrim or
+//               series of BuilderPrim objects, suitable for building
+//               with.
+//
+//               More than one primitive might be returned because
+//               higher-order polygons may be broken up into
+//               triangles, and linestrips and points are broken into
+//               their component pieces.  The output primitives are
+//               written into the STL container defined by result.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+bool
+expand(const PrimType &prim, BuilderBucket &bucket, OutputIterator result) {
+  // Make a copy of the prim so we can fiddle with it.
+  PrimType new_prim = prim;
+
+  switch (new_prim.get_type()) {
+  case BPT_poly:
+  case BPT_tri:
+  case BPT_quad:
+    // These three types are all treated the same, as polygons.  We
+    // don't entirely trust the user to match the polygon type with
+    // the number of verts, so we'll do it ourselves later.
+    new_prim.remove_doubled_verts(true);
+
+    if (!bucket._subdivide_polys ||
+	(bucket._mesh && new_prim.get_num_verts() <= 4)) {
+      // If we're meshing, we'd like to send quads through without
+      // subdividing.  The mesher can take advantage of the extra
+      // information (and will eventually produce tris anyway).
+      return expand_polys(new_prim, bucket, result);
+    } else {
+      // If we're not meshing, we'll break them into tris now.
+      return triangulate_poly(new_prim, bucket, result);
+    }
+
+  case BPT_point:
+    new_prim.remove_doubled_verts(false);
+    return expand_points(new_prim, bucket, result);
+
+  case BPT_line:
+    new_prim.remove_doubled_verts(false);
+    return expand_lines(new_prim, bucket, result);
+
+  default:
+    builder_cat.error() << "Unknown prim type\n";
+    return false;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: build_geoms
+//  Description: Accepts a list of BuilderPrim or BuilderPrimI
+//               objects, defined by the iterators first and last, and
+//               creates corresponding geometry for them in the
+//               indicated GeomNode.
+////////////////////////////////////////////////////////////////////
+template<class InputIterator, class PrimType>
+static int
+build_geoms(InputIterator first, InputIterator last,
+	    BuilderBucket &bucket, GeomNode *geom_node,
+	    PrimType *) {
+  if (first==last) {
+    return 0;
+  }
+
+  // By the time we get here, we have a list of primitives that all have
+  // the same properties:
+
+  // 1. The BuilderBucket.
+  // 2. The indexed/nonindexed type.
+  // 3. The primitive type (polygon, line, point).
+  // 4. The pixel size.
+
+  // The binding of normals, colors, or texcoords: per-vertex,
+  // per-prim, or per-component, will generally be the same across all
+  // primitives, but it might not always be the same, because in
+  // certain special cases the mesher might have changed these
+  // properties when it built tristrips.
+
+  typedef TYPENAME PrimType::VType VType;
+  typedef TYPENAME PrimType::NType NType;
+  typedef TYPENAME PrimType::TType TType;
+  typedef TYPENAME PrimType::CType CType;
+
+  // We need to determine the common binding type for all primitives.
+  // For a given attribute, say normals, there are at most five cases,
+  // in the order of priority:
+  //
+  //  1. If at least one primitive in the list does not have normals,
+  //     the binding will be G_OFF.
+  //
+  //  2. If at least one primitive has per-vertex normals, the binding
+  //     will be G_PER_VERTEX.
+  //
+  //  3. If at least one primitive has per-component normals, the
+  //     binding will be G_PER_COMPONENT.
+  //
+  //  4. If none of the first three apply, it follows that all
+  //     primitives have an overall normal.  If any primitive's
+  //     overall normal differs from any other, the binding will be
+  //     G_PER_PRIM.
+  //
+  //  5. If none of the above apply, the binding will be G_OVERALL.
+  //
+  // An exception to the above is for texcoords, which is either G_OFF
+  // (by rule 1) or G_PER_VERTEX.
+
+  GeomBindType bind_normals = G_OVERALL;
+  GeomBindType bind_colors = G_OVERALL;
+  GeomBindType bind_texcoords = G_PER_VERTEX;
+
+  NType overall_normal;
+  CType overall_color;
+  bool first_normal = true;
+  bool first_color = true;
+
+  InputIterator i;
+  for (i = first; i != last; ++i) {
+
+    // Normals.
+
+    // Test rule 1.
+    if (!(*i).has_any_normal()) {
+      bind_normals = G_OFF;
+    } else if (bind_normals != G_OFF) {
+      // Test rule 2.
+      if ((*i).has_vertex_normal()) {
+	bind_normals = G_PER_VERTEX;
+      } else if (bind_normals != G_PER_VERTEX) {
+	// Test rule 3.
+	if ((*i).has_component_normal()) {
+	  bind_normals = G_PER_COMPONENT;
+	} else if (bind_normals != G_PER_COMPONENT) {
+	  // Test rule 4.
+	  nassertr((*i).has_overall_normal(), 0);
+	  if (first_normal) {
+	    overall_normal = (*i).get_normal();
+	    first_normal = false;
+	  } else if ( !((*i).get_normal() == overall_normal)) {
+	    bind_normals = G_PER_PRIM;
+	  }
+	}
+      }
+    }
+
+    // Colors.
+
+    // Test rule 1.
+    if (!(*i).has_any_color()) {
+      bind_colors = G_OFF;
+    } else if (bind_colors != G_OFF) {
+      // Test rule 2.
+      if ((*i).has_vertex_color()) {
+	bind_colors = G_PER_VERTEX;
+      } else if (bind_colors != G_PER_VERTEX) {
+	// Test rule 3.
+	if ((*i).has_component_color()) {
+	  bind_colors = G_PER_COMPONENT;
+	} else if (bind_colors != G_PER_COMPONENT) {
+	  // Test rule 4.
+	  nassertr((*i).has_overall_color(), 0);
+	  if (first_color) {
+	    overall_color = (*i).get_color();
+	    first_color = false;
+	  } else if ( !((*i).get_color() == overall_color)) {
+	    bind_colors = G_PER_PRIM;
+	  }
+	}
+      }
+    }
+
+    // Texcoords.
+
+    // Test rule 1.
+    if (!(*i).has_any_texcoord()) {
+      bind_texcoords = G_OFF;
+    }
+  }
+
+  // Determine the primitive type and build the lengths array, if needed.
+  PTA_int lengths;
+  bool want_lengths = false;
+  int j;
+
+  Geom *geom = NULL;
+  BuilderPrimType type = (*first).get_type();
+  switch (type) {
+  case BPT_poly:
+    geom = new GeomPolygon;
+    want_lengths = true;
+    break;
+    
+  case BPT_tristrip:
+    geom = new GeomTristrip;
+    want_lengths = true;
+    break;
+    
+  case BPT_trifan:
+    geom = new GeomTrifan;
+    want_lengths = true;
+    break;
+
+  case BPT_line:
+    geom = new GeomLine;
+    break;
+
+  case BPT_point:
+    geom = new GeomPoint;
+    break;
+
+  case BPT_tri:
+    geom = new GeomTri;
+    break;
+
+  case BPT_quad:
+    geom = new GeomQuad;
+    break;
+
+  default:
+    builder_cat.fatal() << "Invalid primitive type.\n";
+    abort();
+  }
+
+  if (geom == NULL) {
+    builder_cat.error() << "Unsupported primitive type " << type << "\n";
+    return 0;
+  }
+
+  // Count up the number of prims we're actually building.
+  int num_prims = 0;
+  for (i = first; i != last; ++i) {
+    if ((*i).is_valid()) {
+      num_prims++;
+    }
+  }
+  
+  if (num_prims==0) {
+    builder_cat.error() << "All primitives were invalid!\n";
+    return 0;
+  }
+  
+  if (want_lengths) {
+    lengths = PTA_int(num_prims);
+    j = 0;
+    for (i = first; i != last; ++i) {
+      if ((*i).is_valid()) {
+	lengths[j++] = (*i).get_num_verts();
+      }
+    }
+    nassertr(j == num_prims, 0);
+  }
+
+  // Now build up some arrays.
+  PTA(VType) coords(0);
+  PTA(NType) normals(0);
+  PTA(TType) texcoords(0);
+  PTA(CType) colors(0);
+
+  int total_verts = 0;
+  int total_components = 0;
+
+  int v, num_verts;
+  int c, num_components;
+  for (i = first; i != last; ++i) {
+    if ((*i).is_valid()) {
+      num_verts = (*i).get_num_verts();
+      total_verts += num_verts;
+      for (v = 0; v < num_verts; v++) {
+	coords.push_back((*i).get_vertex(v).get_coord());
+	
+	if (bind_normals == G_PER_VERTEX) {
+	  normals.push_back((*i).get_vertex(v).get_normal());
+	}
+	if (bind_texcoords == G_PER_VERTEX) {
+	  texcoords.push_back((*i).get_vertex(v).get_texcoord());
+	}
+	if (bind_colors == G_PER_VERTEX) {
+	  colors.push_back((*i).get_vertex(v).get_color());
+	}
+      }
+
+      num_components = (*i).get_num_components();
+      total_components += num_components;
+      for (c = 0; c < num_components; c++) {
+	if (bind_normals == G_PER_COMPONENT) {
+	  normals.push_back((*i).get_component(c).get_normal());
+	}
+	if (bind_colors == G_PER_COMPONENT) {
+	  colors.push_back((*i).get_component(c).get_color());
+	}
+      }
+      
+      if (bind_normals == G_PER_PRIM) {
+	normals.push_back((*i).get_normal());
+      }
+      if (bind_colors == G_PER_PRIM) {
+	colors.push_back((*i).get_color());
+      }
+    }
+  }
+
+  if (bind_normals == G_OVERALL) {
+    normals.push_back(overall_normal);
+  }
+  if (bind_colors == G_OVERALL) {
+    colors.push_back(overall_color);
+  }
+
+  // Now add all the stuff to our Geom.
+
+  geom->set_num_prims(num_prims);
+  
+  if (lengths != (int *)NULL) {
+    geom->set_lengths(lengths);
+  }
+  
+  PrimType::fill_geom(geom, coords,
+		      bind_normals, normals,
+		      bind_texcoords, texcoords,
+		      bind_colors, colors,
+		      bucket, num_prims,
+		      total_components, total_verts);
+
+    /*
+  if ((*first).has_pixel_size()) {
+    // Again, we only have to test the first one in the list for a
+    // pixel_size attribute.  If this one has it, then they all have
+    // the same value.
+    geom->setPntSize((*first).get_pixel_size());
+    geom->setLineWidth((*first).get_pixel_size());
+  }
+    */
+
+  //    geom->setGState((pfGeoState *)bucket.getGState());
+  //  geom->setDrawBin(bucket._drawBin);
+  //  geom->setDrawOrder(bucket._drawOrder);
+
+  Geom *new_geom = bucket.done_geom(geom);
+  if (new_geom != (Geom *)NULL) {
+    geom_node->add_geom(new_geom);
+  }
+  
+  return 1;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : PrimByType
+// Description : An STL function object to sort primitives in order by
+//               type.
+////////////////////////////////////////////////////////////////////
+template<class PrimType>
+class PrimByType {
+public:
+  int operator () (const PrimType &p1, const PrimType &p2) const {
+    return p1.get_type() < p2.get_type();
+  }
+};
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: __mesh_and_build
+//  Description: The implementation of mesh_and_build(), below.  This
+//               extra function call is just to allow mesh_and_build()
+//               to infer the PrimType (BuilderPrim or BuilderPrimI)
+//               from the iterator's value type, and template on that.
+////////////////////////////////////////////////////////////////////
+template<class InputIterator, class PrimType>
+static int
+__mesh_and_build(InputIterator first, InputIterator last,
+		 BuilderBucket &bucket, GeomNode *geom_node,
+		 PrimType *) {
+  if (first==last) {
+    return 0;
+  }
+
+  typedef vector<PrimType> Prims;
+  Prims prims;
+  BuilderBucket *local_bucket = NULL;
+  BuilderBucket *bucket_ptr = &bucket;
+
+  if (bucket._mesh) {
+    // Send all the prims through the mesher.  First, make a copy of
+    // the bucket so the mesher can modify it if it wants.
+    local_bucket = bucket.make_copy();
+    bucket_ptr = local_bucket;
+    MesherTempl<PrimType> mesher(local_bucket);
+
+    for (InputIterator ii = first; ii != last; ++ii) {
+      mesher.add_prim(*ii);
+    }
+    mesher.mesh();
+    PrimType prim;
+    prim = mesher.getPrim();
+    while (prim.get_num_verts() > 0) {
+      prims.push_back(prim);
+      prim = mesher.getPrim();
+    }
+
+  } else {
+    // Send the prims through without meshing.
+    copy(first, last, back_inserter(prims));
+  }
+
+  // Now we have an array of prims which all share the same
+  // properties, except possibly type.  Sort them by type and send
+  // them to build_geoms.
+  sort(prims.begin(), prims.end(), PrimByType<PrimType>());
+
+  int count = 0;
+  if (!prims.empty()) {
+    Prims::iterator pi, last_pi;
+    pi = prims.begin();
+    last_pi = pi;
+    for (++pi; pi != prims.end(); ++pi) {
+      if ((*pi).get_type() != (*last_pi).get_type()) {
+	count += build_geoms(last_pi, pi, *bucket_ptr, geom_node, (PrimType*)0);
+	last_pi = pi;
+      }
+    }
+    count += build_geoms(last_pi, pi, *bucket_ptr, geom_node, (PrimType*)0);
+  }
+
+  if (local_bucket!=NULL) {
+    delete local_bucket;
+  }
+
+  // Finally, if the user so requested, create some visualization for
+  // the normals.
+#ifdef SUPPORT_SHOW_NORMALS
+  if (bucket._show_normals) {
+    BuilderNormalVisualizer bnv(bucket);
+    for (InputIterator ii = first; ii != last; ++ii) {
+      bnv.add_prim(*ii);
+    }
+    bnv.show_normals(geom_node);
+  }
+#endif
+
+  return count;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: mesh_and_build
+//  Description: Accepts a list of BuilderPrim or BuilderPrimI
+//               objects, defined by the iterators first and list,
+//               runs them through the mesher if specified by the
+//               bucket, and builds them into the indicated GeomNode.
+////////////////////////////////////////////////////////////////////
+template<class InputIterator, class value_type>
+int
+mesh_and_build(InputIterator first, InputIterator last,
+	       BuilderBucket &bucket, GeomNode *geom_node,
+               value_type *value_type_ptr) {
+  return __mesh_and_build(first, last, bucket, geom_node, value_type_ptr);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: split
+//  Description: Splits an STL list into two other lists, according to
+//               the return value from pred.
+////////////////////////////////////////////////////////////////////
+template <class InputIterator, class OutputIterator, class Predicate>
+OutputIterator split(InputIterator first, InputIterator last,
+		     OutputIterator true_result, OutputIterator false_result,
+		     Predicate pred) {
+  while (first != last) {
+    if (pred(*first)) {
+      *true_result++ = *first++;
+    } else {
+      *false_result++ = *first++;
+    }
+  }
+  return true_result;
+}
+

+ 66 - 0
panda/src/builder/builderFuncs.h

@@ -0,0 +1,66 @@
+// Filename: builderFuncs.h
+// Created by:  drose (09Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef BUILDERFUNCS_H
+#define BUILDERFUNCS_H
+
+#include <pandabase.h>
+
+#include <vector>
+
+#include <string>
+
+class BuilderBucket;
+class GeomNode;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: expand
+//  Description: Receives a single primitive as a BuilderPrim or
+//               BuilderPrimI object, as input by the user.  Does some
+//               initial processing on the primitive to verify
+//               internal consistency (for instance, that a quad has
+//               four vertices), and returns a new BuilderPrim or
+//               series of BuilderPrim objects, suitable for building
+//               with.
+//
+//               More than one primitive might be returned because
+//               higher-order polygons may be broken up into
+//               triangles, and linestrips and points are broken into
+//               their component pieces.  The output primitives are
+//               written into the STL container defined by result.
+////////////////////////////////////////////////////////////////////
+template <class PrimType, class OutputIterator>
+bool
+expand(const PrimType &prim, BuilderBucket &bucket,
+       OutputIterator result);
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: mesh_and_build
+//  Description: Accepts a list of BuilderPrim or BuilderPrimI
+//               objects, defined by the iterators first and list,
+//               runs them through the mesher if specified by the
+//               bucket, and builds them into the indicated GeomNode.
+////////////////////////////////////////////////////////////////////
+template<class InputIterator>
+int
+mesh_and_build(InputIterator first, InputIterator last,
+	       BuilderBucket &bucket, GeomNode *geom_node);
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: split
+//  Description: Splits an STL list into two other lists, according to
+//               the return value from pred.
+////////////////////////////////////////////////////////////////////
+template <class InputIterator, class OutputIterator, class Predicate>
+OutputIterator split(InputIterator first, InputIterator last,
+		     OutputIterator true_result, OutputIterator false_result,
+		     Predicate pred);
+
+#include "builderFuncs.I"
+
+#endif

+ 37 - 0
panda/src/builder/builderMisc.cxx

@@ -0,0 +1,37 @@
+// Filename: builderMisc.cxx
+// Created by:  drose (18Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderMisc.h"
+#include "builderTypes.h"
+
+#include <luse.h>
+#include <stdlib.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: make_random_color
+//  Description: Chooses a reasonable random color.
+////////////////////////////////////////////////////////////////////
+void
+make_random_color(Colorf &color) {
+  LVector3f rgb;
+  float len;
+  do {
+    for (int i = 0; i < 3; i++) {
+      rgb[i] = (double)rand() / (double)RAND_MAX;
+    }
+    len = length(rgb);
+
+    // Repeat until we have a color that's not too dark or too light.
+  } while (len < .1 || len > 1.5);
+
+#ifdef WIN32_VC
+  color.set(rgb[0], rgb[1], rgb[2], 
+	    0.25 + 0.75 * (double)rand() / (double)RAND_MAX);
+#else
+  color.set(rgb[0], rgb[1], rgb[2], 
+	    0.25 + 0.75 * (double)random() / (double)RAND_MAX);
+#endif
+}
+

+ 18 - 0
panda/src/builder/builderMisc.h

@@ -0,0 +1,18 @@
+// Filename: builderMisc.h
+// Created by:  drose (18Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef BUILDERMISC_H
+#define BUILDERMISC_H
+
+#include <pandabase.h>
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: make_random_color
+//  Description: Chooses a reasonable random color.
+////////////////////////////////////////////////////////////////////
+void make_random_color(Colorf &color);
+
+#endif

+ 11 - 0
panda/src/builder/builderNormalVisualizer.I

@@ -0,0 +1,11 @@
+// Filename: builderNormalVisualizer.I
+// Created by:  drose (08Sep99)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE BuilderNormalVisualizer::
+BuilderNormalVisualizer(BuilderBucket &bucket) : _bucket(bucket) {
+  _num_vertices = 0;
+  _net_vertex._v.set(0.0, 0.0, 0.0);
+}
+

+ 65 - 0
panda/src/builder/builderNormalVisualizer.cxx

@@ -0,0 +1,65 @@
+// Filename: builderNormalVisualizer.cxx
+// Created by:  drose (08Sep99)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifdef SUPPORT_SHOW_NORMALS
+
+#include "builderNormalVisualizer.h"
+#include "builderFuncs.h"
+
+void BuilderNormalVisualizer::
+add_prim(const BuilderPrim &prim) {
+  if (prim.has_overall_normal()) {
+    // Average up all the vertex values to get the primitive center.
+    BuilderV net_vertex;
+    net_vertex._v.set(0.0, 0.0, 0.0);
+    int num_verts = prim.get_num_verts();
+    for (int i = 0; i < num_verts; i++) {
+      net_vertex._v += prim.get_vertex(i).get_coord();
+    }
+    net_vertex._v /= num_verts;
+    add_normal(net_vertex, prim.get_normal());
+
+  } else if (prim.has_vertex_normal()) {
+    // Each vertex gets its own normal.
+    int num_verts = prim.get_num_verts();
+    for (int i = 0; i < num_verts; i++) {
+      add_normal(prim.get_vertex(i).get_coord(), prim.get_vertex(i).get_normal());
+    }
+  } else if (prim.has_component_normal()) {
+    // Each component gets its own normal.  We don't presently handle
+    // this, as it should only happen when the user passes
+    // already-meshed tristrips into the builder, an unusual
+    // situation.
+  }
+}
+
+void BuilderNormalVisualizer::
+add_prim(const BuilderPrimI &prim) {
+  BuilderPrim prim_ni;
+  prim_ni.nonindexed_copy(prim, _bucket);
+  add_prim(prim_ni);
+}
+
+void BuilderNormalVisualizer::
+show_normals(GeomNode *node) {
+  // Ok, now we've got a bunch of normals saved up; create some geometry.
+  mesh_and_build(_lines.begin(), _lines.end(), _bucket, node, (BuilderPrim *)0);
+}
+
+void BuilderNormalVisualizer::
+add_normal(const BuilderV &center, const BuilderN &normal) {
+  BuilderV to = center._v + normal._v * _bucket._normal_scale;
+    
+  BuilderPrim line;
+  line.set_type(BPT_line);
+  line.set_color(_bucket._normal_color);
+  line.add_vertex(BuilderVertex(center));
+  line.add_vertex(BuilderVertex(to));
+  
+  _lines.push_back(line);
+}
+
+#endif
+

+ 51 - 0
panda/src/builder/builderNormalVisualizer.h

@@ -0,0 +1,51 @@
+// Filename: builderNormalVisualizer.h
+// Created by:  drose (08Sep99)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERNORMALVISUALIZER_H
+#define BUILDERNORMALVISUALIZER_H
+
+#ifdef SUPPORT_SHOW_NORMALS
+
+#include <pandabase.h>
+
+#include "builderBucket.h"
+#include "builderAttrib.h"
+#include "builderVertex.h"
+#include "builderPrim.h"
+
+#include <vector>
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : BuilderNormalVisualizer
+// Description : A useful class for collecting information about
+//               vertices and their associated normals as geometry is
+//               built, so that its normals may be visualized via
+//               renderable geometry.  This is activated by the
+//               _show_normals flag in the BuilderProperties.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderNormalVisualizer {
+public:
+  INLINE BuilderNormalVisualizer(BuilderBucket &bucket);
+
+  void add_prim(const BuilderPrim &prim);
+  void add_prim(const BuilderPrimI &prim);
+
+  void show_normals(GeomNode *node);
+
+private:
+  void add_normal(const BuilderV &center, const BuilderN &normal);
+
+  BuilderBucket &_bucket;
+
+  BuilderV _net_vertex;
+  int _num_vertices;
+  vector<BuilderPrim> _lines;
+};
+
+#include "builderNormalVisualizer.I"
+
+#endif  // SUPPORT_SHOW_NORMALS
+
+#endif

+ 194 - 0
panda/src/builder/builderPrim.cxx

@@ -0,0 +1,194 @@
+// Filename: builderPrim.cxx
+// Created by:  drose (10Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderPrim.h"
+
+#include <geom.h>
+#include <notify.h>
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrim::nonindexed_copy
+//       Access: Public
+//  Description: Makes a nonindexed copy of the given indexed prim, by
+//               looking up the current values of the indexed
+//               coordinates in the given bucket.
+////////////////////////////////////////////////////////////////////
+BuilderPrim &BuilderPrim::
+nonindexed_copy(const BuilderPrimTempl<BuilderVertexI> &copy, 
+		const BuilderBucket &bucket) {
+  clear();
+
+  set_type(copy.get_type());
+
+  if (copy.has_normal()) {
+    nassertr(bucket.get_normals() != (Normalf *)NULL, *this);
+    set_normal(bucket.get_normals()[copy.get_normal()]);
+  }
+  if (copy.has_color()) {
+    nassertr(bucket.get_colors() != (Colorf *)NULL, *this);
+    set_color(bucket.get_colors()[copy.get_color()]);
+  }
+  if (copy.has_pixel_size()) {
+    set_pixel_size(copy.get_pixel_size());
+  }
+
+  int num_verts = copy.get_num_verts();
+  int i;
+  for (i = 0; i < num_verts; i++) {
+    const BuilderVertexI &cv = copy.get_vertex(i);
+    BuilderVertex v;
+    if (cv.has_coord()) {
+      v.set_coord(cv.get_coord_value(bucket));
+    }
+
+    if (cv.has_normal()) {
+      v.set_normal(cv.get_normal_value(bucket));
+    }
+
+    if (cv.has_texcoord()) {
+      v.set_texcoord(cv.get_texcoord_value(bucket));
+    }
+
+    if (cv.has_color()) {
+      v.set_color(cv.get_color_value(bucket));
+    }
+
+    if (cv.has_pixel_size()) {
+      v.set_pixel_size(cv.get_pixel_size());
+    }
+    add_vertex(v);
+  }
+  return *this;
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrim::fill_geom
+//       Access: Public
+//  Description: Fills up the attribute values of a Geom with the
+//               indicated arrays.  This creates a nonindexed Geom.
+////////////////////////////////////////////////////////////////////
+void BuilderPrim::
+fill_geom(Geom *geom, const PTA_BuilderV &v_array,
+	  GeomBindType n_attr, const PTA_BuilderN &n_array,
+	  GeomBindType t_attr, const PTA_BuilderTC &t_array,
+	  GeomBindType c_attr, const PTA_BuilderC &c_array,
+	  const BuilderBucket &, int, int, int) {
+
+  // WARNING!  This is questionable practice.  We have a
+  // PTA_BuilderV etc.; since a BuilderV is just a proxy
+  // to a Vertexf, we can get away with casting this to a
+  // PTA_Vertexf.
+
+  geom->set_coords((PTA_Vertexf &)v_array, G_PER_VERTEX);
+
+  if (n_attr != G_OFF) {
+    geom->set_normals((PTA_Normalf &)n_array, n_attr);
+  }
+
+  if (t_attr != G_OFF) {
+    geom->set_texcoords((PTA_TexCoordf &)t_array, t_attr);
+  }
+
+  if (c_attr != G_OFF) {
+    geom->set_colors((PTA_Colorf &)c_array, c_attr);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimI::fill_geom
+//       Access: Public
+//  Description: Fills up the attribute values of a Geom with the
+//               indicated arrays.  This creates an indexed Geom.
+////////////////////////////////////////////////////////////////////
+void BuilderPrimI::
+fill_geom(Geom *geom, const PTA_ushort &v_array,
+	  GeomBindType n_attr, PTA_ushort n_array,
+	  GeomBindType t_attr, PTA_ushort t_array,
+	  GeomBindType c_attr, PTA_ushort c_array,
+	  const BuilderBucket &bucket,
+	  int num_prims, int num_components, int num_verts) {
+  PTA_Vertexf v_data = bucket.get_coords();
+  PTA_Normalf n_data = bucket.get_normals();
+  PTA_TexCoordf t_data = bucket.get_texcoords();
+  PTA_Colorf c_data = bucket.get_colors();
+
+  // Make sure the data pointers are NULL if the attribute is off.
+  if (n_attr == G_OFF) {
+    n_data = NULL;
+    n_array = NULL;
+  }
+  if (t_attr == G_OFF) {
+    t_data = NULL;
+    t_array = NULL;
+  }
+  if (c_attr == G_OFF) {
+    c_data = NULL;
+    c_array = NULL;
+  }
+    
+  int n_len = 
+    (n_attr==G_PER_VERTEX) ? num_verts :
+    (n_attr==G_PER_COMPONENT) ? num_components :
+    (n_attr==G_PER_PRIM) ? num_prims :
+    (n_attr==G_OVERALL) ? 1 : 0;
+  int t_len =
+    (t_attr==G_PER_VERTEX) ? num_verts :
+    (t_attr==G_PER_COMPONENT) ? num_components :
+    (t_attr==G_PER_PRIM) ? num_prims :
+    (t_attr==G_OVERALL) ? 1 : 0;
+  int c_len = 
+    (c_attr==G_PER_VERTEX) ? num_verts :
+    (c_attr==G_PER_COMPONENT) ? num_components :
+    (c_attr==G_PER_PRIM) ? num_prims :
+    (c_attr==G_OVERALL) ? 1 : 0;
+    
+  // See if we can share some of the index lists.
+  if (n_attr != G_OFF &&
+      memcmp(v_array, n_array, sizeof(ushort) * n_len)==0) {
+    n_array = v_array;
+  }
+  if (t_attr != G_OFF) {
+    if (memcmp(v_array, t_array, sizeof(ushort) * t_len)==0) {
+      t_array = v_array;
+    } else if (t_len <= n_len &&
+	       memcmp(n_array, t_array, sizeof(ushort) * t_len)==0) {
+      t_array = n_array;
+    }
+  }
+  if (c_attr != G_OFF) {
+    if (memcmp(v_array, c_array, sizeof(ushort) * c_len)==0) {
+      c_array = v_array;
+    } else if (c_len <= n_len &&
+	       memcmp(n_array, c_array, sizeof(ushort) * c_len)==0) {
+      c_array = n_array;
+    } else if (c_len <= t_len &&
+	       memcmp(t_array, c_array, sizeof(ushort) * c_len)==0) {
+      c_array = t_array;
+    }
+  }
+
+  geom->set_coords(v_data, G_PER_VERTEX, v_array);
+
+  if (n_attr != G_OFF) {
+    geom->set_normals(n_data, n_attr, n_array);
+  }
+
+  if (t_attr != G_OFF) {
+    geom->set_texcoords(t_data, t_attr, t_array);
+  }
+
+  if (c_attr != G_OFF) {
+    geom->set_colors(c_data, c_attr, c_array);
+  }
+}
+

+ 119 - 0
panda/src/builder/builderPrim.h

@@ -0,0 +1,119 @@
+// Filename: builderPrim.h
+// Created by:  drose (09Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERPRIM_H
+#define BUILDERPRIM_H
+
+///////////////////////////////////////////////////////////////////
+//
+// BuilderPrim, BuilderPrimI
+//
+// The basic class for passing primitives (polygons, etc.) to the
+// builder.  See the comments at the beginning of builder.h and
+// builderVertex.h.
+//
+// A BuilderPrim has a few attributes of its own--color and
+// normal--which are defined in builderAttrib.h (which it inherits
+// from).  It also has a collection of vertices, represented as
+// BuilderVertex objects, each of which can have its own attributes as
+// well.  Any additional attributes, such as texture, lighting, etc.,
+// are considered to be external to the primitive, and are defined in
+// the BuilderBucket object.
+//
+// BuilderPrimI is exactly like BuilderPrim, except that it represents
+// an indexed primitive.  A BuilderPrimI keeps its collection of
+// vertices as BuilderVertexI's, which store their values as index
+// numbers into an array rather than as actual coordinate values. The
+// arrays themselves are stored in the BuilderBucket.
+//
+// In fact, BuilderPrim and BuilderPrimI are both instantiations of
+// the same template object, BuilderPrimTempl, with different vertex
+// types (BuilderVertex and BuilderVertexI, respectively).
+//
+// It is this templating that drives most of the code in this package.
+// A lot of stuff in the builder tool, and everything in the mesher
+// tool, is templated on the BuilderPrim type, so the same code is
+// used to support both indexed and nonindexed primitives.
+//
+//
+// In addition to storing the primitives--individual polygons,
+// generally--as passed in by user code, BuilderPrim objects can store
+// the compound primitives that might have been generated by the
+// mesher, like triangle strips.  In this case, in addition to an
+// array of vertices, it has an array of component attributes, which
+// store the attributes specific to each individual component
+// (e.g. the normal of each triangle in a triangle strip).
+//
+///////////////////////////////////////////////////////////////////
+
+
+
+#include <pandabase.h>
+
+#include "builderPrimTempl.h"
+#include "builderBucket.h"
+#include "pta_BuilderV.h"
+#include "pta_BuilderN.h"
+#include "pta_BuilderTC.h"
+#include "pta_BuilderC.h"
+
+#include <pta_ushort.h>
+#include <geom.h>
+
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BuilderPrimTempl<BuilderVertex>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BuilderPrimTempl<BuilderVertexI>);
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : BuilderPrim
+// Description : The basic class for passing nonindexed primitives
+//               to the builder.  See the comments at the the head of
+//               this file, and in builder.h.
+//
+//               Look in builderPrimTempl.h and builderAttribTempl.h
+//               for most of the interface to BuilderPrim.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderPrim : public BuilderPrimTempl<BuilderVertex> {
+public:
+  BuilderPrim() {}
+ 
+  BuilderPrim &nonindexed_copy(const BuilderPrimTempl<BuilderVertexI> &copy,
+			       const BuilderBucket &bucket);
+
+  static void fill_geom(Geom *geom, const PTA_BuilderV &v_array,
+			GeomBindType n_attr, const PTA_BuilderN &n_array,
+			GeomBindType t_attr, const PTA_BuilderTC &t_array,
+			GeomBindType c_attr, const PTA_BuilderC &c_array,
+			const BuilderBucket &bucket,
+			int num_prims, int num_components, int num_verts);
+};
+
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : BuilderPrimI
+// Description : The basic class for passing indexed primitives
+//               to the builder.
+//
+//               Look in builderPrimTempl.h and builderAttribTempl.h
+//               for most of the interface to BuilderPrimI.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderPrimI : public BuilderPrimTempl<BuilderVertexI> {
+public:
+  BuilderPrimI() {}
+
+  static void fill_geom(Geom *geom, const PTA_ushort &v_array,
+			GeomBindType n_attr, PTA_ushort n_array,
+			GeomBindType t_attr, PTA_ushort t_array,
+			GeomBindType c_attr, PTA_ushort c_array,
+			const BuilderBucket &bucket,
+			int num_prims, int num_components, int num_verts);
+};
+
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#endif

+ 1025 - 0
panda/src/builder/builderPrimTempl.I

@@ -0,0 +1,1025 @@
+// Filename: builderPrimTempl.I
+// Created by:  drose (11Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <notify.h>
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::
+BuilderPrimTempl() {
+  clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::Copy constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::
+BuilderPrimTempl(const BuilderPrimTempl<VTX> &copy) {
+  (*this) = copy;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::Copy assignment operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+operator = (const BuilderPrimTempl<VTX> &copy) {
+  DAttrib::operator = (copy);
+  _verts = copy._verts;
+  _components = copy._components;
+  _type = copy._type;
+  _overall = copy._overall;
+
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_normal
+//       Access: Public
+//  Description: Returns true if set_normal() has been called on the
+//               primitive.  This is unrelated to the normal values
+//               that may or may not override from the vertices.  Also
+//               see has_overall_normal().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_normal() const {
+  return DAttrib::has_normal();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_color
+//       Access: Public
+//  Description: Returns true if set_color() has been called on the
+//               primitive.  This is unrelated to the color values
+//               that may or may not override from the vertices.  Also
+//               see has_overall_color().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_color() const {
+  return DAttrib::has_color();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_pixel_size
+//       Access: Public
+//  Description: Returns true if set_pixel_size() has been called on
+//               the primitive.  This is unrelated to the pixel_size
+//               values that may or may not override from the
+//               vertices.  Also see has_overall_pixel_size().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_pixel_size() const {
+  return DAttrib::has_pixel_size();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_overall_normal
+//       Access: Public
+//  Description: Returns true if the primitive has a single, overall
+//               normal value.  This can happen because of one of: (a)
+//               the primitive had a normal value assigned to it
+//               directly, and its individual vertices and components
+//               did not; (b) each vertex was assigned the same normal
+//               value; (c) each component was assigned the same normal
+//               value.
+//
+//               If has_overall_normal() returns true, then get_normal()
+//               will return the overall normal value.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_overall_normal() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_overall_normal)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_overall_color
+//       Access: Public
+//  Description: Returns true if the primitive has a single, overall
+//               color value.  This can happen because of one of: (a)
+//               the primitive had a color value assigned to it
+//               directly, and its individual vertices and components
+//               did not; (b) each vertex was assigned the same color
+//               value; (c) each component was assigned the same color
+//               value.
+//
+//               If has_overall_color() returns true, then get_color()
+//               will return the overall color value.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_overall_color() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_overall_color)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_overall_pixel_size
+//       Access: Public
+//  Description: Returns true if the primitive has a single, overall
+//               pixel_size value.  This can happen because of one of:
+//               (a) the primitive had a pixel_size value assigned to
+//               it directly, and its individual vertices and
+//               components did not; (b) each vertex was assigned the
+//               same pixel_size value; (c) each component was
+//               assigned the same pixel_size value.
+//
+//               If has_overall_pixel_size() returns true, then
+//               get_pixel_size() will return the overall pixel_size
+//               value.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_overall_pixel_size() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_overall_pixel_size)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_vertex_normal
+//       Access: Public
+//  Description: Returns true if each of the primitive's vertices has
+//               a different normal value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_vertex_normal() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_vertex_normal)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_vertex_color
+//       Access: Public
+//  Description: Returns true if each of the primitive's vertices has
+//               a different color value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_vertex_color() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_vertex_color)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_vertex_texcoord
+//       Access: Public
+//  Description: Returns true if each of the primitive's vertices has
+//               a texcoord value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_vertex_texcoord() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_vertex_texcoord)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_vertex_pixel_size
+//       Access: Public
+//  Description: Returns true if each of the primitive's vertices has
+//               a different pixel_size value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_vertex_pixel_size() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_vertex_pixel_size)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_component_normal
+//       Access: Public
+//  Description: Returns true if the prim is a composite primitive
+//               like a tristrip, and its individual components each
+//               have a different normal value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_component_normal() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_component_normal)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_component_color
+//       Access: Public
+//  Description: Returns true if the prim is a composite primitive
+//               like a tristrip, and its individual components each
+//               have a different color value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_component_color() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_component_color)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_component_pixel_size
+//       Access: Public
+//  Description: Returns true if the prim is a composite primitive
+//               like a tristrip, and its individual components each
+//               have a different pixel_size value set.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_component_pixel_size() const {
+  if ((_overall & BAF_overall_updated) == 0) {
+    ((BuilderPrimTempl<VTX> *)this)->update_overall_attrib();
+  }
+  return (_overall & BAF_component_pixel_size)!=0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_any_normal
+//       Access: Public
+//  Description: Returns true if the prim has a normal value set
+//               at any level.  That is, it returns true if any of
+//               has_overall_normal(), has_vertex_normal(), or
+//               has_component_normal() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_any_normal() const {
+  return has_overall_normal() || has_vertex_normal() || has_component_normal();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_any_color
+//       Access: Public
+//  Description: Returns true if the prim has a color value set at any
+//               level.  That is, it returns true if any of
+//               has_overall_color(), has_vertex_color(), or
+//               has_component_color() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_any_color() const {
+  return has_overall_color() || has_vertex_color() || has_component_color();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_any_texcoord
+//       Access: Public
+//  Description: Returns true if the prim has a texcoord value set
+//               at any level.  Since texture coordinates can only be
+//               set on vertices, this is the same thing as
+//               has_vertex_texcoord().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_any_texcoord() const {
+  return has_vertex_texcoord();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::has_any_pixel_size
+//       Access: Public
+//  Description: Returns true if the prim has a pixel_size value set
+//               at any level.  That is, it returns true if any of
+//               has_overall_pixel_size(), has_vertex_pixel_size(), or
+//               has_component_pixel_size() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+has_any_pixel_size() const {
+  return has_overall_pixel_size() || has_vertex_pixel_size() ||
+    has_component_pixel_size();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::clear
+//       Access: Public
+//  Description: Resets the BuilderPrim to its initial, default state.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+clear() {
+  DAttrib::clear();
+  _type = BPT_poly;
+  return clear_vertices();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::clear_vertices
+//       Access: Public
+//  Description: Removes all the vertices that have been added to the
+//               BuilderPrim, without otherwise affecting its
+//               properties.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+clear_vertices() {
+  _verts.clear();
+  _components.clear();
+  _overall = 0;
+
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::set_attrib
+//       Access: Public
+//  Description: Copies the polygon attributes, color and normal, from
+//               the indicated attribute structure (which might be
+//               another BuilderPrim).
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+set_attrib(const DAttrib &attrib) {
+  DAttrib::operator = (attrib);
+  _overall = 0;
+
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_type
+//       Access: Public
+//  Description: Indicates the type of primitive represented by the
+//               BuilderPrim.  See set_type().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimType BuilderPrimTempl<VTX>::
+get_type() const {
+  return _type;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::set_type
+//       Access: Public
+//  Description: Indicates the type of primitive represented by the
+//               BuilderPrim.  Normally, the user should set this to
+//               one of BPT_poly, BPT_point, or BPT_line.  However,
+//               other types are possible, and are created by the
+//               mesher, including triangle strips, quad strips, and
+//               triangle fans.
+//
+//               Setting this value changes the interpretation of the
+//               vertices, for instance, a triangle strip is defined
+//               by vertices that zigzag back and forth between the
+//               triangles.  Note that when defining a composite
+//               primitive such as a triangle strip, it is also
+//               necessary to call add_component() the correct number
+//               of times to match the calls to add_vertex(),
+//               according to the primitive type.  See
+//               add_component().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+set_type(BuilderPrimType t) {
+  _type = t;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_normal
+//       Access: Public
+//  Description: Returns the value previously set by set_normal(), or
+//               the overall normal value common to all vertices.  It
+//               is an error to call this without first testing that
+//               one of has_normal() or has_overall_normal() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::NType BuilderPrimTempl<VTX>::
+get_normal() const {
+  return DAttrib::get_normal();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::set_normal
+//       Access: Public
+//  Description: Sets the flat normal associated with the primitive.
+//               If each of the primitive's vertices also has a normal
+//               set, the vertex normals will override.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+set_normal(const NType &n) {
+  DAttrib::set_normal(n);
+  _overall = 0;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_color
+//       Access: Public
+//  Description: Returns the value previously set by set_color(), or
+//               the overall color value common to all vertices.  It
+//               is an error to call this without first testing that
+//               one of has_color() or has_overall_color() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::CType BuilderPrimTempl<VTX>::
+get_color() const {
+  return DAttrib::get_color();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::set_color
+//       Access: Public
+//  Description: Sets the flat color associated with the primitive.
+//               If each of the primitive's vertices also has a color
+//               set, the vertex colors will override.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+set_color(const CType &c) {
+  DAttrib::set_color(c);
+  _overall = 0;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_pixel_size
+//       Access: Public
+//  Description: Returns the value previously set by set_pixel_size(),
+//               or the overall pixel_size value common to all
+//               vertices.  It is an error to call this without first
+//               testing that one of has_pixel_size() or
+//               has_overall_pixel_size() is true.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE float BuilderPrimTempl<VTX>::
+get_pixel_size() const {
+  return DAttrib::get_pixel_size();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::set_pixel_size
+//       Access: Public
+//  Description: Sets the line thickness (for a polygon or line) or
+//               size (for a point) in pixels.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+set_pixel_size(float s) {
+  DAttrib::set_pixel_size(s);
+  _overall = 0;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::add_vertex
+//       Access: Public
+//  Description: Adds a new vertex to the BuilderPrim.  The vertex
+//               data is copied into the prim's internal structure.
+//               Vertices should be added in counterclockwise order,
+//               when viewed from the front.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+add_vertex(const Vertex &v) {
+  _overall = 0;
+  _verts.push_back(v);
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_num_verts
+//       Access: Public
+//  Description: Returns the number of vertices in the primitive.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE int BuilderPrimTempl<VTX>::
+get_num_verts() const {
+  return _verts.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_vertex
+//       Access: Public
+//  Description: Returns a reference to the nth vertex of the
+//               primitive, where 0 <= n < get_num_verts().  This
+//               reference may be modified directly, e.g. to set a
+//               vertex normal on a vertex after it has been added to
+//               the primitive.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::Vertex &BuilderPrimTempl<VTX>::
+get_vertex(int n) {
+  nassertr(n >= 0 && n < _verts.size(), *(new Vertex));
+  return _verts[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_vertex (const)
+//       Access: Public
+//  Description: Returns the nth vertex of the primitive, where 0 <= n
+//               < get_num_verts().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE const BuilderPrimTempl<VTX>::Vertex &BuilderPrimTempl<VTX>::
+get_vertex(int n) const {
+  nassertr(n >= 0 && n < _verts.size(), *(new Vertex));
+  return _verts[n];
+}
+
+  
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::add_component
+//       Access: Public
+//  Description: Normally, this function is only used by the mesher
+//               code; user code generally should not need to call it.
+//
+//               In addition to storing a simple polygon, a
+//               BuilderPrim can store a composite primitive, like a
+//               triangle strip.  In this case, it is useful to store
+//               the attributes (like color and normal) associated
+//               with each component.
+//
+//               When get_type() is one of the composite types, it is
+//               the responsibility of the caller to call
+//               add_component() once for each component, where the
+//               number of components is defined by the type and the
+//               number of vertices (e.g. for a triangle strip, the
+//               number of components is the number of vertices - 2).
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX> &BuilderPrimTempl<VTX>::
+add_component(const DAttrib &attrib) {
+  _overall = 0;
+  _components.push_back(attrib);
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_num_components
+//       Access: Public
+//  Description: Returns the number of times add_component() has been
+//               called.  This should be, but is not necessarily, the
+//               same as the number of components in the composite
+//               primitive (e.g. a triangle strip).  See
+//               add_component().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE int BuilderPrimTempl<VTX>::
+get_num_components() const {
+  return _components.size();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_component
+//       Access: Public
+//  Description: Returns a refernece to the attribute structure
+//               associated with the nth component, where 0 <= n <
+//               get_num_components().  This reference may be modified
+//               directly, e.g. to change the color of a polygon after
+//               it has been added to the tristrip.  See
+//               add_component() and get_num_components().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE BuilderPrimTempl<VTX>::DAttrib &BuilderPrimTempl<VTX>::
+get_component(int n) {
+  nassertr(n >= 0 && n < _components.size(), *(new DAttrib));
+  return _components[n];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::get_component (const)
+//       Access: Public
+//  Description: Returns the attributes associated with the nth
+//               component, where 0 <= n < get_num_components().  See
+//               add_component() and get_num_components().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE const BuilderPrimTempl<VTX>::DAttrib &BuilderPrimTempl<VTX>::
+get_component(int n) const {
+  nassertr(n >= 0 && n < _components.size(), *(new DAttrib));
+  return _components[n];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::Ordering operator
+//       Access: Public
+//  Description: Returns true if this primitive should come before the
+//               other one, in the bin ordering.  This is used to sort
+//               polygons into groups which can be tristripped
+//               together, similar to the same operation on the
+//               BuilderBucket, except that this works at a finer
+//               level of detail (i.e. on prims within the same
+//               bucket).
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE bool BuilderPrimTempl<VTX>::
+operator < (const BuilderPrimTempl<VTX> &other) const {
+  int sv1 = sort_value();
+  int sv2 = other.sort_value();
+
+  // Normally, we compare only based on the integer sort_value().
+  // However, other things being equal, if two prims have a different
+  // pixel_size then they sort differently.
+
+  // We can ignore per-vertex pixel_size, since there's no way to
+  // render that anyway.
+
+  return sv1 < sv2 ||
+    (sv1==sv2 && has_pixel_size() && get_pixel_size() < other.get_pixel_size());
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::sort_value
+//       Access: Protected
+//  Description: Returns a number for grouping primitives, such that
+//               only primitives that share the same number can be
+//               tristripped together.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+INLINE int BuilderPrimTempl<VTX>::
+sort_value() const {
+  // If a polygon is missing a normal, color, or uv, it shouldn't be
+  // stripped along with polygons that have normals, colors, or uv's.
+  // Furthermore, if one polygon has vertex normals and another is
+  // flat shaded, they shouldn't be stripped together.
+  return
+    ((has_vertex_normal()!=0) << 5) |
+    ((has_vertex_color()!=0) << 4) |
+    ((has_any_texcoord()!=0) << 3) |
+    ((has_any_normal()!=0) << 2) |
+    ((has_any_color()!=0) << 1) |
+    ((has_pixel_size()!=0) << 0);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//        Class: SameCoord
+//  Description: An STL function object that identifies vertices as
+//               equivalent when they have the same coordinate value,
+//               regardless of their attribute values like texture
+//               coordinates.  This is used in remove_doubled_verts().
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+class SameCoord {
+public:
+  SameCoord() {}
+  bool operator () (const VTX &a, const VTX &b) const {
+    return a.get_coord() == b.get_coord();
+  }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::remove_doubled_verts
+//       Access: Public
+//  Description: Removes consecutive identical vertices from the prim
+//               definition.  These are meaningless and only confuse
+//               polygon subdividing and meshing.
+//
+//               If closed is true, this also removes vertices doubled
+//               at the beginning and end (as if the list of vertices
+//               were implicitly closed, as it is for a polygon).
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+void BuilderPrimTempl<VTX>::
+remove_doubled_verts(int closed) {
+  SameCoord<Vertex> sc;
+  Verts::iterator new_end = unique(_verts.begin(), _verts.end(), sc);
+  _verts.erase(new_end, _verts.end());
+
+  if (closed) {
+    // Then, if this is a polygon (which will be closed anyway),
+    // remove the vertex from the end if it's a repeat of the
+    // beginning.
+    while (!_verts.empty() && sc(_verts.back(), _verts.front())) {
+      _verts.pop_back();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::is_valid
+//       Access: Public
+//  Description: Returns true if the primitive is well-defined and has
+//               the right number of vertices for its primitive type,
+//               and all of its vertices are valid.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+bool BuilderPrimTempl<VTX>::
+is_valid() const {
+  int num_verts = get_num_verts();
+  for (int i = 0; i < num_verts; i++) {
+    if (!get_vertex(i).is_valid()) {
+      return false;
+    }
+  }
+
+  switch (get_type()) {
+  case BPT_poly:
+    return num_verts >= 3;
+  
+  case BPT_point:
+    return num_verts >= 1;
+
+  case BPT_line:
+    return num_verts >= 2;
+
+  case BPT_tri:
+    return num_verts == 3;
+
+  case BPT_tristrip:
+  case BPT_trifan:
+    return num_verts >= 3;
+   
+  case BPT_quad:
+    return num_verts == 4;
+
+  default:
+    return false;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::output
+//       Access: Public
+//  Description: Formats the prim for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+ostream &BuilderPrimTempl<VTX>::
+output(ostream &out) const {
+  int num_verts = get_num_verts();
+
+  out << get_type() << ", " << num_verts << " vertices:\n";
+
+  for (int i = 0; i < num_verts; i++) {
+    out << "  " << i << ". " << get_vertex(i) << "\n";
+  }
+
+  if (has_overall_normal()) {
+    out << "normal = " << get_normal() << "\n";
+  }
+  if (has_overall_color()) {
+    out << "color = " << get_color() << "\n";
+  }
+  if (has_overall_pixel_size()) {
+    out << "pixel_size = " << get_pixel_size() << "\n";
+  }
+
+  return out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::update_overall_attrib
+//       Access: Protected
+//  Description: Examines the primitive and all of its vertices and
+//               components to determine the amount of commonality of
+//               each attribute, and updates the bits in _overall to
+//               indicate this.  If an overall attribute is found, the
+//               primitive attribute value is set to that common
+//               value.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+void BuilderPrimTempl<VTX>::
+update_overall_attrib() {
+  int num_verts = get_num_verts();
+  int num_components = get_num_components();
+  
+  NType common_normal;
+  CType common_color;
+  float common_pixel_size;
+
+  bool has_common_normal = false;
+  bool has_common_color = false;
+  bool has_common_pixel_size = false;
+
+  bool has_component_normal = false;
+  bool has_component_color = false;
+  bool has_component_pixel_size = false;
+
+  bool has_vertex_normal = false;
+  bool has_vertex_color = false;
+  bool has_vertex_texcoord = false;
+  bool has_vertex_pixel_size = false;
+
+  int i;
+  if (num_verts > 0) {
+    has_vertex_texcoord = true;
+    for (i = 0; i < num_verts && has_vertex_texcoord; i++) {
+      has_vertex_texcoord = get_vertex(i).has_texcoord();
+    }
+
+    if (get_vertex(0).has_normal()) {
+      common_normal = get_vertex(0).get_normal();
+      has_common_normal = true;
+      has_vertex_normal = true;
+
+      for (i = 1; i < num_verts; i++) {
+	Vertex &vertex = get_vertex(i);
+	has_vertex_normal = 
+	  (has_vertex_normal && vertex.has_normal());
+	if (!vertex.has_normal() ||
+	    !(vertex.get_normal() == common_normal)) {
+	  has_common_normal = false;
+	}
+      }
+    }
+    if (get_vertex(0).has_color()) {
+      common_color = get_vertex(0).get_color();
+      has_common_color = true;
+      has_vertex_color = true;
+
+      for (i = 1; i < num_verts; i++) {
+	Vertex &vertex = get_vertex(i);
+	has_vertex_color = 
+	  (has_vertex_color && vertex.has_color());
+	if (!vertex.has_color() ||
+	    !(vertex.get_color() == common_color)) {
+	  has_common_color = false;
+	}
+      }
+    }
+    if (get_vertex(0).has_pixel_size()) {
+      common_pixel_size = get_vertex(0).get_pixel_size();
+      has_common_pixel_size = true;
+      has_vertex_pixel_size = true;
+
+      for (i = 1; i < num_verts; i++) {
+	Vertex &vertex = get_vertex(i);
+	has_vertex_pixel_size = 
+	  (has_vertex_pixel_size && vertex.has_pixel_size());
+	if (!vertex.has_pixel_size() || 
+	    !(vertex.get_pixel_size() == common_pixel_size)) {
+	  has_common_pixel_size = false;
+	}
+      }
+    }
+  }
+
+  if (num_components > 0) {
+    if (!has_vertex_normal && get_component(0).has_normal()) {
+      common_normal = get_component(0).get_normal();
+      has_common_normal = true;
+      has_component_normal = true;
+
+      for (i = 1; i < num_components; i++) {
+	DAttrib &component = get_component(i);
+	has_component_normal = 
+	  (has_component_normal && component.has_normal());
+	if (!component.has_normal() ||
+	    !(component.get_normal() == common_normal)) {
+	  has_common_normal = false;
+	}
+      }
+    }
+    if (!has_vertex_color && get_component(0).has_color()) {
+      common_color = get_component(0).get_color();
+      has_common_color = true;
+      has_component_color = true;
+
+      for (i = 1; i < num_components; i++) {
+	DAttrib &component = get_component(i);
+	has_component_color = 
+	  (has_component_color && component.has_color());
+	if (!component.has_color() ||
+	    !(component.get_color() == common_color)) {
+	  has_common_color = false;
+	}
+      }
+    }
+    if (!has_vertex_pixel_size && get_component(0).has_pixel_size()) {
+      common_pixel_size = get_component(0).get_pixel_size();
+      has_common_pixel_size = true;
+      has_component_pixel_size = true;
+
+      for (i = 1; i < num_components; i++) {
+	DAttrib &component = get_component(i);
+	has_component_pixel_size = 
+	  (has_component_pixel_size && component.has_pixel_size());
+	if (!component.has_pixel_size() ||
+	    !(component.get_pixel_size() == common_pixel_size)) {
+	  has_common_pixel_size = false;
+	}
+      }
+    }
+  }
+
+  _overall = BAF_overall_updated;
+  
+  if (has_common_normal) {
+    // The primitive has one overall normal, or each of the vertices
+    // has the same normal.
+    _overall |= BAF_overall_normal;
+    DAttrib::set_normal(common_normal);
+
+  } else if (has_component_normal) {
+    // Each component primitive has a different normal.
+    _overall |= BAF_component_normal;
+
+  } else if (has_vertex_normal) {
+    // Each vertex has a different normal.
+    _overall |= BAF_vertex_normal;
+
+  } else if (has_normal()) {
+    // Well, none of the above, but the prim itself has a normal.
+    _overall |= BAF_overall_normal;
+  }
+    
+  
+  if (has_common_color) {
+    // The primitive has one overall color, or each of the vertices
+    // has the same color.
+    _overall |= BAF_overall_color;
+    DAttrib::set_color(common_color);
+
+  } else if (has_component_color) {
+    // Each component primitive has a different color.
+    _overall |= BAF_component_color;
+
+  } else if (has_vertex_color) {
+    // Each vertex has a different color.
+    _overall |= BAF_vertex_color;
+
+  } else if (has_color()) {
+    // None of the above, but the prim itself has a color.
+    _overall |= BAF_overall_color;
+  }
+
+  if (has_vertex_texcoord) {
+    // Each vertex has a texture coordinate.
+    _overall |= BAF_vertex_texcoord;
+  }
+  
+  if (has_common_pixel_size) {
+    // The primitive has one overall pixel size, or each of the vertices
+    // has the same pixel size.
+    _overall |= BAF_overall_pixel_size;
+    DAttrib::set_pixel_size(common_pixel_size);
+
+  } else if (has_component_pixel_size) {
+    // Each component primitive has a different pixel size.
+    _overall |= BAF_component_pixel_size;
+
+  } else if (has_vertex_pixel_size) {
+    // Each vertex has a different pixel size.
+    _overall |= BAF_vertex_pixel_size;
+
+  } else if (has_pixel_size()) {
+    // The prim itself has a pixel size.
+    _overall |= BAF_overall_pixel_size;
+  }
+}

+ 143 - 0
panda/src/builder/builderPrimTempl.h

@@ -0,0 +1,143 @@
+// Filename: builderPrimTempl.h
+// Created by:  drose (11Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERPRIMTEMPL_H
+#define BUILDERPRIMTEMPL_H
+
+#include <pandabase.h>
+
+#include "builderVertex.h"
+#include "builderAttrib.h"
+#include "builderTypes.h"
+
+#include <vector>
+
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : BuilderPrimTempl
+// Description : The main body of BuilderPrim and BuilderPrimI.  This
+//               is a template class on vertex type, which must be
+//               either BuilderVertex or BuilderVertexI; these classes
+//               are themselves template classes on vertex type,
+//               texcoord type, color type, etc.
+////////////////////////////////////////////////////////////////////
+template <class VTX>
+class BuilderPrimTempl : public VTX::Attrib {
+public:
+  typedef VTX Vertex;
+  typedef TYPENAME VTX::VType VType;
+  typedef TYPENAME VTX::NType NType;
+  typedef TYPENAME VTX::TType TType;
+  typedef TYPENAME VTX::CType CType;
+  typedef TYPENAME VTX::Attrib DAttrib;
+
+  INLINE BuilderPrimTempl();
+  INLINE BuilderPrimTempl(const BuilderPrimTempl &copy);
+  INLINE BuilderPrimTempl &operator = (const BuilderPrimTempl &copy);
+
+  void remove_doubled_verts(int closed);
+  bool is_valid() const;
+
+  // has_normal() etc. is true if the primitive has a normal, as
+  // assigned by the user.  This is unrelated to the vertices or
+  // component primitives that may or may not also have normals.
+  INLINE bool has_normal() const;
+  INLINE bool has_color() const;
+  INLINE bool has_pixel_size() const;
+
+  // The following has_* functions are based on information derived
+  // from examining the vertices and the component primitives that
+  // make up this primitive.
+
+  // has_overall_normal() etc. is true if the primitive has a single,
+  // overall normal shared among all vertices and all component
+  // primitives, or if its normal was assigned directly via
+  // set_normal().  For a polygon, this is the polygon normal.  For a
+  // tristrip, this means all triangles share the same normal.
+  INLINE bool has_overall_normal() const;
+  INLINE bool has_overall_color() const;
+  INLINE bool has_overall_pixel_size() const;
+
+  // has_vertex_normal() etc. is true if each vertex in the primitive
+  // has its own normal.  It is not true if any vertex does not have a
+  // normal.
+  INLINE bool has_vertex_normal() const;
+  INLINE bool has_vertex_color() const;
+  INLINE bool has_vertex_texcoord() const;
+  INLINE bool has_vertex_pixel_size() const;
+
+  // has_component_normal() can only be true for aggregate primitive
+  // types like tristrips.  In that case, it is true if each
+  // individual component (e.g. each triangle of the tristrip) has its
+  // own normal.
+  INLINE bool has_component_normal() const;
+  INLINE bool has_component_color() const;
+  INLINE bool has_component_pixel_size() const;
+
+  // In the above, only one of has_overall_normal(),
+  // has_vertex_normal(), and has_component_normal() can be true for a
+  // given primitive.  For convenience, the following functions return
+  // true if any of the above is true:
+
+  INLINE bool has_any_normal() const;
+  INLINE bool has_any_color() const;
+  INLINE bool has_any_texcoord() const;
+  INLINE bool has_any_pixel_size() const;
+
+  INLINE BuilderPrimTempl &clear();
+  INLINE BuilderPrimTempl &clear_vertices();
+
+  INLINE BuilderPrimTempl &set_attrib(const DAttrib &attrib);
+
+  INLINE BuilderPrimType get_type() const;
+  INLINE BuilderPrimTempl &set_type(BuilderPrimType t);
+
+  INLINE NType get_normal() const;
+  INLINE BuilderPrimTempl &set_normal(const NType &n);
+
+  INLINE CType get_color() const;
+  INLINE BuilderPrimTempl &set_color(const CType &c);
+
+  INLINE float get_pixel_size() const;
+  INLINE BuilderPrimTempl &set_pixel_size(float s);
+
+  INLINE BuilderPrimTempl &add_vertex(const Vertex &v);
+
+  INLINE int get_num_verts() const;
+  INLINE Vertex &get_vertex(int n);
+  INLINE const Vertex &get_vertex(int n) const;
+
+  INLINE BuilderPrimTempl &add_component(const DAttrib &attrib);
+  INLINE int get_num_components() const;
+  INLINE DAttrib &get_component(int n);
+  INLINE const DAttrib &get_component(int n) const;
+
+  INLINE bool operator < (const BuilderPrimTempl &other) const;
+
+  ostream &output(ostream &out) const;
+
+protected:
+  INLINE int sort_value() const;
+  void update_overall_attrib();
+
+  typedef vector<Vertex> Verts;
+  typedef vector<DAttrib> Components;
+
+  Verts _verts;
+  Components _components;
+  BuilderPrimType _type;
+  int _overall;
+};
+
+template <class VTX>
+INLINE ostream &operator << (ostream &out,
+			     const BuilderPrimTempl<VTX> &prim) {
+  return prim.output(out);
+}
+
+
+#include "builderPrimTempl.I"
+
+#endif

+ 116 - 0
panda/src/builder/builderProperties.cxx

@@ -0,0 +1,116 @@
+// Filename: builderProperties.cxx
+// Created by:  drose (17Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderProperties.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderProperties::Ordering operator
+//       Access: Public
+//  Description: Defines an arbitrary ordering among different
+//               properties, and groups identical sets of properties
+//               together.  This operator is used for the more
+//               important task of grouping BuilderBuckets together;
+//               see the comments for the similar function in
+//               builderBucket.cxx.
+////////////////////////////////////////////////////////////////////
+bool BuilderProperties::
+operator < (const BuilderProperties &other) const {
+  int sv1 = sort_value();
+  int sv2 = other.sort_value();
+
+  if (sv1 != sv2) {
+    return sv1 < sv2;
+  }
+
+  if (_coplanar_threshold != other._coplanar_threshold) {
+    return _coplanar_threshold < other._coplanar_threshold;
+  }
+
+  if (_max_tfan_angle != other._max_tfan_angle) {
+    return _max_tfan_angle < other._max_tfan_angle;
+  }
+
+  if (_min_tfan_tris != other._min_tfan_tris) {
+    return _min_tfan_tris < other._min_tfan_tris;
+  }
+
+  if (_show_normals) {
+    if (_normal_scale != other._normal_scale) {
+      return _normal_scale < other._normal_scale;
+    }
+    if (_normal_color != other._normal_color) {
+      return _normal_color < other._normal_color;
+    }
+  }
+
+  return false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::output
+//       Access: Public
+//  Description: Outputs the properties meaningfully.
+////////////////////////////////////////////////////////////////////
+void BuilderProperties::
+output(ostream &out) const {
+  if (_mesh) {
+    out << "T-Mesh using Mesher\n";
+
+    if (_show_tstrips) {
+      out << "Color individual tristrips\n";
+    } else if (_show_qsheets) {
+      out << "Color individual quadsheets\n";
+    } else if (_show_quads) {
+      out << "Color individual quads\n";
+    }
+    if (_retesselate_coplanar) {
+      out << "Retesselate coplanar triangles when needed; threshold = "
+	  << _coplanar_threshold << "\n";
+    }
+  }
+  if (_subdivide_polys) {
+    out << "Subdivide polygons into tris.\n";
+  }
+  if (_consider_fans) {
+    out << "Look for possible triangle fans with max per-triangle angle of " 
+	<< _max_tfan_angle << " degrees.\n";
+    if (_min_tfan_tris==0) {
+      out << "Do not create tfans";
+    } else {
+      out << "Do not create tfans smaller than " << _min_tfan_tris << " tris";
+    }
+    if (_unroll_fans) {
+      out << "; retesselate to tstrips.\n";
+    } else {
+      out << ".\n";
+    }
+  }
+}
+  
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderPrimTempl::sort_value
+//       Access: Protected
+//  Description: Returns a number for grouping properties.  This is
+//               used as a helper function to the ordering operator,
+//               above.  It simply collects all the booleans together
+//               into a single number.
+////////////////////////////////////////////////////////////////////
+int BuilderProperties::
+sort_value() const {
+  return
+    ((_mesh!=0) << 8) |
+    ((_show_tstrips!=0) << 7) |
+    ((_show_qsheets!=0) << 6) |
+    ((_show_quads!=0) << 5) |
+    ((_show_normals!=0) << 4) |
+    ((_retesselate_coplanar!=0) << 3) |
+    ((_unroll_fans!=0) << 2) |
+    ((_subdivide_polys!=0) << 1) |
+    ((_consider_fans!=0) << 0);
+}

+ 115 - 0
panda/src/builder/builderProperties.h

@@ -0,0 +1,115 @@
+// Filename: builderProperties.h
+// Created by:  drose (17Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef BUILDERPROPERTIES_H
+#define BUILDERPROPERTIES_H
+
+#include <pandabase.h>
+
+#include "builderTypes.h"
+
+#ifndef WIN32_VC
+#include <stl_config.h>
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : BuilderProperties
+// Description : A class which defines several parameters used to
+//               control specific behavior of the builder and mesher.
+//               BuilderBucket inherits from this class, so each of
+//               these properties may be set directly on a
+//               BuilderBucket to control the geometry made with just
+//               that bucket.  There may in this way be several
+//               different sets of properties in effect at a given
+//               time.
+//
+//               The initial values for these are set in the private
+//               constructor to BuilderBucket, at the bottom of
+//               builderBucket.cxx.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderProperties {
+public:
+  bool operator < (const BuilderProperties &other) const;
+  void output(ostream &out) const;
+
+  // If this is true, the mesher will be invoked to break up large
+  // polygons and build triangles into tristrips wherever possible.
+  bool _mesh;
+
+  // If this is true, a pair of adjacent coplanar triangles that form
+  // a quad may be replaced with a pair of triangles forming the same
+  // quad but with the opposite diagonal, if this will help the
+  // building of tristrips.
+  bool _retesselate_coplanar;
+
+  // If this is true, a coplanar fan may be treated as a single large
+  // polygon, and then subdivided into a single tristrip, instead of
+  // treating it as a fan.  This can sometimes help form longer
+  // continuous tristrips.
+  bool _unroll_fans;
+
+  // The following three flags serve to illustrate the mesher's
+  // effectiveness by coloring geometry.
+
+  // This colors each created triangle strip with a random color.  The
+  // first triangle of each strip is a little darker than the rest of
+  // the strip.
+  bool _show_tstrips;
+
+  // This colors each rectangular group of quads--called a quadsheet
+  // by the mesher--with a random color.  The mesher always creates
+  // linear tristrips across whatever quadsheets it can identify.
+  bool _show_qsheets;
+
+  // This colors quads blue, and doesn't mesh them further.  Since a
+  // large part of the mesher's algorithm is reassembling adjacent
+  // triangles into quads, it's sometimes helpful to see where it
+  // thinks the best quads lie.
+  bool _show_quads;
+
+  // This shows normals created by creating little colored line
+  // segments to represent each normal.
+  bool _show_normals;
+  double _normal_scale;
+  BuilderC _normal_color;
+
+  // If this is true, large polygons will be subdivided into
+  // triangles.  Otherwise, they will remain large polygons.
+  bool _subdivide_polys;
+
+  // This is the flatness tolerance below which two polygons will be
+  // considered coplanar.  Making it larger makes it easier for the
+  // mesher to reverse triangle diagonals to achieve a good mesh, at
+  // the expense of precision of the surface.
+  double _coplanar_threshold;
+
+  // True if fans are to be considered at all.  Sometimes making fans
+  // is more trouble than they're worth, since they tend to get in the
+  // way of long tristrips.
+  bool _consider_fans;
+
+  // The maximum average angle of the apex of each triangle involved
+  // in a fan.  If the triangles are more loosely packed than this,
+  // don't consider putting them into a fan.
+  double _max_tfan_angle;
+
+  // The minimum number of triangles to be involved in a fan.  Setting
+  // this number lower results in more fans, probably at the expense
+  // of tristrips.  However, setting it to zero means no tfans will be
+  // built (although fans may still be unrolled into tstrips if
+  // _unroll_fans is true).
+  int _min_tfan_tris;
+
+protected:
+  int sort_value() const;
+};
+
+INLINE ostream &operator << (ostream &out, const BuilderProperties &props) {
+  props.output(out);
+  return out;
+}
+
+#endif

+ 102 - 0
panda/src/builder/builderTypes.cxx

@@ -0,0 +1,102 @@
+// Filename: builderTypes.cxx
+// Created by:  drose (11Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderTypes.h"
+
+#include <notify.h>
+
+ostream &operator << (ostream &out, BuilderAttribFlags baf) {
+  const char *space = "";
+  if (baf & BAF_coord) {
+    out << space << "coord";
+    space = " ";
+  }
+  if (baf & BAF_normal) {
+    out << space << "normal";
+    space = " ";
+  }
+  if (baf & BAF_texcoord) {
+    out << space << "texcoord";
+    space = " ";
+  }
+  if (baf & BAF_color) {
+    out << space << "color";
+    space = " ";
+  }
+  if (baf & BAF_pixel_size) {
+    out << space << "pixel_size";
+    space = " ";
+  }
+  if (baf & BAF_overall_updated) {
+    out << space << "overall_updated";
+    space = " ";
+  }
+  if (baf & BAF_overall_normal) {
+    out << space << "overall_normal";
+    space = " ";
+  }
+  if (baf & BAF_overall_color) {
+    out << space << "overall_color";
+    space = " ";
+  }
+  if (baf & BAF_overall_pixel_size) {
+    out << space << "overall_pixel_size";
+    space = " ";
+  }
+  if (baf & BAF_vertex_normal) {
+    out << space << "vertex_normal";
+    space = " ";
+  }
+  if (baf & BAF_vertex_texcoord) {
+    out << space << "vertex_texcoord";
+    space = " ";
+  }
+  if (baf & BAF_vertex_color) {
+    out << space << "vertex_color";
+    space = " ";
+  }
+  if (baf & BAF_vertex_pixel_size) {
+    out << space << "vertex_pixel_size";
+    space = " ";
+  }
+  if (baf & BAF_component_normal) {
+    out << space << "component_normal";
+    space = " ";
+  }
+  if (baf & BAF_component_color) {
+    out << space << "component_color";
+    space = " ";
+  }
+  if (baf & BAF_component_pixel_size) {
+    out << space << "component_pixel_size";
+    space = " ";
+  }
+  return out;
+}
+
+
+ostream &operator << (ostream &out, BuilderPrimType bpt) {
+  switch (bpt) {
+  case BPT_poly:
+    return out << "poly";
+  case BPT_point:
+    return out << "point";
+  case BPT_line:
+    return out << "line";
+  case BPT_tri:
+    return out << "tri";
+  case BPT_tristrip:
+    return out << "tristrip";
+  case BPT_trifan:
+    return out << "trifan";
+  case BPT_quad:
+    return out << "quad";
+  case BPT_quadstrip:
+    return out << "quadstrip";
+  }
+  nassertr(false, out);
+  return out << "(**invalid**)";
+}
+

+ 221 - 0
panda/src/builder/builderTypes.h

@@ -0,0 +1,221 @@
+// Filename: builderTypes.h
+// Created by:  drose (11Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef BUILDERTYPES_H
+#define BUILDERTYPES_H
+
+#include <pandabase.h>
+
+#include <luse.h>
+#include <typedef.h>
+
+#ifndef WIN32_VC
+#include <stl_config.h>
+#endif
+
+static const float nearly_zero = 0.0001;
+
+// The BuilderVec classes are a series of proxies around Vertexf,
+// Normalf, TexCoordf, and Colorf.  They're useful for building
+// collections of these vertex values, and provide handy things like
+// (almost) equivalence operators and sorting operators.
+
+// The BuilderVec's each have a special constructor with a single int.
+// These constructors create an instance of the vector with all values
+// initialized to zero.  This is a cheat to create a uniform way to create
+// a zero-valued VType, CType, or TType without knowing whether the type
+// is indexed (a ushort) or nonindexed (a BuilderVec).
+
+class EXPCL_PANDAEGG BuilderTC {
+public:
+  BuilderTC() {}
+  BuilderTC(int) : _v(0.0, 0.0) {}
+  BuilderTC(const TexCoordf &v) : _v(v) {}
+  BuilderTC(const TexCoordd &v) : _v(v[0], v[1]) {}
+  BuilderTC(const BuilderTC &copy) : _v(copy._v) {}
+
+  operator TexCoordf & () {
+    return _v;
+  }
+
+  operator const TexCoordf & () const {
+    return _v;
+  }
+
+  float operator [] (int n) const { return _v[n]; }
+  float &operator [] (int n) { return _v[n]; }
+
+  BuilderTC &operator = (const BuilderTC &copy) {
+    _v = copy._v;
+    return *this;
+  }
+  bool operator == (const BuilderTC &other) const {
+    return _v.almost_equal(other._v, nearly_zero);
+  }
+
+  // The < operator is simply for ordering vectors in a sorted
+  // container; it has no useful mathematical meaning.
+  bool operator < (const BuilderTC &other) const {
+    return (_v.compare_to(other._v) < 0);
+  }
+  TexCoordf _v;
+};
+
+class EXPCL_PANDAEGG BuilderV {
+public:
+  BuilderV() {}
+  BuilderV(int) : _v(0.0, 0.0, 0.0) {}
+  BuilderV(const Vertexf &v) : _v(v) {}
+  BuilderV(const Vertexd &v) : _v(v[0], v[1], v[2]) {}
+  BuilderV(const BuilderV &copy) : _v(copy._v) {}
+
+  operator Vertexf & () {
+    return _v;
+  }
+
+  operator const Vertexf & () const {
+    return _v;
+  }
+
+  float operator [] (int n) const { return _v[n]; }
+  float &operator [] (int n) { return _v[n]; }
+
+  BuilderV &operator = (const BuilderV &copy) {
+    _v = copy._v;
+    return *this;
+  }
+  bool operator == (const BuilderV &other) const {
+    return _v.almost_equal(other._v, nearly_zero);
+  }
+  bool operator < (const BuilderV &other) const {
+    return (_v.compare_to(other._v) < 0);
+  }
+  Vertexf _v;
+};
+
+class EXPCL_PANDAEGG BuilderN {
+public:
+  BuilderN() {}
+  BuilderN(int) : _v(0.0, 0.0, 0.0) {}
+  BuilderN(const Normalf &v) : _v(v) {}
+  BuilderN(const Normald &v) : _v(v[0], v[1], v[2]) {}
+  BuilderN(const BuilderN &copy) : _v(copy._v) {}
+
+  operator Normalf & () {
+    return _v;
+  }
+
+  operator const Normalf & () const {
+    return _v;
+  }
+
+  float operator [] (int n) const { return _v[n]; }
+  float &operator [] (int n) { return _v[n]; }
+
+  BuilderN &operator = (const BuilderN &copy) {
+    _v = copy._v;
+    return *this;
+  }
+  bool operator == (const BuilderN &other) const {
+    return _v.almost_equal(other._v, nearly_zero);
+  }
+  bool operator < (const BuilderN &other) const {
+    return (_v.compare_to(other._v) < 0);
+  }
+  Normalf _v;
+};
+
+class EXPCL_PANDAEGG BuilderC {
+public:
+  BuilderC() {}
+  BuilderC(int) : _v(0.0, 0.0, 0.0, 0.0) {}
+  BuilderC(const Colorf &v) : _v(v) {}
+  BuilderC(const Colord &v) : _v(v[0], v[1], v[2], v[3]) {}
+  BuilderC(const BuilderC &copy) : _v(copy._v) {}
+
+  operator Colorf & () {
+    return _v;
+  }
+
+  operator const Colorf & () const {
+    return _v;
+  }
+
+  float operator [] (int n) const { return _v[n]; }
+  float &operator [] (int n) { return _v[n]; }
+
+  BuilderC &operator = (const BuilderC &copy) {
+    _v = copy._v;
+    return *this;
+  }
+  bool operator != (const BuilderC &other) const {
+    return !(*this == other);
+  }
+  bool operator == (const BuilderC &other) const {
+    return _v.almost_equal(other._v, nearly_zero);
+  }
+  bool operator < (const BuilderC &other) const {
+    return (_v.compare_to(other._v) < 0);
+  }
+  Colorf _v;
+};
+
+INLINE ostream &operator << (ostream &out, const BuilderTC &v) {
+  return out << "(" << v[0] << " " << v[1] << ")";
+}
+
+INLINE ostream &operator << (ostream &out, const BuilderV &v) {
+  return out << "(" << v[0] << " " << v[1] << " " << v[2] << ")";
+}
+
+INLINE ostream &operator << (ostream &out, const BuilderN &v) {
+  return out << "(" << v[0] << " " << v[1] << " " << v[2] << ")";
+}
+
+INLINE ostream &operator << (ostream &out, const BuilderC &v) {
+  return out << "(" << v[0] << " " << v[1] << " " << v[2] << " "
+	     << v[3] << ")";
+}
+
+enum BuilderAttribFlags {
+  BAF_coord                  = 0x00001,
+  BAF_normal                 = 0x00002,
+  BAF_texcoord               = 0x00004,
+  BAF_color                  = 0x00008,
+  BAF_pixel_size             = 0x00010,
+
+  BAF_overall_updated        = 0x00100,
+  BAF_overall_normal         = 0x00200,
+  BAF_overall_color          = 0x00400,
+  BAF_overall_pixel_size     = 0x00800,
+
+  BAF_vertex_normal          = 0x01000,
+  BAF_vertex_texcoord        = 0x02000,
+  BAF_vertex_color           = 0x04000,
+  BAF_vertex_pixel_size      = 0x08000,
+
+  BAF_component_normal       = 0x10000,
+  BAF_component_color        = 0x20000,
+  BAF_component_pixel_size   = 0x04000,
+};
+
+ostream &operator << (ostream &out, BuilderAttribFlags baf);
+
+enum BuilderPrimType {
+  BPT_poly,
+  BPT_point,
+  BPT_line,
+
+  // The following types are generated internally by the builder and
+  // mesher.  Normally they will not be seen by the user.
+  BPT_tri,
+  BPT_tristrip,
+  BPT_trifan,
+  BPT_quad,
+  BPT_quadstrip,
+};
+
+ostream &operator << (ostream &out, BuilderPrimType bpt);
+
+#endif

+ 265 - 0
panda/src/builder/builderVertex.I

@@ -0,0 +1,265 @@
+// Filename: builderVertex.I
+// Created by:  drose (18Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <notify.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_coord_value
+//       Access: Public
+//  Description: Reassigns the vertex coordinate, without knowing
+//               whether the vertex is indexed or nonindexed.  A
+//               nonindexed vertex will look up the index in the array
+//               and store the resulting value, while an indexed
+//               vertex will just store the index number (which
+//               assumes the array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertex::
+set_coord_value(const BuilderV *array, ushort index) {
+  set_coord(array[index]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_normal_value
+//       Access: Public
+//  Description: Reassigns the vertex normal, without knowing whether
+//               the vertex is indexed or nonindexed.  A nonindexed
+//               vertex will look up the index in the array and store
+//               the resulting value, while an indexed vertex will
+//               just store the index number (which assumes the array
+//               is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertex::
+set_normal_value(const BuilderN *array, ushort index) {
+  set_normal(array[index]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_texcoord_value
+//       Access: Public
+//  Description: Reassigns the vertex texture coordinate, without
+//               knowing whether the vertex is indexed or nonindexed.
+//               A nonindexed vertex will look up the index in the
+//               array and store the resulting value, while an indexed
+//               vertex will just store the index number (which
+//               assumes the array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertex::
+set_texcoord_value(const BuilderTC *array, ushort index) {
+  set_texcoord(array[index]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_color_value
+//       Access: Public
+//  Description: Reassigns the vertex color, without knowing whether
+//               the vertex is indexed or nonindexed.  A nonindexed
+//               vertex will look up the index in the array and store
+//               the resulting value, while an indexed vertex will
+//               just store the index number (which assumes the array
+//               is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertex::
+set_color_value(const BuilderC *array, ushort index) {
+  set_color(array[index]);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_coord_value
+//       Access: Public
+//  Description: Returns the actual coordinate value of the vertex,
+//               whether it is indexed or nonindexed.  Normally, the
+//               value returned by get_coord(), which will be either a
+//               BuilderV or a ushort, is sufficient, but there are
+//               occasional times when it is necessary to get the
+//               actual location in space of the vertex position (for
+//               instance, to subdivide a concave polygon).
+//
+//               This function returns the actual coordinate value.
+//               For a nonindexed vertex, its return value is the same
+//               as get_coord(); for an indexed vertex, it looks up
+//               the vertex's index in the bucket's coord array, and
+//               returns that value.
+//
+//               Note that this makes some perhaps unwarranted
+//               assumptions about indexed geometry; specifically,
+//               that its value is valid at creation time, and that it
+//               won't change too drastically during runtime.
+////////////////////////////////////////////////////////////////////
+INLINE BuilderV BuilderVertex::
+get_coord_value(const BuilderBucket &) const {
+  return get_coord();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_normal_value
+//       Access: Public
+//  Description: Returns the actual normal value of the vertex,
+//               whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderN BuilderVertex::
+get_normal_value(const BuilderBucket &) const {
+  return get_normal();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_texcoord_value
+//       Access: Public
+//  Description: Returns the actual texture coordinate value of the
+//               vertex, whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderTC BuilderVertex::
+get_texcoord_value(const BuilderBucket &) const {
+  return get_texcoord();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_color_value
+//       Access: Public
+//  Description: Returns the actual color value of the vertex,
+//               whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderC BuilderVertex::
+get_color_value(const BuilderBucket &) const {
+  return get_color();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_coord_value
+//       Access: Public
+//  Description: Reassigns the vertex coordinate, without knowing
+//               whether the vertex is indexed or nonindexed.  A
+//               nonindexed vertex will look up the index in the array
+//               and store the resulting value, while an indexed
+//               vertex will just store the index number (which
+//               assumes the array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertexI::
+set_coord_value(const BuilderV *, ushort index) {
+  set_coord(index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_normal_value
+//       Access: Public
+//  Description: Reassigns the vertex normal, without knowing whether
+//               the vertex is indexed or nonindexed.  A nonindexed
+//               vertex will look up the index in the array and store
+//               the resulting value, while an indexed vertex will
+//               just store the index number (which assumes the array
+//               is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertexI::
+set_normal_value(const BuilderN *, ushort index) {
+  set_normal(index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_texcoord_value
+//       Access: Public
+//  Description: Reassigns the vertex texture coordinate, without
+//               knowing whether the vertex is indexed or nonindexed.
+//               A nonindexed vertex will look up the index in the
+//               array and store the resulting value, while an indexed
+//               vertex will just store the index number (which
+//               assumes the array is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertexI::
+set_texcoord_value(const BuilderTC *, ushort index) {
+  set_texcoord(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::set_color_value
+//       Access: Public
+//  Description: Reassigns the vertex color, without knowing whether
+//               the vertex is indexed or nonindexed.  A nonindexed
+//               vertex will look up the index in the array and store
+//               the resulting value, while an indexed vertex will
+//               just store the index number (which assumes the array
+//               is the same one it's indexing on).
+////////////////////////////////////////////////////////////////////
+INLINE void BuilderVertexI::
+set_color_value(const BuilderC *, ushort index) {
+  set_color(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_coord_value
+//       Access: Public
+//  Description: Returns the actual coordinate value of the vertex,
+//               whether it is indexed or nonindexed.  Normally, the
+//               value returned by get_coord(), which will be either a
+//               BuilderV or a ushort, is sufficient, but there are
+//               occasional times when it is necessary to get the
+//               actual location in space of the vertex position (for
+//               instance, to subdivide a concave polygon).
+//
+//               This function returns the actual coordinate value.
+//               For a nonindexed vertex, its return value is the same
+//               as get_coord(); for an indexed vertex, it looks up
+//               the vertex's index in the bucket's coord array, and
+//               returns that value.
+//
+//               Note that this makes some perhaps unwarranted
+//               assumptions about indexed geometry; specifically,
+//               that its value is valid at creation time, and that it
+//               won't change too drastically during runtime.
+////////////////////////////////////////////////////////////////////
+INLINE BuilderV BuilderVertexI::
+get_coord_value(const BuilderBucket &bucket) const {
+  nassertr(bucket.get_coords() != (Vertexf *)NULL, BuilderV());
+  return bucket.get_coords()[get_coord()];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_normal_value
+//       Access: Public
+//  Description: Returns the actual normal value of the vertex,
+//               whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderN BuilderVertexI::
+get_normal_value(const BuilderBucket &bucket) const {
+  nassertr(bucket.get_normals() != (Normalf *)NULL, BuilderN());
+  return bucket.get_normals()[get_normal()];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_texcoord_value
+//       Access: Public
+//  Description: Returns the actual texture coordinate value of the
+//               vertex, whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderTC BuilderVertexI::
+get_texcoord_value(const BuilderBucket &bucket) const {
+  nassertr(bucket.get_texcoords() != (TexCoordf *)NULL, BuilderTC());
+  return bucket.get_texcoords()[get_texcoord()];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertex::get_color_value
+//       Access: Public
+//  Description: Returns the actual color value of the vertex,
+//               whether it is indexed or nonindexed.  See
+//               get_coord_value().
+////////////////////////////////////////////////////////////////////
+INLINE BuilderC BuilderVertexI::
+get_color_value(const BuilderBucket &bucket) const {
+  nassertr(bucket.get_colors() != (Colorf *)NULL, BuilderC());
+  return bucket.get_colors()[get_color()];
+}

+ 12 - 0
panda/src/builder/builderVertex.cxx

@@ -0,0 +1,12 @@
+// Filename: builderVertex.cxx
+// Created by:  drose (11May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderAttrib.h"
+#include "builderVertex.h"
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma implementation
+#endif

+ 119 - 0
panda/src/builder/builderVertex.h

@@ -0,0 +1,119 @@
+// Filename: builderVertex.h
+// Created by:  drose (18Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERVERTEX_H
+#define BUILDERVERTEX_H
+
+///////////////////////////////////////////////////////////////////
+//
+// BuilderVertex, BuilderVertexI
+//
+// The basic class for passing vertex values to the builder.  See the
+// comments at the beginning of builder.h and builderPrim.h.
+//
+// Like BuilderPrim, the BuilderVertex and BuilderVertexI classes are
+// actually two different instantiations of the same template class,
+// BuilderVertexTempl.  The difference is in the types of the
+// attribute values for the four kinds of attributes: vertices
+// (coords), normals, texture coordinates, and colors.  BuilderVertex
+// specifies Coordf, Normalf, TexCoordf, and Colorf for each of these
+// (actually, it's BuilderV, BuilderN, BuilderTC, and BuilderC, which
+// are simply wrappers around the above types), while BuilderVertexI
+// specifies ushort for all of them.
+//
+// It is this templating that drives the whole indexed/nonindexed
+// support in this package and in the mesher.  The two kinds of
+// BuilderVertex are designed to present largely the same interface,
+// regardless of whether its component values are actual vector
+// values, or simply index numbers.  Lots of things, therefore, can
+// template on the BuilderPrim type (which in turn termplates on the
+// BuilderVertex type) and thus easily support both indexed and
+// nonindexed geometry.
+//
+///////////////////////////////////////////////////////////////////
+
+#include <pandabase.h>
+
+#include "builderAttrib.h"
+#include "builderVertexTempl.h"
+#include "builderBucket.h"
+
+// We need to define this temporary macro so we can pass a parameter
+// containing a comma through the macro.
+#define BUILDERVERTEXTEMPL_BUILDERV BuilderVertexTempl<BuilderV, BuilderN, BuilderTC, BuilderC>
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERVERTEXTEMPL_BUILDERV);
+#define BUILDERVERTEXTEMPL_USHORT BuilderVertexTempl<ushort, ushort, ushort, ushort>
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, BUILDERVERTEXTEMPL_USHORT);
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : BuilderVertex
+// Description : The basic class for passing nonindexed vertices to
+//               the builder.  See the comments at the the head of
+//               this file, and in builder.h.
+//
+//               Look in builderVertexTempl.h and builderAttribTempl.h
+//               for most of the interface to BuilderVertex.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderVertex
+  : public BuilderVertexTempl<BuilderV, BuilderN, BuilderTC, BuilderC> {
+public:
+  typedef BuilderAttrib Attrib;
+
+  BuilderVertex() {}
+  BuilderVertex(const BuilderV &c) :
+    BuilderVertexTempl<BuilderV, BuilderN, BuilderTC, BuilderC>(c) {}
+
+  INLINE void set_coord_value(const BuilderV *array, ushort index);
+  INLINE void set_normal_value(const BuilderN *array, ushort index);
+  INLINE void set_texcoord_value(const BuilderTC *array, ushort index);
+  INLINE void set_color_value(const BuilderC *array, ushort index);
+
+  INLINE BuilderV get_coord_value(const BuilderBucket &bucket) const;
+  INLINE BuilderN get_normal_value(const BuilderBucket &bucket) const;
+  INLINE BuilderTC get_texcoord_value(const BuilderBucket &bucket) const;
+  INLINE BuilderC get_color_value(const BuilderBucket &bucket) const;
+
+};
+
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : BuilderVertexI
+// Description : The basic class for passing indexed vertices to the
+//               builder.  See the comments at the the head of this
+//               file, and in builder.h.
+//
+//               Look in builderVertexTempl.h and builderAttribTempl.h
+//               for most of the interface to BuilderVertex.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG BuilderVertexI
+  : public BuilderVertexTempl<ushort, ushort, ushort, ushort> {
+public:
+  typedef BuilderAttribI Attrib;
+
+  BuilderVertexI() {}
+  BuilderVertexI(ushort c) :
+    BuilderVertexTempl<ushort, ushort, ushort, ushort>(c) {}
+
+  INLINE void set_coord_value(const BuilderV *array, ushort index);
+  INLINE void set_normal_value(const BuilderN *array, ushort index);
+  INLINE void set_texcoord_value(const BuilderTC *array, ushort index);
+  INLINE void set_color_value(const BuilderC *array, ushort index);
+
+  INLINE BuilderV get_coord_value(const BuilderBucket &bucket) const;
+  INLINE BuilderN get_normal_value(const BuilderBucket &bucket) const;
+  INLINE BuilderTC get_texcoord_value(const BuilderBucket &bucket) const;
+  INLINE BuilderC get_color_value(const BuilderBucket &bucket) const;
+};
+
+#include "builderVertex.I"
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#endif
+
+

+ 291 - 0
panda/src/builder/builderVertexTempl.I

@@ -0,0 +1,291 @@
+// Filename: builderVertexTempl.I
+// Created by:  drose (11Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include <notify.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT>::
+BuilderVertexTempl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::Constructor (with VType)
+//       Access: Public
+//  Description: Initializes the vertex coordinate with an initial
+//               value.  A handy constructor.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT>::
+BuilderVertexTempl(const VType &c) {
+  set_coord(c);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::Copy constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT>::
+BuilderVertexTempl(const BuilderVertexTempl &copy) {
+  (*this) = copy;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::Copy assignment operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+operator = (const BuilderVertexTempl<VT, NT, TT, CT> &copy) {
+  BuilderAttribTempl<VT, NT, TT, CT>::operator = (copy);
+  _coord = copy._coord;
+  _texcoord = copy._texcoord;
+  _pixel_size = copy._pixel_size;
+
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::is_valid
+//       Access: Public
+//  Description: Returns true if the vertex is valid, i.e. if it has a
+//               vertex coordinate.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderVertexTempl<VT, NT, TT, CT>::
+is_valid() const {
+  return has_coord(); 
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::clear
+//       Access: Public
+//  Description: Resets the vertex to its initial, empty state.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+clear() {
+  BuilderAttribTempl<VT, NT, TT, CT>::clear();
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::has_coord
+//       Access: Public
+//  Description: Returns true if the vertex has a vertex coordinate.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderVertexTempl<VT, NT, TT, CT>::
+has_coord() const {
+  return _flags & BAF_coord;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::get_coord
+//       Access: Public
+//  Description: Returns the vertex's coordinate.  It is an error to
+//               call this without first verifying that has_coord() is
+//               true.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT>::VType BuilderVertexTempl<VT, NT, TT, CT>::
+get_coord() const {
+  nassertr(has_coord(), _coord);
+  return _coord;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::set_coord
+//       Access: Public
+//  Description: Resets the vertex's coordinate.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+set_coord(const VType &c) {
+  _flags |= BAF_coord;
+  _coord = c;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::set_normal
+//       Access: Public
+//  Description: Resets the vertex's normal.  This is overridden from
+//               BuilderAttrib just so we can typecast the return
+//               value to BuilderVertex.  The other functions,
+//               has_normal() and get_normal(), are inherited
+//               untouched from BuilderAttrib.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+set_normal(const NType &n) {
+  BuilderAttribTempl<VT, NT, TT, CT>::set_normal(n);
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::has_texcoord
+//       Access: Public
+//  Description: Returns true if the vertex has a texture coordinate.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE bool BuilderVertexTempl<VT, NT, TT, CT>::
+has_texcoord() const {
+  return (_flags & BAF_texcoord) != 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::set_texcoord
+//       Access: Public
+//  Description: Resets the vertex's texture coordinate.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+set_texcoord(const TType &t) {
+  _flags |= BAF_texcoord;
+  _texcoord = t;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::get_texcoord
+//       Access: Public
+//  Description: Returns the vertex's texture coordinate.  It is an
+//               error to call this without first verifying that
+//               has_texcoord() is true.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT>::TType BuilderVertexTempl<VT, NT, TT, CT>::
+get_texcoord() const {
+  nassertr(has_texcoord(), _texcoord);
+  return _texcoord;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::set_color
+//       Access: Public
+//  Description: Resets the vertex's color.  This is overridden from
+//               BuilderAttrib just so we can typecast the return
+//               value to BuilderVertex.  The other functions,
+//               has_color() and get_color(), are inherited
+//               untouched from BuilderAttrib.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+set_color(const CType &c) {
+  BuilderAttribTempl<VT, NT, TT, CT>::set_color(c);
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::set_pixel_size
+//       Access: Public
+//  Description: Resets the vertex's pixel_size.  This is overridden
+//               from BuilderAttrib just so we can typecast the return
+//               value to BuilderVertex.  The other functions,
+//               has_pixel_size() and get_pixel_size(), are inherited
+//               untouched from BuilderAttrib.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+INLINE BuilderVertexTempl<VT, NT, TT, CT> &BuilderVertexTempl<VT, NT, TT, CT>::
+set_pixel_size(float s) {
+  BuilderAttribTempl<VT, NT, TT, CT>::set_pixel_size(s);
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::operator ==
+//       Access: Public
+//  Description: Assigns an ordering to the vertices.  This is used by
+//               the Mesher to group identical vertices.  This assumes
+//               that all vertices in the locus of consideration will
+//               share the same state: with or without normals,
+//               texcoords, etc.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+bool BuilderVertexTempl<VT, NT, TT, CT>::
+operator == (const BuilderVertexTempl<VT, NT, TT, CT> &other) const {
+  if (has_coord() && !(_coord == other._coord))
+    return false;
+
+  if (has_texcoord() && !(_texcoord == other._texcoord))
+    return false;
+
+  return BuilderAttribTempl<VT, NT, TT, CT>::operator == (other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::operator <
+//       Access: Public
+//  Description: Assigns an ordering to the vertices.  This is used by
+//               the Mesher to group identical vertices.  This assumes
+//               that all vertices to be meshed together must share
+//               the same state: with or without normals, texcoords,
+//               etc.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+bool BuilderVertexTempl<VT, NT, TT, CT>::
+operator < (const BuilderVertexTempl<VT, NT, TT, CT> &other) const {
+  if (has_coord() && !(_coord == other._coord))
+    return _coord < other._coord;
+
+  if (has_texcoord() && !(_texcoord == other._texcoord))
+    return _texcoord < other._texcoord;
+
+  return BuilderAttribTempl<VT, NT, TT, CT>::operator < (other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BuilderVertexTempl::output
+//       Access: Public
+//  Description: Formats the vertex for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+ostream &BuilderVertexTempl<VT, NT, TT, CT>::
+output(ostream &out) const {
+  if (this!=NULL) {
+    if (has_coord()) {
+      out << get_coord();
+    }
+
+    /*
+    if (has_normal()) {
+      out << " normal " << get_normal();
+    }
+    
+    if (has_texcoord()) {
+      out << " texcoord " << get_texcoord();
+    }
+    
+    if (has_color()) {
+      out << " color " << get_color();
+    }
+    
+    if (has_pixel_size()) {
+      out << " pixel_size " << get_pixel_size();
+    }
+    */
+  }
+  return out;
+}

+ 73 - 0
panda/src/builder/builderVertexTempl.h

@@ -0,0 +1,73 @@
+// Filename: builderVertexTempl.h
+// Created by:  drose (09Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUILDERVERTEXTEMPL_H
+#define BUILDERVERTEXTEMPL_H
+
+#include <pandabase.h>
+
+#include "builderTypes.h"
+#include "builderAttribTempl.h"
+
+#include <vector>
+
+
+/////////////////////////////////////////////////////////////////////
+// 	 Class : BuilderVertexTempl
+// Description : The main body of BuilderVertex and BuilderVertexI.
+//               This is a template class on each of the four
+//               attribute types: vertex coordinates, normal, texture
+//               coordinates, and color.  See builderVertex.h.
+////////////////////////////////////////////////////////////////////
+template <class VT, class NT, class TT, class CT>
+class BuilderVertexTempl : public BuilderAttribTempl<VT, NT, TT, CT> {
+public:
+  typedef VT VType;
+  typedef NT NType;
+  typedef TT TType;
+  typedef CT CType;
+
+  INLINE BuilderVertexTempl();
+  INLINE BuilderVertexTempl(const VType &c);
+  INLINE BuilderVertexTempl(const BuilderVertexTempl &copy);
+  INLINE BuilderVertexTempl &operator = (const BuilderVertexTempl &copy);
+
+  INLINE bool is_valid() const;
+  INLINE BuilderVertexTempl &clear();
+
+  INLINE bool has_coord() const;
+  INLINE VType get_coord() const;
+  INLINE BuilderVertexTempl &set_coord(const VType &c);
+
+  INLINE BuilderVertexTempl &set_normal(const NType &c);
+
+  INLINE bool has_texcoord() const;
+  INLINE TType get_texcoord() const;
+  INLINE BuilderVertexTempl &set_texcoord(const TType &t);
+
+  INLINE BuilderVertexTempl &set_color(const CType &c);
+
+  INLINE BuilderVertexTempl &set_pixel_size(float s);
+
+  bool operator == (const BuilderVertexTempl &other) const;
+  bool operator < (const BuilderVertexTempl &other) const;
+
+  ostream &output(ostream &out) const;
+
+protected:
+  VType _coord;
+  TType _texcoord;
+};
+
+template <class VT, class NT, class TT, class CT>
+INLINE ostream &operator << (ostream &out,
+			     const BuilderVertexTempl<VT, NT, TT, CT> &vertex) {
+  return vertex.output(out);
+}
+
+
+#include "builderVertexTempl.I"
+
+#endif

+ 14 - 0
panda/src/builder/config_builder.cxx

@@ -0,0 +1,14 @@
+// Filename: config_builder.cxx
+// Created by:  drose (29Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_builder.h"
+
+#include <dconfig.h>
+
+Configure(config_builder);
+NotifyCategoryDef(builder, "");
+
+ConfigureFn(config_builder) {
+}

+ 14 - 0
panda/src/builder/config_builder.h

@@ -0,0 +1,14 @@
+// Filename: config_builder.h
+// Created by:  drose (29Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_BUILDER_H
+#define CONFIG_BUILDER_H
+
+#include <pandabase.h>
+#include <notifyCategoryProxy.h>
+
+NotifyCategoryDecl(builder, EXPCL_PANDAEGG, EXPTP_PANDAEGG);
+
+#endif

+ 11 - 0
panda/src/builder/mesher.cxx

@@ -0,0 +1,11 @@
+// Filename: mesher.cxx
+// Created by:  drose (11May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "mesher.h"
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma implementation
+#endif

+ 42 - 0
panda/src/builder/mesher.h

@@ -0,0 +1,42 @@
+// Filename: mesher.h
+// Created by:  drose (17Sep97)
+//
+////////////////////////////////////////////////////////////////////
+#ifndef MESHER_H
+#define MESHER_H
+
+#include <pandabase.h>
+
+#include "mesherFanMaker.h"
+#include "mesherEdge.h"
+#include "mesherStrip.h"
+#include "mesherTempl.h"
+#include "builderPrim.h"
+
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherFanMaker<BuilderPrim>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherEdge<BuilderPrim>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherStrip<BuilderPrim>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherTempl<BuilderPrim>);
+
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherFanMaker<BuilderPrimI>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherEdge<BuilderPrimI>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherStrip<BuilderPrimI>);
+EXPORT_TEMPLATE_CLASS(EXPCL_PANDAEGG, EXPTP_PANDAEGG, MesherTempl<BuilderPrimI>);
+
+class EXPCL_PANDAEGG Mesher : public MesherTempl<BuilderPrim> {
+public:
+  Mesher(BuilderBucket *bucket) : MesherTempl<BuilderPrim>(bucket) {}
+};
+
+class EXPCL_PANDAEGG MesherI : public MesherTempl<BuilderPrimI> {
+public:
+  MesherI(BuilderBucket *bucket) : MesherTempl<BuilderPrimI>(bucket) {}
+};
+
+// Tell GCC that we'll take care of the instantiation explicitly here.
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#endif
+

+ 143 - 0
panda/src/builder/mesherEdge.I

@@ -0,0 +1,143 @@
+// Filename: mesherEdge.I
+// Created by:  drose (15Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+
+template <class PrimType>
+INLINE MesherEdge<PrimType>::
+MesherEdge(const Vertex *a, const Vertex *b) : _a(a), _b(b) {
+  _opposite = NULL;
+}
+
+template <class PrimType>
+INLINE MesherEdge<PrimType>::
+MesherEdge(const MesherEdge &copy) : 
+  _a(copy._a),
+  _b(copy._b),
+  _strips(copy._strips),
+  _opposite(copy._opposite)
+{
+}
+  
+template <class PrimType>
+INLINE bool MesherEdge<PrimType>::
+contains_vertex(const Vertex *v) const {
+  return (_a==v || _b==v);
+}
+
+  
+template <class PrimType>
+INLINE bool MesherEdge<PrimType>::
+matches(const MesherEdge &other) const {
+  return (_a == other._a && _b == other._b) ||
+    (_b == other._a && _a == other._b);
+}
+  
+template <class PrimType>
+INLINE MesherEdge<PrimType> *MesherEdge<PrimType>::
+common_ptr() {
+  return min(this, _opposite);
+}
+  
+template <class PrimType>
+INLINE bool MesherEdge<PrimType>::
+operator == (const MesherEdge &other) const {
+  return _a == other._a && _b == other._b;
+}
+  
+template <class PrimType>
+INLINE bool MesherEdge<PrimType>::
+operator < (const MesherEdge &other) const {
+  return _a < other._a || (_a == other._a && _b < other._b);
+}
+  
+template <class PrimType>
+INLINE float MesherEdge<PrimType>::
+compute_length(const BuilderBucket &bucket) const {
+  LVector3f v = ((const Vertexf &)_a->get_coord_value(bucket) - 
+		 (const Vertexf &)_b->get_coord_value(bucket));
+  return length(v);
+}
+  
+template <class PrimType>
+INLINE Vertexf MesherEdge<PrimType>::
+compute_box(const BuilderBucket &bucket) const {
+  LVector3f v = ((const Vertexf &)_a->get_coord_value(bucket) - 
+		 (const Vertexf &)_b->get_coord_value(bucket));
+  return Vertexf(fabs(v[0]), fabs(v[1]), fabs(v[2]));
+}
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: MesherEdge::remove
+//       Access: Public
+//  Description: Removes an edge from a particular strip.
+////////////////////////////////////////////////////////////////////
+template <class PrimType>
+void MesherEdge<PrimType>::
+remove(Strip *strip) {
+  strip->_edges.remove(this);
+  strip->_edges.remove(_opposite);
+
+  _strips.remove(strip);
+  _opposite->_strips.remove(strip);
+}
+
+  
+
+////////////////////////////////////////////////////////////////////
+//     Function: MesherEdge::change_strip
+//       Access: Public
+//  Description: Reparents the edge from strip "from" to strip "to".
+////////////////////////////////////////////////////////////////////
+template <class PrimType>
+void MesherEdge<PrimType>::
+change_strip(Strip *from, Strip *to) {
+  Strips::iterator si;
+
+  for (si = _strips.begin(); si != _strips.end(); ++si) {
+    if (*si == from) {
+      *si = to;
+    }
+  }
+
+  for (si = _opposite->_strips.begin(); 
+       si != _opposite->_strips.end();
+       ++si) {
+    if (*si == from) {
+      *si = to;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MesherEdge::output
+//       Access: Public
+//  Description: Formats the edge for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+template <class PrimType>
+ostream &MesherEdge<PrimType>::
+output(ostream &out) const {
+  out << "Edge [" << *_a << " to " << *_b << "], "
+      << _strips.size() << " strips:";
+
+  Strips::const_iterator si;
+  for (si = _strips.begin(); si != _strips.end(); ++si) {
+    out << " " << (*si)->_index;
+  }
+
+  if (_opposite!=NULL) {
+    out << " opposite "
+	<< _opposite->_strips.size() << " strips:";
+    
+    for (si = _opposite->_strips.begin(); 
+	 si != _opposite->_strips.end(); 
+	 ++si) {
+      out << " " << (*si)->_index;
+    }
+  }
+
+  return out;
+}

+ 62 - 0
panda/src/builder/mesherEdge.h

@@ -0,0 +1,62 @@
+// Filename: mesherEdge.h
+// Created by:  drose (15Sep97)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MESHEREDGE_H
+#define MESHEREDGE_H
+
+#include <pandabase.h>
+
+#include "builderBucket.h"
+
+#include <list>
+#include <math.h>
+
+
+template <class PrimType>
+class MesherStrip;
+
+template <class PrimType>
+class MesherEdge {
+public:
+  typedef PrimType Prim;
+  typedef TYPENAME PrimType::Vertex Vertex;
+  typedef MesherStrip<PrimType> Strip;
+
+  INLINE MesherEdge(const Vertex *a, const Vertex *b);
+  INLINE MesherEdge(const MesherEdge &copy);
+
+  void remove(Strip *strip);
+  void change_strip(Strip *from, Strip *to);
+
+  INLINE bool contains_vertex(const Vertex *v) const;
+
+  INLINE bool matches(const MesherEdge &other) const;
+
+  INLINE MesherEdge *common_ptr();
+
+  INLINE bool operator == (const MesherEdge &other) const;
+  INLINE bool operator < (const MesherEdge &other) const;
+
+  INLINE float compute_length(const BuilderBucket &bucket) const;
+  INLINE Vertexf compute_box(const BuilderBucket &bucket) const;
+
+  ostream &output(ostream &out) const;
+
+  const Vertex *_a, *_b;
+
+  typedef list<Strip *> Strips;
+  Strips _strips;
+  MesherEdge *_opposite;
+};
+
+template <class PrimType>
+INLINE ostream &operator << (ostream &out,
+			     const MesherEdge<PrimType> &edge) {
+  return edge.output(out);
+}
+
+#include "mesherEdge.I"
+
+#endif

+ 312 - 0
panda/src/builder/mesherFanMaker.I

@@ -0,0 +1,312 @@
+// Filename: mesherFanMaker.I
+// Created by:  drose (21Sep97)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "builderFuncs.h"
+
+#include <notify.h>
+#include <mathNumbers.h>
+
+#include <math.h>
+
+template <class PrimType>
+INLINE bool MesherFanMaker<PrimType>::
+operator < (const MesherFanMaker &other) const {
+  return _edges.front() < other._edges.front();
+}
+
+
+template <class PrimType>
+INLINE bool MesherFanMaker<PrimType>::
+operator == (const MesherFanMaker &other) const {
+  return _edges.front() == other._edges.front();
+}
+
+template <class PrimType>
+INLINE bool MesherFanMaker<PrimType>::
+is_empty() const {
+  return (_edges.empty());
+}
+
+template <class PrimType>
+INLINE bool MesherFanMaker<PrimType>::
+is_valid() const {
+  return (_edges.size() > 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MesherFanMaker::is_coplanar_with
+//       Access: Public
+//  Description: Returns true if the strip and the other strip are
+//               coplanar.
+////////////////////////////////////////////////////////////////////
+template <class PrimType>
+INLINE bool MesherFanMaker<PrimType>::
+is_coplanar_with(const MesherFanMaker &other) const {
+  return _planar && other._planar &&
+    _strips.front()->is_coplanar_with(*other._strips.front(),
+				    _bucket->_coplanar_threshold);
+}
+
+template <class PrimType>
+MesherFanMaker<PrimType>::
+MesherFanMaker(const Vertex *vertex, Strip *tri, Mesher *mesher) {
+  _vertex = vertex;
+  _edges.push_back(tri->find_opposite_edge(vertex));
+  _strips.push_back(tri);
+  _planar = tri->_planar;
+  _mesher = mesher;
+  _bucket = _mesher->_bucket;
+}
+
+template <class PrimType>
+bool MesherFanMaker<PrimType>::
+join(MesherFanMaker &other) {
+  nassertr(_vertex == other._vertex, false);
+  nassertr(_mesher == other._mesher, false);
+  nassertr(_bucket == other._bucket, false);
+
+  if (_edges.back()->_b == other._edges.front()->_a) {
+    _planar = is_coplanar_with(other);
+    _edges.splice(_edges.end(), other._edges);
+    _strips.splice(_strips.end(), other._strips);
+    return true;
+  } else if (_edges.front()->_a == other._edges.back()->_b) {
+    _planar = is_coplanar_with(other);
+    _edges.splice(_edges.begin(), other._edges);
+    _strips.splice(_strips.begin(), other._strips);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+
+template <class PrimType>
+float MesherFanMaker<PrimType>::
+compute_angle() const {
+  // We sum up the angles of each triangle.  This is more correct than
+  // taking the net angle from the first edge to the last (since we
+  // may not be in a plane).
+  nassertr(is_valid(), 0.0);
+
+  double angle = 0.0;
+  Vertexf v0 = _vertex->get_coord_value(*_bucket);
+
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    Normalf v1 = (Vertexf &)(*ei)->_a->get_coord_value(*_bucket) - v0;
+    Normalf v2 = (Vertexf &)(*ei)->_b->get_coord_value(*_bucket) - v0;
+    
+    v1 = normalize(v1);
+    v2 = normalize(v2);
+    angle += acos(dot(v1, v2));
+  }
+
+  return angle * 180.0 / MathNumbers::pi;
+}
+
+template <class PrimType>
+int MesherFanMaker<PrimType>::
+build() {
+  nassertr(_edges.size() == _strips.size(), 0);
+
+  int num_tris = _edges.size();
+  float net_angle = compute_angle();
+  float avg_angle = net_angle / num_tris;
+
+  if (avg_angle > _bucket->_max_tfan_angle) {
+    // The triangles are too loose to justify making a fan; it'll
+    // probably make a better quadsheet.
+    return 0;
+  }
+
+  if (_bucket->_min_tfan_tris==0 || num_tris < _bucket->_min_tfan_tris) {
+    // Oops, not enough triangles to justify a fan.
+    if (!_bucket->_unroll_fans) {
+      return 0;
+    }
+
+    // However, we could (maybe) make it a few tristrips!
+
+    // Each section of the fan which is made up of coplanar tris with
+    // identical properties may be retesselated into a tristrip.  What
+    // a sneaky trick!  To do this, we must first identify each such
+    // qualifying section.
+
+    // We define a seam as the edge between any two tris which are
+    // noncoplanar or which do not share identical properties.  Then
+    // we can send each piece between the seams to unroll().
+
+    Strips::iterator si, last_si;
+    Edges::iterator ei, last_ei;
+    
+    // First, rotate the fan so it begins at a seam.  We do this so we
+    // won't be left out with part of one piece at the beginning and
+    // also at the end.
+    si = _strips.begin();
+    last_si = si;
+    ei = _edges.begin();
+    last_ei = ei;
+    int found_seam = false;
+
+    for (++si, ++ei; si != _strips.end() && !found_seam; ++si, ++ei) {
+      nassertr(ei != _edges.end(), 0);
+      if ( !((*si)->_prims.front() == (*last_si)->_prims.front()) ||
+	   !(*si)->is_coplanar_with(*(*last_si), _bucket->_coplanar_threshold)) {
+	// Here's a seam.  Break the fan here.
+	found_seam = true;
+	_edges.splice(_edges.begin(), _edges, ei, _edges.end());
+	_strips.splice(_strips.begin(), _strips, si, _strips.end());
+      }
+    }
+
+    // Now break the fan up along its seams and unroll each piece
+    // separately.
+    si = _strips.begin();
+    last_si = si;
+    ei = _edges.begin();
+    last_ei = ei;
+
+    int count = 0;
+    for (++si, ++ei; si != _strips.end(); ++si, ++ei) {
+      nassertr(ei != _edges.end(), 0);
+      if ( !((*si)->_prims.front() == (*last_si)->_prims.front()) ||
+	   !(*si)->is_coplanar_with(*(*last_si), _bucket->_coplanar_threshold)) {
+	// Here's the end of a run of matching pieces.
+	count += unroll(last_si, si, last_ei, ei);
+	last_si = si;
+	last_ei = ei;
+      }
+    }
+    count += unroll(last_si, si, last_ei, ei);
+
+    return count;
+
+  } else {
+    Strip new_fan;
+    new_fan._type = BPT_trifan;
+    new_fan._verts.push_back(_vertex);
+    
+    new_fan._verts.push_back(_edges.front()->_a);
+    Edges::iterator ei;
+    for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+      new_fan._verts.push_back((*ei)->_b);
+    }
+    
+    Strips::iterator si;
+    for (si = _strips.begin(); si != _strips.end(); ++si) {
+      new_fan._prims.splice(new_fan._prims.end(), (*si)->_prims);
+      (*si)->remove_all_edges();
+      (*si)->_verts.clear();
+      (*si)->_status = MS_dead;
+    }
+    
+    // If we'd built our list of edges and strips right, this sum should
+    // come out so that there are two more vertices than triangles in
+    // the new fan.
+    nassertr(new_fan._verts.size() == new_fan._prims.size() + 2, 0);
+
+    // Now we've built a fan, and it won't be able to mate with
+    // anything else, so add it to the done list.
+    _mesher->_done.push_back(new_fan);
+  }
+
+  return 1;
+}
+
+
+
+template <class PrimType>
+int MesherFanMaker<PrimType>::
+unroll(Strips::iterator strip_begin, Strips::iterator strip_end,
+       Edges::iterator edge_begin, Edges::iterator edge_end) {
+  Edges::iterator ei;
+  Strips::iterator si;
+  
+  int num_tris = 0;
+  for (ei = edge_begin; ei != edge_end; ++ei) {
+    num_tris++;
+  }
+
+  if (num_tris < 3) {
+    // Don't even bother.
+    return 0;
+  }
+
+  Prim poly;
+  
+  // Now we build an n-sided polygon.  We'll decompose it into tris
+  // in a second.
+  poly.set_type(BPT_poly);
+  poly.set_attrib((*strip_begin)->_prims.front());
+
+  ei = edge_end;
+  --ei;
+  if ( !((*ei)->_b == (*edge_begin)->_a)) {
+    // If the fan is less than a full circle, we need to keep the
+    // hub vertex and initial vertex in the poly.  Otherwise, we'll
+    // discard them.
+    poly.add_vertex(*_vertex);
+    poly.add_vertex(*(*edge_begin)->_a);
+  }
+  
+  for (ei = edge_begin; ei != edge_end; ++ei) {
+    poly.add_vertex(*(*ei)->_b);
+  }
+  
+  int result = true;
+
+  if (_bucket->_show_quads) {
+    // If we're showing quads, also show retesselated triangles.
+    _mesher->add_prim(poly, MO_fanpoly);
+
+  } else {
+    // Now decompose the new polygon into triangles.
+    vector<Prim> tris;
+    result = expand(poly, *_bucket, back_inserter(tris));
+
+    if (result) {
+      // Now add each triangle back into the mesher.
+      vector<Prim>::iterator ti;
+      
+      for (ti = tris.begin(); ti != tris.end(); ++ti) {
+	_mesher->add_prim(*ti);
+      }
+    }
+  }
+
+  if (result) {
+    // Now that we've created a new poly, kill off all the old ones.
+    for (si = strip_begin; si != strip_end; ++si) {
+      (*si)->remove_all_edges();
+      (*si)->_verts.clear();
+      (*si)->_prims.clear();
+      (*si)->_status = MS_dead;
+    }
+    return 1;
+  } else {
+    return 0;
+  }
+
+}
+
+template <class PrimType>
+ostream &MesherFanMaker<PrimType>::
+output(ostream &out) const {
+  out << *_vertex << ":[";
+  if (!_edges.empty()) {
+    Edges::const_iterator ei;
+    for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+      out << " " << *(*ei)->_a;
+    }
+    out << " " << *_edges.back()->_b;
+  }
+  out << " ]";
+  
+  if (_planar) {
+    out << " (planar)";
+  }
+  return out;
+}

Some files were not shown because too many files changed in this diff