diff --git a/direct/src/tkpanels/ParticlePanel.py b/direct/src/tkpanels/ParticlePanel.py index 47faa71cb6..b7a632f61a 100644 --- a/direct/src/tkpanels/ParticlePanel.py +++ b/direct/src/tkpanels/ParticlePanel.py @@ -214,23 +214,23 @@ class ParticlePanel(AppShell): ('System', 'Pool Size', 'Max number of simultaneous particles', self.setSystemPoolSize, - 1.0, 1.0), + 1.0, 2000000, 1.0), ('System', 'Birth Rate', 'Seconds between particle births', self.setSystemBirthRate, - 0.0, None), + 0.0, None, None), ('System', 'Litter Size', 'Number of particle created at each birth', self.setSystemLitterSize, - 1.0, 1.0), + 1.0, 0x7fffffff, 1.0), ('System', 'Litter Spread', 'Variation in litter size', self.setSystemLitterSpread, - 0.0, 1.0), + 0.0, 0x7fffffff, 1.0), ('System', 'Lifespan', 'Age in seconds at which the system (vs. particles) should die', self.setSystemLifespan, - 0.0, None) + 0.0, None, None) ) self.createFloaters(systemPage, systemFloaterDefs) @@ -269,27 +269,27 @@ class ParticlePanel(AppShell): ('Factory', 'Life Span', 'Average particle lifespan in seconds', self.setFactoryLifeSpan, - 0.0, None), + 0.0, None, None), ('Factory', 'Life Span Spread', 'Variation in lifespan', self.setFactoryLifeSpanSpread, - 0.0, None), + 0.0, None, None), ('Factory', 'Mass', 'Average particle mass', self.setFactoryParticleMass, - 0.001, None), + 0.001, None, None), ('Factory', 'Mass Spread', 'Variation in particle mass', self.setFactoryParticleMassSpread, - 0.0, None), + 0.0, None, None), ('Factory', 'Terminal Velocity', 'Cap on average particle velocity', self.setFactoryTerminalVelocity, - 0.0, None), + 0.0, None, None), ('Factory', 'Terminal Vel. Spread', 'Variation in terminal velocity', self.setFactoryTerminalVelocitySpread, - 0.0, None), + 0.0, None, None), ) self.createFloaters(factoryPage, factoryWidgets) @@ -966,19 +966,29 @@ class ParticlePanel(AppShell): def createFloaters(self, parent, widgetDefinitions): widgets = [] - for category, label, balloonHelp, command, min, resolution in widgetDefinitions: + for category, label, balloonHelp, command, min, max, resolution in widgetDefinitions: widgets.append( self.createFloater(parent, category, label, balloonHelp, - command, min, resolution) + command, min, max, resolution) ) return widgets def createFloater(self, parent, category, text, balloonHelp, - command = None, min = 0.0, resolution = None, - numDigits = 3, **kw): + command = None, min = 0.0, max = None, resolution = None, + numDigits = None, **kw): kw['text'] = text kw['min'] = min + if max is not None: + kw['max'] = max kw['resolution'] = resolution + if numDigits is None: + # If this is apparently an integer setting, show no decimals. + if resolution is not None and int(resolution) == resolution and \ + (min is None or int(min) == min) and \ + (max is None or int(max) == max): + numDigits = 0 + else: + numDigits = 3 kw['numDigits'] = numDigits widget = Floater.Floater(parent, **kw) # Do this after the widget so command isn't called on creation diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index af30943212..d8ce85041c 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -6408,9 +6408,21 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, TypeManager::is_vector_unsigned_char(type)) { // Most types are now handled by the many overloads of Dtool_WrapValue, // defined in py_panda.h. - indent(out, indent_level) - << "return Dtool_WrapValue(" << return_expr << ");\n"; - + if (!remap->_has_this && remap->_cppfunc != nullptr && + remap->_cppfunc->get_simple_name() == "encrypt_string" && + return_expr == "return_value") { + // Temporary hack to fix #684 to avoid an ABI change. + out << "#if PY_MAJOR_VERSION >= 3\n"; + indent(out, indent_level) + << "return PyBytes_FromStringAndSize((char *)return_value.data(), (Py_ssize_t)return_value.size());\n"; + out << "#else\n"; + indent(out, indent_level) + << "return PyString_FromStringAndSize((char *)return_value.data(), (Py_ssize_t)return_value.size());\n"; + out << "#endif\n"; + } else { + indent(out, indent_level) + << "return Dtool_WrapValue(" << return_expr << ");\n"; + } } else if (TypeManager::is_pointer(type)) { bool is_const = TypeManager::is_const_pointer_to_anything(type); bool owns_memory = remap->_return_value_needs_management; diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 9941fa8175..5d5fb7a742 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -924,6 +924,10 @@ if (COMPILER=="GCC"): else: PkgDisable("OPENCV") + if GetTarget() == "darwin" and not PkgSkip("OPENAL"): + LibName("OPENAL", "-framework AudioToolbox") + LibName("OPENAL", "-framework CoreAudio") + if not PkgSkip("ASSIMP") and \ os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"): # Also pick up IrrXML, which is needed when linking statically. diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index afa406e921..73aaf77c40 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -1612,7 +1612,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, // Find the first non-NaN vertex. while (!found_any && i < cdata->_num_vertices) { reader.set_row(cdata->_first_vertex + i); - LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + LPoint3 first_vertex = mat.xform_point_general(reader.get_data3()); if (!first_vertex.is_nan()) { min_point = first_vertex; max_point = first_vertex; @@ -1624,7 +1624,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, for (; i < cdata->_num_vertices; ++i) { reader.set_row_unsafe(cdata->_first_vertex + i); - LPoint3 vertex = mat.xform_point(reader.get_data3()); + LPoint3 vertex = mat.xform_point_general(reader.get_data3()); min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1677,7 +1677,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, int ii = index.get_data1i(); if (ii != strip_cut_index) { reader.set_row(ii); - LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + LPoint3 first_vertex = mat.xform_point_general(reader.get_data3()); if (!first_vertex.is_nan()) { min_point = first_vertex; max_point = first_vertex; @@ -1693,7 +1693,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, continue; } reader.set_row_unsafe(ii); - LPoint3 vertex = mat.xform_point(reader.get_data3()); + LPoint3 vertex = mat.xform_point_general(reader.get_data3()); min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 8732168212..1243fed956 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -3136,6 +3136,19 @@ get_texture(TextureStage *stage) const { return nullptr; } +/** + * Recursively searches the scene graph for references to the given texture, + * and replaces them with the new texture. + */ +void NodePath:: +replace_texture(Texture *tex, Texture *new_tex) { + nassertv_always(!is_empty()); + nassertv(tex != nullptr); + nassertv(new_tex != nullptr); + + r_replace_texture(node(), tex, new_tex); +} + /** * Returns the sampler state that has been given for the base-level texture * that has been set on this particular node. If no sampler state was given, @@ -6425,6 +6438,53 @@ r_find_all_textures(PandaNode *node, TextureStage *stage, } } +/** + * Recursively replaces references to the given texture on this section of the + * scene graph with the given other texture. + */ +void NodePath:: +r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex) { + // Consider the state of the node itself. + { + CPT(RenderState) node_state = node->get_state(); + const TextureAttrib *ta; + if (node_state->get_attrib(ta)) { + CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex); + if (new_ta != ta) { + node->set_state(node_state->set_attrib(new_ta)); + } + } + } + + // If this is a GeomNode, consider the state of any of its Geoms. + if (node->is_geom_node()) { + GeomNode *gnode; + DCAST_INTO_V(gnode, node); + + int num_geoms = gnode->get_num_geoms(); + for (int i = 0; i < num_geoms; i++) { + CPT(RenderState) geom_state = gnode->get_geom_state(i); + + // Look for a TextureAttrib on the state. + const TextureAttrib *ta; + if (geom_state->get_attrib(ta)) { + CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex); + if (new_ta != ta) { + gnode->set_geom_state(i, geom_state->set_attrib(new_ta)); + } + } + } + } + + // Now consider children. + PandaNode::Children cr = node->get_children(); + size_t num_children = cr.get_num_children(); + for (size_t i = 0; i < num_children; ++i) { + PandaNode *child = cr.get_child(i); + r_replace_texture(child, tex, new_tex); + } +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 3d5d4a6aa3..4f0849fac4 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -620,6 +620,7 @@ PUBLISHED: bool has_texture_off(TextureStage *stage) const; Texture *get_texture() const; Texture *get_texture(TextureStage *stage) const; + void replace_texture(Texture *tex, Texture *new_tex); const SamplerState &get_texture_sampler() const; const SamplerState &get_texture_sampler(TextureStage *stage) const; @@ -1003,6 +1004,7 @@ private: Texture *r_find_texture(PandaNode *node, TextureStage *stage) const; void r_find_all_textures(PandaNode *node, TextureStage *stage, Textures &textures) const; + static void r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex); typedef phash_set TextureStages; TextureStage *r_find_texture_stage(PandaNode *node, const RenderState *state, diff --git a/panda/src/pgraph/scissorAttrib.cxx b/panda/src/pgraph/scissorAttrib.cxx index 4e137eec5b..f84bab3fc0 100644 --- a/panda/src/pgraph/scissorAttrib.cxx +++ b/panda/src/pgraph/scissorAttrib.cxx @@ -101,7 +101,7 @@ int ScissorAttrib:: compare_to_impl(const RenderAttrib *other) const { const ScissorAttrib *ta = (const ScissorAttrib *)other; - if (!_off && !ta->_off) { + if (_off && ta->_off) { return 0; } diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index 7f9c6ff8b8..58225185d7 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -250,6 +250,32 @@ unify_texture_stages(TextureStage *stage) const { return return_new(attrib); } +/** + * Returns a new TextureAttrib, just like this one, but with all references to + * the given texture replaced with the new texture. + */ +CPT(RenderAttrib) TextureAttrib:: +replace_texture(Texture *tex, Texture *new_tex) const { + TextureAttrib *attrib = nullptr; + + for (size_t i = 0; i < _on_stages.size(); ++i) { + const StageNode &sn = _on_stages[i]; + if (sn._texture == tex) { + if (attrib == nullptr) { + attrib = new TextureAttrib(*this); + } + + attrib->_on_stages[i]._texture = new_tex; + } + } + + if (attrib != nullptr) { + return return_new(attrib); + } else { + return this; + } +} + /** * Returns a new TextureAttrib, very much like this one, but with the number * of on_stages reduced to be no more than max_texture_stages. The number of diff --git a/panda/src/pgraph/textureAttrib.h b/panda/src/pgraph/textureAttrib.h index 908e4c50af..0858cc68a7 100644 --- a/panda/src/pgraph/textureAttrib.h +++ b/panda/src/pgraph/textureAttrib.h @@ -89,6 +89,7 @@ PUBLISHED: CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const; CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const; CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const; + CPT(RenderAttrib) replace_texture(Texture *tex, Texture *new_tex) const; public: CPT(TextureAttrib) filter_to_max(int max_texture_stages) const; diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 32cf41eb89..1453103fc2 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -1843,10 +1843,10 @@ get_character_glyphs(int character, const TextProperties *properties, // Maybe we should remap the character to something else--e.g. a small // capital. const UnicodeLatinMap::Entry *map_entry = - UnicodeLatinMap::look_up(character); + UnicodeLatinMap::look_up((char32_t)character); if (map_entry != nullptr) { if (properties->get_small_caps() && - map_entry->_toupper_character != character) { + map_entry->_toupper_character != (char32_t)character) { character = map_entry->_toupper_character; map_entry = UnicodeLatinMap::look_up(character); glyph_scale = properties->get_small_caps_scale(); @@ -1871,7 +1871,7 @@ get_character_glyphs(int character, const TextProperties *properties, got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph); } - if (!got_glyph && map_entry->_toupper_character != character) { + if (!got_glyph && map_entry->_toupper_character != (char32_t)character) { // If we still couldn't find it, try the uppercase equivalent. character = map_entry->_toupper_character; map_entry = UnicodeLatinMap::look_up(character); diff --git a/tests/pgraph/test_nodepath.py b/tests/pgraph/test_nodepath.py index 19a40021ca..7dcbd2662b 100644 --- a/tests/pgraph/test_nodepath.py +++ b/tests/pgraph/test_nodepath.py @@ -149,3 +149,22 @@ def test_nodepath_python_tags(): rc1 = sys.getrefcount(path.python_tags) rc2 = sys.getrefcount(path.python_tags) assert rc1 == rc2 + + +def test_nodepath_replace_texture(): + from panda3d.core import NodePath, Texture + + tex1 = Texture() + tex2 = Texture() + + path1 = NodePath("node1") + path1.set_texture(tex1) + path1.replace_texture(tex1, tex2) + assert path1.get_texture() == tex2 + + path1 = NodePath("node1") + path2 = path1.attach_new_node("node2") + path2.set_texture(tex1) + path1.replace_texture(tex1, tex2) + assert not path1.has_texture() + assert path2.get_texture() == tex2