mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
Merge branch 'master' into cmake
This commit is contained in:
commit
671f15e052
@ -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
|
||||
```
|
||||
|
||||
|
@ -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;
|
||||
|
@ -15,7 +15,6 @@
|
||||
#define _AI_GLOBALS_H
|
||||
|
||||
#include "config_ai.h"
|
||||
#include "pandaFramework.h"
|
||||
#include "textNode.h"
|
||||
#include "pandaSystem.h"
|
||||
|
||||
|
@ -13,6 +13,9 @@
|
||||
|
||||
#include "arrival.h"
|
||||
|
||||
#include "pursue.h"
|
||||
#include "seek.h"
|
||||
|
||||
Arrival::Arrival(AICharacter *ai_ch, double distance) {
|
||||
_ai_char = ai_ch;
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "obstacleAvoidance.h"
|
||||
|
||||
#include "aiWorld.h"
|
||||
|
||||
ObstacleAvoidance::
|
||||
ObstacleAvoidance(AICharacter *ai_char, float feeler_length) {
|
||||
_ai_char = ai_char;
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "pathFind.h"
|
||||
|
||||
#include "pathFollow.h"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
@ -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;
|
||||
|
@ -28,7 +28,7 @@
|
||||
#define SHADOWATLAS_H
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "lvecBase4.h"
|
||||
#include "luse.h"
|
||||
|
||||
NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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 <class %s>>' % (repr(types.InstanceType)[:-1],
|
||||
str(obj.__class__))
|
||||
if sys.version_info < (3, 0) and t is types.InstanceType:
|
||||
return "<type 'instance' of <class %s>>" % (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 "<type 'instance' of %s>" % (obj.__class__)
|
||||
return t
|
||||
|
||||
def deeptype(obj, maxLen=100, _visitedIds=None):
|
||||
|
@ -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 \
|
||||
|
@ -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")
|
||||
|
@ -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/
|
||||
|
@ -2147,7 +2147,6 @@
|
||||
<File RelativePath="..\panda\src\express\test_types.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\weakPointerTo.I"></File>
|
||||
<File RelativePath="..\panda\src\express\pta_int.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerToBase.I"></File>
|
||||
<File RelativePath="..\panda\src\express\namable.I"></File>
|
||||
<File RelativePath="..\panda\src\express\vector_float.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\pointerTo.h"></File>
|
||||
@ -2174,7 +2173,6 @@
|
||||
<File RelativePath="..\panda\src\express\datagramGenerator.h"></File>
|
||||
<File RelativePath="..\panda\src\express\weakPointerToVoid.h"></File>
|
||||
<File RelativePath="..\panda\src\express\datagram.I"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerTo.h"></File>
|
||||
<File RelativePath="..\panda\src\express\weakPointerCallback.h"></File>
|
||||
<File RelativePath="..\panda\src\express\datagram.h"></File>
|
||||
<File RelativePath="..\panda\src\express\textEncoder.h"></File>
|
||||
@ -2215,7 +2213,6 @@
|
||||
<File RelativePath="..\panda\src\express\datagramIterator.I"></File>
|
||||
<File RelativePath="..\panda\src\express\vector_uchar.h"></File>
|
||||
<File RelativePath="..\panda\src\express\datagramIterator.h"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerTo.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\datagramGenerator.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\hashGeneratorBase.h"></File>
|
||||
<File RelativePath="..\panda\src\express\patchfile.cxx"></File>
|
||||
@ -2259,7 +2256,6 @@
|
||||
<File RelativePath="..\panda\src\express\namable.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\trueClock.I"></File>
|
||||
<File RelativePath="..\panda\src\express\config_express.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerToBase.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\pointerToVoid.I"></File>
|
||||
<File RelativePath="..\panda\src\express\vector_uchar.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\vector_float.h"></File>
|
||||
@ -2286,7 +2282,6 @@
|
||||
<File RelativePath="..\panda\src\express\datagramGenerator.I"></File>
|
||||
<File RelativePath="..\panda\src\express\compress_string.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\subStreamBuf.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerToBase.h"></File>
|
||||
<File RelativePath="..\panda\src\express\pointerToArrayBase.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\express_composite.cxx"></File>
|
||||
<File RelativePath="..\panda\src\express\virtualFileComposite.I"></File>
|
||||
@ -2294,7 +2289,6 @@
|
||||
<File RelativePath="..\panda\src\express\multifile.I"></File>
|
||||
<File RelativePath="..\panda\src\express\virtualFile.I"></File>
|
||||
<File RelativePath="..\panda\src\express\patchfile.I"></File>
|
||||
<File RelativePath="..\panda\src\express\threadSafePointerTo.I"></File>
|
||||
<File RelativePath="..\panda\src\express\virtualFileList.h"></File>
|
||||
</Filter>
|
||||
<Filter Name="iphonedisplay">
|
||||
|
@ -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 + "\\")
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -186,6 +186,7 @@ get_test_pcollector() {
|
||||
*/
|
||||
void CollisionBox::
|
||||
output(std::ostream &out) const {
|
||||
out << "box, (" << get_min() << ") to (" << get_max() << ")";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)) {
|
||||
|
0
panda/src/device/inputDeviceSet.I
Executable file → Normal file
0
panda/src/device/inputDeviceSet.I
Executable file → Normal file
0
panda/src/device/inputDeviceSet.cxx
Executable file → Normal file
0
panda/src/device/inputDeviceSet.cxx
Executable file → Normal file
0
panda/src/device/inputDeviceSet.h
Executable file → Normal file
0
panda/src/device/inputDeviceSet.h
Executable file → Normal file
@ -15,6 +15,7 @@
|
||||
#include "gamepadButton.h"
|
||||
#include "mouseButton.h"
|
||||
#include "buttonRegistry.h"
|
||||
#include "winInputDeviceManager.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(CPPPARSER)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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<class T>
|
||||
INLINE ThreadSafePointerTo<T>::
|
||||
ThreadSafePointerTo(To *ptr) : ThreadSafePointerToBase<T>(ptr) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerTo<T>::
|
||||
ThreadSafePointerTo(const ThreadSafePointerTo<T> ©) :
|
||||
ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerTo<T>::
|
||||
~ThreadSafePointerTo() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE typename ThreadSafePointerTo<T>::To &ThreadSafePointerTo<T>::
|
||||
operator *() const {
|
||||
return *((To *)AtomicAdjust::get_ptr(this->_void_ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE typename ThreadSafePointerTo<T>::To *ThreadSafePointerTo<T>::
|
||||
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<class T>
|
||||
INLINE ThreadSafePointerTo<T>::
|
||||
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<class T>
|
||||
INLINE typename ThreadSafePointerTo<T>::To *ThreadSafePointerTo<T>::
|
||||
p() const {
|
||||
return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerTo<T> &ThreadSafePointerTo<T>::
|
||||
operator = (To *ptr) {
|
||||
this->reassign(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerTo<T> &ThreadSafePointerTo<T>::
|
||||
operator = (const ThreadSafePointerTo<T> ©) {
|
||||
this->reassign((const ThreadSafePointerToBase<T> &)copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T>::
|
||||
ThreadSafeConstPointerTo(const typename ThreadSafeConstPointerTo<T>::To *ptr) :
|
||||
ThreadSafePointerToBase<T>((typename ThreadSafeConstPointerTo<T>::To *)ptr)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T>::
|
||||
ThreadSafeConstPointerTo(const ThreadSafePointerTo<T> ©) :
|
||||
ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T>::
|
||||
~ThreadSafeConstPointerTo() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T>::
|
||||
ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> ©) :
|
||||
ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE const typename ThreadSafeConstPointerTo<T>::To &ThreadSafeConstPointerTo<T>::
|
||||
operator *() const {
|
||||
return *((To *)AtomicAdjust::get_ptr(this->_void_ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE const typename ThreadSafeConstPointerTo<T>::To *ThreadSafeConstPointerTo<T>::
|
||||
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<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T>::
|
||||
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<class T>
|
||||
INLINE const typename ThreadSafeConstPointerTo<T>::To *ThreadSafeConstPointerTo<T>::
|
||||
p() const {
|
||||
return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
|
||||
operator = (const To *ptr) {
|
||||
this->reassign((To *)ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
|
||||
operator = (const ThreadSafePointerTo<T> ©) {
|
||||
this->reassign((const ThreadSafePointerToBase<T> &)copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
|
||||
operator = (const ThreadSafeConstPointerTo<T> ©) {
|
||||
this->reassign((const ThreadSafePointerToBase<T> &)copy);
|
||||
return *this;
|
||||
}
|
@ -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"
|
@ -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 T>
|
||||
class ThreadSafePointerTo : public ThreadSafePointerToBase<T> {
|
||||
public:
|
||||
typedef typename ThreadSafePointerToBase<T>::To To;
|
||||
PUBLISHED:
|
||||
INLINE ThreadSafePointerTo(To *ptr = nullptr);
|
||||
INLINE ThreadSafePointerTo(const ThreadSafePointerTo<T> ©);
|
||||
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<BaseClass>, 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<T> &operator = (To *ptr);
|
||||
INLINE ThreadSafePointerTo<T> &operator = (const ThreadSafePointerTo<T> ©);
|
||||
|
||||
// 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<T>::is_null(); }
|
||||
INLINE void clear() { ThreadSafePointerToBase<T>::clear(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class ThreadSafeConstPointerTo : public ThreadSafePointerToBase<T> {
|
||||
public:
|
||||
typedef typename ThreadSafePointerToBase<T>::To To;
|
||||
PUBLISHED:
|
||||
INLINE ThreadSafeConstPointerTo(const To *ptr = nullptr);
|
||||
INLINE ThreadSafeConstPointerTo(const ThreadSafePointerTo<T> ©);
|
||||
INLINE ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> ©);
|
||||
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<T> &operator = (const To *ptr);
|
||||
INLINE ThreadSafeConstPointerTo<T> &operator = (const ThreadSafePointerTo<T> ©);
|
||||
INLINE ThreadSafeConstPointerTo<T> &operator = (const ThreadSafeConstPointerTo<T> ©);
|
||||
|
||||
// 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<T>::clear(); }
|
||||
};
|
||||
|
||||
#define TSPT(type) ThreadSafePointerTo< type >
|
||||
#define TSCPT(type) ThreadSafeConstPointerTo< type >
|
||||
|
||||
#include "threadSafePointerTo.I"
|
||||
|
||||
#endif
|
@ -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<class T>
|
||||
INLINE ThreadSafePointerToBase<T>::
|
||||
ThreadSafePointerToBase(To *ptr) {
|
||||
reassign(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerToBase<T>::
|
||||
ThreadSafePointerToBase(const ThreadSafePointerToBase<T> ©) {
|
||||
reassign(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE ThreadSafePointerToBase<T>::
|
||||
~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<class T>
|
||||
INLINE void ThreadSafePointerToBase<T>::
|
||||
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<class T>
|
||||
INLINE void ThreadSafePointerToBase<T>::
|
||||
reassign(const ThreadSafePointerToBase<To> ©) {
|
||||
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<class T>
|
||||
void ThreadSafePointerToBase<T>::
|
||||
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<class T>
|
||||
INLINE void ThreadSafePointerToBase<T>::
|
||||
clear() {
|
||||
reassign(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* A handy function to output ThreadSafePointerTo's as a hex pointer followed
|
||||
* by a reference count.
|
||||
*/
|
||||
template<class T>
|
||||
INLINE void ThreadSafePointerToBase<T>::
|
||||
output(std::ostream &out) const {
|
||||
out << _void_ptr;
|
||||
if (_void_ptr != nullptr) {
|
||||
out << ":" << ((To *)_void_ptr)->get_ref_count();
|
||||
}
|
||||
}
|
@ -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"
|
@ -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 T>
|
||||
class ThreadSafePointerToBase : public PointerToVoid {
|
||||
public:
|
||||
typedef T To;
|
||||
|
||||
protected:
|
||||
INLINE ThreadSafePointerToBase(To *ptr);
|
||||
INLINE ThreadSafePointerToBase(const ThreadSafePointerToBase<T> ©);
|
||||
INLINE ~ThreadSafePointerToBase();
|
||||
|
||||
INLINE void reassign(To *ptr);
|
||||
INLINE void reassign(const ThreadSafePointerToBase<To> ©);
|
||||
|
||||
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<class T>
|
||||
INLINE std::ostream &operator <<(std::ostream &out, const ThreadSafePointerToBase<T> &pointer) {
|
||||
pointer.output(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#include "threadSafePointerToBase.I"
|
||||
|
||||
#endif
|
@ -17,8 +17,8 @@
|
||||
#include "ffmpegVideoCursor.h"
|
||||
#include "ffmpegAudio.h"
|
||||
#include "ffmpegAudioCursor.h"
|
||||
|
||||
#include "movieTypeRegistry.h"
|
||||
#include "pandaSystem.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
@ -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, "*");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "winStatsServer.h"
|
||||
#include "config_pstatclient.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
static const char *toplevel_class_name = "pstats";
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsMonitor;
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include "winStatsLabelStack.h"
|
||||
#include "pmap.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsMonitor;
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsMonitor;
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include "pandatoolbase.h"
|
||||
#include "pvector.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsLabel;
|
||||
|
@ -23,6 +23,9 @@
|
||||
#include "pvector.h"
|
||||
#include "pmap.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsServer;
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include "pStatPianoRoll.h"
|
||||
#include "pointerTo.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsMonitor;
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include "pStatStripChart.h"
|
||||
#include "pointerTo.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
class WinStatsMonitor;
|
||||
|
@ -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
|
||||
|
||||
|
278
samples/particles/advanced.py
Executable file
278
samples/particles/advanced.py
Executable file
@ -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()
|
@ -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)
|
||||
|
||||
|
Binary file not shown.
@ -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
|
||||
|
30
tests/collide/collisions.py
Normal file
30
tests/collide/collisions.py
Normal file
@ -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)
|
45
tests/collide/test_into_box.py
Normal file
45
tests/collide/test_into_box.py
Normal file
@ -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
|
24
tests/collide/test_into_lines.py
Normal file
24
tests/collide/test_into_lines.py
Normal file
@ -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
|
48
tests/collide/test_into_poly.py
Normal file
48
tests/collide/test_into_poly.py
Normal file
@ -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
|
89
tests/collide/test_into_sphere.py
Normal file
89
tests/collide/test_into_sphere.py
Normal file
@ -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
|
@ -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))
|
||||
|
77
tests/gobj/test_lenses.py
Normal file
77
tests/gobj/test_lenses.py
Normal file
@ -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)
|
6
tests/putil/conftest.py
Normal file
6
tests/putil/conftest.py
Normal file
@ -0,0 +1,6 @@
|
||||
import pytest
|
||||
from panda3d.core import ClockObject
|
||||
|
||||
@pytest.fixture
|
||||
def clockobj():
|
||||
return ClockObject()
|
23
tests/putil/test_clockobject.py
Normal file
23
tests/putil/test_clockobject.py
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user