Browse Source

Merge branch 'master' into cmake

Sam Edwards 10 years ago
parent
commit
b1a294631a

+ 1 - 0
direct/src/motiontrail/MotionTrail.py

@@ -4,6 +4,7 @@ from panda3d.core import *
 from panda3d.direct import *
 from direct.task import Task
 from direct.showbase.DirectObject import DirectObject
+from direct.directnotify.DirectNotifyGlobal import directNotify
 
 
 def remove_task ( ):

+ 1 - 1
direct/src/p3d/Packager.py

@@ -3361,7 +3361,7 @@ class Packager:
         self.currentPackage.addExtensionModules()
         self.currentPackage.freezer.reset()
 
-        self.do_file('panda3d/_core.pyd', newDir='panda3d')
+        self.do_file('panda3d/core.pyd', newDir='panda3d')
 
         # This is the key Python module that is imported at runtime to
         # start an application running.

+ 1 - 1
direct/src/plugin/p3dPythonRun.cxx

@@ -193,7 +193,7 @@ run_python() {
     return 1;
   }
 
-  // Set the __path__ such that it can find panda3d/_core.pyd, etc.
+  // Set the __path__ such that it can find panda3d/core.pyd, etc.
   Filename panda3d_dir(dir, "panda3d");
   string dir_str = panda3d_dir.to_os_specific();
   PyModule_AddObject(panda3d_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));

+ 1 - 1
direct/src/showbase/VFSImporter.py

@@ -1,7 +1,7 @@
 __all__ = ['register', 'sharedPackages',
            'reloadSharedPackage', 'reloadSharedPackages']
 
-from panda3d._core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
+from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
 import sys
 import marshal
 import imp

+ 1 - 1
direct/src/stdpy/file.py

@@ -10,7 +10,7 @@ __all__ = [
     'execfile',
     ]
 
-import panda3d._core as core
+from panda3d import core
 import sys
 import os
 import io

+ 1 - 0
dtool/src/cppparser/cppMakeSeq.h

@@ -19,6 +19,7 @@
 
 #include "cppDeclaration.h"
 #include "cppIdentifier.h"
+#include "cppFunctionGroup.h"
 
 ///////////////////////////////////////////////////////////////////
 //       Class : CPPMakeSeq

+ 2 - 9
panda/src/display/graphicsStateGuardian.cxx

@@ -3008,15 +3008,8 @@ close_gsg() {
   // will be responsible for cleaning up anything we don't clean up
   // explicitly.  We'll just let them drop.
 
-  // However, if any objects have recently been released, we have to
-  // ensure they are actually deleted properly.
-  Thread *current_thread = Thread::get_current_thread();
-  _prepared_objects->begin_frame(this, current_thread);
-  _prepared_objects->end_frame(current_thread);
-
-  // We have to clear the list of timer queries, though, otherwise
-  // their destructors will cause a crash when they try to access
-  // the GSG object.
+  // Make sure that all the contexts belonging to the GSG are deleted.
+  _prepared_objects.clear();
 #ifdef DO_PSTATS
   _pending_timer_queries.clear();
 #endif

+ 11 - 16
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -409,20 +409,19 @@ rebuild_bitplanes() {
 
   // Now create the FBO's.
   _have_any_color = false;
-  _fbo.reserve(num_fbos);
-  for (int layer = 0; layer < num_fbos; ++layer) {
-    if (layer >= _fbo.size()) {
-      _fbo.push_back(0);
-    }
 
+  if (num_fbos > _fbo.size()) {
+    // Generate more FBO handles.
+    int start = _fbo.size();
+    _fbo.resize(num_fbos, 0);
+    glgsg->_glGenFramebuffers(num_fbos - start, &_fbo[start]);
+  }
+
+  for (int layer = 0; layer < num_fbos; ++layer) {
     // Bind the FBO
     if (_fbo[layer] == 0) {
-      glgsg->_glGenFramebuffers(1, &_fbo[layer]);
-
-      if (_fbo[layer] == 0) {
-        report_my_gl_errors();
-        return;
-      }
+      report_my_gl_errors();
+      return;
     }
     glgsg->bind_fbo(_fbo[layer]);
 
@@ -1118,11 +1117,7 @@ generate_mipmaps() {
     CLP(TextureContext) *gtc = *it;
 
     if (gtc->_generate_mipmaps) {
-      glgsg->_state_texture = 0;
-      glgsg->update_texture(gtc, true);
-      glgsg->apply_texture(gtc);
-      glgsg->_glGenerateMipmap(gtc->_target);
-      glBindTexture(gtc->_target, 0);
+      glgsg->generate_mipmaps(gtc);
     }
   }
 

+ 115 - 14
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -284,12 +284,14 @@ fix_component_ordering(PTA_uchar &new_image,
   case GL_RGB:
     switch (tex->get_component_type()) {
     case Texture::T_unsigned_byte:
+    case Texture::T_byte:
       new_image = PTA_uchar::empty_array(orig_image_size);
       uchar_bgr_to_rgb(new_image, orig_image, orig_image_size / 3);
       result = new_image;
       break;
 
     case Texture::T_unsigned_short:
+    case Texture::T_short:
       new_image = PTA_uchar::empty_array(orig_image_size);
       ushort_bgr_to_rgb((unsigned short *)new_image.p(),
                         (const unsigned short *)orig_image,
@@ -305,12 +307,14 @@ fix_component_ordering(PTA_uchar &new_image,
   case GL_RGBA:
     switch (tex->get_component_type()) {
     case Texture::T_unsigned_byte:
+    case Texture::T_byte:
       new_image = PTA_uchar::empty_array(orig_image_size);
       uchar_bgra_to_rgba(new_image, orig_image, orig_image_size / 4);
       result = new_image;
       break;
 
     case Texture::T_unsigned_short:
+    case Texture::T_short:
       new_image = PTA_uchar::empty_array(orig_image_size);
       ushort_bgra_to_rgba((unsigned short *)new_image.p(),
                           (const unsigned short *)orig_image,
@@ -1873,6 +1877,15 @@ reset() {
 #endif  // OPENGLES
 #endif
 
+#ifndef OPENGLES
+  if (is_at_least_gl_version(4, 5) || has_extension("GL_ARB_direct_state_access")) {
+    _glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)
+      get_extension_func("glGenerateTextureMipmap");
+  } else {
+    _glGenerateTextureMipmap = NULL;
+  }
+#endif
+
   _supports_framebuffer_multisample = false;
   if ( has_extension("GL_EXT_framebuffer_multisample") ) {
     _supports_framebuffer_multisample = true;
@@ -4685,6 +4698,13 @@ void CLP(GraphicsStateGuardian)::
 release_texture(TextureContext *tc) {
   CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
 
+#ifndef OPENGLES
+  _textures_needing_fetch_barrier.erase(gtc);
+  _textures_needing_image_access_barrier.erase(gtc);
+  _textures_needing_update_barrier.erase(gtc);
+  _textures_needing_framebuffer_barrier.erase(gtc);
+#endif
+
   glDeleteTextures(1, &gtc->_index);
 
   if (gtc->_buffer != 0) {
@@ -7808,6 +7828,10 @@ get_component_type(Texture::ComponentType component_type) {
 #ifndef OPENGLES_1
     return GL_INT;
 #endif
+  case Texture::T_byte:
+    return GL_BYTE;
+  case Texture::T_short:
+    return GL_SHORT;
   default:
     GLCAT.error() << "Invalid Texture::Type value!\n";
     return GL_UNSIGNED_BYTE;
@@ -8432,6 +8456,10 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
 #ifndef OPENGLES
     if (tex->get_component_type() == Texture::T_unsigned_short) {
       return GL_RGBA16;
+    } else if (tex->get_component_type() == Texture::T_short) {
+      return GL_RGBA16_SNORM;
+    } else if (tex->get_component_type() == Texture::T_byte) {
+      return GL_RGBA8_SNORM;
     } else
 #endif
     {
@@ -8448,27 +8476,32 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
     return force_sized ? GL_RGBA8 : GL_RGBA;
 #else
   case Texture::F_rgba8:
-    return GL_RGBA8;
+    if (Texture::is_unsigned(tex->get_component_type())) {
+      return GL_RGBA8;
+    } else {
+      return GL_RGBA8_SNORM;
+    }
+
   case Texture::F_r8i:
-    if (tex->get_component_type() == Texture::T_unsigned_byte) {
+    if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_R8UI;
     } else {
       return GL_R8I;
     }
   case Texture::F_rg8i:
-    if (tex->get_component_type() == Texture::T_unsigned_byte) {
+    if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RG8UI;
     } else {
       return GL_RG8I;
     }
   case Texture::F_rgb8i:
-    if (tex->get_component_type() == Texture::T_unsigned_byte) {
+    if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RGB8UI;
     } else {
       return GL_RGB8I;
     }
   case Texture::F_rgba8i:
-    if (tex->get_component_type() == Texture::T_unsigned_byte) {
+    if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RGBA8UI;
     } else {
       return GL_RGBA8I;
@@ -8480,17 +8513,24 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
   case Texture::F_rgba16:
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RGBA16F;
-    } else {
+    } else if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RGBA16;
+    } else {
+      return GL_RGBA16_SNORM;
     }
   case Texture::F_rgba32:
     return GL_RGBA32F;
 #endif  // OPENGLES
 
   case Texture::F_rgb:
-    if (tex->get_component_type() == Texture::T_float) {
-      return GL_RGB16F;
-    } else {
+    switch (tex->get_component_type()) {
+    case Texture::T_float: return GL_RGB16F;
+#ifndef OPENGLES
+    case Texture::T_unsigned_short: return GL_RGB16;
+    case Texture::T_short: return GL_RGB16_SNORM;
+    case Texture::T_byte: return GL_RGB8_SNORM;
+#endif
+    default:
       return force_sized ? GL_RGB8 : GL_RGB;
     }
 
@@ -8513,14 +8553,20 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
     return GL_RGB16F;
 #else
   case Texture::F_rgb8:
-    return GL_RGB8;
+    if (Texture::is_unsigned(tex->get_component_type())) {
+      return GL_RGB8;
+    } else {
+      return GL_RGB8_SNORM;
+    }
   case Texture::F_rgb12:
     return GL_RGB12;
   case Texture::F_rgb16:
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RGB16F;
-    } else {
+    } else if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RGB16;
+    } else {
+      return GL_RGB16_SNORM;
     }
 #endif  // OPENGLES
   case Texture::F_rgb32:
@@ -8540,14 +8586,18 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
   case Texture::F_r16:
     if (tex->get_component_type() == Texture::T_float) {
       return GL_R16F;
-    } else {
+    } else if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_R16;
+    } else {
+      return GL_R16_SNORM;
     }
   case Texture::F_rg16:
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RG16F;
-    } else {
+    } else if (Texture::is_unsigned(tex->get_component_type())) {
       return GL_RG16;
+    } else {
+      return GL_RG16_SNORM;
     }
 #endif
 
@@ -8560,7 +8610,14 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
   case Texture::F_red:
   case Texture::F_green:
   case Texture::F_blue:
-    return force_sized ? GL_R8 : GL_RED;
+#ifndef OPENGLES
+    if (!Texture::is_unsigned(tex->get_component_type())) {
+      return GL_R8_SNORM;
+    } else
+#endif
+    {
+      return force_sized ? GL_R8 : GL_RED;
+    }
 #endif
 
   case Texture::F_alpha:
@@ -8574,6 +8631,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
 #ifndef OPENGLES
     if (tex->get_component_type() == Texture::T_float) {
       return GL_LUMINANCE16F_ARB;
+    } else if (tex->get_component_type() == Texture::T_short) {
+      return GL_LUMINANCE16_SNORM;
     } else if (tex->get_component_type() == Texture::T_unsigned_short) {
       return GL_LUMINANCE16;
     } else
@@ -11710,6 +11769,31 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::generate_mipmaps
+//       Access: Protected
+//  Description: Causes mipmaps to be generated for an uploaded
+//               texture.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+generate_mipmaps(CLP(TextureContext) *gtc) {
+#ifndef OPENGLES
+  if (_glGenerateTextureMipmap != NULL) {
+    // OpenGL 4.5 offers an easy way to do this without binding.
+    _glGenerateTextureMipmap(gtc->_index);
+    return;
+  }
+#endif
+
+  if (_glGenerateMipmap != NULL) {
+    _state_texture = 0;
+    update_texture(gtc, true);
+    apply_texture(gtc);
+    _glGenerateMipmap(gtc->_target);
+    glBindTexture(gtc->_target, 0);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::upload_simple_texture
 //       Access: Protected
@@ -12117,15 +12201,19 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     break;
 
   case GL_R8I:
+    type = Texture::T_byte;
     format = Texture::F_r8i;
     break;
   case GL_RG8I:
+    type = Texture::T_byte;
     format = Texture::F_rg8i;
     break;
   case GL_RGB8I:
+    type = Texture::T_byte;
     format = Texture::F_rgb8i;
     break;
   case GL_RGBA8I:
+    type = Texture::T_byte;
     format = Texture::F_rgba8i;
     break;
 
@@ -12195,6 +12283,19 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     type = Texture::T_unsigned_short;
     format = Texture::F_r16;
     break;
+
+  case GL_RGB16_SNORM:
+    type = Texture::T_short;
+    format = Texture::F_rgb16;
+    break;
+  case GL_RG16_SNORM:
+    type = Texture::T_short;
+    format = Texture::F_rg16;
+    break;
+  case GL_R16_SNORM:
+    type = Texture::T_short;
+    format = Texture::F_r16;
+    break;
 #endif
 
 #ifndef OPENGLES

+ 5 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -546,6 +546,7 @@ protected:
                             GLenum component_type,
                             bool one_page_only, int z,
                             Texture::CompressionMode image_compression);
+  void generate_mipmaps(CLP(TextureContext) *gtc);
   bool upload_simple_texture(CLP(TextureContext) *gtc);
 
   size_t get_texture_memory_size(CLP(TextureContext) *gtc);
@@ -794,6 +795,10 @@ public:
   PFNGLGENERATEMIPMAPEXTPROC _glGenerateMipmap;
   PFNGLBINDPROGRAMARBPROC _glBindProgram;
 
+#ifndef OPENGLES
+  PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
+#endif
+
   bool _supports_framebuffer_multisample;
   bool _supports_framebuffer_multisample_coverage_nv;
   INLINE bool get_supports_framebuffer_multisample();

+ 19 - 4
panda/src/glstuff/glShaderContext_src.cxx

@@ -2479,6 +2479,15 @@ glsl_report_program_errors(GLuint program, bool fatal) {
 ////////////////////////////////////////////////////////////////////
 bool CLP(ShaderContext)::
 glsl_compile_shader(Shader::ShaderType type) {
+  static const char *types[] = {"", "vertex ", "fragment ", "geometry ",
+      "tessellation control ", "tessellation evaluation ", "compute ", ""};
+
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "Compiling GLSL " << types[type] << "shader "
+      << _shader->get_filename(type) << "\n";
+  }
+
   GLuint handle = 0;
   switch (type) {
     case Shader::ST_vertex:
@@ -2514,7 +2523,7 @@ glsl_compile_shader(Shader::ShaderType type) {
   }
   if (!handle) {
     GLCAT.error()
-      << "Could not create a GLSL shader of the requested type.\n";
+      << "Could not create a GLSL " << types[type] << "shader.\n";
     _glgsg->report_my_gl_errors();
     return false;
   }
@@ -2533,8 +2542,8 @@ glsl_compile_shader(Shader::ShaderType type) {
 
   if (status != GL_TRUE) {
     GLCAT.error()
-      << "An error occurred while compiling GLSL shader "
-      << _shader->get_filename(type) << ":\n";
+      << "An error occurred while compiling GLSL " << types[type]
+      << "shader " << _shader->get_filename(type) << ":\n";
     glsl_report_shader_errors(handle, type, true);
     _glgsg->_glDeleteShader(handle);
     _glgsg->report_my_gl_errors();
@@ -2629,12 +2638,18 @@ glsl_compile_and_link() {
   }
 #endif
 
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "Linking GLSL shader " << _shader->get_filename() << "\n";
+  }
+
   _glgsg->_glLinkProgram(_glsl_program);
 
   GLint status;
   _glgsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status);
   if (status != GL_TRUE) {
-    GLCAT.error() << "An error occurred while linking GLSL shader program!\n";
+    GLCAT.error() << "An error occurred while linking GLSL shader "
+                  << _shader->get_filename() << "\n";
     glsl_report_program_errors(_glsl_program, true);
     return false;
   }

+ 0 - 9
panda/src/glstuff/glTextureContext_src.cxx

@@ -23,15 +23,6 @@ TypeHandle CLP(TextureContext)::_type_handle;
 ////////////////////////////////////////////////////////////////////
 CLP(TextureContext)::
 ~CLP(TextureContext)() {
-#ifndef OPENGLES
-  if (gl_enable_memory_barriers) {
-    _glgsg->_textures_needing_fetch_barrier.erase(this);
-    _glgsg->_textures_needing_image_access_barrier.erase(this);
-    _glgsg->_textures_needing_update_barrier.erase(this);
-    _glgsg->_textures_needing_framebuffer_barrier.erase(this);
-  }
-#endif
-
   // Don't call glDeleteTextures; we may not have an active context.
 }
 

+ 67 - 0
panda/src/gobj/texture.cxx

@@ -1552,10 +1552,12 @@ write(ostream &out, int indent_level) const {
 
   switch (cdata->_component_type) {
   case T_unsigned_byte:
+  case T_byte:
     out << " bytes";
     break;
 
   case T_unsigned_short:
+  case T_short:
     out << " shorts";
     break;
 
@@ -2001,6 +2003,10 @@ format_component_type(ComponentType ct) {
     return "unsigned_int_24_8";
   case T_int:
     return "int";
+  case T_byte:
+    return "unsigned_byte";
+  case T_short:
+    return "short";
   }
 
   return "**invalid**";
@@ -2024,6 +2030,10 @@ string_component_type(const string &str) {
     return T_unsigned_int_24_8;
   } else if (cmp_nocase(str, "int") == 0) {
     return T_int;
+  } else if (cmp_nocase(str, "byte") == 0) {
+    return T_byte;
+  } else if (cmp_nocase(str, "short") == 0) {
+    return T_short;
   }
 
   gobj_cat->error()
@@ -2411,6 +2421,19 @@ make_texture() {
   return new Texture;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::is_unsigned
+//       Access: Public, Static
+//  Description: Returns true if the indicated component type is
+//               unsigned, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+is_unsigned(Texture::ComponentType ctype) {
+  return (ctype == T_unsigned_byte ||
+          ctype == T_unsigned_short ||
+          ctype == T_unsigned_int_24_8);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::is_specific
 //       Access: Public, Static
@@ -4525,6 +4548,48 @@ do_get_clear_data(const CData *cdata, unsigned char *into) const {
       }
       break;
     }
+
+  case T_byte:
+    {
+      LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor(-1));
+      scaled *= 127;
+      switch (cdata->_num_components) {
+      case 2:
+        into[1] = (char)scaled[1];
+      case 1:
+        into[0] = (char)scaled[0];
+        break;
+      case 4:
+        into[3] = (char)scaled[3];
+      case 3: // BGR <-> RGB
+        into[0] = (char)scaled[2];
+        into[1] = (char)scaled[1];
+        into[2] = (char)scaled[0];
+        break;
+      }
+      break;
+    }
+
+  case T_short:
+    {
+      LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor(-1));
+      scaled *= 32767;
+      switch (cdata->_num_components) {
+      case 2:
+        ((short *)into)[1] = (short)scaled[1];
+      case 1:
+        ((short *)into)[0] = (short)scaled[0];
+        break;
+      case 4:
+        ((short *)into)[3] = (short)scaled[3];
+      case 3: // BGR <-> RGB
+        ((short *)into)[0] = (short)scaled[2];
+        ((short *)into)[1] = (short)scaled[1];
+        ((short *)into)[2] = (short)scaled[0];
+        break;
+      }
+      break;
+    }
   }
 
   return cdata->_num_components * cdata->_component_width;
@@ -5222,10 +5287,12 @@ do_set_component_type(CData *cdata, Texture::ComponentType component_type) {
 
   switch (component_type) {
   case T_unsigned_byte:
+  case T_byte:
     cdata->_component_width = 1;
     break;
 
   case T_unsigned_short:
+  case T_short:
     cdata->_component_width = 2;
     break;
 

+ 3 - 0
panda/src/gobj/texture.h

@@ -93,6 +93,8 @@ PUBLISHED:
     T_float,
     T_unsigned_int_24_8,  // Packed
     T_int,
+    T_byte,
+    T_short,
   };
 
   enum Format {
@@ -527,6 +529,7 @@ public:
   static PT(Texture) make_texture();
 
 public:
+  static bool is_unsigned(ComponentType ctype);
   static bool is_specific(CompressionMode compression);
   static bool has_alpha(Format format);
   static bool has_binary_alpha(Format format);

+ 4 - 21
panda/src/text/textAssembler.cxx

@@ -587,33 +587,16 @@ assemble_text() {
     if (placement->_properties != properties) {
       // Get a new set of properties for future glyphs.
       properties = placement->_properties;
-      text_state = RenderState::make_empty();
-      shadow_state = RenderState::make_empty();
+      text_state = properties->get_text_state();
       shadow_xform = LMatrix4::ident_mat();
 
-      if (properties->has_text_color()) {
-        text_state = text_state->add_attrib(ColorAttrib::make_flat(properties->get_text_color()));
-        if (properties->get_text_color()[3] != 1.0) {
-          text_state = text_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-        }
-      }
-
-      if (properties->has_bin()) {
-        text_state = text_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 2));
-      }
-
       if (properties->has_shadow()) {
-        shadow_state = shadow_state->add_attrib(ColorAttrib::make_flat(properties->get_shadow_color()));
-        if (properties->get_shadow_color()[3] != 1.0) {
-          shadow_state = shadow_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-        }
-
-        if (properties->has_bin()) {
-          shadow_state = shadow_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 1));
-        }
+        shadow_state = properties->get_shadow_state();
 
         LVector2 offset = properties->get_shadow();
         shadow_xform = LMatrix4::translate_mat(offset[0], 0.0f, -offset[1]);
+      } else {
+        shadow_state.clear();
       }
     }
 

+ 12 - 0
panda/src/text/textProperties.I

@@ -550,6 +550,7 @@ INLINE void TextProperties::
 set_text_color(const LColor &text_color) {
   _text_color = text_color;
   _specified |= F_has_text_color;
+  _text_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -562,6 +563,7 @@ INLINE void TextProperties::
 clear_text_color() {
   _text_color.set(1.0f, 1.0f, 1.0f, 1.0f);
   _specified &= ~F_has_text_color;
+  _text_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -603,6 +605,7 @@ INLINE void TextProperties::
 set_shadow_color(const LColor &shadow_color) {
   _shadow_color = shadow_color;
   _specified |= F_has_shadow_color;
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -614,6 +617,7 @@ INLINE void TextProperties::
 clear_shadow_color() {
   _shadow_color.set(0.0f, 0.0f, 0.0f, 1.0f);
   _specified &= ~F_has_shadow_color;
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -711,6 +715,8 @@ INLINE void TextProperties::
 set_bin(const string &bin) {
   _bin = bin;
   _specified |= F_has_bin;
+  _text_state.clear();
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -725,6 +731,8 @@ INLINE void TextProperties::
 clear_bin() {
   _bin = string();
   _specified &= ~F_has_bin;
+  _text_state.clear();
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -768,6 +776,8 @@ INLINE int TextProperties::
 set_draw_order(int draw_order) {
   _draw_order = draw_order;
   _specified |= F_has_draw_order;
+  _text_state.clear();
+  _shadow_state.clear();
   return _draw_order + 3;
 }
 
@@ -780,6 +790,8 @@ INLINE void TextProperties::
 clear_draw_order() {
   _draw_order = 1;
   _specified &= ~F_has_draw_order;
+  _text_state.clear();
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 64 - 0
panda/src/text/textProperties.cxx

@@ -19,6 +19,9 @@
 #include "staticTextFont.h"
 #include "bamFile.h"
 #include "fontPool.h"
+#include "colorAttrib.h"
+#include "cullBinAttrib.h"
+#include "transparencyAttrib.h"
 
 PT(TextFont) TextProperties::_default_font;
 bool TextProperties::_loaded_default_font = false;
@@ -61,6 +64,8 @@ TextProperties() {
 TextProperties::
 TextProperties(const TextProperties &copy) {
   (*this) = copy;
+  _text_state = copy._text_state;
+  _shadow_state = copy._shadow_state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -91,6 +96,9 @@ operator = (const TextProperties &copy) {
   _glyph_scale = copy._glyph_scale;
   _glyph_shift = copy._glyph_shift;
   _text_scale = copy._text_scale;
+
+  _text_state.clear();
+  _shadow_state.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -372,6 +380,62 @@ write(ostream &out, int indent_level) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextProperties::get_text_state
+//       Access: Public
+//  Description: Returns a RenderState object suitable for rendering
+//               text with these properties.
+////////////////////////////////////////////////////////////////////
+const RenderState *TextProperties::
+get_text_state() const {
+  if (!_text_state.is_null()) {
+    return _text_state;
+  }
+
+  CPT(RenderState) state = RenderState::make_empty();
+
+  if (has_text_color()) {
+    state = state->add_attrib(ColorAttrib::make_flat(get_text_color()));
+    if (get_text_color()[3] != 1.0) {
+      state = state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    }
+  }
+
+  if (has_bin()) {
+    state = state->add_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 2));
+  }
+
+  swap(_text_state, state);
+  return _text_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextProperties::get_shadow_state
+//       Access: Public
+//  Description: Returns a RenderState object suitable for rendering
+//               the shadow of this text with these properties.
+////////////////////////////////////////////////////////////////////
+const RenderState *TextProperties::
+get_shadow_state() const {
+  if (!_shadow_state.is_null()) {
+    return _shadow_state;
+  }
+
+  CPT(RenderState) state = RenderState::make_empty();
+
+  state = state->add_attrib(ColorAttrib::make_flat(get_shadow_color()));
+  if (get_shadow_color()[3] != 1.0) {
+    state = state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+  }
+
+  if (has_bin()) {
+    state = state->add_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 1));
+  }
+
+  swap(_shadow_state, state);
+  return _shadow_state;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TextProperties::load_default_font
 //       Access: Private, Static

+ 8 - 0
panda/src/text/textProperties.h

@@ -21,6 +21,7 @@
 #include "luse.h"
 #include "textFont.h"
 #include "pointerTo.h"
+#include "renderState.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : TextProperties
@@ -169,6 +170,10 @@ PUBLISHED:
 
   void write(ostream &out, int indent_level = 0) const;
 
+public:
+  const RenderState *get_text_state() const;
+  const RenderState *get_shadow_state() const;
+
 private:
   static void load_default_font();
 
@@ -216,6 +221,9 @@ private:
   PN_stdfloat _glyph_shift;
   PN_stdfloat _text_scale;
 
+  mutable CPT(RenderState) _text_state;
+  mutable CPT(RenderState) _shadow_state;
+
   static PT(TextFont) _default_font;
   static bool _loaded_default_font;