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