mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-29 16:20:11 -04:00
Merge branch 'release/1.10.x'
This commit is contained in:
commit
d088263f6f
2
direct/src/dist/commands.py
vendored
2
direct/src/dist/commands.py
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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::
|
||||
|
@ -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) {
|
||||
|
||||
// Re-setup the texture; its properties have changed.
|
||||
tex->setup_texture(texture_type, w, h, z_size,
|
||||
component_type, format);
|
||||
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());
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
|
@ -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
tests/particles/test_particlesystem.py
Normal file
103
tests/particles/test_particlesystem.py
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user