diff --git a/README.md b/README.md index 314ed49ff5..7d0c5bff54 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Installing Panda3D ================== The latest Panda3D SDK can be downloaded from -[this page](https://www.panda3d.org/download/sdk-1-10-1/). +[this page](https://www.panda3d.org/download/sdk-1-10-2/). If you are familiar with installing Python packages, you can use the following comand: @@ -191,7 +191,7 @@ pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libo Then, you can build and install the .apk right away using these commands: ```bash -python makepanda/makepanda.py --everything --target android-21 --installer +python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer xdg-open panda3d.apk ``` diff --git a/contrib/src/ai/aiBehaviors.cxx b/contrib/src/ai/aiBehaviors.cxx index 1ace436e5b..62f577241b 100644 --- a/contrib/src/ai/aiBehaviors.cxx +++ b/contrib/src/ai/aiBehaviors.cxx @@ -13,6 +13,17 @@ #include "aiBehaviors.h" +#include "arrival.h" +#include "evade.h" +#include "flee.h" +#include "flock.h" +#include "obstacleAvoidance.h" +#include "pathFind.h" +#include "pathFollow.h" +#include "pursue.h" +#include "seek.h" +#include "wander.h" + using std::cout; using std::endl; using std::string; diff --git a/contrib/src/ai/aiGlobals.h b/contrib/src/ai/aiGlobals.h index 1d8e8a9c41..f068bd4594 100644 --- a/contrib/src/ai/aiGlobals.h +++ b/contrib/src/ai/aiGlobals.h @@ -15,7 +15,6 @@ #define _AI_GLOBALS_H #include "config_ai.h" -#include "pandaFramework.h" #include "textNode.h" #include "pandaSystem.h" diff --git a/contrib/src/ai/arrival.cxx b/contrib/src/ai/arrival.cxx index 966a6b2611..3ce0f706da 100644 --- a/contrib/src/ai/arrival.cxx +++ b/contrib/src/ai/arrival.cxx @@ -13,6 +13,9 @@ #include "arrival.h" +#include "pursue.h" +#include "seek.h" + Arrival::Arrival(AICharacter *ai_ch, double distance) { _ai_char = ai_ch; diff --git a/contrib/src/ai/obstacleAvoidance.cxx b/contrib/src/ai/obstacleAvoidance.cxx index 87ce79026b..42c301e033 100644 --- a/contrib/src/ai/obstacleAvoidance.cxx +++ b/contrib/src/ai/obstacleAvoidance.cxx @@ -13,6 +13,8 @@ #include "obstacleAvoidance.h" +#include "aiWorld.h" + ObstacleAvoidance:: ObstacleAvoidance(AICharacter *ai_char, float feeler_length) { _ai_char = ai_char; diff --git a/contrib/src/ai/pathFind.cxx b/contrib/src/ai/pathFind.cxx index ee58f4940d..5813c9d503 100644 --- a/contrib/src/ai/pathFind.cxx +++ b/contrib/src/ai/pathFind.cxx @@ -13,6 +13,8 @@ #include "pathFind.h" +#include "pathFollow.h" + using std::cout; using std::endl; using std::string; diff --git a/contrib/src/ai/pathFollow.cxx b/contrib/src/ai/pathFollow.cxx index dd7720e318..942d7c7daa 100644 --- a/contrib/src/ai/pathFollow.cxx +++ b/contrib/src/ai/pathFollow.cxx @@ -1,6 +1,20 @@ +/** + * 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 pathFind.cxx + * @author Deepak, John, Navin + * @date 2009-10-24 + */ #include "pathFollow.h" +#include "pathFind.h" + PathFollow::PathFollow(AICharacter *ai_ch, float follow_wt) { _follow_weight = follow_wt; _curr_path_waypoint = -1; diff --git a/contrib/src/rplight/shadowAtlas.h b/contrib/src/rplight/shadowAtlas.h index e3f0958a50..d6296e7658 100644 --- a/contrib/src/rplight/shadowAtlas.h +++ b/contrib/src/rplight/shadowAtlas.h @@ -28,7 +28,7 @@ #define SHADOWATLAS_H #include "pandabase.h" -#include "lvecBase4.h" +#include "luse.h" NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL); diff --git a/direct/src/controls/GravityWalker.py b/direct/src/controls/GravityWalker.py index 32c18c05f3..bf3ea8a9bd 100755 --- a/direct/src/controls/GravityWalker.py +++ b/direct/src/controls/GravityWalker.py @@ -71,90 +71,6 @@ class GravityWalker(DirectObject.DirectObject): self.isAirborne = 0 self.highMark = 0 - """ - def spawnTest(self): - assert self.notify.debugStateCall(self) - if not self.wantDebugIndicator: - return - from pandac.PandaModules import * - from direct.interval.IntervalGlobal import * - from toontown.coghq import MovingPlatform - - if hasattr(self, "platform"): - # Remove the prior instantiation: - self.moveIval.pause() - del self.moveIval - self.platform.destroy() - del self.platform - self.platform2.destroy() - del self.platform2 - - model = loader.loadModel('phase_9/models/cogHQ/platform1') - fakeId = id(self) - self.platform = MovingPlatform.MovingPlatform() - self.platform.setupCopyModel(fakeId, model, 'platformcollision') - self.platformRoot = render.attachNewNode("GravityWalker-spawnTest-%s"%fakeId) - self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 0.0, 1.0)) - self.platformRoot.setHpr(base.localAvatar, Vec3.zero()) - self.platform.reparentTo(self.platformRoot) - - self.platform2 = MovingPlatform.MovingPlatform() - self.platform2.setupCopyModel(1+fakeId, model, 'platformcollision') - self.platform2Root = render.attachNewNode("GravityWalker-spawnTest2-%s"%fakeId) - self.platform2Root.setPos(base.localAvatar, Vec3(-16.0, 30.0, 1.0)) - self.platform2Root.setHpr(base.localAvatar, Vec3.zero()) - self.platform2.reparentTo(self.platform2Root) - - duration = 5 - self.moveIval = Parallel( - Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 30.0, 0.0), - name='platformOut%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 0.0), - name='platformBack%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 30.0), - name='platformUp%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - Vec3(0.0, 0.0, 0.0), - name='platformDown%s' % fakeId, - fluid = 1), - ), - Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, -30.0, 0.0), - name='platform2Out%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, 30.0, 30.0), - name='platform2Back%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, -30.0, 0.0), - name='platform2Up%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform2, duration, - Vec3(0.0, 0.0, 0.0), - name='platformDown%s' % fakeId, - fluid = 1), - ), - name='platformIval%s' % fakeId, - ) - self.moveIval.loop() - """ def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.notify.debugStateCall(self) self.avatarControlForwardSpeed=forward diff --git a/direct/src/controls/PhysicsWalker.py b/direct/src/controls/PhysicsWalker.py index fc6fe1ca70..de6ebcefa8 100755 --- a/direct/src/controls/PhysicsWalker.py +++ b/direct/src/controls/PhysicsWalker.py @@ -67,51 +67,6 @@ class PhysicsWalker(DirectObject.DirectObject): self.isAirborne = 0 self.highMark = 0 - """ - def spawnTest(self): - assert self.debugPrint("\n\nspawnTest()\n") - if not self.wantDebugIndicator: - return - from pandac.PandaModules import * - from direct.interval.IntervalGlobal import * - from toontown.coghq import MovingPlatform - - if hasattr(self, "platform"): - # Remove the prior instantiation: - self.moveIval.pause() - del self.moveIval - self.platform.destroy() - del self.platform - - model = loader.loadModel('phase_9/models/cogHQ/platform1') - fakeId = id(self) - self.platform = MovingPlatform.MovingPlatform() - self.platform.setupCopyModel(fakeId, model, 'platformcollision') - self.platformRoot = render.attachNewNode("physicsWalker-spawnTest-%s"%fakeId) - self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 3.0, 1.0)) - self.platformRoot.setHpr(base.localAvatar, Vec3.zero()) - self.platform.reparentTo(self.platformRoot) - - startPos = Vec3(0.0, -15.0, 0.0) - endPos = Vec3(0.0, 15.0, 0.0) - distance = Vec3(startPos-endPos).length() - duration = distance/4 - self.moveIval = Sequence( - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - endPos, startPos=startPos, - name='platformOut%s' % fakeId, - fluid = 1), - WaitInterval(0.3), - LerpPosInterval(self.platform, duration, - startPos, startPos=endPos, - name='platformBack%s' % fakeId, - fluid = 1), - name='platformIval%s' % fakeId, - ) - self.moveIval.loop() - """ - def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.debugPrint("setWalkSpeed()") self.avatarControlForwardSpeed=forward diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 4ac1b96f56..f1b7bcc1a7 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -624,8 +624,9 @@ class Messenger: for key in list(acceptorDict.keys()): function, extraArgs, persistent = acceptorDict[key] object = self._getObject(key) - if (type(object) == types.InstanceType): - className = object.__class__.__name__ + objectClass = getattr(object, '__class__', None) + if objectClass: + className = objectClass.__name__ else: className = "Not a class" functionName = function.__name__ diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 721a67453e..2370742957 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1652,17 +1652,15 @@ def itype(obj): # version of type that gives more complete information about instance types global dtoolSuperBase t = type(obj) - if t is types.InstanceType: - return '%s of >' % (repr(types.InstanceType)[:-1], - str(obj.__class__)) + if sys.version_info < (3, 0) and t is types.InstanceType: + return ">" % (obj.__class__) else: # C++ object instances appear to be types via type() # check if this is a C++ object if dtoolSuperBase is None: _getDtoolSuperBase() if isinstance(obj, dtoolSuperBase): - return '%s of %s>' % (repr(types.InstanceType)[:-1], - str(obj.__class__)) + return "" % (obj.__class__) return t def deeptype(obj, maxLen=100, _visitedIds=None): diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 8645e5bc43..031c403644 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -388,13 +388,10 @@ class TaskManager: def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath): if isinstance(funcOrTask, AsyncTask): task = funcOrTask - elif hasattr(funcOrTask, '__call__'): - task = PythonTask(funcOrTask) - if name is None: - name = getattr(funcOrTask, '__qualname__', None) or \ - getattr(funcOrTask, '__name__', None) - elif hasattr(funcOrTask, 'cr_await') or type(funcOrTask) == types.GeneratorType: - # It's a coroutine, or something emulating one. + elif hasattr(funcOrTask, '__call__') or \ + hasattr(funcOrTask, 'cr_await') or \ + type(funcOrTask) == types.GeneratorType: + # It's a function, coroutine, or something emulating a coroutine. task = PythonTask(funcOrTask) if name is None: name = getattr(funcOrTask, '__qualname__', None) or \ diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py index 9b34192e94..9994b8fe78 100755 --- a/makepanda/makepackage.py +++ b/makepanda/makepackage.py @@ -748,7 +748,7 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs): oscmd('rm -f Panda3D-rw.dmg') -def MakeInstallerFreeBSD(version, runtime=False, **kwargs): +def MakeInstallerFreeBSD(version, runtime=False, python_versions=[], **kwargs): outputdir = GetOutputDir() oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST") @@ -758,7 +758,7 @@ def MakeInstallerFreeBSD(version, runtime=False, **kwargs): if runtime: InstallRuntime(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) else: - InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir) + InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir, python_versions=python_versions) if not os.path.exists("/usr/sbin/pkg"): exit("Cannot create an installer without pkg") diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index e7d12ce4d6..4f2abef8a9 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -554,6 +554,17 @@ if RUNTIME and not HOST_URL: # Set this to a nice default. HOST_URL = "https://runtime.panda3d.org/" +if not PkgSkip("PYTHON") and SDK["PYTHONVERSION"] == "python2.7": + warn_prefix = "%sWARNING:%s " % (GetColor("red"), GetColor()) + print("=========================================================================") + print(warn_prefix + "Python 2.7 will reach EOL after December 31, 2019, and will not") + print(warn_prefix + "be supported after that date. Please ensure you are prepared") + print(warn_prefix + "by planning your upgrade to Python 3 now.") + print("=========================================================================") + sys.stdout.flush() + # Give the user some time to contemplate their sins + time.sleep(6.0) + ######################################################################## ## ## Choose a Compiler. @@ -3003,9 +3014,6 @@ else: if (GetTarget() == 'darwin'): configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$HOME/Library/Caches/Panda3D-%s" % MAJOR_VERSION) - # OpenAL is not yet working well on OSX for us, so let's do this for now. - configprc = configprc.replace("p3openal_audio", "p3fmod_audio") - if GetTarget() == 'windows': # Convert to Windows newlines. ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc, newline='\r\n') @@ -4709,7 +4717,7 @@ if not RUNTIME and not PkgSkip("EGG"): TargetAdd('p3egg2pg_composite2.obj', opts=OPTS, input='p3egg2pg_composite2.cxx') OPTS=['DIR:panda/src/egg2pg'] - IGATEFILES=['load_egg_file.h'] + IGATEFILES=['load_egg_file.h', 'save_egg_file.h'] TargetAdd('libp3egg2pg.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3egg2pg.in', opts=['IMOD:panda3d.egg', 'ILIB:libp3egg2pg', 'SRCDIR:panda/src/egg2pg']) @@ -5156,7 +5164,7 @@ if (not RUNTIME and GetTarget() == 'android'): TargetAdd('libppython.dll', input='libp3framework.dll') TargetAdd('libppython.dll', input='libp3android.dll') TargetAdd('libppython.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libppython.dll', opts=['MODULE', 'ANDROID']) + TargetAdd('libppython.dll', opts=['MODULE', 'ANDROID', 'PYTHON']) # # DIRECTORY: panda/src/androiddisplay/ diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj index 785061eea8..a1b720bfde 100644 --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -2147,7 +2147,6 @@ - @@ -2174,7 +2173,6 @@ - @@ -2215,7 +2213,6 @@ - @@ -2259,7 +2256,6 @@ - @@ -2286,7 +2282,6 @@ - @@ -2294,7 +2289,6 @@ - diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 386570a0d8..46ab62cbf9 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2797,8 +2797,14 @@ def SetupVisualStudioEnviron(): elif not win_kit.endswith('\\'): win_kit += '\\' - AddToPathEnv("LIB", win_kit + "Lib\\10.0.10150.0\\ucrt\\" + arch) - AddToPathEnv("INCLUDE", win_kit + "Include\\10.0.10150.0\\ucrt") + for vnum in 10150, 10240, 10586, 14393, 15063, 16299, 17134, 17763: + version = "10.0.{0}.0".format(vnum) + if os.path.isfile(win_kit + "Include\\" + version + "\\ucrt\\assert.h"): + print("Using Universal CRT %s" % (version)) + break + + AddToPathEnv("LIB", "%s\\Lib\\%s\\ucrt\\%s" % (win_kit, version, arch)) + AddToPathEnv("INCLUDE", "%s\\Include\\%s\\ucrt" % (win_kit, version)) # Copy the DLLs to the bin directory. CopyAllFiles(GetOutputDir() + "/bin/", win_kit + "Redist\\ucrt\\DLLs\\" + arch + "\\") diff --git a/panda/src/audio/audio.h b/panda/src/audio/audio.h index c498834502..ca1f5bfd94 100644 --- a/panda/src/audio/audio.h +++ b/panda/src/audio/audio.h @@ -11,12 +11,12 @@ * @date 2000-07-06 */ -#ifndef __AUDIO_H__ -#define __AUDIO_H__ +#ifndef AUDIO_H +#define AUDIO_H #include "filterProperties.h" #include "audioLoadRequest.h" #include "audioSound.h" #include "audioManager.h" -#endif /* __AUDIO_H__ */ +#endif /* AUDIO_H */ diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 62d9a5d090..aef3932246 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __AUDIO_MANAGER_H__ -#define __AUDIO_MANAGER_H__ +#ifndef AUDIOMANAGER_H +#define AUDIOMANAGER_H #include "config_audio.h" #include "audioSound.h" @@ -222,4 +222,4 @@ operator << (std::ostream &out, const AudioManager &mgr) { #include "audioManager.I" -#endif /* __AUDIO_MANAGER_H__ */ +#endif /* AUDIOMANAGER_H */ diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index e348ad4daf..ecfda1ef50 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __AUDIOSOUND_H__ -#define __AUDIOSOUND_H__ +#ifndef AUDIOSOUND_H +#define AUDIOSOUND_H #include "config_audio.h" #include "typedReferenceCount.h" @@ -160,4 +160,4 @@ operator << (std::ostream &out, const AudioSound &sound) { EXPCL_PANDA_AUDIO std::ostream & operator << (std::ostream &out, AudioSound::SoundStatus status); -#endif /* __AUDIOSOUND_H__ */ +#endif /* AUDIOSOUND_H */ diff --git a/panda/src/audio/nullAudioManager.h b/panda/src/audio/nullAudioManager.h index a4e2c47511..61db5a6e6d 100644 --- a/panda/src/audio/nullAudioManager.h +++ b/panda/src/audio/nullAudioManager.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __NULL_AUDIO_MANAGER_H__ -#define __NULL_AUDIO_MANAGER_H__ +#ifndef NULLAUDIOMANAGER_H +#define NULLAUDIOMANAGER_H #include "audioManager.h" #include "nullAudioSound.h" @@ -89,4 +89,4 @@ private: static TypeHandle _type_handle; }; -#endif /* __NULL_AUDIO_MANAGER_H__ */ +#endif /* NULLAUDIOMANAGER_H */ diff --git a/panda/src/audio/nullAudioSound.h b/panda/src/audio/nullAudioSound.h index d388493132..0c90c6ef5b 100644 --- a/panda/src/audio/nullAudioSound.h +++ b/panda/src/audio/nullAudioSound.h @@ -12,8 +12,8 @@ * Prior system by: cary */ -#ifndef __NULL_AUDIO_SOUND_H__ -#define __NULL_AUDIO_SOUND_H__ +#ifndef NULLAUDIOSOUND_H +#define NULLAUDIOSOUND_H #include "audioSound.h" @@ -91,4 +91,4 @@ private: friend class NullAudioManager; }; -#endif /* __NULL_AUDIO_SOUND_H__ */ +#endif /* NULLAUDIOSOUND_H */ diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index 8528e7bc27..2f5385b9bf 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -186,6 +186,7 @@ get_test_pcollector() { */ void CollisionBox:: output(std::ostream &out) const { + out << "box, (" << get_min() << ") to (" << get_max() << ")"; } /** diff --git a/panda/src/collide/collisionSphere.cxx b/panda/src/collide/collisionSphere.cxx index aacc43fcb2..0d3275ec36 100644 --- a/panda/src/collide/collisionSphere.cxx +++ b/panda/src/collide/collisionSphere.cxx @@ -620,14 +620,20 @@ intersects_line(double &t1, double &t2, double A = dot(delta, delta); - nassertr(A != 0.0, false); - LVector3 fc = from - get_center(); - double B = 2.0f* dot(delta, fc); double fc_d2 = dot(fc, fc); double radius = get_radius() + inflate_radius; double C = fc_d2 - radius * radius; + if (A == 0.0) { + // Degenerate case where delta is zero. This is effectively a test + // against a point (or sphere, for nonzero inflate_radius). + t1 = 0.0; + t2 = 0.0; + return C < 0.0; + } + + double B = 2.0f * dot(delta, fc); double radical = B*B - 4.0*A*C; if (IS_NEARLY_ZERO(radical)) { diff --git a/panda/src/device/inputDeviceSet.I b/panda/src/device/inputDeviceSet.I old mode 100755 new mode 100644 diff --git a/panda/src/device/inputDeviceSet.cxx b/panda/src/device/inputDeviceSet.cxx old mode 100755 new mode 100644 diff --git a/panda/src/device/inputDeviceSet.h b/panda/src/device/inputDeviceSet.h old mode 100755 new mode 100644 diff --git a/panda/src/device/winRawInputDevice.cxx b/panda/src/device/winRawInputDevice.cxx index 583ad920da..e2e27761bc 100644 --- a/panda/src/device/winRawInputDevice.cxx +++ b/panda/src/device/winRawInputDevice.cxx @@ -15,6 +15,7 @@ #include "gamepadButton.h" #include "mouseButton.h" #include "buttonRegistry.h" +#include "winInputDeviceManager.h" #if defined(_WIN32) && !defined(CPPPARSER) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 5ca7e6bc32..749fbc7b35 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -1433,12 +1433,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, // Apply the default OpenGL lights otherwise. // Special exception for light 0, which defaults to white. string basename = name->get_basename(); - if (basename == "color" || basename == "diffuse") { - t.set_row(3, _light_color_scale); - return &t; - } else if (basename == "specular") { - return &LMatrix4::ones_mat(); - } + return &LMatrix4::ones_mat(); } return fetch_specified_member(NodePath(), name, t); } @@ -1494,7 +1489,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, } else if (index == 0) { // Apply the default OpenGL lights otherwise. // Special exception for light 0, which defaults to white. - t.set_row(0, _light_color_scale); + t.set_row(0, LVecBase4(1, 1, 1, 1)); } return &t; } @@ -1539,7 +1534,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) Light *light = node->as_light(); nassertr(light != nullptr, &LMatrix4::ident_mat()); LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); return &t; @@ -1551,7 +1545,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) nassertr(light != nullptr, &LMatrix4::ident_mat()); if (node->is_ambient_light()) { LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); } else { // Non-ambient lights don't currently have an ambient color in Panda3D. @@ -1570,7 +1563,6 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t) t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f)); } else { LColor c = light->get_color(); - c.componentwise_mult(_light_color_scale); t.set_row(3, c); } return &t; diff --git a/panda/src/distort/oSphereLens.cxx b/panda/src/distort/oSphereLens.cxx index 7b8bd35591..9175453fd9 100644 --- a/panda/src/distort/oSphereLens.cxx +++ b/panda/src/distort/oSphereLens.cxx @@ -48,7 +48,7 @@ do_extrude(const Lens::CData *lens_cdata, LPoint3 f = point2d * do_get_film_mat_inv(lens_cdata); PN_stdfloat focal_length = do_get_focal_length(lens_cdata); - PN_stdfloat angle = f[0] * cylindrical_k / focal_length; + PN_stdfloat angle = f[0] * ospherical_k / focal_length; PN_stdfloat sinAngle, cosAngle; csincos(deg_2_rad(angle), &sinAngle, &cosAngle); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 6aeb84f54f..b9620cbbe3 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -1814,10 +1814,11 @@ make_node(EggGroup *egg_group, PandaNode *parent) { // A collision group: create collision geometry. node = new CollisionNode(egg_group->get_name()); + // Piggy-back the desired transform to apply onto the node, since we can't + // break the ABI in 1.10. + node->set_transform(TransformState::make_mat(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()))); make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p()); - - // Transform all of the collision solids into local space. - node->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node())); + node->clear_transform(); if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) { // If we also specified to keep the geometry, continue the traversal. @@ -2773,7 +2774,7 @@ make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags, */ bool EggLoader:: make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, - LPoint3 &min_p, LPoint3 &max_p, LColor &color) { + const LMatrix4 &xform, LPoint3 &min_p, LPoint3 &max_p) { EggGroup *geom_group = find_collision_geometry(egg_group, flags); if (geom_group != nullptr) { // Collect all of the vertices. @@ -2802,13 +2803,12 @@ make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, } EggVertex *vertex = (*vi); - LVertexd min_pd = vertex->get_pos3(); - LVertexd max_pd = min_pd; - color = vertex->get_color(); + LPoint3 min_pd = LCAST(PN_stdfloat, vertex->get_pos3()) * xform; + LPoint3 max_pd = min_pd; for (++vi; vi != vertices.end(); ++vi) { vertex = (*vi); - const LVertexd &pos = vertex->get_pos3(); + LPoint3 pos = LCAST(PN_stdfloat, vertex->get_pos3()) * xform; min_pd.set(min(min_pd[0], pos[0]), min(min_pd[1], pos[1]), min(min_pd[2], pos[2])); @@ -2817,13 +2817,25 @@ make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, max(max_pd[2], pos[2])); } - min_p = LCAST(PN_stdfloat, min_pd); - max_p = LCAST(PN_stdfloat, max_pd); + min_p = min_pd; + max_p = max_pd; return (min_pd != max_pd); } return false; } +/** + * Creates a single generic Box corresponding to the polygons associated with + * this group. This box is used by make_collision_box. + */ +bool EggLoader:: +make_box(EggGroup *egg_group, EggGroup::CollideFlags flags, + LPoint3 &min_p, LPoint3 &max_p, LColor &color) { + + color.set(1.0, 1.0, 1.0, 1.0); + return make_box(egg_group, flags, LMatrix4::ident_mat(), min_p, max_p); +} + /** * Creates CollisionSolids corresponding to the collision geometry indicated * at the given node and below. @@ -2904,6 +2916,7 @@ make_collision_plane(EggGroup *egg_group, CollisionNode *cnode, create_collision_plane(DCAST(EggPolygon, *ci), egg_group); if (csplane != nullptr) { apply_collision_flags(csplane, flags); + csplane->xform(cnode->get_transform()->get_mat()); cnode->add_solid(csplane); return; } @@ -3004,6 +3017,7 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode, CollisionSphere *cssphere = new CollisionSphere(center, radius); apply_collision_flags(cssphere, flags); + cssphere->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cssphere); } } @@ -3017,8 +3031,8 @@ make_collision_box(EggGroup *egg_group, CollisionNode *cnode, EggGroup::CollideFlags flags) { LPoint3 min_p; LPoint3 max_p; - LColor dummycolor; - if (make_box(egg_group, flags, min_p, max_p, dummycolor)) { + CPT(TransformState) transform = cnode->get_transform(); + if (make_box(egg_group, flags, transform->get_mat(), min_p, max_p)) { CollisionBox *csbox = new CollisionBox(min_p, max_p); apply_collision_flags(csbox, flags); @@ -3040,6 +3054,7 @@ make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode, CollisionInvSphere *cssphere = new CollisionInvSphere(center, radius); apply_collision_flags(cssphere, flags); + cssphere->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cssphere); } } @@ -3234,6 +3249,7 @@ make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode, new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b), radius); apply_collision_flags(cscapsule, flags); + cscapsule->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cscapsule); } } @@ -3395,6 +3411,7 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly, new CollisionPolygon(vertices_begin, vertices_end); if (cspoly->is_valid()) { apply_collision_flags(cspoly, flags); + cspoly->xform(cnode->get_transform()->get_mat()); cnode->add_solid(cspoly); } } @@ -3485,6 +3502,7 @@ create_collision_floor_mesh(CollisionNode *cnode, CollisionFloorMesh::TriangleIndices triangle = *ti; csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3); } + csfloor->xform(cnode->get_transform()->get_mat()); cnode->add_solid(csfloor); } diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 0f974f41c2..ab11214e19 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -163,6 +163,8 @@ private: bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags, LPoint3 ¢er, PN_stdfloat &radius, LColor &color); + bool make_box(EggGroup *start_group, EggGroup::CollideFlags flags, + const LMatrix4 &xform, LPoint3 &min_p, LPoint3 &max_p); bool make_box(EggGroup *start_group, EggGroup::CollideFlags flags, LPoint3 &min_p, LPoint3 &max_p, LColor &color); diff --git a/panda/src/express/p3express_composite2.cxx b/panda/src/express/p3express_composite2.cxx index 5caa03986d..29b1da723e 100644 --- a/panda/src/express/p3express_composite2.cxx +++ b/panda/src/express/p3express_composite2.cxx @@ -8,8 +8,6 @@ #include "subStream.cxx" #include "subStreamBuf.cxx" #include "temporaryFile.cxx" -#include "threadSafePointerTo.cxx" -#include "threadSafePointerToBase.cxx" #include "trueClock.cxx" #include "typedReferenceCount.cxx" #include "virtualFile.cxx" diff --git a/panda/src/express/threadSafePointerTo.I b/panda/src/express/threadSafePointerTo.I deleted file mode 100644 index ef2b8db410..0000000000 --- a/panda/src/express/threadSafePointerTo.I +++ /dev/null @@ -1,207 +0,0 @@ -/** - * 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 threadSafePointerTo.I - * @author drose - * @date 2006-04-28 - */ - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -ThreadSafePointerTo(To *ptr) : ThreadSafePointerToBase(ptr) { -} - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -ThreadSafePointerTo(const ThreadSafePointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE ThreadSafePointerTo:: -~ThreadSafePointerTo() { -} - -/** - * - */ -template -INLINE typename ThreadSafePointerTo::To &ThreadSafePointerTo:: -operator *() const { - return *((To *)AtomicAdjust::get_ptr(this->_void_ptr)); -} - -/** - * - */ -template -INLINE typename ThreadSafePointerTo::To *ThreadSafePointerTo:: -operator -> () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * We also have the typecast operator to automatically convert - * ThreadSafePointerTo's to the required kind of actual pointer. This - * introduces ambiguities which the compiler will resolve one way or the - * other, but we don't care which way it goes because either will be correct. - */ -template -INLINE ThreadSafePointerTo:: -operator T * () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * Returns an ordinary pointer instead of a ThreadSafePointerTo. Useful to - * work around compiler problems, particularly for implicit upcasts. - */ -template -INLINE typename ThreadSafePointerTo::To *ThreadSafePointerTo:: -p() const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * - */ -template -INLINE ThreadSafePointerTo &ThreadSafePointerTo:: -operator = (To *ptr) { - this->reassign(ptr); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafePointerTo &ThreadSafePointerTo:: -operator = (const ThreadSafePointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const typename ThreadSafeConstPointerTo::To *ptr) : - ThreadSafePointerToBase((typename ThreadSafeConstPointerTo::To *)ptr) -{ -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const ThreadSafePointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -~ThreadSafeConstPointerTo() { -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo:: -ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo ©) : - ThreadSafePointerToBase((const ThreadSafePointerToBase &)copy) -{ -} - -/** - * - */ -template -INLINE const typename ThreadSafeConstPointerTo::To &ThreadSafeConstPointerTo:: -operator *() const { - return *((To *)AtomicAdjust::get_ptr(this->_void_ptr)); -} - -/** - * - */ -template -INLINE const typename ThreadSafeConstPointerTo::To *ThreadSafeConstPointerTo:: -operator -> () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * We also have the typecast operator to automatically convert - * ThreadSafeConstPointerTo's to the required kind of actual pointer. This - * introduces ambiguities which the compiler will resolve one way or the - * other, but we don't care which way it goes because either will be correct. - */ - -template -INLINE ThreadSafeConstPointerTo:: -operator const T * () const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * Returns an ordinary pointer instead of a ThreadSafeConstPointerTo. Useful - * to work around compiler problems, particularly for implicit upcasts. - */ -template -INLINE const typename ThreadSafeConstPointerTo::To *ThreadSafeConstPointerTo:: -p() const { - return (To *)AtomicAdjust::get_ptr(this->_void_ptr); -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const To *ptr) { - this->reassign((To *)ptr); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const ThreadSafePointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} - -/** - * - */ -template -INLINE ThreadSafeConstPointerTo &ThreadSafeConstPointerTo:: -operator = (const ThreadSafeConstPointerTo ©) { - this->reassign((const ThreadSafePointerToBase &)copy); - return *this; -} diff --git a/panda/src/express/threadSafePointerTo.cxx b/panda/src/express/threadSafePointerTo.cxx deleted file mode 100644 index 4c3692d32e..0000000000 --- a/panda/src/express/threadSafePointerTo.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 threadSafePointerTo.cxx - * @author drose - * @date 2006-04-28 - */ - -#include "threadSafePointerTo.h" diff --git a/panda/src/express/threadSafePointerTo.h b/panda/src/express/threadSafePointerTo.h deleted file mode 100644 index 88699fa23a..0000000000 --- a/panda/src/express/threadSafePointerTo.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 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 threadSafePointerTo.h - * @author drose - * @date 2006-04-28 - */ - -#ifndef THREADSAFEPOINTERTO_H -#define THREADSAFEPOINTERTO_H - -#include "pandabase.h" -#include "threadSafePointerToBase.h" - -/** - * This works exactly like PointerTo, except that the object is designed to be - * thread-safe: it is generally safe to make unprotected assignments to this - * pointer, in the sense that the last assignment will win and the reference - * counts will be properly maintained. - */ -template -class ThreadSafePointerTo : public ThreadSafePointerToBase { -public: - typedef typename ThreadSafePointerToBase::To To; -PUBLISHED: - INLINE ThreadSafePointerTo(To *ptr = nullptr); - INLINE ThreadSafePointerTo(const ThreadSafePointerTo ©); - INLINE ~ThreadSafePointerTo(); - -public: - INLINE To &operator *() const; - INLINE To *operator -> () const; - // MSVC.NET 2005 insists that we use T *, and not To *, here. - INLINE operator T *() const; - -PUBLISHED: - // When downcasting to a derived class from a - // ThreadSafePointerTo, C++ would normally require you to cast - // twice: once to an actual BaseClass pointer, and then again to your - // desired pointer. You can use the handy function p() to avoid this first - // cast and make your code look a bit cleaner. - - // e.g. instead of (MyType *)(BaseClass *)ptr, use (MyType *)ptr.p() - - // If your base class is a derivative of TypedObject, you might want to use - // the DCAST macro defined in typedObject.h instead, e.g. DCAST(MyType, - // ptr). This provides a clean downcast that doesn't require .p() or any - // double-casting, and it can be run-time checked for correctness. - INLINE To *p() const; - - INLINE ThreadSafePointerTo &operator = (To *ptr); - INLINE ThreadSafePointerTo &operator = (const ThreadSafePointerTo ©); - - // These functions normally wouldn't need to be redefined here, but we do so - // anyway just to help out interrogate (which doesn't seem to want to - // automatically export the ThreadSafePointerToBase class). When this works - // again in interrogate, we can remove these. - INLINE bool is_null() const { return ThreadSafePointerToBase::is_null(); } - INLINE void clear() { ThreadSafePointerToBase::clear(); } -}; - - -/** - * - */ -template -class ThreadSafeConstPointerTo : public ThreadSafePointerToBase { -public: - typedef typename ThreadSafePointerToBase::To To; -PUBLISHED: - INLINE ThreadSafeConstPointerTo(const To *ptr = nullptr); - INLINE ThreadSafeConstPointerTo(const ThreadSafePointerTo ©); - INLINE ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo ©); - INLINE ~ThreadSafeConstPointerTo(); - -public: - INLINE const To &operator *() const; - INLINE const To *operator -> () const; - INLINE operator const T *() const; - -PUBLISHED: - INLINE const To *p() const; - - INLINE ThreadSafeConstPointerTo &operator = (const To *ptr); - INLINE ThreadSafeConstPointerTo &operator = (const ThreadSafePointerTo ©); - INLINE ThreadSafeConstPointerTo &operator = (const ThreadSafeConstPointerTo ©); - - // This functions normally wouldn't need to be redefined here, but we do so - // anyway just to help out interrogate (which doesn't seem to want to - // automatically export the ThreadSafePointerToBase class). When this works - // again in interrogate, we can remove this. - INLINE void clear() { ThreadSafePointerToBase::clear(); } -}; - -#define TSPT(type) ThreadSafePointerTo< type > -#define TSCPT(type) ThreadSafeConstPointerTo< type > - -#include "threadSafePointerTo.I" - -#endif diff --git a/panda/src/express/threadSafePointerToBase.I b/panda/src/express/threadSafePointerToBase.I deleted file mode 100644 index bee16180b1..0000000000 --- a/panda/src/express/threadSafePointerToBase.I +++ /dev/null @@ -1,132 +0,0 @@ -/** - * 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 threadSafePointerToBase.I - * @author drose - * @date 2006-04-28 - */ - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -ThreadSafePointerToBase(To *ptr) { - reassign(ptr); -} - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -ThreadSafePointerToBase(const ThreadSafePointerToBase ©) { - reassign(copy); -} - -/** - * - */ -template -INLINE ThreadSafePointerToBase:: -~ThreadSafePointerToBase() { - reassign(nullptr); -} - -/** - * This is the main work of the ThreadSafePointerTo family. When the pointer - * is reassigned, decrement the old reference count and increment the new one. - */ -template -INLINE void ThreadSafePointerToBase:: -reassign(To *ptr) { - To *old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr); - if (ptr == old_ptr) { - return; - } - -#ifdef HAVE_THREADS - void *orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr); - while (orig_ptr != old_ptr) { - // Some other thread assigned it first. Try again. - old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr); - if (ptr == old_ptr) { - return; - } - - orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr); - } -#else // HAVE_THREADS - _void_ptr = ptr; -#endif // HAVE_THREADS - - if (ptr != nullptr) { - ptr->ref(); -#ifdef DO_MEMORY_USAGE - if (MemoryUsage::get_track_memory_usage()) { - update_type(ptr); - } -#endif - } - - // Now delete the old pointer. - if (old_ptr != nullptr) { - unref_delete(old_ptr); - } -} - -/** - * - */ -template -INLINE void ThreadSafePointerToBase:: -reassign(const ThreadSafePointerToBase ©) { - reassign((To *)copy._void_ptr); -} - -/** - * Ensures that the MemoryUsage record for the pointer has the right type of - * object, if we know the type ourselves. - */ -template -void ThreadSafePointerToBase:: -update_type(To *ptr) { -#ifdef DO_MEMORY_USAGE - TypeHandle type = get_type_handle(To); - if (type == TypeHandle::none()) { - do_init_type(To); - type = get_type_handle(To); - } - if (type != TypeHandle::none()) { - MemoryUsage::update_type(ptr, type); - } -#endif // DO_MEMORY_USAGE -} - -/** - * A convenient way to set the ThreadSafePointerTo object to NULL. (Assignment - * to a NULL pointer also works, of course.) - */ -template -INLINE void ThreadSafePointerToBase:: -clear() { - reassign(nullptr); -} - -/** - * A handy function to output ThreadSafePointerTo's as a hex pointer followed - * by a reference count. - */ -template -INLINE void ThreadSafePointerToBase:: -output(std::ostream &out) const { - out << _void_ptr; - if (_void_ptr != nullptr) { - out << ":" << ((To *)_void_ptr)->get_ref_count(); - } -} diff --git a/panda/src/express/threadSafePointerToBase.cxx b/panda/src/express/threadSafePointerToBase.cxx deleted file mode 100644 index 20d0230c86..0000000000 --- a/panda/src/express/threadSafePointerToBase.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 threadSafePointerToBase.cxx - * @author drose - * @date 2006-04-28 - */ - -#include "threadSafePointerToBase.h" diff --git a/panda/src/express/threadSafePointerToBase.h b/panda/src/express/threadSafePointerToBase.h deleted file mode 100644 index 764e538d2b..0000000000 --- a/panda/src/express/threadSafePointerToBase.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 threadSafePointerToBase.h - * @author drose - * @date 2006-04-28 - */ - -#ifndef THREADSAFEPOINTERTOBASE_H -#define THREADSAFEPOINTERTOBASE_H - -#include "pandabase.h" -#include "pointerToVoid.h" -#include "referenceCount.h" -#include "typedef.h" -#include "memoryUsage.h" -#include "config_express.h" -#include "atomicAdjust.h" - -/** - * This is the base class for ThreadSafePointerTo and - * ThreadSafeConstPointerTo. Don't try to use it directly; use either derived - * class instead. - */ -template -class ThreadSafePointerToBase : public PointerToVoid { -public: - typedef T To; - -protected: - INLINE ThreadSafePointerToBase(To *ptr); - INLINE ThreadSafePointerToBase(const ThreadSafePointerToBase ©); - INLINE ~ThreadSafePointerToBase(); - - INLINE void reassign(To *ptr); - INLINE void reassign(const ThreadSafePointerToBase ©); - - void update_type(To *ptr); - - // No assignment or retrieval functions are declared in - // ThreadSafePointerToBase, because we will have to specialize on const vs. - // non-const later. - -PUBLISHED: - INLINE void clear(); - - void output(std::ostream &out) const; -}; - -template -INLINE std::ostream &operator <<(std::ostream &out, const ThreadSafePointerToBase &pointer) { - pointer.output(out); - return out; -} - -#include "threadSafePointerToBase.I" - -#endif diff --git a/panda/src/ffmpeg/config_ffmpeg.cxx b/panda/src/ffmpeg/config_ffmpeg.cxx index 0dbe147d27..fddd9bb81d 100644 --- a/panda/src/ffmpeg/config_ffmpeg.cxx +++ b/panda/src/ffmpeg/config_ffmpeg.cxx @@ -17,8 +17,8 @@ #include "ffmpegVideoCursor.h" #include "ffmpegAudio.h" #include "ffmpegAudioCursor.h" - #include "movieTypeRegistry.h" +#include "pandaSystem.h" extern "C" { #include @@ -129,6 +129,9 @@ init_libffmpeg() { FfmpegVideo::register_with_read_factory(); FfmpegVideoCursor::register_with_read_factory(); + PandaSystem *ps = PandaSystem::get_global_ptr(); + ps->add_system("FFmpeg"); + // Register ffmpeg as catch-all audiovideo type. MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr(); reg->register_audio_type(&FfmpegAudio::make, "*"); diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index bdeb26345f..b85a3e76c1 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -659,6 +659,10 @@ rebuild_bitplanes() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } + + // Mark the GSG as supporting multisampling, so that it will respect an + // AntialiasAttrib with mode M_multisample. + glgsg->_supports_multisample = true; } else { glDisable(GL_MULTISAMPLE); } diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index a2ac21cbc4..5a7eda65fc 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2462,7 +2462,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { } GLint p = bind._id._seqno; - max_p = max(max_p, p + 1); + max_p = max(max_p, p + bind._elements); // Don't apply vertex colors if they are disabled with a ColorAttrib. int num_elements, element_stride, divisor; diff --git a/panda/src/gobj/lens.cxx b/panda/src/gobj/lens.cxx index f66f2ba6b9..6fabc0cf8f 100644 --- a/panda/src/gobj/lens.cxx +++ b/panda/src/gobj/lens.cxx @@ -1221,8 +1221,8 @@ do_project(const CData *cdata, const LPoint3 &point3d, LPoint3 &point2d) const { point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3); return (full[3] > 0.0f) && - (point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) && - (point2d[1] >= -1.0f) && (point2d[1] <= 1.0f); + (point2d[0] >= -1.0f - NEARLY_ZERO(PN_stdfloat)) && (point2d[0] <= 1.0f + NEARLY_ZERO(PN_stdfloat)) && + (point2d[1] >= -1.0f - NEARLY_ZERO(PN_stdfloat)) && (point2d[1] <= 1.0f + NEARLY_ZERO(PN_stdfloat)); } /** diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index e8078f7b68..3aaaccc8b0 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -30,6 +30,7 @@ #include "renderAttrib.h" #include "shaderInput.h" #include "boundingBox.h" +#include "boundingSphere.h" #include "samplerState.h" #include "config_grutil.h" #include "typeHandle.h" @@ -106,8 +107,6 @@ ShaderTerrainMesh::ShaderTerrainMesh() : _update_enabled(true), _heightfield_tex(nullptr) { - set_final(true); - set_bounds(new OmniBoundingVolume()); } /** @@ -563,6 +562,64 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat _basic_collector.stop(); } +/** + * This is used to support NodePath::calc_tight_bounds(). It is not intended + * to be called directly, and it has nothing to do with the normal Panda + * bounding-volume computation. + * + * If the node contains any geometry, this updates min_point and max_point to + * enclose its bounding box. found_any is to be set true if the node has any + * geometry at all, or left alone if it has none. This method may be called + * over several nodes, so it may enter with min_point, max_point, and + * found_any already set. + */ +CPT(TransformState) ShaderTerrainMesh:: +calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, + const TransformState *transform, Thread *current_thread) const { + CPT(TransformState) next_transform = + PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, + current_thread); + + const LMatrix4 &mat = next_transform->get_mat(); + LPoint3 terrain_min_point = LPoint3(0, 0, 0) * mat; + LPoint3 terrain_max_point = LPoint3(1, 1, 1) * mat; + if (!found_any) { + min_point = terrain_min_point; + max_point = terrain_max_point; + found_any = true; + } else { + min_point = min_point.fmin(terrain_min_point); + max_point = max_point.fmax(terrain_max_point); + } + + return next_transform; +} + +/** + * Returns a newly-allocated BoundingVolume that represents the internal + * contents of the node. Should be overridden by PandaNode classes that + * contain something internally. + */ +void ShaderTerrainMesh:: +compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const { + + BoundingVolume::BoundsType btype = get_bounds_type(); + if (btype == BoundingVolume::BT_default) { + btype = bounds_type; + } + + if (btype == BoundingVolume::BT_sphere) { + internal_bounds = new BoundingSphere(LPoint3(0.5, 0.5, 0.5), csqrt(0.75)); + } else { + internal_bounds = new BoundingBox(LPoint3(0, 0, 0), LPoint3(1, 1, 1)); + } + + internal_vertices = 0; +} + /** * @brief Traverses the quadtree * @details This method traverses the given chunk, deciding whether it should diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index 9c4f8d9a3a..9b08e12c0d 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -91,6 +91,16 @@ public: virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); private: + virtual CPT(TransformState) + calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, + bool &found_any, + const TransformState *transform, + Thread *current_thread = Thread::get_current_thread()) const; + + virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const; // Chunk data struct Chunk { diff --git a/panda/src/mathutil/boundingBox.I b/panda/src/mathutil/boundingBox.I index 2bb67a500e..f0b12f8dfd 100644 --- a/panda/src/mathutil/boundingBox.I +++ b/panda/src/mathutil/boundingBox.I @@ -101,7 +101,7 @@ get_plane(int n) const { INLINE_MATHUTIL void BoundingBox:: set_min_max(const LPoint3 &min, const LPoint3 &max) { nassertv(!min.is_nan() && !max.is_nan()); - nassertv(_min[0] <= _max[0] && _min[1] <= _max[1] && _min[2] <= _max[2]); + nassertv(min[0] <= max[0] && min[1] <= max[1] && min[2] <= max[2]); _min = min; _max = max; _flags = 0; diff --git a/panda/src/putil/uniqueIdAllocator.cxx b/panda/src/putil/uniqueIdAllocator.cxx index 6c4edd9795..19e3a8ee5a 100644 --- a/panda/src/putil/uniqueIdAllocator.cxx +++ b/panda/src/putil/uniqueIdAllocator.cxx @@ -173,6 +173,20 @@ initial_reserve_id(uint32_t id) { --_free; } +/** + * Checks the allocated state of an index. Returns true for + * indices that are currently allocated and in use. + */ +bool UniqueIdAllocator:: +is_allocated(uint32_t id) { + if (id < _min || id > _max) { + // This id is out of range, not allocated. + return false; + } + + uint32_t index = id - _min; // Convert to _table index. + return _table[index] == IndexAllocated; +} /** * Free an allocated index (index must be between _min and _max that were diff --git a/panda/src/putil/uniqueIdAllocator.h b/panda/src/putil/uniqueIdAllocator.h index 50db80b0b0..2f3e3b9e8f 100644 --- a/panda/src/putil/uniqueIdAllocator.h +++ b/panda/src/putil/uniqueIdAllocator.h @@ -43,6 +43,8 @@ PUBLISHED: uint32_t allocate(); void initial_reserve_id(uint32_t id); + bool is_allocated(uint32_t index); + void free(uint32_t index); PN_stdfloat fraction_used() const; diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index 12079da46d..707694a8ca 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -75,6 +75,10 @@ static struct _inittab extensions[] = { #endif #endif +#ifdef _WIN32 +static wchar_t *log_pathw = NULL; +#endif + #if defined(_WIN32) && PY_VERSION_HEX < 0x03060000 static int supports_code_page(UINT cp) { if (cp == 0) { @@ -225,7 +229,8 @@ static int mkdir_parent(const char *path) { static int setup_logging(const char *path, int append) { #ifdef _WIN32 // Does it start with a tilde? Perform tilde expansion if so. - wchar_t pathw[MAX_PATH * 2]; + wchar_t *pathw = (wchar_t *)malloc(sizeof(wchar_t) * MAX_PATH); + pathw[0] = 0; size_t offset = 0; if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) { // Strip off the tilde. @@ -233,6 +238,7 @@ static int setup_logging(const char *path, int append) { // Get the home directory path for the current user. if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) { + free(pathw); return 0; } offset = wcslen(pathw); @@ -240,26 +246,30 @@ static int setup_logging(const char *path, int append) { // We need to convert the rest of the path from UTF-8 to UTF-16. if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset, - (int)(_countof(pathw) - offset)) == 0) { + (int)(MAX_PATH - offset)) == 0) { + free(pathw); return 0; } DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE); int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS; - HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { // Make the parent directories first. mkdir_parent(pathw); - handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ, + handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); } if (handle == INVALID_HANDLE_VALUE) { + free(pathw); return 0; } + log_pathw = pathw; + if (append) { SetFilePointer(handle, 0, NULL, FILE_END); } @@ -282,7 +292,7 @@ static int setup_logging(const char *path, int append) { // Now replace the stdout and stderr file descriptors with one pointing to // our desired handle. - int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | (append ? _O_APPEND : 0)); + int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | _O_APPEND); _dup2(fd, _fileno(stdout)); _dup2(fd, _fileno(stderr)); _close(fd); @@ -421,6 +431,32 @@ int Py_FrozenMain(int argc, char **argv) #endif #if defined(MS_WINDOWS) && PY_VERSION_HEX < 0x03040000 + /* We can't rely on our overriding of the standard I/O to work on older + * versions of Python, since they are compiled with an incompatible CRT. + * The best solution I've found was to just replace sys.stdout/stderr with + * the log file reopened in append mode (which requires not locking it for + * write, and also passing in _O_APPEND above, and disabling buffering). + * It's not the most elegant solution, but it's better than crashing. */ +#if PY_MAJOR_VERSION < 3 + if (log_pathw != NULL) { + PyObject *uniobj = PyUnicode_FromWideChar(log_pathw, (Py_ssize_t)wcslen(log_pathw)); + PyObject *file = PyObject_CallFunction((PyObject*)&PyFile_Type, "Nsi", uniobj, "a", 0); + + if (file != NULL) { + PyFile_SetEncodingAndErrors(file, "utf-8", NULL); + + PySys_SetObject("stdout", file); + PySys_SetObject("stderr", file); + PySys_SetObject("__stdout__", file); + PySys_SetObject("__stderr__", file); + + /* Be sure to disable buffering, otherwise we'll get overlap */ + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); + } + } + else +#endif if (!supports_code_page(GetConsoleOutputCP()) || !supports_code_page(GetConsoleCP())) { /* Same hack as before except for Python 2.7, which doesn't seem to have diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx index 7818ea5ed8..e40830e189 100644 --- a/pandatool/src/win-stats/winStats.cxx +++ b/pandatool/src/win-stats/winStats.cxx @@ -16,6 +16,9 @@ #include "winStatsServer.h" #include "config_pstatclient.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include static const char *toplevel_class_name = "pstats"; diff --git a/pandatool/src/win-stats/winStatsChartMenu.h b/pandatool/src/win-stats/winStatsChartMenu.h index cc372f0a80..d96a807965 100644 --- a/pandatool/src/win-stats/winStatsChartMenu.h +++ b/pandatool/src/win-stats/winStatsChartMenu.h @@ -16,6 +16,9 @@ #include "pandatoolbase.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h index a4f9a61467..82fb04f71d 100644 --- a/pandatool/src/win-stats/winStatsGraph.h +++ b/pandatool/src/win-stats/winStatsGraph.h @@ -18,6 +18,9 @@ #include "winStatsLabelStack.h" #include "pmap.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsLabel.h b/pandatool/src/win-stats/winStatsLabel.h index 0317f0dcfb..5642cdb8d0 100644 --- a/pandatool/src/win-stats/winStatsLabel.h +++ b/pandatool/src/win-stats/winStatsLabel.h @@ -16,6 +16,9 @@ #include "pandatoolbase.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsLabelStack.h b/pandatool/src/win-stats/winStatsLabelStack.h index afb8ecf6c7..e7fd1da41f 100644 --- a/pandatool/src/win-stats/winStatsLabelStack.h +++ b/pandatool/src/win-stats/winStatsLabelStack.h @@ -17,6 +17,9 @@ #include "pandatoolbase.h" #include "pvector.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsLabel; diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h index de74aa0d68..c70a71507e 100644 --- a/pandatool/src/win-stats/winStatsMonitor.h +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -23,6 +23,9 @@ #include "pvector.h" #include "pmap.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsServer; diff --git a/pandatool/src/win-stats/winStatsPianoRoll.h b/pandatool/src/win-stats/winStatsPianoRoll.h index b0c9410003..b130c99b18 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.h +++ b/pandatool/src/win-stats/winStatsPianoRoll.h @@ -20,6 +20,9 @@ #include "pStatPianoRoll.h" #include "pointerTo.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h index 59953fbe8c..9e6f91667f 100644 --- a/pandatool/src/win-stats/winStatsStripChart.h +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -20,6 +20,9 @@ #include "pStatStripChart.h" #include "pointerTo.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif #include class WinStatsMonitor; diff --git a/samples/fireflies/main.py b/samples/fireflies/main.py index 41527beb88..f5f7c2e2a1 100755 --- a/samples/fireflies/main.py +++ b/samples/fireflies/main.py @@ -65,7 +65,7 @@ class FireflyDemo(ShowBase): # doesn't support the necessary OpenGL extensions. if self.modelbuffer is None or self.lightbuffer is None: - self.t = addTitle("Toon Shader: Video driver does not support " + self.t = addTitle("Firefly Demo: Video driver does not support " "multiple render targets") return diff --git a/samples/particles/advanced.py b/samples/particles/advanced.py new file mode 100755 index 0000000000..1521c1453d --- /dev/null +++ b/samples/particles/advanced.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python + +# This program shows a shader-based particle system. With this approach, you +# can define an inertial particle system with a moving emitter whose position +# can not be pre-determined. + +from array import array +from itertools import chain +from random import uniform +from math import pi, sin, cos +from panda3d.core import TextNode +from panda3d.core import AmbientLight, DirectionalLight +from panda3d.core import LVector3 +from panda3d.core import NodePath +from panda3d.core import GeomPoints +from panda3d.core import GeomEnums +from panda3d.core import GeomVertexFormat +from panda3d.core import GeomVertexData +from panda3d.core import GeomNode +from panda3d.core import Geom +from panda3d.core import OmniBoundingVolume +from panda3d.core import Texture +from panda3d.core import TextureStage +from panda3d.core import TexGenAttrib +from panda3d.core import Shader +from panda3d.core import ShaderAttrib +from panda3d.core import loadPrcFileData +from direct.showbase.ShowBase import ShowBase +from direct.gui.OnscreenText import OnscreenText +import sys + +HELP_TEXT = """ +left/right arrow: Rotate teapot +ESC: Quit +""" + +# We need to use GLSL 1.50 for these, and some drivers (notably Mesa) require +# us to explicitly ask for an OpenGL 3.2 context in that case. +config = """ +gl-version 3 2 +""" + +vert = """ +#version 150 +#extension GL_ARB_shader_image_load_store : require + +layout(rgba32f) uniform imageBuffer positions; // current positions +layout(rgba32f) uniform imageBuffer start_vel; // emission velocities +layout(rgba32f) uniform imageBuffer velocities; // current velocities +layout(rgba32f) uniform imageBuffer emission_times; // emission times +uniform mat4 p3d_ModelViewProjectionMatrix; +uniform vec3 emitter_pos; // emitter's position +uniform vec3 accel; // the acceleration of the particles +uniform float osg_FrameTime; // time of the current frame (absolute) +uniform float osg_DeltaFrameTime;// time since last frame +uniform float start_time; // particle system's start time (absolute) +uniform float part_duration; // single particle's duration + +out float from_emission; // time from specific particle's emission +out vec4 color; + +void main() { + float emission_time = imageLoad(emission_times, gl_VertexID).x; + vec4 pos = imageLoad(positions, gl_VertexID); + vec4 vel = imageLoad(velocities, gl_VertexID); + float from_start = osg_FrameTime - start_time; // time from system's start + from_emission = 0; + color = vec4(1); + if (from_start > emission_time) { // we've to show the particle + from_emission = from_start - emission_time; + if (from_emission <= osg_DeltaFrameTime + .01) { + // it's particle's emission frame: let's set its position at the + // emitter's position and set the initial velocity + pos = vec4(emitter_pos, 1); + vel = imageLoad(start_vel, gl_VertexID); + } + pos += vec4((vel * osg_DeltaFrameTime).xyz, 0); + vel += vec4(accel, 0) * osg_DeltaFrameTime; + } else color = vec4(0); + + // update the emission time (for particle recycling) + if (from_start >= emission_time + part_duration) { + imageStore(emission_times, gl_VertexID, vec4(from_start, 0, 0, 1)); + } + gl_PointSize = 10; + gl_Position = p3d_ModelViewProjectionMatrix * pos; + imageStore(positions, gl_VertexID, pos); + imageStore(velocities, gl_VertexID, vel); +} +""" + +frag = """ +#version 150 + +in float from_emission; // time elapsed from particle's emission +in vec4 color; +uniform float part_duration; // single particle's duration +uniform sampler2D image; // particle's texture +out vec4 p3d_FragData[1]; + +void main() { + vec4 col = texture(image, gl_PointCoord) * color; + // fade the particle considering the time from its emission + float alpha = clamp(1 - from_emission / part_duration, 0, 1); + p3d_FragData[0] = vec4(col.rgb, col.a * alpha); +} +""" + + +class Particle: + + def __init__( + self, + emitter, # the node which is emitting + texture, # particle's image + rate=.001, # the emission rate + gravity=-9.81, # z-component of the gravity force + vel=1.0, # length of emission vector + partDuration=1.0 # single particle's duration + ): + self.__emitter = emitter + self.__texture = texture + # let's compute the total number of particles + self.__numPart = int(round(partDuration * 1 / rate)) + self.__rate = rate + self.__gravity = gravity + self.__vel = vel + self.__partDuration = partDuration + self.__nodepath = render.attachNewNode(self.__node()) + self.__nodepath.setTransparency(True) # particles have alpha + self.__nodepath.setBin("fixed", 0) # render it at the end + self.__setTextures() + self.__setShader() + self.__nodepath.setRenderModeThickness(10) # we want sprite particles + self.__nodepath.setTexGen(TextureStage.getDefault(), + TexGenAttrib.MPointSprite) + self.__nodepath.setDepthWrite(False) # don't sort the particles + self.__upd_tsk = taskMgr.add(self.__update, "update") + + def __node(self): + # this function creates and returns particles' GeomNode + points = GeomPoints(GeomEnums.UH_static) + points.addNextVertices(self.__numPart) + format_ = GeomVertexFormat.getEmpty() + geom = Geom(GeomVertexData("abc", format_, GeomEnums.UH_static)) + geom.addPrimitive(points) + geom.setBounds(OmniBoundingVolume()) # always render it + node = GeomNode("node") + node.addGeom(geom) + return node + + def __setTextures(self): + # initial positions are all zeros (each position is denoted by 4 values) + # positions are stored in a texture + positions = [(0, 0, 0, 1) for i in range(self.__numPart)] + posLst = list(chain.from_iterable(positions)) + self.__texPos = self.__buffTex(posLst) + + # define emission times' texture + emissionTimes = [(self.__rate * i, 0, 0, 0) + for i in range(self.__numPart)] + timesLst = list(chain.from_iterable(emissionTimes)) + self.__texTimes = self.__buffTex(timesLst) + + # define a list with emission velocities + velocities = [self.__rndVel() for _ in range(self.__numPart)] + velLst = list(chain.from_iterable(velocities)) + # we need two textures, + # the first one contains the emission velocity (we need to keep it for + # particle recycling)... + self.__texStartVel = self.__buffTex(velLst) + # ... and the second one contains the current velocities + self.__texCurrVel = self.__buffTex(velLst) + + def __buffTex(self, values): + # this function returns a buffer texture with the received values + data = array("f", values) + tex = Texture("tex") + tex.setupBufferTexture(self.__numPart, Texture.T_float, + Texture.F_rgba32, GeomEnums.UH_static) + tex.setRamImage(data) + return tex + + def __rndVel(self): + # this method returns a random vector for emitting the particle + theta = uniform(0, pi / 12) + phi = uniform(0, 2 * pi) + vec = LVector3( + sin(theta) * cos(phi), + sin(theta) * sin(phi), + cos(theta)) + vec *= uniform(self.__vel * .8, self.__vel * 1.2) + return [vec.x, vec.y, vec.z, 1] + + def __setShader(self): + shader = Shader.make(Shader.SL_GLSL, vert, frag) + + # Apply the shader to the node, but set a special flag indicating that + # the point size is controlled bythe shader. + attrib = ShaderAttrib.make(shader) + attrib = attrib.setFlag(ShaderAttrib.F_shader_point_size, True) + self.__nodepath.setAttrib(attrib) + + self.__nodepath.setShaderInputs( + positions=self.__texPos, + emitter_pos=self.__emitter.getPos(render), + start_vel=self.__texStartVel, + velocities=self.__texCurrVel, + accel=(0, 0, self.__gravity), + start_time=globalClock.getFrameTime(), + emission_times=self.__texTimes, + part_duration=self.__partDuration, + image=loader.loadTexture(self.__texture)) + + def __update(self, task): + pos = self.__emitter.getPos(render) + self.__nodepath.setShaderInput("emitter_pos", pos) + return task.again + + +class ParticleDemo(ShowBase): + + def __init__(self): + loadPrcFileData("config", config) + + ShowBase.__init__(self) + + # Standard title and instruction text + self.title = OnscreenText( + text="Panda3D: Tutorial - Shader-based Particles", + parent=base.a2dBottomCenter, + style=1, fg=(1, 1, 1, 1), pos=(0, 0.1), scale=.08) + self.escapeEvent = OnscreenText( + text=HELP_TEXT, parent=base.a2dTopLeft, + style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.06), + align=TextNode.ALeft, scale=.05) + + # More standard initialization + self.accept('escape', sys.exit) + self.accept('arrow_left', self.rotate, ['left']) + self.accept('arrow_right', self.rotate, ['right']) + base.disableMouse() + base.camera.setPos(0, -20, 2) + base.setBackgroundColor(0, 0, 0) + + self.teapot = loader.loadModel("teapot") + self.teapot.setPos(0, 10, 0) + self.teapot.reparentTo(render) + self.setupLights() + + # we define a nodepath as particle's emitter + self.emitter = NodePath("emitter") + self.emitter.reparentTo(self.teapot) + self.emitter.setPos(3.000, 0.000, 2.550) + + # let's create the particle system + Particle(self.emitter, "smoke.png", gravity=.01, vel=1.2, + partDuration=5.0) + + def rotate(self, direction): + direction_factor = (1 if direction == "left" else -1) + self.teapot.setH(self.teapot.getH() + 10 * direction_factor) + + # Set up lighting + def setupLights(self): + ambientLight = AmbientLight("ambientLight") + ambientLight.setColor((.4, .4, .35, 1)) + directionalLight = DirectionalLight("directionalLight") + directionalLight.setDirection(LVector3(0, 8, -2.5)) + directionalLight.setColor((0.9, 0.8, 0.9, 1)) + + # Set lighting on teapot so steam doesn't get affected + self.teapot.setLight(self.teapot.attachNewNode(directionalLight)) + self.teapot.setLight(self.teapot.attachNewNode(ambientLight)) + + +demo = ParticleDemo() +demo.run() diff --git a/samples/roaming-ralph/main.py b/samples/roaming-ralph/main.py index 0290c3e74b..93ed173d5b 100755 --- a/samples/roaming-ralph/main.py +++ b/samples/roaming-ralph/main.py @@ -12,6 +12,7 @@ from direct.showbase.ShowBase import ShowBase from panda3d.core import CollisionTraverser, CollisionNode from panda3d.core import CollisionHandlerQueue, CollisionRay +from panda3d.core import CollisionHandlerPusher, CollisionSphere from panda3d.core import Filename, AmbientLight, DirectionalLight from panda3d.core import PandaNode, NodePath, Camera, TextNode from panda3d.core import CollideMask @@ -40,12 +41,15 @@ class RoamingRalphDemo(ShowBase): # Set up the window, camera, etc. ShowBase.__init__(self) - # Set the background color to black - self.win.setClearColor((0, 0, 0, 1)) - # This is used to store which keys are currently pressed. self.keyMap = { - "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} + "left": 0, + "right": 0, + "forward": 0, + "backward": 0, + "cam-left": 0, + "cam-right": 0, + } # Post the instructions self.title = addTitle( @@ -54,8 +58,9 @@ class RoamingRalphDemo(ShowBase): self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") - self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") - self.inst7 = addInstructions(0.36, "[S]: Rotate Camera Right") + self.inst5 = addInstructions(0.30, "[Down Arrow]: Walk Ralph Backward") + self.inst6 = addInstructions(0.36, "[A]: Rotate Camera Left") + self.inst7 = addInstructions(0.42, "[S]: Rotate Camera Right") # Set up the environment # @@ -72,6 +77,9 @@ class RoamingRalphDemo(ShowBase): self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) + # We do not have a skybox, so we will just use a sky blue background color + self.setBackgroundColor(0.53, 0.80, 0.92, 1) + # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() @@ -80,7 +88,7 @@ class RoamingRalphDemo(ShowBase): "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) - self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) + self.ralph.setPos(ralphStartPos + (0, 0, 1.5)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. @@ -95,31 +103,51 @@ class RoamingRalphDemo(ShowBase): self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) + self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) + self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) taskMgr.add(self.move, "moveTask") - # Game state variables - self.isMoving = False - # Set up the camera self.disableMouse() self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) + self.cTrav = CollisionTraverser() + + # Use a CollisionHandlerPusher to handle collisions between Ralph and + # the environment. Ralph is added as a "from" object which will be + # "pushed" out of the environment if he walks into obstacles. + # + # Ralph is composed of two spheres, one around the torso and one + # around the head. They are slightly oversized since we want Ralph to + # keep some distance from obstacles. + self.ralphCol = CollisionNode('ralph') + self.ralphCol.addSolid(CollisionSphere(center=(0, 0, 2), radius=1.5)) + self.ralphCol.addSolid(CollisionSphere(center=(0, -0.25, 4), radius=1.5)) + self.ralphCol.setFromCollideMask(CollideMask.bit(0)) + self.ralphCol.setIntoCollideMask(CollideMask.allOff()) + self.ralphColNp = self.ralph.attachNewNode(self.ralphCol) + self.ralphPusher = CollisionHandlerPusher() + self.ralphPusher.horizontal = True + + # Note that we need to add ralph both to the pusher and to the + # traverser; the pusher needs to know which node to push back when a + # collision occurs! + self.ralphPusher.addCollider(self.ralphColNp, self.ralph) + self.cTrav.addCollider(self.ralphColNp, self.ralphPusher) + # We will detect the height of the terrain by creating a collision # ray and casting it downward toward the terrain. One ray will # start above ralph's head, and the other will start above the camera. # A ray may hit the terrain, or it may hit a rock or a tree. If it - # hits the terrain, we can detect the height. If it hits anything - # else, we rule that the move is illegal. - self.cTrav = CollisionTraverser() - + # hits the terrain, we can detect the height. self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setDirection(0, 0, -1) @@ -143,7 +171,7 @@ class RoamingRalphDemo(ShowBase): self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays - #self.ralphGroundColNp.show() + #self.ralphColNp.show() #self.camGroundColNp.show() # Uncomment this line to show a visual representation of the @@ -181,11 +209,6 @@ class RoamingRalphDemo(ShowBase): if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) - # save ralph's initial position so that we can restore it, - # in case he falls off the map or runs into something. - - startpos = self.ralph.getPos() - # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: @@ -193,17 +216,28 @@ class RoamingRalphDemo(ShowBase): if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) if self.keyMap["forward"]: - self.ralph.setY(self.ralph, -25 * dt) + self.ralph.setY(self.ralph, -20 * dt) + if self.keyMap["backward"]: + self.ralph.setY(self.ralph, +10 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. + currentAnim = self.ralph.getCurrentAnim() - if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"]: - if self.isMoving is False: + if self.keyMap["forward"]: + if currentAnim != "run": self.ralph.loop("run") - self.isMoving = True + elif self.keyMap["backward"]: + # Play the walk animation backwards. + if currentAnim != "walk": + self.ralph.loop("walk") + self.ralph.setPlayRate(-1.0, "walk") + elif self.keyMap["left"] or self.keyMap["right"]: + if currentAnim != "walk": + self.ralph.loop("walk") + self.ralph.setPlayRate(1.0, "walk") else: - if self.isMoving: + if currentAnim is not None: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False @@ -228,25 +262,24 @@ class RoamingRalphDemo(ShowBase): #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, - # update his Z. If it hit anything else, or didn't hit anything, put - # him back where he was last frame. + # update his Z - entries = list(self.ralphGroundHandler.getEntries()) + entries = list(self.ralphGroundHandler.entries) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) - if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": - self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) - else: - self.ralph.setPos(startpos) + for entry in entries: + if entry.getIntoNode().getName() == "terrain": + self.ralph.setZ(entry.getSurfacePoint(render).getZ()) - # Keep the camera at one foot above the terrain, - # or two feet above ralph, whichever is greater. + # Keep the camera at one unit above the terrain, + # or two units above ralph, whichever is greater. - entries = list(self.camGroundHandler.getEntries()) + entries = list(self.camGroundHandler.entries) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) - if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": - self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) + for entry in entries: + if entry.getIntoNode().getName() == "terrain": + self.camera.setZ(entry.getSurfacePoint(render).getZ() + 1.5) if self.camera.getZ() < self.ralph.getZ() + 2.0: self.camera.setZ(self.ralph.getZ() + 2.0) diff --git a/samples/roaming-ralph/models/world.egg.pz b/samples/roaming-ralph/models/world.egg.pz index 9ab9439f74..9a1d67ea03 100644 Binary files a/samples/roaming-ralph/models/world.egg.pz and b/samples/roaming-ralph/models/world.egg.pz differ diff --git a/samples/shader-terrain/main.py b/samples/shader-terrain/main.py index 10bffb3767..3d88331ae9 100644 --- a/samples/shader-terrain/main.py +++ b/samples/shader-terrain/main.py @@ -20,6 +20,14 @@ class ShaderTerrainDemo(ShowBase): textures-power-2 none gl-coordinate-system default window-title Panda3D ShaderTerrainMesh Demo + + # As an optimization, set this to the maximum number of cameras + # or lights that will be rendering the terrain at any given time. + stm-max-views 8 + + # Further optimize the performance by reducing this to the max + # number of chunks that will be visible at any given time. + stm-max-chunk-count 2048 """) # Initialize the showbase diff --git a/tests/collide/collisions.py b/tests/collide/collisions.py new file mode 100644 index 0000000000..8a48a18d5c --- /dev/null +++ b/tests/collide/collisions.py @@ -0,0 +1,30 @@ +from panda3d.core import CollisionNode, NodePath +from panda3d.core import CollisionTraverser, CollisionHandlerQueue +from panda3d.core import CollisionSphere, CollisionBox, CollisionPolygon, CollisionCapsule +from panda3d.core import CollisionLine, CollisionRay, CollisionSegment, CollisionParabola +from panda3d.core import CollisionPlane +from panda3d.core import Point3, Vec3, Plane, LParabola + + +def make_collision(solid_from, solid_into): + node_from = CollisionNode("from") + node_from.add_solid(solid_from) + node_into = CollisionNode("into") + node_into.add_solid(solid_into) + + root = NodePath("root") + trav = CollisionTraverser() + queue = CollisionHandlerQueue() + + np_from = root.attach_new_node(node_from) + np_into = root.attach_new_node(node_into) + + trav.add_collider(np_from, queue) + trav.traverse(root) + + entry = None + for e in queue.get_entries(): + if e.get_into() == solid_into: + entry = e + + return (entry, np_from, np_into) diff --git a/tests/collide/test_into_box.py b/tests/collide/test_into_box.py new file mode 100644 index 0000000000..5cda4c51a2 --- /dev/null +++ b/tests/collide/test_into_box.py @@ -0,0 +1,45 @@ +from collisions import * + + +def test_sphere_into_box(): + sphere = CollisionSphere(0, 0, 4, 3) + box = CollisionBox((0, 0, 0), 2, 3, 4) + entry = make_collision(sphere, box)[0] + assert entry is not None + assert entry.get_from() == sphere + assert entry.get_into() == box + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 10, 6), box) + assert entry.get_surface_point(np_from) == Point3(0, 0, 4) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(100, 100, 100, 100), box)[0] + assert entry is None + + +def test_plane_into_box(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + box = CollisionBox((0, 0, 0), 2, 3, 4) + + entry = make_collision(plane, box)[0] + assert entry is None + + +def test_ray_into_box(): + ray = CollisionRay(1, 1, 1, 0, 1, 0) + box = CollisionBox((0, 0, 0), 3, 3, 5) + entry = make_collision(ray, box)[0] + assert entry is not None + assert entry.get_from() == ray + assert entry.get_into() == box + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionRay(3, 3, 0, 1, -1, 0), box) + assert entry.get_surface_point(np_from) == Point3(3, 3, 0) + + # No collision + entry = make_collision(CollisionRay(0, 0, 100, 1, 0, 0), box)[0] + assert entry is None diff --git a/tests/collide/test_into_lines.py b/tests/collide/test_into_lines.py new file mode 100644 index 0000000000..9787558a3a --- /dev/null +++ b/tests/collide/test_into_lines.py @@ -0,0 +1,24 @@ +# Testing that all variants of CollisionLine +# cannot be used as "into" objects +from collisions import * + + +def test_sphere_into_line(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionLine(0, 0, 0, 1, 0, 0))[0] + assert entry is None + + +def test_sphere_into_ray(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionRay(0, 0, 0, 3, 3, 3))[0] + assert entry is None + + +def test_sphere_into_segment(): + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionSegment(0, 0, 0, 3, 3, 3))[0] + assert entry is None + + +def test_sphere_into_parabola(): + parabola = LParabola((1, 0, 0), (0, 1, 0), (0, 0, 1)) + entry = make_collision(CollisionSphere(0, 0, 0, 3), CollisionParabola(parabola, 1, 2))[0] + assert entry is None diff --git a/tests/collide/test_into_poly.py b/tests/collide/test_into_poly.py new file mode 100644 index 0000000000..ccfd15fcd1 --- /dev/null +++ b/tests/collide/test_into_poly.py @@ -0,0 +1,48 @@ +from collisions import * + + +def test_box_into_poly(): + box = CollisionBox((0, 0, 0), 2, 3, 4) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(box, poly)[0] + assert entry is not None + assert entry.get_from() == box + assert entry.get_into() == poly + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionBox((0, 3, 0), 1, 2, 1), poly) + assert entry.get_surface_point(np_from) == Point3(0, 3, 0) + assert entry.get_surface_normal(np_into) == Vec3(-1, 0, 0) # Testing surface normal + + # No collision + entry = make_collision(CollisionBox((10, 10, 10), 8, 9, 10), poly)[0] + assert entry is None + + +def test_sphere_into_poly(): + sphere = CollisionSphere(0, 0, 0, 1) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(sphere, poly)[0] + assert entry is not None + assert entry.get_from() == sphere + assert entry.get_into() == poly + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 3, 2), poly) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(-1, 0, 0) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(100, 100, 100, 100), poly)[0] + assert entry is None + + +def test_plane_into_poly(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + poly = CollisionPolygon(Point3(0, 0, 0), Point3(0, 0, 1), Point3(0, 1, 1), Point3(0, 1, 0)) + + entry = make_collision(plane, poly)[0] + assert entry is None diff --git a/tests/collide/test_into_sphere.py b/tests/collide/test_into_sphere.py new file mode 100644 index 0000000000..b59764d871 --- /dev/null +++ b/tests/collide/test_into_sphere.py @@ -0,0 +1,89 @@ +from collisions import * + + +def test_sphere_into_sphere(): + sphere1 = CollisionSphere(0, 0, 3, 3) + sphere2 = CollisionSphere(0, 0, 0, 3) + + entry = make_collision(sphere1, sphere2)[0] + assert entry is not None + assert entry.get_from() == sphere1 + assert entry.get_into() == sphere2 + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionSphere(0, 0, 10, 7), sphere2) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionSphere(0, 0, 10, 6), sphere2)[0] + assert entry is None + + +def test_box_into_sphere(): + box = CollisionBox((0, 0, 0), 2, 3, 4) + sphere = CollisionSphere(0, 0, 0, 3) + + entry = make_collision(box, sphere)[0] + assert entry is not None + assert entry.get_from() == box + assert entry.get_into() == sphere + + # Colliding just on the edge + entry, np_from, np_into = make_collision(CollisionBox((0, 0, 10), 6, 6, 7), sphere) + assert entry.get_surface_point(np_from) == Point3(0, 0, 3) + assert entry.get_surface_normal(np_into) == Vec3(0, 0, 1) # Testing surface normal + + # No collision + entry = make_collision(CollisionBox((0, 0, 10), 6, 6, 6), sphere)[0] + assert entry is None + + +def test_capsule_into_sphere(): + # First test a sphere that is fully touching the inner line of the capsule + capsule = CollisionCapsule((0, 0, 1.0), (10, 0, 1.0), 1.0) + sphere = CollisionSphere(5, 0, 1.5, 1.0) + + entry = make_collision(capsule, sphere)[0] + assert entry is not None + assert entry.get_from() == capsule + assert entry.get_into() == sphere + + # Now test one that merely grazes. + entry = make_collision(CollisionCapsule((0, 0, 0), (10, 0, 0), 1.0), sphere)[0] + assert entry is not None + + # No collision + entry = make_collision(CollisionCapsule((0, 0, 0), (10, 0, 0), 0.25), sphere)[0] + assert entry is None + + # Degenerate case: capsule is actually a sphere. + entry = make_collision(CollisionCapsule((5, 0, 0), (5, 0, 0), 1.0), sphere)[0] + assert entry is not None + + # Degenerate case, but not colliding. + entry = make_collision(CollisionCapsule((5, 0, 0), (5, 0, 0), 0.25), sphere)[0] + assert entry is None + + +def test_segment_into_sphere(): + segment = CollisionSegment((0, 0, 0), (10, 0, 0)) + sphere = CollisionSphere(5, 0, 0.5, 1.0) + + entry = make_collision(segment, sphere)[0] + assert entry is not None + assert entry.get_from() == segment + assert entry.get_into() == sphere + + # No collision + entry = make_collision(CollisionSegment((0, 0, 0), (3, 0, 0)), sphere)[0] + assert entry is None + + +def test_plane_into_sphere(): + # CollisionPlane is not a 'from' object + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) + sphere = CollisionSphere(0, 0, 0, 1) + + entry = make_collision(plane, sphere)[0] + assert entry is None diff --git a/tests/display/test_color_buffer.py b/tests/display/test_color_buffer.py index d95913b0f7..bb2bf3deb1 100644 --- a/tests/display/test_color_buffer.py +++ b/tests/display/test_color_buffer.py @@ -105,6 +105,12 @@ def render_color_pixel(region, state, vertex_color=None): """Renders a fragment using the specified render settings, and returns the resulting color value.""" + # Skip auto-shader tests if we don't support Cg shaders. + if not region.window.gsg.supports_basic_shaders: + sattr = state.get_attrib(core.ShaderAttrib) + if sattr and sattr.auto_shader(): + pytest.skip("Cannot test auto-shader without Cg shader support") + # Set up the scene with a blank card rendering at specified distance. scene = core.NodePath("root") scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always)) diff --git a/tests/gobj/test_lenses.py b/tests/gobj/test_lenses.py new file mode 100644 index 0000000000..a51e497c8e --- /dev/null +++ b/tests/gobj/test_lenses.py @@ -0,0 +1,77 @@ +from panda3d.core import PerspectiveLens, Point3, Point2 + + +def test_perspectivelens_extrude(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + near = Point3() + far = Point3() + + assert lens.extrude((0, 0), near, far) + assert near.almost_equal((0, 0.5, 0), 0.001) + assert far.almost_equal((0, 100, 0), 0.1) + + assert lens.extrude((-1, -1), near, far) + assert near.almost_equal((-0.5, 0.5, -0.5), 0.001) + assert far.almost_equal((-100, 100, -100), 0.1) + + assert lens.extrude((1, 0), near, far) + assert near.almost_equal((0.5, 0.5, 0), 0.001) + assert far.almost_equal((100, 100, 0), 0.1) + + +def test_perspectivelens_extrude_depth(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + point = Point3() + + assert lens.extrude_depth((0, 0, -1), point) + assert point.almost_equal((0, 0.5, 0), 0.001) + + assert lens.extrude_depth((0, 0, 1), point) + assert point.almost_equal((0, 100, 0), 0.001) + + assert lens.extrude_depth((-1, -1, -1), point) + assert point.almost_equal((-0.5, 0.5, -0.5), 0.001) + + assert lens.extrude_depth((-1, -1, 1), point) + assert point.almost_equal((-100, 100, -100), 0.1) + + assert lens.extrude_depth((1, 0, -1), point) + assert point.almost_equal((0.5, 0.5, 0), 0.001) + + assert lens.extrude_depth((1, 0, 1), point) + assert point.almost_equal((100, 100, 0), 0.1) + + +def test_perspectivelens_project(): + lens = PerspectiveLens() + lens.set_fov(90, 90) + lens.set_near_far(0.5, 100) + + point = Point2() + + assert not lens.project((0, 0, 0), point) + assert not lens.project((-1, 0.5, 0), point) + + assert lens.project((0, 0.5, 0), point) + assert point.almost_equal((0, 0), 0.001) + + assert lens.project((0, 100, 0), point) + assert point.almost_equal((0, 0), 0.001) + + assert lens.project((-0.5, 0.5, -0.5), point) + assert point.almost_equal((-1, -1), 0.001) + + assert lens.project((-100, 100, -100), point) + assert point.almost_equal((-1, -1), 0.001) + + assert lens.project((0.5, 0.5, 0), point) + assert point.almost_equal((1, 0), 0.001) + + assert lens.project((100, 100, 0), point) + assert point.almost_equal((1, 0), 0.001) diff --git a/tests/putil/conftest.py b/tests/putil/conftest.py new file mode 100644 index 0000000000..6240f0111c --- /dev/null +++ b/tests/putil/conftest.py @@ -0,0 +1,6 @@ +import pytest +from panda3d.core import ClockObject + +@pytest.fixture +def clockobj(): + return ClockObject() diff --git a/tests/putil/test_clockobject.py b/tests/putil/test_clockobject.py new file mode 100644 index 0000000000..afff7f9b3e --- /dev/null +++ b/tests/putil/test_clockobject.py @@ -0,0 +1,23 @@ +import time + +def test_get_frame_time(clockobj): + current_time = clockobj.get_frame_time() + time.sleep(2) + assert clockobj.get_frame_time() == current_time + +def test_jump_frame_time(clockobj): + current_time = clockobj.get_frame_time() + clockobj.tick() + assert clockobj.get_frame_time() == current_time + clockobj.get_frame_time() + +def test_get_real_time(clockobj): + current_time = clockobj.get_real_time() + time.sleep(2) + assert current_time != clockobj.get_real_time() + +def test_get_dt(clockobj): + clockobj.tick() + first_tick = clockobj.get_frame_time() + clockobj.tick() + second_tick = clockobj.get_frame_time() + assert clockobj.get_dt() == second_tick - first_tick