def label(self, text, x, y): result = OnscreenText( parent = self.frame, mayChange = True, text = text, scale = 1, fg = self.TEXT_COLOR, align = panda.TextNode.ALeft ) result.setPos(x, self.top - y * self.V_SPACING) return result
def __init__(self): #Standard title and instruction text self.title = OnscreenText(text="Panda3D: Tutorial - Particles", font=font, style=1, fg=(1, 1, 1, 1), pos=(0.8, -0.95), scale=.07) self.escapeEvent = OnscreenText(text=HELPTEXT, font=font, style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05) #More standard initialization self.accept('escape', sys.exit) self.accept('1', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/steam.ptf')]) self.accept('2', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/dust.ptf')]) self.accept('3', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/fountain.ptf')]) self.accept('4', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/smoke.ptf')]) self.accept('5', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/smokering.ptf')]) self.accept('6', self.loadParticleConfig , \ [os.path.join(PANDA_FILE_PATH, \ 'models/samples/particles/fireish.ptf')]) self.accept('escape', sys.exit) base.disableMouse() camera.setPos(0, -20, 2) base.setBackgroundColor(0, 0, 0) #This command is required for Panda to render particles base.enableParticles() self.t = loader.loadModel("models/teapot") self.t.setPos(0, 10, 0) self.t.reparentTo(render) self.setupLights() self.p = ParticleEffect() self.loadParticleConfig( os.path.join(PANDA_FILE_PATH, 'models/samples/particles/steam.ptf'))
def genLabelText(text, i): return OnscreenText(text=text, pos=(-1.3, .95 - .06 * i), fg=(1, 1, 1, 1), align=TextNode.ALeft, scale=.05, font=font)
def addInstructions(pos, msg): return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), font=font, pos=(-1.3, pos), align=TextNode.ALeft, scale=.05)
def addTitle(text): return OnscreenText(text=text, style=1, fg=(1, 1, 1, 1), font=font, pos=(1.3, -0.95), align=TextNode.ARight, scale=.07)
def addTitle(text): return OnscreenText(text=text, style=1, fg=(0, 0, 0, 1), font=font, pos=(1.3, -0.95), align=TextNode.ARight, scale=.07, shadow=(1, 1, 1, 1), shadowOffset=(0.05, 0.05))
def addInstructions(pos, msg): return OnscreenText(text=msg, style=1, fg=(0, 0, 0, 1), mayChange=1, font=font, pos=(-1.3, pos), align=TextNode.ALeft, scale=.05, shadow=(1, 1, 1, 1), shadowOffset=(0.1, 0.1))
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Tasks", style=1, fg=(1, 1, 0, 1), pos=(0.8, -0.95), scale=.07, font=font) self.escapeText = genLabelText("ESC: Quit", 0) self.leftkeyText = genLabelText("[Left Arrow]: Turn Left (CCW)", 1) self.rightkeyText = genLabelText("[Right Arrow]: Turn Right (CW)", 2) self.upkeyText = genLabelText("[Up Arrow]: Accelerate", 3) self.spacekeyText = genLabelText("[Space Bar]: Fire", 4) base.disableMouse() #Disable default mouse-based camera control self.bg = loadObject( "stars", scale=146, depth=200, transparency=False) #Load the background starfield self.ship = loadObject("ship") #Load the ship self.setVelocity(self.ship, Vec3(0, 0, 0)) #Initial velocity #A dictionary of what keys are currently being pressed #The key events update this list, and our task will query it as input self.keys = {"turnLeft": 0, "turnRight": 0, "accel": 0, "fire": 0} self.accept("escape", sys.exit) #Escape quits #Other keys events set the appropriate value in our key dictionary self.accept("arrow_left", self.setKey, ["turnLeft", 1]) self.accept("arrow_left-up", self.setKey, ["turnLeft", 0]) self.accept("arrow_right", self.setKey, ["turnRight", 1]) self.accept("arrow_right-up", self.setKey, ["turnRight", 0]) self.accept("arrow_up", self.setKey, ["accel", 1]) self.accept("arrow_up-up", self.setKey, ["accel", 0]) self.accept("space", self.setKey, ["fire", 1]) #Now we create the task. taskMgr is the task manager that actually calls #The function each frame. The add method creates a new task. The first #argument is the function to be called, and the second argument is the name #for the task. It returns a task object, that is passed to the function #each frame self.gameTask = taskMgr.add(self.gameLoop, "gameLoop") #The task object is a good place to put variables that should stay #persistant for the task function from frame to frame self.gameTask.last = 0 #Task time of the last frame self.gameTask.nextBullet = 0 #Task time when the next bullet may be fired self.bullets = [] #This empty list will contain fired bullets self.spawnAsteroids( ) #Complete initialization by spawning the asteroids
def __init__(self): formatArray=GeomVertexArrayFormat() formatArray.addColumn(InternalName.make("drawFlag"), 1, Geom.NTUint8, Geom.COther) format=GeomVertexFormat(GeomVertexFormat.getV3n3cpt2()) format.addArray(formatArray) self.format=GeomVertexFormat.registerFormat(format) bodydata=GeomVertexData("body vertices", format, Geom.UHStatic) self.barkTexture=loader.loadTexture( \ "models/samples/fractal_plants/bark.jpg") treeNodePath=NodePath("Tree Holder") makeFractalTree(bodydata,treeNodePath,Vec3(4,4,7)) treeNodePath.setTexture(self.barkTexture,1) treeNodePath.reparentTo(render) self.accept("q", self.regenTree) self.accept("w", self.addTree) self.accept("arrow_up", self.upIterations) self.accept("arrow_down", self.downIterations) self.accept("arrow_right", self.upCopies) self.accept("arrow_left", self.downCopies) self.numIterations=11 self.numCopies=4 self.upDownEvent = OnscreenText( text="Up/Down: Increase/Decrease the number of iterations ("+str(self.numIterations)+")", style=1, fg=(1,1,1,1), pos=(-1.3, 0.85), font = font, align=TextNode.ALeft, scale = .05, mayChange=True) self.leftRightEvent = OnscreenText( text="Left/Right: Increase/Decrease branching("+str(self.numCopies)+")", style=1, fg=(1,1,1,1), pos=(-1.3, 0.80), font = font, align=TextNode.ALeft, scale = .05, mayChange=True)
def __init__(self): #Standard initialization stuff #Standard title that's on screen in every tutorial self.title = OnscreenText( text='Panda3D: Tutorial - Texture "Movies" (Elevator)', style=1, fg=(1, 1, 1, 1), pos=(0.6, -0.95), scale=.07, font=loader.loadFont("cmss12")) #Load the elevator and attach it to render self.elevator = loader.loadModel( 'models/samples/texture_swapping/elevator') self.elevator.reparentTo(render) #Load the plane that will be animated and attach it to the elevator iteslf self.shadowPlane = loader.loadModel( \ 'models/samples/texture_swapping/shadow_plane') self.shadowPlane.reparentTo(self.elevator) self.shadowPlane.setPos(0, 0, .01) #Load the textures that will be applied to the polygon self.shadowTexs = loadTextureMovie(60, \ 'models/samples/texture_swapping/shadow/bar_shadows.', 'jpg') #Add the task that will animate the plane taskMgr.add(self.elevatorShadows, 'elevatorTask') #Builds the shaft, which is a 30ft repeatable segment self.shaft = [] for i in range(-1, 2): sh = loader.loadModel('models/samples/texture_swapping/shaft') sh.reparentTo(render) sh.setPos(-6.977, 0, 30 * i) self.shaft.append(sh) #Linearly move the elevator's height using an interval. #If you replaced this with some other way of moving the elevator, the #texture would compensate since it's based on height and not time LerpFunc(self.elevator.setZ, fromData=30, toData=-30, duration=5).loop() #Puts the camera relative to the elevator in a position that #shows off the texture movie base.disableMouse() camera.reparentTo(self.elevator) camera.setPosHpr(-9, 0, 20, -90, -60, 0)
class MyTapper(DirectObject): def __init__(self): formatArray=GeomVertexArrayFormat() formatArray.addColumn(InternalName.make("drawFlag"), 1, Geom.NTUint8, Geom.COther) format=GeomVertexFormat(GeomVertexFormat.getV3n3cpt2()) format.addArray(formatArray) self.format=GeomVertexFormat.registerFormat(format) bodydata=GeomVertexData("body vertices", format, Geom.UHStatic) self.barkTexture=loader.loadTexture( \ "models/samples/fractal_plants/bark.jpg") treeNodePath=NodePath("Tree Holder") makeFractalTree(bodydata,treeNodePath,Vec3(4,4,7)) treeNodePath.setTexture(self.barkTexture,1) treeNodePath.reparentTo(render) self.accept("q", self.regenTree) self.accept("w", self.addTree) self.accept("arrow_up", self.upIterations) self.accept("arrow_down", self.downIterations) self.accept("arrow_right", self.upCopies) self.accept("arrow_left", self.downCopies) self.numIterations=11 self.numCopies=4 self.upDownEvent = OnscreenText( text="Up/Down: Increase/Decrease the number of iterations ("+str(self.numIterations)+")", style=1, fg=(1,1,1,1), pos=(-1.3, 0.85), font = font, align=TextNode.ALeft, scale = .05, mayChange=True) self.leftRightEvent = OnscreenText( text="Left/Right: Increase/Decrease branching("+str(self.numCopies)+")", style=1, fg=(1,1,1,1), pos=(-1.3, 0.80), font = font, align=TextNode.ALeft, scale = .05, mayChange=True) def upIterations(self): self.numIterations+=1 self.upDownEvent.setText("Up/Down: Increase/Decrease the number of iterations ("+str(self.numIterations)+")") def downIterations(self): self.numIterations-=1 self.upDownEvent.setText("Up/Down: Increase/Decrease the number of Iteratations("+str(self.numIterations)+")") def upCopies(self): self.numCopies+=1 self.leftRightEvent.setText("Left/Right: Increase/Decrease branching("+str(self.numCopies)+")") def downCopies(self): self.numCopies-=1 self.leftRightEvent.setText("Left/Right: Increase/Decrease branching("+str(self.numCopies)+")") def regenTree(self): forest= render.findAllMatches("Tree Holder") forest.detach() bodydata=GeomVertexData("body vertices", self.format, Geom.UHStatic) treeNodePath=NodePath("Tree Holder") makeFractalTree(bodydata, treeNodePath,Vec3(4,4,7), Vec3(0,0,0),self.numIterations, self.numCopies) treeNodePath.setTexture(self.barkTexture,1) treeNodePath.reparentTo(render) def addTree(self): bodydata=GeomVertexData("body vertices", self.format, Geom.UHStatic) randomPlace=Vec3(200*random.random()-100, 200*random.random()-100, 0) #randomPlace.normalize() treeNodePath=NodePath("Tree Holder") makeFractalTree(bodydata, treeNodePath,Vec3(4,4,7), randomPlace, self.numIterations, self.numCopies) treeNodePath.setTexture(self.barkTexture,1) treeNodePath.reparentTo(render)
from panda3d.pandac import NodePath from panda3d.pandac import Vec3,Vec4,Mat4 from panda3d.direct.task.Task import Task from panda3d.direct.gui.OnscreenText import OnscreenText from panda3d.direct.showbase.DirectObject import DirectObject import math, random, time, sys, os random.seed() base.disableMouse() base.camera.setPos(0,-180,30) numPrimitives=0 font = loader.loadFont("cmss12") title = OnscreenText(text="Panda3D: Tutorial - Procdurally Making a Tree", style=1, fg=(1,1,1,1), font = font, pos=(0.6,-0.95), scale = .07) qEvent = OnscreenText( text="Q: Start Scene Over", font = font, style=1, fg=(1,1,1,1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale = .05) wEvent = OnscreenText( text="W: Add Another Tree", font = font, style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), align=TextNode.ALeft, scale = .05) #this is a helper function you can use to make a circle in the x-y plane #i didnt end up needing it but this comes up fairly often so I thought #I should keep this in the code. Feel free to use.
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Actors", style=1, fg=(0, 0, 0, 1), font=font, pos=(0.8, -0.95), scale=.07) self.escapeEventText = self.genLabelText("ESC: Quit", 0) self.akeyEventText = self.genLabelText("[A]: Robot 1 Left Punch", 1) self.skeyEventText = self.genLabelText("[S]: Robot 1 Right Punch", 2) self.kkeyEventText = self.genLabelText("[K]: Robot 2 Left Punch", 3) self.lkeyEventText = self.genLabelText("[L]: Robot 2 Right Punch", 4) #Set the camera in a fixed position base.disableMouse() camera.setPosHpr(14.5, -15.4, 14, 45, -14, 0) base.setBackgroundColor(0, 0, 0) #Add lighting so that the objects are not drawn flat self.setupLights() #Load the ring self.ring = loader.loadModel('models/samples/boxing_robots/ring') self.ring.reparentTo(render) #Models that use skeletal animation are known as Actors instead of models #Instead of just one file, the have one file for the main model, and an #additional file for each playable animation. #They are loaded using Actor.Actor instead of loader.LoadModel. #The constructor takes the location of the main object as with a normal #model and a dictionary (A fancy python structure that is like a lookup #table) that contains names for animations, and paths to the appropriate #files self.robot1 = Actor.Actor( 'models/samples/boxing_robots/robot', { 'leftPunch': 'models/samples/boxing_robots/robot_left_punch', 'rightPunch': 'models/samples/boxing_robots/robot_right_punch', 'headUp': 'models/samples/boxing_robots/robot_head_up', 'headDown': 'models/samples/boxing_robots/robot_head_down' }) #Actors need to be positioned and parented like normal objects self.robot1.setPosHprScale(-1, -2.5, 4, 45, 0, 0, 1.25, 1.25, 1.25) self.robot1.reparentTo(render) #We'll repeat the process for the second robot. The only thing that changes #here is the robot's color and position self.robot2 = Actor.Actor( 'models/samples/boxing_robots/robot', { 'leftPunch': 'models/samples/boxing_robots/robot_left_punch', 'rightPunch': 'models/samples/boxing_robots/robot_right_punch', 'headUp': 'models/samples/boxing_robots/robot_head_up', 'headDown': 'models/samples/boxing_robots/robot_head_down' }) #Set the properties of this robot self.robot2.setPosHprScale(1, 1.5, 4, 225, 0, 0, 1.25, 1.25, 1.25) self.robot2.setColor(Vec4(.7, 0, 0, 1)) self.robot2.reparentTo(render) #Now we define how the animated models will move. Animations are played #through special intervals. In this case we use actor intervals in a #sequence to play the part of the punch animation where the arm extends, #call a function to check if the punch landed, and then play the part of the #animation where the arm retracts #Punch sequence for robot 1's left arm self.robot1.punchLeft = Sequence( #Interval for the outstreched animation self.robot1.actorInterval('leftPunch', startFrame=1, endFrame=10), Func(self.checkPunch, 2), #Function to check if the punch was successful #Interval for the retract animation self.robot1.actorInterval('leftPunch', startFrame=11, endFrame=32)) #Punch sequence for robot 1's right arm self.robot1.punchRight = Sequence( self.robot1.actorInterval('rightPunch', startFrame=1, endFrame=10), Func(self.checkPunch, 2), self.robot1.actorInterval('rightPunch', startFrame=11, endFrame=32)) #Punch sequence for robot 2's left arm self.robot2.punchLeft = Sequence( self.robot2.actorInterval('leftPunch', startFrame=1, endFrame=10), Func(self.checkPunch, 1), self.robot2.actorInterval('leftPunch', startFrame=11, endFrame=32)) #Punch sequence for robot 2's right arm self.robot2.punchRight = Sequence( self.robot2.actorInterval('rightPunch', startFrame=1, endFrame=10), Func(self.checkPunch, 1), self.robot2.actorInterval('rightPunch', startFrame=11, endFrame=32)) #We use the same techinique to create a sequence for when a robot is knocked #out where the head pops up, waits a while, and then resets #Head animation for robot 1 self.robot1.resetHead = Sequence( #Interval for the head going up. Since no start or end frames were given, #the entire animation is played. self.robot1.actorInterval('headUp'), Wait(3), #The head down animation was animated a little too quickly, so this will #play it at 75% of it's normal speed self.robot1.actorInterval('headDown', playRate=.75)) #Head animation for robot 2 self.robot2.resetHead = Sequence( self.robot2.actorInterval('headUp'), Wait(3), self.robot2.actorInterval('headDown', playRate=.75)) #Now that we have defined the motion, we can define our key input. #Each fist is bound to a key. When a key is pressed, self.tryPunch checks to #make sure that the both robots have their heads down, and if they do it #plays the given interval self.accept('escape', sys.exit) self.accept('a', self.tryPunch, [self.robot1.punchLeft]) self.accept('s', self.tryPunch, [self.robot1.punchRight]) self.accept('k', self.tryPunch, [self.robot2.punchLeft]) self.accept('l', self.tryPunch, [self.robot2.punchRight])
def __init__( self ): #The main initialization of our class #This creates the on screen title that is in every tutorial self.title = OnscreenText(text="Panda3D: Tutorial - Lighting", style=1, fg=(1,1,0,1), font = font, pos=(0.87,-0.95), scale = .07) #Creates labels used for onscreen instructions self.ambientText = self.makeStatusLabel(0) self.directionalText = self.makeStatusLabel(1) self.spotlightText = self.makeStatusLabel(2) self.pointLightText = self.makeStatusLabel(3) self.spinningText = self.makeStatusLabel(4) self.ambientBrightnessText = self.makeStatusLabel(5) self.directionalBrightnessText = self.makeStatusLabel(6) self.spotlightBrightnessText = self.makeStatusLabel(7) self.spotlightExponentText = self.makeStatusLabel(8) self.lightingPerPixelText = self.makeStatusLabel(9) self.disco = loader.loadModel("models/samples/disco_lights/disco_hall") self.disco.reparentTo(render) self.disco.setPosHpr(0, 50, -4, 90, 0, 0) # First we create an ambient light. All objects are affected by ambient # light equally #Create and name the ambient light self.ambientLight = render.attachNewNode( AmbientLight( "ambientLight" ) ) #Set the color of the ambient light self.ambientLight.node().setColor( Vec4( .1, .1, .1, 1 ) ) #add the newly created light to the lightAttrib # Now we create a directional light. Directional lights add shading from a # given angle. This is good for far away sources like the sun self.directionalLight = render.attachNewNode( DirectionalLight( "directionalLight" ) ) self.directionalLight.node().setColor( Vec4( .35, .35, .35, 1 ) ) # The direction of a directional light is set as a 3D vector self.directionalLight.node().setDirection( Vec3( 1, 1, -2 ) ) # Now we create a spotlight. Spotlights light objects in a given cone # They are good for simulating things like flashlights self.spotlight = camera.attachNewNode( Spotlight( "spotlight" ) ) self.spotlight.node().setColor( Vec4( .45, .45, .45, 1 ) ) #The cone of a spotlight is controlled by it's lens. This creates the lens self.spotlight.node().setLens( PerspectiveLens() ) #This sets the Field of View (fov) of the lens, in degrees for width and #height. The lower the numbers, the tighter the spotlight. self.spotlight.node().getLens().setFov( 16, 16 ) # Attenuation controls how the light fades with distance. The numbers are # The three values represent the three constants (constant, linear, and # quadratic) in the internal lighting equation. The higher the numbers the # shorter the light goes. self.spotlight.node().setAttenuation( Vec3( 1, 0.0, 0.0 ) ) # This exponent value sets how soft the edge of the spotlight is. 0 means a # hard edge. 128 means a very soft edge. self.spotlight.node().setExponent( 60.0 ) # Now we create three colored Point lights. Point lights are lights that # radiate from a single point, like a light bulb. Like spotlights, they # are given position by attaching them to NodePaths in the world self.redHelper = loader.loadModel('models/samples/disco_lights/sphere') self.redHelper.setColor( Vec4( 1, 0, 0, 1 ) ) self.redHelper.setPos( -6.5, -3.75, 0 ) self.redHelper.setScale(.25) self.redPointLight = self.redHelper.attachNewNode( PointLight( "redPointLight" ) ) self.redPointLight.node().setColor( Vec4( .35, 0, 0, 1 ) ) self.redPointLight.node().setAttenuation( Vec3( .1, 0.04, 0.0 ) ) #The green point light and helper self.greenHelper = loader.loadModel('models/samples/disco_lights/sphere') self.greenHelper.setColor( Vec4( 0, 1, 0, 1 ) ) self.greenHelper.setPos( 0, 7.5, 0 ) self.greenHelper.setScale(.25) self.greenPointLight = self.greenHelper.attachNewNode( PointLight( "greenPointLight" ) ) self.greenPointLight.node().setAttenuation( Vec3( .1, .04, .0 ) ) self.greenPointLight.node().setColor( Vec4( 0, .35, 0, 1 ) ) #The blue point light and helper self.blueHelper = loader.loadModel('models/samples/disco_lights/sphere') self.blueHelper.setColor( Vec4( 0, 0, 1, 1 ) ) self.blueHelper.setPos( 6.5, -3.75, 0 ) self.blueHelper.setScale(.25) self.bluePointLight = self.blueHelper.attachNewNode( PointLight( "bluePointLight" ) ) self.bluePointLight.node().setAttenuation( Vec3( .1, 0.04, 0.0 ) ) self.bluePointLight.node().setColor( Vec4( 0, 0, .35, 1 ) ) self.bluePointLight.node().setSpecularColor( Vec4( 1 ) ) #Create a dummy node so the lights can be spun with one command self.pointLightHelper = render.attachNewNode( "pointLightHelper" ) self.pointLightHelper.setPos(0, 50, 11) self.redHelper.reparentTo( self.pointLightHelper ) self.greenHelper.reparentTo( self.pointLightHelper ) self.blueHelper.reparentTo( self.pointLightHelper ) #Finally we store the lights on the root of the scene graph. #This will cause them to affect everything in the scene. render.setLight( self.ambientLight ) render.setLight( self.directionalLight ) render.setLight( self.spotlight ) render.setLight( self.redPointLight ) render.setLight( self.greenPointLight ) render.setLight( self.bluePointLight ) # Create and start interval to spin the lights, and a variable to # manage them. self.pointLightsSpin = self.pointLightHelper.hprInterval(6, Vec3(360, 0, 0)) self.pointLightsSpin.loop() self.arePointLightsSpinning = True # Per-pixel lighting is initially off self.perPixelEnabled = False # listen to keys for controlling the lights self.accept( "escape", sys.exit) self.accept( "a", self.toggleLights, [[self.ambientLight]] ) self.accept( "d", self.toggleLights, [[self.directionalLight]] ) self.accept( "s", self.toggleLights, [[self.spotlight]] ) self.accept( "p", self.toggleLights, [[self.redPointLight, self.greenPointLight, self.bluePointLight]] ) self.accept( "r", self.toggleSpinningPointLights ) self.accept( "l", self.togglePerPixelLighting ) self.accept( "z", self.addBrightness, [self.ambientLight, -.05] ) self.accept( "x", self.addBrightness, [self.ambientLight, .05] ) self.accept( "c", self.addBrightness, [self.directionalLight, -.05] ) self.accept( "v", self.addBrightness, [self.directionalLight, .05] ) self.accept( "b", self.addBrightness, [self.spotlight, -.05] ) self.accept( "n", self.addBrightness, [self.spotlight, .05] ) self.accept( "q", self.adjustSpotlightExponent, [self.spotlight, -1] ) self.accept( "w", self.adjustSpotlightExponent, [self.spotlight, 1] ) #Finally call the function that builds the instruction texts self.updateStatusLabel()
def __init__(self): # create a texture into which we can copy the main window. self.tex = Texture() self.tex.setMinfilter(Texture.FTLinear) base.win.addRenderTexture(self.tex, GraphicsOutput.RTMTriggeredCopyTexture) # Create another 2D camera. Tell it to render before the main camera. self.backcam = base.makeCamera2d(base.win, sort=-10) self.background = NodePath("background") self.backcam.reparentTo(self.background) self.background.setDepthTest(0) self.background.setDepthWrite(0) self.backcam.node().getDisplayRegion(0).setClearDepthActive(0) # Obtain two texture cards. One renders before the dragon, the other after. self.bcard = base.win.getTextureCard() self.bcard.reparentTo(self.background) self.bcard.setTransparency(1) self.fcard = base.win.getTextureCard() self.fcard.reparentTo(render2d) self.fcard.setTransparency(1) # Initialize one of the nice effects. self.chooseEffectGhost() # Add the task that initiates the screenshots. taskMgr.add(self.takeSnapShot, "takeSnapShot") # Create some black squares on top of which we will # place the instructions. blackmaker = CardMaker("blackmaker") blackmaker.setColor(0, 0, 0, 1) blackmaker.setFrame(-1.00, -0.50, 0.65, 1.00) instcard = NodePath(blackmaker.generate()) instcard.reparentTo(render2d) blackmaker.setFrame(-0.5, 0.5, -1.00, -0.85) titlecard = NodePath(blackmaker.generate()) titlecard.reparentTo(render2d) # Panda does its best to hide the differences between DirectX and # OpenGL. But there are a few differences that it cannot hide. # One such difference is that when OpenGL copies from a # visible window to a texture, it gets it right-side-up. When # DirectX does it, it gets it upside-down. There is nothing panda # can do to compensate except to expose a flag and let the # application programmer deal with it. You should only do this # in the rare event that you're copying from a visible window # to a texture. if (base.win.getGsg().getCopyTextureInverted()): print "Copy texture is inverted." self.bcard.setScale(1, 1, -1) self.fcard.setScale(1, 1, -1) # Put up the instructions title = OnscreenText(text="Panda3D: Tutorial - Motion Trails", style=1, fg=(1, 1, 1, 1), font=font, pos=(0, -0.95), scale=.07) instr0 = addInstructions(0.95, "Press ESC to exit") instr1 = addInstructions(0.90, "Press 1: Ghost effect") instr2 = addInstructions(0.85, "Press 2: PaintBrush effect") instr3 = addInstructions(0.80, "Press 3: Double Vision effect") instr4 = addInstructions(0.75, "Press 4: Wings of Blue effect") instr5 = addInstructions(0.70, "Press 5: Whirlpool effect") # enable the key events self.accept("escape", sys.exit, [0]) self.accept("1", self.chooseEffectGhost) self.accept("2", self.chooseEffectPaintBrush) self.accept("3", self.chooseEffectDoubleVision) self.accept("4", self.chooseEffectWingsOfBlue) self.accept("5", self.chooseEffectWhirlpool)
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking", style=1, fg=(1,1,1,1), font = font, pos=(0.8,-0.95), scale = .07) self.escapeEvent = OnscreenText( text="ESC: Quit", font = font, style=1, fg=(1,1,1,1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale = .05) self.mouse1Event = OnscreenText( text="Left-click and drag: Pick up and drag piece", style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), font = font, align=TextNode.ALeft, scale = .05) self.accept('escape', sys.exit) #Escape quits base.disableMouse() #Disble mouse camera control camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera self.setupLights() #Setup default lighting #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #Now we create the chess board and its pieces #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = [None for i in range(64)] self.pieces = [None for i in range(64)] for i in range(64): #Load, parent, color, and position the model (a single square polygon) self.squares[i] = loader.loadModel("models/samples/chessboard/square") self.squares[i].reparentTo(self.squareRoot) self.squares[i].setPos(SquarePos(i)) self.squares[i].setColor(SquareColor(i)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) #We will use this variable as a pointer to whatever piece is currently #in this square #The order of pieces on a chessboard from white's perspective. This list #contains the constructor functions for the piece classes defined below pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook) for i in range (8,16): #Load the white pawns self.pieces[i] = Pawn(i, WHITE) for i in range (48,56): #load the black pawns self.pieces[i] = Pawn(i, PIECEBLACK) for i in range(8): #Load the special pieces for the front row and color them white self.pieces[i] = pieceOrder[i](i, WHITE) #Load the special pieces for the back row and color them black self.pieces[i+56] = pieceOrder[i](i+56, PIECEBLACK) #This will represent the index of the currently highlited square self.hiSq = False #This wil represent the index of the square where currently dragged piece #was grabbed from self.dragging = False #Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') self.accept("mouse1", self.grabPiece) #left-click grabs a piece self.accept("mouse1-up", self.releasePiece) #releasing places it
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText( text="Panda3D: Tutorial - Collision Detection", style=1, fg=(1, 1, 1, 1), pos=(0.7, -0.95), scale=.07, font=font) self.instructions = OnscreenText(text="Mouse pointer tilts the board", pos=(-1.3, .95), fg=(1, 1, 1, 1), font=font, align=TextNode.ALeft, scale=.05) self.accept("escape", sys.exit) #Escape quits base.disableMouse() #Disable mouse-based camera control camera.setPosHpr(0, 0, 25, 0, -90, 0) #Place the camera #Load the maze and place it in the scene self.maze = loader.loadModel("models/samples/ball_in_maze/maze") self.maze.reparentTo(render) #Most times, you want collisions to be tested against invisible geometry #rather than every polygon. This is because testing against every polygon #in the scene is usually too slow. You can have simplified or approximate #geometry for the solids and still get good results. # #Sometimes you'll want to create and position your own collision solids in #code, but it's often easier to have them built automatically. This can be #done by adding special tags into an egg file. Check maze.egg and ball.egg #and look for lines starting with <Collide>. The part is brackets tells #Panda exactly what to do. Polyset means to use the polygons in that group #as solids, while Sphere tells panda to make a collision sphere around them #Keep means to keep the polygons in the group as visable geometry (good #for the ball, not for the triggers), and descend means to make sure that #the settings are applied to any subgroups. # #Once we have the collision tags in the models, we can get to them using #NodePath's find command #Find the collision node named wall_collide self.walls = self.maze.find("**/wall_collide") #Collision objects are sorted using BitMasks. BitMasks are ordinary numbers #with extra methods for working with them as binary bits. Every collision #solid has both a from mask and an into mask. Before Panda tests two #objects, it checks to make sure that the from and into collision masks #have at least one bit in common. That way things that shouldn't interact #won't. Normal model nodes have collision masks as well. By default they #are set to bit 20. If you want to collide against actual visable polygons, #set a from collide mask to include bit 20 # #For this example, we will make everything we want the ball to collide with #include bit 0 self.walls.node().setIntoCollideMask(BitMask32.bit(0)) #CollisionNodes are usually invisible but can be shown. Uncomment the next #line to see the collision walls #self.walls.show() #We will now find the triggers for the holes and set their masks to 0 as #well. We also set their names to make them easier to identify during #collisions self.loseTriggers = [] for i in range(6): trigger = self.maze.find("**/hole_collide" + str(i)) trigger.node().setIntoCollideMask(BitMask32.bit(0)) trigger.node().setName("loseTrigger") self.loseTriggers.append(trigger) #Uncomment this line to see the triggers #trigger.show() #Ground_collide is a single polygon on the same plane as the ground in the #maze. We will use a ray to collide with it so that we will know exactly #what height to put the ball at every frame. Since this is not something #that we want the ball itself to collide with, it has a different #bitmask. self.mazeGround = self.maze.find("**/ground_collide") self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1)) #Load the ball and attach it to the scene #It is on a root dummy node so that we can rotate the ball itself without #rotating the ray that will be attached to it self.ballRoot = render.attachNewNode("ballRoot") self.ball = loader.loadModel("models/samples/ball_in_maze/ball") self.ball.reparentTo(self.ballRoot) #Find the collison sphere for the ball which was created in the egg file #Notice that it has a from collision mask of bit 0, and an into collison #mask of no bits. This means that the ball can only cause collisions, not #be collided into self.ballSphere = self.ball.find("**/ball") self.ballSphere.node().setFromCollideMask(BitMask32.bit(0)) self.ballSphere.node().setIntoCollideMask(BitMask32.allOff()) #No we create a ray to start above the ball and cast down. This is to #Determine the height the ball should be at and the angle the floor is #tilting. We could have used the sphere around the ball itself, but it #would not be as reliable self.ballGroundRay = CollisionRay() #Create the ray self.ballGroundRay.setOrigin(0, 0, 10) #Set its origin self.ballGroundRay.setDirection(0, 0, -1) #And its direction #Collision solids go in CollisionNode self.ballGroundCol = CollisionNode( 'groundRay') #Create and name the node self.ballGroundCol.addSolid(self.ballGroundRay) #Add the ray self.ballGroundCol.setFromCollideMask( BitMask32.bit(1)) #Set its bitmasks self.ballGroundCol.setIntoCollideMask(BitMask32.allOff()) #Attach the node to the ballRoot so that the ray is relative to the ball #(it will always be 10 feet over the ball and point down) self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol) #Uncomment this line to see the ray #self.ballGroundColNp.show() #Finally, we create a CollisionTraverser. CollisionTraversers are what #do the job of calculating collisions self.cTrav = CollisionTraverser() #Collision traverservs tell collision handlers about collisions, and then #the handler decides what to do with the information. We are using a #CollisionHandlerQueue, which simply creates a list of all of the #collisions in a given pass. There are more sophisticated handlers like #one that sends events and another that tries to keep collided objects #apart, but the results are often better with a simple queue self.cHandler = CollisionHandlerQueue() #Now we add the collision nodes that can create a collision to the #traverser. The traverser will compare these to all others nodes in the #scene. There is a limit of 32 CollisionNodes per traverser #We add the collider, and the handler to use as a pair self.cTrav.addCollider(self.ballSphere, self.cHandler) self.cTrav.addCollider(self.ballGroundColNp, self.cHandler) #Collision traversers have a built in tool to help visualize collisions. #Uncomment the next line to see it. #self.cTrav.showCollisions(render) #This section deals with lighting for the ball. Only the ball was lit #because the maze has static lighting pregenerated by the modeler lAttrib = LightAttrib.makeAllOff() ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(.55, .55, .55, 1)) lAttrib = lAttrib.addLight(ambientLight) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(0, 0, -1)) directionalLight.setColor(Vec4(0.375, 0.375, 0.375, 1)) directionalLight.setSpecularColor(Vec4(1, 1, 1, 1)) lAttrib = lAttrib.addLight(directionalLight) self.ballRoot.node().setAttrib(lAttrib) #This section deals with adding a specular highlight to the ball to make #it look shiny m = Material() m.setSpecular(Vec4(1, 1, 1, 1)) m.setShininess(96) self.ball.setMaterial(m, 1) #Finally, we call start for more initialization self.start()
def __init__(self): #Our standard title and instructions text self.title = OnscreenText(text="Panda3D: Tutorial - Musicbox(sounds)", font=font, style=1, fg=(1, 1, 1, 1), pos=(0.7, -0.95), scale=.07) self.escapeEventText = OnscreenText(text="ESC: Quit", font=font, style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05) #Set up the key input self.accept('escape', sys.exit) #Fix the camera position base.disableMouse() #Loading sounds is done in a similar way to loading other things #Loading the main music box song self.musicBoxSound = base.loadMusic( 'models/samples/music_box/musicbox.mp3') self.musicBoxSound.setVolume(.5) #Volume is a percentage from 0 to 1 self.musicBoxSound.setLoopCount( 0) #0 means loop forever, 1 (default) means #play once. 2 or higher means play that #many times #Sound objects do not have a pause function, just play and stop. So we will #Use this variable to keep track of where the sound is at when it was stoped #to impliment pausing self.musicTime = 0 #Loading the open/close effect #loadSFX and loadMusic are identical. They are often used for organization #(loadMusic is used for background music, loadSfx is used for other effects) self.lidSfx = base.loadSfx('models/samples/music_box/openclose.mp3') #The open/close file has both effects in it. Fortunatly we can use intervals #to easily define parts of a sound file to play self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0) self.lidCloseSfx = SoundInterval(self.lidSfx, startTime=5) #For this tutorial, it seemed appropriate to have on screen controls. The #following code creates them #This is a label for a slider self.sliderText = OnscreenText("Volume", font=font, style=1, fg=(1, 1, 1, 1), pos=(0, 0.8), scale=.07) #The slider itself. It calls self.setMusicBoxVolume when changed self.slider = DirectSlider(pos=Vec3(0, 0, .7), value=.50, command=self.setMusicBoxVolume) #A button that calls self.toggleMusicBox when pressed self.button = DirectButton(pos=Vec3(.7, 0, .7), text="Open Box", scale=.1, pad=(.5, .5), text_font=font, rolloverSound=None, clickSound=None, command=self.toggleMusicBox) #A variable to represent the state of the simulation. It starts closed self.boxOpen = False #Here we load and set up the music box. It was modeled in a complex way, so #setting it up will be complicated self.musicBox = loader.loadModel('models/samples/music_box/musicbox') self.musicBox.setPos(0, 60, -10) self.musicBox.reparentTo(render) #Just like the scene graph contains hierarchies of nodes, so can #models. You can get the NodePath for the node using the find #function, and then you can animate the model by moving its parts #To see the hierarchy of a model, use, the ls function #self.musicBox.ls() prints out the entire hierarchy of the model #Finding pieces of the model self.Lid = self.musicBox.find('**/lid') self.Panda = self.musicBox.find('**/turningthing') #This model was made with the hinge in the wrong place #this is here so we have something to turn self.HingeNode = self.musicBox.find('**/box').attachNewNode( 'nHingeNode') self.HingeNode.setPos(.8659, 6.5, 5.4) #WRT - ie with respect to. Reparents the object without changing #its position, size, or orientation self.Lid.wrtReparentTo(self.HingeNode) self.HingeNode.setHpr(0, 90, 0) #This sets up an interval to play the close sound and actually close the box #at the same time. self.lidClose = Parallel( self.lidCloseSfx, LerpFunc(self.HingeNode.setP, duration=2, fromData=0, toData=90, blendType='easeInOut')) #Same thing for opening the box self.lidOpen = Parallel( self.lidOpenSfx, LerpFunc(self.HingeNode.setP, duration=2, fromData=90, toData=0, blendType='easeInOut')) #The interval for turning the panda self.PandaTurn = self.Panda.hprInterval(7, Vec3(360, 0, 0)) #Do a quick loop and pause to set it as a looping interval so it can be #started with resume and loop properly self.PandaTurn.loop() self.PandaTurn.pause()
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText( text="Panda3D: Tutorial - Joint Manipulation", style=1, fg=(1, 1, 1, 1), font=font, pos=(0.7, -0.95), scale=.07) self.onekeyText = genLabelText("ESC: Quit", 0) self.onekeyText = genLabelText("[1]: Teapot", 1) self.twokeyText = genLabelText("[2]: Candy cane", 2) self.threekeyText = genLabelText("[3]: Banana", 3) self.fourkeyText = genLabelText("[4]: Sword", 4) #setup key input self.accept('escape', sys.exit) self.accept('1', self.setObject, [0]) self.accept('2', self.setObject, [1]) self.accept('3', self.setObject, [2]) self.accept('4', self.setObject, [3]) base.disableMouse() #Disable mouse-based camera-control camera.setPos(0, -15, 2) #Position the camera self.eve = Actor( "models/samples/looking_and_gripping/eve", #Load our animated charachter {'walk': "models/samples/looking_and_gripping/eve_walk"}) self.eve.reparentTo(render) #Put it in the scene #Now we use controlJoint to get a NodePath that's in control of her neck #This must be done before any animations are played self.eveNeck = self.eve.controlJoint(None, 'modelRoot', 'Neck') #We now play an animation. An animation must be played, or at least posed #for the nodepath we just got from controlJoint to actually effect the model self.eve.actorInterval("walk", playRate=2).loop() #Now we add a task that will take care of turning the head taskMgr.add(self.turnHead, "turnHead") #Now we will expose the joint the hand joint. ExposeJoint allows us to #get the position of a joint while it is animating. This is different than #controlJonit which stops that joint from animating but lets us move it. #This is particularly usefull for putting an object (like a weapon) in an #actor's hand self.rightHand = self.eve.exposeJoint(None, 'modelRoot', 'RightHand') #This is a table with models, positions, rotations, and scales of objects to #be attached to our exposed joint. These are stock models and so they needed #to be repositioned to look right. positions = [("models/samples/looking_and_gripping/teapot", \ (0,-.66,-.95), (90,0,90), .4), ("models/samples/looking_and_gripping/candycane", \ (.15,-.99,-.22), (90,0,90), 1), ("models/samples/looking_and_gripping/banana", \ (.08,-.1,.09), (0,-90,0), 1.75), ("models/samples/looking_and_gripping/sword", \ (.11,.19,.06), (0,0,90), 1)] self.models = [] #A list that will store our models objects for row in positions: np = loader.loadModel(row[0]) #Load the model np.setPos(row[1][0], row[1][1], row[1][2]) #Position it np.setHpr(row[2][0], row[2][1], row[2][2]) #Rotate it np.setScale(row[3]) #Scale it #Reparent the model to the exposed joint. That way when the joint moves, #the model we just loaded will move with it. np.reparentTo(self.rightHand) self.models.append(np) #Add it to our models list self.setObject(0) #Make object 0 the first shown self.setupLights() #Put in some default lighting
def __init__(self): #Standard initialization stuff #Standard title that's on screen in every tutorial self.title = OnscreenText(text='Panda3D: Tutorial - Texture "Movies"', style=1, fg=(1, 1, 1, 1), pos=(0.7, -0.95), scale=.07, font=font) #Text to show the keyboard keys and their functions on screen self.escapeEventText = OnscreenText(text="ESC: Quit", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05, font=font) self.onekeyEventText = OnscreenText(text="[1]: Freeview camera", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.90), align=TextNode.ALeft, scale=.05, font=font) self.twokeyEventText = OnscreenText( text="[2]: Preset Camera Angle 2 (Verify billboard effect)", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.85), font=font, align=TextNode.ALeft, scale=.05, mayChange=1) base.setBackgroundColor(0, 0, 0) #Set the background color #Set up the key input self.accept('escape', sys.exit) #Escape quits self.accept('1', self.setViewMain) #Free view self.accept('2', self.setViewBillboard) #Billboard effect view #Initialization specific to this world #Load a polygon plane (4 sided square) to put an animated duck sprite on self.duckPlane = loader.loadModel( 'models/samples/texture_swapping/plane') self.duckPlane.setPos(-2, 8, 0) #set its position self.duckPlane.reparentTo(render) #reparent to render #Enable tranparency: this attribute needs to be set for Panda to render the #transparency in the duck's texture as transparent rather than opaque self.duckPlane.setTransparency(1) #Now we call our special 'loadTextureMovie' function that returns a list #containing all of the textures for the duck sprite. #Check the function definition later in this file for its parameters self.duckTexs = self.loadTextureMovie( 24, 'models/samples/texture_swapping/duck/duck_fly_left', 'png', padding=2) #Next we add a task to our task list that will animate the texture on the #duck plane according to the time elapsed. self.duckTask = taskMgr.add(self.textureMovie, "duckTask") #The function self.textureMovie is set to run any texture movie that #animates and loops based on time (rather that some other value like #position). To do that, it is set up to expect a number of parameters set #in the task object. The following lines set those parameters #Framerate: The texture will be changed 36 times per second self.duckTask.fps = 36 #self.duckPlane is the object whose texture should be changed self.duckTask.obj = self.duckPlane #self.duckTexs (which we created earlier with self.oadTextureMovie) #contains the list of textures to animate from self.duckTask.textures = self.duckTexs #Now, instead of a duck, we will put an animated explosion onto a polygon #This is the same as loading the duck animation, with the expection that #we will "billboard" the explosion so that it always faces the camera self.expPlane = loader.loadModel( 'models/samples/texture_swapping/plane') #load the object self.expPlane.setPos(2, 8, 0) #set the position self.expPlane.reparentTo(render) #reparent to render self.expPlane.setTransparency(1) #enable transparency #load the texture movie self.expTexs = self.loadTextureMovie( 51, 'models/samples/texture_swapping/explosion/explosion', 'png', padding=4) #create the animation task self.expTask = taskMgr.add(self.textureMovie, "explosionTask") self.expTask.fps = 30 #set framerate self.expTask.obj = self.expPlane #set object self.expTask.textures = self.expTexs #set texture list #This create the "billboard" effect that will rotate the object so that it #is always rendered as facing the eye (camera) self.expPlane.node().setEffect(BillboardEffect.makePointEye()) #The code below generates the plane you see with the numbers and arrows. #This is just to give a sense of orientation as the camera is moved around. self.orientPlane = loader.loadModel( 'models/samples/texture_swapping/plane') #Load the object #load the texture self.orientTex = \ loader.loadTexture("models/samples/texture_swapping/orientation.png") self.orientPlane.setTexture(self.orientTex, 1) #Set the texture self.orientPlane.reparentTo(render) #Parent to render #Set the position, orientation, and scale self.orientPlane.setPosHprScale(0, 8, -1, 0, -90, 0, 10, 10, 10)
def makeStatusLabel(self, i): return OnscreenText( style=1, fg=(1,1,0,1), pos=(-1.3, 0.95 - (.05 * i)), font = font, align=TextNode.ALeft, scale = .05, mayChange = 1)
def __init__(self): ###Standard initialization stuff #Standard title that's on screen in every tutorial self.title = OnscreenText(text="Panda3D: Tutorial - Fog", style=1, fg=(1, 1, 1, 1), pos=(0.9, -0.95), scale=.07, font=font) #Code to generate the on screen instructions self.escapeEventText = self.genLabelText("ESC: Quit", 0) self.pkeyEventText = self.genLabelText("[P]: Pause", 1) self.tkeyEventText = self.genLabelText("[T]: Toggle Fog", 2) self.dkeyEventText = self.genLabelText("[D]: Make fog color black", 3) self.sdkeyEventText = self.genLabelText( "[SHIFT+D]: Make background color black", 4) self.rkeyEventText = self.genLabelText("[R]: Make fog color red", 5) self.srkeyEventText = self.genLabelText( "[SHIFT+R]: Make background color red", 6) self.bkeyEventText = self.genLabelText("[B]: Make fog color blue", 7) self.sbkeyEventText = self.genLabelText( "[SHIFT+B]: Make background color blue", 8) self.gkeyEventText = self.genLabelText("[G]: Make fog color green", 9) self.sgkeyEventText = self.genLabelText( "[SHIFT+G]: Make background color green", 10) self.lkeyEventText = self.genLabelText( "[L]: Make fog color light grey", 11) self.slkeyEventText = self.genLabelText( "[SHIFT+L]: Make background color light grey", 12) self.pluskeyEventText = self.genLabelText("[+]: Increase fog density", 13) self.minuskeyEventText = self.genLabelText("[-]: Decrease fog density", 14) base.disableMouse( ) #disable mouse control so that we can place the camera camera.setPosHpr(0, 0, 10, 0, -90, 0) base.setBackgroundColor(0, 0, 0) #set the background color to black ###World specific-code #Create an instance of fog called 'distanceFog'. #'distanceFog' is just a name for our fog, not a specific type of fog. self.fog = Fog('distanceFog') #Set the initial color of our fog to black. self.fog.setColor(0, 0, 0) #Set the density/falloff of the fog. The range is 0-1. #The higher the numer, the "bigger" the fog effect. self.fog.setExpDensity(.08) #We will set fog on render which means that everything in our scene will #be affected by fog. Alternatively, you could only set fog on a specific #object/node and only it and the nodes below it would be affected by #the fog. render.setFog(self.fog) #Define the keyboard input #Escape closes the demo self.accept('escape', sys.exit) #Handle pausing the tunnel self.accept('p', self.handlePause) #Handle turning the fog on and off self.accept('t', ToggleFog, [render, self.fog]) #Sets keys to set the fog to various colors self.accept('r', self.fog.setColor, [1, 0, 0]) self.accept('g', self.fog.setColor, [0, 1, 0]) self.accept('b', self.fog.setColor, [0, 0, 1]) self.accept('l', self.fog.setColor, [.7, .7, .7]) self.accept('d', self.fog.setColor, [0, 0, 0]) #Sets keys to change the background colors self.accept('shift-r', base.setBackgroundColor, [1, 0, 0]) self.accept('shift-g', base.setBackgroundColor, [0, 1, 0]) self.accept('shift-b', base.setBackgroundColor, [0, 0, 1]) self.accept('shift-l', base.setBackgroundColor, [.7, .7, .7]) self.accept('shift-d', base.setBackgroundColor, [0, 0, 0]) #Increases the fog density when "+" key is pressed self.accept('+', self.addFogDensity, [.01]) #This is to handle the other "+" key (it's over = on the keyboard) self.accept('=', self.addFogDensity, [.01]) self.accept('shift-=', self.addFogDensity, [.01]) #Decreases the fog density when the "-" key is pressed self.accept('-', self.addFogDensity, [-.01]) #Load the tunel and start the tunnel self.initTunnel() self.contTunnel()