Merge branch 'master' into deploy-ng

This commit is contained in:
rdb 2018-10-15 15:42:48 +02:00
commit c28045990c
91 changed files with 2045 additions and 742 deletions

View File

@ -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
=============

View File

@ -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

View File

@ -147,9 +147,6 @@ public:
private:
constexpr TypeHandle(int index);
// Only kept temporarily for ABI compatibility.
static TypeHandle _none;
int _index;
friend class TypeRegistry;
};

View File

@ -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 &copy) :
_directories(copy._directories)
{
}
/**
*
*/
void DSearchPath::
operator = (const DSearchPath &copy) {
_directories = copy._directories;
}
/**
*
*/
DSearchPath::
~DSearchPath() {
}
/**
* Removes all the directories from the search list.
*/

View File

@ -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 &copy);
void operator = (const DSearchPath &copy);
~DSearchPath();
DSearchPath(const DSearchPath &copy) = default;
DSearchPath(DSearchPath &&from) = default;
~DSearchPath() = default;
DSearchPath &operator = (const DSearchPath &copy) = default;
DSearchPath &operator = (DSearchPath &&from) = default;
void clear();
void append_directory(const Filename &directory);

View File

@ -1,2 +1,3 @@
#include "filename_ext.cxx"
#include "globPattern_ext.cxx"
#include "textEncoder_ext.cxx"

View File

@ -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) {
}

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}
}
/**

View File

@ -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) {

View File

@ -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 &copy);
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 {

View File

@ -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<TextEncoder>::
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<TextEncoder>::
decode_text(PyObject *text) const {
return decode_text(text, _this->get_encoding());
}

View File

@ -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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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<TextEncoder>::
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

View File

@ -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<TextEncoder> : public ExtensionBase<TextEncoder> {
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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
typedef struct __dirstream DIR;
struct dirent;
typedef unsigned long ino_t;

View File

@ -0,0 +1 @@
struct inotify_event;

View File

@ -0,0 +1,3 @@
struct winsize;
struct termio;

View File

@ -0,0 +1,3 @@
#include <sys/types.h>
struct posix_typed_mem_info;

View File

@ -0,0 +1 @@
#include <sys/time.h>

View File

@ -0,0 +1 @@
struct sysinfo;

View File

@ -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;
}
/**
*
*/

View File

@ -19,6 +19,7 @@
#include "configPage.h"
#include "vector_string.h"
#include "numeric_types.h"
#include "filename.h"
#include <vector>
@ -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;

View File

@ -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();
}

View File

@ -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();

View File

@ -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;
}
/**

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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.

View File

@ -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",

View File

@ -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"

View File

@ -12,6 +12,7 @@
*/
#import "cocoaPandaApp.h"
#include "config_cocoadisplay.h"
@implementation CocoaPandaApp
- (void) sendEvent: (NSEvent *) event {

View File

@ -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();
}

View File

@ -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.

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -18,6 +18,8 @@
#include "graphicsEngine.h"
#include "config_display.h"
#include "nativeWindowHandle.h"
#include "mouseButton.h"
#include "throw_event.h"
using std::string;

View File

@ -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);
}

View File

@ -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);
}
/**
*
*/

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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)) {

View File

@ -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();

View File

@ -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 &copy);

View File

@ -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<const EggMesherEdge *> Edges;
typedef plist<EggMesherStrip *> Strips;

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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__;

View File

@ -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;

View File

@ -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<PTA_uchar>;
template class EXPORT_THIS Extension<PTA_ushort>;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -25,6 +25,8 @@
#include "configVariableInt.h"
#include "pStatCollector.h"
#include "filename.h"
#include "pmutex.h"
#include "mutexHolder.h"
#include <stdint.h>
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;

View File

@ -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

View File

@ -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;

View File

@ -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<std::string, MakeAudioFunc> _audio_type_registry;
pmap<std::string, std::string> _deferred_audio_types;
ReMutex _video_lock;
pmap<std::string, MakeVideoFunc> _video_type_registry;
pmap<std::string, std::string> _deferred_video_types;
};

View File

@ -64,7 +64,7 @@ private:
CGGammaValue _gOriginalRedTable[ 256 ];
CGGammaValue _gOriginalGreenTable[ 256 ];
CGGammaValue _gOriginalBlueTable[ 256 ];
CGTableCount _sampleCount;
uint32_t _sampleCount;
CGDisplayErr _cgErr;
public:

View File

@ -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
}

View File

@ -62,8 +62,6 @@ private:
PN_stdfloat _lifespan;
bool _alive;
int _index;
LPoint3 _last_position;
};
#include "baseParticle.I"

View File

@ -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:

View File

@ -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();

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -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());

View File

@ -58,4 +58,6 @@ wait(double timeout) {
_mutex.lock();
}
#undef PAUSE
#endif // MUTEX_SPINLOCK

View File

@ -54,4 +54,6 @@ try_lock() {
}
}
#undef PAUSE
#endif // MUTEX_SPINLOCK

View File

@ -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();
}
/**

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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.

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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')))

106
tests/text/test_textnode.py Normal file
View File

@ -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))