Browse Source

Merge branch 'release/1.10.x'

rdb 6 years ago
parent
commit
d088263f6f

+ 1 - 1
direct/src/dist/commands.py

@@ -365,7 +365,7 @@ class build_apps(setuptools.Command):
         tmp.update(self.file_handlers)
         self.file_handlers = tmp
 
-        tmp = self.package_data_dirs.copy()
+        tmp = PACKAGE_DATA_DIRS.copy()
         tmp.update(self.package_data_dirs)
         self.package_data_dirs = tmp
 

+ 11 - 1
direct/src/particles/ForceGroup.py

@@ -6,13 +6,14 @@ from direct.showbase.PhysicsManagerGlobal import *
 from direct.directnotify import DirectNotifyGlobal
 import sys
 
+
 class ForceGroup(DirectObject):
 
     notify = DirectNotifyGlobal.directNotify.newCategory('ForceGroup')
     id = 1
 
     def __init__(self, name=None):
-        if (name == None):
+        if name is None:
             self.name = 'ForceGroup-%d' % ForceGroup.id
             ForceGroup.id += 1
         else:
@@ -60,9 +61,12 @@ class ForceGroup(DirectObject):
 
     # Get/set
     def getName(self):
+        """Deprecated: access .name directly instead."""
         return self.name
+
     def getNode(self):
         return self.node
+
     def getNodePath(self):
         return self.nodePath
 
@@ -124,3 +128,9 @@ class ForceGroup(DirectObject):
                     file.write(fname + ' = AngularVectorForce(Quat(%.4f, %.4f, %.4f))\n' % (vec[0], vec[1], vec[2], vec[3]))
             file.write(fname + '.setActive(%d)\n' % f.getActive())
             file.write(targ + '.addForce(%s)\n' % fname)
+
+    is_enabled = isEnabled
+    get_node = getNode
+    get_node_path = getNodePath
+    as_list = asList
+    print_params = printParams

+ 32 - 6
direct/src/particles/ParticleEffect.py

@@ -14,12 +14,13 @@ if sys.version_info < (3, 0):
     FileNotFoundError = IOError
 
 
+
 class ParticleEffect(NodePath):
     notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect')
     pid = 1
 
     def __init__(self, name=None, particles=None):
-        if name == None:
+        if name is None:
             name = 'particle-effect-%d' % ParticleEffect.pid
             ParticleEffect.pid += 1
         NodePath.__init__(self, name)
@@ -31,7 +32,7 @@ class ParticleEffect(NodePath):
         self.particlesDict = {}
         self.forceGroupDict = {}
         # The effect's particle system
-        if particles != None:
+        if particles is not None:
             self.addParticles(particles)
         self.renderParent = None
 
@@ -61,7 +62,7 @@ class ParticleEffect(NodePath):
         assert self.notify.debug('start() - name: %s' % self.name)
         self.renderParent = renderParent
         self.enable()
-        if parent != None:
+        if parent is not None:
             self.reparentTo(parent)
 
     def enable(self):
@@ -134,7 +135,7 @@ class ParticleEffect(NodePath):
                 particles.addForce(fg[i])
 
     def removeParticles(self, particles):
-        if particles == None:
+        if particles is None:
             self.notify.warning('removeParticles() - particles == None!')
             return
         particles.nodePath.detachNode()
@@ -231,10 +232,13 @@ class ParticleEffect(NodePath):
         for particles in self.getParticlesList():
             particles.softStop()
 
-    def softStart(self):
+    def softStart(self, firstBirthDelay=None):
         if self.__isValid():
             for particles in self.getParticlesList():
-                particles.softStart()
+                if firstBirthDelay is not None:
+                    particles.softStart(br=-1, first_birth_delay=firstBirthDelay)
+                else:
+                    particles.softStart()
         else:
             # Not asserting here since we want to crash live clients for more expedient bugfix
             # (Sorry, live clients)
@@ -243,3 +247,25 @@ class ParticleEffect(NodePath):
     def __isValid(self):
         return hasattr(self, 'forceGroupDict') and \
                hasattr(self, 'particlesDict')
+
+    # Snake-case aliases.
+    is_enabled = isEnabled
+    add_force_group = addForceGroup
+    add_force = addForce
+    remove_force_group = removeForceGroup
+    remove_force = removeForce
+    remove_all_forces = removeAllForces
+    add_particles = addParticles
+    remove_particles = removeParticles
+    remove_all_particles = removeAllParticles
+    get_particles_list = getParticlesList
+    get_particles_named = getParticlesNamed
+    get_particles_dict = getParticlesDict
+    get_force_group_list = getForceGroupList
+    get_force_group_named = getForceGroupNamed
+    get_force_group_dict = getForceGroupDict
+    save_config = saveConfig
+    load_config = loadConfig
+    clear_to_initial = clearToInitial
+    soft_stop = softStop
+    soft_start = softStart

+ 14 - 0
direct/src/particles/Particles.py

@@ -600,3 +600,17 @@ class Particles(ParticleSystem):
                 base.physicsMgr.doPhysics(remainder,self)
 
             self.render()
+
+    # Snake-case aliases.
+    is_enabled = isEnabled
+    set_factory = setFactory
+    set_renderer = setRenderer
+    set_emitter = setEmitter
+    add_force = addForce
+    remove_force = removeForce
+    set_render_node_path = setRenderNodePath
+    get_factory = getFactory
+    get_emitter = getEmitter
+    get_renderer = getRenderer
+    print_params = printParams
+    get_pool_size_ranges = getPoolSizeRanges

+ 1 - 1
panda/src/express/virtualFileSystem.cxx

@@ -187,7 +187,7 @@ mount_loop(const Filename &virtual_filename, const Filename &mount_point,
 
 /**
  * Adds the given VirtualFileMount object to the mount list.  This is a lower-
- * level function that the other flavors of mount(); it requires you to create
+ * level function than the other flavors of mount(); it requires you to create
  * a VirtualFileMount object specifically.
  */
 bool VirtualFileSystem::

+ 30 - 8
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -7378,15 +7378,21 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
     z_size = 1;
   }
 
+  int num_views = tex->get_num_views();
   if (tex->get_x_size() != w || tex->get_y_size() != h ||
       tex->get_z_size() != z_size ||
       tex->get_component_type() != component_type ||
       tex->get_format() != format ||
-      tex->get_texture_type() != texture_type) {
+      tex->get_texture_type() != texture_type ||
+      view >= num_views) {
+
+    tex->setup_texture(texture_type, w, h, z_size, component_type, format);
 
-    // Re-setup the texture; its properties have changed.
-    tex->setup_texture(texture_type, w, h, z_size,
-                       component_type, format);
+    // The above resets the number of views to 1, so set this back.
+    num_views = std::max(view + 1, num_views);
+    if (num_views > 1) {
+      tex->set_num_views(num_views);
+    }
   }
 
   nassertr(z < tex->get_z_size(), false);
@@ -7458,6 +7464,7 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
     }
     if (view > 0) {
       image_ptr += (view * tex->get_z_size()) * image_size;
+      nassertr(view < tex->get_num_views(), false);
     }
   }
 
@@ -14445,7 +14452,17 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     return false;
   }
 
-  tex->set_ram_image(image, compression, page_size);
+  int num_views = tex->get_num_views();
+  if (num_views == 1) {
+    // Replace the entire image, since we are modifying the only view.
+    tex->set_ram_image(image, compression, page_size);
+  } else {
+    // We're only modifying a single view, so we can't stomp all over the
+    // existing content.
+    PTA_uchar ram_image = tex->modify_ram_image();
+    nassertr(ram_image.size() == image.size() * num_views, false);
+    memcpy(ram_image.p() + image.size() * gtc->get_view(), image.p(), image.size());
+  }
 
   if (gtc->_uses_mipmaps) {
     // Also get the mipmap levels.
@@ -14461,7 +14478,12 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
                                  type, compression, n)) {
         return false;
       }
-      tex->set_ram_mipmap_image(n, image, page_size);
+      if (num_views == 1) {
+        tex->set_ram_mipmap_image(n, image, page_size);
+      } else {
+        PTA_uchar ram_mipmap_image = tex->modify_ram_mipmap_image(n);
+        memcpy(ram_mipmap_image.p() + image.size() * gtc->get_view(), image.p(), image.size());
+      }
     }
   }
 
@@ -14525,13 +14547,13 @@ extract_texture_image(PTA_uchar &image, size_t &page_size,
 #ifndef OPENGLES
   } else if (target == GL_TEXTURE_BUFFER) {
     // In the case of a buffer texture, we need to get it from the buffer.
-    image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));
+    image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_view_size(n));
     _glGetBufferSubData(target, 0, image.size(), image.p());
 #endif
 
   } else if (compression == Texture::CM_off) {
     // An uncompressed 1-d, 2-d, or 3-d texture.
-    image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));
+    image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_view_size(n));
     GLenum external_format = get_external_image_format(tex);
     GLenum pixel_type = get_component_type(type);
     glGetTexImage(target, n, external_format, pixel_type, image.p());

+ 7 - 1
panda/src/gobj/indexBufferContext.cxx

@@ -20,7 +20,13 @@ TypeHandle IndexBufferContext::_type_handle;
  */
 void IndexBufferContext::
 output(std::ostream &out) const {
-  out << *get_data() << ", " << get_data_size_bytes();
+  GeomPrimitive *prim = get_data();
+  if (prim != nullptr) {
+    out << *prim;
+  } else {
+    out << "NULL";
+  }
+  out << ", " << get_data_size_bytes();
 }
 
 /**

+ 7 - 1
panda/src/gobj/vertexBufferContext.cxx

@@ -21,7 +21,13 @@ TypeHandle VertexBufferContext::_type_handle;
  */
 void VertexBufferContext::
 output(std::ostream &out) const {
-  out << *get_data() << ", " << get_data_size_bytes();
+  GeomVertexArrayData *data = get_data();
+  if (data != nullptr) {
+    out << *data;
+  } else {
+    out << "NULL";
+  }
+  out << ", " << get_data_size_bytes();
 }
 
 /**

+ 20 - 0
panda/src/particlesystem/particleSystem.I

@@ -58,6 +58,18 @@ soft_start(PN_stdfloat br) {
   _tics_since_birth = 0.0f;
 }
 
+/**
+ * Causes system to use birth rate set by set_birth_rate(), with the system's
+ * first birth being delayed by the value of first_birth_delay. Note that a
+ * negative delay is perfectly valid, causing the first birth to happen
+ * sooner rather than later.
+ */
+INLINE void ParticleSystem::
+soft_start(PN_stdfloat br, PN_stdfloat first_birth_delay) {
+  soft_start(br);
+  _tics_since_birth = -first_birth_delay;
+}
+
 /**
  * Causes system to use birth rate set by set_soft_birth_rate()
  */
@@ -342,6 +354,14 @@ get_floor_z() const {
   return _floor_z;
 }
 
+/**
+
+*/
+INLINE PN_stdfloat ParticleSystem::
+get_tics_since_birth() const {
+  return _tics_since_birth;
+}
+
 /**
 
  */

+ 2 - 0
panda/src/particlesystem/particleSystem.h

@@ -83,6 +83,7 @@ PUBLISHED:
   INLINE BaseParticleEmitter *get_emitter() const;
   INLINE BaseParticleFactory *get_factory() const;
   INLINE PN_stdfloat get_floor_z() const;
+  INLINE PN_stdfloat get_tics_since_birth() const;
 
   // particle template vector
 
@@ -96,6 +97,7 @@ PUBLISHED:
   INLINE void clear_to_initial();
   INLINE void soft_stop(PN_stdfloat br = 0.0);
   INLINE void soft_start(PN_stdfloat br = 0.0);
+  INLINE void soft_start(PN_stdfloat br, PN_stdfloat first_birth_delay);
   void update(PN_stdfloat dt);
 
   virtual void output(std::ostream &out) const;

+ 103 - 0
tests/particles/test_particlesystem.py

@@ -0,0 +1,103 @@
+from panda3d.core import NodePath, PandaNode
+from direct.particles.ParticleEffect import ParticleEffect
+from direct.particles.Particles import Particles
+
+
+def test_particle_birth_rate():
+    # Tests a system with a standard birth rate of 0.5, that it is
+    # indeed birthing at that rate.  It serves as a control for the
+    # next test as well.
+    system = Particles("testSystem", 2)
+
+    system.set_render_parent(NodePath(PandaNode("test")))
+    system.set_spawn_render_node_path(NodePath(PandaNode("test")))
+
+    assert system.get_birth_rate() == 0.5
+    assert system.get_tics_since_birth() == 0
+    assert system.get_living_particles() == 0
+
+    system.update(0.6)
+    assert system.get_living_particles() == 1
+
+    system.update(0.5)
+    assert system.get_living_particles() == 2
+
+    # Should still be 2, since the pool size was 2.
+    system.update(0.5)
+    assert system.get_living_particles() == 2
+
+
+def test_particle_soft_start():
+    # Create a particle effect and a particle system.
+    # The effect serves to test the Python-level "soft_start"  method,
+    # while the system serves to test the C++-level "soft_start" method
+    # (via the associated Python "soft_start" method)
+    effect = ParticleEffect()
+    system = Particles("testSystem", 10)
+
+    # Setup some dummy nodes, since it seems to want them
+    system.set_render_parent(NodePath(PandaNode("test")))
+    system.set_spawn_render_node_path(NodePath(PandaNode("test")))
+
+    # Add the system to the effect
+    effect.add_particles(system)
+
+    # Re-assign the system, just to make sure that we have the
+    # right object.
+    system = effect.get_particles_list()[0]
+
+    # First, standard "soft_start"--i.e. without either changing
+    # the birth-rate or applying a delay. This should work as it
+    # used to.
+    effect.soft_start()
+
+    assert system.get_birth_rate() == 0.5
+
+    # Now, check that the pre-existing single-parameter soft-start,
+    # which alters the birth-rate, still does so.
+    system.soft_start(1)
+
+    assert system.get_birth_rate() == 1
+
+    # Next, birth-delaying.
+
+    # Run a standard soft-start, then check that the birth-timer
+    # is zero, as used to be the case on running this command.
+    effect.soft_start()
+
+    assert system.get_tics_since_birth() == 0
+
+    # Run an delayed soft-start via the system, then check that the
+    # birth-timer has the assigned value, and that the birth-rate is
+    # unchanged.
+
+    # (We pass in a birth-rate ("br") of -1 because the related code
+    # checks for a birth-rate greater than 0, I believe. This allows
+    # us to change the delay without affecting the birth-rate.)
+    system.soft_start(br=-1, first_birth_delay=-2)
+
+    assert system.get_birth_rate() == 1
+    assert system.get_tics_since_birth() == 2
+
+    # Now, run a delayed soft-start via the effect, and
+    # again check that the birth-timer has changed as intended,
+    # and the birth-rate hasn't changed at all.
+    effect.soft_start(firstBirthDelay=0.25)
+
+    assert system.get_birth_rate() == 1
+    assert system.get_tics_since_birth() == -0.25
+
+    # Update the system, advancing it far enough that it should
+    # have birthed a particle if not for the delay, but not
+    # so far that it should have birthed a particle >with<
+    # the delay. Check thus that no particles have been birthed.
+    system.update(1)
+
+    assert system.get_living_particles() == 0
+
+    # Update the system again, this time far enough that with the
+    # delay it should have birthed just one particle, and
+    # then check that this is the case.
+    system.update(1)
+
+    assert system.get_living_particles() == 1