Merge branch 'master' into cmake

This commit is contained in:
Sam Edwards 2019-04-13 17:29:44 -06:00
commit 671f15e052
71 changed files with 996 additions and 790 deletions

View File

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

View File

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

View File

@ -15,7 +15,6 @@
#define _AI_GLOBALS_H
#include "config_ai.h"
#include "pandaFramework.h"
#include "textNode.h"
#include "pandaSystem.h"

View File

@ -13,6 +13,9 @@
#include "arrival.h"
#include "pursue.h"
#include "seek.h"
Arrival::Arrival(AICharacter *ai_ch, double distance) {
_ai_char = ai_ch;

View File

@ -13,6 +13,8 @@
#include "obstacleAvoidance.h"
#include "aiWorld.h"
ObstacleAvoidance::
ObstacleAvoidance(AICharacter *ai_char, float feeler_length) {
_ai_char = ai_char;

View File

@ -13,6 +13,8 @@
#include "pathFind.h"
#include "pathFollow.h"
using std::cout;
using std::endl;
using std::string;

View File

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

View File

@ -28,7 +28,7 @@
#define SHADOWATLAS_H
#include "pandabase.h"
#include "lvecBase4.h"
#include "luse.h"
NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 + "\\")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -186,6 +186,7 @@ get_test_pcollector() {
*/
void CollisionBox::
output(std::ostream &out) const {
out << "box, (" << get_min() << ") to (" << get_max() << ")";
}
/**

View File

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

0
panda/src/device/inputDeviceSet.cxx Executable file → Normal file
View File

0
panda/src/device/inputDeviceSet.h Executable file → Normal file
View File

View File

@ -15,6 +15,7 @@
#include "gamepadButton.h"
#include "mouseButton.h"
#include "buttonRegistry.h"
#include "winInputDeviceManager.h"
#if defined(_WIN32) && !defined(CPPPARSER)

View File

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

View File

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

View File

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

View File

@ -163,6 +163,8 @@ private:
bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags,
LPoint3 &center, 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);

View File

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

View File

@ -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> &copy) :
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> &copy) {
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> &copy) :
ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
{
}
/**
*
*/
template<class T>
INLINE ThreadSafeConstPointerTo<T>::
~ThreadSafeConstPointerTo() {
}
/**
*
*/
template<class T>
INLINE ThreadSafeConstPointerTo<T>::
ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> &copy) :
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> &copy) {
this->reassign((const ThreadSafePointerToBase<T> &)copy);
return *this;
}
/**
*
*/
template<class T>
INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
operator = (const ThreadSafeConstPointerTo<T> &copy) {
this->reassign((const ThreadSafePointerToBase<T> &)copy);
return *this;
}

View File

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

View File

@ -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> &copy);
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> &copy);
// 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> &copy);
INLINE ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> &copy);
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> &copy);
INLINE ThreadSafeConstPointerTo<T> &operator = (const ThreadSafeConstPointerTo<T> &copy);
// 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

View File

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

View File

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

View File

@ -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> &copy);
INLINE ~ThreadSafePointerToBase();
INLINE void reassign(To *ptr);
INLINE void reassign(const ThreadSafePointerToBase<To> &copy);
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,6 +16,9 @@
#include "pandatoolbase.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
class WinStatsMonitor;

View File

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

View File

@ -16,6 +16,9 @@
#include "pandatoolbase.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
class WinStatsMonitor;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View 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

View 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

View 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

View File

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

@ -0,0 +1,6 @@
import pytest
from panda3d.core import ClockObject
@pytest.fixture
def clockobj():
return ClockObject()

View 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