diff --git a/README.md b/README.md index cf45f0dfd2..43feb9b19c 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,9 @@ Building Panda3D Windows ------- -We currently build using the Microsoft Visual C++ 2015 compiler. You will -also need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk), +You can build Panda3D with the Microsoft Visual C++ 2015 or 2017 compiler, +which can be downloaded for free from the [Visual Studio site](https://visualstudio.microsoft.com/downloads/). +You will also need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk), and if you intend to target Windows XP, you will also need the [Windows 7.1 SDK](https://www.microsoft.com/en-us/download/details.aspx?id=8279). @@ -58,11 +59,12 @@ http://rdb.name/thirdparty-vc14-x64.7z http://rdb.name/thirdparty-vc14.7z After acquiring these dependencies, you may simply build Panda3D from the -command prompt using the following command. (Add the `--windows-sdk=10` -option if you don't need to support Windows XP.) +command prompt using the following command. (Change `14.1` to `14` if you are +using Visual C++ 2015 instead of 2017. Add the `--windows-sdk=10` option if +you don't need to support Windows XP and did not install the Windows 7.1 SDK.) ```bash -makepanda\makepanda.bat --everything --installer --no-eigen --threads=2 +makepanda\makepanda.bat --everything --installer --msvc-version=14.1 --no-eigen --threads=2 ``` When the build succeeds, it will produce an .exe file that you can use to @@ -101,7 +103,7 @@ If you are on Ubuntu, this command should cover the most frequently used third-party packages: ```bash -sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev +sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev ``` Once Panda3D has built, you can either install the .deb or .rpm package that @@ -163,6 +165,36 @@ python3.6 makepanda/makepanda.py --everything --installer --no-egl --no-gles --n If successful, this will produce a .pkg file in the root of the source directory which you can install using `pkg install`. +Android +------- + +Note: building on Android is very experimental and not guaranteed to work. + +You can experimentally build the Android Python runner via the [termux](https://termux.com/) +shell. You will need to install [Termux](https://play.google.com/store/apps/details?id=com.termux) +and [Termux API](https://play.google.com/store/apps/details?id=com.termux.api) +from the Play Store. Many of the dependencies can be installed by running the +following command in the Termux shell: + +```bash +pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libopus-dev opusfile-dev openal-soft-dev freetype-dev harfbuzz-dev libpng-dev ecj4.6 dx patchelf aapt apksigner libcrypt-dev +``` + +Then, you can build and install the .apk right away using these commands: + +```bash +python makepanda/makepanda.py --everything --target android-21 --installer +xdg-open panda3d.apk +``` + +To launch a Python program from Termux, you can use the `run_python.sh` script +inside the `panda/src/android` directory. It will launch Python in a separate +activity, load it with the Python script you passed as argument, and use a +socket for returning the command-line output to the Termux shell. Do note +that this requires the Python application to reside on the SD card and that +Termux needs to be set up with access to the SD card (using the +`termux-setup-storage` command). + Running Tests ============= 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/dtoolutil/p3dtoolutil_ext_composite.cxx b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx index 89a0ebcc30..2cd825ff58 100644 --- a/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx +++ b/dtool/src/dtoolutil/p3dtoolutil_ext_composite.cxx @@ -1,2 +1,3 @@ #include "filename_ext.cxx" #include "globPattern_ext.cxx" +#include "textEncoder_ext.cxx" diff --git a/dtool/src/dtoolutil/stringDecoder.I b/dtool/src/dtoolutil/stringDecoder.I index f7a3b14701..ce128833d0 100644 --- a/dtool/src/dtoolutil/stringDecoder.I +++ b/dtool/src/dtoolutil/stringDecoder.I @@ -53,5 +53,5 @@ StringUtf8Decoder(const std::string &input) : StringDecoder(input) { * */ INLINE StringUnicodeDecoder:: -StringUnicodeDecoder(const std::string &input) : StringDecoder(input) { +StringUtf16Decoder(const std::string &input) : StringDecoder(input) { } diff --git a/dtool/src/dtoolutil/stringDecoder.cxx b/dtool/src/dtoolutil/stringDecoder.cxx index e77e0c5e13..f9ecfdecd3 100644 --- a/dtool/src/dtoolutil/stringDecoder.cxx +++ b/dtool/src/dtoolutil/stringDecoder.cxx @@ -26,7 +26,7 @@ StringDecoder:: /** * Returns the next character in sequence. */ -int StringDecoder:: +char32_t StringDecoder:: get_next_character() { if (test_eof()) { return -1; @@ -57,19 +57,20 @@ get_notify_ptr() { /* In UTF-8, each 16-bit Unicode character is encoded as a sequence of -one, two, or three 8-bit bytes, depending on the value of the +one, two, three or four 8-bit bytes, depending on the value of the character. The following table shows the format of such UTF-8 byte sequences (where the "free bits" shown by x's in the table are combined in the order shown, and interpreted from most significant to least significant): Binary format of bytes in sequence: - Number of Maximum expressible - 1st byte 2nd byte 3rd byte free bits: Unicode value: + Number of Maximum expressible + 1st byte 2nd byte 3rd byte 4th byte free bits: Unicode value: - 0xxxxxxx 7 007F hex (127) - 110xxxxx 10xxxxxx (5+6)=11 07FF hex (2047) - 1110xxxx 10xxxxxx 10xxxxxx (4+6+6)=16 FFFF hex (65535) + 0xxxxxxx 7 007F hex (127) + 110xxxxx 10xxxxxx (5+6)=11 07FF hex (2047) + 1110xxxx 10xxxxxx 10xxxxxx (4+6+6)=16 FFFF hex (65535) + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (4+6*3)=21 10FFFF hex (1114111) The value of each individual byte indicates its UTF-8 function, as follows: @@ -77,12 +78,13 @@ The value of each individual byte indicates its UTF-8 function, as follows: 80 to BF hex (128 to 191): continuing byte in a multi-byte sequence. C2 to DF hex (194 to 223): first byte of a two-byte sequence. E0 to EF hex (224 to 239): first byte of a three-byte sequence. + F0 to F7 hex (240 to 247): first byte of a four-byte sequence. */ /** * Returns the next character in sequence. */ -int StringUtf8Decoder:: +char32_t StringUtf8Decoder:: get_next_character() { unsigned int result; while (!test_eof()) { @@ -125,6 +127,35 @@ get_next_character() { unsigned int three = (unsigned char)_input[_p++]; result = ((result & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f); return result; + + } else if ((result & 0xf8) == 0xf0) { + // First byte of four. + if (test_eof()) { + if (_notify_ptr != nullptr) { + (*_notify_ptr) + << "utf-8 encoded string '" << _input << "' ends abruptly.\n"; + } + return -1; + } + unsigned int two = (unsigned char)_input[_p++]; + if (test_eof()) { + if (_notify_ptr != nullptr) { + (*_notify_ptr) + << "utf-8 encoded string '" << _input << "' ends abruptly.\n"; + } + return -1; + } + unsigned int three = (unsigned char)_input[_p++]; + if (test_eof()) { + if (_notify_ptr != nullptr) { + (*_notify_ptr) + << "utf-8 encoded string '" << _input << "' ends abruptly.\n"; + } + return -1; + } + unsigned int four = (unsigned char)_input[_p++]; + result = ((result & 0x07) << 18) | ((two & 0x3f) << 12) | ((three & 0x3f) << 6) | (four & 0x3f); + return result; } // Otherwise--the high bit is set but it is not one of the introductory @@ -144,7 +175,7 @@ get_next_character() { /** * Returns the next character in sequence. */ -int StringUnicodeDecoder:: +char32_t StringUtf16Decoder:: get_next_character() { if (test_eof()) { return -1; @@ -159,5 +190,33 @@ get_next_character() { return -1; } unsigned int low = (unsigned char)_input[_p++]; - return ((high << 8) | low); + int ch = ((high << 8) | low); + + /* + using std::swap; + + if (ch == 0xfffe) { + // This is a byte-swapped byte-order-marker. That means we need to swap + // the endianness of the rest of the stream. + char *data = (char *)_input.data(); + for (size_t p = _p; p < _input.size() - 1; p += 2) { + std::swap(data[p], data[p + 1]); + } + ch = 0xfeff; + } + */ + + if (ch >= 0xd800 && ch < 0xdc00 && (_p + 1) < _input.size()) { + // This is a high surrogate. Look for a subsequent low surrogate. + unsigned int high = (unsigned char)_input[_p]; + unsigned int low = (unsigned char)_input[_p + 1]; + int ch2 = ((high << 8) | low); + if (ch2 >= 0xdc00 && ch2 < 0xe000) { + // Yes, this is a low surrogate. + _p += 2; + return 0x10000 + ((ch - 0xd800) << 10) + (ch2 - 0xdc00); + } + } + // No, this is just a regular character, or an unpaired surrogate. + return ch; } diff --git a/dtool/src/dtoolutil/stringDecoder.h b/dtool/src/dtoolutil/stringDecoder.h index c0b2534ee2..6885f77e08 100644 --- a/dtool/src/dtoolutil/stringDecoder.h +++ b/dtool/src/dtoolutil/stringDecoder.h @@ -26,7 +26,7 @@ public: INLINE StringDecoder(const std::string &input); virtual ~StringDecoder(); - virtual int get_next_character(); + virtual char32_t get_next_character(); INLINE bool is_eof(); static void set_notify_ptr(std::ostream *ptr); @@ -48,20 +48,23 @@ class StringUtf8Decoder : public StringDecoder { public: INLINE StringUtf8Decoder(const std::string &input); - virtual int get_next_character(); + virtual char32_t get_next_character(); }; /** * This decoder extracts characters two at a time to get a plain wide - * character sequence. + * character sequence. It supports surrogate pairs. */ -class StringUnicodeDecoder : public StringDecoder { +class StringUtf16Decoder : public StringDecoder { public: - INLINE StringUnicodeDecoder(const std::string &input); + INLINE StringUtf16Decoder(const std::string &input); - virtual int get_next_character(); + virtual char32_t get_next_character(); }; +// Deprecated alias of StringUtf16Encoder. +typedef StringUtf16Decoder StringUnicodeDecoder; + #include "stringDecoder.I" #endif diff --git a/dtool/src/dtoolutil/textEncoder.I b/dtool/src/dtoolutil/textEncoder.I index e07f489ab0..766319d6da 100644 --- a/dtool/src/dtoolutil/textEncoder.I +++ b/dtool/src/dtoolutil/textEncoder.I @@ -90,6 +90,7 @@ set_text(const std::string &text) { if (!has_text() || _text != text) { _text = text; _flags = (_flags | F_got_text) & ~F_got_wtext; + text_changed(); } } @@ -101,7 +102,11 @@ set_text(const std::string &text) { */ INLINE void TextEncoder:: set_text(const std::string &text, TextEncoder::Encoding encoding) { - set_wtext(decode_text(text, encoding)); + if (encoding == _encoding) { + set_text(text); + } else { + set_wtext(decode_text(text, encoding)); + } } /** @@ -112,6 +117,7 @@ clear_text() { _text = std::string(); _wtext = std::wstring(); _flags |= (F_got_text | F_got_wtext); + text_changed(); } /** @@ -151,8 +157,11 @@ get_text(TextEncoder::Encoding encoding) const { */ INLINE void TextEncoder:: append_text(const std::string &text) { - _text = get_text() + text; - _flags = (_flags | F_got_text) & ~F_got_wtext; + if (!text.empty()) { + _text = get_text() + text; + _flags = (_flags | F_got_text) & ~F_got_wtext; + text_changed(); + } } /** @@ -160,9 +169,25 @@ append_text(const std::string &text) { * wide character, up to 16 bits in Unicode. */ INLINE void TextEncoder:: -append_unicode_char(int character) { +append_unicode_char(char32_t character) { +#if WCHAR_MAX >= 0x10FFFF + // wchar_t might be UTF-32. _wtext = get_wtext() + std::wstring(1, (wchar_t)character); +#else + if ((character & ~0xffff) == 0) { + _wtext = get_wtext() + std::wstring(1, (wchar_t)character); + } else { + // Encode as a surrogate pair. + uint32_t v = (uint32_t)character - 0x10000u; + wchar_t wstr[2] = { + (wchar_t)((v >> 10u) | 0xd800u), + (wchar_t)((v & 0x3ffu) | 0xdc00u), + }; + _wtext = get_wtext() + std::wstring(wstr, 2); + } +#endif _flags = (_flags | F_got_wtext) & ~F_got_text; + text_changed(); } /** @@ -200,6 +225,7 @@ set_unicode_char(size_t index, int character) { if (index < _wtext.length()) { _wtext[index] = character; _flags &= ~F_got_text; + text_changed(); } } @@ -418,6 +444,7 @@ set_wtext(const std::wstring &wtext) { if (!has_text() || _wtext != wtext) { _wtext = wtext; _flags = (_flags | F_got_wtext) & ~F_got_text; + text_changed(); } } @@ -439,8 +466,11 @@ get_wtext() const { */ INLINE void TextEncoder:: append_wtext(const std::wstring &wtext) { - _wtext = get_wtext() + wtext; - _flags = (_flags | F_got_wtext) & ~F_got_text; + if (!wtext.empty()) { + _wtext = get_wtext() + wtext; + _flags = (_flags | F_got_wtext) & ~F_got_text; + text_changed(); + } } /** diff --git a/dtool/src/dtoolutil/textEncoder.cxx b/dtool/src/dtoolutil/textEncoder.cxx index 1e1cd4bc61..1065f21dcb 100644 --- a/dtool/src/dtoolutil/textEncoder.cxx +++ b/dtool/src/dtoolutil/textEncoder.cxx @@ -21,7 +21,7 @@ using std::ostream; using std::string; using std::wstring; -TextEncoder::Encoding TextEncoder::_default_encoding = TextEncoder::E_iso8859; +TextEncoder::Encoding TextEncoder::_default_encoding = TextEncoder::E_utf8; /** * Adjusts the text stored within the encoder to all uppercase letters @@ -35,6 +35,7 @@ make_upper() { (*si) = unicode_toupper(*si); } _flags &= ~F_got_text; + text_changed(); } /** @@ -49,6 +50,7 @@ make_lower() { (*si) = unicode_tolower(*si); } _flags &= ~F_got_text; + text_changed(); } /** @@ -107,11 +109,11 @@ is_wtext() const { } /** - * Encodes a single wide char into a one-, two-, or three-byte string, - * according to the given encoding system. + * Encodes a single Unicode character into a one-, two-, three-, or four-byte + * string, according to the given encoding system. */ string TextEncoder:: -encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) { +encode_wchar(char32_t ch, TextEncoder::Encoding encoding) { switch (encoding) { case E_iso8859: if ((ch & ~0xff) == 0) { @@ -143,17 +145,38 @@ encode_wchar(wchar_t ch, TextEncoder::Encoding encoding) { return string(1, (char)((ch >> 6) | 0xc0)) + string(1, (char)((ch & 0x3f) | 0x80)); - } else { + } else if ((ch & ~0xffff) == 0) { return string(1, (char)((ch >> 12) | 0xe0)) + string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) + string(1, (char)((ch & 0x3f) | 0x80)); + } else { + return + string(1, (char)((ch >> 18) | 0xf0)) + + string(1, (char)(((ch >> 12) & 0x3f) | 0x80)) + + string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) + + string(1, (char)((ch & 0x3f) | 0x80)); } - case E_unicode: - return - string(1, (char)(ch >> 8)) + - string(1, (char)(ch & 0xff)); + case E_utf16be: + if ((ch & ~0xffff) == 0) { + // Note that this passes through surrogates and BOMs unharmed. + return + string(1, (char)(ch >> 8)) + + string(1, (char)(ch & 0xff)); + } else { + // Use a surrogate pair. + uint32_t v = (uint32_t)ch - 0x10000u; + uint16_t hi = (v >> 10u) | 0xd800u; + uint16_t lo = (v & 0x3ffu) | 0xdc00u; + char encoded[4] = { + (char)(hi >> 8), + (char)(hi & 0xff), + (char)(lo >> 8), + (char)(lo & 0xff), + }; + return string(encoded, 4); + } } return ""; @@ -167,8 +190,25 @@ string TextEncoder:: encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) { string result; - for (wstring::const_iterator pi = wtext.begin(); pi != wtext.end(); ++pi) { - result += encode_wchar(*pi, encoding); + for (size_t i = 0; i < wtext.size(); ++i) { + wchar_t ch = wtext[i]; + + // On some systems, wstring may be UTF-16, and contain surrogate pairs. +#if WCHAR_MAX < 0x10FFFF + if (ch >= 0xd800 && ch < 0xdc00 && (i + 1) < wtext.size()) { + // This is a high surrogate. Look for a subsequent low surrogate. + wchar_t ch2 = wtext[i + 1]; + if (ch2 >= 0xdc00 && ch2 < 0xe000) { + // Yes, this is a low surrogate. + char32_t code_point = 0x10000 + ((ch - 0xd800) << 10) + (ch2 - 0xdc00); + result += encode_wchar(code_point, encoding); + i++; + continue; + } + } +#endif + + result += encode_wchar(ch, encoding); } return result; @@ -187,9 +227,9 @@ decode_text(const string &text, TextEncoder::Encoding encoding) { return decode_text_impl(decoder); } - case E_unicode: + case E_utf16be: { - StringUnicodeDecoder decoder(text); + StringUtf16Decoder decoder(text); return decode_text_impl(decoder); } @@ -211,7 +251,7 @@ decode_text_impl(StringDecoder &decoder) { wstring result; // bool expand_amp = get_expand_amp(); - wchar_t character = decoder.get_next_character(); + char32_t character = decoder.get_next_character(); while (!decoder.is_eof()) { /* if (character == '&' && expand_amp) { @@ -219,7 +259,14 @@ decode_text_impl(StringDecoder &decoder) { character = expand_amp_sequence(decoder); } */ - result += character; + if (character <= WCHAR_MAX) { + result += character; + } else { + // We need to encode this as a surrogate pair. + uint32_t v = (uint32_t)character - 0x10000u; + result += (wchar_t)((v >> 10u) | 0xd800u); + result += (wchar_t)((v & 0x3ffu) | 0xdc00u); + } character = decoder.get_next_character(); } @@ -314,6 +361,12 @@ expand_amp_sequence(StringDecoder &decoder) const { } */ +/** + * Called whenever the text has been changed. + */ +void TextEncoder:: +text_changed() { +} /** * @@ -327,8 +380,8 @@ operator << (ostream &out, TextEncoder::Encoding encoding) { case TextEncoder::E_utf8: return out << "utf8"; - case TextEncoder::E_unicode: - return out << "unicode"; + case TextEncoder::E_utf16be: + return out << "utf16be"; }; return out << "**invalid TextEncoder::Encoding(" << (int)encoding << ")**"; @@ -346,8 +399,9 @@ operator >> (istream &in, TextEncoder::Encoding &encoding) { encoding = TextEncoder::E_iso8859; } else if (word == "utf8" || word == "utf-8") { encoding = TextEncoder::E_utf8; - } else if (word == "unicode") { - encoding = TextEncoder::E_unicode; + } else if (word == "unicode" || word == "utf16be" || word == "utf-16be" || + word == "utf16-be" || word == "utf-16-be") { + encoding = TextEncoder::E_utf16be; } else { ostream *notify_ptr = StringDecoder::get_notify_ptr(); if (notify_ptr != nullptr) { diff --git a/dtool/src/dtoolutil/textEncoder.h b/dtool/src/dtoolutil/textEncoder.h index a7eaf395ff..30004ef5d3 100644 --- a/dtool/src/dtoolutil/textEncoder.h +++ b/dtool/src/dtoolutil/textEncoder.h @@ -35,12 +35,17 @@ PUBLISHED: enum Encoding { E_iso8859, E_utf8, - E_unicode + E_utf16be, + + // Deprecated alias for E_utf16be + E_unicode = E_utf16be, }; INLINE TextEncoder(); INLINE TextEncoder(const TextEncoder ©); + virtual ~TextEncoder() = default; + INLINE void set_encoding(Encoding encoding); INLINE Encoding get_encoding() const; @@ -48,18 +53,29 @@ PUBLISHED: INLINE static Encoding get_default_encoding(); MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding); +#ifdef CPPPARSER + EXTEND void set_text(PyObject *text); + EXTEND void set_text(PyObject *text, Encoding encoding); +#else INLINE void set_text(const std::string &text); INLINE void set_text(const std::string &text, Encoding encoding); +#endif INLINE void clear_text(); INLINE bool has_text() const; void make_upper(); void make_lower(); +#ifdef CPPPARSER + EXTEND PyObject *get_text() const; + EXTEND PyObject *get_text(Encoding encoding) const; + EXTEND void append_text(PyObject *text); +#else INLINE std::string get_text() const; INLINE std::string get_text(Encoding encoding) const; INLINE void append_text(const std::string &text); - INLINE void append_unicode_char(int character); +#endif + INLINE void append_unicode_char(char32_t character); INLINE size_t get_num_chars() const; INLINE int get_unicode_char(size_t index) const; INLINE void set_unicode_char(size_t index, int character); @@ -91,11 +107,24 @@ PUBLISHED: std::wstring get_wtext_as_ascii() const; bool is_wtext() const; - static std::string encode_wchar(wchar_t ch, Encoding encoding); +#ifdef CPPPARSER + EXTEND static PyObject *encode_wchar(char32_t ch, Encoding encoding); + EXTEND INLINE PyObject *encode_wtext(const std::wstring &wtext) const; + EXTEND static PyObject *encode_wtext(const std::wstring &wtext, Encoding encoding); + EXTEND INLINE PyObject *decode_text(PyObject *text) const; + EXTEND static PyObject *decode_text(PyObject *text, Encoding encoding); +#else + static std::string encode_wchar(char32_t ch, Encoding encoding); INLINE std::string encode_wtext(const std::wstring &wtext) const; static std::string encode_wtext(const std::wstring &wtext, Encoding encoding); INLINE std::wstring decode_text(const std::string &text) const; static std::wstring decode_text(const std::string &text, Encoding encoding); +#endif + + MAKE_PROPERTY(text, get_text, set_text); + +protected: + virtual void text_changed(); private: enum Flags { diff --git a/dtool/src/dtoolutil/textEncoder_ext.I b/dtool/src/dtoolutil/textEncoder_ext.I new file mode 100644 index 0000000000..2924fda011 --- /dev/null +++ b/dtool/src/dtoolutil/textEncoder_ext.I @@ -0,0 +1,30 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file textEncoder_ext.I + * @author rdb + * @date 2018-10-08 + */ + +/** + * Encodes a wide-text string into a single-char string, according to the + * current encoding. + */ +INLINE PyObject *Extension:: +encode_wtext(const std::wstring &wtext) const { + return encode_wtext(wtext, _this->get_encoding()); +} + +/** + * Returns the given wstring decoded to a single-byte string, via the current + * encoding system. + */ +INLINE PyObject *Extension:: +decode_text(PyObject *text) const { + return decode_text(text, _this->get_encoding()); +} diff --git a/dtool/src/dtoolutil/textEncoder_ext.cxx b/dtool/src/dtoolutil/textEncoder_ext.cxx new file mode 100644 index 0000000000..1947e6c749 --- /dev/null +++ b/dtool/src/dtoolutil/textEncoder_ext.cxx @@ -0,0 +1,164 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file textEncoder_ext.cxx + * @author rdb + * @date 2018-09-29 + */ + +#include "textEncoder_ext.h" + +#ifdef HAVE_PYTHON + +/** + * Sets the text as a Unicode string. In Python 2, if a regular str is given, + * it is assumed to be in the TextEncoder's specified encoding. + */ +void Extension:: +set_text(PyObject *text) { + if (PyUnicode_Check(text)) { +#if PY_VERSION_HEX >= 0x03030000 + Py_ssize_t len; + const char *str = PyUnicode_AsUTF8AndSize(text, &len); + _this->set_text(std::string(str, len), TextEncoder::E_utf8); +#else + Py_ssize_t len = PyUnicode_GET_SIZE(text); + wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1)); + PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len); + _this->set_wtext(std::wstring(str, len)); +#endif + } else { +#if PY_MAJOR_VERSION >= 3 + Dtool_Raise_TypeError("expected string"); +#else + char *str; + Py_ssize_t len; + if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) { + _this->set_text(std::string(str, len)); + } +#endif + } +} + +/** + * Sets the text as an encoded byte string of the given encoding. + */ +void Extension:: +set_text(PyObject *text, TextEncoder::Encoding encoding) { + char *str; + Py_ssize_t len; + if (PyBytes_AsStringAndSize(text, &str, &len) >= 0) { + _this->set_text(std::string(str, len), encoding); + } +} + +/** + * Returns the text as a string. In Python 2, the returned string is in the + * TextEncoder's specified encoding. In Python 3, it is returned as unicode. + */ +PyObject *Extension:: +get_text() const { +#if PY_MAJOR_VERSION >= 3 + std::wstring text = _this->get_wtext(); + return PyUnicode_FromWideChar(text.data(), (Py_ssize_t)text.size()); +#else + std::string text = _this->get_text(); + return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size()); +#endif +} + +/** + * Returns the text as a bytes object in the given encoding. + */ +PyObject *Extension:: +get_text(TextEncoder::Encoding encoding) const { + std::string text = _this->get_text(encoding); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size()); +#else + return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size()); +#endif +} + +/** + * Appends the text as a string (or Unicode object in Python 2). + */ +void Extension:: +append_text(PyObject *text) { + if (PyUnicode_Check(text)) { +#if PY_VERSION_HEX >= 0x03030000 + Py_ssize_t len; + const char *str = PyUnicode_AsUTF8AndSize(text, &len); + std::string text_str(str, len); + if (_this->get_encoding() == TextEncoder::E_utf8) { + _this->append_text(text_str); + } else { + _this->append_wtext(TextEncoder::decode_text(text_str, TextEncoder::E_utf8)); + } +#else + Py_ssize_t len = PyUnicode_GET_SIZE(text); + wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1)); + PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len); + _this->append_wtext(std::wstring(str, len)); +#endif + } else { +#if PY_MAJOR_VERSION >= 3 + Dtool_Raise_TypeError("expected string"); +#else + char *str; + Py_ssize_t len; + if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) { + _this->append_text(std::string(str, len)); + } +#endif + } +} + +/** + * Encodes the given wide character as byte string in the given encoding. + */ +PyObject *Extension:: +encode_wchar(char32_t ch, TextEncoder::Encoding encoding) { + std::string value = TextEncoder::encode_wchar(ch, encoding); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); +#else + return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); +#endif +} + +/** + * Encodes a wide-text string into a single-char string, according to the + * given encoding. + */ +PyObject *Extension:: +encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) { + std::string value = TextEncoder::encode_wtext(wtext, encoding); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); +#else + return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); +#endif +} + +/** + * Returns the given wstring decoded to a single-byte string, via the given + * encoding system. + */ +PyObject *Extension:: +decode_text(PyObject *text, TextEncoder::Encoding encoding) { + char *str; + Py_ssize_t len; + if (PyBytes_AsStringAndSize(text, &str, &len) >= 0) { + return Dtool_WrapValue(TextEncoder::decode_text(std::string(str, len), encoding)); + } else { + return nullptr; + } +} + +#endif // HAVE_PYTHON diff --git a/dtool/src/dtoolutil/textEncoder_ext.h b/dtool/src/dtoolutil/textEncoder_ext.h new file mode 100644 index 0000000000..049c16e493 --- /dev/null +++ b/dtool/src/dtoolutil/textEncoder_ext.h @@ -0,0 +1,50 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file textEncoder_ext.h + * @author rdb + * @date 2018-09-29 + */ + +#ifndef TEXTENCODER_EXT_H +#define TEXTENCODER_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "textEncoder.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for TextEncoder, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + void set_text(PyObject *text); + void set_text(PyObject *text, TextEncoder::Encoding encoding); + + PyObject *get_text() const; + PyObject *get_text(TextEncoder::Encoding encoding) const; + void append_text(PyObject *text); + + static PyObject *encode_wchar(char32_t ch, TextEncoder::Encoding encoding); + INLINE PyObject *encode_wtext(const std::wstring &wtext) const; + static PyObject *encode_wtext(const std::wstring &wtext, TextEncoder::Encoding encoding); + INLINE PyObject *decode_text(PyObject *text) const; + static PyObject *decode_text(PyObject *text, TextEncoder::Encoding encoding); +}; + +#include "textEncoder_ext.I" + +#endif // HAVE_PYTHON + +#endif // TEXTENCODER_EXT_H diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index f579f1586d..15607c95ea 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -2943,7 +2943,7 @@ define_method(CPPInstance *function, InterrogateType &itype, // specifically flag get_class_type() as published. bool force_publish = false; if (function->get_simple_name() == "get_class_type" && - (function->_storage_class && CPPInstance::SC_static) != 0 && + (function->_storage_class & CPPInstance::SC_static) != 0 && function->_vis <= V_public) { force_publish = true; } diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 2522a55cc2..38e774eb88 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -235,8 +235,8 @@ PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) { "'%.100s' object has no attribute '%.200s'", Py_TYPE(obj)->tp_name, attribute); - Py_INCREF(PyExc_TypeError); - PyErr_Restore(PyExc_TypeError, message, nullptr); + Py_INCREF(PyExc_AttributeError); + PyErr_Restore(PyExc_AttributeError, message, nullptr); return nullptr; } 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/makepanda/confauto.in b/makepanda/confauto.in index 608a6daf68..41655f1bbc 100644 --- a/makepanda/confauto.in +++ b/makepanda/confauto.in @@ -21,6 +21,11 @@ load-file-type egg pandaegg +# If we built with Assimp support, we can enable the Assimp loader, +# which allows us to load many model formats natively. + +load-file-type p3assimp + # These entries work very similar to load-file-type, except they are # used by the MovieVideo and MovieAudio code to determine which module # should be loaded in order to decode files of the given extension. diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index d275cf2119..8259c2af82 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1010,8 +1010,6 @@ if (COMPILER=="GCC"): if GetTarget() == 'darwin': LibName("ALWAYS", "-framework AppKit") - if (PkgSkip("OPENCV")==0): - LibName("OPENCV", "-framework QuickTime") LibName("AGL", "-framework AGL") LibName("CARBON", "-framework Carbon") LibName("COCOA", "-framework Cocoa") @@ -1398,9 +1396,10 @@ def CompileCxx(obj,src,opts): # Work around Apple compiler bug. cmd += " -U__EXCEPTIONS" - if 'RTTI' not in opts: + target = GetTarget() + if 'RTTI' not in opts and target != "darwin": # We always disable RTTI on Android for memory usage reasons. - if optlevel >= 4 or GetTarget() == "android": + if optlevel >= 4 or target == "android": cmd += " -fno-rtti" if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm") and arch != 'aarch64': @@ -2979,6 +2978,9 @@ else: # otherwise, disable it. confautoprc = confautoprc.replace('#st#', '#') +if PkgSkip("ASSIMP"): + confautoprc = confautoprc.replace("load-file-type p3assimp", "#load-file-type p3assimp") + if (os.path.isfile("makepanda/myconfig.in")): configprc = ReadFile("makepanda/myconfig.in") else: @@ -3645,6 +3647,7 @@ IGATEFILES += [ "dSearchPath.h", "executionEnvironment.h", "textEncoder.h", + "textEncoder_ext.h", "filename.h", "filename_ext.h", "globPattern.h", 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/cocoadisplay/cocoaPandaApp.mm b/panda/src/cocoadisplay/cocoaPandaApp.mm index e7226786da..83f0b8ef42 100644 --- a/panda/src/cocoadisplay/cocoaPandaApp.mm +++ b/panda/src/cocoadisplay/cocoaPandaApp.mm @@ -12,6 +12,7 @@ */ #import "cocoaPandaApp.h" +#include "config_cocoadisplay.h" @implementation CocoaPandaApp - (void) sendEvent: (NSEvent *) event { diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index dd637d7a9a..57a592f05d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -593,8 +593,7 @@ remove_all_windows() { Windows old_windows; old_windows.swap(_windows); Windows::iterator wi; - for (wi = old_windows.begin(); wi != old_windows.end(); ++wi) { - GraphicsOutput *win = (*wi); + for (GraphicsOutput *win : old_windows) { nassertv(win != nullptr); do_remove_window(win, current_thread); GraphicsStateGuardian *gsg = win->get_gsg(); @@ -605,6 +604,14 @@ remove_all_windows() { { MutexHolder new_windows_holder(_new_windows_lock, current_thread); + for (GraphicsOutput *win : _new_windows) { + nassertv(win != nullptr); + do_remove_window(win, current_thread); + GraphicsStateGuardian *gsg = win->get_gsg(); + if (gsg != nullptr) { + gsg->release_all(); + } + } _new_windows.clear(); } diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 1050f8372f..b76a7f751c 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -2722,7 +2722,7 @@ do_issue_color_scale() { } if (_alpha_scale_via_texture && !_has_scene_graph_color && - target_color_scale->has_alpha_scale()) { + _vertex_colors_enabled && target_color_scale->has_alpha_scale()) { // This color scale will set a special texture--so again, clear the // texture. _state_mask.clear_bit(TextureAttrib::get_class_slot()); @@ -3168,6 +3168,17 @@ determine_light_color_scale() { _scene_graph_color[3] * _current_color_scale[3]); } + } else if (!_vertex_colors_enabled) { + // We don't have a scene graph color, but we don't want to enable vertex + // colors either, so we still need to force a white material color in + // absence of any other color. + _has_material_force_color = true; + _material_force_color.set(1.0f, 1.0f, 1.0f, 1.0f); + _light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); + if (!_color_blend_involves_color_scale && _color_scale_enabled) { + _material_force_color.componentwise_mult(_current_color_scale); + } + } else { // Otherise, leave the materials alone, but we might still scale the // lights. diff --git a/panda/src/display/graphicsWindowProc.h b/panda/src/display/graphicsWindowProc.h index c9c056eebd..a330c06a11 100644 --- a/panda/src/display/graphicsWindowProc.h +++ b/panda/src/display/graphicsWindowProc.h @@ -32,6 +32,7 @@ class GraphicsWindow; class EXPCL_PANDA_DISPLAY GraphicsWindowProc { public: GraphicsWindowProc(); + virtual ~GraphicsWindowProc() = default; #if defined(__WIN32__) || defined(_WIN32) virtual LONG wnd_proc(GraphicsWindow* graphicsWindow, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index 5d2a3f5102..2dba4aa6c0 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -12,10 +12,14 @@ */ #include "standardMunger.h" -#include "renderState.h" -#include "graphicsStateGuardian.h" + #include "config_gobj.h" + #include "displayRegion.h" +#include "graphicsStateGuardian.h" +#include "lightAttrib.h" +#include "materialAttrib.h" +#include "renderState.h" TypeHandle StandardMunger::_type_handle; @@ -36,7 +40,8 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, _munge_color(false), _munge_color_scale(false), _auto_shader(false), - _shader_skinning(false) + _shader_skinning(false), + _remove_material(false) { const ShaderAttrib *shader_attrib; state->get_attrib_def(shader_attrib); @@ -54,24 +59,10 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, const ColorScaleAttrib *color_scale_attrib; if (state->get_attrib(color_attrib) && - color_attrib->get_color_type() == ColorAttrib::T_flat) { + color_attrib->get_color_type() != ColorAttrib::T_vertex) { - if (!get_gsg()->get_color_scale_via_lighting()) { - // We only need to munge the color directly if the GSG says it can't - // cheat the color via lighting (presumably, in this case, by applying - // a material). - _color = color_attrib->get_color(); - if (state->get_attrib(color_scale_attrib) && - color_scale_attrib->has_scale()) { - const LVecBase4 &cs = color_scale_attrib->get_scale(); - _color.set(_color[0] * cs[0], - _color[1] * cs[1], - _color[2] * cs[2], - _color[3] * cs[3]); - } - _munge_color = true; - _should_munge_state = true; - } + // In this case, we don't need to munge anything as we can apply the + // color and color scale via glColor4f. } else if (state->get_attrib(color_scale_attrib) && color_scale_attrib->has_scale()) { @@ -94,6 +85,19 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, // effort to detect this contrived situation and handle it correctly. } } + + // If we have no lights but do have a material, we will need to remove it so + // that it won't appear when we enable color scale via lighting. + const LightAttrib *light_attrib; + const MaterialAttrib *material_attrib; + if (get_gsg()->get_color_scale_via_lighting() && + (!state->get_attrib(light_attrib) || !light_attrib->has_any_on_light()) && + state->get_attrib(material_attrib) && + material_attrib->get_material() != nullptr && + shader_attrib->get_shader() == nullptr) { + _remove_material = true; + _should_munge_state = true; + } } /** @@ -291,6 +295,9 @@ compare_to_impl(const GeomMunger *other) const { if (_auto_shader != om->_auto_shader) { return (int)_auto_shader - (int)om->_auto_shader; } + if (_remove_material != om->_remove_material) { + return (int)_remove_material - (int)om->_remove_material; + } return StateMunger::compare_to_impl(other); } @@ -344,5 +351,9 @@ munge_state_impl(const RenderState *state) { munged_state = munged_state->remove_attrib(ColorScaleAttrib::get_class_slot()); } + if (_remove_material) { + munged_state = munged_state->remove_attrib(MaterialAttrib::get_class_slot()); + } + return munged_state; } diff --git a/panda/src/display/standardMunger.h b/panda/src/display/standardMunger.h index 05e8ee0344..d7c71e28e5 100644 --- a/panda/src/display/standardMunger.h +++ b/panda/src/display/standardMunger.h @@ -51,11 +51,13 @@ private: NumericType _numeric_type; Contents _contents; - bool _munge_color; - bool _munge_color_scale; bool _auto_shader; bool _shader_skinning; + bool _remove_material; +protected: + bool _munge_color; + bool _munge_color_scale; LColor _color; LVecBase4 _color_scale; diff --git a/panda/src/display/subprocessWindow.cxx b/panda/src/display/subprocessWindow.cxx index fe4b2eff5d..5ea85c3cc9 100644 --- a/panda/src/display/subprocessWindow.cxx +++ b/panda/src/display/subprocessWindow.cxx @@ -18,6 +18,8 @@ #include "graphicsEngine.h" #include "config_display.h" #include "nativeWindowHandle.h" +#include "mouseButton.h" +#include "throw_event.h" using std::string; diff --git a/panda/src/dxgsg9/dxGeomMunger9.I b/panda/src/dxgsg9/dxGeomMunger9.I index cc972bf94b..2accff3b11 100644 --- a/panda/src/dxgsg9/dxGeomMunger9.I +++ b/panda/src/dxgsg9/dxGeomMunger9.I @@ -10,34 +10,3 @@ * @author drose * @date 2005-03-11 */ - -/** - * - */ -INLINE DXGeomMunger9:: -DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state) : - StandardMunger(gsg, state, 1, NT_packed_dabc, C_color), - _texture(nullptr), - _tex_gen(nullptr) -{ - const TextureAttrib *texture = nullptr; - const TexGenAttrib *tex_gen = nullptr; - state->get_attrib(texture); - state->get_attrib(tex_gen); - _texture = texture; - _tex_gen = tex_gen; - - _filtered_texture = nullptr; - _reffed_filtered_texture = false; - if (texture != nullptr) { - _filtered_texture = texture->filter_to_max(gsg->get_max_texture_stages()); - if (_filtered_texture != texture) { - _filtered_texture->ref(); - _reffed_filtered_texture = true; - } - } - // Set a callback to unregister ourselves when either the Texture or the - // TexGen object gets deleted. - _texture.add_callback(this); - _tex_gen.add_callback(this); -} diff --git a/panda/src/dxgsg9/dxGeomMunger9.cxx b/panda/src/dxgsg9/dxGeomMunger9.cxx index 638b121f27..0e95070c91 100644 --- a/panda/src/dxgsg9/dxGeomMunger9.cxx +++ b/panda/src/dxgsg9/dxGeomMunger9.cxx @@ -19,6 +19,66 @@ GeomMunger *DXGeomMunger9::_deleted_chain = nullptr; TypeHandle DXGeomMunger9::_type_handle; +/** + * + */ +DXGeomMunger9:: +DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state) : + StandardMunger(gsg, state, 1, NT_packed_dabc, C_color), + _texture(nullptr), + _tex_gen(nullptr) +{ + const TextureAttrib *texture = nullptr; + const TexGenAttrib *tex_gen = nullptr; + state->get_attrib(texture); + state->get_attrib(tex_gen); + _texture = texture; + _tex_gen = tex_gen; + + if (!gsg->get_color_scale_via_lighting()) { + // We might need to munge the colors, if we are overriding the vertex + // colors and the GSG can't cheat the color via lighting. + + const ColorAttrib *color_attrib; + const ShaderAttrib *shader_attrib; + state->get_attrib_def(shader_attrib); + + if (!shader_attrib->auto_shader() && + shader_attrib->get_shader() == nullptr && + state->get_attrib(color_attrib) && + color_attrib->get_color_type() != ColorAttrib::T_vertex) { + + if (color_attrib->get_color_type() == ColorAttrib::T_off) { + _color.set(1, 1, 1, 1); + } else { + _color = color_attrib->get_color(); + } + + const ColorScaleAttrib *color_scale_attrib; + if (state->get_attrib(color_scale_attrib) && + color_scale_attrib->has_scale()) { + _color.componentwise_mult(color_scale_attrib->get_scale()); + } + _munge_color = true; + _should_munge_state = true; + } + } + + _filtered_texture = nullptr; + _reffed_filtered_texture = false; + if (texture != nullptr) { + _filtered_texture = texture->filter_to_max(gsg->get_max_texture_stages()); + if (_filtered_texture != texture) { + _filtered_texture->ref(); + _reffed_filtered_texture = true; + } + } + // Set a callback to unregister ourselves when either the Texture or the + // TexGen object gets deleted. + _texture.add_callback(this); + _tex_gen.add_callback(this); +} + /** * */ diff --git a/panda/src/dxgsg9/dxGeomMunger9.h b/panda/src/dxgsg9/dxGeomMunger9.h index c0762e3cf3..c0cb8f8edd 100644 --- a/panda/src/dxgsg9/dxGeomMunger9.h +++ b/panda/src/dxgsg9/dxGeomMunger9.h @@ -28,7 +28,7 @@ */ class EXPCL_PANDADX DXGeomMunger9 : public StandardMunger, public WeakPointerCallback { public: - INLINE DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state); + DXGeomMunger9(GraphicsStateGuardian *gsg, const RenderState *state); virtual ~DXGeomMunger9(); ALLOC_DELETED_CHAIN(DXGeomMunger9); diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 0921350629..904f47afc3 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -140,6 +140,7 @@ DXGraphicsStateGuardian9(GraphicsEngine *engine, GraphicsPipe *pipe) : _last_fvf = 0; _num_bound_streams = 0; + _white_vbuffer = nullptr; _vertex_shader_version_major = 0; _vertex_shader_version_minor = 0; @@ -798,9 +799,9 @@ clear(DrawableRegion *clearable) { main_flags |= D3DCLEAR_TARGET; } - if (clearable->get_clear_depth_active()) { + if (clearable->get_clear_depth_active() && + _screen->_presentation_params.EnableAutoDepthStencil) { aux_flags |= D3DCLEAR_ZBUFFER; - nassertv(_screen->_presentation_params.EnableAutoDepthStencil); } if (clearable->get_clear_stencil_active()) { @@ -4545,6 +4546,11 @@ reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params, release_all_vertex_buffers(); release_all_index_buffers(); + if (_white_vbuffer != nullptr) { + _white_vbuffer->Release(); + _white_vbuffer = nullptr; + } + // must be called before reset Thread *current_thread = Thread::get_current_thread(); _prepared_objects->begin_frame(this, current_thread); @@ -5404,6 +5410,40 @@ set_cg_device(LPDIRECT3DDEVICE9 cg_device) { #endif // HAVE_CG } +/** + * Returns a vertex buffer containing only a full-white color. + */ +LPDIRECT3DVERTEXBUFFER9 DXGraphicsStateGuardian9:: +get_white_vbuffer() { + if (_white_vbuffer != nullptr) { + return _white_vbuffer; + } + + LPDIRECT3DVERTEXBUFFER9 vbuffer; + HRESULT hr; + hr = _screen->_d3d_device->CreateVertexBuffer(sizeof(D3DCOLOR), D3DUSAGE_WRITEONLY, D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vbuffer, nullptr); + + if (FAILED(hr)) { + dxgsg9_cat.error() + << "CreateVertexBuffer failed" << D3DERRORSTRING(hr); + return nullptr; + } + + D3DCOLOR *local_pointer; + hr = vbuffer->Lock(0, sizeof(D3DCOLOR), (void **) &local_pointer, D3DLOCK_DISCARD); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr); + return false; + } + + *local_pointer = D3DCOLOR_ARGB(255, 255, 255, 255); + + vbuffer->Unlock(); + _white_vbuffer = vbuffer; + return vbuffer; +} + typedef std::string KEY; typedef struct _KEY_ELEMENT diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index 66b2fb82d5..8852f413a7 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -168,6 +168,7 @@ public: static void set_cg_device(LPDIRECT3DDEVICE9 cg_device); virtual bool get_supports_cg_profile(const std::string &name) const; + LPDIRECT3DVERTEXBUFFER9 get_white_vbuffer(); protected: void do_issue_transform(); @@ -274,12 +275,6 @@ protected: RenderBuffer::Type _cur_read_pixel_buffer; // source for copy_pixel_buffer operation - PN_stdfloat _material_ambient; - PN_stdfloat _material_diffuse; - PN_stdfloat _material_specular; - PN_stdfloat _material_shininess; - PN_stdfloat _material_emission; - enum DxgsgFogType { None, PerVertexFog=D3DRS_FOGVERTEXMODE, @@ -320,6 +315,7 @@ protected: DWORD _last_fvf; int _num_bound_streams; + LPDIRECT3DVERTEXBUFFER9 _white_vbuffer; // Cache the data necessary to bind each particular light each frame, so if // we bind a given light multiple times, we only have to compute its data diff --git a/panda/src/dxgsg9/dxShaderContext9.cxx b/panda/src/dxgsg9/dxShaderContext9.cxx index 6513ce8a89..a737c342bf 100644 --- a/panda/src/dxgsg9/dxShaderContext9.cxx +++ b/panda/src/dxgsg9/dxShaderContext9.cxx @@ -390,6 +390,8 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) { // arrays ("streams"), and we repeatedly iterate the parameters to pull // out only those for a single stream. + bool apply_white_color = false; + int number_of_arrays = gsg->_data_reader->get_num_arrays(); for (int array_index = 0; array_index < number_of_arrays; ++array_index) { const GeomVertexArrayDataHandle* array_reader = @@ -423,6 +425,11 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) { } } + if (name == InternalName::get_color() && !gsg->_vertex_colors_enabled) { + apply_white_color = true; + continue; + } + const GeomVertexArrayDataHandle *param_array_reader; Geom::NumericType numeric_type; int num_values, start, stride; @@ -435,6 +442,9 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) { // shader parameter, which can cause Bad Things to happen so I'd // like to at least get a hint as to what's gone wrong. dxgsg9_cat.info() << "Geometry contains no data for shader parameter " << *name << "\n"; + if (name == InternalName::get_color()) { + apply_white_color = true; + } continue; } @@ -564,6 +574,19 @@ update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force) { _num_bound_streams = number_of_arrays; + if (apply_white_color) { + // The shader needs a vertex color, but vertex colors are disabled. + // Bind a vertex buffer containing only one white colour. + int array_index = number_of_arrays; + LPDIRECT3DVERTEXBUFFER9 vbuffer = gsg->get_white_vbuffer(); + hr = device->SetStreamSource(array_index, vbuffer, 0, 0); + if (FAILED(hr)) { + dxgsg9_cat.error() << "SetStreamSource failed" << D3DERRORSTRING(hr); + } + vertex_element_array->add_diffuse_color_vertex_element(array_index, 0); + ++_num_bound_streams; + } + if (_vertex_element_array != nullptr && _vertex_element_array->add_end_vertex_element()) { if (dxgsg9_cat.is_debug()) { diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx index 3136d75fd0..3207c81189 100644 --- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx @@ -1229,7 +1229,10 @@ init_resized_window() { DWORD flags; D3DCOLOR clear_color; - flags = D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER; + flags = D3DCLEAR_TARGET; + if (_fb_properties.get_depth_bits() > 0) { + flags |= D3DCLEAR_ZBUFFER; + } clear_color = 0x00000000; hr = _wcontext._d3d_device-> Clear (0, nullptr, flags, clear_color, 0.0f, 0); if (FAILED(hr)) { 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/event/asyncTask.h b/panda/src/event/asyncTask.h index f5871ae2f2..5a7a2cd805 100644 --- a/panda/src/event/asyncTask.h +++ b/panda/src/event/asyncTask.h @@ -99,6 +99,27 @@ PUBLISHED: virtual void output(std::ostream &out) const; +PUBLISHED: + MAKE_PROPERTY(state, get_state); + MAKE_PROPERTY(alive, is_alive); + MAKE_PROPERTY(manager, get_manager); + + // The name of this task. + MAKE_PROPERTY(name, get_name, set_name); + + // This is a number guaranteed to be unique for each different AsyncTask + // object in the universe. + MAKE_PROPERTY(id, get_task_id); + + MAKE_PROPERTY(task_chain, get_task_chain, set_task_chain); + MAKE_PROPERTY(sort, get_sort, set_sort); + MAKE_PROPERTY(priority, get_priority, set_priority); + MAKE_PROPERTY(done_event, get_done_event, set_done_event); + + MAKE_PROPERTY(dt, get_dt); + MAKE_PROPERTY(max_dt, get_max_dt); + MAKE_PROPERTY(average_dt, get_average_dt); + protected: void jump_to_task_chain(AsyncTaskManager *manager); DoneStatus unlock_and_do_task(); diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index 3771d46d8b..06ff8b6fe4 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -61,9 +61,6 @@ PUBLISHED: int __clear__(); PUBLISHED: - // The name of this task. - MAKE_PROPERTY(name, get_name, set_name); - // The amount of seconds that have elapsed since the task was started, // according to the task manager's clock. MAKE_PROPERTY(time, get_elapsed_time); @@ -88,10 +85,6 @@ PUBLISHED: // according to the task manager's clock. MAKE_PROPERTY(frame, get_elapsed_frames); - // This is a number guaranteed to be unique for each different AsyncTask - // object in the universe. - MAKE_PROPERTY(id, get_task_id); - // This is a special variable to hold the instance dictionary in which // custom variables may be stored. PyObject *__dict__; diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index ee4c10e155..5bcd1b77e9 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -38,7 +38,8 @@ INLINE void set_matrix_view(Py_buffer &view, int flags, int length, int size, bo } else if (size == 4 && double_prec) { mat_size = sizeof(UnalignedLMatrix4d); } else { - assert(false); + nassertv_always(false); + return; // Make sure compiler knows control flow doesn't proceed. } view.len = length * mat_size; diff --git a/panda/src/express/pointerToArray_ext.h b/panda/src/express/pointerToArray_ext.h index 2daf885943..f17d1b634a 100644 --- a/panda/src/express/pointerToArray_ext.h +++ b/panda/src/express/pointerToArray_ext.h @@ -97,11 +97,11 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const; #ifdef _MSC_VER // Ugh... MSVC needs this because they still don't have a decent linker. -#include "PTA_uchar.h" -#include "PTA_ushort.h" -#include "PTA_float.h" -#include "PTA_double.h" -#include "PTA_int.h" +#include "pta_uchar.h" +#include "pta_ushort.h" +#include "pta_float.h" +#include "pta_double.h" +#include "pta_int.h" template class EXPORT_THIS Extension; template class EXPORT_THIS Extension; diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 3a9b2fea6a..649208fb47 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -226,12 +226,14 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte if (!resource) { resource = "unknown"; } - GLCAT.error() - << "Could not find Cg varying " << cgGetParameterName(p); - if (attribname) { - GLCAT.error(false) << " : " << attribname; + if (GLCAT.is_debug()) { + GLCAT.debug() + << "Could not find Cg varying " << cgGetParameterName(p); + if (attribname) { + GLCAT.debug(false) << " : " << attribname; + } + GLCAT.debug(false) << " (" << resource << ") in the compiled GLSL program.\n"; } - GLCAT.error(false) << " (" << resource << ") in the compiled GLSL program.\n"; } else if (loc != 0 && bind._id._name == "vtx_position") { // We really have to bind the vertex position to attribute 0, since @@ -312,10 +314,10 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte GLCAT.debug(false) << " is bound to a conventional attribute (" << resource << ")\n"; } - } - if (loc == CA_unknown) { - // Suggest fix to developer. - GLCAT.error() << "Try using a different semantic.\n"; + if (loc == CA_unknown) { + // Suggest fix to developer. + GLCAT.debug() << "Try using a different semantic.\n"; + } } #endif diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 66a9e6d407..eab850bdba 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -283,6 +283,13 @@ begin_frame(FrameMode mode, Thread *current_thread) { rebuild_bitplanes(); } + // The host window may not have had sRGB enabled, so we need to do this. +#ifndef OPENGLES + if (get_fb_properties().get_srgb_color()) { + glEnable(GL_FRAMEBUFFER_SRGB); + } +#endif + _gsg->set_current_properties(&get_fb_properties()); report_my_gl_errors(); return true; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 48b9112387..616784e0cc 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -4392,7 +4392,8 @@ update_standard_vertex_arrays(bool force) { GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f); } else #endif // NDEBUG - if (_data_reader->get_color_info(array_reader, num_values, numeric_type, + if (_vertex_colors_enabled && + _data_reader->get_color_info(array_reader, num_values, numeric_type, start, stride)) { if (!setup_array_data(client_pointer, array_reader, force)) { return false; @@ -4409,7 +4410,13 @@ update_standard_vertex_arrays(bool force) { glDisableClientState(GL_COLOR_ARRAY); // Since we don't have per-vertex color, the implicit color is white. - GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f); + if (_color_scale_via_lighting) { + GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f); + } else { + LColor color = _scene_graph_color; + color.componentwise_mult(_current_color_scale); + GLPf(Color4)(color[0], color[1], color[2], color[3]); + } } // Now set up each of the active texture coordinate stages--or at least diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index a96a07a2e8..64a5e77553 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2440,9 +2440,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { if (p == _color_attrib_index) { // Vertex colors are disabled or not present. Apply flat color. #ifdef STDFLOAT_DOUBLE - _glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data()); + _glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data()); #else - _glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data()); + _glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data()); #endif } } diff --git a/panda/src/gobj/textureStage.cxx b/panda/src/gobj/textureStage.cxx index 4fecd20999..0d6202d949 100644 --- a/panda/src/gobj/textureStage.cxx +++ b/panda/src/gobj/textureStage.cxx @@ -84,7 +84,7 @@ operator = (const TextureStage &other) { _combine_rgb_operand2 = other._combine_rgb_operand2; _combine_alpha_mode = other._combine_alpha_mode; _combine_alpha_source0 = other._combine_alpha_source0; - _combine_alpha_operand0 = _combine_alpha_operand0; + _combine_alpha_operand0 = other._combine_alpha_operand0; _combine_alpha_source1 = other._combine_alpha_source1; _combine_alpha_operand1 = other._combine_alpha_operand1; _combine_alpha_source2 = other._combine_alpha_source2; 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/mathutil/mersenne.h b/panda/src/mathutil/mersenne.h index 7bcb00cb8f..15d96029e4 100644 --- a/panda/src/mathutil/mersenne.h +++ b/panda/src/mathutil/mersenne.h @@ -69,14 +69,12 @@ PUBLISHED: }; private: - enum { - // Period parameters - N = 624, - M = 397, - MATRIX_A = 0x9908b0dfUL, // constant vector a - UPPER_MASK = 0x80000000UL, // most significant w-r bits - LOWER_MASK = 0x7fffffffUL, // least significant r bits - }; + // Period parameters + static const unsigned long N = 624; + static const unsigned long M = 397; + static const unsigned long MATRIX_A = 0x9908b0dfUL; // constant vector a + static const unsigned long UPPER_MASK = 0x80000000UL; // most significant w-r bits + static const unsigned long LOWER_MASK = 0x7fffffffUL; // least significant r bits unsigned long mt[N]; // the array for the state vector unsigned int mti; // mti==N+1 means mt[N] is not initialized diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 012deff1cd..5bc8228bb4 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -12,10 +12,12 @@ */ #include "movieTypeRegistry.h" + #include "string_utils.h" #include "config_movies.h" #include "config_putil.h" #include "load_dso.h" +#include "reMutexHolder.h" using std::endl; using std::string; @@ -29,6 +31,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 +45,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 +58,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 +75,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 +97,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 +154,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 +168,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 +181,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 +198,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 +220,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 +275,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/osxdisplay/osxGraphicsStateGuardian.h b/panda/src/osxdisplay/osxGraphicsStateGuardian.h index 038ba42055..96c86f28dd 100644 --- a/panda/src/osxdisplay/osxGraphicsStateGuardian.h +++ b/panda/src/osxdisplay/osxGraphicsStateGuardian.h @@ -64,7 +64,7 @@ private: CGGammaValue _gOriginalRedTable[ 256 ]; CGGammaValue _gOriginalGreenTable[ 256 ]; CGGammaValue _gOriginalBlueTable[ 256 ]; - CGTableCount _sampleCount; + uint32_t _sampleCount; CGDisplayErr _cgErr; public: diff --git a/panda/src/particlesystem/baseParticle.cxx b/panda/src/particlesystem/baseParticle.cxx index 046b2dfb40..46e18179da 100644 --- a/panda/src/particlesystem/baseParticle.cxx +++ b/panda/src/particlesystem/baseParticle.cxx @@ -68,7 +68,6 @@ write(std::ostream &out, int indent) const { out.width(indent+2); out<<""; out<<"_lifespan "<<_lifespan<<"\n"; out.width(indent+2); out<<""; out<<"_alive "<<_alive<<"\n"; out.width(indent+2); out<<""; out<<"_index "<<_index<<"\n"; - out.width(indent+2); out<<""; out<<"_last_position "<<_last_position<<"\n"; PhysicsObject::write(out, indent+2); #endif //] NDEBUG } diff --git a/panda/src/particlesystem/baseParticle.h b/panda/src/particlesystem/baseParticle.h index 487f9ba476..834327bfec 100644 --- a/panda/src/particlesystem/baseParticle.h +++ b/panda/src/particlesystem/baseParticle.h @@ -62,8 +62,6 @@ private: PN_stdfloat _lifespan; bool _alive; int _index; - - LPoint3 _last_position; }; #include "baseParticle.I" diff --git a/panda/src/pgraph/colorAttrib.cxx b/panda/src/pgraph/colorAttrib.cxx index 7bfbecee79..f1d71845cd 100644 --- a/panda/src/pgraph/colorAttrib.cxx +++ b/panda/src/pgraph/colorAttrib.cxx @@ -68,7 +68,7 @@ make_off() { */ CPT(RenderAttrib) ColorAttrib:: make_default() { - return make_off(); + return make_vertex(); } /** @@ -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/colorAttrib.h b/panda/src/pgraph/colorAttrib.h index a92543c855..b652233a62 100644 --- a/panda/src/pgraph/colorAttrib.h +++ b/panda/src/pgraph/colorAttrib.h @@ -88,7 +88,7 @@ public: register_type(_type_handle, "ColorAttrib", RenderAttrib::get_class_type()); _attrib_slot = register_slot(_type_handle, 100, - new ColorAttrib(T_off, LColor(1, 1, 1, 1))); + new ColorAttrib(T_vertex, LColor::zero())); } virtual TypeHandle get_type() const { return get_class_type(); 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 c339d4918e..ab4db6ea2f 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -200,7 +200,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..1d02442451 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,60 +1005,11 @@ set_glyph_shift(PN_stdfloat glyph_shift) { */ INLINE void TextNode:: clear_glyph_shift() { + MutexHolder holder(_lock); TextProperties::clear_glyph_shift(); invalidate_with_measure(); } - -/** - * Changes the text that is displayed under the TextNode. - */ -INLINE void TextNode:: -set_text(const std::string &text) { - TextEncoder::set_text(text); - invalidate_with_measure(); -} - -/** - * The two-parameter version of set_text() accepts an explicit encoding; the - * text is immediately decoded and stored as a wide-character string. - * Subsequent calls to get_text() will return the same text re-encoded using - * whichever encoding is specified by set_encoding(). - */ -INLINE void TextNode:: -set_text(const std::string &text, TextNode::Encoding encoding) { - TextEncoder::set_text(text, encoding); - invalidate_with_measure(); -} - -/** - * Removes the text from the TextNode. - */ -INLINE void TextNode:: -clear_text() { - TextEncoder::clear_text(); - invalidate_with_measure(); -} - -/** - * Appends the indicates string to the end of the stored text. - */ -INLINE void TextNode:: -append_text(const std::string &text) { - TextEncoder::append_text(text); - invalidate_with_measure(); -} - -/** - * Appends a single character to the end of the stored text. This may be a - * wide character, up to 16 bits in Unicode. - */ -INLINE void TextNode:: -append_unicode_char(wchar_t character) { - TextEncoder::append_unicode_char(character); - invalidate_with_measure(); -} - /** * Returns a string that represents the contents of the text, as it has been * formatted by wordwrap rules. @@ -1001,26 +1031,6 @@ calc_width(const std::string &line) const { return calc_width(decode_text(line)); } -/** - * Changes the text that is displayed under the TextNode, with a wide text. - * This automatically sets the string reported by get_text() to the 8-bit - * encoded version of the same string. - */ -INLINE void TextNode:: -set_wtext(const std::wstring &wtext) { - TextEncoder::set_wtext(wtext); - invalidate_with_measure(); -} - -/** - * Appends the indicates string to the end of the stored wide-character text. - */ -INLINE void TextNode:: -append_wtext(const std::wstring &wtext) { - TextEncoder::append_wtext(wtext); - invalidate_with_measure(); -} - /** * Returns a wstring that represents the contents of the text, as it has been * formatted by wordwrap rules. @@ -1030,6 +1040,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 +1051,7 @@ get_wordwrapped_wtext() const { */ INLINE PN_stdfloat TextNode:: get_left() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[0]; } @@ -1050,6 +1062,7 @@ get_left() const { */ INLINE PN_stdfloat TextNode:: get_right() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[0]; } @@ -1060,6 +1073,7 @@ get_right() const { */ INLINE PN_stdfloat TextNode:: get_bottom() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[1]; } @@ -1070,6 +1084,7 @@ get_bottom() const { */ INLINE PN_stdfloat TextNode:: get_top() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[1]; } @@ -1079,6 +1094,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 +1104,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 +1115,7 @@ get_width() const { */ INLINE LPoint3 TextNode:: get_upper_left_3d() const { + MutexHolder holder(_lock); check_measure(); return _ul3d; } @@ -1108,6 +1126,7 @@ get_upper_left_3d() const { */ INLINE LPoint3 TextNode:: get_lower_right_3d() const { + MutexHolder holder(_lock); check_measure(); return _lr3d; } @@ -1118,10 +1137,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 +1160,7 @@ get_num_rows() const { */ INLINE void TextNode:: update() { + MutexHolder holder(_lock); check_rebuild(); } @@ -1140,8 +1172,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..5a0c0c7f7f 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,272 @@ 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(); +} + +/** + * Called whenever the text has been changed. + */ +void TextNode:: +text_changed() { + MutexHolder holder(_lock); + invalidate_with_measure(); +} + +/** + * 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 +668,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 +697,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 +725,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 +777,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 +793,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 +845,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 +869,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..02e1cb51c7 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; @@ -180,14 +182,6 @@ PUBLISHED: INLINE void set_glyph_shift(PN_stdfloat glyph_shift); INLINE void clear_glyph_shift(); - // These methods are inherited from TextEncoder, but we override here so we - // can flag the TextNode as dirty when they have been changed. - INLINE void set_text(const std::string &text); - INLINE void set_text(const std::string &text, Encoding encoding); - INLINE void clear_text(); - INLINE void append_text(const std::string &text); - INLINE void append_unicode_char(wchar_t character); - // After the text has been set, you can query this to determine how it will // be wordwrapped. INLINE std::string get_wordwrapped_text() const; @@ -201,10 +195,6 @@ PUBLISHED: bool has_character(wchar_t character) const; bool is_whitespace(wchar_t character) const; - // Direct support for wide-character strings. - INLINE void set_wtext(const std::wstring &wtext); - INLINE void append_wtext(const std::wstring &text); - INLINE std::wstring get_wordwrapped_wtext() const; PN_stdfloat calc_width(const std::wstring &line) const; @@ -225,11 +215,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); @@ -243,8 +233,6 @@ PUBLISHED: MAKE_PROPERTY(usage_hint, get_usage_hint, set_usage_hint); MAKE_PROPERTY(flatten_flags, get_flatten_flags, set_flatten_flags); - MAKE_PROPERTY(text, get_text, set_text); - MAKE_PROPERTY2(font, has_font, get_font, set_font, clear_font); MAKE_PROPERTY2(small_caps, has_small_caps, get_small_caps, set_small_caps, clear_small_caps); @@ -279,6 +267,9 @@ PUBLISHED: set_text_scale, clear_text_scale); public: + // From parent class TextEncoder; + virtual void text_changed() final; + // From parent class PandaNode virtual int get_unsafe_to_apply_attribs() const; virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, @@ -312,12 +303,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/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 954d8aca41..b42ee43992 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -283,6 +283,25 @@ set_properties_now(WindowProperties &properties) { return; } + if (properties.has_undecorated() || + properties.has_fixed_size()) { + if (properties.has_undecorated()) { + _properties.set_undecorated(properties.get_undecorated()); + properties.clear_undecorated(); + } + if (properties.has_fixed_size()) { + _properties.set_fixed_size(properties.get_fixed_size()); + properties.clear_fixed_size(); + } + DWORD window_style = make_style(_properties); + SetWindowLong(_hWnd, GWL_STYLE, window_style); + + // We need to call this to ensure that the style change takes effect. + SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | + SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_SHOWWINDOW); + } + if (properties.has_title()) { std::string title = properties.get_title(); _properties.set_title(title); @@ -487,7 +506,7 @@ open_window() { // CreateWindow() and know which window it is sending events to even before // it gives us a handle. Warning: this is not thread safe! _creating_window = this; - bool opened = open_graphic_window(is_fullscreen()); + bool opened = open_graphic_window(); _creating_window = nullptr; if (!opened) { @@ -865,7 +884,9 @@ do_fullscreen_switch() { return false; } - DWORD window_style = make_style(true); + WindowProperties props(_properties); + props.set_fullscreen(true); + DWORD window_style = make_style(props); SetWindowLong(_hWnd, GWL_STYLE, window_style); WINDOW_METRICS metrics; @@ -885,7 +906,10 @@ do_fullscreen_switch() { bool WinGraphicsWindow:: do_windowed_switch() { do_fullscreen_disable(); - DWORD window_style = make_style(false); + + WindowProperties props(_properties); + props.set_fullscreen(false); + DWORD window_style = make_style(props); SetWindowLong(_hWnd, GWL_STYLE, window_style); WINDOW_METRICS metrics; @@ -928,7 +952,7 @@ support_overlay_window(bool) { * Constructs a dwStyle for the specified mode, be it windowed or fullscreen. */ DWORD WinGraphicsWindow:: -make_style(bool fullscreen) { +make_style(const WindowProperties &properties) { // from MSDN: An OpenGL window has its own pixel format. Because of this, // only device contexts retrieved for the client area of an OpenGL window // are allowed to draw into the window. As a result, an OpenGL window @@ -938,7 +962,7 @@ make_style(bool fullscreen) { DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - if (fullscreen){ + if (_properties.get_fullscreen()) { window_style |= WS_SYSMENU; } else if (!_properties.get_undecorated()) { window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); @@ -1015,8 +1039,8 @@ calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics, * Creates a regular or fullscreen window. */ bool WinGraphicsWindow:: -open_graphic_window(bool fullscreen) { - DWORD window_style = make_style(fullscreen); +open_graphic_window() { + DWORD window_style = make_style(_properties); wstring title; if (_properties.has_title()) { @@ -2186,12 +2210,12 @@ update_cursor_window(WinGraphicsWindow *to_window) { // We are leaving a graphics window; we should restore the Win2000 // effects. if (_got_saved_params) { - SystemParametersInfo(SPI_SETMOUSETRAILS, 0, - (PVOID)_saved_mouse_trails, 0); + SystemParametersInfo(SPI_SETMOUSETRAILS, _saved_mouse_trails, + 0, 0); SystemParametersInfo(SPI_SETCURSORSHADOW, 0, - (PVOID)_saved_cursor_shadow, 0); + _saved_cursor_shadow ? (PVOID)1 : nullptr, 0); SystemParametersInfo(SPI_SETMOUSEVANISH, 0, - (PVOID)_saved_mouse_vanish, 0); + _saved_mouse_vanish ? (PVOID)1 : nullptr, 0); _got_saved_params = false; } diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index ff0340aa0d..5b0bb208c1 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -119,7 +119,7 @@ protected: virtual bool calculate_metrics(bool fullscreen, DWORD style, WINDOW_METRICS &metrics, bool &has_origin); - virtual DWORD make_style(bool fullscreen); + DWORD make_style(const WindowProperties &properties); virtual void reconsider_fullscreen_size(DWORD &x_size, DWORD &y_size, DWORD &bitdepth); @@ -127,7 +127,7 @@ protected: virtual void support_overlay_window(bool flag); private: - bool open_graphic_window(bool fullscreen); + bool open_graphic_window(); void adjust_z_order(); void adjust_z_order(WindowProperties::ZOrder last_z_order, WindowProperties::ZOrder this_z_order); 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/display/test_color_buffer.py b/tests/display/test_color_buffer.py new file mode 100644 index 0000000000..d95913b0f7 --- /dev/null +++ b/tests/display/test_color_buffer.py @@ -0,0 +1,293 @@ +from panda3d import core +import pytest + +TEST_COLOR = core.LColor(1, 127/255.0, 0, 127/255.0) +TEST_COLOR_SCALE = core.LVecBase4(0.5, 0.5, 0.5, 0.5) +TEST_SCALED_COLOR = core.LColor(TEST_COLOR) +TEST_SCALED_COLOR.componentwise_mult(TEST_COLOR_SCALE) +FUZZ = 0.02 + + +@pytest.fixture(scope='session', params=[False, True], ids=["shader:off", "shader:auto"]) +def shader_attrib(request): + """Returns two ShaderAttribs: one with auto shader, one without.""" + if request.param: + return core.ShaderAttrib.make_default().set_shader_auto(True) + else: + return core.ShaderAttrib.make_off() + + +@pytest.fixture(scope='session', params=["mat:off", "mat:empty", "mat:amb", "mat:diff", "mat:both"]) +def material_attrib(request): + """Returns two MaterialAttribs: one with material, one without. It + shouldn't really matter what we set them to, since the tests in here do + not use lighting, and therefore the material should be ignored.""" + + if request.param == "mat:off": + return core.MaterialAttrib.make_off() + + elif request.param == "mat:empty": + return core.MaterialAttrib.make(core.Material()) + + elif request.param == "mat:amb": + mat = core.Material() + mat.ambient = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + elif request.param == "mat:diff": + mat = core.Material() + mat.diffuse = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + elif request.param == "mat:both": + mat = core.Material() + mat.diffuse = (0.1, 1, 0.5, 1) + mat.ambient = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + +@pytest.fixture(scope='module', params=[False, True], ids=["srgb:off", "srgb:on"]) +def color_region(request, graphics_pipe): + """Creates and returns a DisplayRegion with a depth buffer.""" + + engine = core.GraphicsEngine() + engine.set_threading_model("") + + host_fbprops = core.FrameBufferProperties() + host_fbprops.force_hardware = True + + host = engine.make_output( + graphics_pipe, + 'host', + 0, + host_fbprops, + core.WindowProperties.size(32, 32), + core.GraphicsPipe.BF_refuse_window, + ) + engine.open_windows() + + if host is None: + pytest.skip("GraphicsPipe cannot make offscreen buffers") + + fbprops = core.FrameBufferProperties() + fbprops.force_hardware = True + fbprops.set_rgba_bits(8, 8, 8, 8) + fbprops.srgb_color = request.param + + buffer = engine.make_output( + graphics_pipe, + 'buffer', + 0, + fbprops, + core.WindowProperties.size(32, 32), + core.GraphicsPipe.BF_refuse_window, + host.gsg, + host + ) + engine.open_windows() + + if buffer is None: + pytest.skip("Cannot make color buffer") + + if fbprops.srgb_color != buffer.get_fb_properties().srgb_color: + pytest.skip("Cannot make buffer with required srgb_color setting") + + buffer.set_clear_color_active(True) + buffer.set_clear_color((0, 0, 0, 1)) + + yield buffer.make_display_region() + + if buffer is not None: + engine.remove_window(buffer) + + +def render_color_pixel(region, state, vertex_color=None): + """Renders a fragment using the specified render settings, and returns the + resulting color value.""" + + # Set up the scene with a blank card rendering at specified distance. + scene = core.NodePath("root") + scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always)) + + camera = scene.attach_new_node(core.Camera("camera")) + camera.node().get_lens(0).set_near_far(1, 3) + camera.node().set_cull_bounds(core.OmniBoundingVolume()) + + if vertex_color is not None: + format = core.GeomVertexFormat.get_v3cp() + else: + format = core.GeomVertexFormat.get_v3() + + vdata = core.GeomVertexData("card", format, core.Geom.UH_static) + vdata.unclean_set_num_rows(4) + + vertex = core.GeomVertexWriter(vdata, "vertex") + vertex.set_data3(core.Vec3.rfu(-1, 0, 1)) + vertex.set_data3(core.Vec3.rfu(-1, 0, -1)) + vertex.set_data3(core.Vec3.rfu(1, 0, 1)) + vertex.set_data3(core.Vec3.rfu(1, 0, -1)) + + if vertex_color is not None: + color = core.GeomVertexWriter(vdata, "color") + color.set_data4(vertex_color) + color.set_data4(vertex_color) + color.set_data4(vertex_color) + color.set_data4(vertex_color) + + strip = core.GeomTristrips(core.Geom.UH_static) + strip.set_shade_model(core.Geom.SM_uniform) + strip.add_next_vertices(4) + strip.close_primitive() + + geom = core.Geom(vdata) + geom.add_primitive(strip) + + gnode = core.GeomNode("card") + gnode.add_geom(geom, state) + card = scene.attach_new_node(gnode) + card.set_pos(0, 2, 0) + card.set_scale(60) + + region.active = True + region.camera = camera + + color_texture = core.Texture("color") + region.window.add_render_texture(color_texture, + core.GraphicsOutput.RTM_copy_ram, + core.GraphicsOutput.RTP_color) + + region.window.engine.render_frame() + region.window.clear_render_textures() + + col = core.LColor() + color_texture.peek().lookup(col, 0.5, 0.5) + return col + + +def test_color_write_mask(color_region): + state = core.RenderState.make( + core.ColorWriteAttrib.make(core.ColorWriteAttrib.C_green), + ) + result = render_color_pixel(color_region, state) + assert result == (0, 1, 0, 1) + + +def test_color_empty(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_color_off(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_color_flat(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_flat(TEST_COLOR), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_vertex(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_empty_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_off_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result == (1, 1, 1, 1) + + +def test_scaled_color_empty(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_scaled_color_off(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_scaled_color_flat(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_flat(TEST_COLOR), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_vertex(), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_empty_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_off_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR_SCALE, FUZZ) + diff --git a/tests/display/test_depth_buffer.py b/tests/display/test_depth_buffer.py index 8581fe47ea..c655758703 100644 --- a/tests/display/test_depth_buffer.py +++ b/tests/display/test_depth_buffer.py @@ -91,8 +91,6 @@ def render_depth_pixel(region, distance, near, far, clear=None, write=True): region.window.engine.render_frame() region.window.clear_render_textures() - depth_texture.write("test2.png") - col = core.LColor() depth_texture.peek().lookup(col, 0.5, 0.5) return col[0] diff --git a/tests/dtoolutil/test_textencoder.py b/tests/dtoolutil/test_textencoder.py new file mode 100644 index 0000000000..ef03c7d826 --- /dev/null +++ b/tests/dtoolutil/test_textencoder.py @@ -0,0 +1,101 @@ +import sys +import pytest +from panda3d.core import TextEncoder + +if sys.version_info >= (3, 0): + unichr = chr + xrange = range + + +def valid_characters(): + """Generator yielding all valid Unicode code points.""" + + for i in xrange(0xd800): + yield unichr(i) + + for i in xrange(0xe000, sys.maxunicode + 1): + if i != 0xfeff and i & 0xfffe != 0xfffe: + yield unichr(i) + + +def test_text_decode_iso8859(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_iso8859) + + for i in xrange(255): + enc = unichr(i).encode('latin-1') + assert len(enc) == 1 + + dec = encoder.decode_text(enc) + assert len(dec) == 1 + assert ord(dec) == i + + +def test_text_decode_utf8(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_utf8) + + for c in valid_characters(): + enc = c.encode('utf-8') + assert len(enc) <= 4 + + dec = encoder.decode_text(enc) + assert len(dec) == 1 + assert dec == c + + +def test_text_decode_utf16be(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_utf16be) + + for c in valid_characters(): + enc = c.encode('utf-16be') + + dec = encoder.decode_text(enc) + assert len(c) == len(dec) + assert c == dec + + +def test_text_encode_iso8859(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_iso8859) + + for i in xrange(255): + c = unichr(i) + enc = encoder.encode_wtext(c) + assert enc == c.encode('latin-1') + + +def test_text_encode_utf8(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_utf8) + + for c in valid_characters(): + enc = encoder.encode_wtext(c) + assert enc == c.encode('utf-8') + + +def test_text_encode_utf16be(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_utf16be) + + for c in valid_characters(): + enc = encoder.encode_wtext(c) + assert enc == c.encode('utf-16-be') + + +def test_text_append_unicode_char(): + encoder = TextEncoder() + encoder.set_encoding(TextEncoder.E_iso8859) + + code_points = [] + for code_point in [0, 1, 127, 128, 255, 256, 0xfffd, 0x10000, 0x10ffff]: + if code_point <= sys.maxunicode: + code_points.append(code_point) + encoder.append_unicode_char(code_point) + + encoded = encoder.get_wtext() + assert len(encoded) == len(code_points) + + for a, b in zip(code_points, encoded): + assert a == ord(b) 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))