浏览代码

Merge branch 'master' into deploy-ng

Mitchell Stokes 8 年之前
父节点
当前提交
e13ad8e146

+ 94 - 0
direct/src/actor/Actor.py

@@ -50,6 +50,10 @@ class Actor(DirectObject, NodePath):
         def __repr__(self):
             return 'Actor.PartDef(%s, %s)' % (repr(self.partBundleNP), repr(self.partModel))
 
+
+        #snake_case alias:
+        get_bundle = getBundle
+
     class AnimDef:
 
         """Instances of this class are stored within the
@@ -72,6 +76,10 @@ class Actor(DirectObject, NodePath):
         def __repr__(self):
             return 'Actor.AnimDef(%s)' % (repr(self.filename))
 
+
+        #snake_case alias:
+        make_copy = makeCopy
+
     class SubpartDef:
 
         """Instances of this class are stored within the SubpartDict
@@ -2549,3 +2557,89 @@ class Actor(DirectObject, NodePath):
         for partBundleDict in self.__partBundleDict.values():
             partDef = partBundleDict.get(subpartDef.truePartName)
             partDef.getBundle().setName(newBundleName)
+
+    #snake_case alias:
+    control_joint = controlJoint
+    set_lod_animation = setLODAnimation
+    get_anim_control_dict = getAnimControlDict
+    get_actor_info = getActorInfo
+    clear_lod_animation = clearLODAnimation
+    reset_lod = resetLOD
+    fix_bounds = fixBounds
+    get_anim_filename = getAnimFilename
+    get_subparts_complete = getSubpartsComplete
+    verify_subparts_complete = verifySubpartsComplete
+    get_play_rate = getPlayRate
+    clear_python_data = clearPythonData
+    load_anims = loadAnims
+    set_subparts_complete = setSubpartsComplete
+    draw_in_front = drawInFront
+    get_lod_node = getLODNode
+    hide_part = hidePart
+    get_joint_transform_state = getJointTransformState
+    set_control_effect = setControlEffect
+    get_anim_controls = getAnimControls
+    release_joint = releaseJoint
+    print_anim_blends = printAnimBlends
+    get_lod = getLOD
+    disable_blend = disableBlend
+    show_part = showPart
+    get_joint_transform = getJointTransform
+    face_away_from_viewer = faceAwayFromViewer
+    set_lod = setLOD
+    osd_anim_blends = osdAnimBlends
+    get_current_frame = getCurrentFrame
+    set_play_rate = setPlayRate
+    bind_all_anims = bindAllAnims
+    unload_anims = unloadAnims
+    remove_part = removePart
+    use_lod = useLOD
+    get_anim_blends = getAnimBlends
+    get_lod_index = getLODIndex
+    get_num_frames = getNumFrames
+    post_flatten = postFlatten
+    get_lod_names = getLODNames
+    list_joints = listJoints
+    make_subpart = makeSubpart
+    get_anim_control = getAnimControl
+    get_part_bundle = getPartBundle
+    get_part_bundle_dict = getPartBundleDict
+    get_duration = getDuration
+    has_lod = hasLOD
+    print_lod = printLOD
+    fix_bounds_old = fixBounds_old
+    get_anim_names = getAnimNames
+    get_part_bundles = getPartBundles
+    anim_panel = animPanel
+    stop_joint = stopJoint
+    actor_interval = actorInterval
+    hide_all_bounds = hideAllBounds
+    show_all_bounds = showAllBounds
+    init_anims_on_all_lods = initAnimsOnAllLODs
+    get_part = getPart
+    add_lod = addLOD
+    show_all_parts = showAllParts
+    get_joints = getJoints
+    get_overlapping_joints = getOverlappingJoints
+    enable_blend = enableBlend
+    face_towards_viewer = faceTowardsViewer
+    bind_anim = bindAnim
+    set_blend = setBlend
+    get_frame_time = getFrameTime
+    remove_node = removeNode
+    wait_pending = waitPending
+    expose_joint = exposeJoint
+    set_lod_node = setLODNode
+    get_frame_rate = getFrameRate
+    get_current_anim = getCurrentAnim
+    get_part_names = getPartNames
+    freeze_joint = freezeJoint
+    set_center = setCenter
+    rename_part_bundles = renamePartBundles
+    get_geom_node = getGeomNode
+    set_geom_node = setGeomNode
+    load_model = loadModel
+    copy_actor = copyActor
+    get_base_frame_rate = getBaseFrameRate
+    remove_anim_control_dict = removeAnimControlDict
+    load_anims_on_all_lods = loadAnimsOnAllLODs

+ 13 - 0
direct/src/controls/InputState.py

@@ -21,6 +21,9 @@ class InputStateToken:
     def __hash__(self):
         return self._hash
 
+    #snake_case alias:
+    is_valid = isValid
+
 class InputStateWatchToken(InputStateToken, DirectObject.DirectObject):
     def release(self):
         self._inputState._ignore(self)
@@ -39,6 +42,9 @@ class InputStateTokenGroup:
             token.release()
         self._tokens = []
 
+    #snake_case alias:
+    add_token = addToken
+
 class InputState(DirectObject.DirectObject):
     """
     InputState is for tracking the on/off state of some events.
@@ -235,3 +241,10 @@ class InputState(DirectObject.DirectObject):
         """for debugging"""
         return self.notify.debug(
             "%s (%s) %s"%(id(self), len(self._state), message))
+
+    #snake_case alias:
+    watch_with_modifiers = watchWithModifiers
+    is_set = isSet
+    get_event_name = getEventName
+    debug_print = debugPrint
+    release_inputs = releaseInputs

+ 21 - 0
direct/src/filter/CommonFilters.py

@@ -537,3 +537,24 @@ class CommonFilters:
             del self.configuration["GammaAdjust"]
             return self.reconfigure((old_gamma != 1.0), "GammaAdjust")
         return True
+
+    #snake_case alias:
+    del_cartoon_ink = delCartoonInk
+    set_half_pixel_shift = setHalfPixelShift
+    del_half_pixel_shift = delHalfPixelShift
+    set_inverted = setInverted
+    del_inverted = delInverted
+    del_view_glow = delViewGlow
+    set_volumetric_lighting = setVolumetricLighting
+    del_gamma_adjust = delGammaAdjust
+    set_bloom = setBloom
+    set_view_glow = setViewGlow
+    set_ambient_occlusion = setAmbientOcclusion
+    set_cartoon_ink = setCartoonInk
+    del_bloom = delBloom
+    del_ambient_occlusion = delAmbientOcclusion
+    load_shader = loadShader
+    set_blur_sharpen = setBlurSharpen
+    del_blur_sharpen = delBlurSharpen
+    del_volumetric_lighting = delVolumetricLighting
+    set_gamma_adjust = setGammaAdjust

+ 12 - 0
direct/src/filter/FilterManager.py

@@ -349,3 +349,15 @@ class FilterManager(DirectObject):
         self.nextsort = self.win.getSort() - 1000
         self.basex = 0
         self.basey = 0
+
+    #snake_case alias:
+    is_fullscreen = isFullscreen
+    resize_buffers = resizeBuffers
+    set_stacked_clears = setStackedClears
+    render_scene_into = renderSceneInto
+    get_scaled_size = getScaledSize
+    render_quad_into = renderQuadInto
+    get_clears = getClears
+    set_clears = setClears
+    create_buffer = createBuffer
+    window_event = windowEvent

+ 3 - 2
direct/src/filter/filterBloomX.py

@@ -13,8 +13,9 @@ void vshader(float4 vtx_position : POSITION,
   l_position=mul(mat_modelproj, vtx_position);
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float offset = texpix_src.x;
-  l_texcoord0 = float4(c.x-offset* -4, c.x-offset* -3, c.x-offset* -2, c.y);
-  l_texcoord1 = float4(c.x-offset* -1, c.x-offset*  0, c.x-offset*  1, c.y);
+  float pad = texpad_src.x * 2;
+  l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y);
+  l_texcoord1 = float4(min(c.x-offset* -1, pad), c.x-offset*  0, c.x-offset*  1, c.y);
   l_texcoord2 = float4(c.x-offset*  2, c.x-offset*  3, c.x-offset*  4, c.y);
 }
 

+ 3 - 2
direct/src/filter/filterBloomY.py

@@ -13,8 +13,9 @@ void vshader(float4 vtx_position : POSITION,
   l_position=mul(mat_modelproj, vtx_position);
   float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
   float offset = texpix_src.y;
-  l_texcoord0 = float4(c.y-offset* -4, c.y-offset* -3, c.y-offset* -2, c.x);
-  l_texcoord1 = float4(c.y-offset* -1, c.y-offset*  0, c.y-offset*  1, c.x);
+  float pad = texpad_src.y * 2;
+  l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x);
+  l_texcoord1 = float4(min(c.y-offset* -1, pad), c.y-offset*  0, c.y-offset*  1, c.x);
   l_texcoord2 = float4(c.y-offset*  2, c.y-offset*  3, c.y-offset*  4, c.x);
 }
 

+ 5 - 3
direct/src/filter/filterBlurX.py

@@ -18,16 +18,18 @@ void vshader(float4 vtx_position : POSITION,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
              out float4 o_color : COLOR,
              uniform float2 texpix_src,
+             uniform float4 texpad_src,
              uniform sampler2D k_src : TEXUNIT0)
 {
+  float pad = texpad_src.x * 2;
   float3 offset = float3(1.0*texpix_src.x, 2.0*texpix_src.x, 3.0*texpix_src.x);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.z, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.y, l_texcoord0.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x - offset.x, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.x, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.y, l_texcoord0.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x + offset.z, l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.x, pad), l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.y, pad), l_texcoord0.y));
+  o_color += tex2D(k_src, float2(min(l_texcoord0.x + offset.z, pad), l_texcoord0.y));
   o_color /= 7;
   o_color.w = 1;
 }

+ 5 - 3
direct/src/filter/filterBlurY.py

@@ -18,16 +18,18 @@ void vshader(float4 vtx_position : POSITION,
 void fshader(float2 l_texcoord0 : TEXCOORD0,
              out float4 o_color : COLOR,
              uniform float2 texpix_src,
+             uniform float4 texpad_src,
              uniform sampler2D k_src : TEXUNIT0)
 {
+  float pad = texpad_src.y * 2;
   float3 offset = float3(1.0*texpix_src.y, 2.0*texpix_src.y, 3.0*texpix_src.y);
   o_color  = tex2D(k_src, l_texcoord0);
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.z));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.y));
   o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y - offset.x));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.x));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.y));
-  o_color += tex2D(k_src, float2(l_texcoord0.x, l_texcoord0.y + offset.z));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.x, pad)));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.y, pad)));
+  o_color += tex2D(k_src, float2(l_texcoord0.x, min(l_texcoord0.y + offset.z, pad)));
   o_color /= 7;
   o_color.w = 1;
 }

+ 24 - 0
direct/src/showbase/Audio3DManager.py

@@ -289,3 +289,27 @@ class Audio3DManager:
             for sound in self.sound_dict[object]:
                 self.detachSound(sound)
 
+    #snake_case alias:
+    get_doppler_factor = getDopplerFactor
+    set_listener_velocity_auto = setListenerVelocityAuto
+    attach_listener = attachListener
+    set_distance_factor = setDistanceFactor
+    attach_sound_to_object = attachSoundToObject
+    get_drop_off_factor = getDropOffFactor
+    set_doppler_factor = setDopplerFactor
+    get_sounds_on_object = getSoundsOnObject
+    set_sound_velocity_auto = setSoundVelocityAuto
+    get_sound_max_distance = getSoundMaxDistance
+    load_sfx = loadSfx
+    get_distance_factor = getDistanceFactor
+    set_listener_velocity = setListenerVelocity
+    set_sound_max_distance = setSoundMaxDistance
+    get_sound_velocity = getSoundVelocity
+    get_listener_velocity = getListenerVelocity
+    set_sound_velocity = setSoundVelocity
+    set_sound_min_distance = setSoundMinDistance
+    get_sound_min_distance = getSoundMinDistance
+    detach_listener = detachListener
+    set_drop_off_factor = setDropOffFactor
+    detach_sound = detachSound
+

+ 12 - 0
direct/src/showbase/DirectObject.py

@@ -101,3 +101,15 @@ class DirectObject:
             func = choice(getRepository()._crashOnProactiveLeakDetect,
                           self.notify.error, self.notify.warning)
             func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr))
+
+    #snake_case alias:
+    add_task = addTask
+    do_method_later = doMethodLater
+    detect_leaks = detectLeaks
+    accept_once = acceptOnce
+    ignore_all = ignoreAll
+    get_all_accepting = getAllAccepting
+    is_ignoring = isIgnoring
+    remove_all_tasks = removeAllTasks
+    remove_task = removeTask
+    is_accepting = isAccepting

+ 13 - 0
direct/src/showbase/Messenger.py

@@ -635,3 +635,16 @@ class Messenger:
         str = str + '='*50 + '\n'
         return str
 
+    #snake_case alias:
+    get_events = getEvents
+    is_ignoring = isIgnoring
+    who_accepts = whoAccepts
+    find_all = findAll
+    replace_method = replaceMethod
+    ignore_all = ignoreAll
+    is_accepting = isAccepting
+    is_empty = isEmpty
+    detailed_repr = detailedRepr
+    get_all_accepting = getAllAccepting
+    toggle_verbose = toggleVerbose
+

+ 9 - 6
makepanda/makepanda.py

@@ -835,13 +835,16 @@ if (COMPILER=="GCC"):
                 # Needed when linking ffmpeg statically on Linux.
                 LibName("FFMPEG", "-Wl,-Bsymbolic")
 
-        cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
-        if cv_lib == "opencv_core":
-            OPENCV_VER_23 = True
-            SmartPkgEnable("OPENCV", "opencv",   ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp"))
+        if PkgSkip("FFMPEG") or GetTarget() == "darwin":
+            cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
+            if cv_lib == "opencv_core":
+                OPENCV_VER_23 = True
+                SmartPkgEnable("OPENCV", "opencv",   ("opencv_core", "opencv_highgui"), ("opencv2/core/core.hpp"))
+            else:
+                SmartPkgEnable("OPENCV", "opencv",   ("cv", "highgui", "cvaux", "ml", "cxcore"),
+                               ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h"))
         else:
-            SmartPkgEnable("OPENCV", "opencv",   ("cv", "highgui", "cvaux", "ml", "cxcore"),
-                           ("opencv", "opencv/cv.h", "opencv/cxcore.h", "opencv/highgui.h"))
+            PkgDisable("OPENCV")
 
         rocket_libs = ("RocketCore", "RocketControls")
         if (GetOptimize() <= 3):

+ 0 - 10
panda/src/display/graphicsEngine.cxx

@@ -1849,16 +1849,6 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
   CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform);
   scene_setup->set_cs_world_transform(cs_world_transform);
 
-  // Make sure that the GSG has a ShaderGenerator for the munger to use.  We
-  // have to do this here because the ShaderGenerator needs a host window
-  // pointer.  Hopefully we'll be able to eliminate that requirement in the
-  // future.
-#ifdef HAVE_CG
-  if (gsg->get_shader_generator() == NULL) {
-    gsg->set_shader_generator(new ShaderGenerator(gsg, window));
-  }
-#endif
-
   return scene_setup;
 }
 

+ 5 - 5
panda/src/display/standardMunger.cxx

@@ -344,11 +344,11 @@ munge_state_impl(const RenderState *state) {
 #ifdef HAVE_CG
   if (_auto_shader) {
     CPT(RenderState) shader_state = munged_state->get_auto_shader_state();
-    ShaderGenerator *shader_generator = get_gsg()->get_shader_generator();
-    if (shader_generator == NULL) {
-      pgraph_cat.error()
-        << "auto_shader enabled, but GSG has no shader generator assigned!\n";
-      return munged_state;
+    GraphicsStateGuardian *gsg = get_gsg();
+    ShaderGenerator *shader_generator = gsg->get_shader_generator();
+    if (shader_generator == nullptr) {
+      shader_generator = new ShaderGenerator(gsg);
+      gsg->set_shader_generator(shader_generator);
     }
     if (shader_state->_generated_shader == NULL) {
       // Cache the generated ShaderAttrib on the shader state.

+ 2 - 2
panda/src/express/virtualFile.h

@@ -56,12 +56,12 @@ PUBLISHED:
   BLOCKING void ls(ostream &out = cout) const;
   BLOCKING void ls_all(ostream &out = cout) const;
 
-  EXTENSION(BLOCKING PyObject *read_file(bool auto_unwrap) const);
+  EXTENSION(PyObject *read_file(bool auto_unwrap) const);
   BLOCKING virtual istream *open_read_file(bool auto_unwrap) const;
   BLOCKING virtual void close_read_file(istream *stream) const;
   virtual bool was_read_successful() const;
 
-  EXTENSION(BLOCKING PyObject *write_file(PyObject *data, bool auto_wrap));
+  EXTENSION(PyObject *write_file(PyObject *data, bool auto_wrap));
   BLOCKING virtual ostream *open_write_file(bool auto_wrap, bool truncate);
   BLOCKING virtual ostream *open_append_file();
   BLOCKING virtual void close_write_file(ostream *stream);

+ 2 - 2
panda/src/express/virtualFileSystem.h

@@ -95,11 +95,11 @@ PUBLISHED:
 
   static VirtualFileSystem *get_global_ptr();
 
-  EXTENSION(BLOCKING PyObject *read_file(const Filename &filename, bool auto_unwrap) const);
+  EXTENSION(PyObject *read_file(const Filename &filename, bool auto_unwrap) const);
   BLOCKING istream *open_read_file(const Filename &filename, bool auto_unwrap) const;
   BLOCKING static void close_read_file(istream *stream);
 
-  EXTENSION(BLOCKING PyObject *write_file(const Filename &filename, PyObject *data, bool auto_wrap));
+  EXTENSION(PyObject *write_file(const Filename &filename, PyObject *data, bool auto_wrap));
   BLOCKING ostream *open_write_file(const Filename &filename, bool auto_wrap, bool truncate);
   BLOCKING ostream *open_append_file(const Filename &filename);
   BLOCKING static void close_write_file(ostream *stream);

+ 14 - 1
panda/src/express/virtualFileSystem_ext.cxx

@@ -26,9 +26,22 @@
  */
 PyObject *Extension<VirtualFileSystem>::
 read_file(const Filename &filename, bool auto_unwrap) const {
+  // Release the GIL while we do this potentially slow operation.
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyThreadState *_save;
+  Py_UNBLOCK_THREADS
+#endif
+
   vector_uchar pv;
   bool okflag = _this->read_file(filename, pv, auto_unwrap);
-  nassertr(okflag, NULL);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  Py_BLOCK_THREADS
+#endif
+
+  if (!okflag) {
+    return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", filename.c_str());
+  }
 
 #if PY_MAJOR_VERSION >= 3
   if (pv.empty()) {

+ 26 - 1
panda/src/express/virtualFile_ext.cxx

@@ -26,9 +26,23 @@
  */
 PyObject *Extension<VirtualFile>::
 read_file(bool auto_unwrap) const {
+  // Release the GIL while we do this potentially slow operation.
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyThreadState *_save;
+  Py_UNBLOCK_THREADS
+#endif
+
   vector_uchar pv;
   bool okflag = _this->read_file(pv, auto_unwrap);
-  nassertr(okflag, NULL);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  Py_BLOCK_THREADS
+#endif
+
+  if (!okflag) {
+    Filename fn = _this->get_filename();
+    return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", fn.c_str());
+  }
 
 #if PY_MAJOR_VERSION >= 3
   if (pv.empty()) {
@@ -68,7 +82,18 @@ write_file(PyObject *data, bool auto_wrap) {
   }
 #endif
 
+  // Release the GIL while we do this potentially slow operation.
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyThreadState *_save;
+  Py_UNBLOCK_THREADS
+#endif
+
   bool result = _this->write_file((const unsigned char *)buffer, length, auto_wrap);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  Py_BLOCK_THREADS
+#endif
+
   return PyBool_FromLong(result);
 }
 

+ 17 - 13
panda/src/gobj/material.h

@@ -127,19 +127,7 @@ PUBLISHED:
   MAKE_PROPERTY(local, get_local, set_local);
   MAKE_PROPERTY(twoside, get_twoside, set_twoside);
 
-private:
-  LColor _base_color;
-  LColor _ambient;
-  LColor _diffuse;
-  LColor _specular;
-  LColor _emission;
-  PN_stdfloat _shininess;
-  PN_stdfloat _roughness;
-  PN_stdfloat _metallic;
-  PN_stdfloat _refractive_index;
-
-  static PT(Material) _default;
-
+public:
   enum Flags {
     F_ambient     = 0x001,
     F_diffuse     = 0x002,
@@ -153,8 +141,24 @@ private:
     F_base_color  = 0x200,
     F_refractive_index = 0x400,
   };
+
+private:
+  LColor _base_color;
+  LColor _ambient;
+  LColor _diffuse;
+  LColor _specular;
+  LColor _emission;
+  PN_stdfloat _shininess;
+  PN_stdfloat _roughness;
+  PN_stdfloat _metallic;
+  PN_stdfloat _refractive_index;
+
+  static PT(Material) _default;
+
   int _flags;
 
+  friend class MaterialAttrib;
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);

+ 3 - 1
panda/src/gobj/shader.cxx

@@ -428,7 +428,9 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_view_to_apiclip_x)) {
     dep |= SSD_shaderinputs;
 
-    if ((inp == SMO_alight_x) ||
+    if ((inp == SMO_texpad_x) ||
+        (inp == SMO_texpix_x) ||
+        (inp == SMO_alight_x) ||
         (inp == SMO_dlight_x) ||
         (inp == SMO_plight_x) ||
         (inp == SMO_slight_x) ||

+ 10 - 1
panda/src/pgraph/alphaTestAttrib.cxx

@@ -18,6 +18,7 @@
 #include "bamWriter.h"
 #include "datagram.h"
 #include "datagramIterator.h"
+#include "auxBitplaneAttrib.h"
 
 TypeHandle AlphaTestAttrib::_type_handle;
 int AlphaTestAttrib::_attrib_slot;
@@ -98,7 +99,15 @@ get_hash_impl() const {
  */
 CPT(RenderAttrib) AlphaTestAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
-  return this;
+  // This is only important if the shader subsumes the alpha test, which only
+  // happens if there is an AuxBitplaneAttrib with ABO_glow.
+  const AuxBitplaneAttrib *aux;
+  if (!state->get_attrib(aux) ||
+      (aux->get_outputs() & AuxBitplaneAttrib::ABO_glow) == 0) {
+    return nullptr;
+  } else {
+    return this;
+  }
 }
 
 /**

+ 13 - 13
panda/src/pgraph/geomNode.cxx

@@ -15,6 +15,7 @@
 #include "geom.h"
 #include "geomTransformer.h"
 #include "sceneGraphReducer.h"
+#include "stateMunger.h"
 #include "accumulatedAttribs.h"
 #include "colorAttrib.h"
 #include "colorScaleAttrib.h"
@@ -391,33 +392,32 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
       ((GeomPrimitive *)prim.p())->prepare(prepared_objects);
     }
 
+    if (munger->is_of_type(StateMunger::get_class_type())) {
+      StateMunger *state_munger = (StateMunger *)munger.p();
+      geom_state = state_munger->munge_state(geom_state);
+    }
+
     // And now prepare each of the textures.
-    const RenderAttrib *attrib =
-      geom_state->get_attrib(TextureAttrib::get_class_slot());
-    if (attrib != (const RenderAttrib *)NULL) {
-      const TextureAttrib *ta;
-      DCAST_INTO_V(ta, attrib);
+    const TextureAttrib *ta;
+    if (geom_state->get_attrib(ta)) {
       int num_stages = ta->get_num_on_stages();
       for (int i = 0; i < num_stages; ++i) {
         Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
         // TODO: prepare the sampler states, if specified.
-        if (texture != (Texture *)NULL) {
+        if (texture != nullptr) {
           texture->prepare(prepared_objects);
         }
       }
     }
 
     // As well as the shaders.
-    attrib = geom_state->get_attrib(ShaderAttrib::get_class_slot());
-    if (attrib != (const RenderAttrib *)NULL) {
-      const ShaderAttrib *sa;
-      DCAST_INTO_V(sa, attrib);
+    const ShaderAttrib *sa;
+    if (geom_state->get_attrib(sa)) {
       Shader *shader = (Shader *)sa->get_shader();
-      if (shader != (Shader *)NULL) {
+      if (shader != nullptr) {
         shader->prepare(prepared_objects);
       }
-      // TODO: prepare the shader inputs.  TODO: Invoke the shader generator
-      // if enabled.
+      // TODO: prepare the shader inputs.
     }
   }
 

+ 10 - 2
panda/src/pgraph/materialAttrib.I

@@ -15,7 +15,7 @@
  * Use MaterialAttrib::make() to construct a new MaterialAttrib object.
  */
 INLINE MaterialAttrib::
-MaterialAttrib() {
+MaterialAttrib() : _flags(0) {
 }
 
 /**
@@ -24,7 +24,7 @@ MaterialAttrib() {
  */
 INLINE bool MaterialAttrib::
 is_off() const {
-  return _material == (const Material *)NULL;
+  return _flags == 0;
 }
 
 /**
@@ -35,3 +35,11 @@ INLINE Material *MaterialAttrib::
 get_material() const {
   return _material;
 }
+
+/**
+ * Equivalent to get_material()->get_flags().
+ */
+INLINE int MaterialAttrib::
+get_material_flags() const {
+  return _flags;
+}

+ 44 - 5
panda/src/pgraph/materialAttrib.cxx

@@ -30,6 +30,8 @@ make(Material *material) {
   MaterialAttrib *attrib = new MaterialAttrib;
   attrib->_material = material;
   material->set_attrib_lock();
+  attrib->_flags = material->_flags;
+  nassertr(attrib->_flags & Material::F_attrib_lock, nullptr);
   return return_new(attrib);
 }
 
@@ -58,10 +60,34 @@ make_default() {
 void MaterialAttrib::
 output(ostream &out) const {
   out << get_type() << ":";
-  if (is_off()) {
+  if (_material != nullptr) {
+    out << *_material;
+  } else if (is_off()) {
     out << "(off)";
   } else {
-    out << *_material;
+    // This is a state returned from get_auto_shader_attrib().
+    out << "(on";
+#ifndef NDEBUG
+    if (_flags & Material::F_ambient) {
+      out << " amb";
+    }
+    if (_flags & Material::F_diffuse) {
+      out << " diff";
+    }
+    if (_flags & Material::F_specular) {
+      out << " spec";
+    }
+    if (_flags & Material::F_emission) {
+      out << " emit";
+    }
+    if (_flags & Material::F_local) {
+      out << " local";
+    }
+    if (_flags & Material::F_twoside) {
+      out << " twoside";
+    }
+#endif
+    out << ")";
   }
 }
 
@@ -87,7 +113,7 @@ compare_to_impl(const RenderAttrib *other) const {
   if (_material != ta->_material) {
     return _material < ta->_material ? -1 : 1;
   }
-  return 0;
+  return _flags < ta->_flags;
 }
 
 /**
@@ -100,6 +126,7 @@ size_t MaterialAttrib::
 get_hash_impl() const {
   size_t hash = 0;
   hash = pointer_hash::add_hash(hash, _material);
+  hash = int_hash::add_hash(hash, _flags);
   return hash;
 }
 
@@ -108,7 +135,15 @@ get_hash_impl() const {
  */
 CPT(RenderAttrib) MaterialAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
-  return this;
+  if (_material == nullptr) {
+    return this;
+  } else {
+    // Make a copy, but only with the flags, not with the material itself.
+    MaterialAttrib *attrib = new MaterialAttrib();
+    attrib->_material = nullptr;
+    attrib->_flags = _flags;
+    return return_new(attrib);
+  }
 }
 
 /**
@@ -139,8 +174,12 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
 
   TypedWritable *material = p_list[pi++];
-  if (material != (TypedWritable *)NULL) {
+  if (material != nullptr) {
     _material = DCAST(Material, material);
+    _flags = _material->_flags | Material::F_attrib_lock;
+  } else {
+    _material = nullptr;
+    _flags = 0;
   }
 
   return pi;

+ 4 - 0
panda/src/pgraph/materialAttrib.h

@@ -36,6 +36,9 @@ PUBLISHED:
   INLINE bool is_off() const;
   INLINE Material *get_material() const;
 
+public:
+  INLINE int get_material_flags() const;
+
 PUBLISHED:
   MAKE_PROPERTY(material, get_material);
 
@@ -49,6 +52,7 @@ protected:
 
 private:
   PT(Material) _material;
+  int _flags;
 
 PUBLISHED:
   static int get_class_slot() {

+ 5 - 2
panda/src/pgraph/rescaleNormalAttrib.cxx

@@ -86,11 +86,14 @@ get_hash_impl() const {
 CPT(RenderAttrib) RescaleNormalAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
   // We currently only support M_normalize in the ShaderGenerator.
-  if (_mode == M_none || _mode == M_normalize) {
+  /*if (_mode == M_none || _mode == M_normalize) {
     return this;
   } else {
     return RescaleNormalAttrib::make(M_normalize);
-  }
+  }*/
+  // Actually, we currently ignore this attribute in the shader generator,
+  // and always normalize the normals.  It's too much of a bother.
+  return nullptr;
 }
 
 /**

+ 5 - 4
panda/src/pgraph/shaderAttrib.cxx

@@ -523,14 +523,15 @@ void ShaderAttrib::
 output(ostream &out) const {
   out << "ShaderAttrib:";
 
-  if (_has_shader) {
-    if (_shader == NULL) {
+  if (_auto_shader) {
+    out << "auto";
+    return;
+  } else if (_has_shader) {
+    if (_shader == nullptr) {
       out << "off";
     } else {
       out << _shader->get_filename().get_basename();
     }
-  } else if (_auto_shader) {
-    out << "auto";
   }
 
   out << "," << _inputs.size() << " inputs";

+ 41 - 1
panda/src/pgraph/textureAttrib.I

@@ -140,6 +140,26 @@ get_on_texture(TextureStage *stage) const {
   return NULL;
 }
 
+/**
+ * Returns the type of the texture associated with the indicated stage.  It is
+ * an error to call this if has_on_stage(stage) returns false.
+ *
+ * This method is no different than get_on_texture(stage)->get_texture_type();
+ * it merely exists to address a corner case for the shader generator.
+ */
+INLINE Texture::TextureType TextureAttrib::
+get_on_texture_type(TextureStage *stage) const {
+  Stages::const_iterator si;
+  si = _on_stages.find(StageNode(stage));
+  nassertr(si != _on_stages.end(), Texture::TT_2d_texture);
+
+  if ((*si)._texture == nullptr) {
+    return (*si)._texture_type;
+  } else {
+    return (*si)._texture->get_texture_type();
+  }
+}
+
 /**
  * Returns the sampler associated with the indicated stage, or the one
  * associated with its texture if no custom stage has been specified.  It is
@@ -215,6 +235,26 @@ is_identity() const {
   return _on_stages.empty() && _off_stages.empty() && !_off_all_stages;
 }
 
+/**
+ * Call only on a state returned by get_auto_shader_attrib.
+ */
+bool TextureAttrib::
+on_stage_affects_rgb(size_t n) const {
+  nassertr(n < _render_stages.size(), false);
+  StageNode *stage = _render_stages[n];
+  return stage->_texture_affects_rgb;
+}
+
+/**
+ * Call only on a state returned by get_auto_shader_attrib.
+ */
+bool TextureAttrib::
+on_stage_affects_alpha(size_t n) const {
+  nassertr(n < _render_stages.size(), false);
+  StageNode *stage = _render_stages[n];
+  return stage->_texture_affects_alpha;
+}
+
 /**
  * Confirms whether the _on_stages list is still sorted.  It will become
  * unsorted if someone calls TextureStage::set_sort().
@@ -223,7 +263,7 @@ is_identity() const {
  */
 INLINE void TextureAttrib::
 check_sorted() const {
-  if (_sort_seq != TextureStage::get_sort_seq()) {
+  if (_sort_seq != TextureStage::get_sort_seq() && !_sort_seq.is_fresh()) {
     ((TextureAttrib *)this)->sort_on_stages();
   }
 }

+ 74 - 3
panda/src/pgraph/textureAttrib.cxx

@@ -106,6 +106,8 @@ find_on_stage(const TextureStage *stage) const {
  */
 CPT(RenderAttrib) TextureAttrib::
 add_on_stage(TextureStage *stage, Texture *tex, int override) const {
+  nassertr(tex != nullptr, this);
+
   TextureAttrib *attrib = new TextureAttrib(*this);
   Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first;
   (*si)._override = override;
@@ -127,6 +129,8 @@ add_on_stage(TextureStage *stage, Texture *tex, int override) const {
  */
 CPT(RenderAttrib) TextureAttrib::
 add_on_stage(TextureStage *stage, Texture *tex, const SamplerState &sampler, int override) const {
+  nassertr(tex != nullptr, this);
+
   TextureAttrib *attrib = new TextureAttrib(*this);
   Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first;
   (*si)._override = override;
@@ -381,10 +385,19 @@ output(ostream &out) const {
     const StageNode &sn = *(*ri);
     TextureStage *stage = sn._stage;
     Texture *tex = sn._texture;
-    if (tex != NULL) {
+    if (tex != nullptr) {
       out << " " << stage->get_name() << ":" << tex->get_name();
     } else {
-      out << " " << stage->get_name();
+      out << " " << stage->get_name() << ":(" << sn._texture_type;
+      if (sn._texture_affects_rgb) {
+        out << " rgb";
+        if (sn._texture_affects_alpha) {
+          out << "a";
+        }
+      } else if (sn._texture_affects_alpha) {
+        out << " alpha";
+      }
+      out << ")";
     }
     if (sn._override != 0) {
       out << "^" << sn._override;
@@ -500,6 +513,19 @@ compare_to_impl(const RenderAttrib *other) const {
       }
     }
 
+    if (texture == nullptr) {
+      // This is an attribute returned by get_auto_shader_attrib_impl.
+      if ((*si)._texture_type != (*osi)._texture_type) {
+        return (*si)._texture_type < (*osi)._texture_type ? -1 : 1;
+      }
+      if ((*si)._texture_affects_rgb != (*osi)._texture_affects_rgb) {
+        return (*si)._texture_affects_rgb < (*osi)._texture_affects_rgb ? -1 : 1;
+      }
+      if ((*si)._texture_affects_alpha != (*osi)._texture_affects_alpha) {
+        return (*si)._texture_affects_alpha < (*osi)._texture_affects_alpha ? -1 : 1;
+      }
+    }
+
     ++si;
     ++osi;
   }
@@ -563,6 +589,13 @@ get_hash_impl() const {
     hash = pointer_hash::add_hash(hash, sn._texture);
     hash = int_hash::add_hash(hash, (int)sn._implicit_sort);
     hash = int_hash::add_hash(hash, sn._override);
+
+    if (sn._texture == nullptr) {
+      // This is an attribute returned by get_auto_shader_attrib_impl.
+      hash = int_hash::add_hash(hash, (int)sn._texture_type);
+      hash = int_hash::add_hash(hash, (int)sn._texture_affects_rgb);
+      hash = int_hash::add_hash(hash, (int)sn._texture_affects_alpha);
+    }
   }
 
   // This bool value goes here, between the two lists, to differentiate
@@ -739,7 +772,45 @@ invert_compose_impl(const RenderAttrib *other) const {
  */
 CPT(RenderAttrib) TextureAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
-  return this;
+  if (_on_stages.empty()) {
+    // Having no stages is the same as not applying a texture attribute.
+    return nullptr;
+  }
+
+  // We make a texture attribute that does not store the texture and sampler,
+  // so that we don't have to generate a shader for every possible texture.
+  // We do have to store the few texture properties that do affect the
+  // generated shader.
+  PT(TextureAttrib) attrib = new TextureAttrib;
+  attrib->_on_stages = _on_stages;
+  attrib->sort_on_stages();
+
+  Stages::iterator si;
+  for (si = attrib->_on_stages.begin(); si != attrib->_on_stages.end(); ++si) {
+    Texture::Format format = (*si)._texture->get_format();
+    (*si)._texture_type = (*si)._texture->get_texture_type();
+    (*si)._texture_affects_rgb = (format != Texture::F_alpha);
+    (*si)._texture_affects_alpha = Texture::has_alpha(format);
+    (*si)._texture = nullptr;
+    (*si)._has_sampler = false;
+
+    // We will no longer be composing or sorting this state so we can get rid
+    // of these values.
+    (*si)._override = 0;
+    (*si)._implicit_sort = 0;
+
+    // Exception to optimize a common case: if the first texture is an RGB
+    // texture, we don't care about whether it affects the alpha channel.
+    if (attrib->_render_stages[0] == &(*si) &&
+        (*si)._texture_affects_rgb && !(*si)._texture_affects_alpha) {
+      (*si)._texture_affects_alpha = true;
+    }
+  }
+
+  // Prevent check_sorted() from being called on this state.
+  attrib->_sort_seq = UpdateSeq::fresh();
+
+  return return_new(attrib);
 }
 
 /**

+ 8 - 0
panda/src/pgraph/textureAttrib.h

@@ -59,6 +59,7 @@ PUBLISHED:
   INLINE int get_ff_tc_index(int n) const;
   INLINE bool has_on_stage(TextureStage *stage) const;
   INLINE Texture *get_on_texture(TextureStage *stage) const;
+  INLINE Texture::TextureType get_on_texture_type(TextureStage *stage) const;
   INLINE const SamplerState &get_on_sampler(TextureStage *stage) const;
   INLINE int get_on_stage_override(TextureStage *stage) const;
 
@@ -81,6 +82,8 @@ PUBLISHED:
   CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const;
 
 public:
+  INLINE bool on_stage_affects_rgb(size_t n) const;
+  INLINE bool on_stage_affects_alpha(size_t n) const;
   CPT(TextureAttrib) filter_to_max(int max_texture_stages) const;
 
   virtual bool lower_attrib_can_override() const;
@@ -114,6 +117,11 @@ private:
     int _ff_tc_index;
     unsigned int _implicit_sort;
     int _override;
+
+    // These fields are used by the shader generator.
+    Texture::TextureType _texture_type : 16;
+    bool _texture_affects_rgb : 8;
+    bool _texture_affects_alpha : 8;
   };
 
   class CompareTextureStagePriorities {

+ 8 - 1
panda/src/pgraph/transparencyAttrib.cxx

@@ -114,7 +114,14 @@ get_hash_impl() const {
  */
 CPT(RenderAttrib) TransparencyAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
-  return this;
+  if (_mode == TransparencyAttrib::M_alpha) {
+    return this;
+  } else if (_mode == TransparencyAttrib::M_premultiplied_alpha ||
+             _mode == TransparencyAttrib::M_dual) {
+    return return_new(new TransparencyAttrib(M_alpha));
+  } else {
+    return nullptr;
+  }
 }
 
 /**

+ 47 - 75
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -55,8 +55,8 @@ TypeHandle ShaderGenerator::_type_handle;
  * shader generator belongs.
  */
 ShaderGenerator::
-ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host) :
-  _gsg(gsg), _host(host) {
+ShaderGenerator(GraphicsStateGuardianBase *gsg) :
+  _gsg(gsg) {
 
   // The ATTR# input semantics seem to map to generic vertex attributes in
   // both arbvp1 and glslv, which behave more consistently.  However, they
@@ -265,12 +265,7 @@ analyze_renderstate(const RenderState *rs) {
 
   const MaterialAttrib *material;
   rs->get_attrib_def(material);
-
-  if (!material->is_off()) {
-    _material = material->get_material();
-  } else {
-    _material = Material::get_default();
-  }
+  _material_flags = material->get_material_flags();
 
   // Break out the lights by type.
 
@@ -285,14 +280,7 @@ analyze_renderstate(const RenderState *rs) {
     nassertv(light_obj != (PandaNode *)NULL);
 
     if (light_obj->is_ambient_light()) {
-      if (_material->has_ambient()) {
-        LColor a = _material->get_ambient();
-        if ((a[0]!=0.0)||(a[1]!=0.0)||(a[2]!=0.0)) {
-          _have_ambient = true;
-        }
-      } else {
-        _have_ambient = true;
-      }
+      _have_ambient = true;
       _lighting = true;
 
     } else if (light_obj->is_of_type(LightLensNode::get_class_type())) {
@@ -357,37 +345,28 @@ analyze_renderstate(const RenderState *rs) {
   }
 
   // Determine whether we should normalize the normals.
-  const RescaleNormalAttrib *rescale;
+  /*const RescaleNormalAttrib *rescale;
   rs->get_attrib_def(rescale);
 
   _normalize_normals = (rescale->get_mode() != RescaleNormalAttrib::M_none);
+  */
+  // Actually, let's always normalize the normals for now.  This helps to
+  // reduce combinatoric explosion of shaders.
+  _normalize_normals = true;
 
   // Decide which material modes need to be calculated.
 
   if (_lighting) {
-    if (_material->has_diffuse()) {
-      LColor d = _material->get_diffuse();
-      if ((d[0]!=0.0)||(d[1]!=0.0)||(d[2]!=0.0)) {
-        _have_diffuse = true;
-      }
-    } else {
-      _have_diffuse = true;
-    }
+    _have_diffuse = true;
   }
 
-  if (_lighting && (_material->has_emission())) {
-    LColor e = _material->get_emission();
-    if ((e[0]!=0.0)||(e[1]!=0.0)||(e[2]!=0.0)) {
-      _have_emission = true;
-    }
+  if (_lighting && (_material_flags & Material::F_emission) != 0) {
+    _have_emission = true;
   }
 
   if (_lighting) {
-    if (_material->has_specular()) {
-      LColor s = _material->get_specular();
-      if ((s[0]!=0.0)||(s[1]!=0.0)||(s[2]!=0.0)) {
-        _have_specular = true;
-      }
+    if (_material_flags & Material::F_specular) {
+      _have_specular = true;
     } else if (_map_index_gloss >= 0) {
       _have_specular = true;
     }
@@ -398,14 +377,10 @@ analyze_renderstate(const RenderState *rs) {
   // Decide whether to separate ambient and diffuse calculations.
 
   if (_have_ambient && _have_diffuse) {
-    if (_material->has_ambient()) {
-      if (_material->has_diffuse()) {
-        _separate_ambient_diffuse = _material->get_ambient() != _material->get_diffuse();
-      } else {
-        _separate_ambient_diffuse = true;
-      }
+    if (_material_flags & Material::F_ambient) {
+      _separate_ambient_diffuse = true;
     } else {
-      if (_material->has_diffuse()) {
+      if (_material_flags & Material::F_diffuse) {
         _separate_ambient_diffuse = true;
       } else {
         _separate_ambient_diffuse = false;
@@ -427,10 +402,10 @@ analyze_renderstate(const RenderState *rs) {
   // Does the shader need material properties as input?
 
   _need_material_props =
-    (_have_ambient  && (_material->has_ambient()))||
-    (_have_diffuse  && (_material->has_diffuse()))||
-    (_have_emission && (_material->has_emission()))||
-    (_have_specular && (_material->has_specular()));
+    (_have_ambient  && (_material_flags & Material::F_ambient) != 0) ||
+    (_have_diffuse  && (_material_flags & Material::F_diffuse) != 0) ||
+    (_have_emission && (_material_flags & Material::F_emission) != 0) ||
+    (_have_specular && (_material_flags & Material::F_specular) != 0);
 
   // Check for clip planes.
 
@@ -490,7 +465,7 @@ clear_analysis() {
   _out_aux_normal   = false;
   _out_aux_glow     = false;
   _out_aux_any      = false;
-  _material = (Material*)NULL;
+  _material_flags = 0;
   _need_material_props = false;
   _need_world_position = false;
   _need_world_normal = false;
@@ -823,11 +798,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
     text << "\t in float4 l_" << it->first->join("_") << " : " << it->second << ",\n";
   }
   const TexMatrixAttrib *tex_matrix = DCAST(TexMatrixAttrib, rs->get_attrib_def(TexMatrixAttrib::get_class_slot()));
-  for (int i=0; i<_num_textures; i++) {
+  for (int i = 0; i < _num_textures; ++i) {
     TextureStage *stage = texture->get_on_stage(i);
-    Texture *tex = texture->get_on_texture(stage);
-    nassertr(tex != NULL, NULL);
-    text << "\t uniform sampler" << texture_type_as_string(tex->get_texture_type()) << " tex_" << i << ",\n";
+    Texture::TextureType type = texture->get_on_texture_type(stage);
+    text << "\t uniform sampler" << texture_type_as_string(type) << " tex_" << i << ",\n";
     if (tex_matrix->has_stage(stage)) {
       text << "\t uniform float4x4 texmat_" << i << ",\n";
     }
@@ -864,7 +838,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
       text << "\t uniform float4x4 attr_material,\n";
     }
     if (_have_specular) {
-      if (_material->get_local()) {
+      if (_material_flags & Material::F_local) {
         text << "\t uniform float4 mspos_view,\n";
       } else {
         text << "\t uniform float4 row1_view_to_model,\n";
@@ -935,11 +909,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
   }
   text << "\t // Fetch all textures.\n";
   if (_map_index_height >= 0 && parallax_mapping_samples > 0) {
-    Texture *tex = texture->get_on_texture(texture->get_on_stage(_map_index_height));
-    nassertr(tex != NULL, NULL);
-    text << "\t float4 tex" << _map_index_height << " = tex" << texture_type_as_string(tex->get_texture_type());
+    Texture::TextureType type = texture->get_on_texture_type(texture->get_on_stage(_map_index_height));
+    text << "\t float4 tex" << _map_index_height << " = tex" << texture_type_as_string(type);
     text << "(tex_" << _map_index_height << ", texcoord" << _map_index_height << ".";
-    switch (tex->get_texture_type()) {
+    switch (type) {
     case Texture::TT_cube_map:
     case Texture::TT_3d_texture:
     case Texture::TT_2d_texture_array:
@@ -972,18 +945,17 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
       text << " * 2.0 - 1.0)) * " << 0.5 * parallax_mapping_scale << ";\n";
     }
   }
-  for (int i=0; i<_num_textures; i++) {
+  for (int i = 0; i < _num_textures; ++i) {
     if (i != _map_index_height) {
-      Texture *tex = texture->get_on_texture(texture->get_on_stage(i));
-      nassertr(tex != NULL, NULL);
+      Texture::TextureType type = texture->get_on_texture_type(texture->get_on_stage(i));
       // Parallax mapping pushes the texture coordinates of the other textures
       // away from the camera.
       if (_map_index_height >= 0 && parallax_mapping_samples > 0) {
         text << "\t texcoord" << i << ".xyz -= parallax_offset;\n";
       }
-      text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex->get_texture_type());
+      text << "\t float4 tex" << i << " = tex" << texture_type_as_string(type);
       text << "(tex_" << i << ", texcoord" << i << ".";
-      switch(tex->get_texture_type()) {
+      switch (type) {
       case Texture::TT_cube_map:
       case Texture::TT_3d_texture:
       case Texture::TT_2d_texture_array:
@@ -1040,7 +1012,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
     }
     if (_have_specular) {
       text << "\t float4 tot_specular = float4(0,0,0,0);\n";
-      if (_material->has_specular()) {
+      if (_material_flags & Material::F_specular) {
         text << "\t float shininess = attr_material[3].w;\n";
       } else {
         text << "\t float shininess = 50; // no shininess specified, using default\n";
@@ -1072,7 +1044,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
         text << "\t tot_diffuse += lcolor;\n";
       }
       if (_have_specular) {
-        if (_material->get_local()) {
+        if (_material_flags & Material::F_local) {
           text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
         } else {
           text << "\t lhalf = dlight_light" << i << "_rel_view[3].xyz;\n";
@@ -1102,7 +1074,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
         text << "\t tot_diffuse += lcolor;\n";
       }
       if (_have_specular) {
-        if (_material->get_local()) {
+        if (_material_flags & Material::F_local) {
           text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
         } else {
           text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n";
@@ -1140,7 +1112,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
         text << "\t tot_diffuse += lcolor;\n";
       }
       if (_have_specular) {
-        if (_material->get_local()) {
+        if (_material_flags & Material::F_local) {
           text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
         } else {
           text << "\t lhalf = normalize(lvec - float3(0,1,0));\n";
@@ -1198,7 +1170,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
       }
     }
     if ((_have_ambient)&&(_separate_ambient_diffuse)) {
-      if (_material->has_ambient()) {
+      if (_material_flags & Material::F_ambient) {
         text << "\t result += tot_ambient * attr_material[0];\n";
       } else if (_vertex_colors) {
         text << "\t result += tot_ambient * l_color;\n";
@@ -1209,7 +1181,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
       }
     }
     if (_have_diffuse) {
-      if (_material->has_diffuse()) {
+      if (_material_flags & Material::F_diffuse) {
         text << "\t result += tot_diffuse * attr_material[1];\n";
       } else if (_vertex_colors) {
         text << "\t result += tot_diffuse * l_color;\n";
@@ -1263,20 +1235,20 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
   }
 
   // Now loop through the textures to compose our magic blending formulas.
-  for (int i=0; i<_num_textures; i++) {
+  for (int i = 0; i < _num_textures; ++i) {
     TextureStage *stage = texture->get_on_stage(i);
     switch (stage->get_mode()) {
     case TextureStage::M_modulate: {
-      int num_components = texture->get_on_texture(texture->get_on_stage(i))->get_num_components();
+      bool affects_rgb = texture->on_stage_affects_rgb(i);
+      bool affects_alpha = texture->on_stage_affects_alpha(i);
 
-      if (num_components == 1) {
+      if (affects_rgb && affects_alpha) {
+        text << "\t result.rgba *= tex" << i << ".rgba;\n";
+      } else if (affects_alpha) {
         text << "\t result.a *= tex" << i << ".a;\n";
-      } else if (num_components == 3) {
+      } else if (affects_rgb) {
         text << "\t result.rgb *= tex" << i << ".rgb;\n";
-      } else {
-        text << "\t result.rgba *= tex" << i << ".rgba;\n";
       }
-
       break; }
     case TextureStage::M_modulate_glow:
     case TextureStage::M_modulate_gloss:
@@ -1388,7 +1360,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
 
   if (_lighting) {
     if (_have_specular) {
-      if (_material->has_specular()) {
+      if (_material_flags & Material::F_specular) {
         text << "\t tot_specular *= attr_material[3];\n";
       }
       if (_map_index_gloss >= 0 && _auto_gloss_on) {

+ 2 - 3
panda/src/pgraphnodes/shaderGenerator.h

@@ -60,7 +60,7 @@ class GeomVertexAnimationSpec;
  */
 class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedReferenceCount {
 PUBLISHED:
-  ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host);
+  ShaderGenerator(GraphicsStateGuardianBase *gsg);
   virtual ~ShaderGenerator();
   virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs,
                                               const GeomVertexAnimationSpec &anim);
@@ -87,7 +87,7 @@ protected:
   // RenderState analysis information.  Created by analyze_renderstate:
 
   CPT(RenderState) _state;
-  Material *_material;
+  int _material_flags;
   int _num_textures;
 
   pvector<LightLensNode *> _lights;
@@ -144,7 +144,6 @@ protected:
 
   // This is not a PT() to prevent a circular reference.
   GraphicsStateGuardianBase *_gsg;
-  GraphicsOutputBase *_host;
 
 public:
   static TypeHandle get_class_type() {