316 lines
12 KiB
Python
316 lines
12 KiB
Python
"""
|
|
|
|
Roaming Ralph Sample (modified)
|
|
|
|
This is the default roaming ralph sample, with the render pipeline.
|
|
Using the render pipeline is only the matter of a few lines, which have
|
|
been explicitely marked.
|
|
|
|
NOTICE: Since this is a straight copy of the standard roaming ralph sample,
|
|
this attempts to keep as close to the original code to make it easier
|
|
to see where to load the render pipeline.
|
|
|
|
If you find a bug/suggestion in this code, then you should report
|
|
that to the sample included in Panda3D, and not this code.
|
|
|
|
(and yeah, this code could surely be written in a much nicer way)
|
|
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
from panda3d.core import CollisionTraverser, CollisionNode
|
|
from panda3d.core import CollisionHandlerQueue, CollisionRay
|
|
from panda3d.core import AmbientLight, DirectionalLight
|
|
from panda3d.core import PandaNode, NodePath, TextNode
|
|
from panda3d.core import Vec3, Vec4, BitMask32, load_prc_file_data
|
|
from direct.gui.OnscreenText import OnscreenText
|
|
from direct.actor.Actor import Actor
|
|
from direct.showbase.ShowBase import ShowBase
|
|
|
|
|
|
# Switch into the current directory
|
|
os.chdir(os.path.realpath(os.path.dirname(__file__)))
|
|
|
|
SPEED = 0.5
|
|
|
|
|
|
# Function to put instructions on the screen.
|
|
def addInstructions(pos, msg):
|
|
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
|
|
pos=(-0.9, pos - 0.2), align=TextNode.ALeft, scale=.035)
|
|
|
|
|
|
class World(ShowBase):
|
|
|
|
def __init__(self):
|
|
|
|
# Setup window size, title and so on
|
|
load_prc_file_data("", """
|
|
win-size 1600 900
|
|
window-title Render Pipeline - Roaming Ralph Demo
|
|
""")
|
|
|
|
# ------ Begin of render pipeline code ------
|
|
|
|
# Insert the pipeline path to the system path, this is required to be
|
|
# able to import the pipeline classes
|
|
pipeline_path = "../../"
|
|
|
|
# Just a special case for my development setup, so I don't accidentally
|
|
# commit a wrong path. You can remove this in your own programs.
|
|
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
|
|
pipeline_path = "../../RenderPipeline/"
|
|
|
|
sys.path.insert(0, pipeline_path)
|
|
|
|
from rpcore import RenderPipeline, SpotLight
|
|
self.render_pipeline = RenderPipeline()
|
|
self.render_pipeline.create(self)
|
|
|
|
# ------ End of render pipeline code, thats it! ------
|
|
|
|
# Set time of day
|
|
self.render_pipeline.daytime_mgr.time = "7:40"
|
|
|
|
# Use a special effect for rendering the scene, this is because the
|
|
# roaming ralph model has no normals or valid materials
|
|
self.render_pipeline.set_effect(render, "scene-effect.yaml", {}, sort=250)
|
|
|
|
self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, "cam-right":0}
|
|
self.speed = 1.0
|
|
base.win.setClearColor(Vec4(0,0,0,1))
|
|
|
|
# Post the instructions
|
|
|
|
self.inst1 = addInstructions(0.95, "[ESC] Quit")
|
|
self.inst4 = addInstructions(0.90, "[W] Run Ralph Forward")
|
|
self.inst4 = addInstructions(0.85, "[S] Run Ralph Backward")
|
|
self.inst2 = addInstructions(0.80, "[A] Rotate Ralph Left")
|
|
self.inst3 = addInstructions(0.75, "[D] Rotate Ralph Right")
|
|
self.inst6 = addInstructions(0.70, "[Left Arrow] Rotate Camera Left")
|
|
self.inst7 = addInstructions(0.65, "[Right Arrow] Rotate Camera Right")
|
|
|
|
# Set up the environment
|
|
#
|
|
# This environment model contains collision meshes. If you look
|
|
# in the egg file, you will see the following:
|
|
#
|
|
# <Collide> { Polyset keep descend }
|
|
#
|
|
# This tag causes the following mesh to be converted to a collision
|
|
# mesh -- a mesh which is optimized for collision, not rendering.
|
|
# It also keeps the original mesh, so there are now two copies ---
|
|
# one optimized for rendering, one for collisions.
|
|
|
|
self.environ = loader.loadModel("resources/world")
|
|
self.environ.reparentTo(render)
|
|
self.environ.setPos(0,0,0)
|
|
|
|
|
|
# Remove wall nodes
|
|
self.environ.find("**/wall").remove_node()
|
|
|
|
# Create the main character, Ralph
|
|
self.ralph = Actor("resources/ralph",
|
|
{"run":"resources/ralph-run",
|
|
"walk":"resources/ralph-walk"})
|
|
self.ralph.reparentTo(render)
|
|
self.ralph.setScale(.2)
|
|
self.ralph.setPos(Vec3(-110.9, 29.4, 1.8))
|
|
|
|
# Create a floater object. We use the "floater" as a temporary
|
|
# variable in a variety of calculations.
|
|
|
|
self.floater = NodePath(PandaNode("floater"))
|
|
self.floater.reparentTo(render)
|
|
|
|
# Accept the control keys for movement and rotation
|
|
|
|
self.accept("escape", sys.exit)
|
|
self.accept("a", self.setKey, ["left",1])
|
|
self.accept("d", self.setKey, ["right",1])
|
|
self.accept("w", self.setKey, ["forward",1])
|
|
self.accept("s", self.setKey, ["backward",1])
|
|
self.accept("arrow_left", self.setKey, ["cam-left",1])
|
|
self.accept("arrow_right", self.setKey, ["cam-right",1])
|
|
self.accept("a-up", self.setKey, ["left",0])
|
|
self.accept("d-up", self.setKey, ["right",0])
|
|
self.accept("w-up", self.setKey, ["forward",0])
|
|
self.accept("s-up", self.setKey, ["backward",0])
|
|
self.accept("arrow_left-up", self.setKey, ["cam-left",0])
|
|
self.accept("arrow_right-up", self.setKey, ["cam-right",0])
|
|
self.accept("=", self.adjustSpeed, [0.25])
|
|
self.accept("+", self.adjustSpeed, [0.25])
|
|
self.accept("-", self.adjustSpeed, [-0.25])
|
|
|
|
taskMgr.add(self.move,"moveTask")
|
|
|
|
# Game state variables
|
|
self.isMoving = False
|
|
|
|
# Set up the camera
|
|
|
|
base.disableMouse()
|
|
base.camera.setPos(self.ralph.getX() + 10,self.ralph.getY() + 10, 2)
|
|
base.camLens.setFov(80)
|
|
|
|
# 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()
|
|
|
|
self.ralphGroundRay = CollisionRay()
|
|
self.ralphGroundRay.setOrigin(0,0,1000)
|
|
self.ralphGroundRay.setDirection(0,0,-1)
|
|
self.ralphGroundCol = CollisionNode('ralphRay')
|
|
self.ralphGroundCol.addSolid(self.ralphGroundRay)
|
|
self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
|
|
self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
|
|
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
|
|
self.ralphGroundHandler = CollisionHandlerQueue()
|
|
self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)
|
|
|
|
self.camGroundRay = CollisionRay()
|
|
self.camGroundRay.setOrigin(0,0,1000)
|
|
self.camGroundRay.setDirection(0,0,-1)
|
|
self.camGroundCol = CollisionNode('camRay')
|
|
self.camGroundCol.addSolid(self.camGroundRay)
|
|
self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
|
|
self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
|
|
self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
|
|
self.camGroundHandler = CollisionHandlerQueue()
|
|
self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
|
|
|
|
# Uncomment this line to see the collision rays
|
|
#self.ralphGroundColNp.show()
|
|
#self.camGroundColNp.show()
|
|
|
|
# Uncomment this line to show a visual representation of the
|
|
# collisions occuring
|
|
#self.cTrav.showCollisions(render)
|
|
|
|
# Create some lighting
|
|
ambientLight = AmbientLight("ambientLight")
|
|
ambientLight.setColor(Vec4(.3, .3, .3, 1))
|
|
directionalLight = DirectionalLight("directionalLight")
|
|
directionalLight.setDirection(Vec3(-5, -5, -5))
|
|
directionalLight.setColor(Vec4(1, 1, 1, 1))
|
|
directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
|
|
render.setLight(render.attachNewNode(ambientLight))
|
|
render.setLight(render.attachNewNode(directionalLight))
|
|
|
|
#Records the state of the arrow keys
|
|
def setKey(self, key, value):
|
|
self.keyMap[key] = value
|
|
|
|
# Adjust movement speed
|
|
def adjustSpeed(self, delta):
|
|
newSpeed = self.speed + delta
|
|
if 0 <= newSpeed <= 3:
|
|
self.speed = newSpeed
|
|
|
|
# Accepts arrow keys to move either the player or the menu cursor,
|
|
# Also deals with grid checking and collision detection
|
|
def move(self, task):
|
|
|
|
# If the camera-left key is pressed, move camera left.
|
|
# If the camera-right key is pressed, move camera right.
|
|
|
|
base.camera.lookAt(self.ralph)
|
|
if (self.keyMap["cam-left"]!=0):
|
|
base.camera.setX(base.camera, +20 * globalClock.getDt())
|
|
if (self.keyMap["cam-right"]!=0):
|
|
base.camera.setX(base.camera, -20 * globalClock.getDt())
|
|
|
|
# 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"]!=0):
|
|
self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
|
|
elif (self.keyMap["right"]!=0):
|
|
self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
|
|
if (self.keyMap["forward"]!=0):
|
|
self.ralph.setY(self.ralph, -25 * self.speed * globalClock.getDt())
|
|
elif (self.keyMap["backward"]!=0):
|
|
self.ralph.setY(self.ralph, 25 * self.speed * globalClock.getDt())
|
|
|
|
# If ralph is moving, loop the run animation.
|
|
# If he is standing still, stop the animation.
|
|
|
|
if (self.keyMap["forward"]!=0) or (self.keyMap["backward"]!=0) or \
|
|
(self.keyMap["left"]!=0) or (self.keyMap["right"]!=0):
|
|
if self.isMoving is False:
|
|
self.ralph.loop("run")
|
|
self.isMoving = True
|
|
else:
|
|
if self.isMoving:
|
|
self.ralph.stop()
|
|
self.ralph.pose("walk",5)
|
|
self.isMoving = False
|
|
|
|
# If the camera is too far from ralph, move it closer.
|
|
# If the camera is too close to ralph, move it farther.
|
|
|
|
camvec = self.ralph.getPos() - base.camera.getPos()
|
|
camvec.setZ(0)
|
|
camdist = camvec.length()
|
|
camvec.normalize()
|
|
if (camdist > 10.0):
|
|
base.camera.setPos(base.camera.getPos() + camvec*(camdist-10))
|
|
camdist = 10.0
|
|
if (camdist < 5.0):
|
|
base.camera.setPos(base.camera.getPos() - camvec*(5-camdist))
|
|
camdist = 5.0
|
|
|
|
# Now check for collisions.
|
|
|
|
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.
|
|
|
|
entries = []
|
|
for i in range(self.ralphGroundHandler.getNumEntries()):
|
|
entry = self.ralphGroundHandler.getEntry(i)
|
|
entries.append(entry)
|
|
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
|
|
self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
|
|
else:
|
|
self.ralph.setPos(startpos)
|
|
|
|
# Keep the camera at one foot above the terrain,
|
|
# or two feet above ralph, whichever is greater.
|
|
|
|
entries = []
|
|
for i in range(self.camGroundHandler.getNumEntries()):
|
|
entry = self.camGroundHandler.getEntry(i)
|
|
entries.append(entry)
|
|
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
|
|
base.camera.setZ(entries[0].getSurfacePoint(render).getZ()+1.0)
|
|
if (base.camera.getZ() < self.ralph.getZ() + 2.0):
|
|
base.camera.setZ(self.ralph.getZ() + 2.0)
|
|
|
|
# The camera should look in ralph's direction,
|
|
# but it should also try to stay horizontal, so look at
|
|
# a floater which hovers above ralph's head.
|
|
|
|
self.floater.setPos(self.ralph.getPos())
|
|
self.floater.setZ(self.ralph.getZ() + 2.0)
|
|
base.camera.lookAt(self.floater)
|
|
|
|
return task.cont
|
|
|
|
|
|
w = World().run()
|
|
|