diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index 1b96352723..15fac6b60f 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -15,9 +15,6 @@ #include "typeRegistryNode.h" #include "atomicAdjust.h" -// This is initialized to zero by static initialization. -TypeHandle TypeHandle::_none; - /** * Returns the total allocated memory used by objects of this type, for the * indicated memory class. This is only updated if track-memory-usage is set diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index da66074750..97dc445443 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -147,9 +147,6 @@ public: private: constexpr TypeHandle(int index); - // Only kept temporarily for ABI compatibility. - static TypeHandle _none; - int _index; friend class TypeRegistry; }; diff --git a/dtool/src/dtoolutil/dSearchPath.cxx b/dtool/src/dtoolutil/dSearchPath.cxx index 4a094f1d98..c17b3d61ec 100644 --- a/dtool/src/dtoolutil/dSearchPath.cxx +++ b/dtool/src/dtoolutil/dSearchPath.cxx @@ -116,13 +116,6 @@ write(ostream &out, int indent_level) const { } } -/** - * Creates an empty search path. - */ -DSearchPath:: -DSearchPath() { -} - /** * */ @@ -139,30 +132,6 @@ DSearchPath(const Filename &directory) { append_directory(directory); } -/** - * - */ -DSearchPath:: -DSearchPath(const DSearchPath ©) : - _directories(copy._directories) -{ -} - -/** - * - */ -void DSearchPath:: -operator = (const DSearchPath ©) { - _directories = copy._directories; -} - -/** - * - */ -DSearchPath:: -~DSearchPath() { -} - /** * Removes all the directories from the search list. */ diff --git a/dtool/src/dtoolutil/dSearchPath.h b/dtool/src/dtoolutil/dSearchPath.h index 8cb769c378..7258a71f26 100644 --- a/dtool/src/dtoolutil/dSearchPath.h +++ b/dtool/src/dtoolutil/dSearchPath.h @@ -52,12 +52,15 @@ PUBLISHED: Files _files; }; - DSearchPath(); + DSearchPath() = default; DSearchPath(const std::string &path, const std::string &separator = std::string()); DSearchPath(const Filename &directory); - DSearchPath(const DSearchPath ©); - void operator = (const DSearchPath ©); - ~DSearchPath(); + DSearchPath(const DSearchPath ©) = default; + DSearchPath(DSearchPath &&from) = default; + ~DSearchPath() = default; + + DSearchPath &operator = (const DSearchPath ©) = default; + DSearchPath &operator = (DSearchPath &&from) = default; void clear(); void append_directory(const Filename &directory); diff --git a/dtool/src/parser-inc/dirent.h b/dtool/src/parser-inc/dirent.h new file mode 100644 index 0000000000..5c0877bfcd --- /dev/null +++ b/dtool/src/parser-inc/dirent.h @@ -0,0 +1,3 @@ +typedef struct __dirstream DIR; +struct dirent; +typedef unsigned long ino_t; diff --git a/dtool/src/parser-inc/sys/inotify.h b/dtool/src/parser-inc/sys/inotify.h new file mode 100644 index 0000000000..52ef884e79 --- /dev/null +++ b/dtool/src/parser-inc/sys/inotify.h @@ -0,0 +1 @@ +struct inotify_event; diff --git a/dtool/src/parser-inc/sys/ioctl.h b/dtool/src/parser-inc/sys/ioctl.h new file mode 100644 index 0000000000..d57d62d983 --- /dev/null +++ b/dtool/src/parser-inc/sys/ioctl.h @@ -0,0 +1,3 @@ +struct winsize; +struct termio; + diff --git a/dtool/src/parser-inc/sys/mman.h b/dtool/src/parser-inc/sys/mman.h new file mode 100644 index 0000000000..c540c66b78 --- /dev/null +++ b/dtool/src/parser-inc/sys/mman.h @@ -0,0 +1,3 @@ +#include + +struct posix_typed_mem_info; diff --git a/dtool/src/parser-inc/sys/select.h b/dtool/src/parser-inc/sys/select.h new file mode 100644 index 0000000000..18a03a58e7 --- /dev/null +++ b/dtool/src/parser-inc/sys/select.h @@ -0,0 +1 @@ +#include diff --git a/dtool/src/parser-inc/sys/sysinfo.h b/dtool/src/parser-inc/sys/sysinfo.h new file mode 100644 index 0000000000..72e2bef1b2 --- /dev/null +++ b/dtool/src/parser-inc/sys/sysinfo.h @@ -0,0 +1 @@ +struct sysinfo; diff --git a/dtool/src/prc/configDeclaration.cxx b/dtool/src/prc/configDeclaration.cxx index 7cbfa5ba6e..b401df73d4 100644 --- a/dtool/src/prc/configDeclaration.cxx +++ b/dtool/src/prc/configDeclaration.cxx @@ -16,6 +16,8 @@ #include "config_prc.h" #include "pstrtod.h" #include "string_utils.h" +#include "executionEnvironment.h" +#include "mutexImpl.h" using std::string; @@ -131,6 +133,41 @@ set_double_word(size_t n, double value) { invalidate_cache(); } +/** + * Interprets the string value as a filename and returns it, with any + * variables expanded. + */ +Filename ConfigDeclaration:: +get_filename_value() const { + // Since we are about to set THIS_PRC_DIR globally, we need to ensure that + // no two threads call this method at the same time. + // NB. MSVC doesn't guarantee that this mutex is initialized in a + // thread-safe manner. But chances are that the first time this is called + // is at static init time, when there is no risk of data races. + static MutexImpl lock; + + string str = _string_value; + + // Are there any variables to be expanded? + if (str.find('$') != string::npos) { + Filename page_filename(_page->get_name()); + Filename page_dirname = page_filename.get_dirname(); + + lock.lock(); + ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); + str = ExecutionEnvironment::expand_string(str); + ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); + lock.unlock(); + } + + Filename fn; + if (!str.empty()) { + fn = Filename::from_os_specific(str); + fn.make_true_case(); + } + return fn; +} + /** * */ diff --git a/dtool/src/prc/configDeclaration.h b/dtool/src/prc/configDeclaration.h index 38129670d5..11cb97728a 100644 --- a/dtool/src/prc/configDeclaration.h +++ b/dtool/src/prc/configDeclaration.h @@ -19,6 +19,7 @@ #include "configPage.h" #include "vector_string.h" #include "numeric_types.h" +#include "filename.h" #include @@ -68,6 +69,8 @@ PUBLISHED: void set_int64_word(size_t n, int64_t value); void set_double_word(size_t n, double value); + Filename get_filename_value() const; + INLINE int get_decl_seq() const; void output(std::ostream &out) const; diff --git a/dtool/src/prc/configVariableBool.cxx b/dtool/src/prc/configVariableBool.cxx index 7fb604642d..701f40c65d 100644 --- a/dtool/src/prc/configVariableBool.cxx +++ b/dtool/src/prc/configVariableBool.cxx @@ -18,6 +18,18 @@ */ void ConfigVariableBool:: reload_value() const { - mark_cache_valid(_local_modified); - _cache = get_bool_word(0); + // NB. MSVC doesn't guarantee that this mutex is initialized in a + // thread-safe manner. But chances are that the first time this is called + // is at static init time, when there is no risk of data races. + static MutexImpl lock; + lock.lock(); + + // We check again for cache validity since another thread may have beaten + // us to the punch while we were waiting for the lock. + if (!is_cache_valid(_local_modified)) { + _cache = get_bool_word(0); + mark_cache_valid(_local_modified); + } + + lock.unlock(); } diff --git a/dtool/src/prc/configVariableFilename.cxx b/dtool/src/prc/configVariableFilename.cxx index 4c8941babd..1015a32e66 100644 --- a/dtool/src/prc/configVariableFilename.cxx +++ b/dtool/src/prc/configVariableFilename.cxx @@ -29,17 +29,9 @@ reload_cache() { // us to the punch while we were waiting for the lock. if (!is_cache_valid(_local_modified)) { nassertv(_core != nullptr); - const ConfigDeclaration *decl = _core->get_declaration(0); - const ConfigPage *page = decl->get_page(); - - Filename page_filename(page->get_name()); - Filename page_dirname = page_filename.get_dirname(); - ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); - - _cache = Filename::expand_from(decl->get_string_value()); - ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); + _cache = decl->get_filename_value(); mark_cache_valid(_local_modified); } lock.unlock(); diff --git a/dtool/src/prc/configVariableSearchPath.I b/dtool/src/prc/configVariableSearchPath.I index 46e542e24c..37c4383c02 100644 --- a/dtool/src/prc/configVariableSearchPath.I +++ b/dtool/src/prc/configVariableSearchPath.I @@ -93,20 +93,24 @@ INLINE ConfigVariableSearchPath:: * Returns the variable's value. */ INLINE ConfigVariableSearchPath:: -operator const DSearchPath & () const { +operator DSearchPath () const { return get_value(); } /** * */ -INLINE const DSearchPath &ConfigVariableSearchPath:: +INLINE DSearchPath ConfigVariableSearchPath:: get_value() const { TAU_PROFILE("const DSearchPath &ConfigVariableSearchPath::get_value() const", " ", TAU_USER); + DSearchPath value; + _lock.lock(); if (!is_cache_valid(_local_modified)) { ((ConfigVariableSearchPath *)this)->reload_search_path(); } - return _cache; + value = _cache; + _lock.unlock(); + return value; } /** @@ -123,6 +127,7 @@ get_default_value() const { */ INLINE bool ConfigVariableSearchPath:: clear_local_value() { + _lock.lock(); nassertr(_core != nullptr, false); bool any_to_clear = !_prefix.is_empty() || _postfix.is_empty(); @@ -134,6 +139,7 @@ clear_local_value() { } _local_modified = initial_invalid_cache(); + _lock.unlock(); return any_to_clear; } @@ -151,8 +157,10 @@ clear() { */ INLINE void ConfigVariableSearchPath:: append_directory(const Filename &directory) { + _lock.lock(); _postfix.append_directory(directory); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -160,8 +168,10 @@ append_directory(const Filename &directory) { */ INLINE void ConfigVariableSearchPath:: prepend_directory(const Filename &directory) { + _lock.lock(); _prefix.prepend_directory(directory); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -170,8 +180,10 @@ prepend_directory(const Filename &directory) { */ INLINE void ConfigVariableSearchPath:: append_path(const std::string &path, const std::string &separator) { + _lock.lock(); _postfix.append_path(path, separator); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -180,8 +192,10 @@ append_path(const std::string &path, const std::string &separator) { */ INLINE void ConfigVariableSearchPath:: append_path(const DSearchPath &path) { + _lock.lock(); _postfix.append_path(path); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -190,8 +204,10 @@ append_path(const DSearchPath &path) { */ INLINE void ConfigVariableSearchPath:: prepend_path(const DSearchPath &path) { + _lock.lock(); _prefix.prepend_path(path); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -213,9 +229,13 @@ get_num_directories() const { /** * Returns the nth directory on the search list. */ -INLINE const Filename &ConfigVariableSearchPath:: +INLINE Filename ConfigVariableSearchPath:: get_directory(size_t n) const { - return get_value().get_directory(n); + Filename dir; + _lock.lock(); + dir = _cache.get_directory(n); + _lock.unlock(); + return dir; } /** diff --git a/dtool/src/prc/configVariableSearchPath.cxx b/dtool/src/prc/configVariableSearchPath.cxx index 2231626a7a..32620253b3 100644 --- a/dtool/src/prc/configVariableSearchPath.cxx +++ b/dtool/src/prc/configVariableSearchPath.cxx @@ -27,17 +27,10 @@ reload_search_path() { size_t num_unique_references = _core->get_num_unique_references(); for (size_t i = 0; i < num_unique_references; i++) { const ConfigDeclaration *decl = _core->get_unique_reference(i); - const ConfigPage *page = decl->get_page(); - Filename page_filename(page->get_name()); - Filename page_dirname = page_filename.get_dirname(); - ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); - std::string expanded = ExecutionEnvironment::expand_string(decl->get_string_value()); - ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); - if (!expanded.empty()) { - Filename dir = Filename::from_os_specific(expanded); - dir.make_true_case(); - _cache.append_directory(dir); + Filename fn = decl->get_filename_value(); + if (!fn.empty()) { + _cache.append_directory(std::move(fn)); } } diff --git a/dtool/src/prc/configVariableSearchPath.h b/dtool/src/prc/configVariableSearchPath.h index 02a4fd65c3..ca575c078a 100644 --- a/dtool/src/prc/configVariableSearchPath.h +++ b/dtool/src/prc/configVariableSearchPath.h @@ -48,8 +48,8 @@ PUBLISHED: int flags = 0); INLINE ~ConfigVariableSearchPath(); - INLINE operator const DSearchPath & () const; - INLINE const DSearchPath &get_value() const; + INLINE operator DSearchPath () const; + INLINE DSearchPath get_value() const; INLINE const DSearchPath &get_default_value() const; MAKE_PROPERTY(value, get_value); MAKE_PROPERTY(default_value, get_default_value); @@ -66,7 +66,7 @@ PUBLISHED: INLINE bool is_empty() const; INLINE size_t get_num_directories() const; - INLINE const Filename &get_directory(size_t n) const; + INLINE Filename get_directory(size_t n) const; MAKE_SEQ(get_directories, get_num_directories, get_directory); MAKE_SEQ_PROPERTY(directories, get_num_directories, get_directory); @@ -81,6 +81,7 @@ PUBLISHED: private: void reload_search_path(); + mutable MutexImpl _lock; DSearchPath _default_value; DSearchPath _prefix, _postfix; diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index f8fda4bd47..7e9d21259d 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -248,8 +248,8 @@ inline std::ostream &operator <<(std::ostream &out, const PartBundle &bundle) { return out; } -std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type); -std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type); +EXPCL_PANDA_CHAN std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type); +EXPCL_PANDA_CHAN std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type); #include "partBundle.I" diff --git a/panda/src/egg/eggMesher.h b/panda/src/egg/eggMesher.h index 5d1a3e0070..5b4d9b63d1 100644 --- a/panda/src/egg/eggMesher.h +++ b/panda/src/egg/eggMesher.h @@ -30,7 +30,7 @@ * connectivity, and generates a set of EggTriangleStrips that represent the * same geometry. */ -class EggMesher { +class EXPCL_PANDA_EGG EggMesher { public: EggMesher(); diff --git a/panda/src/egg/eggMesherEdge.h b/panda/src/egg/eggMesherEdge.h index 5ef30c1c69..17e463b79d 100644 --- a/panda/src/egg/eggMesherEdge.h +++ b/panda/src/egg/eggMesherEdge.h @@ -26,7 +26,7 @@ class EggMesherStrip; * connected triangles. The edge is actually represented as a pair of vertex * indices into the same vertex pool. */ -class EggMesherEdge { +class EXPCL_PANDA_EGG EggMesherEdge { public: INLINE EggMesherEdge(int vi_a, int vi_b); INLINE EggMesherEdge(const EggMesherEdge ©); diff --git a/panda/src/egg/eggMesherFanMaker.h b/panda/src/egg/eggMesherFanMaker.h index f320b2858b..894462b493 100644 --- a/panda/src/egg/eggMesherFanMaker.h +++ b/panda/src/egg/eggMesherFanMaker.h @@ -31,7 +31,7 @@ class EggMesher; * This class is used by EggMesher::find_fans() to attempt to make an * EggTriangleFan out of the polygons connected to the indicated vertex. */ -class EggMesherFanMaker { +class EXPCL_PANDA_EGG EggMesherFanMaker { public: typedef plist Edges; typedef plist Strips; diff --git a/panda/src/egg/eggMesherStrip.h b/panda/src/egg/eggMesherStrip.h index 6064388ce0..72a2e54fa3 100644 --- a/panda/src/egg/eggMesherStrip.h +++ b/panda/src/egg/eggMesherStrip.h @@ -27,7 +27,7 @@ class EggMesherEdge; * mesher. It might also represent a single polygon such as a triangle or * quad, since that's how strips generally start out. */ -class EggMesherStrip { +class EXPCL_PANDA_EGG EggMesherStrip { public: enum PrimType { PT_poly, diff --git a/panda/src/egg2pg/eggBinner.h b/panda/src/egg2pg/eggBinner.h index a7e3c10894..007f434190 100644 --- a/panda/src/egg2pg/eggBinner.h +++ b/panda/src/egg2pg/eggBinner.h @@ -27,7 +27,7 @@ class EggLoader; * It is used to collect similar polygons together for a Geom, as well as to * group related LOD children together under a single LOD node. */ -class EggBinner : public EggBinMaker { +class EXPCL_PANDA_EGG2PG EggBinner : public EggBinMaker { public: // The BinNumber serves to identify why a particular EggBin was created. enum BinNumber { diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index aa36475e05..eb81317bd8 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -64,7 +64,7 @@ class CharacterMaker; * * This class isn't exported from this package. */ -class EggLoader { +class EXPCL_PANDA_EGG2PG EggLoader { public: EggLoader(); EggLoader(const EggData *data); diff --git a/panda/src/egg2pg/eggRenderState.h b/panda/src/egg2pg/eggRenderState.h index 44993dffcf..82af78be70 100644 --- a/panda/src/egg2pg/eggRenderState.h +++ b/panda/src/egg2pg/eggRenderState.h @@ -36,7 +36,7 @@ class EggMaterial; * should be assigned to each primitive. It is assigned to EggPrimitive * objects via the EggBinner. */ -class EggRenderState : public EggUserData { +class EXPCL_PANDA_EGG2PG EggRenderState : public EggUserData { public: INLINE EggRenderState(EggLoader &loader); INLINE void add_attrib(const RenderAttrib *attrib); diff --git a/panda/src/egg2pg/eggSaver.h b/panda/src/egg2pg/eggSaver.h index 8041d346cc..48a9693dec 100644 --- a/panda/src/egg2pg/eggSaver.h +++ b/panda/src/egg2pg/eggSaver.h @@ -50,7 +50,7 @@ class EggVertex; * complete (some Panda or egg constructs are not fully supported by this * class). */ -class EggSaver { +class EXPCL_PANDA_EGG2PG EggSaver { PUBLISHED: EggSaver(EggData *data = nullptr); diff --git a/panda/src/grutil/shaderTerrainMesh.I b/panda/src/grutil/shaderTerrainMesh.I index f6a66bc7f5..f4a6b4286e 100644 --- a/panda/src/grutil/shaderTerrainMesh.I +++ b/panda/src/grutil/shaderTerrainMesh.I @@ -22,6 +22,7 @@ * @param filename Heightfield texture */ INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) { + MutexHolder holder(_lock); _heightfield_tex = heightfield; } @@ -33,6 +34,7 @@ INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) { * @return Path to the heightfield */ INLINE Texture* ShaderTerrainMesh::get_heightfield() const { + MutexHolder holder(_lock); return _heightfield_tex; } @@ -54,6 +56,7 @@ INLINE Texture* ShaderTerrainMesh::get_heightfield() const { * @param chunk_size Size of the chunks, has to be a power of two */ INLINE void ShaderTerrainMesh::set_chunk_size(size_t chunk_size) { + MutexHolder holder(_lock); _chunk_size = chunk_size; } @@ -63,6 +66,7 @@ INLINE void ShaderTerrainMesh::set_chunk_size(size_t chunk_size) { * @return Chunk size */ INLINE size_t ShaderTerrainMesh::get_chunk_size() const { + MutexHolder holder(_lock); return _chunk_size; } @@ -81,6 +85,7 @@ INLINE size_t ShaderTerrainMesh::get_chunk_size() const { * @param generate_patches [description] */ INLINE void ShaderTerrainMesh::set_generate_patches(bool generate_patches) { + MutexHolder holder(_lock); _generate_patches = generate_patches; } @@ -92,6 +97,7 @@ INLINE void ShaderTerrainMesh::set_generate_patches(bool generate_patches) { * @return Whether to generate patches */ INLINE bool ShaderTerrainMesh::get_generate_patches() const { + MutexHolder holder(_lock); return _generate_patches; } @@ -107,6 +113,7 @@ INLINE bool ShaderTerrainMesh::get_generate_patches() const { * @param target_triangle_width Desired triangle width in pixels */ INLINE void ShaderTerrainMesh::set_target_triangle_width(PN_stdfloat target_triangle_width) { + MutexHolder holder(_lock); _target_triangle_width = target_triangle_width; } @@ -118,6 +125,7 @@ INLINE void ShaderTerrainMesh::set_target_triangle_width(PN_stdfloat target_tria * @return Target triangle width */ INLINE PN_stdfloat ShaderTerrainMesh::get_target_triangle_width() const { + MutexHolder holder(_lock); return _target_triangle_width; } @@ -131,6 +139,7 @@ INLINE PN_stdfloat ShaderTerrainMesh::get_target_triangle_width() const { * @param update_enabled Whether to update the terrain */ INLINE void ShaderTerrainMesh::set_update_enabled(bool update_enabled) { + MutexHolder holder(_lock); _update_enabled = update_enabled; } @@ -142,6 +151,7 @@ INLINE void ShaderTerrainMesh::set_update_enabled(bool update_enabled) { * @return Whether to update the terrain */ INLINE bool ShaderTerrainMesh::get_update_enabled() const { + MutexHolder holder(_lock); return _update_enabled; } diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index 44c8397279..a0f3c34690 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -122,6 +122,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() : * @return true if the terrain was initialized, false if an error occured */ bool ShaderTerrainMesh::generate() { + MutexHolder holder(_lock); if (!do_check_heightfield()) return false; @@ -461,6 +462,7 @@ bool ShaderTerrainMesh::safe_to_combine() const { * @copydoc PandaNode::add_for_draw() */ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &data) { + MutexHolder holder(_lock); // Make sure the terrain was properly initialized, and the geom was created // successfully @@ -711,6 +713,7 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) { * @return World-Space point */ LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const { + MutexHolder holder(_lock); nassertr(_heightfield_tex != nullptr, LPoint3(0)); // Heightfield not set yet nassertr(_heightfield_tex->has_ram_image(), LPoint3(0)); // Heightfield not in memory diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index c57a5d4e74..9c4f8d9a3a 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -25,6 +25,8 @@ #include "configVariableInt.h" #include "pStatCollector.h" #include "filename.h" +#include "pmutex.h" +#include "mutexHolder.h" #include extern ConfigVariableBool stm_use_hexagonal_layout; @@ -160,6 +162,7 @@ private: void do_emit_chunk(Chunk* chunk, TraversalData* data); bool do_check_lod_matches(Chunk* chunk, TraversalData* data); + Mutex _lock; Chunk _base_chunk; size_t _size; size_t _chunk_size; diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 012deff1cd..51468ac2a4 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -29,6 +29,8 @@ PT(MovieAudio) MovieTypeRegistry:: make_audio(const Filename &name) { string ext = downcase(name.get_extension()); + _audio_lock.lock(); + // Make sure that the list of audio types has been read in. load_audio_types(); @@ -41,6 +43,7 @@ make_audio(const Filename &name) { // Explicit extension is preferred over catch-all. if (_audio_type_registry.count(ext)) { MakeAudioFunc func = _audio_type_registry[ext]; + _audio_lock.unlock(); return (*func)(name); } @@ -53,12 +56,14 @@ make_audio(const Filename &name) { if (_audio_type_registry.count("*")) { MakeAudioFunc func = _audio_type_registry["*"]; + _audio_lock.unlock(); return (*func)(name); } movies_cat.error() << "Support for audio files with extension ." << ext << " was not enabled.\n"; + _audio_lock.unlock(); return new MovieAudio("Load-Failure Stub"); } @@ -68,6 +73,7 @@ make_audio(const Filename &name) { */ void MovieTypeRegistry:: register_audio_type(MakeAudioFunc func, const string &extensions) { + ReMutexHolder holder(_audio_lock); vector_string words; extract_words(downcase(extensions), words); @@ -89,6 +95,7 @@ register_audio_type(MakeAudioFunc func, const string &extensions) { */ void MovieTypeRegistry:: load_audio_types() { + ReMutexHolder holder(_audio_lock); static bool audio_types_loaded = false; if (!audio_types_loaded) { @@ -145,6 +152,8 @@ PT(MovieVideo) MovieTypeRegistry:: make_video(const Filename &name) { string ext = downcase(name.get_extension()); + _video_lock.lock(); + // Make sure that the list of video types has been read in. load_video_types(); @@ -157,6 +166,7 @@ make_video(const Filename &name) { // Explicit extension is preferred over catch-all. if (_video_type_registry.count(ext)) { MakeVideoFunc func = _video_type_registry[ext]; + _video_lock.unlock(); return (*func)(name); } @@ -169,12 +179,14 @@ make_video(const Filename &name) { if (_video_type_registry.count("*")) { MakeVideoFunc func = _video_type_registry["*"]; + _video_lock.unlock(); return (*func)(name); } movies_cat.error() << "Support for video files with extension ." << ext << " was not enabled.\n"; + _video_lock.unlock(); return new MovieVideo("Load-Failure Stub"); } @@ -184,6 +196,7 @@ make_video(const Filename &name) { */ void MovieTypeRegistry:: register_video_type(MakeVideoFunc func, const string &extensions) { + ReMutexHolder holder(_video_lock); vector_string words; extract_words(downcase(extensions), words); @@ -205,6 +218,7 @@ register_video_type(MakeVideoFunc func, const string &extensions) { */ void MovieTypeRegistry:: load_video_types() { + ReMutexHolder holder(_video_lock); static bool video_types_loaded = false; if (!video_types_loaded) { @@ -259,6 +273,7 @@ load_video_types() { */ void MovieTypeRegistry:: load_movie_library(const string &name) { + ReMutexHolder holder(_video_lock); Filename dlname = Filename::dso_filename("lib" + name + ".so"); movies_cat.info() << "loading video type module: " << name << endl; diff --git a/panda/src/movies/movieTypeRegistry.h b/panda/src/movies/movieTypeRegistry.h index ee5fc14da2..c9ddb53f15 100644 --- a/panda/src/movies/movieTypeRegistry.h +++ b/panda/src/movies/movieTypeRegistry.h @@ -19,6 +19,7 @@ #include "movieVideo.h" #include "filename.h" #include "pmap.h" +#include "reMutex.h" /** * This class records the different types of MovieAudio and MovieVideo that @@ -43,9 +44,11 @@ public: private: static MovieTypeRegistry *_global_ptr; + ReMutex _audio_lock; pmap _audio_type_registry; pmap _deferred_audio_types; + ReMutex _video_lock; pmap _video_type_registry; pmap _deferred_video_types; }; diff --git a/panda/src/pgraph/colorAttrib.cxx b/panda/src/pgraph/colorAttrib.cxx index 7bfbecee79..129f5a9184 100644 --- a/panda/src/pgraph/colorAttrib.cxx +++ b/panda/src/pgraph/colorAttrib.cxx @@ -133,17 +133,17 @@ get_hash_impl() const { } /** - * Quantizes the color color to the nearest multiple of 1000, just to prevent + * Quantizes the flat color to the nearest multiple of 1024, just to prevent * runaway accumulation of only slightly-different ColorAttribs. */ void ColorAttrib:: quantize_color() { switch (_type) { case T_flat: - _color[0] = cfloor(_color[0] * 1000.0f + 0.5f) * 0.001f; - _color[1] = cfloor(_color[1] * 1000.0f + 0.5f) * 0.001f; - _color[2] = cfloor(_color[2] * 1000.0f + 0.5f) * 0.001f; - _color[3] = cfloor(_color[3] * 1000.0f + 0.5f) * 0.001f; + _color[0] = cfloor(_color[0] * 1024.0f + 0.5f) / 1024.0f; + _color[1] = cfloor(_color[1] * 1024.0f + 0.5f) / 1024.0f; + _color[2] = cfloor(_color[2] * 1024.0f + 0.5f) / 1024.0f; + _color[3] = cfloor(_color[3] * 1024.0f + 0.5f) / 1024.0f; break; case T_off: diff --git a/panda/src/pgraph/colorScaleAttrib.cxx b/panda/src/pgraph/colorScaleAttrib.cxx index 088c3142ef..04001dd0ab 100644 --- a/panda/src/pgraph/colorScaleAttrib.cxx +++ b/panda/src/pgraph/colorScaleAttrib.cxx @@ -230,15 +230,15 @@ invert_compose_impl(const RenderAttrib *other) const { } /** - * Quantizes the color scale to the nearest multiple of 1000, just to prevent + * Quantizes the color scale to the nearest multiple of 1024, just to prevent * runaway accumulation of only slightly-different ColorScaleAttribs. */ void ColorScaleAttrib:: quantize_scale() { - _scale[0] = cfloor(_scale[0] * 1000.0f + 0.5f) * 0.001f; - _scale[1] = cfloor(_scale[1] * 1000.0f + 0.5f) * 0.001f; - _scale[2] = cfloor(_scale[2] * 1000.0f + 0.5f) * 0.001f; - _scale[3] = cfloor(_scale[3] * 1000.0f + 0.5f) * 0.001f; + _scale[0] = cfloor(_scale[0] * 1024.0f + 0.5f) / 1024.0f; + _scale[1] = cfloor(_scale[1] * 1024.0f + 0.5f) / 1024.0f; + _scale[2] = cfloor(_scale[2] * 1024.0f + 0.5f) / 1024.0f; + _scale[3] = cfloor(_scale[3] * 1024.0f + 0.5f) / 1024.0f; } /** diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index 03ae0e0587..3c04578a55 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -207,7 +207,7 @@ load_file(const Filename &filename, const LoaderOptions &options) const { if (search) { // Look for the file along the model path. - const ConfigVariableSearchPath &model_path = get_model_path(); + DSearchPath model_path(get_model_path()); int num_dirs = model_path.get_num_directories(); for (int i = 0; i < num_dirs; ++i) { Filename pathname(model_path.get_directory(i), this_filename); diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index d954ddcafc..1b08e921b0 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -53,14 +53,22 @@ TypeHandle ShaderGenerator::_type_handle; #ifdef HAVE_CG -#define PACK_COMBINE(src0, op0, src1, op1, src2, op2) ( \ - ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | \ - ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | \ - ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u)) - #define UNPACK_COMBINE_SRC(from, n) (TextureStage::CombineSource)((from >> ((uint16_t)n * 5u)) & 7u) #define UNPACK_COMBINE_OP(from, n) (TextureStage::CombineOperand)(((from >> (((uint16_t)n * 5u) + 3u)) & 3u) + 1u) +static inline uint16_t +pack_combine(TextureStage::CombineSource src0, TextureStage::CombineOperand op0, + TextureStage::CombineSource src1, TextureStage::CombineOperand op1, + TextureStage::CombineSource src2, TextureStage::CombineOperand op2) { + if (op0 == TextureStage::CO_undefined) op0 = TextureStage::CO_src_alpha; + if (op1 == TextureStage::CO_undefined) op1 = TextureStage::CO_src_alpha; + if (op2 == TextureStage::CO_undefined) op2 = TextureStage::CO_src_alpha; + + return ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | + ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | + ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u); +} + static PStatCollector lookup_collector("*:Munge:ShaderGen:Lookup"); static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize"); @@ -399,11 +407,12 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { if (stage->get_alpha_scale() == 4) { info._flags |= ShaderKey::TF_alpha_scale_4; } - info._combine_rgb = PACK_COMBINE( + + info._combine_rgb = pack_combine( stage->get_combine_rgb_source0(), stage->get_combine_rgb_operand0(), stage->get_combine_rgb_source1(), stage->get_combine_rgb_operand1(), stage->get_combine_rgb_source2(), stage->get_combine_rgb_operand2()); - info._combine_alpha = PACK_COMBINE( + info._combine_alpha = pack_combine( stage->get_combine_alpha_source0(), stage->get_combine_alpha_operand0(), stage->get_combine_alpha_source1(), stage->get_combine_alpha_operand1(), stage->get_combine_alpha_source2(), stage->get_combine_alpha_operand2()); diff --git a/panda/src/pipeline/conditionVarSpinlockImpl.cxx b/panda/src/pipeline/conditionVarSpinlockImpl.cxx index d35d8cd1f7..2b4be0d4e7 100644 --- a/panda/src/pipeline/conditionVarSpinlockImpl.cxx +++ b/panda/src/pipeline/conditionVarSpinlockImpl.cxx @@ -58,4 +58,6 @@ wait(double timeout) { _mutex.lock(); } +#undef PAUSE + #endif // MUTEX_SPINLOCK diff --git a/panda/src/pipeline/reMutexSpinlockImpl.cxx b/panda/src/pipeline/reMutexSpinlockImpl.cxx index 0de12f986f..de0b2c0355 100644 --- a/panda/src/pipeline/reMutexSpinlockImpl.cxx +++ b/panda/src/pipeline/reMutexSpinlockImpl.cxx @@ -54,4 +54,6 @@ try_lock() { } } +#undef PAUSE + #endif // MUTEX_SPINLOCK diff --git a/panda/src/text/textNode.I b/panda/src/text/textNode.I index 313685c504..8070504c6b 100644 --- a/panda/src/text/textNode.I +++ b/panda/src/text/textNode.I @@ -33,6 +33,7 @@ get_line_height() const { */ INLINE void TextNode:: set_max_rows(int max_rows) { + MutexHolder holder(_lock); _max_rows = max_rows; invalidate_with_measure(); } @@ -43,6 +44,7 @@ set_max_rows(int max_rows) { */ INLINE void TextNode:: clear_max_rows() { + MutexHolder holder(_lock); _max_rows = 0; invalidate_with_measure(); } @@ -53,6 +55,7 @@ clear_max_rows() { */ INLINE bool TextNode:: has_max_rows() const { + MutexHolder holder(_lock); return _max_rows > 0; } @@ -62,6 +65,7 @@ has_max_rows() const { */ INLINE int TextNode:: get_max_rows() const { + MutexHolder holder(_lock); return _max_rows; } @@ -71,6 +75,7 @@ get_max_rows() const { */ INLINE bool TextNode:: has_overflow() const { + MutexHolder holder(_lock); check_measure(); return (_flags & F_has_overflow) != 0; } @@ -88,6 +93,7 @@ set_frame_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_frame_color(const LColor &frame_color) { + MutexHolder holder(_lock); if (_frame_color != frame_color) { _frame_color = frame_color; invalidate_no_measure(); @@ -99,6 +105,7 @@ set_frame_color(const LColor &frame_color) { */ INLINE LColor TextNode:: get_frame_color() const { + MutexHolder holder(_lock); return _frame_color; } @@ -107,7 +114,8 @@ get_frame_color() const { */ INLINE void TextNode:: set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) { - if (!has_card_border() || _card_border_size != size || _card_border_uv_portion != uv_portion) { + MutexHolder holder(_lock); + if ((_flags & F_has_card_border) == 0 || _card_border_size != size || _card_border_uv_portion != uv_portion) { _flags |= F_has_card_border; _card_border_size = size; _card_border_uv_portion = uv_portion; @@ -120,7 +128,8 @@ set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) { */ INLINE void TextNode:: clear_card_border() { - if (has_card_border()) { + MutexHolder holder(_lock); + if (_flags & F_has_card_border) { _flags &= ~F_has_card_border; invalidate_no_measure(); } @@ -131,6 +140,7 @@ clear_card_border() { */ INLINE PN_stdfloat TextNode:: get_card_border_size() const { + MutexHolder holder(_lock); return _card_border_size; } @@ -139,6 +149,7 @@ get_card_border_size() const { */ INLINE PN_stdfloat TextNode:: get_card_border_uv_portion() const { + MutexHolder holder(_lock); return _card_border_uv_portion; } @@ -147,6 +158,7 @@ get_card_border_uv_portion() const { */ INLINE bool TextNode:: has_card_border() const { + MutexHolder holder(_lock); return (_flags & F_has_card_border) != 0; } @@ -163,6 +175,7 @@ set_card_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_card_color(const LColor &card_color) { + MutexHolder holder(_lock); if (_card_color != card_color) { _card_color = card_color; invalidate_no_measure(); @@ -174,6 +187,7 @@ set_card_color(const LColor &card_color) { */ INLINE LColor TextNode:: get_card_color() const { + MutexHolder holder(_lock); return _card_color; } @@ -185,7 +199,8 @@ set_card_texture(Texture *card_texture) { if (card_texture == nullptr) { clear_card_texture(); } else { - if (!has_card_texture() || _card_texture != card_texture) { + MutexHolder holder(_lock); + if ((_flags & F_has_card_texture) == 0 || _card_texture != card_texture) { _flags |= F_has_card_texture; _card_texture = card_texture; invalidate_no_measure(); @@ -198,7 +213,8 @@ set_card_texture(Texture *card_texture) { */ INLINE void TextNode:: clear_card_texture() { - if (has_card_texture()) { + MutexHolder holder(_lock); + if (_flags & F_has_card_texture) { _flags &= ~F_has_card_texture; _card_texture = nullptr; invalidate_no_measure(); @@ -210,6 +226,7 @@ clear_card_texture() { */ INLINE bool TextNode:: has_card_texture() const { + MutexHolder holder(_lock); return (_flags & F_has_card_texture) != 0; } @@ -218,6 +235,7 @@ has_card_texture() const { */ INLINE Texture *TextNode:: get_card_texture() const { + MutexHolder holder(_lock); return _card_texture; } @@ -229,6 +247,7 @@ get_card_texture() const { */ INLINE void TextNode:: set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= (F_has_frame | F_frame_as_margin); _frame_ul.set(left, top); _frame_lr.set(right, bottom); @@ -243,6 +262,7 @@ set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_ */ INLINE void TextNode:: set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= F_has_frame; _flags &= ~F_frame_as_margin; _frame_ul.set(left, top); @@ -255,6 +275,7 @@ set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_std */ INLINE void TextNode:: clear_frame() { + MutexHolder holder(_lock); _flags &= ~F_has_frame; invalidate_no_measure(); } @@ -264,6 +285,7 @@ clear_frame() { */ INLINE bool TextNode:: has_frame() const { + MutexHolder holder(_lock); return (_flags & F_has_frame) != 0; } @@ -276,7 +298,8 @@ has_frame() const { */ INLINE bool TextNode:: is_frame_as_margin() const { - nassertr(has_frame(), false); + MutexHolder holder(_lock); + nassertr((_flags & F_has_frame) != 0, false); return (_flags & F_frame_as_margin) != 0; } @@ -288,7 +311,8 @@ is_frame_as_margin() const { */ INLINE LVecBase4 TextNode:: get_frame_as_set() const { - nassertr(has_frame(), LVecBase4(0.0, 0.0, 0.0, 0.0)); + MutexHolder holder(_lock); + nassertr((_flags & F_has_frame) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0)); return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]); } @@ -303,18 +327,20 @@ get_frame_as_set() const { */ INLINE LVecBase4 TextNode:: get_frame_actual() const { - if (!has_frame()) { + MutexHolder holder(_lock); + if (_flags & F_has_frame) { + if (_flags & F_frame_as_margin) { + check_measure(); + return LVecBase4(_text_ul[0] - _frame_ul[0], + _text_lr[0] + _frame_lr[0], + _text_lr[1] - _frame_lr[1], + _text_ul[1] + _frame_ul[1]); + } else { + return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]); + } + } else { check_measure(); return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]); - - } else if (is_frame_as_margin()) { - check_measure(); - return LVecBase4(_text_ul[0] - _frame_ul[0], - _text_lr[0] + _frame_lr[0], - _text_lr[1] - _frame_lr[1], - _text_ul[1] + _frame_ul[1]); - } else { - return get_frame_as_set(); } } @@ -323,6 +349,7 @@ get_frame_actual() const { */ INLINE void TextNode:: set_frame_line_width(PN_stdfloat frame_width) { + MutexHolder holder(_lock); _frame_width = frame_width; invalidate_no_measure(); } @@ -332,6 +359,7 @@ set_frame_line_width(PN_stdfloat frame_width) { */ INLINE PN_stdfloat TextNode:: get_frame_line_width() const { + MutexHolder holder(_lock); return _frame_width; } @@ -342,6 +370,7 @@ get_frame_line_width() const { */ INLINE void TextNode:: set_frame_corners(bool corners) { + MutexHolder holder(_lock); if (corners) { _flags |= F_frame_corners; } else { @@ -355,6 +384,7 @@ set_frame_corners(bool corners) { */ INLINE bool TextNode:: get_frame_corners() const { + MutexHolder holder(_lock); return (_flags & F_frame_corners) != 0; } @@ -366,6 +396,7 @@ get_frame_corners() const { */ INLINE void TextNode:: set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= (F_has_card | F_card_as_margin); _card_ul.set(left, top); _card_lr.set(right, bottom); @@ -380,6 +411,7 @@ set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_s */ INLINE void TextNode:: set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= F_has_card; _flags &= ~F_card_as_margin; _card_ul.set(left, top); @@ -394,6 +426,7 @@ set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdf */ INLINE void TextNode:: set_card_decal(bool card_decal) { + MutexHolder holder(_lock); if (card_decal) { _flags |= F_card_decal; } else { @@ -407,6 +440,7 @@ set_card_decal(bool card_decal) { */ INLINE void TextNode:: clear_card() { + MutexHolder holder(_lock); _flags &= ~F_has_card; invalidate_no_measure(); } @@ -416,6 +450,7 @@ clear_card() { */ INLINE bool TextNode:: has_card() const { + MutexHolder holder(_lock); return (_flags & F_has_card) != 0; } @@ -424,6 +459,7 @@ has_card() const { */ INLINE bool TextNode:: get_card_decal() const { + MutexHolder holder(_lock); return (_flags & F_card_decal) != 0; } @@ -436,7 +472,8 @@ get_card_decal() const { */ INLINE bool TextNode:: is_card_as_margin() const { - nassertr(has_card(), false); + MutexHolder holder(_lock); + nassertr((_flags & F_has_card) != 0, false); return (_flags & F_card_as_margin) != 0; } @@ -448,7 +485,8 @@ is_card_as_margin() const { */ INLINE LVecBase4 TextNode:: get_card_as_set() const { - nassertr(has_card(), LVecBase4(0.0, 0.0, 0.0, 0.0)); + MutexHolder holder(_lock); + nassertr((_flags & F_has_card) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0)); return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]); } @@ -463,18 +501,20 @@ get_card_as_set() const { */ INLINE LVecBase4 TextNode:: get_card_actual() const { - if (!has_card()) { + MutexHolder holder(_lock); + if (_flags & F_has_card) { + if (_flags & F_card_as_margin) { + check_measure(); + return LVecBase4(_text_ul[0] - _card_ul[0], + _text_lr[0] + _card_lr[0], + _text_lr[1] - _card_lr[1], + _text_ul[1] + _card_ul[1]); + } else { + return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]); + } + } else { check_measure(); return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]); - - } else if (is_card_as_margin()) { - check_measure(); - return LVecBase4(_text_ul[0] - _card_ul[0], - _text_lr[0] + _card_lr[0], - _text_lr[1] - _card_lr[1], - _text_ul[1] + _card_ul[1]); - } else { - return get_card_as_set(); } } @@ -487,6 +527,8 @@ get_card_actual() const { INLINE LVecBase4 TextNode:: get_card_transformed() const { LVecBase4 card = get_card_actual(); + + MutexHolder holder(_lock); LPoint3 ul = LPoint3(card[0], 0.0, card[3]) * _transform; LPoint3 lr = LPoint3(card[1], 0.0, card[2]) * _transform; @@ -498,6 +540,7 @@ get_card_transformed() const { */ INLINE void TextNode:: set_transform(const LMatrix4 &transform) { + MutexHolder holder(_lock); _transform = transform; invalidate_with_measure(); } @@ -507,6 +550,7 @@ set_transform(const LMatrix4 &transform) { */ INLINE LMatrix4 TextNode:: get_transform() const { + MutexHolder holder(_lock); return _transform; } @@ -515,6 +559,7 @@ get_transform() const { */ INLINE void TextNode:: set_coordinate_system(CoordinateSystem coordinate_system) { + MutexHolder holder(_lock); _coordinate_system = coordinate_system; invalidate_with_measure(); } @@ -524,6 +569,7 @@ set_coordinate_system(CoordinateSystem coordinate_system) { */ INLINE CoordinateSystem TextNode:: get_coordinate_system() const { + MutexHolder holder(_lock); return _coordinate_system; } @@ -535,6 +581,7 @@ get_coordinate_system() const { */ INLINE void TextNode:: set_usage_hint(Geom::UsageHint usage_hint) { + MutexHolder holder(_lock); _usage_hint = usage_hint; invalidate_no_measure(); } @@ -545,6 +592,7 @@ set_usage_hint(Geom::UsageHint usage_hint) { */ INLINE Geom::UsageHint TextNode:: get_usage_hint() const { + MutexHolder holder(_lock); return _usage_hint; } @@ -585,6 +633,7 @@ get_usage_hint() const { */ INLINE void TextNode:: set_flatten_flags(int flatten_flags) { + MutexHolder holder(_lock); _flatten_flags = flatten_flags; } @@ -593,6 +642,7 @@ set_flatten_flags(int flatten_flags) { */ INLINE int TextNode:: get_flatten_flags() const { + MutexHolder holder(_lock); return _flatten_flags; } @@ -602,6 +652,7 @@ get_flatten_flags() const { */ INLINE void TextNode:: set_font(TextFont *font) { + MutexHolder holder(_lock); TextProperties::set_font(font); invalidate_with_measure(); } @@ -611,6 +662,7 @@ set_font(TextFont *font) { */ INLINE void TextNode:: clear_font() { + MutexHolder holder(_lock); TextProperties::clear_font(); invalidate_with_measure(); } @@ -631,6 +683,7 @@ clear_font() { */ INLINE void TextNode:: set_small_caps(bool small_caps) { + MutexHolder holder(_lock); TextProperties::set_small_caps(small_caps); invalidate_with_measure(); } @@ -640,6 +693,7 @@ set_small_caps(bool small_caps) { */ INLINE void TextNode:: clear_small_caps() { + MutexHolder holder(_lock); TextProperties::clear_small_caps(); invalidate_with_measure(); } @@ -651,6 +705,7 @@ clear_small_caps() { */ INLINE void TextNode:: set_small_caps_scale(PN_stdfloat small_caps_scale) { + MutexHolder holder(_lock); TextProperties::set_small_caps_scale(small_caps_scale); invalidate_with_measure(); } @@ -660,6 +715,7 @@ set_small_caps_scale(PN_stdfloat small_caps_scale) { */ INLINE void TextNode:: clear_small_caps_scale() { + MutexHolder holder(_lock); TextProperties::clear_small_caps_scale(); invalidate_with_measure(); } @@ -669,6 +725,7 @@ clear_small_caps_scale() { */ INLINE void TextNode:: set_slant(PN_stdfloat slant) { + MutexHolder holder(_lock); TextProperties::set_slant(slant); invalidate_with_measure(); } @@ -678,6 +735,7 @@ set_slant(PN_stdfloat slant) { */ INLINE void TextNode:: clear_slant() { + MutexHolder holder(_lock); TextProperties::clear_slant(); invalidate_with_measure(); } @@ -687,6 +745,7 @@ clear_slant() { */ INLINE void TextNode:: set_align(TextNode::Alignment align_type) { + MutexHolder holder(_lock); TextProperties::set_align(align_type); invalidate_with_measure(); } @@ -696,6 +755,7 @@ set_align(TextNode::Alignment align_type) { */ INLINE void TextNode:: clear_align() { + MutexHolder holder(_lock); TextProperties::clear_align(); invalidate_with_measure(); } @@ -706,6 +766,7 @@ clear_align() { */ INLINE void TextNode:: set_indent(PN_stdfloat indent) { + MutexHolder holder(_lock); TextProperties::set_indent(indent); invalidate_with_measure(); } @@ -715,6 +776,7 @@ set_indent(PN_stdfloat indent) { */ INLINE void TextNode:: clear_indent() { + MutexHolder holder(_lock); TextProperties::clear_indent(); invalidate_with_measure(); } @@ -725,6 +787,7 @@ clear_indent() { */ INLINE void TextNode:: set_wordwrap(PN_stdfloat wordwrap) { + MutexHolder holder(_lock); TextProperties::set_wordwrap(wordwrap); invalidate_with_measure(); } @@ -735,6 +798,7 @@ set_wordwrap(PN_stdfloat wordwrap) { */ INLINE void TextNode:: clear_wordwrap() { + MutexHolder holder(_lock); TextProperties::clear_wordwrap(); invalidate_with_measure(); } @@ -744,6 +808,7 @@ clear_wordwrap() { */ INLINE void TextNode:: set_text_color(const LColor &text_color) { + MutexHolder holder(_lock); TextProperties::set_text_color(text_color); invalidate_no_measure(); } @@ -762,6 +827,7 @@ set_text_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: clear_text_color() { + MutexHolder holder(_lock); TextProperties::clear_text_color(); invalidate_no_measure(); } @@ -779,6 +845,7 @@ set_shadow_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_shadow_color(const LColor &shadow_color) { + MutexHolder holder(_lock); TextProperties::set_shadow_color(shadow_color); invalidate_no_measure(); } @@ -788,6 +855,7 @@ set_shadow_color(const LColor &shadow_color) { */ INLINE void TextNode:: clear_shadow_color() { + MutexHolder holder(_lock); TextProperties::clear_shadow_color(); invalidate_with_measure(); } @@ -807,6 +875,7 @@ set_shadow(PN_stdfloat xoffset, PN_stdfloat yoffset) { */ INLINE void TextNode:: set_shadow(const LVecBase2 &shadow_offset) { + MutexHolder holder(_lock); TextProperties::set_shadow(shadow_offset); invalidate_no_measure(); } @@ -816,6 +885,7 @@ set_shadow(const LVecBase2 &shadow_offset) { */ INLINE void TextNode:: clear_shadow() { + MutexHolder holder(_lock); TextProperties::clear_shadow(); invalidate_no_measure(); } @@ -831,6 +901,7 @@ clear_shadow() { */ INLINE void TextNode:: set_bin(const std::string &bin) { + MutexHolder holder(_lock); TextProperties::set_bin(bin); invalidate_no_measure(); } @@ -841,6 +912,7 @@ set_bin(const std::string &bin) { */ INLINE void TextNode:: clear_bin() { + MutexHolder holder(_lock); TextProperties::clear_bin(); invalidate_no_measure(); } @@ -858,6 +930,7 @@ clear_bin() { */ INLINE int TextNode:: set_draw_order(int draw_order) { + MutexHolder holder(_lock); invalidate_no_measure(); return TextProperties::set_draw_order(draw_order); } @@ -867,6 +940,7 @@ set_draw_order(int draw_order) { */ INLINE void TextNode:: clear_draw_order() { + MutexHolder holder(_lock); TextProperties::clear_draw_order(); invalidate_with_measure(); } @@ -877,6 +951,7 @@ clear_draw_order() { */ INLINE void TextNode:: set_tab_width(PN_stdfloat tab_width) { + MutexHolder holder(_lock); TextProperties::set_tab_width(tab_width); invalidate_with_measure(); } @@ -886,6 +961,7 @@ set_tab_width(PN_stdfloat tab_width) { */ INLINE void TextNode:: clear_tab_width() { + MutexHolder holder(_lock); TextProperties::clear_tab_width(); invalidate_with_measure(); } @@ -897,6 +973,7 @@ clear_tab_width() { */ INLINE void TextNode:: set_glyph_scale(PN_stdfloat glyph_scale) { + MutexHolder holder(_lock); TextProperties::set_glyph_scale(glyph_scale); invalidate_with_measure(); } @@ -906,6 +983,7 @@ set_glyph_scale(PN_stdfloat glyph_scale) { */ INLINE void TextNode:: clear_glyph_scale() { + MutexHolder holder(_lock); TextProperties::clear_glyph_scale(); invalidate_with_measure(); } @@ -917,6 +995,7 @@ clear_glyph_scale() { */ INLINE void TextNode:: set_glyph_shift(PN_stdfloat glyph_shift) { + MutexHolder holder(_lock); TextProperties::set_glyph_shift(glyph_shift); invalidate_with_measure(); } @@ -926,6 +1005,7 @@ set_glyph_shift(PN_stdfloat glyph_shift) { */ INLINE void TextNode:: clear_glyph_shift() { + MutexHolder holder(_lock); TextProperties::clear_glyph_shift(); invalidate_with_measure(); } @@ -936,6 +1016,7 @@ clear_glyph_shift() { */ INLINE void TextNode:: set_text(const std::string &text) { + MutexHolder holder(_lock); TextEncoder::set_text(text); invalidate_with_measure(); } @@ -948,6 +1029,7 @@ set_text(const std::string &text) { */ INLINE void TextNode:: set_text(const std::string &text, TextNode::Encoding encoding) { + MutexHolder holder(_lock); TextEncoder::set_text(text, encoding); invalidate_with_measure(); } @@ -957,6 +1039,7 @@ set_text(const std::string &text, TextNode::Encoding encoding) { */ INLINE void TextNode:: clear_text() { + MutexHolder holder(_lock); TextEncoder::clear_text(); invalidate_with_measure(); } @@ -966,6 +1049,7 @@ clear_text() { */ INLINE void TextNode:: append_text(const std::string &text) { + MutexHolder holder(_lock); TextEncoder::append_text(text); invalidate_with_measure(); } @@ -976,6 +1060,7 @@ append_text(const std::string &text) { */ INLINE void TextNode:: append_unicode_char(wchar_t character) { + MutexHolder holder(_lock); TextEncoder::append_unicode_char(character); invalidate_with_measure(); } @@ -1008,6 +1093,7 @@ calc_width(const std::string &line) const { */ INLINE void TextNode:: set_wtext(const std::wstring &wtext) { + MutexHolder holder(_lock); TextEncoder::set_wtext(wtext); invalidate_with_measure(); } @@ -1017,6 +1103,7 @@ set_wtext(const std::wstring &wtext) { */ INLINE void TextNode:: append_wtext(const std::wstring &wtext) { + MutexHolder holder(_lock); TextEncoder::append_wtext(wtext); invalidate_with_measure(); } @@ -1030,6 +1117,7 @@ append_wtext(const std::wstring &wtext) { */ INLINE std::wstring TextNode:: get_wordwrapped_wtext() const { + MutexHolder holder(_lock); check_measure(); return _wordwrapped_wtext; } @@ -1040,6 +1128,7 @@ get_wordwrapped_wtext() const { */ INLINE PN_stdfloat TextNode:: get_left() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[0]; } @@ -1050,6 +1139,7 @@ get_left() const { */ INLINE PN_stdfloat TextNode:: get_right() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[0]; } @@ -1060,6 +1150,7 @@ get_right() const { */ INLINE PN_stdfloat TextNode:: get_bottom() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[1]; } @@ -1070,6 +1161,7 @@ get_bottom() const { */ INLINE PN_stdfloat TextNode:: get_top() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[1]; } @@ -1079,6 +1171,7 @@ get_top() const { */ INLINE PN_stdfloat TextNode:: get_height() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[1] - _text_lr[1]; } @@ -1088,6 +1181,7 @@ get_height() const { */ INLINE PN_stdfloat TextNode:: get_width() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[0] - _text_ul[0]; } @@ -1098,6 +1192,7 @@ get_width() const { */ INLINE LPoint3 TextNode:: get_upper_left_3d() const { + MutexHolder holder(_lock); check_measure(); return _ul3d; } @@ -1108,6 +1203,7 @@ get_upper_left_3d() const { */ INLINE LPoint3 TextNode:: get_lower_right_3d() const { + MutexHolder holder(_lock); check_measure(); return _lr3d; } @@ -1118,10 +1214,22 @@ get_lower_right_3d() const { */ INLINE int TextNode:: get_num_rows() const { + MutexHolder holder(_lock); check_measure(); return _num_rows; } +/** + * Generates the text, according to the parameters indicated within the + * TextNode, and returns a Node that may be parented within the tree to + * represent it. + */ +PT(PandaNode) TextNode:: +generate() { + MutexHolder holder(_lock); + return do_generate(); +} + /** * Can be called after the TextNode has been fully configured, to force the * node to recompute its text immediately, rather than waiting for it to be @@ -1129,6 +1237,7 @@ get_num_rows() const { */ INLINE void TextNode:: update() { + MutexHolder holder(_lock); check_rebuild(); } @@ -1140,8 +1249,9 @@ update() { */ INLINE void TextNode:: force_update() { - invalidate_with_measure(); - check_rebuild(); + MutexHolder holder(_lock); + mark_internal_bounds_stale(); + do_rebuild(); } /** diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index 92001d7b40..1ecfc1ea65 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -74,7 +74,7 @@ TextNode(const string &name) : PandaNode(name) { } if (text_small_caps) { - set_small_caps(true); + TextProperties::set_small_caps(true); } _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f); @@ -255,11 +255,16 @@ is_whitespace(wchar_t character) const { */ PN_stdfloat TextNode:: calc_width(const std::wstring &line) const { + TextFont *font = get_font(); + if (font == nullptr) { + return 0.0f; + } + PN_stdfloat width = 0.0f; std::wstring::const_iterator si; for (si = line.begin(); si != line.end(); ++si) { - width += calc_width(*si); + width += TextAssembler::calc_width(*si, *this); } return width; @@ -272,10 +277,10 @@ void TextNode:: output(std::ostream &out) const { PandaNode::output(out); - check_rebuild(); + PT(PandaNode) internal_geom = do_get_internal_geom(); int geom_count = 0; - if (_internal_geom != nullptr) { - geom_count = count_geoms(_internal_geom); + if (internal_geom != nullptr) { + geom_count = count_geoms(internal_geom); } out << " (" << geom_count << " geoms)"; @@ -286,6 +291,7 @@ output(std::ostream &out) const { */ void TextNode:: write(std::ostream &out, int indent_level) const { + MutexHolder holder(_lock); PandaNode::write(out, indent_level); TextProperties::write(out, indent_level + 2); indent(out, indent_level + 2) @@ -296,13 +302,263 @@ write(std::ostream &out, int indent_level) const { << "text is " << get_text() << "\n"; } +/** + * Returns the actual node that is used internally to render the text, if the + * TextNode is parented within the scene graph. + * + * In general, you should not call this method. Call generate() instead if + * you want to get a handle to geometry that represents the text. This method + * is provided as a debugging aid only. + */ +PT(PandaNode) TextNode:: +get_internal_geom() const { + // Output a nuisance warning to discourage the naive from calling this + // method accidentally. + text_cat.info() + << "TextNode::get_internal_geom() called.\n"; + return do_get_internal_geom(); +} + +/** + * Returns the union of all attributes from SceneGraphReducer::AttribTypes + * that may not safely be applied to the vertices of this node. If this is + * nonzero, these attributes must be dropped at this node as a state change. + * + * This is a generalization of safe_to_transform(). + */ +int TextNode:: +get_unsafe_to_apply_attribs() const { + // We have no way to apply these kinds of attributes to our TextNode, so + // insist they get dropped into the PandaNode's basic state. + return + SceneGraphReducer::TT_tex_matrix | + SceneGraphReducer::TT_other; +} + +/** + * Applies whatever attributes are specified in the AccumulatedAttribs object + * (and by the attrib_types bitmask) to the vertices on this node, if + * appropriate. If this node uses geom arrays like a GeomNode, the supplied + * GeomTransformer may be used to unify shared arrays across multiple + * different nodes. + * + * This is a generalization of xform(). + */ +void TextNode:: +apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, + GeomTransformer &transformer) { + MutexHolder holder(_lock); + if ((attrib_types & SceneGraphReducer::TT_transform) != 0) { + const LMatrix4 &mat = attribs._transform->get_mat(); + _transform *= mat; + + if ((_flags & F_needs_measure) == 0) { + // If we already have a measure, transform it too. We don't need to + // invalidate the 2-d parts, since that's not affected by the transform + // anyway. + _ul3d = _ul3d * mat; + _lr3d = _lr3d * mat; + } + } + if ((attrib_types & SceneGraphReducer::TT_color) != 0) { + if (attribs._color != nullptr) { + const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color); + if (ca->get_color_type() == ColorAttrib::T_flat) { + const LColor &c = ca->get_color(); + TextProperties::set_text_color(c); + TextProperties::set_shadow_color(c); + _frame_color = c; + _card_color = c; + invalidate_no_measure(); + } + } + } + if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) { + if (attribs._color_scale != nullptr) { + const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale); + const LVecBase4 &s = csa->get_scale(); + if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) { + LVecBase4 tc = get_text_color(); + tc.componentwise_mult(s); + TextProperties::set_text_color(tc); + + LVecBase4 sc = get_shadow_color(); + sc.componentwise_mult(s); + TextProperties::set_shadow_color(sc); + + _frame_color.componentwise_mult(s); + _card_color.componentwise_mult(s); + + invalidate_no_measure(); + } + } + } + + // Now propagate the attributes down to our already-generated geometry, if + // we have any. + if ((_flags & F_needs_rebuild) == 0 && + _internal_geom != nullptr) { + SceneGraphReducer gr; + gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer); + } +} + +/** + * This is used to support NodePath::calc_tight_bounds(). It is not intended + * to be called directly, and it has nothing to do with the normal Panda + * bounding-volume computation. + * + * If the node contains any geometry, this updates min_point and max_point to + * enclose its bounding box. found_any is to be set true if the node has any + * geometry at all, or left alone if it has none. This method may be called + * over several nodes, so it may enter with min_point, max_point, and + * found_any already set. + */ +CPT(TransformState) TextNode:: +calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, + const TransformState *transform, Thread *current_thread) const { + CPT(TransformState) next_transform = + PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, + current_thread); + + PT(PandaNode) geom = do_get_internal_geom(); + if (geom != nullptr) { + geom->calc_tight_bounds(min_point, max_point, + found_any, next_transform, current_thread); + } + + return next_transform; +} + +/** + * This function will be called during the cull traversal to perform any + * additional operations that should be performed at cull time. This may + * include additional manipulation of render state or additional + * visible/invisible decisions, or any other arbitrary operation. + * + * Note that this function will *not* be called unless set_cull_callback() is + * called in the constructor of the derived class. It is necessary to call + * set_cull_callback() to indicated that we require cull_callback() to be + * called. + * + * By the time this function is called, the node has already passed the + * bounding-volume test for the viewing frustum, and the node's transform and + * state have already been applied to the indicated CullTraverserData object. + * + * The return value is true if this node should be visible, or false if it + * should be culled. + */ +bool TextNode:: +cull_callback(CullTraverser *trav, CullTraverserData &data) { + + PT(PandaNode) internal_geom = do_get_internal_geom(); + if (internal_geom != nullptr) { + // Render the text with this node. + CullTraverserData next_data(data, internal_geom); + trav->traverse(next_data); + } + + // Now continue to render everything else below this node. + return true; +} + +/** + * Returns true if there is some value to visiting this particular node during + * the cull traversal for any camera, false otherwise. This will be used to + * optimize the result of get_net_draw_show_mask(), so that any subtrees that + * contain only nodes for which is_renderable() is false need not be visited. + */ +bool TextNode:: +is_renderable() const { + return true; +} + +/** + * Called when needed to recompute the node's _internal_bound object. Nodes + * that contain anything of substance should redefine this to do the right + * thing. + */ +void TextNode:: +compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const { + // First, get ourselves a fresh, empty bounding volume. + PT(BoundingVolume) bound = new BoundingSphere; + + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound); + + // Now enclose the bounding box around the text. We can do this without + // actually generating the text, if we have at least measured it. + LPoint3 vertices[8]; + { + MutexHolder holder(_lock); + check_measure(); + + vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]); + vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]); + vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]); + vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]); + vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]); + vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]); + vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]); + vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]); + } + + gbv->around(vertices, vertices + 8); + + internal_bounds = bound; + internal_vertices = 0; // TODO: estimate this better. +} + +/** + * The recursive implementation of prepare_scene(). Don't call this directly; + * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead. + */ +void TextNode:: +r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, + GeomTransformer &transformer, Thread *current_thread) { + + PT(PandaNode) child = do_get_internal_geom(); + if (child != nullptr) { + CPT(RenderState) child_state = node_state->compose(child->get_state()); + child->r_prepare_scene(gsg, child_state, transformer, current_thread); + } + + PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread); +} + +/** + * Removes any existing children of the TextNode, and adds the newly generated + * text instead. + */ +void TextNode:: +do_rebuild() { + nassertv(_lock.debug_is_locked()); + _flags &= ~(F_needs_rebuild | F_needs_measure); + _internal_geom = do_generate(); +} + + +/** + * Can be called in lieu of do_rebuild() to measure the text and set up the + * bounding boxes properly without actually assembling it. + */ +void TextNode:: +do_measure() { + // We no longer make this a special case. + do_rebuild(); +} + /** * Generates the text, according to the parameters indicated within the * TextNode, and returns a Node that may be parented within the tree to * represent it. */ PT(PandaNode) TextNode:: -generate() { +do_generate() { + nassertr(_lock.debug_is_locked(), nullptr); + PStatTimer timer(_text_generate_pcollector); if (text_cat.is_debug()) { text_cat.debug() @@ -403,20 +659,20 @@ generate() { // Now deal with the decorations. - if (has_card()) { + if (_flags & F_has_card) { PT(PandaNode) card_root; - if (has_card_border()) { + if (_flags & F_has_card_border) { card_root = make_card_with_border(); } else { card_root = make_card(); } card_root->set_transform(transform); - card_root->set_attrib(ColorAttrib::make_flat(get_card_color())); - if (get_card_color()[3] != 1.0f) { + card_root->set_attrib(ColorAttrib::make_flat(_card_color)); + if (_card_color[3] != 1.0f) { card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); } - if (has_card_texture()) { - card_root->set_attrib(TextureAttrib::make(get_card_texture())); + if (_flags & F_has_card_texture) { + card_root->set_attrib(TextureAttrib::make(_card_texture)); } if (has_bin()) { @@ -432,17 +688,17 @@ generate() { card_root->add_child(root); root = card_root; - if (get_card_decal()) { + if (_flags & F_card_decal) { card_root->set_effect(DecalEffect::make()); } } - if (has_frame()) { + if (_flags & F_has_frame) { PT(PandaNode) frame_root = make_frame(); frame_root->set_transform(transform); root->add_child(frame_root, get_draw_order() + 1); - frame_root->set_attrib(ColorAttrib::make_flat(get_frame_color())); - if (get_frame_color()[3] != 1.0f) { + frame_root->set_attrib(ColorAttrib::make_flat(_frame_color)); + if (_frame_color[3] != 1.0f) { frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); } @@ -460,285 +716,50 @@ generate() { /** * Returns the actual node that is used internally to render the text, if the * TextNode is parented within the scene graph. - * - * In general, you should not call this method. Call generate() instead if - * you want to get a handle to geometry that represents the text. This method - * is provided as a debugging aid only. */ -PandaNode *TextNode:: -get_internal_geom() const { - // Output a nuisance warning to discourage the naive from calling this - // method accidentally. - text_cat.info() - << "TextNode::get_internal_geom() called.\n"; +PT(PandaNode) TextNode:: +do_get_internal_geom() const { + MutexHolder holder(_lock); check_rebuild(); return _internal_geom; } -/** - * Returns the union of all attributes from SceneGraphReducer::AttribTypes - * that may not safely be applied to the vertices of this node. If this is - * nonzero, these attributes must be dropped at this node as a state change. - * - * This is a generalization of safe_to_transform(). - */ -int TextNode:: -get_unsafe_to_apply_attribs() const { - // We have no way to apply these kinds of attributes to our TextNode, so - // insist they get dropped into the PandaNode's basic state. - return - SceneGraphReducer::TT_tex_matrix | - SceneGraphReducer::TT_other; -} - -/** - * Applies whatever attributes are specified in the AccumulatedAttribs object - * (and by the attrib_types bitmask) to the vertices on this node, if - * appropriate. If this node uses geom arrays like a GeomNode, the supplied - * GeomTransformer may be used to unify shared arrays across multiple - * different nodes. - * - * This is a generalization of xform(). - */ -void TextNode:: -apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, - GeomTransformer &transformer) { - if ((attrib_types & SceneGraphReducer::TT_transform) != 0) { - const LMatrix4 &mat = attribs._transform->get_mat(); - _transform *= mat; - - if ((_flags & F_needs_measure) == 0) { - // If we already have a measure, transform it too. We don't need to - // invalidate the 2-d parts, since that's not affected by the transform - // anyway. - _ul3d = _ul3d * mat; - _lr3d = _lr3d * mat; - } - } - if ((attrib_types & SceneGraphReducer::TT_color) != 0) { - if (attribs._color != nullptr) { - const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color); - if (ca->get_color_type() == ColorAttrib::T_flat) { - const LColor &c = ca->get_color(); - set_text_color(c); - set_frame_color(c); - set_card_color(c); - set_shadow_color(c); - } - } - } - if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) { - if (attribs._color_scale != nullptr) { - const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale); - const LVecBase4 &s = csa->get_scale(); - if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) { - LVecBase4 tc = get_text_color(); - tc[0] *= s[0]; - tc[1] *= s[1]; - tc[2] *= s[2]; - tc[3] *= s[3]; - set_text_color(tc); - LVecBase4 sc = get_shadow_color(); - sc[0] *= s[0]; - sc[1] *= s[1]; - sc[2] *= s[2]; - sc[3] *= s[3]; - set_shadow_color(sc); - LVecBase4 fc = get_frame_color(); - fc[0] *= s[0]; - fc[1] *= s[1]; - fc[2] *= s[2]; - fc[3] *= s[3]; - set_frame_color(fc); - LVecBase4 cc = get_card_color(); - cc[0] *= s[0]; - cc[1] *= s[1]; - cc[2] *= s[2]; - cc[3] *= s[3]; - set_card_color(cc); - } - } - } - - // Now propagate the attributes down to our already-generated geometry, if - // we have any. - if ((_flags & F_needs_rebuild) == 0 && - _internal_geom != nullptr) { - SceneGraphReducer gr; - gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer); - } -} - -/** - * This is used to support NodePath::calc_tight_bounds(). It is not intended - * to be called directly, and it has nothing to do with the normal Panda - * bounding-volume computation. - * - * If the node contains any geometry, this updates min_point and max_point to - * enclose its bounding box. found_any is to be set true if the node has any - * geometry at all, or left alone if it has none. This method may be called - * over several nodes, so it may enter with min_point, max_point, and - * found_any already set. - */ -CPT(TransformState) TextNode:: -calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, - const TransformState *transform, Thread *current_thread) const { - CPT(TransformState) next_transform = - PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, - current_thread); - - check_rebuild(); - - if (_internal_geom != nullptr) { - _internal_geom->calc_tight_bounds(min_point, max_point, - found_any, next_transform, current_thread); - } - - return next_transform; -} - -/** - * This function will be called during the cull traversal to perform any - * additional operations that should be performed at cull time. This may - * include additional manipulation of render state or additional - * visible/invisible decisions, or any other arbitrary operation. - * - * Note that this function will *not* be called unless set_cull_callback() is - * called in the constructor of the derived class. It is necessary to call - * set_cull_callback() to indicated that we require cull_callback() to be - * called. - * - * By the time this function is called, the node has already passed the - * bounding-volume test for the viewing frustum, and the node's transform and - * state have already been applied to the indicated CullTraverserData object. - * - * The return value is true if this node should be visible, or false if it - * should be culled. - */ -bool TextNode:: -cull_callback(CullTraverser *trav, CullTraverserData &data) { - check_rebuild(); - if (_internal_geom != nullptr) { - // Render the text with this node. - CullTraverserData next_data(data, _internal_geom); - trav->traverse(next_data); - } - - // Now continue to render everything else below this node. - return true; -} - -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool TextNode:: -is_renderable() const { - return true; -} - -/** - * Called when needed to recompute the node's _internal_bound object. Nodes - * that contain anything of substance should redefine this to do the right - * thing. - */ -void TextNode:: -compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, - int &internal_vertices, - int pipeline_stage, - Thread *current_thread) const { - // First, get ourselves a fresh, empty bounding volume. - PT(BoundingVolume) bound = new BoundingSphere; - - GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound); - - // Now enclose the bounding box around the text. We can do this without - // actually generating the text, if we have at least measured it. - check_measure(); - - LPoint3 vertices[8]; - vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]); - vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]); - vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]); - vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]); - vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]); - vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]); - vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]); - vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]); - - gbv->around(vertices, vertices + 8); - - internal_bounds = bound; - internal_vertices = 0; // TODO: estimate this better. -} - -/** - * The recursive implementation of prepare_scene(). Don't call this directly; - * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead. - */ -void TextNode:: -r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, - GeomTransformer &transformer, Thread *current_thread) { - check_rebuild(); - - PandaNode *child = _internal_geom; - if (child != nullptr) { - CPT(RenderState) child_state = node_state->compose(child->get_state()); - child->r_prepare_scene(gsg, child_state, transformer, current_thread); - } - - PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread); -} - -/** - * Removes any existing children of the TextNode, and adds the newly generated - * text instead. - */ -void TextNode:: -do_rebuild() { - _flags &= ~(F_needs_rebuild | F_needs_measure); - _internal_geom = generate(); -} - - -/** - * Can be called in lieu of do_rebuild() to measure the text and set up the - * bounding boxes properly without actually assembling it. - */ -void TextNode:: -do_measure() { - // We no longer make this a special case. - do_rebuild(); -} - /** * Creates a frame around the text. */ PT(PandaNode) TextNode:: make_frame() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) frame_node = new GeomNode("frame"); - LVector4 dimensions = get_frame_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _frame_ul[0]; + PN_stdfloat right = _frame_lr[0]; + PN_stdfloat bottom = _frame_lr[1]; + PN_stdfloat top = _frame_ul[1]; + + if (_flags & F_frame_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width); CPT(RenderState) state = RenderState::make(thick); PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3(), _usage_hint); + vdata->unclean_set_num_rows(4); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); - vertex.add_data3(left, 0.0f, top); - vertex.add_data3(left, 0.0f, bottom); - vertex.add_data3(right, 0.0f, bottom); - vertex.add_data3(right, 0.0f, top); + vertex.set_data3(left, 0.0f, top); + vertex.set_data3(left, 0.0f, bottom); + vertex.set_data3(right, 0.0f, bottom); + vertex.set_data3(right, 0.0f, top); - PT(GeomLinestrips) frame = new GeomLinestrips(get_usage_hint()); + PT(GeomLinestrips) frame = new GeomLinestrips(_usage_hint); frame->add_consecutive_vertices(0, 4); frame->add_vertex(0); frame->close_primitive(); @@ -747,8 +768,8 @@ make_frame() { geom->add_primitive(frame); frame_node->add_geom(geom, state); - if (get_frame_corners()) { - PT(GeomPoints) corners = new GeomPoints(get_usage_hint()); + if (_flags & F_frame_corners) { + PT(GeomPoints) corners = new GeomPoints(_usage_hint); corners->add_consecutive_vertices(0, 4); PT(Geom) geom2 = new Geom(vdata); geom2->add_primitive(corners); @@ -763,30 +784,40 @@ make_frame() { */ PT(PandaNode) TextNode:: make_card() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) card_node = new GeomNode("card"); - LVector4 dimensions = get_card_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _card_ul[0]; + PN_stdfloat right = _card_lr[0]; + PN_stdfloat bottom = _card_lr[1]; + PN_stdfloat top = _card_ul[1]; + + if (_flags & F_card_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3t2(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3t2(), _usage_hint); + vdata->unclean_set_num_rows(4); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); - vertex.add_data3(left, 0.0f, top); - vertex.add_data3(left, 0.0f, bottom); - vertex.add_data3(right, 0.0f, top); - vertex.add_data3(right, 0.0f, bottom); + vertex.set_data3(left, 0.0f, top); + vertex.set_data3(left, 0.0f, bottom); + vertex.set_data3(right, 0.0f, top); + vertex.set_data3(right, 0.0f, bottom); - texcoord.add_data2(0.0f, 1.0f); - texcoord.add_data2(0.0f, 0.0f); - texcoord.add_data2(1.0f, 1.0f); - texcoord.add_data2(1.0f, 0.0f); + texcoord.set_data2(0.0f, 1.0f); + texcoord.set_data2(0.0f, 0.0f); + texcoord.set_data2(1.0f, 1.0f); + texcoord.set_data2(1.0f, 0.0f); - PT(GeomTristrips) card = new GeomTristrips(get_usage_hint()); + PT(GeomTristrips) card = new GeomTristrips(_usage_hint); card->add_consecutive_vertices(0, 4); card->close_primitive(); @@ -805,13 +836,22 @@ make_card() { */ PT(PandaNode) TextNode:: make_card_with_border() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) card_node = new GeomNode("card"); - LVector4 dimensions = get_card_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _card_ul[0]; + PN_stdfloat right = _card_lr[0]; + PN_stdfloat bottom = _card_lr[1]; + PN_stdfloat top = _card_ul[1]; + + if (_flags & F_card_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } /* * we now create three tri-strips instead of one with vertices arranged as @@ -820,57 +860,59 @@ make_card_with_border() { */ PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3t2(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3t2(), _usage_hint); + vdata->unclean_set_num_rows(16); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); // verts 1,2,3,4 - vertex.add_data3(left, 0.02, top); - vertex.add_data3(left, 0.02, top - _card_border_size); - vertex.add_data3(left + _card_border_size, 0.02, top); - vertex.add_data3(left + _card_border_size, 0.02, + vertex.set_data3(left, 0.02, top); + vertex.set_data3(left, 0.02, top - _card_border_size); + vertex.set_data3(left + _card_border_size, 0.02, top); + vertex.set_data3(left + _card_border_size, 0.02, top - _card_border_size); // verts 5,6,7,8 - vertex.add_data3(right - _card_border_size, 0.02, top); - vertex.add_data3(right - _card_border_size, 0.02, + vertex.set_data3(right - _card_border_size, 0.02, top); + vertex.set_data3(right - _card_border_size, 0.02, top - _card_border_size); - vertex.add_data3(right, 0.02, top); - vertex.add_data3(right, 0.02, top - _card_border_size); + vertex.set_data3(right, 0.02, top); + vertex.set_data3(right, 0.02, top - _card_border_size); // verts 9,10,11,12 - vertex.add_data3(left, 0.02, bottom + _card_border_size); - vertex.add_data3(left, 0.02, bottom); - vertex.add_data3(left + _card_border_size, 0.02, + vertex.set_data3(left, 0.02, bottom + _card_border_size); + vertex.set_data3(left, 0.02, bottom); + vertex.set_data3(left + _card_border_size, 0.02, bottom + _card_border_size); - vertex.add_data3(left + _card_border_size, 0.02, bottom); + vertex.set_data3(left + _card_border_size, 0.02, bottom); // verts 13,14,15,16 - vertex.add_data3(right - _card_border_size, 0.02, + vertex.set_data3(right - _card_border_size, 0.02, bottom + _card_border_size); - vertex.add_data3(right - _card_border_size, 0.02, bottom); - vertex.add_data3(right, 0.02, bottom + _card_border_size); - vertex.add_data3(right, 0.02, bottom); + vertex.set_data3(right - _card_border_size, 0.02, bottom); + vertex.set_data3(right, 0.02, bottom + _card_border_size); + vertex.set_data3(right, 0.02, bottom); - texcoord.add_data2(0.0f, 1.0f); //1 - texcoord.add_data2(0.0f, 1.0f - _card_border_uv_portion); //2 - texcoord.add_data2(0.0f + _card_border_uv_portion, 1.0f); //3 - texcoord.add_data2(0.0f + _card_border_uv_portion, + texcoord.set_data2(0.0f, 1.0f); //1 + texcoord.set_data2(0.0f, 1.0f - _card_border_uv_portion); //2 + texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f); //3 + texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f - _card_border_uv_portion); //4 - texcoord.add_data2(1.0f -_card_border_uv_portion, 1.0f); //5 - texcoord.add_data2(1.0f -_card_border_uv_portion, + texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f); //5 + texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f - _card_border_uv_portion); //6 - texcoord.add_data2(1.0f, 1.0f); //7 - texcoord.add_data2(1.0f, 1.0f - _card_border_uv_portion); //8 + texcoord.set_data2(1.0f, 1.0f); //7 + texcoord.set_data2(1.0f, 1.0f - _card_border_uv_portion); //8 - texcoord.add_data2(0.0f, _card_border_uv_portion); //9 - texcoord.add_data2(0.0f, 0.0f); //10 - texcoord.add_data2(_card_border_uv_portion, _card_border_uv_portion); //11 - texcoord.add_data2(_card_border_uv_portion, 0.0f); //12 + texcoord.set_data2(0.0f, _card_border_uv_portion); //9 + texcoord.set_data2(0.0f, 0.0f); //10 + texcoord.set_data2(_card_border_uv_portion, _card_border_uv_portion); //11 + texcoord.set_data2(_card_border_uv_portion, 0.0f); //12 - texcoord.add_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13 - texcoord.add_data2(1.0f - _card_border_uv_portion, 0.0f);//14 - texcoord.add_data2(1.0f, _card_border_uv_portion);//15 - texcoord.add_data2(1.0f, 0.0f);//16 + texcoord.set_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13 + texcoord.set_data2(1.0f - _card_border_uv_portion, 0.0f);//14 + texcoord.set_data2(1.0f, _card_border_uv_portion);//15 + texcoord.set_data2(1.0f, 0.0f);//16 - PT(GeomTristrips) card = new GeomTristrips(get_usage_hint()); + PT(GeomTristrips) card = new GeomTristrips(_usage_hint); + card->reserve_num_vertices(24); // tristrip #1 card->add_consecutive_vertices(0, 8); diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index 4f37f806f4..ecf7dcafcf 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -24,6 +24,8 @@ #include "pandaNode.h" #include "luse.h" #include "geom.h" +#include "pmutex.h" +#include "mutexHolder.h" /** * The primary interface to this module. This class does basic text assembly; @@ -225,11 +227,11 @@ PUBLISHED: INLINE int get_num_rows() const; - PT(PandaNode) generate(); + INLINE PT(PandaNode) generate(); INLINE void update(); INLINE void force_update(); - PandaNode *get_internal_geom() const; + PT(PandaNode) get_internal_geom() const; PUBLISHED: MAKE_PROPERTY(max_rows, get_max_rows, set_max_rows); @@ -312,12 +314,16 @@ private: void do_rebuild(); void do_measure(); + PT(PandaNode) do_generate(); + PT(PandaNode) do_get_internal_geom() const; + PT(PandaNode) make_frame(); PT(PandaNode) make_card(); PT(PandaNode) make_card_with_border(); static int count_geoms(PandaNode *node); + Mutex _lock; PT(PandaNode) _internal_geom; PT(Texture) _card_texture; diff --git a/samples/shader-terrain/main.py b/samples/shader-terrain/main.py index 92ff61467a..10bffb3767 100644 --- a/samples/shader-terrain/main.py +++ b/samples/shader-terrain/main.py @@ -34,7 +34,10 @@ class ShaderTerrainDemo(ShowBase): # Set a heightfield, the heightfield should be a 16-bit png and # have a quadratic size of a power of two. - self.terrain_node.heightfield = self.loader.loadTexture("heightfield.png") + heightfield = self.loader.loadTexture("heightfield.png") + heightfield.wrap_u = SamplerState.WM_clamp + heightfield.wrap_v = SamplerState.WM_clamp + self.terrain_node.heightfield = heightfield # Set the target triangle width. For a value of 10.0 for example, # the terrain will attempt to make every triangle 10 pixels wide on screen. diff --git a/tests/interrogate/test_property.py b/tests/interrogate/test_property.py index e39f45f9dc..4d521d03e5 100755 --- a/tests/interrogate/test_property.py +++ b/tests/interrogate/test_property.py @@ -2,7 +2,11 @@ import sys import pytest from panda3d import core from contextlib import contextmanager -import collections + +if sys.version_info >= (3, 3): + import collections.abc as collections_abc +else: + import _abcoll as collections_abc @contextmanager @@ -52,7 +56,6 @@ def test_property2(): # The next tests are for MAKE_SEQ_PROPERTY. -@pytest.fixture def seq_property(*items): """ Returns a sequence property initialized with the given items. """ @@ -73,11 +76,11 @@ item_c = core.CollisionSphere((0, 0, 0), 3) def test_seq_property_abc(): prop = seq_property() - assert isinstance(prop, collections.Container) - assert isinstance(prop, collections.Sized) - assert isinstance(prop, collections.Iterable) - assert isinstance(prop, collections.MutableSequence) - assert isinstance(prop, collections.Sequence) + assert isinstance(prop, collections_abc.Container) + assert isinstance(prop, collections_abc.Sized) + assert isinstance(prop, collections_abc.Iterable) + assert isinstance(prop, collections_abc.MutableSequence) + assert isinstance(prop, collections_abc.Sequence) def test_seq_property_empty(): @@ -411,7 +414,6 @@ def test_seq_property_extend(): # The next tests are for MAKE_MAP_PROPERTY. -@pytest.fixture def map_property(**items): """ Returns a mapping property initialized with the given values. """ @@ -425,11 +427,11 @@ def map_property(**items): def test_map_property_abc(): prop = map_property() - assert isinstance(prop, collections.Container) - assert isinstance(prop, collections.Sized) - assert isinstance(prop, collections.Iterable) - assert isinstance(prop, collections.MutableMapping) - assert isinstance(prop, collections.Mapping) + assert isinstance(prop, collections_abc.Container) + assert isinstance(prop, collections_abc.Sized) + assert isinstance(prop, collections_abc.Iterable) + assert isinstance(prop, collections_abc.MutableMapping) + assert isinstance(prop, collections_abc.Mapping) def test_map_property_empty(): @@ -607,19 +609,19 @@ def test_map_property_update(): def test_map_property_keys(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.keys(), collections.MappingView) + assert isinstance(prop.keys(), collections_abc.MappingView) assert frozenset(prop.keys()) == frozenset(('key', 'key2')) def test_map_property_values(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.values(), collections.ValuesView) + assert isinstance(prop.values(), collections_abc.ValuesView) assert frozenset(prop.values()) == frozenset(('value', 'value2')) def test_map_property_items(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.items(), collections.MappingView) + assert isinstance(prop.items(), collections_abc.MappingView) assert frozenset(prop.items()) == frozenset((('key', 'value'), ('key2', 'value2'))) diff --git a/tests/text/test_textnode.py b/tests/text/test_textnode.py new file mode 100644 index 0000000000..e96c9e5371 --- /dev/null +++ b/tests/text/test_textnode.py @@ -0,0 +1,106 @@ +from panda3d import core + + +def test_textnode_card_as_margin(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_card_as_margin(l, r, b, t) + + assert text.has_card() + assert text.is_card_as_margin() + assert text.get_card_as_set() == (l, r, b, t) + + card_actual = text.get_card_actual() + card_expect = core.LVecBase4( + text.get_left() - l, + text.get_right() + r, + text.get_bottom() - b, + text.get_top() + t) + assert card_actual == card_expect + + +def test_textnode_card_actual(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_card_actual(l, r, b, t) + + assert text.has_card() + assert not text.is_card_as_margin() + assert text.get_card_as_set() == (l, r, b, t) + + card_actual = text.get_card_actual() + card_expect = core.LVecBase4(l, r, b, t) + assert card_actual == card_expect + + +def test_textnode_frame_as_margin(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_frame_as_margin(l, r, b, t) + + assert text.has_frame() + assert text.is_frame_as_margin() + assert text.get_frame_as_set() == (l, r, b, t) + + frame_actual = text.get_frame_actual() + frame_expect = core.LVecBase4( + text.get_left() - l, + text.get_right() + r, + text.get_bottom() - b, + text.get_top() + t) + assert frame_actual == frame_expect + + +def test_textnode_frame_actual(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_frame_actual(l, r, b, t) + + assert text.has_frame() + assert not text.is_frame_as_margin() + assert text.get_frame_as_set() == (l, r, b, t) + + frame_actual = text.get_frame_actual() + frame_expect = core.LVecBase4(l, r, b, t) + assert frame_actual == frame_expect + + +def test_textnode_flatten_color(): + text = core.TextNode("test") + text.text_color = (0, 0, 0, 1) + path = core.NodePath(text) + + color = core.LColor(1, 0, 0, 1) + path.set_color(color) + path.flatten_strong() + + assert text.text_color.almost_equal(color) + assert text.shadow_color.almost_equal(color) + assert text.frame_color.almost_equal(color) + assert text.card_color.almost_equal(color) + + +def test_textnode_flatten_colorscale(): + text = core.TextNode("test") + text.text_color = (1, 0, 0, 0) + text.shadow_color = (0, 1, 0, 0) + text.frame_color = (0, 0, 1, 0) + text.card_color = (0, 0, 0, 1) + path = core.NodePath(text) + + color = core.LColor(.5, .5, .5, .5) + path.set_color_scale(color) + path.flatten_strong() + + assert text.text_color.almost_equal((.5, 0, 0, 0)) + assert text.shadow_color.almost_equal((0, .5, 0, 0)) + assert text.frame_color.almost_equal((0, 0, .5, 0)) + assert text.card_color.almost_equal((0, 0, 0, .5))