Merge remote-tracking branch 'upstream/master'

This commit is contained in:
John Cote 2021-12-04 21:17:44 -05:00
commit 10b291379f
207 changed files with 4622 additions and 1497 deletions

View File

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

View File

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

View File

@ -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/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/0/website)
[![](https://opencollective.com/panda3d/tiers/gold-sponsor/1/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/1/website)
[![](https://opencollective.com/panda3d/tiers/gold-sponsor/2/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/2/website)
[![](https://opencollective.com/panda3d/tiers/gold-sponsor/3/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/3/website)
[![](https://opencollective.com/panda3d/tiers/gold-sponsor/4/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/4/website)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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> &copy);
INLINE void operator = (const CollisionLevelState<MaskType> &copy);
@ -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();

View File

@ -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 &copy) :
_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 &copy) {
_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.

View File

@ -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 &copy);
INLINE void operator = (const CollisionLevelStateBase &copy);
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1157,7 +1157,7 @@ create_simple_texture(DXScreenData &scrn) {
goto error_exit;
}
mark_simple_loaded();
mark_loaded();
return true;
error_exit:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &copy);
INLINE void operator = (const ButtonEvent &copy);
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 == '#') {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -82,6 +82,7 @@ reset_data() {
_handle_resident = false;
#endif
_has_storage = false;
_simple_loaded = false;
_immutable = false;
#ifndef OPENGLES_1

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &params) {
// 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 &params) {
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 &copy) {
_properties_modified = copy._properties_modified;
_image_modified = copy._image_modified;
_simple_image_modified = copy._simple_image_modified;
_modified_pages = copy._modified_pages;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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