Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
10b291379f
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
|
||||
matrix:
|
||||
profile:
|
||||
- ubuntu-xenial-standard-unity-makefile
|
||||
- ubuntu-bionic-standard-unity-makefile
|
||||
- ubuntu-bionic-coverage-ninja
|
||||
- macos-eigen-coverage-unity-xcode
|
||||
- macos-nometa-standard-makefile
|
||||
|
|
@ -19,8 +19,8 @@ jobs:
|
|||
- windows-nopython-nometa-standard-msvc
|
||||
|
||||
include:
|
||||
- profile: ubuntu-xenial-standard-unity-makefile
|
||||
os: ubuntu-16.04
|
||||
- profile: ubuntu-bionic-standard-unity-makefile
|
||||
os: ubuntu-18.04
|
||||
config: Standard
|
||||
unity: YES
|
||||
generator: Unix Makefiles
|
||||
|
|
@ -333,12 +333,12 @@ jobs:
|
|||
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-16.04, windows-2016, macOS-latest]
|
||||
os: [ubuntu-18.04, windows-2016, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install dependencies (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-16.04'
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential bison flex libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ It is important for acceptance that the change is implemented in a way that fits
|
|||
the general design principles of the Panda3D API, and fits well with the general
|
||||
priorities of the team. Therefore, prior discussion with other developers is
|
||||
critical. Issues can be used to facilitate this, but we also invite you to
|
||||
visit the #development channel on Discord (or #panda3d-devel on FreeNode IRC).
|
||||
visit the #development channel on Discord (or #panda3d-devel on Libera Chat).
|
||||
|
||||
We also recommend that you familiarize yourself with the established coding
|
||||
style and design patterns of Panda3D, to reduce the amount of changes that have
|
||||
|
|
|
|||
|
|
@ -249,10 +249,3 @@ to everyone who has donated!
|
|||
<a href="https://opencollective.com/panda3d" target="_blank">
|
||||
<img src="https://opencollective.com/panda3d/contribute/button@2x.png?color=blue" width=300 />
|
||||
</a>
|
||||
|
||||
### Gold Sponsors
|
||||
[](https://opencollective.com/panda3d/tiers/gold-sponsor/0/website)
|
||||
[](https://opencollective.com/panda3d/tiers/gold-sponsor/1/website)
|
||||
[](https://opencollective.com/panda3d/tiers/gold-sponsor/2/website)
|
||||
[](https://opencollective.com/panda3d/tiers/gold-sponsor/3/website)
|
||||
[](https://opencollective.com/panda3d/tiers/gold-sponsor/4/website)
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ NodePath AICharacter::get_char_render() {
|
|||
return _window_render;
|
||||
}
|
||||
|
||||
std::string AICharacter::get_name() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
void AICharacter::set_pf_guide(bool pf_guide) {
|
||||
_pf_guide = pf_guide;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ class EXPCL_PANDAAI AICharacter : public ReferenceCount {
|
|||
void set_char_render(NodePath render);
|
||||
NodePath get_char_render();
|
||||
|
||||
std::string get_name();
|
||||
|
||||
PUBLISHED:
|
||||
double get_mass();
|
||||
void set_mass(double m);
|
||||
|
|
@ -65,6 +67,8 @@ PUBLISHED:
|
|||
|
||||
explicit AICharacter(std::string model_name, NodePath model_np, double mass, double movt_force, double max_force);
|
||||
~AICharacter();
|
||||
|
||||
MAKE_PROPERTY(name, get_name);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1002,17 +1002,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
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 SpeedTreeNode::
|
||||
is_renderable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the node's contents to the CullResult we are building up during the
|
||||
* cull traversal, so that it will be drawn at render time. For most nodes
|
||||
|
|
@ -1162,6 +1151,7 @@ set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const {
|
|||
void SpeedTreeNode::
|
||||
init_node() {
|
||||
PandaNode::set_cull_callback();
|
||||
PandaNode::set_renderable();
|
||||
|
||||
_is_valid = false;
|
||||
_needs_repopulate = false;
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ public:
|
|||
GeomTransformer &transformer);
|
||||
|
||||
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
|
||||
virtual bool is_renderable() const;
|
||||
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
|
||||
|
||||
void prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *net_state);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""Actor module: contains the Actor class.
|
||||
|
||||
See the :ref:`models-and-actors` page in the Programming Guide to learn
|
||||
more about loading models and animated actors.
|
||||
See the :ref:`loading-actors-and-animations` page in the Programming Guide
|
||||
to learn more about loading animated models.
|
||||
"""
|
||||
|
||||
__all__ = ['Actor']
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ the lower-level :class:`panda3d.core.Character` implementation.
|
|||
It loads and controls an animated character and manages the animations
|
||||
playing on it.
|
||||
|
||||
See the :ref:`models-and-actors` page in the Programming Guide to learn
|
||||
more about loading models and animated actors.
|
||||
See the :ref:`loading-actors-and-animations` page in the Programming Guide
|
||||
to learn more about loading animated models.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -226,6 +226,10 @@ unpack_object() {
|
|||
std::string str;
|
||||
_this->unpack_string(str);
|
||||
object = PyUnicode_FromStringAndSize(str.data(), str.size());
|
||||
if (object == nullptr) {
|
||||
nassert_raise("Unable to decode UTF-8 string; use blob type for binary data");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ ignoreImports = {
|
|||
'direct.showbase.PythonUtil': ['pstats', 'profile'],
|
||||
|
||||
'toml.encoder': ['numpy'],
|
||||
'py._builtin': ['__builtin__'],
|
||||
}
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
|
|
@ -106,7 +107,7 @@ overrideModules = {
|
|||
# Used by the warnings module, among others, to get line numbers. Since
|
||||
# we set __file__, this would cause it to try and extract Python code
|
||||
# lines from the main executable, which we don't want.
|
||||
'linecache': """__all__ = ["getline", "clearcache", "checkcache"]
|
||||
'linecache': """__all__ = ["getline", "clearcache", "checkcache", "lazycache"]
|
||||
|
||||
cache = {}
|
||||
|
||||
|
|
@ -662,7 +663,7 @@ okMissing = [
|
|||
'email.Iterators', '_subprocess', 'gestalt', 'java.lang',
|
||||
'direct.extensions_native.extensions_darwin', '_manylinux',
|
||||
'collections.Iterable', 'collections.Mapping', 'collections.MutableMapping',
|
||||
'collections.Sequence', 'numpy_distutils',
|
||||
'collections.Sequence', 'numpy_distutils', '_winapi',
|
||||
]
|
||||
|
||||
# Since around macOS 10.15, Apple's codesigning process has become more strict.
|
||||
|
|
@ -2625,9 +2626,9 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
|
|||
(or self.path if None). Returns a tuple like (fp, path, stuff), where
|
||||
stuff is a tuple like (suffix, mode, type). """
|
||||
|
||||
if imp.is_frozen(name):
|
||||
# Don't pick up modules that are frozen into p3dpython.
|
||||
raise ImportError("'%s' is a frozen module" % (name))
|
||||
#if imp.is_frozen(name):
|
||||
# # Don't pick up modules that are frozen into p3dpython.
|
||||
# raise ImportError("'%s' is a frozen module" % (name))
|
||||
|
||||
if parent is not None:
|
||||
fullname = parent.__name__+'.'+name
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ PACKAGE_DATA_DIRS = {
|
|||
('cefpython3/Chromium Embedded Framework.framework/Chromium Embedded Framework', '', {'PKG_DATA_MAKE_EXECUTABLE'}),
|
||||
],
|
||||
'pytz': [('pytz/zoneinfo/*', 'zoneinfo', ())],
|
||||
'certifi': [('certifi/cacert.pem', '', {})],
|
||||
}
|
||||
|
||||
# Some dependencies have extra directories that need to be scanned for DLLs.
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ class OnscreenImage(DirectObject, NodePath):
|
|||
tex = image
|
||||
else:
|
||||
# It's a Texture file name
|
||||
tex = base.loader.loadTexture(image)
|
||||
tex = TexturePool.loadTexture(image)
|
||||
if not tex:
|
||||
raise IOError('Could not load texture: %s' % (image))
|
||||
cm = CardMaker('OnscreenImage')
|
||||
cm.setFrame(-1, 1, -1, 1)
|
||||
self.assign(parent.attachNewNode(cm.generate(), sort))
|
||||
|
|
|
|||
|
|
@ -348,10 +348,14 @@ class MetaInterval(CMetaInterval):
|
|||
def getManager(self):
|
||||
return self.__manager
|
||||
|
||||
manager = property(getManager, setManager)
|
||||
|
||||
def setT(self, t):
|
||||
self.__updateIvals()
|
||||
CMetaInterval.setT(self, t)
|
||||
|
||||
t = property(CMetaInterval.getT, setT)
|
||||
|
||||
def start(self, startT = 0.0, endT = -1.0, playRate = 1.0):
|
||||
self.__updateIvals()
|
||||
self.setupPlay(startT, endT, playRate, 0)
|
||||
|
|
@ -475,6 +479,8 @@ class MetaInterval(CMetaInterval):
|
|||
else:
|
||||
CMetaInterval.setPlayRate(self, playRate)
|
||||
|
||||
play_rate = property(CMetaInterval.getPlayRate, setPlayRate)
|
||||
|
||||
def __doPythonCallbacks(self):
|
||||
# This function invokes any Python-level Intervals that need
|
||||
# to be invoked at this point in time. It must be called
|
||||
|
|
@ -549,6 +555,8 @@ class MetaInterval(CMetaInterval):
|
|||
self.__updateIvals()
|
||||
return CMetaInterval.getDuration(self)
|
||||
|
||||
duration = property(getDuration)
|
||||
|
||||
def __repr__(self, *args, **kw):
|
||||
# This function overrides from the parent level to force it to
|
||||
# update the interval list first, if necessary.
|
||||
|
|
|
|||
|
|
@ -1127,8 +1127,6 @@ def normalDistrib(a, b, gauss=random.gauss):
|
|||
uniformly onto the curve inside [a, b]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
https://statweb.stanford.edu/~naras/jsm/NormalDensity/NormalDensity.html
|
||||
|
||||
The 68-95-99.7% Rule
|
||||
====================
|
||||
All normal density curves satisfy the following property which is often
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ class ShowBase(DirectObject.DirectObject):
|
|||
of :meth:`~panda3d.core.GraphicsWindow.setUnexposedDraw()`.
|
||||
|
||||
:param callbackWindowDict: If not None, a
|
||||
:class:`~panda3d.core.CallbackGraphicWindow`
|
||||
:class:`~panda3d.core.CallbackGraphicsWindow`
|
||||
is created instead, which allows the caller
|
||||
to create the actual window with its own
|
||||
OpenGL context, and direct Panda's rendering
|
||||
|
|
|
|||
|
|
@ -64,10 +64,14 @@ csincos(float v, float *sin_result, float *cos_result) {
|
|||
fstp DWORD ptr [edx]
|
||||
fstp DWORD ptr [eax]
|
||||
}
|
||||
#else //!_X86_
|
||||
#elif defined(__APPLE__)
|
||||
__sincosf(v, sin_result, cos_result);
|
||||
#elif defined(_GNU_SOURCE)
|
||||
sincosf(v, sin_result, cos_result);
|
||||
#else
|
||||
*sin_result = sinf(v);
|
||||
*cos_result = cosf(v);
|
||||
#endif //!_X86_
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -231,6 +235,10 @@ csincos(double v, double *sin_result, double *cos_result) {
|
|||
fstp QWORD ptr [edx]
|
||||
fstp QWORD ptr [eax]
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
__sincos(v, sin_result, cos_result);
|
||||
#elif defined(_GNU_SOURCE)
|
||||
sincos(v, sin_result, cos_result);
|
||||
#else //!_X86_
|
||||
*sin_result = sin(v);
|
||||
*cos_result = cos(v);
|
||||
|
|
|
|||
|
|
@ -1876,13 +1876,13 @@ open_read(std::ifstream &stream) const {
|
|||
#endif
|
||||
|
||||
stream.clear();
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
wstring os_specific = to_os_specific_w();
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
#else
|
||||
string os_specific = to_os_specific();
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
#endif // _WIN32
|
||||
#endif // _MSC_VER
|
||||
|
||||
return (!stream.fail());
|
||||
}
|
||||
|
|
@ -1926,11 +1926,11 @@ open_write(std::ofstream &stream, bool truncate) const {
|
|||
#endif
|
||||
|
||||
stream.clear();
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
wstring os_specific = to_os_specific_w();
|
||||
#else
|
||||
string os_specific = to_os_specific();
|
||||
#endif // _WIN32
|
||||
#endif // _MSC_VER
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
|
||||
return (!stream.fail());
|
||||
|
|
@ -1958,11 +1958,11 @@ open_append(std::ofstream &stream) const {
|
|||
#endif
|
||||
|
||||
stream.clear();
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
wstring os_specific = to_os_specific_w();
|
||||
#else
|
||||
string os_specific = to_os_specific();
|
||||
#endif // _WIN32
|
||||
#endif // _MSC_VER
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
|
||||
return (!stream.fail());
|
||||
|
|
@ -2000,11 +2000,11 @@ open_read_write(std::fstream &stream, bool truncate) const {
|
|||
#endif
|
||||
|
||||
stream.clear();
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
wstring os_specific = to_os_specific_w();
|
||||
#else
|
||||
string os_specific = to_os_specific();
|
||||
#endif // _WIN32
|
||||
#endif // _MSC_VER
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
|
||||
return (!stream.fail());
|
||||
|
|
@ -2032,11 +2032,11 @@ open_read_append(std::fstream &stream) const {
|
|||
#endif
|
||||
|
||||
stream.clear();
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
wstring os_specific = to_os_specific_w();
|
||||
#else
|
||||
string os_specific = to_os_specific();
|
||||
#endif // _WIN32
|
||||
#endif // _MSC_VER
|
||||
stream.open(os_specific.c_str(), open_mode);
|
||||
|
||||
return (!stream.fail());
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ SectionGroup "Python modules" SecGroupPython
|
|||
!insertmacro PyBindingSection 3.8-32 .cp38-win32.pyd
|
||||
!insertmacro PyBindingSection 3.9-32 .cp39-win32.pyd
|
||||
!insertmacro PyBindingSection 3.10-32 .cp310-win32.pyd
|
||||
!insertmacro PyBindingSection 3.11-32 .cp311-win32.pyd
|
||||
!else
|
||||
!insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd
|
||||
|
|
@ -383,6 +384,7 @@ SectionGroup "Python modules" SecGroupPython
|
|||
!insertmacro PyBindingSection 3.8 .cp38-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.9 .cp39-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.10 .cp310-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.11 .cp311-win_amd64.pyd
|
||||
!endif
|
||||
SectionGroupEnd
|
||||
|
||||
|
|
@ -491,6 +493,7 @@ Function .onInit
|
|||
${If} ${AtLeastWin8}
|
||||
!insertmacro MaybeEnablePyBindingSection 3.9-32
|
||||
!insertmacro MaybeEnablePyBindingSection 3.10-32
|
||||
!insertmacro MaybeEnablePyBindingSection 3.11-32
|
||||
${EndIf}
|
||||
!else
|
||||
!insertmacro MaybeEnablePyBindingSection 3.5
|
||||
|
|
@ -500,6 +503,7 @@ Function .onInit
|
|||
${If} ${AtLeastWin8}
|
||||
!insertmacro MaybeEnablePyBindingSection 3.9
|
||||
!insertmacro MaybeEnablePyBindingSection 3.10
|
||||
!insertmacro MaybeEnablePyBindingSection 3.11
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
|
|
@ -513,6 +517,10 @@ Function .onInit
|
|||
SectionSetFlags ${SecPyBindings3.10} ${SF_RO}
|
||||
SectionSetInstTypes ${SecPyBindings3.10} 0
|
||||
!endif
|
||||
!ifdef SecPyBindings3.11
|
||||
SectionSetFlags ${SecPyBindings3.11} ${SF_RO}
|
||||
SectionSetInstTypes ${SecPyBindings3.11} 0
|
||||
!endif
|
||||
${EndUnless}
|
||||
FunctionEnd
|
||||
|
||||
|
|
@ -824,6 +832,7 @@ Section Uninstall
|
|||
!insertmacro RemovePythonPath 3.8-32
|
||||
!insertmacro RemovePythonPath 3.9-32
|
||||
!insertmacro RemovePythonPath 3.10-32
|
||||
!insertmacro RemovePythonPath 3.11-32
|
||||
!else
|
||||
!insertmacro RemovePythonPath 3.5
|
||||
!insertmacro RemovePythonPath 3.6
|
||||
|
|
@ -831,6 +840,7 @@ Section Uninstall
|
|||
!insertmacro RemovePythonPath 3.8
|
||||
!insertmacro RemovePythonPath 3.9
|
||||
!insertmacro RemovePythonPath 3.10
|
||||
!insertmacro RemovePythonPath 3.11
|
||||
!endif
|
||||
|
||||
SetDetailsPrint both
|
||||
|
|
@ -899,6 +909,7 @@ SectionEnd
|
|||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8-32} $(DESC_SecPyBindings3.8-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9-32} $(DESC_SecPyBindings3.9-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.10-32} $(DESC_SecPyBindings3.10-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.11-32} $(DESC_SecPyBindings3.11-32)
|
||||
!else
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6)
|
||||
|
|
@ -906,6 +917,7 @@ SectionEnd
|
|||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8} $(DESC_SecPyBindings3.8)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9} $(DESC_SecPyBindings3.9)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.10} $(DESC_SecPyBindings3.10)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.11} $(DESC_SecPyBindings3.11)
|
||||
!endif
|
||||
!ifdef INCLUDE_PYVER
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
|
||||
|
|
|
|||
|
|
@ -1313,10 +1313,6 @@ def CompileCxx(obj,src,opts):
|
|||
if "SYSROOT" in SDK:
|
||||
if GetTarget() != "android":
|
||||
cmd += ' --sysroot=%s' % (SDK["SYSROOT"])
|
||||
else:
|
||||
ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/')
|
||||
cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir)
|
||||
cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"])
|
||||
cmd += ' -no-canonical-prefixes'
|
||||
|
||||
# Android-specific flags.
|
||||
|
|
@ -1325,33 +1321,24 @@ def CompileCxx(obj,src,opts):
|
|||
if GetTarget() == "android":
|
||||
# Most of the specific optimization flags here were
|
||||
# just copied from the default Android Makefiles.
|
||||
if "ANDROID_API" in SDK:
|
||||
cmd += ' -D__ANDROID_API__=' + str(SDK["ANDROID_API"])
|
||||
if "ANDROID_GCC_TOOLCHAIN" in SDK:
|
||||
cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/')
|
||||
cmd += ' -ffunction-sections -funwind-tables'
|
||||
cmd += ' -target ' + SDK["ANDROID_TRIPLE"]
|
||||
if arch == 'armv7a':
|
||||
cmd += ' -target armv7-none-linux-androideabi'
|
||||
cmd += ' -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'
|
||||
cmd += ' -fno-integrated-as'
|
||||
elif arch == 'arm':
|
||||
cmd += ' -target armv5te-none-linux-androideabi'
|
||||
cmd += ' -march=armv5te -mtune=xscale -msoft-float'
|
||||
cmd += ' -fno-integrated-as'
|
||||
elif arch == 'aarch64':
|
||||
cmd += ' -target aarch64-none-linux-android'
|
||||
elif arch == 'mips':
|
||||
cmd += ' -target mipsel-none-linux-android'
|
||||
cmd += ' -mips32'
|
||||
elif arch == 'mips64':
|
||||
cmd += ' -target mips64el-none-linux-android'
|
||||
cmd += ' -fintegrated-as'
|
||||
elif arch == 'x86':
|
||||
cmd += ' -target i686-none-linux-android'
|
||||
cmd += ' -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32'
|
||||
cmd += ' -mstackrealign'
|
||||
elif arch == 'x86_64':
|
||||
cmd += ' -target x86_64-none-linux-android'
|
||||
cmd += ' -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel'
|
||||
|
||||
cmd += " -Wa,--noexecstack"
|
||||
|
|
@ -1853,28 +1840,16 @@ def CompileLink(dll, obj, opts):
|
|||
if "ANDROID_GCC_TOOLCHAIN" in SDK:
|
||||
cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/')
|
||||
cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
|
||||
cmd += ' -target ' + SDK["ANDROID_TRIPLE"]
|
||||
if arch == 'armv7a':
|
||||
cmd += ' -target armv7-none-linux-androideabi'
|
||||
cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
|
||||
elif arch == 'arm':
|
||||
cmd += ' -target armv5te-none-linux-androideabi'
|
||||
elif arch == 'aarch64':
|
||||
cmd += ' -target aarch64-none-linux-android'
|
||||
elif arch == 'mips':
|
||||
cmd += ' -target mipsel-none-linux-android'
|
||||
cmd += ' -mips32'
|
||||
elif arch == 'mips64':
|
||||
cmd += ' -target mips64el-none-linux-android'
|
||||
elif arch == 'x86':
|
||||
cmd += ' -target i686-none-linux-android'
|
||||
elif arch == 'x86_64':
|
||||
cmd += ' -target x86_64-none-linux-android'
|
||||
cmd += ' -lc -lm'
|
||||
else:
|
||||
cmd += " -pthread"
|
||||
|
||||
if "SYSROOT" in SDK:
|
||||
cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
|
||||
if "SYSROOT" in SDK:
|
||||
cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
|
||||
|
||||
if LDFLAGS != "":
|
||||
cmd += " " + LDFLAGS
|
||||
|
|
@ -2451,6 +2426,7 @@ def WriteConfigSettings():
|
|||
dtool_config["PHAVE_GLOB_H"] = 'UNDEF'
|
||||
dtool_config["PHAVE_LOCKF"] = 'UNDEF'
|
||||
dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
|
||||
dtool_config["HAVE_X11"] = 'UNDEF'
|
||||
|
||||
if (GetOptimize() <= 2 and GetTarget() == "windows"):
|
||||
dtool_config["USE_DEBUG_PYTHON"] = '1'
|
||||
|
|
@ -3080,8 +3056,10 @@ else:
|
|||
if not PkgSkip("PANDATOOL"):
|
||||
CopyAllFiles(GetOutputDir()+"/plugins/", "pandatool/src/scripts/", ".mel")
|
||||
CopyAllFiles(GetOutputDir()+"/plugins/", "pandatool/src/scripts/", ".ms")
|
||||
if not PkgSkip("PYTHON") and os.path.isdir(GetThirdpartyBase()+"/Pmw"):
|
||||
CopyTree(GetOutputDir()+'/Pmw', GetThirdpartyBase()+'/Pmw')
|
||||
|
||||
if not PkgSkip("PYTHON") and os.path.isdir(GetThirdpartyBase() + "/Pmw"):
|
||||
CopyTree(GetOutputDir() + "/Pmw", GetThirdpartyBase() + "/Pmw", exclude=["Pmw_1_3", "Pmw_1_3_3"])
|
||||
|
||||
ConditionalWriteFile(GetOutputDir()+'/include/ctl3d.h', '/* dummy file to make MAX happy */')
|
||||
|
||||
# Since Eigen is included by all sorts of core headers, as a convenience
|
||||
|
|
@ -6322,7 +6300,7 @@ def ParallelMake(tasklist):
|
|||
# Create the workers
|
||||
for slave in range(THREADCOUNT):
|
||||
th = threading.Thread(target=BuildWorker, args=[taskqueue, donequeue])
|
||||
th.setDaemon(1)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
# Feed tasks to the workers.
|
||||
tasksqueued = 0
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ SUFFIX_LIB = [".lib",".ilb"]
|
|||
VCS_DIRS = set(["CVS", "CVSROOT", ".git", ".hg", "__pycache__"])
|
||||
VCS_FILES = set([".cvsignore", ".gitignore", ".gitmodules", ".hgignore"])
|
||||
STARTTIME = time.time()
|
||||
MAINTHREAD = threading.currentThread()
|
||||
MAINTHREAD = threading.current_thread()
|
||||
OUTPUTDIR = "built"
|
||||
CUSTOM_OUTPUTDIR = False
|
||||
THIRDPARTYBASE = None
|
||||
|
|
@ -242,7 +242,7 @@ def ProgressOutput(progress, msg, target = None):
|
|||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
prefix = ""
|
||||
thisthread = threading.currentThread()
|
||||
thisthread = threading.current_thread()
|
||||
if thisthread is MAINTHREAD:
|
||||
if progress is None:
|
||||
prefix = ""
|
||||
|
|
@ -272,7 +272,7 @@ def ProgressOutput(progress, msg, target = None):
|
|||
def exit(msg = ""):
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
if threading.currentThread() == MAINTHREAD:
|
||||
if threading.current_thread() == MAINTHREAD:
|
||||
SaveDependencyCache()
|
||||
print("Elapsed Time: " + PrettyTime(time.time() - STARTTIME))
|
||||
print(msg)
|
||||
|
|
@ -404,8 +404,8 @@ def SetTarget(target, arch=None):
|
|||
# 64-bit platforms were introduced in Android 21.
|
||||
ANDROID_API = 21
|
||||
else:
|
||||
# Default to the lowest API level supported by NDK r16.
|
||||
ANDROID_API = 14
|
||||
# Default to the lowest API level still supported by Google.
|
||||
ANDROID_API = 19
|
||||
|
||||
# Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc
|
||||
global ANDROID_ABI, ANDROID_TRIPLE
|
||||
|
|
@ -433,6 +433,7 @@ def SetTarget(target, arch=None):
|
|||
else:
|
||||
exit('Android architecture must be arm, armv7a, aarch64, mips, mips64, x86 or x86_64')
|
||||
|
||||
ANDROID_TRIPLE += str(ANDROID_API)
|
||||
TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-'
|
||||
|
||||
elif target == 'linux':
|
||||
|
|
@ -1556,8 +1557,8 @@ def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
|
|||
else:
|
||||
handle = os.popen(LocateBinary(tool) + " --cflags")
|
||||
result = handle.read().strip()
|
||||
if len(result) == 0: return []
|
||||
handle.close()
|
||||
if len(result) == 0: return []
|
||||
dirs = []
|
||||
for opt in result.split(" "):
|
||||
if (opt.startswith("-I")):
|
||||
|
|
@ -2544,15 +2545,6 @@ def SdkLocateAndroid():
|
|||
SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), arch_dir).replace('\\', '/')
|
||||
#IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include'))
|
||||
|
||||
# Starting with NDK r16, libc++ is the recommended STL to use.
|
||||
stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'llvm-libc++')
|
||||
IncDirectory("ALWAYS", os.path.join(stdlibc, 'include').replace('\\', '/'))
|
||||
LibDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi).replace('\\', '/'))
|
||||
|
||||
stl_lib = os.path.join(stdlibc, 'libs', abi, 'libc++_shared.so')
|
||||
LibName("ALWAYS", stl_lib.replace('\\', '/'))
|
||||
CopyFile(os.path.join(GetOutputDir(), 'lib', 'libc++_shared.so'), stl_lib)
|
||||
|
||||
# The Android support library polyfills C++ features not available in the
|
||||
# STL that ships with Android.
|
||||
support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
|
||||
|
|
@ -2773,7 +2765,7 @@ def SetupVisualStudioEnviron():
|
|||
elif not win_kit.endswith('\\'):
|
||||
win_kit += '\\'
|
||||
|
||||
for vnum in 10150, 10240, 10586, 14393, 15063, 16299, 17134, 17763, 18362:
|
||||
for vnum in 10150, 10240, 10586, 14393, 15063, 16299, 17134, 17763, 18362, 19041:
|
||||
version = "10.0.{0}.0".format(vnum)
|
||||
if os.path.isfile(win_kit + "Include\\" + version + "\\ucrt\\assert.h"):
|
||||
print("Using Universal CRT %s" % (version))
|
||||
|
|
@ -2961,12 +2953,7 @@ def SetupBuildEnvironment(compiler):
|
|||
|
||||
# Now extract the preprocessor's include directories.
|
||||
cmd = GetCXX() + " -x c++ -v -E " + os.devnull
|
||||
if "ANDROID_NDK" in SDK:
|
||||
ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/')
|
||||
cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir)
|
||||
cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"])
|
||||
else:
|
||||
cmd += sysroot_flag
|
||||
cmd += sysroot_flag
|
||||
|
||||
null = open(os.devnull, 'w')
|
||||
handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
|
||||
|
|
@ -3108,13 +3095,16 @@ def CopyAllHeaders(dir, skip=[]):
|
|||
WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
|
||||
JustBuilt([dstfile], [srcfile])
|
||||
|
||||
def CopyTree(dstdir, srcdir, omitVCS=True):
|
||||
def CopyTree(dstdir, srcdir, omitVCS=True, exclude=()):
|
||||
if os.path.isdir(dstdir):
|
||||
source_entries = os.listdir(srcdir)
|
||||
for entry in source_entries:
|
||||
srcpth = os.path.join(srcdir, entry)
|
||||
dstpth = os.path.join(dstdir, entry)
|
||||
|
||||
if entry in exclude:
|
||||
continue
|
||||
|
||||
if os.path.islink(srcpth) or os.path.isfile(srcpth):
|
||||
if not omitVCS or entry not in VCS_FILES:
|
||||
CopyFile(dstpth, srcpth)
|
||||
|
|
@ -3124,7 +3114,7 @@ def CopyTree(dstdir, srcdir, omitVCS=True):
|
|||
|
||||
# Delete files in dstdir that are not in srcdir.
|
||||
for entry in os.listdir(dstdir):
|
||||
if entry not in source_entries:
|
||||
if entry not in source_entries or entry in exclude:
|
||||
path = os.path.join(dstdir, entry)
|
||||
if os.path.islink(path) or os.path.isfile(path):
|
||||
os.remove(path)
|
||||
|
|
@ -3140,6 +3130,13 @@ def CopyTree(dstdir, srcdir, omitVCS=True):
|
|||
if subprocess.call(['cp', '-R', '-f', srcdir, dstdir]) != 0:
|
||||
exit("Copy failed.")
|
||||
|
||||
for entry in exclude:
|
||||
path = os.path.join(dstdir, entry)
|
||||
if os.path.islink(path) or os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
if omitVCS:
|
||||
DeleteVCS(dstdir)
|
||||
|
||||
|
|
@ -3270,6 +3267,22 @@ def WriteResourceFile(basename, **kwargs):
|
|||
return basename
|
||||
|
||||
|
||||
def GenerateEmbeddedStringFile(string_name, data):
|
||||
yield 'extern const char %s[] = {\n' % (string_name)
|
||||
i = 0
|
||||
for byte in data:
|
||||
if i == 0:
|
||||
yield ' '
|
||||
|
||||
yield ' 0x%02x,' % (byte)
|
||||
i += 1
|
||||
if i >= 12:
|
||||
yield '\n'
|
||||
i = 0
|
||||
|
||||
yield '\n};\n'
|
||||
|
||||
|
||||
def WriteEmbeddedStringFile(basename, inputs, string_name=None):
|
||||
if os.path.splitext(basename)[1] not in SUFFIX_INC:
|
||||
basename += '.cxx'
|
||||
|
|
@ -3294,20 +3307,7 @@ def WriteEmbeddedStringFile(basename, inputs, string_name=None):
|
|||
|
||||
data.append(0)
|
||||
|
||||
output = 'extern const char %s[] = {\n' % (string_name)
|
||||
|
||||
i = 0
|
||||
for byte in data:
|
||||
if i == 0:
|
||||
output += ' '
|
||||
|
||||
output += ' 0x%02x,' % (byte)
|
||||
i += 1
|
||||
if i >= 12:
|
||||
output += '\n'
|
||||
i = 0
|
||||
|
||||
output += '\n};\n'
|
||||
output = ''.join(GenerateEmbeddedStringFile(string_name, data))
|
||||
ConditionalWriteFile(target, output)
|
||||
return target
|
||||
|
||||
|
|
@ -3478,7 +3478,8 @@ def UpdatePythonVersionInfoFile(new_info):
|
|||
json_data = []
|
||||
if os.path.isfile(json_file) and not PkgSkip("PYTHON"):
|
||||
try:
|
||||
json_data = json.load(open(json_file, 'r'))
|
||||
with open(json_file, 'r') as fh:
|
||||
json_data = json.load(fh)
|
||||
except:
|
||||
json_data = []
|
||||
|
||||
|
|
@ -3498,7 +3499,9 @@ def UpdatePythonVersionInfoFile(new_info):
|
|||
|
||||
if VERBOSE:
|
||||
print("Writing %s" % (json_file))
|
||||
json.dump(json_data, open(json_file, 'w'), indent=4)
|
||||
|
||||
with open(json_file, 'w') as fh:
|
||||
json.dump(json_data, fh, indent=4)
|
||||
|
||||
|
||||
def ReadPythonVersionInfoFile():
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ INLINE BulletDebugNode::
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If true, displays collision shapes in wireframe mode.
|
||||
*/
|
||||
INLINE void BulletDebugNode::
|
||||
show_wireframe(bool show) {
|
||||
|
|
@ -39,7 +39,8 @@ get_show_wireframe() const {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If true, display limits defined for constraints, e.g. a pivot axis or maximum
|
||||
* amplitude.
|
||||
*/
|
||||
INLINE void BulletDebugNode::
|
||||
show_constraints(bool show) {
|
||||
|
|
@ -58,7 +59,7 @@ get_show_constraints() const {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If true, displays axis aligned bounding boxes for objects.
|
||||
*/
|
||||
INLINE void BulletDebugNode::
|
||||
show_bounding_boxes(bool show) {
|
||||
|
|
@ -77,7 +78,7 @@ get_show_bounding_boxes() const {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If true, displays normal vectors for triangle mesh and heightfield faces.
|
||||
*/
|
||||
INLINE void BulletDebugNode::
|
||||
show_normals(bool show) {
|
||||
|
|
@ -92,4 +93,4 @@ INLINE bool BulletDebugNode::
|
|||
get_show_normals() const {
|
||||
|
||||
return _drawer._normals;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ BulletDebugNode(const char *name) : PandaNode(name) {
|
|||
set_bounds(bounds);
|
||||
set_final(true);
|
||||
set_overall_hidden(true);
|
||||
set_renderable();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,17 +152,6 @@ draw_mask_changed() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 BulletDebugNode::
|
||||
is_renderable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the node's contents to the CullResult we are building up during the
|
||||
* cull traversal, so that it will be drawn at render time. For most nodes
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ public:
|
|||
virtual bool safe_to_combine_children() const;
|
||||
virtual bool safe_to_flatten_below() const;
|
||||
|
||||
virtual bool is_renderable() const;
|
||||
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ get_contact_processing_threshold() const {
|
|||
*/
|
||||
void BulletPersistentManifold::
|
||||
clear_manifold() {
|
||||
nassertv_always(_manifold != nullptr);
|
||||
|
||||
LightMutexHolder holder(BulletWorld::get_global_lock());
|
||||
|
||||
_manifold->clearManifold();
|
||||
|
|
@ -59,6 +61,8 @@ clear_manifold() {
|
|||
*/
|
||||
PandaNode *BulletPersistentManifold::
|
||||
get_node0() {
|
||||
nassertr_always(_manifold != nullptr, nullptr);
|
||||
|
||||
LightMutexHolder holder(BulletWorld::get_global_lock());
|
||||
|
||||
#if BT_BULLET_VERSION >= 281
|
||||
|
|
@ -75,6 +79,8 @@ get_node0() {
|
|||
*/
|
||||
PandaNode *BulletPersistentManifold::
|
||||
get_node1() {
|
||||
nassertr_always(_manifold != nullptr, nullptr);
|
||||
|
||||
LightMutexHolder holder(BulletWorld::get_global_lock());
|
||||
|
||||
#if BT_BULLET_VERSION >= 281
|
||||
|
|
@ -91,6 +97,8 @@ get_node1() {
|
|||
*/
|
||||
int BulletPersistentManifold::
|
||||
get_num_manifold_points() const {
|
||||
nassertr_always(_manifold != nullptr, 0);
|
||||
|
||||
LightMutexHolder holder(BulletWorld::get_global_lock());
|
||||
|
||||
return _manifold->getNumContacts();
|
||||
|
|
@ -99,11 +107,8 @@ get_num_manifold_points() const {
|
|||
/**
|
||||
*
|
||||
*/
|
||||
BulletManifoldPoint *BulletPersistentManifold::
|
||||
BulletManifoldPoint BulletPersistentManifold::
|
||||
get_manifold_point(int idx) const {
|
||||
LightMutexHolder holder(BulletWorld::get_global_lock());
|
||||
|
||||
nassertr(idx < _manifold->getNumContacts(), nullptr)
|
||||
|
||||
return new BulletManifoldPoint(_manifold->getContactPoint(idx));
|
||||
return BulletManifoldPoint(_manifold->getContactPoint(idx));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ PUBLISHED:
|
|||
PandaNode *get_node1();
|
||||
|
||||
int get_num_manifold_points() const;
|
||||
BulletManifoldPoint *get_manifold_point(int idx) const;
|
||||
BulletManifoldPoint get_manifold_point(int idx) const;
|
||||
MAKE_SEQ(get_manifold_points, get_num_manifold_points, get_manifold_point);
|
||||
|
||||
PN_stdfloat get_contact_breaking_threshold() const;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ BulletWorld() {
|
|||
// Some prefered settings
|
||||
_world->getDispatchInfo().m_enableSPU = true; // default: true
|
||||
_world->getDispatchInfo().m_useContinuous = true; // default: true
|
||||
_world->getSolverInfo().m_splitImpulse = false; // default: false
|
||||
_world->getSolverInfo().m_splitImpulse = bullet_split_impulse;
|
||||
_world->getSolverInfo().m_numIterations = bullet_solver_iterations;
|
||||
}
|
||||
|
||||
|
|
@ -1064,14 +1064,14 @@ contact_test_pair(PandaNode *node0, PandaNode *node1) const {
|
|||
/**
|
||||
*
|
||||
*/
|
||||
BulletPersistentManifold *BulletWorld::
|
||||
BulletPersistentManifold BulletWorld::
|
||||
get_manifold(int idx) const {
|
||||
LightMutexHolder holder(get_global_lock());
|
||||
|
||||
nassertr(idx < _dispatcher->getNumManifolds(), nullptr);
|
||||
nassertr(idx < _dispatcher->getNumManifolds(), BulletPersistentManifold(nullptr));
|
||||
|
||||
btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
|
||||
return (ptr) ? new BulletPersistentManifold(ptr) : nullptr;
|
||||
return BulletPersistentManifold(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ PUBLISHED:
|
|||
|
||||
// Manifolds
|
||||
int get_num_manifolds() const;
|
||||
BulletPersistentManifold *get_manifold(int idx) const;
|
||||
BulletPersistentManifold get_manifold(int idx) const;
|
||||
MAKE_SEQ(get_manifolds, get_num_manifolds, get_manifold);
|
||||
|
||||
// Collision filtering
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ PRC_DESC("Specifies if events should be send when new contacts are "
|
|||
"contact events might create more load on the event queue "
|
||||
"then you might want! Default value is FALSE."));
|
||||
|
||||
ConfigVariableBool bullet_split_impulse
|
||||
("bullet-split-impulse", false,
|
||||
PRC_DESC("Penetrating recovery won't add momentum. "
|
||||
"btContactSolverInfo::m_splitImpulse. Default value is false."));
|
||||
|
||||
ConfigVariableInt bullet_solver_iterations
|
||||
("bullet-solver-iterations", 10,
|
||||
PRC_DESC("Specifies the number of iterations for the Bullet contact "
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ extern ConfigVariableEnum<BulletWorld::BroadphaseAlgorithm> bullet_broadphase_al
|
|||
extern ConfigVariableEnum<BulletWorld::FilterAlgorithm> bullet_filter_algorithm;
|
||||
extern ConfigVariableDouble bullet_sap_extents;
|
||||
extern ConfigVariableBool bullet_enable_contact_events;
|
||||
extern ConfigVariableBool bullet_split_impulse;
|
||||
extern ConfigVariableInt bullet_solver_iterations;
|
||||
extern ConfigVariableBool bullet_additional_damping;
|
||||
extern ConfigVariableDouble bullet_additional_damping_linear_factor;
|
||||
|
|
|
|||
|
|
@ -599,6 +599,10 @@ do_bind_anim(AnimControl *control, AnimBundle *anim,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Grabbing the lock early prevents any other thread in stage 0 from also
|
||||
// trying to modify the channel list at the same time.
|
||||
CDLockedReader cdata(_cycler);
|
||||
|
||||
plist<int> holes;
|
||||
int channel_index = 0;
|
||||
pick_channel_index(holes, channel_index);
|
||||
|
|
@ -616,7 +620,6 @@ do_bind_anim(AnimControl *control, AnimBundle *anim,
|
|||
subset.is_include_empty(), bound_joints, subset);
|
||||
control->setup_anim(this, anim, channel_index, bound_joints);
|
||||
|
||||
CDReader cdata(_cycler);
|
||||
determine_effective_channels(cdata);
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ CollisionLevelState(const NodePath &node_path) :
|
|||
*/
|
||||
template<class MaskType>
|
||||
INLINE CollisionLevelState<MaskType>::
|
||||
CollisionLevelState(const CollisionLevelState<MaskType> &parent, PandaNode *child) :
|
||||
CollisionLevelState(const CollisionLevelState<MaskType> &parent, const PandaNode::DownConnection &child, MaskType mask) :
|
||||
CollisionLevelStateBase(parent, child),
|
||||
_current(parent._current)
|
||||
_current(mask)
|
||||
{
|
||||
}
|
||||
#endif // CPPPARSER
|
||||
|
|
@ -101,21 +101,21 @@ prepare_collider(const ColliderDef &def, const NodePath &root) {
|
|||
template<class MaskType>
|
||||
bool CollisionLevelState<MaskType>::
|
||||
any_in_bounds() {
|
||||
#ifndef NDEBUG
|
||||
#ifdef NDEBUG
|
||||
const bool is_spam = false;
|
||||
#else
|
||||
const bool is_spam = collide_cat.is_spam();
|
||||
#endif
|
||||
int indent_level = 0;
|
||||
if (collide_cat.is_spam()) {
|
||||
if (is_spam) {
|
||||
indent_level = _node_path.get_num_nodes() * 2;
|
||||
collide_cat.spam();
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Considering " << _node_path.get_node_path() << "\n";
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
PandaNode *pnode = node();
|
||||
|
||||
CPT(BoundingVolume) node_bv = pnode->get_bounds();
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
const GeometricBoundingVolume *node_gbv = (const GeometricBoundingVolume *)node_bv.p();
|
||||
if (_node_gbv != nullptr) {
|
||||
PandaNode *pnode = node();
|
||||
CollideMask this_mask = pnode->get_net_collide_mask();
|
||||
|
||||
int num_colliders = get_num_colliders();
|
||||
|
|
@ -131,13 +131,11 @@ any_in_bounds() {
|
|||
// Also don't test a node with itself, or with any of its
|
||||
// descendants.
|
||||
if (pnode == cnode) {
|
||||
#ifndef NDEBUG
|
||||
if (collide_cat.is_spam()) {
|
||||
if (is_spam) {
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Not comparing " << c << " to " << _node_path
|
||||
<< " (same node)\n";
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
} else {
|
||||
// There are bits in common, and it's not the same instance, so go
|
||||
|
|
@ -148,16 +146,14 @@ any_in_bounds() {
|
|||
is_in = true; // If there's no bounding volume, we're implicitly in.
|
||||
|
||||
if (col_gbv != nullptr) {
|
||||
is_in = (node_gbv->contains(col_gbv) != 0);
|
||||
is_in = (_node_gbv->contains(col_gbv) != 0);
|
||||
_node_volume_pcollector.add_level(1);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (collide_cat.is_spam()) {
|
||||
if (is_spam) {
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Comparing " << c << ": " << *col_gbv
|
||||
<< " to " << *node_gbv << ", is_in = " << is_in << "\n";
|
||||
<< " to " << *_node_gbv << ", is_in = " << is_in << "\n";
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -171,8 +167,7 @@ any_in_bounds() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (collide_cat.is_spam()) {
|
||||
if (is_spam) {
|
||||
int num_active_colliders = 0;
|
||||
int num_colliders = get_num_colliders();
|
||||
for (int c = 0; c < num_colliders; c++) {
|
||||
|
|
@ -201,11 +196,121 @@ any_in_bounds() {
|
|||
collide_cat.spam(false)
|
||||
<< "\n";
|
||||
}
|
||||
#endif // NDEBUG
|
||||
return has_any_collider();
|
||||
}
|
||||
#endif // CPPPARSER
|
||||
|
||||
#ifndef CPPPARSER
|
||||
/**
|
||||
* Checks the bounding volume of the given child of the current node against
|
||||
* each of our colliders. Returns a mask indicating which colliders are inside
|
||||
* of the bounding volume.
|
||||
*/
|
||||
template<class MaskType>
|
||||
MaskType CollisionLevelState<MaskType>::
|
||||
get_child_mask(const PandaNode::DownConnection &child) const {
|
||||
PandaNode *pnode = child.get_child();
|
||||
#ifdef NDEBUG
|
||||
const bool is_spam = false;
|
||||
#else
|
||||
const bool is_spam = collide_cat.is_spam();
|
||||
#endif
|
||||
int indent_level = 0;
|
||||
if (is_spam) {
|
||||
indent_level = (_node_path.get_num_nodes() + 1) * 2;
|
||||
collide_cat.spam();
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Considering " << _node_path << "/" << pnode->get_name() << "\n";
|
||||
}
|
||||
|
||||
MaskType mask = _current;
|
||||
|
||||
const GeometricBoundingVolume *node_gbv = child.get_bounds();
|
||||
if (node_gbv != nullptr) {
|
||||
CollideMask node_mask = child.get_net_collide_mask();
|
||||
|
||||
int num_colliders = get_num_colliders();
|
||||
for (int c = 0; c < num_colliders; c++) {
|
||||
if (mask.get_bit(c)) {
|
||||
CollisionNode *cnode = get_collider_node(c);
|
||||
bool is_in = false;
|
||||
|
||||
// Don't even bother testing the bounding volume if there are no
|
||||
// collide bits in common between our collider and this node.
|
||||
CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask;
|
||||
if (!(from_mask & node_mask).is_zero()) {
|
||||
// Also don't test a node with itself, or with any of its
|
||||
// descendants.
|
||||
if (pnode == cnode) {
|
||||
if (is_spam) {
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Not comparing " << c << " to " << _node_path << "/"
|
||||
<< pnode->get_name() << " (same node)\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
// There are bits in common, and it's not the same instance, so go
|
||||
// ahead and try the bounding volume.
|
||||
const GeometricBoundingVolume *col_gbv =
|
||||
get_local_bound(c);
|
||||
|
||||
is_in = true; // If there's no bounding volume, we're implicitly in.
|
||||
|
||||
if (col_gbv != nullptr) {
|
||||
is_in = (node_gbv->contains(col_gbv) != 0);
|
||||
_node_volume_pcollector.add_level(1);
|
||||
|
||||
if (is_spam) {
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< "Comparing " << c << ": " << *col_gbv
|
||||
<< " to " << *node_gbv << ", is_in = " << is_in << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_in) {
|
||||
// This collider cannot intersect with any geometry at this node or
|
||||
// below.
|
||||
mask.clear_bit(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_spam) {
|
||||
int num_active_colliders = 0;
|
||||
int num_colliders = get_num_colliders();
|
||||
for (int c = 0; c < num_colliders; c++) {
|
||||
if (mask.get_bit(c)) {
|
||||
num_active_colliders++;
|
||||
}
|
||||
}
|
||||
|
||||
collide_cat.spam();
|
||||
indent(collide_cat.spam(false), indent_level)
|
||||
<< _node_path.get_node_path() << "/" << pnode->get_name() << " has "
|
||||
<< num_active_colliders << " interested colliders";
|
||||
if (num_colliders != 0) {
|
||||
collide_cat.spam(false)
|
||||
<< " (";
|
||||
for (int c = 0; c < num_colliders; c++) {
|
||||
if (mask.get_bit(c)) {
|
||||
CollisionNode *cnode = get_collider_node(c);
|
||||
collide_cat.spam(false)
|
||||
<< " " << c << ". " << cnode->get_name();
|
||||
}
|
||||
}
|
||||
collide_cat.spam(false)
|
||||
<< " )";
|
||||
}
|
||||
collide_cat.spam(false)
|
||||
<< "\n";
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
#endif // CPPPARSER
|
||||
|
||||
#ifndef CPPPARSER
|
||||
/**
|
||||
* Applies the inverse transform from the current node, if any, onto all the
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ public:
|
|||
#ifndef CPPPARSER
|
||||
INLINE CollisionLevelState(const NodePath &node_path);
|
||||
INLINE CollisionLevelState(const CollisionLevelState<MaskType> &parent,
|
||||
PandaNode *child);
|
||||
const PandaNode::DownConnection &child,
|
||||
MaskType mask);
|
||||
INLINE CollisionLevelState(const CollisionLevelState<MaskType> ©);
|
||||
INLINE void operator = (const CollisionLevelState<MaskType> ©);
|
||||
|
||||
|
|
@ -45,6 +46,7 @@ public:
|
|||
INLINE void prepare_collider(const ColliderDef &def, const NodePath &root);
|
||||
|
||||
bool any_in_bounds();
|
||||
MaskType get_child_mask(const PandaNode::DownConnection &child) const;
|
||||
bool apply_transform();
|
||||
|
||||
INLINE static bool has_max_colliders();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ INLINE CollisionLevelStateBase::
|
|||
CollisionLevelStateBase(const NodePath &node_path) :
|
||||
_node_path(node_path),
|
||||
_colliders(get_class_type()),
|
||||
_include_mask(CollideMask::all_on())
|
||||
_include_mask(CollideMask::all_on()),
|
||||
_node_gbv(node_path.node()->get_bounds()->as_geometric_bounding_volume())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +31,21 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child)
|
|||
_node_path(parent._node_path, child),
|
||||
_colliders(parent._colliders),
|
||||
_include_mask(parent._include_mask),
|
||||
_local_bounds(parent._local_bounds)
|
||||
_local_bounds(parent._local_bounds),
|
||||
_node_gbv(child->get_bounds()->as_geometric_bounding_volume())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor goes to the next child node in the traversal.
|
||||
*/
|
||||
INLINE CollisionLevelStateBase::
|
||||
CollisionLevelStateBase(const CollisionLevelStateBase &parent, const PandaNode::DownConnection &child) :
|
||||
_node_path(parent._node_path, child.get_child()),
|
||||
_colliders(parent._colliders),
|
||||
_include_mask(parent._include_mask),
|
||||
_local_bounds(parent._local_bounds),
|
||||
_node_gbv(child.get_bounds())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +58,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase ©) :
|
|||
_colliders(copy._colliders),
|
||||
_include_mask(copy._include_mask),
|
||||
_local_bounds(copy._local_bounds),
|
||||
_parent_bounds(copy._parent_bounds)
|
||||
_parent_bounds(copy._parent_bounds),
|
||||
_node_gbv(copy._node_gbv)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +73,7 @@ operator = (const CollisionLevelStateBase ©) {
|
|||
_include_mask = copy._include_mask;
|
||||
_local_bounds = copy._local_bounds;
|
||||
_parent_bounds = copy._parent_bounds;
|
||||
_node_gbv = copy._node_gbv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,6 +130,14 @@ get_collider_node_path(int n) const {
|
|||
return _colliders[n]._node_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding volume of the current node.
|
||||
*/
|
||||
INLINE const GeometricBoundingVolume *CollisionLevelStateBase::
|
||||
get_node_bound() const {
|
||||
return _node_gbv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding volume of the indicated collider, transformed into the
|
||||
* current node's transform space.
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ public:
|
|||
INLINE CollisionLevelStateBase(const NodePath &node_path);
|
||||
INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent,
|
||||
PandaNode *child);
|
||||
INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent,
|
||||
const PandaNode::DownConnection &child);
|
||||
INLINE CollisionLevelStateBase(const CollisionLevelStateBase ©);
|
||||
INLINE void operator = (const CollisionLevelStateBase ©);
|
||||
|
||||
|
|
@ -67,6 +69,7 @@ public:
|
|||
INLINE const CollisionSolid *get_collider(int n) const;
|
||||
INLINE CollisionNode *get_collider_node(int n) const;
|
||||
INLINE NodePath get_collider_node_path(int n) const;
|
||||
INLINE const GeometricBoundingVolume *get_node_bound() const;
|
||||
INLINE const GeometricBoundingVolume *get_local_bound(int n) const;
|
||||
INLINE const GeometricBoundingVolume *get_parent_bound(int n) const;
|
||||
|
||||
|
|
@ -80,6 +83,8 @@ protected:
|
|||
Colliders _colliders;
|
||||
CollideMask _include_mask;
|
||||
|
||||
const GeometricBoundingVolume *_node_gbv = nullptr;
|
||||
|
||||
typedef PTA(CPT(GeometricBoundingVolume)) BoundingVolumes;
|
||||
BoundingVolumes _local_bounds;
|
||||
BoundingVolumes _parent_bounds;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ CollisionNode(const std::string &name) :
|
|||
_collider_sort(0)
|
||||
{
|
||||
set_cull_callback();
|
||||
set_renderable();
|
||||
|
||||
// CollisionNodes are hidden by default.
|
||||
set_overall_hidden(true);
|
||||
|
|
@ -186,11 +187,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
CPT(CollisionSolid) solid = (*si).get_read_pointer();
|
||||
PT(PandaNode) node = solid->get_viz(trav, data, false);
|
||||
if (node != nullptr) {
|
||||
CullTraverserData next_data(data, node);
|
||||
|
||||
// We don't want to inherit the render state from above for these guys.
|
||||
next_data._state = RenderState::make_empty();
|
||||
trav->traverse(next_data);
|
||||
trav->traverse_down(data, node, data._net_transform, RenderState::make_empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,12 +206,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
CPT(CollisionSolid) solid = (*si).get_read_pointer();
|
||||
PT(PandaNode) node = solid->get_viz(trav, data, false);
|
||||
if (node != nullptr) {
|
||||
CullTraverserData next_data(data, node);
|
||||
|
||||
next_data._net_transform =
|
||||
next_data._net_transform->compose(transform);
|
||||
next_data._state = get_last_pos_state();
|
||||
trav->traverse(next_data);
|
||||
trav->traverse_down(data, node,
|
||||
data._net_transform->compose(transform), get_last_pos_state());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,17 +217,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
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 CollisionNode::
|
||||
is_renderable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple downcast check. Returns true if this kind of node happens to
|
||||
* inherit from CollisionNode, false otherwise.
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ public:
|
|||
virtual CollideMask get_legal_collide_mask() const;
|
||||
|
||||
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
|
||||
virtual bool is_renderable() const;
|
||||
virtual bool is_collision_node() const;
|
||||
|
||||
virtual void output(std::ostream &out) const;
|
||||
|
|
|
|||
|
|
@ -288,7 +288,9 @@ traverse(const NodePath &root) {
|
|||
#ifdef DO_PSTATS
|
||||
PStatTimer pass_timer(get_pass_collector(pass));
|
||||
#endif
|
||||
r_traverse_single(level_states[pass], pass);
|
||||
if (level_states[pass].any_in_bounds()) {
|
||||
r_traverse_single(level_states[pass], pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -562,22 +564,13 @@ prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states,
|
|||
*/
|
||||
void CollisionTraverser::
|
||||
r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
||||
if (!level_state.any_in_bounds()) {
|
||||
return;
|
||||
}
|
||||
if (!level_state.apply_transform()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PandaNode *node = level_state.node();
|
||||
if (node->is_collision_node()) {
|
||||
CollisionNode *cnode;
|
||||
DCAST_INTO_V(cnode, node);
|
||||
CPT(BoundingVolume) node_bv = cnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
CollisionNode *cnode = (CollisionNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = cnode;
|
||||
|
|
@ -589,13 +582,14 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
int num_colliders = level_state.get_num_colliders();
|
||||
for (int c = 0; c < num_colliders; ++c) {
|
||||
if (level_state.has_collider(c)) {
|
||||
entry._from_node = level_state.get_collider_node(c);
|
||||
CollisionNode *from_node = level_state.get_collider_node(c);
|
||||
|
||||
if ((entry._from_node->get_from_collide_mask() &
|
||||
if ((from_node->get_from_collide_mask() &
|
||||
cnode->get_into_collide_mask()) != 0) {
|
||||
#ifdef DO_PSTATS
|
||||
// PStatTimer collide_timer(_solid_collide_collectors[pass]);
|
||||
#endif
|
||||
entry._from_node = from_node;
|
||||
entry._from_node_path = level_state.get_collider_node_path(c);
|
||||
entry._from = level_state.get_collider(c);
|
||||
|
||||
|
|
@ -603,7 +597,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -616,13 +610,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
}
|
||||
#endif
|
||||
|
||||
GeomNode *gnode;
|
||||
DCAST_INTO_V(gnode, node);
|
||||
CPT(BoundingVolume) node_bv = gnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
GeomNode *gnode = (GeomNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = gnode;
|
||||
|
|
@ -648,7 +636,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -658,9 +646,14 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
// If it's a switch node or sequence node, visit just the one visible
|
||||
// child.
|
||||
int index = node->get_visible_child();
|
||||
if (index >= 0 && index < node->get_num_children()) {
|
||||
CollisionLevelStateSingle next_state(level_state, node->get_child(index));
|
||||
r_traverse_single(next_state, pass);
|
||||
PandaNode::Children children = node->get_children();
|
||||
if (index >= 0 && index < children.get_num_children()) {
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(index);
|
||||
CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateSingle next_state(level_state, child, mask);
|
||||
r_traverse_single(next_state, pass);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (node->is_lod_node()) {
|
||||
|
|
@ -673,12 +666,16 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateSingle next_state(level_state, children.get_child(i));
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateSingle next_state(level_state, child, mask);
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
}
|
||||
r_traverse_single(next_state, pass);
|
||||
}
|
||||
r_traverse_single(next_state, pass);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -686,8 +683,12 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
|
|||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateSingle next_state(level_state, children.get_child(i));
|
||||
r_traverse_single(next_state, pass);
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateSingle next_state(level_state, child, mask);
|
||||
r_traverse_single(next_state, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -773,22 +774,13 @@ prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states,
|
|||
*/
|
||||
void CollisionTraverser::
|
||||
r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
||||
if (!level_state.any_in_bounds()) {
|
||||
return;
|
||||
}
|
||||
if (!level_state.apply_transform()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PandaNode *node = level_state.node();
|
||||
if (node->is_collision_node()) {
|
||||
CollisionNode *cnode;
|
||||
DCAST_INTO_V(cnode, node);
|
||||
CPT(BoundingVolume) node_bv = cnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
CollisionNode *cnode = (CollisionNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = cnode;
|
||||
|
|
@ -814,7 +806,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -827,13 +819,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
}
|
||||
#endif
|
||||
|
||||
GeomNode *gnode;
|
||||
DCAST_INTO_V(gnode, node);
|
||||
CPT(BoundingVolume) node_bv = gnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
GeomNode *gnode = (GeomNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = gnode;
|
||||
|
|
@ -859,7 +845,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -869,9 +855,14 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
// If it's a switch node or sequence node, visit just the one visible
|
||||
// child.
|
||||
int index = node->get_visible_child();
|
||||
if (index >= 0 && index < node->get_num_children()) {
|
||||
CollisionLevelStateDouble next_state(level_state, node->get_child(index));
|
||||
r_traverse_double(next_state, pass);
|
||||
PandaNode::Children children = node->get_children();
|
||||
if (index >= 0 && index < children.get_num_children()) {
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(index);
|
||||
CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateDouble next_state(level_state, child, mask);
|
||||
r_traverse_double(next_state, pass);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (node->is_lod_node()) {
|
||||
|
|
@ -880,16 +871,20 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
// visit all other levels without GeomNode::get_default_collide_mask(),
|
||||
// allowing only collision with CollisionNodes and special geometry under
|
||||
// higher levels of detail.
|
||||
int index = DCAST(LODNode, node)->get_lowest_switch();
|
||||
int index = ((LODNode *)node)->get_lowest_switch();
|
||||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateDouble next_state(level_state, children.get_child(i));
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateDouble next_state(level_state, child, mask);
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
}
|
||||
r_traverse_double(next_state, pass);
|
||||
}
|
||||
r_traverse_double(next_state, pass);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -897,8 +892,12 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
|
|||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateDouble next_state(level_state, children.get_child(i));
|
||||
r_traverse_double(next_state, pass);
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateDouble next_state(level_state, child, mask);
|
||||
r_traverse_double(next_state, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -984,22 +983,13 @@ prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states,
|
|||
*/
|
||||
void CollisionTraverser::
|
||||
r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
||||
if (!level_state.any_in_bounds()) {
|
||||
return;
|
||||
}
|
||||
if (!level_state.apply_transform()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PandaNode *node = level_state.node();
|
||||
if (node->is_collision_node()) {
|
||||
CollisionNode *cnode;
|
||||
DCAST_INTO_V(cnode, node);
|
||||
CPT(BoundingVolume) node_bv = cnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
CollisionNode *cnode = (CollisionNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = cnode;
|
||||
|
|
@ -1025,7 +1015,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1038,13 +1028,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
}
|
||||
#endif
|
||||
|
||||
GeomNode *gnode;
|
||||
DCAST_INTO_V(gnode, node);
|
||||
CPT(BoundingVolume) node_bv = gnode->get_bounds();
|
||||
const GeometricBoundingVolume *node_gbv = nullptr;
|
||||
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
DCAST_INTO_V(node_gbv, node_bv);
|
||||
}
|
||||
GeomNode *gnode = (GeomNode *)node;
|
||||
|
||||
CollisionEntry entry;
|
||||
entry._into_node = gnode;
|
||||
|
|
@ -1070,7 +1054,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
entry,
|
||||
level_state.get_parent_bound(c),
|
||||
level_state.get_local_bound(c),
|
||||
node_gbv);
|
||||
level_state.get_node_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1080,9 +1064,14 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
// If it's a switch node or sequence node, visit just the one visible
|
||||
// child.
|
||||
int index = node->get_visible_child();
|
||||
if (index >= 0 && index < node->get_num_children()) {
|
||||
CollisionLevelStateQuad next_state(level_state, node->get_child(index));
|
||||
r_traverse_quad(next_state, pass);
|
||||
PandaNode::Children children = node->get_children();
|
||||
if (index >= 0 && index < children.get_num_children()) {
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(index);
|
||||
CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateQuad next_state(level_state, child, mask);
|
||||
r_traverse_quad(next_state, pass);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (node->is_lod_node()) {
|
||||
|
|
@ -1091,16 +1080,20 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
// visit all other levels without GeomNode::get_default_collide_mask(),
|
||||
// allowing only collision with CollisionNodes and special geometry under
|
||||
// higher levels of detail.
|
||||
int index = DCAST(LODNode, node)->get_lowest_switch();
|
||||
int index = ((LODNode *)node)->get_lowest_switch();
|
||||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateQuad next_state(level_state, children.get_child(i));
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateQuad next_state(level_state, child, mask);
|
||||
if (i != index) {
|
||||
next_state.set_include_mask(next_state.get_include_mask() &
|
||||
~GeomNode::get_default_collide_mask());
|
||||
}
|
||||
r_traverse_quad(next_state, pass);
|
||||
}
|
||||
r_traverse_quad(next_state, pass);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -1108,8 +1101,12 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
|
|||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
CollisionLevelStateQuad next_state(level_state, children.get_child(i));
|
||||
r_traverse_quad(next_state, pass);
|
||||
const PandaNode::DownConnection &child = children.get_child_connection(i);
|
||||
CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child);
|
||||
if (!mask.is_zero()) {
|
||||
CollisionLevelStateQuad next_state(level_state, child, mask);
|
||||
r_traverse_quad(next_state, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1162,10 +1159,7 @@ compare_collider_to_node(CollisionEntry &entry,
|
|||
// CollisionNodes. We are already filtering out tests for a
|
||||
// CollisionNode into itself.
|
||||
CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
|
||||
const GeometricBoundingVolume *solid_gbv = nullptr;
|
||||
if (solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
solid_gbv = (const GeometricBoundingVolume *)solid_bv.p();
|
||||
}
|
||||
const GeometricBoundingVolume *solid_gbv = solid_bv->as_geometric_bounding_volume();
|
||||
|
||||
compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
|
||||
}
|
||||
|
|
@ -1198,14 +1192,13 @@ compare_collider_to_geom_node(CollisionEntry &entry,
|
|||
if (geom != nullptr) {
|
||||
CPT(BoundingVolume) geom_bv = geom->get_bounds();
|
||||
const GeometricBoundingVolume *geom_gbv = nullptr;
|
||||
if (num_geoms > 1 &&
|
||||
geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
||||
if (num_geoms > 1) {
|
||||
// Only bother to test against each geom's bounding volume if we
|
||||
// have more than one geom in the node, as a slight optimization.
|
||||
// (If the node contains just one geom, then the node's bounding
|
||||
// volume, which we just tested, is the same as the geom's bounding
|
||||
// volume.)
|
||||
DCAST_INTO_V(geom_gbv, geom_bv);
|
||||
geom_gbv = geom_bv->as_geometric_bounding_volume();
|
||||
}
|
||||
|
||||
compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ TypeHandle CollisionVisualizer::_type_handle;
|
|||
CollisionVisualizer::
|
||||
CollisionVisualizer(const std::string &name) : PandaNode(name), _lock("CollisionVisualizer") {
|
||||
set_cull_callback();
|
||||
set_renderable();
|
||||
|
||||
// We always want to render the CollisionVisualizer node itself (even if it
|
||||
// doesn't appear to have any geometry within it).
|
||||
|
|
@ -62,6 +63,7 @@ CollisionVisualizer(const CollisionVisualizer ©) :
|
|||
_normal_scale(copy._normal_scale) {
|
||||
|
||||
set_cull_callback();
|
||||
set_renderable();
|
||||
|
||||
// We always want to render the CollisionVisualizer node itself (even if it
|
||||
// doesn't appear to have any geometry within it).
|
||||
|
|
@ -145,12 +147,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
bool was_detected = (solid_info._detected_count > 0);
|
||||
PT(PandaNode) node = solid->get_viz(trav, xform_data, !was_detected);
|
||||
if (node != nullptr) {
|
||||
CullTraverserData next_data(xform_data, node);
|
||||
|
||||
// We don't want to inherit the render state from above for these
|
||||
// guys.
|
||||
next_data._state = get_viz_state();
|
||||
trav->traverse(next_data);
|
||||
trav->traverse_down(xform_data, node, xform_data._net_transform, get_viz_state());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -251,18 +250,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
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 CollisionVisualizer::
|
||||
is_renderable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a brief description of the node to the indicated output stream.
|
||||
* This is invoked by the << operator. It may be overridden in derived
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ public:
|
|||
// from parent class PandaNode.
|
||||
virtual PandaNode *make_copy() const;
|
||||
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
|
||||
virtual bool is_renderable() const;
|
||||
virtual void output(std::ostream &out) const;
|
||||
|
||||
// from parent class CollisionRecorder.
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ set_dimensions(const LVecBase4 &dimensions) {
|
|||
* Changes the range of the depth buffer this DisplayRegion writes to.
|
||||
* The parameters range from 0 to 1. It is legal for the near value to be
|
||||
* larger than the far value.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
INLINE void DisplayRegion::
|
||||
set_depth_range(PN_stdfloat near_depth, PN_stdfloat far_depth) {
|
||||
|
|
|
|||
|
|
@ -834,6 +834,9 @@ get_active_display_region(int n) const {
|
|||
* which will be a texture suitable for applying to geometry within the scene
|
||||
* rendered into this window.
|
||||
*
|
||||
* If you pass zero as the buffer size, the buffer will have the same size as
|
||||
* the host window, and will automatically be resized when the host window is.
|
||||
*
|
||||
* If tex is not NULL, it is the texture that will be set up for rendering
|
||||
* into; otherwise, a new Texture object will be created. In either case, the
|
||||
* target texture can be retrieved from the return value with
|
||||
|
|
|
|||
|
|
@ -3581,31 +3581,8 @@ async_reload_texture(TextureContext *tc) {
|
|||
priority = _current_display_region->get_texture_reload_priority();
|
||||
}
|
||||
|
||||
string task_name = string("reload:") + tc->get_texture()->get_name();
|
||||
PT(AsyncTaskManager) task_mgr = _loader->get_task_manager();
|
||||
|
||||
// See if we are already loading this task.
|
||||
AsyncTaskCollection orig_tasks = task_mgr->find_tasks(task_name);
|
||||
size_t num_tasks = orig_tasks.get_num_tasks();
|
||||
for (size_t ti = 0; ti < num_tasks; ++ti) {
|
||||
AsyncTask *task = orig_tasks.get_task(ti);
|
||||
if (task->is_exact_type(TextureReloadRequest::get_class_type()) &&
|
||||
((TextureReloadRequest *)task)->get_texture() == tc->get_texture()) {
|
||||
// This texture is already queued to be reloaded. Don't queue it again,
|
||||
// just make sure the priority is updated, and return.
|
||||
task->set_priority(std::max(task->get_priority(), priority));
|
||||
return (AsyncFuture *)task;
|
||||
}
|
||||
}
|
||||
|
||||
// This texture has not yet been queued to be reloaded. Queue it up now.
|
||||
PT(AsyncTask) request =
|
||||
new TextureReloadRequest(task_name,
|
||||
_prepared_objects, tc->get_texture(),
|
||||
_supports_compressed_texture);
|
||||
request->set_priority(priority);
|
||||
_loader->load_async(request);
|
||||
return (AsyncFuture *)request.p();
|
||||
Texture *tex = tc->get_texture();
|
||||
return tex->async_ensure_ram_image(_supports_compressed_texture, priority);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ THE PHILOSOPHY OF EGG FILES (vs. bam files)
|
|||
Egg files are used by Panda3D to describe many properties of a scene:
|
||||
simple geometry, including special effects and collision surfaces,
|
||||
characters including skeletons, morphs, and multiple-joint
|
||||
assignments, and character animation tables.
|
||||
assignments, and character animation tables.
|
||||
|
||||
Egg files are designed to be the lingua franca of model manipulation
|
||||
for Panda tools. A number of utilities are provided that read and
|
||||
|
|
@ -168,8 +168,9 @@ appear before they are referenced.
|
|||
The remaining formats are generic and specify only the semantic
|
||||
meaning of the channels. The size of the texels is determined by
|
||||
the width of the components in the image file. RGBA is the most
|
||||
general; RGB is the same, but without any alpha channel. RGBM is
|
||||
like RGBA, except that it requests only one bit of alpha, if the
|
||||
general; RGB is the same, but without any alpha channel.
|
||||
|
||||
RGBM is like RGBA, except that it requests only one bit of alpha, if the
|
||||
graphics card can provide that, to leave more room for the RGB
|
||||
components, which is especially important for older 16-bit
|
||||
graphics cards (the "M" stands for "mask", as in a cutout).
|
||||
|
|
@ -211,7 +212,7 @@ appear before they are referenced.
|
|||
Although less often used, for 3-d textures wrapw may also be
|
||||
specified, and it behaves similarly to wrapu and wrapv.
|
||||
|
||||
There are other legal values in addtional to REPEAT and CLAMP.
|
||||
There are other legal values in additional to REPEAT and CLAMP.
|
||||
The full list is:
|
||||
|
||||
CLAMP
|
||||
|
|
@ -278,7 +279,7 @@ appear before they are referenced.
|
|||
/ (number of views).
|
||||
|
||||
<Scalar> read-mipmaps { flag }
|
||||
|
||||
|
||||
If this flag is nonzero, then pre-generated mipmap levels will be
|
||||
loaded along with the texture. In this case, the filename should
|
||||
contain a sequence of one or more hash mark ("#") characters,
|
||||
|
|
@ -413,7 +414,7 @@ appear before they are referenced.
|
|||
<Scalar> combine-alpha-operand2 { src-alpha }
|
||||
|
||||
<Scalar> saved-result { flag }
|
||||
|
||||
|
||||
If flag is nonzero, then it indicates that this particular texture
|
||||
stage will be supplied as the "last_saved_result" source for any
|
||||
future texture stages.
|
||||
|
|
@ -704,7 +705,7 @@ appear before they are referenced.
|
|||
<UV> [name] { u v [w] [tangent] [binormal] [morph-list] }
|
||||
|
||||
This gives the texture coordinates of the vertex. This must be
|
||||
specified if a texture is to be mapped onto this geometry.
|
||||
specified if a texture is to be mapped onto this geometry.
|
||||
|
||||
The texture coordinates are usually two-dimensional, with two
|
||||
component values (u v), but they may also be three-dimensional,
|
||||
|
|
@ -739,7 +740,7 @@ appear before they are referenced.
|
|||
meaning to custom code or a custom shader. Like named UV's, there
|
||||
may be multiple Aux entries for a given vertex, each with a
|
||||
different name.
|
||||
|
||||
|
||||
|
||||
|
||||
<DynamicVertexPool> name { vertices }
|
||||
|
|
@ -758,18 +759,18 @@ appear before they are referenced.
|
|||
|
||||
At the present time, the DynamicVertexPool is not implemented in
|
||||
Panda3D.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
GEOMETRY ENTRIES
|
||||
|
||||
<Polygon> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
<Polygon> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
}
|
||||
|
||||
A polygon consists of a sequence of vertices from a single vertex
|
||||
|
|
@ -822,7 +823,7 @@ GEOMETRY ENTRIES
|
|||
used unless all vertices also have a normal. If no normal is
|
||||
defined, none will be supplied. The polygon normal, like the
|
||||
vertex normal, may be morphed by specifying a series of <DNormal>
|
||||
entries.
|
||||
entries.
|
||||
|
||||
The polygon normal is used only for lighting and environment
|
||||
mapping calculations, and is not related to the implicit normal
|
||||
|
|
@ -844,7 +845,7 @@ GEOMETRY ENTRIES
|
|||
disabled, and polygons are one-sided; specifying a nonzero value
|
||||
disables backface culling for this particular polygon and allows
|
||||
it to be viewed from either side.
|
||||
|
||||
|
||||
|
||||
<Scalar> bin { bin-name }
|
||||
|
||||
|
|
@ -916,12 +917,12 @@ GEOMETRY ENTRIES
|
|||
or even within a texture.
|
||||
|
||||
|
||||
<Patch> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
<Patch> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
}
|
||||
|
||||
A patch is similar to a polygon, but it is a special primitive that
|
||||
|
|
@ -936,12 +937,12 @@ GEOMETRY ENTRIES
|
|||
specified for Patch.
|
||||
|
||||
|
||||
<PointLight> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
<PointLight> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
}
|
||||
|
||||
A PointLight is a set of single points. One point is drawn for each
|
||||
|
|
@ -964,12 +965,12 @@ GEOMETRY ENTRIES
|
|||
viewer normally.
|
||||
|
||||
|
||||
<Line> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
<Line> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
[component attributes]
|
||||
}
|
||||
|
||||
|
|
@ -985,11 +986,11 @@ GEOMETRY ENTRIES
|
|||
line segment, as in TriangleStrip, below.
|
||||
|
||||
|
||||
<TriangleStrip> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
<TriangleStrip> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
[component attributes]
|
||||
}
|
||||
|
|
@ -1013,7 +1014,7 @@ GEOMETRY ENTRIES
|
|||
It is possible for the individual triangles of a triangle strip to
|
||||
have a separate normal and/or color. If so, a <Component> entry
|
||||
should be given for each so-modified triangle:
|
||||
|
||||
|
||||
<Component> index {
|
||||
<RGBA> { r g b a [morph-list] }
|
||||
<Normal> { x y z [morph-list] }
|
||||
|
|
@ -1024,11 +1025,11 @@ GEOMETRY ENTRIES
|
|||
must always follow the vertex list.
|
||||
|
||||
|
||||
<TriangleFan> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
<TriangleFan> name {
|
||||
[attributes]
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
[component attributes]
|
||||
}
|
||||
|
|
@ -1057,7 +1058,7 @@ itself doesn't support them and will always create static curves and
|
|||
surfaces. External tools like egg-qtess, however, may respect them.
|
||||
|
||||
<NURBSCurve> {
|
||||
[attributes]
|
||||
[attributes]
|
||||
|
||||
<Order> { order }
|
||||
<Knots> { knot-list }
|
||||
|
|
@ -1083,7 +1084,7 @@ surfaces. External tools like egg-qtess, however, may respect them.
|
|||
|
||||
<Scalar> type { curve-type }
|
||||
|
||||
This defines the semanting meaning of this curve, either XYZ, HPR,
|
||||
This defines the semantic meaning of this curve, either XYZ, HPR,
|
||||
or T. If the type is XYZ, the curve will automatically be
|
||||
transformed between Y-up and Z-up if necessary; otherwise, it will
|
||||
be left alone.
|
||||
|
|
@ -1107,16 +1108,16 @@ surfaces. External tools like egg-qtess, however, may respect them.
|
|||
|
||||
|
||||
<NURBSSurface> name {
|
||||
[attributes]
|
||||
[attributes]
|
||||
|
||||
<Order> { u-order v-order }
|
||||
<U-knots> { u-knot-list }
|
||||
<V-knots> { v-knot-list }
|
||||
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
<VertexRef> {
|
||||
indices
|
||||
<Ref> { pool-name }
|
||||
}
|
||||
}
|
||||
|
||||
A NURBS surface is an extension of a NURBS curve into two parametric
|
||||
|
|
@ -1137,7 +1138,7 @@ surfaces. External tools like egg-qtess, however, may respect them.
|
|||
These define the number of subdivisions to make in the U and V
|
||||
directions to represent the surface. A uniform subdivision is
|
||||
always made, and trim curves are not respected (though they will
|
||||
be drawn in if the trim curves themselves also have a subiv
|
||||
be drawn in if the trim curves themselves also have a subdiv
|
||||
parameter). This is only intended as a cheesy visualization.
|
||||
|
||||
|
||||
|
|
@ -1194,7 +1195,7 @@ surfaces. External tools like egg-qtess, however, may respect them.
|
|||
|
||||
Although the egg syntax supports trim curves, there are at present
|
||||
no egg processing tools that respect them. For instance, egg-qtess
|
||||
ignores trim curves and always tesselates the entire NURBS surface.
|
||||
ignores trim curves and always tessellates the entire NURBS surface.
|
||||
|
||||
|
||||
MORPH DESCRIPTION ENTRIES
|
||||
|
|
@ -1267,7 +1268,7 @@ GROUPING ENTRIES
|
|||
attributes of the group:
|
||||
|
||||
GROUP BINARY ATTRIBUTES
|
||||
|
||||
|
||||
These attributes may be either on or off; they are off by default.
|
||||
They are turned on by specifying a non-zero "boolean-value".
|
||||
|
||||
|
|
@ -1310,6 +1311,15 @@ GROUPING ENTRIES
|
|||
vertices; joints and morphs appearing outside of a hierarchy
|
||||
identified with a <Dart> flag are undefined.
|
||||
|
||||
<Dart> { structured }
|
||||
|
||||
This is an optional alternative for the <Dart> flag.
|
||||
By default, Panda will collapse all of the geometry in a group (with the <Dart> { 1 } flag)
|
||||
a single node. While this is optimal for conditions such as characters moving around
|
||||
a scene, it may be suboptimal for larger or more complex characters.
|
||||
This entry is typically generated by the egg-optchar program with the "-dart structured" flag.
|
||||
<Dart> { structured } implies <Dart> { 1 }.
|
||||
|
||||
<Switch> { boolean-value }
|
||||
|
||||
This attribute indicates that the child nodes of this group
|
||||
|
|
@ -1446,7 +1456,7 @@ GROUPING ENTRIES
|
|||
of the billboard, not at the origin of the scene.
|
||||
|
||||
<SwitchCondition> {
|
||||
<Distance> {
|
||||
<Distance> {
|
||||
in out [fade] <Vertex> { x y z }
|
||||
}
|
||||
}
|
||||
|
|
@ -1483,7 +1493,7 @@ GROUPING ENTRIES
|
|||
Valid types so far are:
|
||||
|
||||
Plane
|
||||
|
||||
|
||||
The geometry represents an infinite plane. The first polygon
|
||||
found in the group will define the plane.
|
||||
|
||||
|
|
@ -1550,7 +1560,7 @@ GROUPING ENTRIES
|
|||
most compatibility.
|
||||
|
||||
keep
|
||||
|
||||
|
||||
Don't discard the visible geometry after using it to define a
|
||||
collision surface; create both an invisible collision surface
|
||||
and the visible geometry.
|
||||
|
|
@ -1619,7 +1629,8 @@ GROUPING ENTRIES
|
|||
|
||||
There may also be additional predefined egg object types not
|
||||
listed here; see the *.pp files that are installed into the etc
|
||||
directory for a complete list.
|
||||
directory for a complete list. Additionally, you can reference
|
||||
the file located in $PANDA/src/doc/howto.MultiGenModelFlags
|
||||
|
||||
<Transform> { transform-definition }
|
||||
|
||||
|
|
@ -1683,9 +1694,9 @@ GROUPING ENTRIES
|
|||
current group, regardless of the group in which the geometry is
|
||||
actually defined. See the <Joint> description, below.
|
||||
|
||||
<AnimPreload> {
|
||||
<AnimPreload> {
|
||||
<Scalar> fps { float-value }
|
||||
<Scalar> num-frames { integer-value }
|
||||
<Scalar> num-frames { integer-value }
|
||||
}
|
||||
|
||||
One or more AnimPreload entries may appear within the <Group> that
|
||||
|
|
@ -1720,7 +1731,7 @@ GROUPING ENTRIES
|
|||
geometry in the scene graph. The syntax is:
|
||||
|
||||
<Instance> name {
|
||||
<Ref> { group-name }
|
||||
<Ref> { group-name }
|
||||
[ <Ref> { group-name } ... ]
|
||||
}
|
||||
|
||||
|
|
@ -1746,8 +1757,8 @@ GROUPING ENTRIES
|
|||
and it may contain other joints.
|
||||
|
||||
A tree of <Joint> nodes only makes sense within a character
|
||||
definition, which is created by applying the <DART> flag to a group.
|
||||
See <DART>, above.
|
||||
definition, which is created by applying the <Dart> flag to a group.
|
||||
See <Dart>, above.
|
||||
|
||||
The vertex assignment is crucial. This is how the geometry of a
|
||||
character is made to move with the joints. The character's geometry
|
||||
|
|
@ -1790,7 +1801,7 @@ GROUPING ENTRIES
|
|||
bundles, or any one of the following (<Scalar> entries are optional,
|
||||
and default as shown):
|
||||
|
||||
<S$Anim> name {
|
||||
<S$Anim> name {
|
||||
<Scalar> fps { 24 }
|
||||
<V> { values }
|
||||
}
|
||||
|
|
@ -1844,7 +1855,7 @@ GROUPING ENTRIES
|
|||
an animation sequence.
|
||||
|
||||
|
||||
<VertexAnim> name {
|
||||
<VertexAnim> name {
|
||||
<Scalar> width { table-width }
|
||||
<Scalar> fps { 24 }
|
||||
<V> { values }
|
||||
|
|
@ -1913,6 +1924,10 @@ ANIMATION STRUCTURE
|
|||
animated model description. Without the <Dart> flag, joints will be
|
||||
treated as ordinary groups, and morphs will be ignored.
|
||||
|
||||
It is important to note that utilizing <Dart> { 1 } will collapse all of the
|
||||
model's geometry into a single node. To omit this, use <Dart> { structured } instead.
|
||||
(See <Dart> above.)
|
||||
|
||||
In the above, UPPERCASE NAMES represent an arbitrary name that you
|
||||
may choose. The name of the enclosing group, CHARACTER_NAME, is
|
||||
taken as the name of the animated model. It should generally match
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
This document describes the different kinds of model flags one can place in
|
||||
the comment field of MultiGen group beads. The general format for a model
|
||||
flag is:
|
||||
flag is:
|
||||
<egg> { <FLAGNAME> {value} }
|
||||
|
||||
The most up-to-date version of this document can be found in:
|
||||
|
|
@ -30,7 +30,7 @@ The most up-to-date version of this document can be found in:
|
|||
<egg> { <ObjectType> {camera-barrier} } Invisible collision surface for camera and colliders
|
||||
<egg> { <ObjectType> {camera-barrier-sphere} } Invisible sphere collision surface for camera and colliders
|
||||
<egg> { <ObjectType> {backstage} } Modeling reference object
|
||||
<egg> { <Decal> {1} } Decal the node below to me
|
||||
<egg> { <Decal> {1} } Decal the node below to me
|
||||
(like a window on a wall)
|
||||
<egg> { <Scalar> fps { # } } Set rate of animation for a pfSequence
|
||||
|
||||
|
|
@ -52,12 +52,12 @@ common flag/value pairs and describes what they are used for.
|
|||
which one places the flag (so names like red-hut are more useful than
|
||||
names like o34).
|
||||
|
||||
<egg> { <Model> {1} }
|
||||
<egg> { <Model> {1} }
|
||||
|
||||
Used to show/hide, change the color, or change the collision properties
|
||||
Used to show/hide, change the color, or change the collision properties
|
||||
of a chunk.
|
||||
|
||||
<egg> { <DCS> {1} }
|
||||
<egg> { <DCS> {1} }
|
||||
|
||||
Used to move, rotate, or scale a chunk of the model. Also can be used
|
||||
(like the <Model> flag) to show/hide, change the color, and change the
|
||||
|
|
@ -91,7 +91,7 @@ common flag/value pairs and describes what they are used for.
|
|||
a door, for example, so the player can tell when the avatar has
|
||||
moved through the door.
|
||||
- BACKSTAGE objects are not translated over to the player. Modelers
|
||||
should use this flag on reference objects that they include to help
|
||||
should use this flag on reference objects that they include to help
|
||||
in the modeling task (such as scale references)
|
||||
|
||||
IMPORTANT NOTE:
|
||||
|
|
@ -108,7 +108,7 @@ common flag/value pairs and describes what they are used for.
|
|||
|
||||
********** PROPERTIES **********
|
||||
|
||||
These are used to control properties of selected chunks.
|
||||
These are used to control properties of selected chunks.
|
||||
|
||||
<egg> { <Scalar> fps { frame-rate } }
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ These are used to control properties of selected chunks.
|
|||
Multiple Flag/value pairs can be combined within an single <egg> field.
|
||||
For example:
|
||||
|
||||
<egg> { <Model> {1}
|
||||
<egg> { <Model> {1}
|
||||
<ObjectType> {barrier} }
|
||||
|
||||
Generally, the <Model> flag can be combined with most other flags
|
||||
|
|
@ -134,7 +134,7 @@ These are used to control properties of selected chunks.
|
|||
could also be written as:
|
||||
|
||||
<egg>{<model>{1}<objecttype>{barrier}}
|
||||
|
||||
|
||||
3) Where to place the flags
|
||||
|
||||
All model flags except <Normal> flags are generally placed in the
|
||||
|
|
@ -154,6 +154,6 @@ These are used to control properties of selected chunks.
|
|||
4) Flags at different levels in the model
|
||||
|
||||
Flags in lower level beads generally override flags in upper level
|
||||
beads.
|
||||
beads.
|
||||
|
||||
5) For more detailed information see $PANDA/src/doc/eggSyntax.txt.
|
||||
|
|
|
|||
|
|
@ -357,10 +357,7 @@ upload_texture(DXTextureContext9 *dtc, bool force) {
|
|||
async_reload_texture(dtc);
|
||||
has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
|
||||
if (!has_image) {
|
||||
if (dtc->was_simple_image_modified()) {
|
||||
return dtc->create_simple_texture(*_screen);
|
||||
}
|
||||
return true;
|
||||
return dtc->create_simple_texture(*_screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1157,7 +1157,7 @@ create_simple_texture(DXScreenData &scrn) {
|
|||
goto error_exit;
|
||||
}
|
||||
|
||||
mark_simple_loaded();
|
||||
mark_loaded();
|
||||
return true;
|
||||
|
||||
error_exit:
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@
|
|||
|
||||
using std::string;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
@ -589,16 +585,15 @@ determine_bin_home(EggBin *egg_bin) {
|
|||
// ahead and make an implicit node for the joint.
|
||||
|
||||
if (egg_group->get_dcs_type() == EggGroup::DC_none) {
|
||||
/*
|
||||
* Unless the user specifically forbade exposing the joint by putting an
|
||||
* explicit "<DCS> { none }" entry in the joint. In this case, we return NULL
|
||||
* to treat the geometry as dynamic (and animate it by animating its
|
||||
* vertices), but display lists and vertex buffers will perform better if more
|
||||
* geometry is rigid. There's a tradeoff, though, since the cull traverser
|
||||
* will have to do more work with additional transforms in the scene graph,
|
||||
* and this may also break up the geometry into more individual pieces, which
|
||||
* is the biggest limiting factor on modern PC graphics cards.
|
||||
*/
|
||||
// Unless the user specifically forbade exposing the joint by putting an
|
||||
// explicit "<DCS> { none }" entry in the joint. In this case, we return
|
||||
// nullptr to treat the geometry as dynamic (and animate it by animating
|
||||
// its vertices), but display lists and vertex buffers will perform better
|
||||
// if more geometry is rigid. There's a tradeoff, though, since the cull
|
||||
// traverser will have to do more work with additional transforms in the
|
||||
// scene graph, and this may also break up the geometry into more
|
||||
// individual pieces, which is the biggest limiting factor on modern PC
|
||||
// graphics cards.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ set(P3EVENT_HEADERS
|
|||
config_event.h
|
||||
buttonEvent.I buttonEvent.h
|
||||
buttonEventList.I buttonEventList.h
|
||||
functionAsyncTask.h functionAsyncTask.I
|
||||
genericAsyncTask.h genericAsyncTask.I
|
||||
pointerEvent.I pointerEvent.h
|
||||
pointerEventList.I pointerEventList.h
|
||||
|
|
@ -28,6 +29,7 @@ set(P3EVENT_SOURCES
|
|||
asyncTaskSequence.cxx
|
||||
buttonEvent.cxx
|
||||
buttonEventList.cxx
|
||||
functionAsyncTask.cxx
|
||||
genericAsyncTask.cxx
|
||||
pointerEvent.cxx
|
||||
pointerEventList.cxx
|
||||
|
|
|
|||
|
|
@ -32,6 +32,21 @@ get_clock() {
|
|||
return _clock;
|
||||
}
|
||||
|
||||
#ifndef CPPPARSER
|
||||
/**
|
||||
* Adds a new task which calls the indicated function to the task manager.
|
||||
* Returns the newly created FunctionAsyncTask object.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
INLINE AsyncTask *AsyncTaskManager::
|
||||
add(const std::string &name, FunctionAsyncTask::TaskFunction function) {
|
||||
AsyncTask *task = new FunctionAsyncTask(name, std::move(function));
|
||||
add(task);
|
||||
return task;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the number of tasks that are currently active or sleeping within
|
||||
* the task manager.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "clockObject.h"
|
||||
#include "ordered_vector.h"
|
||||
#include "indirectCompareNames.h"
|
||||
#include "functionAsyncTask.h"
|
||||
|
||||
/**
|
||||
* A class to manage a loose queue of isolated tasks, which can be performed
|
||||
|
|
@ -64,6 +65,9 @@ PUBLISHED:
|
|||
BLOCKING bool remove_task_chain(const std::string &name);
|
||||
|
||||
void add(AsyncTask *task);
|
||||
#ifndef CPPPARSER
|
||||
INLINE AsyncTask *add(const std::string &name, FunctionAsyncTask::TaskFunction function);
|
||||
#endif
|
||||
bool has_task(AsyncTask *task) const;
|
||||
|
||||
AsyncTask *find_task(const std::string &name) const;
|
||||
|
|
|
|||
|
|
@ -149,3 +149,35 @@ update_mods(ModifierButtons &mods) const {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE ButtonHandle ButtonEvent::
|
||||
get_button() const {
|
||||
return _button;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE int ButtonEvent::
|
||||
get_keycode() const {
|
||||
return _keycode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE ButtonEvent::Type ButtonEvent::
|
||||
get_type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE double ButtonEvent::
|
||||
get_time() const {
|
||||
return _time;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,21 +42,25 @@ class DatagramIterator;
|
|||
* keyboard (and other buttons for which there is a corresponding ButtonHandle
|
||||
* object), while keystroke events are defined across the entire Unicode
|
||||
* character set.
|
||||
*
|
||||
* This API should not be considered stable and may change in a future version
|
||||
* of Panda3D.
|
||||
*/
|
||||
class EXPCL_PANDA_EVENT ButtonEvent {
|
||||
public:
|
||||
PUBLISHED:
|
||||
enum Type {
|
||||
/*
|
||||
* T_down and T_up represent a button changing state correspondingly.
|
||||
* T_resume_down is a special event that is only thrown when focus is returned
|
||||
* to a window and a button is detected as being held down at that point; it
|
||||
* indicates that the button should be considered down now (if it wasn't
|
||||
* already), but it didn't just get pressed down at this moment, it was
|
||||
* depressed some time ago. It's mainly used for correct tracking of modifier
|
||||
* keys like shift and control, and can be ignored for other keys.
|
||||
*/
|
||||
// T_down is sent when a button was just pressed.
|
||||
T_down,
|
||||
|
||||
// T_resume_down is a special event that is only thrown when focus is
|
||||
// returned to a window and a button is detected as being held down at that
|
||||
// point; it indicates that the button should be considered down now (if it
|
||||
// wasn't already), but it didn't just get pressed down at this moment, it
|
||||
// was depressed some time ago. It's mainly used for correct tracking of
|
||||
// modifier keys like shift and control, and can be ignored for other keys.
|
||||
T_resume_down,
|
||||
|
||||
// T_down is sent when a button is released.
|
||||
T_up,
|
||||
|
||||
// T_repeat is sent for each a keyrepeat event generated by the system,
|
||||
|
|
@ -84,6 +88,7 @@ public:
|
|||
T_raw_up,
|
||||
};
|
||||
|
||||
public:
|
||||
INLINE ButtonEvent();
|
||||
INLINE ButtonEvent(ButtonHandle button, Type type, double time = ClockObject::get_global_clock()->get_frame_time());
|
||||
INLINE ButtonEvent(int keycode, double time = ClockObject::get_global_clock()->get_frame_time());
|
||||
|
|
@ -92,17 +97,30 @@ public:
|
|||
INLINE ButtonEvent(const ButtonEvent ©);
|
||||
INLINE void operator = (const ButtonEvent ©);
|
||||
|
||||
PUBLISHED:
|
||||
INLINE bool operator == (const ButtonEvent &other) const;
|
||||
INLINE bool operator != (const ButtonEvent &other) const;
|
||||
INLINE bool operator < (const ButtonEvent &other) const;
|
||||
|
||||
public:
|
||||
INLINE bool update_mods(ModifierButtons &mods) const;
|
||||
|
||||
INLINE ButtonHandle get_button() const;
|
||||
INLINE int get_keycode() const;
|
||||
INLINE Type get_type() const;
|
||||
INLINE double get_time() const;
|
||||
|
||||
void output(std::ostream &out) const;
|
||||
|
||||
void write_datagram(Datagram &dg) const;
|
||||
void read_datagram(DatagramIterator &scan);
|
||||
|
||||
PUBLISHED:
|
||||
MAKE_PROPERTY(button, get_button);
|
||||
MAKE_PROPERTY(keycode, get_keycode);
|
||||
MAKE_PROPERTY(type, get_type);
|
||||
MAKE_PROPERTY(time, get_time);
|
||||
|
||||
public:
|
||||
// _button will be filled in if type is T_down, T_resume_down, or T_up.
|
||||
ButtonHandle _button;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ PUBLISHED:
|
|||
virtual void output(std::ostream &out) const;
|
||||
void write(std::ostream &out, int indent_level = 0) const;
|
||||
|
||||
MAKE_SEQ_PROPERTY(events, get_num_events, get_event);
|
||||
|
||||
private:
|
||||
typedef pvector<ButtonEvent> Events;
|
||||
Events _events;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "event.h"
|
||||
#include "eventHandler.h"
|
||||
#include "eventParameter.h"
|
||||
#include "functionAsyncTask.h"
|
||||
#include "genericAsyncTask.h"
|
||||
#include "pointerEventList.h"
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ ConfigureFn(config_event) {
|
|||
EventHandler::init_type();
|
||||
EventStoreInt::init_type("EventStoreInt");
|
||||
EventStoreDouble::init_type("EventStoreDouble");
|
||||
FunctionAsyncTask::init_type();
|
||||
GenericAsyncTask::init_type();
|
||||
|
||||
ButtonEventList::register_with_read_factory();
|
||||
|
|
|
|||
|
|
@ -104,6 +104,20 @@ dispatch_event(const Event *event) {
|
|||
}
|
||||
}
|
||||
|
||||
// now for lambda hooks
|
||||
LambdaHooks::const_iterator lhi;
|
||||
lhi = _lambdahooks.find(event->get_name());
|
||||
|
||||
if (lhi != _lambdahooks.end()) {
|
||||
// found one
|
||||
const LambdaFunctions &functions = (*lhi).second;
|
||||
|
||||
size_t num_functions = functions.size();
|
||||
for (size_t i = 0; i < num_functions; ++i) {
|
||||
functions[i](event);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check for futures that need to be triggered.
|
||||
Futures::iterator fi;
|
||||
fi = _futures.find(event->get_name());
|
||||
|
|
@ -189,6 +203,19 @@ add_hook(const string &event_name, EventCallbackFunction *function,
|
|||
return _cbhooks[event_name].insert(CallbackFunction(function, data)).second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the indicated function to the list of those that will be called when
|
||||
* the named event is thrown. Returns true if the function was successfully
|
||||
* added, false if it was already defined on the indicated event name. This
|
||||
* version stores an arbitrary C++ lambda.
|
||||
*/
|
||||
void EventHandler::
|
||||
add_hook(const string &event_name, EventLambda function) {
|
||||
assert(!event_name.empty());
|
||||
assert(function);
|
||||
_lambdahooks[event_name].push_back(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is any hook added on the indicated event name, false
|
||||
* otherwise.
|
||||
|
|
@ -212,6 +239,14 @@ has_hook(const string &event_name) const {
|
|||
}
|
||||
}
|
||||
|
||||
LambdaHooks::const_iterator lhi;
|
||||
lhi = _lambdahooks.find(event_name);
|
||||
if (lhi != _lambdahooks.end()) {
|
||||
if (!(*lhi).second.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -306,6 +341,14 @@ remove_hooks(const string &event_name) {
|
|||
_cbhooks.erase(chi);
|
||||
}
|
||||
|
||||
LambdaHooks::iterator lhi = _lambdahooks.find(event_name);
|
||||
if (lhi != _lambdahooks.end()) {
|
||||
if (!(*lhi).second.empty()) {
|
||||
any_removed = true;
|
||||
}
|
||||
_lambdahooks.erase(lhi);
|
||||
}
|
||||
|
||||
return any_removed;
|
||||
}
|
||||
|
||||
|
|
@ -344,6 +387,7 @@ void EventHandler::
|
|||
remove_all_hooks() {
|
||||
_hooks.clear();
|
||||
_cbhooks.clear();
|
||||
_lambdahooks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@
|
|||
#include "pset.h"
|
||||
#include "pmap.h"
|
||||
|
||||
#ifndef CPPPARSER
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
class EventQueue;
|
||||
|
||||
/**
|
||||
|
|
@ -40,6 +44,12 @@ public:
|
|||
typedef void EventFunction(const Event *);
|
||||
typedef void EventCallbackFunction(const Event *, void *);
|
||||
|
||||
#ifdef CPPPARSER
|
||||
typedef EventFunction EventLambda;
|
||||
#else
|
||||
typedef std::function<void (const Event *)> EventLambda;
|
||||
#endif
|
||||
|
||||
PUBLISHED:
|
||||
explicit EventHandler(EventQueue *ev_queue);
|
||||
~EventHandler() {}
|
||||
|
|
@ -58,6 +68,7 @@ public:
|
|||
bool add_hook(const std::string &event_name, EventFunction *function);
|
||||
bool add_hook(const std::string &event_name, EventCallbackFunction *function,
|
||||
void *data);
|
||||
void add_hook(const std::string &event_name, EventLambda function);
|
||||
bool has_hook(const std::string &event_name) const;
|
||||
bool has_hook(const std::string &event_name, EventFunction *function) const;
|
||||
bool has_hook(const std::string &event_name, EventCallbackFunction *function,
|
||||
|
|
@ -78,10 +89,13 @@ protected:
|
|||
typedef std::pair<EventCallbackFunction*, void*> CallbackFunction;
|
||||
typedef pset<CallbackFunction> CallbackFunctions;
|
||||
typedef pmap<std::string, CallbackFunctions> CallbackHooks;
|
||||
typedef pvector<EventLambda> LambdaFunctions;
|
||||
typedef pmap<std::string, LambdaFunctions> LambdaHooks;
|
||||
typedef pmap<std::string, PT(AsyncFuture)> Futures;
|
||||
|
||||
Hooks _hooks;
|
||||
CallbackHooks _cbhooks;
|
||||
LambdaHooks _lambdahooks;
|
||||
Futures _futures;
|
||||
EventQueue &_queue;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* 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 functionAsyncTask.I
|
||||
* @author rdb
|
||||
* @date 2021-11-29
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE FunctionAsyncTask::
|
||||
FunctionAsyncTask(const std::string &name) :
|
||||
AsyncTask(name)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE FunctionAsyncTask::
|
||||
FunctionAsyncTask(const std::string &name, FunctionAsyncTask::TaskFunction function) :
|
||||
AsyncTask(name),
|
||||
_function(std::move(function))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the function that is called when the task runs.
|
||||
*/
|
||||
INLINE void FunctionAsyncTask::
|
||||
set_function(TaskFunction function) {
|
||||
_function = std::move(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function that is called when the task runs.
|
||||
*/
|
||||
INLINE const FunctionAsyncTask::TaskFunction &FunctionAsyncTask::
|
||||
get_function() const {
|
||||
return _function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the function that is called when the task begins. This is an
|
||||
* optional function.
|
||||
*/
|
||||
INLINE void FunctionAsyncTask::
|
||||
set_upon_birth(BirthFunction upon_birth) {
|
||||
_upon_birth = std::move(upon_birth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function that is called when the task begins, or NULL if the
|
||||
* function is not defined.
|
||||
*/
|
||||
INLINE const FunctionAsyncTask::BirthFunction &FunctionAsyncTask::
|
||||
get_upon_birth() const {
|
||||
return _upon_birth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the function that is called when the task ends. This is an
|
||||
* optional function.
|
||||
*/
|
||||
INLINE void FunctionAsyncTask::
|
||||
set_upon_death(FunctionAsyncTask::DeathFunction upon_death) {
|
||||
_upon_death = upon_death;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function that is called when the task ends, or NULL if the
|
||||
* function is not defined.
|
||||
*/
|
||||
INLINE const FunctionAsyncTask::DeathFunction &FunctionAsyncTask::
|
||||
get_upon_death() const {
|
||||
return _upon_death;
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* 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 functionAsyncTask.cxx
|
||||
* @author rdb
|
||||
* @date 2021-11-29
|
||||
*/
|
||||
|
||||
#include "functionAsyncTask.h"
|
||||
#include "pnotify.h"
|
||||
|
||||
#ifndef CPPPARSER
|
||||
|
||||
TypeHandle FunctionAsyncTask::_type_handle;
|
||||
|
||||
/**
|
||||
* Override this function to return true if the task can be successfully
|
||||
* executed, false if it cannot. Mainly intended as a sanity check when
|
||||
* attempting to add the task to a task manager.
|
||||
*
|
||||
* This function is called with the lock held.
|
||||
*/
|
||||
bool FunctionAsyncTask::
|
||||
is_runnable() {
|
||||
return !!_function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to do something useful for the task.
|
||||
*
|
||||
* This function is called with the lock *not* held.
|
||||
*/
|
||||
AsyncTask::DoneStatus FunctionAsyncTask::
|
||||
do_task() {
|
||||
nassertr(_function, DS_interrupt);
|
||||
return _function(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to do something useful when the task has been added
|
||||
* to the active queue.
|
||||
*
|
||||
* This function is called with the lock *not* held.
|
||||
*/
|
||||
void FunctionAsyncTask::
|
||||
upon_birth(AsyncTaskManager *manager) {
|
||||
AsyncTask::upon_birth(manager);
|
||||
|
||||
if (_upon_birth) {
|
||||
_upon_birth(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to do something useful when the task has been
|
||||
* removed from the active queue. The parameter clean_exit is true if the
|
||||
* task has been removed because it exited normally (returning DS_done), or
|
||||
* false if it was removed for some other reason (e.g.
|
||||
* AsyncTaskManager::remove()). By the time this method is called, _manager
|
||||
* has been cleared, so the parameter manager indicates the original
|
||||
* AsyncTaskManager that owned this task.
|
||||
*
|
||||
* The normal behavior is to throw the done_event only if clean_exit is true.
|
||||
*
|
||||
* This function is called with the lock *not* held.
|
||||
*/
|
||||
void FunctionAsyncTask::
|
||||
upon_death(AsyncTaskManager *manager, bool clean_exit) {
|
||||
AsyncTask::upon_death(manager, clean_exit);
|
||||
|
||||
if (_upon_death) {
|
||||
_upon_death(this, clean_exit);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CPPPARSER
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* 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 functionAsyncTask.h
|
||||
* @author rdb
|
||||
* @date 2021-11-29
|
||||
*/
|
||||
|
||||
#ifndef FUNCTIONASYNCTASK_H
|
||||
#define FUNCTIONASYNCTASK_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
#include "asyncTask.h"
|
||||
|
||||
#ifndef CPPPARSER
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* Associates a generic std::function (eg. a lambda) with an AsyncTask object.
|
||||
* You can use this when you want to create an AsyncTask without having to
|
||||
* subclass.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
class EXPCL_PANDA_EVENT FunctionAsyncTask final : public AsyncTask {
|
||||
public:
|
||||
typedef std::function<DoneStatus (FunctionAsyncTask *task)> TaskFunction;
|
||||
typedef std::function<void (FunctionAsyncTask *task)> BirthFunction;
|
||||
typedef std::function<void (FunctionAsyncTask *task, bool clean_exit)> DeathFunction;
|
||||
|
||||
INLINE FunctionAsyncTask(const std::string &name = std::string());
|
||||
INLINE FunctionAsyncTask(const std::string &name, TaskFunction function);
|
||||
ALLOC_DELETED_CHAIN(FunctionAsyncTask);
|
||||
|
||||
INLINE void set_function(TaskFunction function);
|
||||
INLINE const TaskFunction &get_function() const;
|
||||
|
||||
INLINE void set_upon_birth(BirthFunction function);
|
||||
INLINE const BirthFunction &get_upon_birth() const;
|
||||
|
||||
INLINE void set_upon_death(DeathFunction function);
|
||||
INLINE const DeathFunction &get_upon_death() const;
|
||||
|
||||
protected:
|
||||
virtual bool is_runnable() override;
|
||||
virtual DoneStatus do_task() override;
|
||||
virtual void upon_birth(AsyncTaskManager *manager) override;
|
||||
virtual void upon_death(AsyncTaskManager *manager, bool clean_exit) override;
|
||||
|
||||
private:
|
||||
TaskFunction _function;
|
||||
BirthFunction _upon_birth;
|
||||
DeathFunction _upon_death;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
AsyncTask::init_type();
|
||||
register_type(_type_handle, "FunctionAsyncTask",
|
||||
AsyncTask::get_class_type());
|
||||
}
|
||||
virtual TypeHandle get_type() const override {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#include "functionAsyncTask.I"
|
||||
|
||||
#endif // CPPPARSER
|
||||
|
||||
#endif
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
* Associates a generic C-style function pointer with an AsyncTask object.
|
||||
* You can use this when you want to create an AsyncTask without having to
|
||||
* subclass.
|
||||
*
|
||||
* @deprecated See FunctionAsyncTask instead, which is more powerful.
|
||||
*/
|
||||
class EXPCL_PANDA_EVENT GenericAsyncTask : public AsyncTask {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "asyncTaskSequence.cxx"
|
||||
#include "buttonEvent.cxx"
|
||||
#include "buttonEventList.cxx"
|
||||
#include "functionAsyncTask.cxx"
|
||||
#include "genericAsyncTask.cxx"
|
||||
#include "pointerEvent.cxx"
|
||||
#include "pointerEventList.cxx"
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ mount(const Filename &physical_filename, const Filename &mount_point,
|
|||
// read-write on multifiles and .zip files.
|
||||
flags |= MF_read_only;
|
||||
|
||||
char ch = stream->get();
|
||||
int ch = stream->get();
|
||||
if (ch == '#' || ch == 'p') {
|
||||
// It *might* be a multifile.
|
||||
while (ch == '#') {
|
||||
|
|
|
|||
|
|
@ -274,7 +274,9 @@ start_thread() {
|
|||
|
||||
// Create and start the thread object.
|
||||
_thread_status = TS_wait;
|
||||
_thread = new GenericThread(_filename.get_basename(), _sync_name, st_thread_main, this);
|
||||
_thread = new GenericThread(_filename.get_basename(), _sync_name, [this]{
|
||||
thread_main();
|
||||
});
|
||||
if (!_thread->start(_thread_priority, true)) {
|
||||
// Couldn't start the thread.
|
||||
_thread = nullptr;
|
||||
|
|
@ -668,14 +670,6 @@ cleanup() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread main function, static version (for passing to GenericThread).
|
||||
*/
|
||||
void FfmpegVideoCursor::
|
||||
st_thread_main(void *self) {
|
||||
((FfmpegVideoCursor *)self)->thread_main();
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread main function.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -134,7 +134,6 @@ private:
|
|||
|
||||
private:
|
||||
// The following functions will be called in the sub-thread.
|
||||
static void st_thread_main(void *self);
|
||||
void thread_main();
|
||||
bool do_poll();
|
||||
|
||||
|
|
|
|||
|
|
@ -310,6 +310,39 @@ define_key(const string &event_name, const string &description,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a handler for the indicated key. When the key is pressed in a
|
||||
* window, the given callback will be called. The description is a one-line
|
||||
* description of the function of the key, for display to the user.
|
||||
*/
|
||||
void PandaFramework::
|
||||
define_key(const string &event_name, const string &description,
|
||||
EventHandler::EventLambda function) {
|
||||
if (_event_handler.has_hook(event_name)) {
|
||||
// If there is already a hook for the indicated keyname, we're most likely
|
||||
// replacing a previous definition of a key. Search for the old
|
||||
// definition and remove it.
|
||||
KeyDefinitions::iterator di;
|
||||
di = _key_definitions.begin();
|
||||
while (di != _key_definitions.end() && (*di)._event_name != event_name) {
|
||||
++di;
|
||||
}
|
||||
if (di != _key_definitions.end()) {
|
||||
_key_definitions.erase(di);
|
||||
}
|
||||
}
|
||||
|
||||
// Now add a new hook for the keyname, and also add the new description.
|
||||
_event_handler.add_hook(event_name, function);
|
||||
|
||||
if (!description.empty()) {
|
||||
KeyDefinition keydef;
|
||||
keydef._event_name = event_name;
|
||||
keydef._description = description;
|
||||
_key_definitions.push_back(keydef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in the indicated window properties structure according to the normal
|
||||
* window properties for this application.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ public:
|
|||
const std::string &description,
|
||||
EventHandler::EventCallbackFunction *function,
|
||||
void *data);
|
||||
void define_key(const std::string &event_name,
|
||||
const std::string &description,
|
||||
EventHandler::EventLambda function);
|
||||
|
||||
INLINE void set_window_title(const std::string &title);
|
||||
virtual void get_default_window_props(WindowProperties &props);
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ load_default_model(const NodePath &parent) {
|
|||
GeomNode *geomnode = new GeomNode("tri");
|
||||
|
||||
PT(GeomVertexData) vdata = new GeomVertexData
|
||||
("tri", vertex_colors_prefer_packed ? GeomVertexFormat::get_v3n3ct2() : GeomVertexFormat::get_v3n3ct2(),
|
||||
("tri", GeomVertexFormat::get_v3n3t2(),
|
||||
Geom::UH_static);
|
||||
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
|
||||
GeomVertexWriter normal(vdata, InternalName::get_normal());
|
||||
|
|
@ -1429,8 +1429,11 @@ create_anim_controls() {
|
|||
setup_shuttle_button("4", 2, st_play_button);
|
||||
setup_shuttle_button(":", 3, st_forward_button);
|
||||
|
||||
_update_anim_controls_task = new GenericAsyncTask("controls", st_update_anim_controls, (void *)this);
|
||||
_panda_framework->get_task_mgr().add(_update_anim_controls_task);
|
||||
AsyncTaskManager &task_mgr = _panda_framework->get_task_mgr();
|
||||
_update_anim_controls_task = task_mgr.add("controls", [this](AsyncTask *task) {
|
||||
update_anim_controls();
|
||||
return AsyncTask::DS_cont;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1443,7 +1446,7 @@ destroy_anim_controls() {
|
|||
|
||||
_panda_framework->get_event_handler().remove_hooks_with((void *)this);
|
||||
if (_update_anim_controls_task != nullptr) {
|
||||
_panda_framework->get_task_mgr().remove(_update_anim_controls_task);
|
||||
_update_anim_controls_task->remove();
|
||||
_update_anim_controls_task.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -1559,18 +1562,6 @@ forward_button() {
|
|||
control->pose(control->get_frame() + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The static task function.
|
||||
*/
|
||||
AsyncTask::DoneStatus WindowFramework::
|
||||
st_update_anim_controls(GenericAsyncTask *, void *data) {
|
||||
WindowFramework *self = (WindowFramework *)data;
|
||||
self->update_anim_controls();
|
||||
return AsyncTask::DS_cont;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The static event handler function.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -154,8 +154,6 @@ private:
|
|||
void play_button();
|
||||
void forward_button();
|
||||
|
||||
static AsyncTask::DoneStatus st_update_anim_controls(GenericAsyncTask *task, void *data);
|
||||
|
||||
static void st_back_button(const Event *, void *data);
|
||||
static void st_pause_button(const Event *, void *data);
|
||||
static void st_play_button(const Event *, void *data);
|
||||
|
|
@ -183,7 +181,7 @@ private:
|
|||
PT(PGSliderBar) _anim_slider;
|
||||
PT(PGSliderBar) _play_rate_slider;
|
||||
PT(TextNode) _frame_number;
|
||||
PT(GenericAsyncTask) _update_anim_controls_task;
|
||||
PT(AsyncTask) _update_anim_controls_task;
|
||||
|
||||
NodePath _mouse;
|
||||
NodePath _button_thrower;
|
||||
|
|
|
|||
|
|
@ -11,14 +11,18 @@
|
|||
* @date 2006-01-15
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE int CLP(GraphicsBuffer)::
|
||||
get_multisample_count()
|
||||
{
|
||||
return _requested_multisamples;
|
||||
get_multisample_count() {
|
||||
return _requested_multisamples;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE int CLP(GraphicsBuffer)::
|
||||
get_coverage_sample_count()
|
||||
{
|
||||
return _requested_coverage_samples;
|
||||
get_coverage_sample_count() {
|
||||
return _requested_coverage_samples;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@
|
|||
* ARB_draw_buffers extensions. This design has significant advantages over
|
||||
* the older wglGraphicsBuffer and glxGraphicsBuffer:
|
||||
*
|
||||
* * Can export depth and stencil. * Supports auxiliary bitplanes. *
|
||||
* Supports non-power-of-two padding. * Supports tracking of host window
|
||||
* size. * Supports cumulative render-to-texture. * Faster than pbuffers. *
|
||||
* Can render onto a texture without clearing it first. * Supports
|
||||
* multisample antialiased rendering.
|
||||
* - Can export depth and stencil.
|
||||
* - Supports auxiliary bitplanes.
|
||||
* - Supports non-power-of-two padding.
|
||||
* - Supports tracking of host window size.
|
||||
* - Supports cumulative render-to-texture.
|
||||
* - Faster than pbuffers.
|
||||
* - Can render onto a texture without clearing it first.
|
||||
* - Supports multisample antialiased rendering.
|
||||
*
|
||||
* Some of these deserve a little explanation. Auxiliary bitplanes are
|
||||
* additional bitplanes above and beyond the normal depth,stencil,color. One
|
||||
|
|
@ -45,7 +48,6 @@
|
|||
* EXT_framebuffer_blit to allow for multisample antialiasing these offscreen
|
||||
* render targets. If these extensions are unavailable the buffer will render
|
||||
* as if multisamples is 0.
|
||||
*
|
||||
*/
|
||||
class EXPCL_GL CLP(GraphicsBuffer) : public GraphicsBuffer {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -7420,6 +7420,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
|
|||
}
|
||||
|
||||
gtc->_has_storage = true;
|
||||
gtc->_simple_loaded = false;
|
||||
gtc->_uses_mipmaps = uses_mipmaps;
|
||||
gtc->_internal_format = internal_format;
|
||||
gtc->_width = width;
|
||||
|
|
@ -13135,7 +13136,7 @@ apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc
|
|||
}
|
||||
}
|
||||
|
||||
if (sampler.uses_mipmaps() && !gtc->_uses_mipmaps && !gl_ignore_mipmaps) {
|
||||
if (sampler.uses_mipmaps() && !gtc->_uses_mipmaps && !gtc->_simple_loaded && !gl_ignore_mipmaps) {
|
||||
// The texture wasn't created with mipmaps, but we are trying to sample it
|
||||
// with mipmaps. We will need to reload it.
|
||||
GLCAT.info()
|
||||
|
|
@ -13178,10 +13179,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
|
|||
async_reload_texture(gtc);
|
||||
has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
|
||||
if (!has_image) {
|
||||
if (gtc->was_simple_image_modified()) {
|
||||
return upload_simple_texture(gtc);
|
||||
}
|
||||
return true;
|
||||
return upload_simple_texture(gtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13551,6 +13549,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
|
|||
}
|
||||
|
||||
gtc->_has_storage = true;
|
||||
gtc->_simple_loaded = false;
|
||||
gtc->_immutable = true;
|
||||
gtc->_uses_mipmaps = uses_mipmaps;
|
||||
gtc->_internal_format = internal_format;
|
||||
|
|
@ -13598,6 +13597,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
|
|||
if (success) {
|
||||
if (needs_reload) {
|
||||
gtc->_has_storage = true;
|
||||
gtc->_simple_loaded = false;
|
||||
gtc->_uses_mipmaps = uses_mipmaps;
|
||||
gtc->_internal_format = internal_format;
|
||||
gtc->_width = width;
|
||||
|
|
@ -14190,7 +14190,8 @@ upload_simple_texture(CLP(TextureContext) *gtc) {
|
|||
#endif
|
||||
GLenum external_format = GL_BGRA;
|
||||
|
||||
const unsigned char *image_ptr = tex->get_simple_ram_image();
|
||||
CPTA_uchar image = tex->get_simple_ram_image();
|
||||
const unsigned char *image_ptr = image.p();
|
||||
if (image_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -14233,7 +14234,17 @@ upload_simple_texture(CLP(TextureContext) *gtc) {
|
|||
width, height, 0,
|
||||
external_format, component_type, image_ptr);
|
||||
|
||||
gtc->mark_simple_loaded();
|
||||
gtc->_has_storage = true;
|
||||
gtc->_simple_loaded = true;
|
||||
gtc->_immutable = false;
|
||||
gtc->_uses_mipmaps = false;
|
||||
gtc->_internal_format = internal_format;
|
||||
gtc->_width = width;
|
||||
gtc->_height = height;
|
||||
gtc->_depth = 1;
|
||||
gtc->update_data_size_bytes(width * height * 4);
|
||||
|
||||
gtc->mark_loaded();
|
||||
|
||||
report_my_gl_errors();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -722,7 +722,6 @@ protected:
|
|||
typedef pmap<NodePath, DirectionalLightFrameData> DirectionalLights;
|
||||
DirectionalLights _dlights;
|
||||
|
||||
int _pass_number;
|
||||
GLuint _geom_display_list;
|
||||
GLuint _current_vbuffer_index;
|
||||
GLuint _current_ibuffer_index;
|
||||
|
|
|
|||
|
|
@ -2911,10 +2911,13 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|||
|
||||
// Bindless texturing wasn't supported or didn't work, so let's just bind
|
||||
// the texture normally.
|
||||
// Note that simple RAM images are always 2-D for now, so to avoid errors,
|
||||
// we must load the real texture if this is not for a sampler2D.
|
||||
bool force = (spec._desired_type != Texture::TT_2d_texture);
|
||||
#ifndef OPENGLES
|
||||
if (multi_bind) {
|
||||
// Multi-bind case.
|
||||
if (!_glgsg->update_texture(gtc, false)) {
|
||||
if (!_glgsg->update_texture(gtc, force)) {
|
||||
textures[i] = 0;
|
||||
} else {
|
||||
gtc->set_active(true);
|
||||
|
|
@ -2934,7 +2937,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|||
{
|
||||
// Non-multibind case.
|
||||
_glgsg->set_active_texture_stage(i);
|
||||
if (!_glgsg->update_texture(gtc, false)) {
|
||||
if (!_glgsg->update_texture(gtc, force)) {
|
||||
continue;
|
||||
}
|
||||
_glgsg->apply_texture(gtc);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg,
|
|||
_handle_resident = false;
|
||||
#endif
|
||||
_has_storage = false;
|
||||
_simple_loaded = false;
|
||||
_immutable = false;
|
||||
_uses_mipmaps = false;
|
||||
_generate_mipmaps = false;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ reset_data() {
|
|||
_handle_resident = false;
|
||||
#endif
|
||||
_has_storage = false;
|
||||
_simple_loaded = false;
|
||||
_immutable = false;
|
||||
|
||||
#ifndef OPENGLES_1
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public:
|
|||
// or glTexStorage2D() call. If none of these have changed, we can reload
|
||||
// the texture image with a glTexSubImage2D().
|
||||
bool _has_storage;
|
||||
bool _simple_loaded;
|
||||
bool _immutable;
|
||||
bool _uses_mipmaps;
|
||||
bool _generate_mipmaps;
|
||||
|
|
|
|||
|
|
@ -366,6 +366,23 @@ ConfigVariableDouble simple_image_threshold
|
|||
"simple images. Generally the value should be considerably "
|
||||
"less than 1."));
|
||||
|
||||
ConfigVariableInt texture_reload_num_threads
|
||||
("texture-reload-num-threads", 1,
|
||||
PRC_DESC("The number of threads that will be started by the Texture class "
|
||||
"to reload textures asynchronously. These threads will only be "
|
||||
"started if the asynchronous interface is used, and if threading "
|
||||
"support is compiled into Panda. The default is one thread, "
|
||||
"which allows textures to be loaded one at a time in a single "
|
||||
"asychronous thread. You can set this higher, particularly if "
|
||||
"you have many CPU's available, to allow loading multiple models "
|
||||
"simultaneously."));
|
||||
|
||||
ConfigVariableEnum<ThreadPriority> texture_reload_thread_priority
|
||||
("texture-reload-thread-priority", TP_normal,
|
||||
PRC_DESC("The default thread priority to assign to the threads created for "
|
||||
"asynchronous texture loading. The default is 'normal'; you may "
|
||||
"also specify 'low', 'high', or 'urgent'."));
|
||||
|
||||
ConfigVariableInt geom_cache_size
|
||||
("geom-cache-size", 5000,
|
||||
PRC_DESC("Specifies the maximum number of entries in the cache "
|
||||
|
|
@ -503,7 +520,7 @@ PRC_DESC("If this is nonzero, it represents an artificial delay, "
|
|||
"in seconds, that is imposed on every asynchronous load attempt "
|
||||
"(within the thread). Its purpose is to help debug errors that "
|
||||
"may occur when an asynchronous load is delayed. The "
|
||||
"delay is per-model, and all aync loads will be queued "
|
||||
"delay is per-model, and all async loads will be queued "
|
||||
"up behind the delay--it is as if the time it takes to read a "
|
||||
"file is increased by this amount per read."));
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "configVariableString.h"
|
||||
#include "configVariableList.h"
|
||||
#include "autoTextureScale.h"
|
||||
#include "threadPriority.h"
|
||||
|
||||
NotifyCategoryDecl(gobj, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
|
||||
NotifyCategoryDecl(shader, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
|
||||
|
|
@ -62,6 +63,8 @@ extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_auto_power_2;
|
|||
extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt simple_image_size;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableDouble simple_image_threshold;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt texture_reload_num_threads;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableEnum<ThreadPriority> texture_reload_thread_priority;
|
||||
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_size;
|
||||
extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_min_frames;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ make_copy() const {
|
|||
CDWriter cdata_tex(tex->_cycler, true);
|
||||
cdata_tex->inc_properties_modified();
|
||||
cdata_tex->inc_image_modified();
|
||||
cdata_tex->inc_simple_image_modified();
|
||||
return tex;
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +231,6 @@ clear_image() {
|
|||
do_clear_ram_image(cdata);
|
||||
do_clear_simple_ram_image(cdata);
|
||||
cdata->inc_image_modified();
|
||||
cdata->inc_simple_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1766,6 +1764,12 @@ INLINE void Texture::
|
|||
set_simple_ram_image(CPTA_uchar image, int x_size, int y_size) {
|
||||
CDWriter cdata(_cycler, true);
|
||||
do_set_simple_ram_image(cdata, image, x_size, y_size);
|
||||
|
||||
// If we don't have a RAM image currently, we need to update this, to let the
|
||||
// GSG know that it needs to update the simple image it is currently using.
|
||||
if (!do_has_ram_image(cdata)) {
|
||||
cdata->inc_image_modified();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1797,16 +1801,6 @@ get_image_modified() const {
|
|||
return cdata->_image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence number which is guaranteed to change at least every time
|
||||
* the texture's "simple" image data is modified.
|
||||
*/
|
||||
INLINE UpdateSeq Texture::
|
||||
get_simple_image_modified() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_simple_image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the power-of-2 texture-scaling mode that will be applied to this
|
||||
* particular texture when it is next loaded from disk. See
|
||||
|
|
@ -2466,14 +2460,6 @@ inc_image_modified() {
|
|||
_modified_pages[0]._modified = _image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE void Texture::CData::
|
||||
inc_simple_image_modified() {
|
||||
++_simple_image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "pnmImage.h"
|
||||
#include "pnmReader.h"
|
||||
#include "pfmFile.h"
|
||||
#include "pnmFileTypeRegistry.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "datagramInputFile.h"
|
||||
#include "datagramOutputFile.h"
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include "streamReader.h"
|
||||
#include "texturePeeker.h"
|
||||
#include "convert_srgb.h"
|
||||
#include "asyncTaskManager.h"
|
||||
|
||||
#ifdef HAVE_SQUISH
|
||||
#include <squish.h>
|
||||
|
|
@ -1018,6 +1020,69 @@ load_related(const InternalName *suffix) const {
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a background task that reloads the the Texture from its disk file
|
||||
* if there is not currently a RAM image (or uncompressed RAM image, if
|
||||
* allow_compression is false).
|
||||
*
|
||||
* A higher priority value indicates that this texture should be reloaded sooner
|
||||
* than textures with a lower priority value. If the reload hasn't taken place
|
||||
* yet, you can call this again to update the priority value.
|
||||
*
|
||||
* If someone else reloads the texture using an explicit call to reload() while
|
||||
* an async reload request is pending, the async reload request is cancelled.
|
||||
*/
|
||||
PT(AsyncFuture) Texture::
|
||||
async_ensure_ram_image(bool allow_compression, int priority) {
|
||||
CDLockedReader cdata(_cycler);
|
||||
if (allow_compression ? do_has_ram_image(cdata) : do_has_uncompressed_ram_image(cdata)) {
|
||||
// We already have a RAM image.
|
||||
PT(AsyncFuture) fut = new AsyncFuture;
|
||||
fut->set_result(nullptr);
|
||||
return fut;
|
||||
}
|
||||
if (!do_can_reload(cdata)) {
|
||||
// We don't have a filename to load from. This is an error.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AsyncTask *task = cdata->_reload_task;
|
||||
if (task != nullptr) {
|
||||
// This texture is already queued to be reloaded. Don't queue it again,
|
||||
// just make sure the priority is updated, and return.
|
||||
task->set_priority(std::max(task->get_priority(), priority));
|
||||
return (AsyncFuture *)task;
|
||||
}
|
||||
|
||||
CDWriter cdataw(_cycler, cdata, true);
|
||||
|
||||
string task_name = string("reload:") + get_name();
|
||||
AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
|
||||
static PT(AsyncTaskChain) chain = task_mgr->make_task_chain("texture_reload");
|
||||
chain->set_num_threads(texture_reload_num_threads);
|
||||
chain->set_thread_priority(texture_reload_thread_priority);
|
||||
|
||||
double delay = async_load_delay;
|
||||
|
||||
// This texture has not yet been queued to be reloaded. Queue it up now.
|
||||
task = new FunctionAsyncTask(task_name, [=](AsyncTask *task) {
|
||||
if (delay != 0.0) {
|
||||
Thread::sleep(delay);
|
||||
}
|
||||
if (allow_compression) {
|
||||
get_ram_image();
|
||||
} else {
|
||||
get_uncompressed_ram_image();
|
||||
}
|
||||
return AsyncTask::DS_done;
|
||||
});
|
||||
task->set_task_chain("texture_reload");
|
||||
task->set_priority(priority);
|
||||
task_mgr->add(task);
|
||||
cdataw->_reload_task = task;
|
||||
return (AsyncFuture *)task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current system-RAM image with the new data, converting it
|
||||
* first if necessary from the indicated component-order format. See
|
||||
|
|
@ -1313,7 +1378,12 @@ new_simple_ram_image(int x_size, int y_size) {
|
|||
cdata->_simple_ram_image._image = PTA_uchar::empty_array(expected_page_size);
|
||||
cdata->_simple_ram_image._page_size = expected_page_size;
|
||||
cdata->_simple_image_date_generated = (int32_t)time(nullptr);
|
||||
cdata->inc_simple_image_modified();
|
||||
|
||||
// If we don't have a RAM image currently, we need to update this, to let the
|
||||
// GSG know that it needs to update the simple image it is currently using.
|
||||
if (!do_has_ram_image(cdata)) {
|
||||
cdata->inc_image_modified();
|
||||
}
|
||||
|
||||
return cdata->_simple_ram_image._image;
|
||||
}
|
||||
|
|
@ -5212,11 +5282,19 @@ do_write_one(CData *cdata, const Filename &fullpath, int z, int n) {
|
|||
success = pfm.write(fullpath);
|
||||
} else {
|
||||
// Writing a normal, integer texture.
|
||||
PNMFileType *type =
|
||||
PNMFileTypeRegistry::get_global_ptr()->get_type_from_extension(fullpath);
|
||||
if (type == nullptr) {
|
||||
gobj_cat.error()
|
||||
<< "Texture::write() - couldn't determine type from extension: " << fullpath << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
PNMImage pnmimage;
|
||||
if (!do_store_one(cdata, pnmimage, z, n)) {
|
||||
return false;
|
||||
}
|
||||
success = pnmimage.write(fullpath);
|
||||
success = pnmimage.write(fullpath, type);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
|
|
@ -5452,7 +5530,6 @@ unlocked_ensure_ram_image(bool allow_compression) {
|
|||
cdataw->_component_type = cdata_tex->_component_type;
|
||||
|
||||
cdataw->inc_properties_modified();
|
||||
cdataw->inc_image_modified();
|
||||
}
|
||||
|
||||
cdataw->_keep_ram_image = cdata_tex->_keep_ram_image;
|
||||
|
|
@ -5462,9 +5539,7 @@ unlocked_ensure_ram_image(bool allow_compression) {
|
|||
nassertr(_reloading, nullptr);
|
||||
_reloading = false;
|
||||
|
||||
// We don't generally increment the cdata->_image_modified semaphore,
|
||||
// because this is just a reload, and presumably the image hasn't changed
|
||||
// (unless we hit the if condition above).
|
||||
cdataw->inc_image_modified();
|
||||
|
||||
_cvar.notify_all();
|
||||
|
||||
|
|
@ -5602,6 +5677,12 @@ do_reload_ram_image(CData *cdata, bool allow_compression) {
|
|||
cache->store(record);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any pending asynchronous reload operation.
|
||||
if (cdata->_reload_task != nullptr) {
|
||||
cdata->_reload_task->remove();
|
||||
cdata->_reload_task = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -6928,7 +7009,6 @@ do_clear(CData *cdata) {
|
|||
|
||||
cdata->inc_properties_modified();
|
||||
cdata->inc_image_modified();
|
||||
cdata->inc_simple_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -7617,7 +7697,6 @@ do_set_simple_ram_image(CData *cdata, CPTA_uchar image, int x_size, int y_size)
|
|||
cdata->_simple_ram_image._image = image.cast_non_const();
|
||||
cdata->_simple_ram_image._page_size = image.size();
|
||||
cdata->_simple_image_date_generated = (int32_t)time(nullptr);
|
||||
cdata->inc_simple_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -7712,11 +7791,6 @@ do_clear_simple_ram_image(CData *cdata) {
|
|||
cdata->_simple_ram_image._image.clear();
|
||||
cdata->_simple_ram_image._page_size = 0;
|
||||
cdata->_simple_image_date_generated = 0;
|
||||
|
||||
// We allow this exception: we update the _simple_image_modified here, since
|
||||
// no one really cares much about that anyway, and it's convenient to do it
|
||||
// here.
|
||||
cdata->inc_simple_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -10342,10 +10416,12 @@ make_this_from_bam(const FactoryParams ¶ms) {
|
|||
// object to read all of the attributes from the bam stream.
|
||||
Texture *dummy = this;
|
||||
AutoTextureScale auto_texture_scale = ATS_unspecified;
|
||||
bool has_simple_ram_image = false;
|
||||
{
|
||||
CDWriter cdata_dummy(dummy->_cycler, true);
|
||||
dummy->do_fillin_body(cdata_dummy, scan, manager);
|
||||
auto_texture_scale = cdata_dummy->_auto_texture_scale;
|
||||
has_simple_ram_image = !cdata_dummy->_simple_ram_image._image.empty();
|
||||
}
|
||||
|
||||
if (filename.empty()) {
|
||||
|
|
@ -10378,6 +10454,61 @@ make_this_from_bam(const FactoryParams ¶ms) {
|
|||
case TT_1d_texture:
|
||||
case TT_2d_texture:
|
||||
case TT_1d_texture_array:
|
||||
// If we don't want to preload textures, and we already have a simple
|
||||
// RAM image (or don't need one), we don't need to load it from disk.
|
||||
// We do check for it in the texture pool first, though, in case it has
|
||||
// already been loaded.
|
||||
if ((options.get_texture_flags() & LoaderOptions::TF_preload) == 0 &&
|
||||
(has_simple_ram_image || (options.get_texture_flags() & LoaderOptions::TF_preload_simple) == 0)) {
|
||||
if (alpha_filename.empty()) {
|
||||
me = TexturePool::get_texture(filename, primary_file_num_channels,
|
||||
has_read_mipmaps);
|
||||
} else {
|
||||
me = TexturePool::get_texture(filename, alpha_filename,
|
||||
primary_file_num_channels,
|
||||
alpha_file_channel,
|
||||
has_read_mipmaps);
|
||||
}
|
||||
if (me != nullptr && me->get_texture_type() == texture_type) {
|
||||
// We can use this.
|
||||
break;
|
||||
}
|
||||
|
||||
// We don't have a texture, but we didn't need to preload it, so we
|
||||
// can just use this one. We just need to know where we can find it
|
||||
// when we do need to reload it.
|
||||
Filename fullpath = filename;
|
||||
Filename alpha_fullpath = alpha_filename;
|
||||
const DSearchPath &model_path = get_model_path();
|
||||
if (vfs->resolve_filename(fullpath, model_path) &&
|
||||
(alpha_fullpath.empty() || vfs->resolve_filename(alpha_fullpath, model_path))) {
|
||||
me = dummy;
|
||||
me->set_name(name);
|
||||
|
||||
{
|
||||
CDWriter cdata_me(me->_cycler, true);
|
||||
cdata_me->_filename = filename;
|
||||
cdata_me->_alpha_filename = alpha_filename;
|
||||
cdata_me->_fullpath = fullpath;
|
||||
cdata_me->_alpha_fullpath = alpha_fullpath;
|
||||
cdata_me->_primary_file_num_channels = primary_file_num_channels;
|
||||
cdata_me->_alpha_file_channel = alpha_file_channel;
|
||||
cdata_me->_texture_type = texture_type;
|
||||
cdata_me->_loaded_from_image = true;
|
||||
cdata_me->_has_read_mipmaps = has_read_mipmaps;
|
||||
}
|
||||
|
||||
// To manage the reference count, explicitly ref it now, then unref
|
||||
// it in the finalize callback.
|
||||
me->ref();
|
||||
manager->register_finalize(me);
|
||||
|
||||
// Do add it to the cache now, so that future uses of this same
|
||||
// texture are unified.
|
||||
TexturePool::add_texture(me);
|
||||
return me;
|
||||
}
|
||||
}
|
||||
if (alpha_filename.empty()) {
|
||||
me = TexturePool::load_texture(filename, primary_file_num_channels,
|
||||
has_read_mipmaps, options);
|
||||
|
|
@ -10474,7 +10605,6 @@ do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
|
|||
|
||||
cdata->_simple_ram_image._image = image;
|
||||
cdata->_simple_ram_image._page_size = u_size;
|
||||
cdata->inc_simple_image_modified();
|
||||
}
|
||||
|
||||
if (manager->get_file_minor_ver() >= 45) {
|
||||
|
|
@ -10672,7 +10802,6 @@ CData(const Texture::CData ©) {
|
|||
|
||||
_properties_modified = copy._properties_modified;
|
||||
_image_modified = copy._image_modified;
|
||||
_simple_image_modified = copy._simple_image_modified;
|
||||
_modified_pages = copy._modified_pages;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
#include "bamCacheRecord.h"
|
||||
#include "pnmImage.h"
|
||||
#include "pfmFile.h"
|
||||
#include "asyncFuture.h"
|
||||
#include "asyncTask.h"
|
||||
|
||||
class TextureContext;
|
||||
class FactoryParams;
|
||||
|
|
@ -448,6 +448,7 @@ PUBLISHED:
|
|||
MAKE_PROPERTY(expected_ram_image_size, get_expected_ram_image_size);
|
||||
MAKE_PROPERTY(expected_ram_page_size, get_expected_ram_page_size);
|
||||
|
||||
PT(AsyncFuture) async_ensure_ram_image(bool allow_compression = true, int priority = 0);
|
||||
INLINE CPTA_uchar get_ram_image();
|
||||
INLINE CompressionMode get_ram_image_compression() const;
|
||||
INLINE CPTA_uchar get_uncompressed_ram_image();
|
||||
|
|
@ -520,10 +521,8 @@ PUBLISHED:
|
|||
|
||||
INLINE UpdateSeq get_properties_modified() const;
|
||||
INLINE UpdateSeq get_image_modified() const;
|
||||
INLINE UpdateSeq get_simple_image_modified() const;
|
||||
MAKE_PROPERTY(properties_modified, get_properties_modified);
|
||||
MAKE_PROPERTY(image_modified, get_image_modified);
|
||||
MAKE_PROPERTY(simple_image_modified, get_simple_image_modified);
|
||||
|
||||
SparseArray get_image_modified_pages(UpdateSeq since, int n = 0) const;
|
||||
|
||||
|
|
@ -784,6 +783,7 @@ protected:
|
|||
void do_set_pad_size(CData *cdata, int x, int y, int z);
|
||||
virtual bool do_can_reload(const CData *cdata) const;
|
||||
bool do_reload(CData *cdata);
|
||||
AsyncFuture *do_async_ensure_ram_image(const CData *cdata, bool allow_compression, int priority);
|
||||
|
||||
INLINE AutoTextureScale do_get_auto_texture_scale(const CData *cdata) const;
|
||||
|
||||
|
|
@ -959,7 +959,6 @@ protected:
|
|||
INLINE void inc_properties_modified();
|
||||
INLINE void inc_image_modified();
|
||||
void inc_image_page_modified(int z);
|
||||
INLINE void inc_simple_image_modified();
|
||||
|
||||
Filename _filename;
|
||||
Filename _alpha_filename;
|
||||
|
|
@ -1028,10 +1027,11 @@ protected:
|
|||
|
||||
UpdateSeq _properties_modified;
|
||||
UpdateSeq _image_modified;
|
||||
UpdateSeq _simple_image_modified;
|
||||
|
||||
ModifiedPageRanges _modified_pages;
|
||||
|
||||
PT(AsyncTask) _reload_task;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
|
|
|
|||
|
|
@ -67,15 +67,6 @@ was_image_modified() const {
|
|||
return _image_modified != get_texture()->get_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the texture's "simple" image has been modified since the
|
||||
* last time mark_simple_loaded() was called.
|
||||
*/
|
||||
INLINE bool TextureContext::
|
||||
was_simple_image_modified() const {
|
||||
return _simple_image_modified != get_texture()->get_simple_image_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given page of the texture image has been modified since
|
||||
* the last time mark_loaded() was called.
|
||||
|
|
@ -103,15 +94,6 @@ get_image_modified() const {
|
|||
return _image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence number which is guaranteed to change at least every time
|
||||
* the texture's "simple" image data is modified.
|
||||
*/
|
||||
INLINE UpdateSeq TextureContext::
|
||||
get_simple_image_modified() const {
|
||||
return _simple_image_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SparseArray indicating which pages of the texture have been
|
||||
* modified since the last call to mark_loaded().
|
||||
|
|
@ -146,20 +128,6 @@ mark_loaded() {
|
|||
set_resident(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called after the texture's "simple" image has been loaded into
|
||||
* graphics memory.
|
||||
*/
|
||||
INLINE void TextureContext::
|
||||
mark_simple_loaded() {
|
||||
_properties_modified = get_texture()->get_properties_modified();
|
||||
_simple_image_modified = get_texture()->get_simple_image_modified();
|
||||
update_modified(std::max(_properties_modified, _simple_image_modified));
|
||||
|
||||
// The texture's not exactly resident now, but some part of it is.
|
||||
set_resident(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called after the texture has been forced out of texture memory.
|
||||
*/
|
||||
|
|
@ -167,7 +135,6 @@ INLINE void TextureContext::
|
|||
mark_unloaded() {
|
||||
_properties_modified = UpdateSeq::old();
|
||||
_image_modified = UpdateSeq::old();
|
||||
_simple_image_modified = UpdateSeq::old();
|
||||
update_modified(UpdateSeq::old());
|
||||
|
||||
set_resident(false);
|
||||
|
|
|
|||
|
|
@ -43,19 +43,16 @@ PUBLISHED:
|
|||
INLINE bool was_modified() const;
|
||||
INLINE bool was_properties_modified() const;
|
||||
INLINE bool was_image_modified() const;
|
||||
INLINE bool was_simple_image_modified() const;
|
||||
INLINE bool was_image_page_modified(int z, int n) const;
|
||||
|
||||
INLINE UpdateSeq get_properties_modified() const;
|
||||
INLINE UpdateSeq get_image_modified() const;
|
||||
INLINE UpdateSeq get_simple_image_modified() const;
|
||||
|
||||
INLINE SparseArray get_image_modified_pages(int n = 0) const;
|
||||
|
||||
public:
|
||||
INLINE void update_data_size_bytes(size_t new_data_size_bytes);
|
||||
INLINE void mark_loaded();
|
||||
INLINE void mark_simple_loaded();
|
||||
INLINE void mark_unloaded();
|
||||
INLINE void mark_needs_reload();
|
||||
|
||||
|
|
@ -66,7 +63,6 @@ private:
|
|||
int _view;
|
||||
UpdateSeq _properties_modified;
|
||||
UpdateSeq _image_modified;
|
||||
UpdateSeq _simple_image_modified;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ private:
|
|||
int _y_size;
|
||||
int _z_size;
|
||||
int _is_cube;
|
||||
int _unused1;
|
||||
int _pixel_width;
|
||||
Texture::Format _format;
|
||||
Texture::ComponentType _component_type;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,29 @@ verify_texture(const Filename &filename) {
|
|||
return load_texture(filename) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the texture that has already been previously loaded, or NULL
|
||||
* otherwise.
|
||||
*/
|
||||
INLINE Texture *TexturePool::
|
||||
get_texture(const Filename &filename, int primary_file_num_channels,
|
||||
bool read_mipmaps) {
|
||||
return get_global_ptr()->ns_get_texture(filename, primary_file_num_channels,
|
||||
read_mipmaps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the texture that has already been previously loaded, or NULL
|
||||
* otherwise.
|
||||
*/
|
||||
INLINE Texture *TexturePool::
|
||||
get_texture(const Filename &filename, const Filename &alpha_filename,
|
||||
int primary_file_num_channels, int alpha_file_channel,
|
||||
bool read_mipmaps) {
|
||||
return get_global_ptr()->ns_get_texture(filename, primary_file_num_channels,
|
||||
read_mipmaps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given filename up into a texture, if it has not already been
|
||||
* loaded, and returns the new texture. If a texture with the same filename
|
||||
|
|
|
|||
|
|
@ -273,6 +273,62 @@ ns_has_texture(const Filename &orig_filename) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonstatic implementation of get_texture().
|
||||
*/
|
||||
Texture *TexturePool::
|
||||
ns_get_texture(const Filename &orig_filename, int primary_file_num_channels,
|
||||
bool read_mipmaps) {
|
||||
LookupKey key;
|
||||
key._primary_file_num_channels = primary_file_num_channels;
|
||||
{
|
||||
MutexHolder holder(_lock);
|
||||
resolve_filename(key._fullpath, orig_filename, read_mipmaps, LoaderOptions());
|
||||
|
||||
Textures::const_iterator ti;
|
||||
ti = _textures.find(key);
|
||||
if (ti != _textures.end()) {
|
||||
// This texture was previously loaded.
|
||||
Texture *tex = (*ti).second;
|
||||
nassertr(!tex->get_fullpath().empty(), tex);
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonstatic implementation of get_texture().
|
||||
*/
|
||||
Texture *TexturePool::
|
||||
ns_get_texture(const Filename &orig_filename,
|
||||
const Filename &orig_alpha_filename,
|
||||
int primary_file_num_channels,
|
||||
int alpha_file_channel,
|
||||
bool read_mipmaps) {
|
||||
LookupKey key;
|
||||
key._primary_file_num_channels = primary_file_num_channels;
|
||||
key._alpha_file_channel = alpha_file_channel;
|
||||
{
|
||||
MutexHolder holder(_lock);
|
||||
LoaderOptions options;
|
||||
resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
|
||||
resolve_filename(key._alpha_fullpath, orig_alpha_filename, read_mipmaps, options);
|
||||
|
||||
Textures::const_iterator ti;
|
||||
ti = _textures.find(key);
|
||||
if (ti != _textures.end()) {
|
||||
// This texture was previously loaded.
|
||||
Texture *tex = (*ti).second;
|
||||
nassertr(!tex->get_fullpath().empty(), tex);
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonstatic implementation of load_texture().
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -38,6 +38,14 @@ class EXPCL_PANDA_GOBJ TexturePool {
|
|||
PUBLISHED:
|
||||
INLINE static bool has_texture(const Filename &filename);
|
||||
INLINE static bool verify_texture(const Filename &filename);
|
||||
INLINE static Texture *get_texture(const Filename &filename,
|
||||
int primary_file_num_channels = 0,
|
||||
bool read_mipmaps = false);
|
||||
INLINE static Texture *get_texture(const Filename &filename,
|
||||
const Filename &alpha_filename,
|
||||
int primary_file_num_channels = 0,
|
||||
int alpha_file_channel = 0,
|
||||
bool read_mipmaps = false);
|
||||
BLOCKING INLINE static Texture *load_texture(const Filename &filename,
|
||||
int primary_file_num_channels = 0,
|
||||
bool read_mipmaps = false,
|
||||
|
|
@ -109,6 +117,14 @@ private:
|
|||
TexturePool();
|
||||
|
||||
bool ns_has_texture(const Filename &orig_filename);
|
||||
Texture *ns_get_texture(const Filename &filename,
|
||||
int primary_file_num_channels = 0,
|
||||
bool read_mipmaps = false);
|
||||
Texture *ns_get_texture(const Filename &filename,
|
||||
const Filename &alpha_filename,
|
||||
int primary_file_num_channels = 0,
|
||||
int alpha_file_channel = 0,
|
||||
bool read_mipmaps = false);
|
||||
Texture *ns_load_texture(const Filename &orig_filename,
|
||||
int primary_file_num_channels,
|
||||
bool read_mipmaps,
|
||||
|
|
|
|||
|
|
@ -22,28 +22,34 @@ TypeHandle TextureReloadRequest::_type_handle;
|
|||
AsyncTask::DoneStatus TextureReloadRequest::
|
||||
do_task() {
|
||||
// Don't reload the texture if it doesn't need it.
|
||||
if (_texture->was_image_modified(_pgo)) {
|
||||
double delay = async_load_delay;
|
||||
if (delay != 0.0) {
|
||||
Thread::sleep(delay);
|
||||
}
|
||||
if (!_texture->was_image_modified(_pgo) &&
|
||||
(_allow_compressed ? _texture->has_ram_image() : _texture->has_uncompressed_ram_image())) {
|
||||
return DS_done;
|
||||
}
|
||||
|
||||
if (_texture->was_image_modified(_pgo)) {
|
||||
if (_allow_compressed) {
|
||||
_texture->get_ram_image();
|
||||
} else {
|
||||
_texture->get_uncompressed_ram_image();
|
||||
}
|
||||
double delay = async_load_delay;
|
||||
if (delay != 0.0) {
|
||||
Thread::sleep(delay);
|
||||
|
||||
// Now that we've loaded the texture, we should ensure it actually gets
|
||||
// prepared--even if it's no longer visible in the frame--or it may
|
||||
// become a kind of a leak (if the texture is never rendered again on
|
||||
// this GSG, we'll just end up carrying the texture memory in RAM
|
||||
// forever, instead of dumping it as soon as it gets prepared).
|
||||
_pgo->enqueue_texture(_texture);
|
||||
if (!_texture->was_image_modified(_pgo) &&
|
||||
(_allow_compressed ? _texture->has_ram_image() : _texture->has_uncompressed_ram_image())) {
|
||||
return DS_done;
|
||||
}
|
||||
}
|
||||
|
||||
if (_allow_compressed) {
|
||||
_texture->get_ram_image();
|
||||
} else {
|
||||
_texture->get_uncompressed_ram_image();
|
||||
}
|
||||
|
||||
// Now that we've loaded the texture, we should ensure it actually gets
|
||||
// prepared--even if it's no longer visible in the frame--or it may become a
|
||||
// kind of a leak (if the texture is never rendered again on this GSG, we'll
|
||||
// just end up carrying the texture memory in RAM forever, instead of dumping
|
||||
// it as soon as it gets prepared).
|
||||
_pgo->enqueue_texture(_texture);
|
||||
|
||||
// Don't continue the task; we're done.
|
||||
return DS_done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
* force the texture's image to be re-read from disk. It is used by
|
||||
* GraphicsStateGuardian::async_reload_texture(), when get_incomplete_render()
|
||||
* is true.
|
||||
*
|
||||
* @deprecated Use Texture::async_ensure_ram_image() instead.
|
||||
*/
|
||||
class EXPCL_PANDA_GOBJ TextureReloadRequest : public AsyncTask {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ bool PipeOcclusionCullTraverser::
|
|||
is_in_view(CullTraverserData &data) {
|
||||
_next_query = nullptr;
|
||||
|
||||
if (!CullTraverser::is_in_view(data)) {
|
||||
if (!data.is_in_view(get_camera_mask())) {
|
||||
return false;
|
||||
}
|
||||
if (!_live) {
|
||||
|
|
|
|||
|
|
@ -145,8 +145,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
}
|
||||
|
||||
// Render the internal scene only--this is the optimized scene.
|
||||
CullTraverserData next_data(data, _internal_root);
|
||||
trav->traverse(next_data);
|
||||
trav->traverse_down(data, _internal_root);
|
||||
|
||||
// Do not directly render the nodes beneath this node.
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() :
|
|||
_update_enabled(true),
|
||||
_heightfield_tex(nullptr)
|
||||
{
|
||||
set_renderable();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -440,14 +441,7 @@ void ShaderTerrainMesh::do_create_chunk_geom() {
|
|||
}
|
||||
|
||||
/**
|
||||
* @copydoc PandaNode::is_renderable()
|
||||
*/
|
||||
bool ShaderTerrainMesh::is_renderable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PandaNode::is_renderable()
|
||||
* @copydoc PandaNode::safe_to_flatten()
|
||||
*/
|
||||
bool ShaderTerrainMesh::safe_to_flatten() const {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ PUBLISHED:
|
|||
public:
|
||||
|
||||
// Methods derived from PandaNode
|
||||
virtual bool is_renderable() const;
|
||||
virtual bool safe_to_flatten() const;
|
||||
virtual bool safe_to_combine() const;
|
||||
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ set_hpr(const FLOATNAME(LVecBase3) &hpr, CoordinateSystem cs) {
|
|||
v = FLOATNAME(LVector3)::right(cs);
|
||||
a = deg_2_rad(hpr[1] * 0.5f);
|
||||
csincos(a, &s, &c);
|
||||
s = csin(a);
|
||||
quat_p.set(c, v[0] * s, v[1] * s, v[2] * s);
|
||||
v = FLOATNAME(LVector3)::forward(cs);
|
||||
a = deg_2_rad(hpr[2] * 0.5f);
|
||||
|
|
|
|||
|
|
@ -357,5 +357,45 @@ __ceil__(PyObject *self) const {
|
|||
return py_vec;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE_LINMATH int Extension<FLOATNAME(LVecBase2)>::
|
||||
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
|
||||
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
|
||||
PyErr_SetString(PyExc_BufferError,
|
||||
"Object is not writable.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char format[2] = {FLOATTOKEN, 0};
|
||||
static const Py_ssize_t shape = FLOATNAME(LVecBase2)::num_components;
|
||||
|
||||
Py_INCREF(self);
|
||||
|
||||
view->buf = (void *)_this->get_data();
|
||||
view->obj = self;
|
||||
view->len = 2 * sizeof(FLOATTYPE);
|
||||
view->readonly = 1;
|
||||
view->itemsize = sizeof(FLOATTYPE);
|
||||
view->format = nullptr;
|
||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
|
||||
view->format = (char *)format;
|
||||
}
|
||||
view->ndim = 1;
|
||||
view->shape = nullptr;
|
||||
if ((flags & PyBUF_ND) == PyBUF_ND) {
|
||||
view->shape = (Py_ssize_t *)&shape;
|
||||
}
|
||||
view->strides = nullptr;
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
view->strides = &view->itemsize;
|
||||
}
|
||||
view->suboffsets = nullptr;
|
||||
view->internal = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef PYNUMBER_FLOATTYPE
|
||||
#undef PY_AS_FLOATTYPE
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public:
|
|||
INLINE_LINMATH PyObject *__round__(PyObject *self) const;
|
||||
INLINE_LINMATH PyObject *__floor__(PyObject *self) const;
|
||||
INLINE_LINMATH PyObject *__ceil__(PyObject *self) const;
|
||||
|
||||
INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
|
||||
};
|
||||
|
||||
#include "lvecBase2_ext_src.I"
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ PUBLISHED:
|
|||
INLINE_LINMATH void write_datagram(Datagram &destination) const;
|
||||
INLINE_LINMATH void read_datagram(DatagramIterator &source);
|
||||
|
||||
EXTENSION(INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
|
||||
|
||||
public:
|
||||
// The underlying implementation is via the Eigen library, if available.
|
||||
|
||||
|
|
|
|||
|
|
@ -370,5 +370,45 @@ __ceil__(PyObject *self) const {
|
|||
return py_vec;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE_LINMATH int Extension<FLOATNAME(LVecBase3)>::
|
||||
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
|
||||
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
|
||||
PyErr_SetString(PyExc_BufferError,
|
||||
"Object is not writable.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char format[2] = {FLOATTOKEN, 0};
|
||||
static const Py_ssize_t shape = FLOATNAME(LVecBase3)::num_components;
|
||||
|
||||
Py_INCREF(self);
|
||||
|
||||
view->buf = (void *)_this->get_data();
|
||||
view->obj = self;
|
||||
view->len = 3 * sizeof(FLOATTYPE);
|
||||
view->readonly = 1;
|
||||
view->itemsize = sizeof(FLOATTYPE);
|
||||
view->format = nullptr;
|
||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
|
||||
view->format = (char *)format;
|
||||
}
|
||||
view->ndim = 1;
|
||||
view->shape = nullptr;
|
||||
if ((flags & PyBUF_ND) == PyBUF_ND) {
|
||||
view->shape = (Py_ssize_t *)&shape;
|
||||
}
|
||||
view->strides = nullptr;
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
view->strides = &view->itemsize;
|
||||
}
|
||||
view->suboffsets = nullptr;
|
||||
view->internal = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef PYNUMBER_FLOATTYPE
|
||||
#undef PY_AS_FLOATTYPE
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public:
|
|||
INLINE_LINMATH PyObject *__round__(PyObject *self) const;
|
||||
INLINE_LINMATH PyObject *__floor__(PyObject *self) const;
|
||||
INLINE_LINMATH PyObject *__ceil__(PyObject *self) const;
|
||||
|
||||
INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
|
||||
};
|
||||
|
||||
#include "lvecBase3_ext_src.I"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue