class App(ShowBase): def __init__(self): # Load the default configuration.prc. This is recommended, as it # contains some important panda options loadPrcFile("../../Config/configuration.prc") # Init the showbase ShowBase.__init__(self) # Create a new pipeline instance self.renderPipeline = RenderingPipeline(self) # Set the base path for the pipeline. This is required as we are in # a subdirectory self.renderPipeline.getMountManager().setBasePath("../../") # Also set the write path self.renderPipeline.getMountManager().setWritePath("../../Temp/") # Load the default settings self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Now create the pipeline self.renderPipeline.create() # Enable atmospheric scattering self.renderPipeline.enableDefaultEarthScattering()
class Main(ShowBase, DebugObject): """ This is the material explorer. You can try different materials""" def __init__(self): DebugObject.__init__(self, "Main") self.debug("Bit System =", 8 * struct.calcsize("P")) # Load engine configuration self.debug("Loading panda3d configuration from configuration.prc ..") loadPrcFile("../../Config/configuration.prc") # Init the showbase ShowBase.__init__(self) # Create the render pipeline self.debug("Creating pipeline") self.renderPipeline = RenderingPipeline(self) # Set a write directory, where the shader cache and so on is stored # self.renderPipeline.getMountManager().setWritePath(writeDirectory) self.renderPipeline.getMountManager().setBasePath("../../") self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Create the pipeline, and enable scattering self.renderPipeline.create() self.renderPipeline.enableDefaultEarthScattering() # Load some demo source self.sceneSource = "Models/SmoothCube/Cube.bam" # Load scene from disk self.debug("Loading Scene '" + self.sceneSource + "'") self.model = self.loader.loadModel(self.sceneSource) self.scene = render.attachNewNode("Scene") self.model.reparentTo(self.scene) self.model.setZ(1.0) # Wheter to use a ground floor self.usePlane = True self.sceneWireframe = False # Flatten scene self.scene.flattenStrong() # Load ground plane if configured if self.usePlane: self.groundPlane = self.loader.loadModel( "Models/Plane/Model.egg.bam") self.groundPlane.setPos(0, 0, 0) self.groundPlane.setScale(2.0) self.groundPlane.setTwoSided(True) self.groundPlane.flattenStrong() self.groundPlane.reparentTo(self.scene) # Prepare textures with SRGB format self.prepareSRGB(self.scene) # Create movement controller (Freecam) self.controller = MovementController(self) self.controller.setInitialPosition(Vec3(0, -5, 5.0), Vec3(0, 0, 5)) self.controller.setup() # Hotkey for wireframe self.accept("f3", self.toggleSceneWireframe) # Hotkey to reload all shaders self.accept("r", self.setShaders) # Create a sun light dPos = Vec3(60, 30, 100) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(2048) dirLight.setAmbientColor(Vec3(0.0, 0.0, 0.0)) dirLight.setPos(dPos) dirLight.setColor(Vec3(3)) dirLight.setPssmTarget(base.cam, base.camLens) dirLight.setPssmDistance(50.0) dirLight.setCastsShadows(True) self.renderPipeline.addLight(dirLight) self.dirLight = dirLight sunPos = Vec3(56.7587, -31.3601, 189.196) self.dirLight.setPos(sunPos) self.dirLight.setDirection(sunPos) # Tell the GI which light casts the GI self.renderPipeline.setGILightSource(dirLight) # Slider to move the sun if self.renderPipeline.settings.displayOnscreenDebugger: self.renderPipeline.guiManager.demoSlider.node[ "command"] = self.setSunPos self.renderPipeline.guiManager.demoSlider.node["value"] = 20 self.lastSliderValue = 0.0 # Load skyboxn self.skybox = None self.loadSkybox() # Set default object shaders self.setShaders(refreshPipeline=False) self.createGUI() def createGUI(self): self.slider_opts = { "roughness": { "name": "Roughness", "min": 0.0001, "max": 1.0, "default": 0.4, }, "metallic": { "name": "Metallic", "min": 0.0001, "max": 1.0, "default": 0.0, }, "specular": { "name": "Specular", "min": 0.0001, "max": 1.0, "default": 0.5, }, "basecolor_r": { "name": "Base Color [Red]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(1, 0.2, 0.2) }, "basecolor_g": { "name": "Base Color [Green]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(0.6, 1.0, 0.2) }, "basecolor_b": { "name": "Base Color [Blue]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(0.2, 0.6, 1) }, } self.sliderOrder = [ "roughness", "metallic", "specular", "", "basecolor_r", "basecolor_g", "basecolor_b" ] self.guiParent = UIWindow("Material Explorer", 280, 400) self.guiParent.getNode().setPos(self.win.getXSize() - 340, 0, -120) self.windowNode = self.guiParent.getContentNode() currentY = 5 for name in self.sliderOrder: if name == "": currentY += 30 continue opts = self.slider_opts[name] opts["slider"] = BetterSlider(x=20, y=currentY + 20, size=230, minValue=opts["min"], maxValue=opts["max"], value=opts["default"], parent=self.windowNode, callback=self.materialOptionChanged) col = Vec3(1.0) if "color" in opts: col = opts["color"] opts["label"] = BetterOnscreenText(x=20, y=currentY, text=opts["name"], align="left", parent=self.windowNode, size=15, color=col) opts["value_label"] = BetterOnscreenText(x=250, y=currentY, text=str(opts["default"]), align="right", parent=self.windowNode, size=15, color=Vec3(0.6), mayChange=True) currentY += 50 def materialOptionChanged(self): container = self.model for name, opt in self.slider_opts.items(): container.setShaderInput("opt_" + name, opt["slider"].getValue()) opt["value_label"].setText("{:0.4f}".format( opt["slider"].getValue())) def setSunPos(self): """ Sets the sun position based on the debug slider """ radial = True rawValue = self.renderPipeline.guiManager.demoSlider.node["value"] diff = self.lastSliderValue - rawValue self.lastSliderValue = rawValue if radial: rawValue = rawValue / 100.0 * 2.0 * math.pi dPos = Vec3( math.sin(rawValue) * 100.0, math.cos(rawValue) * 100.0, 100) # dPos = Vec3(100, 100, (rawValue - 50) * 10.0) else: dPos = Vec3(30, (rawValue - 50) * 1.5, 100) if abs(diff) > 0.0001: self.dirLight.setPos(dPos) self.dirLight.setDirection(dPos) def toggleSceneWireframe(self): """ Toggles the scene rendermode """ self.sceneWireframe = not self.sceneWireframe if self.sceneWireframe: self.scene.setRenderModeWireframe() else: self.scene.clearRenderMode() def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() # Only diffuse textures should be SRGB if "diffuse" in tex.getName().lower(): print "Preparing texture", tex.getName() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) elif baseFormat == Texture.FSrgb or baseFormat == Texture.FSrgbAlpha: # Format is okay already pass else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # All textures should have the correct filter modes tex.setMinfilter(Texture.FTLinearMipmapLinear) tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) def loadSkybox(self): """ Loads the skybox """ self.skybox = self.loader.loadModel("Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def setShaders(self, refreshPipeline=True): """ Sets all shaders """ self.debug("Reloading Shaders ..") if self.renderPipeline: self.scene.setShader( self.renderPipeline.getDefaultObjectShader(False)) self.model.setShader( BetterShader.load("Shader/DefaultObjectShader/vertex.glsl", "dynamicMaterialFragment.glsl")) if refreshPipeline: self.renderPipeline.reloadShaders() if self.skybox: self.skybox.setShader( BetterShader.load("Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) def convertToPatches(self, model): """ Converts a model to patches. This is REQUIRED before beeing able to use it with tesselation shaders """ self.debug("Converting model to patches ..") for node in model.find_all_matches("**/+GeomNode"): geom_node = node.node() num_geoms = geom_node.get_num_geoms() for i in range(num_geoms): geom_node.modify_geom(i).make_patches_in_place()
class World(ShowBase): def __init__(self): # Load the default configuration.prc. This is recommended, as it # contains some important panda options loadPrcFile("../../Config/configuration.prc") ShowBase.__init__(self) # Create a new pipeline instance self.renderPipeline = RenderingPipeline(self) # Set the base path for the pipeline. This is required as we are in # a subdirectory self.renderPipeline.getMountManager().setBasePath("../../") # Also set the write path self.renderPipeline.getMountManager().setWritePath("../../Temp/") # Load the default settings self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Now create the pipeline self.renderPipeline.create() #This creates the on screen title that is in every tutorial self.title = OnscreenText(text="Panda3D: Tutorial 2 - Carousel", style=1, fg=(1, 1, 1, 1), pos=(0.87, -0.95), scale=.07) base.setBackgroundColor(.6, .6, 1) #Set the background color base.disableMouse() #Allow manual positioning of the camera camera.setPosHpr(0, -8, 2.5, 0, -9, 0) #Set the cameras' position #and orientation self.loadModels() #Load and position our models self.setupLights() #Add some basic lighting self.startCarousel() #Create the needed intervals and put the #carousel into motion def loadModels(self): #Load the carousel base self.carousel = loader.loadModel("models/carousel_base") self.carousel.reparentTo(render) #Attach it to render #Load the modeled lights that are on the outer rim of the carousel #(not Panda lights) #There are 2 groups of lights. At any given time, one group will have the #"on" texture and the other will have the "off" texture. self.lights1 = loader.loadModel("models/carousel_lights") self.lights1.reparentTo(self.carousel) #Load the 2nd set of lights self.lights2 = loader.loadModel("models/carousel_lights") #We need to rotate the 2nd so it doesn't overlap with the 1st set. self.lights2.setH(36) self.lights2.reparentTo(self.carousel) #Load the textures for the lights. One texture is for the "on" state, #the other is for the "off" state. self.lightOffTex = loader.loadTexture("models/carousel_lights_off.jpg") self.lightOnTex = loader.loadTexture("models/carousel_lights_on.jpg") #Create an list (self.pandas) with filled with 4 dummy nodes attached to #the carousel. #This uses a python concept called "Array Comprehensions." Check the Python #manual for more information on how they work self.pandas = [ self.carousel.attachNewNode("panda" + str(i)) for i in range(4) ] self.models = [ loader.loadModel("models/carousel_panda") for i in range(4) ] self.moves = [0 for i in range(4)] for i in range(4): #set the position and orientation of the ith panda node we just created #The Z value of the position will be the base height of the pandas. #The headings are multiplied by i to put each panda in its own position #around the carousel self.pandas[i].setPosHpr(0, 0, 1.3, i * 90, 0, 0) #Load the actual panda model, and parent it to its dummy node self.models[i].reparentTo(self.pandas[i]) #Set the distance from the center. This distance is based on the way the #carousel was modeled in Maya self.models[i].setY(.85) #Load the environment (Sky sphere and ground plane) self.env = loader.loadModel("models/env") # print self.env.ls() self.env.find("**/polySurface1").node().removeGeom(0) self.env.reparentTo(render) self.env.setScale(7) # Add earth scattering self.renderPipeline.enableDefaultEarthScattering() self.loadSkybox() self.reloadShader() def loadSkybox(self): """ Loads the sample skybox. Will get replaced later """ self.skybox = self.loader.loadModel( "../../Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def reloadShader(self): self.renderPipeline.reloadShaders() render.setShader(self.renderPipeline.getDefaultObjectShader()) self.skybox.setShader( BetterShader.load("Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) #Panda Lighting def setupLights(self): # Add a directional light dPos = Vec3(40, 40, 40) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(2048) dirLight.setPssmTarget(self.cam, self.camLens) # dirLight.setAmbientColor(Vec3(0.1,0.1,0.1)) dirLight.setCastsShadows(True) dirLight.setPos(dPos) dirLight.setColor(Vec3(6)) self.renderPipeline.addLight(dirLight) self.renderPipeline.globalIllum.setTargetLight(dirLight) def startCarousel(self): #Here's where we actually create the intervals to move the carousel #The first type of interval we use is one created directly from a NodePath #This interval tells the NodePath to vary its orientation (hpr) from its #current value (0,0,0) to (360,0,0) over 20 seconds. Intervals created from #NodePaths also exist for position, scale, color, and shear self.carouselSpin = self.carousel.hprInterval(20, Vec3(360, 0, 0)) #Once an interval is created, we need to tell it to actually move. #start() will cause an interval to play once. loop() will tell an interval #to repeat once it finished. To keep the carousel turning, we use loop() self.carouselSpin.loop() #The next type of interval we use is called a LerpFunc interval. It is #called that becuase it linearly interpolates (aka Lerp) values passed to #a function over a given amount of time. #In this specific case, horses on a carousel don't move contantly up, #suddenly stop, and then contantly move down again. Instead, they start #slowly, get fast in the middle, and slow down at the top. This motion is #close to a sine wave. This LerpFunc calls the function oscilatePanda #(which we will create below), which changes the hieght of the panda based #on the sin of the value passed in. In this way we achieve non-linear #motion by linearly changing the input to a function for i in range(4): self.moves[i] = LerpFunc( self.oscilatePanda, #function to call duration=3, #3 second duration fromData=0, #starting value (in radians) toData=2 * pi, #ending value (2pi radians = 360 degrees) #Additional information to pass to #self.oscialtePanda extraArgs=[self.models[i], pi * (i % 2)]) #again, we want these to play continuously so we start them with loop() self.moves[i].loop() #Finally, we combine Sequence, Parallel, Func, and Wait intervals, #to schedule texture swapping on the lights to simulate the lights turning #on and off. #Sequence intervals play other intervals in a sequence. In other words, #it waits for the current interval to finish before playing the next #one. #Parallel intervals play a group of intervals at the same time #Wait intervals simply do nothing for a given amount of time #Func intervals simply make a single function call. This is helpful because #it allows us to schedule functions to be called in a larger sequence. They #take virtually no time so they don't cause a Sequence to wait. self.lightBlink = Sequence( #For the first step in our sequence we will set the on texture on one #light and set the off texture on the other light at the same time Parallel(Func(self.lights1.setTexture, self.lightOnTex, 1), Func(self.lights2.setTexture, self.lightOffTex, 1)), Wait(1), #Then we will wait 1 second #Then we will switch the textures at the same time Parallel(Func(self.lights1.setTexture, self.lightOffTex, 1), Func(self.lights2.setTexture, self.lightOnTex, 1)), Wait(1) #Then we will wait another second ) self.lightBlink.loop() #Loop this sequence continuously def oscilatePanda(self, rad, panda, offset): #This is the oscillation function mentioned earlier. It takes in a degree #value, a NodePath to set the height on, and an offset. The offset is there #so that the different pandas can move opposite to each other. #The .2 is the amplitude, so the height of the panda will vary from -.2 to #.2 panda.setZ(sin(rad + offset) * .2)
class World(ShowBase): def __init__(self): # Load the default configuration.prc. This is recommended, as it # contains some important panda options loadPrcFile("../../Config/configuration.prc") ShowBase.__init__(self) # Create a new pipeline instance self.renderPipeline = RenderingPipeline(self) # Set the base path for the pipeline. This is required as we are in # a subdirectory self.renderPipeline.getMountManager().setBasePath("../../") # Also set the write path self.renderPipeline.getMountManager().setWritePath("../../Temp/") # Load the default settings self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Now create the pipeline self.renderPipeline.create() # Add a directional light dPos = Vec3(40, 40, 40) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(4096) # dirLight.setAmbientColor(Vec3(0.1,0.1,0.1)) dirLight.setCastsShadows(True) dirLight.setPos(dPos) dirLight.setColor(Vec3(6)) self.renderPipeline.addLight(dirLight) self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} base.win.setClearColor(Vec4(0, 0, 0, 1)) # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.95, "[ESC]: Quit") self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.70, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.65, "[S]: Rotate Camera Right") # Set up the environment # This environment model contains collision meshes. If you look # in the egg file, you will see the following: # # <Collide> { Polyset keep descend } # # This tag causes the following mesh to be converted to a collision # mesh -- a mesh which is optimized for collision, not rendering. # It also keeps the original mesh, so there are now two copies --- # one optimized for rendering, one for collisions. self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) self.environ.setPos(0, 0, 0) self.environ.ls() self.environ.find("**/wall").removeNode() # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor("models/ralph", {"run": "models/ralph-run", "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos) # Create a floater object. We use the "floater" as a temporary # variable in a variety of calculations. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(render) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", 1]) self.accept("arrow_right", self.setKey, ["right", 1]) self.accept("arrow_up", self.setKey, ["forward", 1]) self.accept("a", self.setKey, ["cam-left", 1]) self.accept("s", self.setKey, ["cam-right", 1]) self.accept("arrow_left-up", self.setKey, ["left", 0]) self.accept("arrow_right-up", self.setKey, ["right", 0]) self.accept("arrow_up-up", self.setKey, ["forward", 0]) self.accept("a-up", self.setKey, ["cam-left", 0]) self.accept("s-up", self.setKey, ["cam-right", 0]) # NOTICE: It is important that your update tasks have a lower priority # than -10000 taskMgr.add(self.move, "moveTask", priority=-20000) self.accept("r", self.reloadShader) # Game state variables self.isMoving = False # Set up the camera base.disableMouse() base.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) # We will detect the height of the terrain by creating a collision # ray and casting it downward toward the terrain. One ray will # start above ralph's head, and the other will start above the camera. # A ray may hit the terrain, or it may hit a rock or a tree. If it # hits the terrain, we can detect the height. If it hits anything # else, we rule that the move is illegal. self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 1000) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 1000) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(BitMask32.bit(0)) self.camGroundCol.setIntoCollideMask(BitMask32.allOff()) self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays # self.ralphGroundColNp.show() # self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring # self.cTrav.showCollisions(render) # Add earth scattering self.renderPipeline.enableDefaultEarthScattering() self.prepareSRGB(render) self.loadSkybox() self.reloadShader() def loadSkybox(self): """ Loads the sample skybox. Will get replaced later """ self.skybox = self.loader.loadModel( "../../Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def reloadShader(self): self.renderPipeline.reloadShaders() render.setShader(self.renderPipeline.getDefaultObjectShader()) self.skybox.setShader(BetterShader.load( "Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # tex.setMinfilter(Texture.FTLinearMipmapLinear) # tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. base.camera.lookAt(self.ralph) if (self.keyMap["cam-left"] != 0): base.camera.setX(base.camera, -20 * globalClock.getDt()) if (self.keyMap["cam-right"] != 0): base.camera.setX(base.camera, +20 * globalClock.getDt()) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if (self.keyMap["left"] != 0): self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt()) if (self.keyMap["right"] != 0): self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt()) if (self.keyMap["forward"] != 0): self.ralph.setY(self.ralph, -25 * globalClock.getDt()) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if (self.keyMap["forward"] != 0) or (self.keyMap["left"] != 0) or (self.keyMap["right"] != 0): if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - base.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if (camdist > 10.0): base.camera.setPos(base.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if (camdist < 5.0): base.camera.setPos(base.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 # Now check for collisions. self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = [] for i in range(self.ralphGroundHandler.getNumEntries()): entry = self.ralphGroundHandler.getEntry(i) entries.append(entry) entries.sort(lambda x, y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if (len(entries) > 0) and (entries[0].getIntoNode().getName() == "terrain"): self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = [] for i in range(self.camGroundHandler.getNumEntries()): entry = self.camGroundHandler.getEntry(i) entries.append(entry) entries.sort(lambda x, y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if (len(entries) > 0) and (entries[0].getIntoNode().getName() == "terrain"): base.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if (base.camera.getZ() < self.ralph.getZ() + 2.0): base.camera.setZ(self.ralph.getZ() + 2.0) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.floater.setPos(self.ralph.getPos()) self.floater.setZ(self.ralph.getZ() + 2.0) base.camera.lookAt(self.floater) return task.cont
class World(ShowBase): def __init__(self): # Load the default configuration.prc. This is recommended, as it # contains some important panda options loadPrcFile("../../Config/configuration.prc") ShowBase.__init__(self) # Create a new pipeline instance self.renderPipeline = RenderingPipeline(self) # Set the base path for the pipeline. This is required as we are in # a subdirectory self.renderPipeline.getMountManager().setBasePath("../../") # Also set the write path self.renderPipeline.getMountManager().setWritePath("../../Temp/") # Load the default settings self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Now create the pipeline self.renderPipeline.create() # Add a directional light dPos = Vec3(40, 40, 40) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(2048) dirLight.setPssmTarget(self.cam, self.camLens) # dirLight.setAmbientColor(Vec3(0.1,0.1,0.1)) dirLight.setCastsShadows(True) dirLight.setPos(dPos) dirLight.setColor(Vec3(6)) self.renderPipeline.addLight(dirLight) self.renderPipeline.globalIllum.setTargetLight(dirLight) self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0 } base.win.setClearColor(Vec4(0, 0, 0, 1)) # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.95, "[ESC]: Quit") self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.70, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.65, "[S]: Rotate Camera Right") # Set up the environment # This environment model contains collision meshes. If you look # in the egg file, you will see the following: # # <Collide> { Polyset keep descend } # # This tag causes the following mesh to be converted to a collision # mesh -- a mesh which is optimized for collision, not rendering. # It also keeps the original mesh, so there are now two copies --- # one optimized for rendering, one for collisions. self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) self.environ.setPos(0, 0, 0) self.environ.find("**/wall").removeNode() # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor("models/ralph", { "run": "models/ralph-run", "walk": "models/ralph-walk" }) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos) # Create a floater object. We use the "floater" as a temporary # variable in a variety of calculations. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(render) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", 1]) self.accept("arrow_right", self.setKey, ["right", 1]) self.accept("arrow_up", self.setKey, ["forward", 1]) self.accept("a", self.setKey, ["cam-left", 1]) self.accept("s", self.setKey, ["cam-right", 1]) self.accept("arrow_left-up", self.setKey, ["left", 0]) self.accept("arrow_right-up", self.setKey, ["right", 0]) self.accept("arrow_up-up", self.setKey, ["forward", 0]) self.accept("a-up", self.setKey, ["cam-left", 0]) self.accept("s-up", self.setKey, ["cam-right", 0]) # NOTICE: It is important that your update tasks have a lower priority # than -10000 taskMgr.add(self.move, "moveTask", priority=-20000) self.accept("r", self.reloadShader) # Game state variables self.isMoving = False # Set up the camera base.disableMouse() base.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) # We will detect the height of the terrain by creating a collision # ray and casting it downward toward the terrain. One ray will # start above ralph's head, and the other will start above the camera. # A ray may hit the terrain, or it may hit a rock or a tree. If it # hits the terrain, we can detect the height. If it hits anything # else, we rule that the move is illegal. self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 1000) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 1000) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(BitMask32.bit(0)) self.camGroundCol.setIntoCollideMask(BitMask32.allOff()) self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays # self.ralphGroundColNp.show() # self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring # self.cTrav.showCollisions(render) # Add earth scattering self.renderPipeline.enableDefaultEarthScattering() self.prepareSRGB(render) self.loadSkybox() self.reloadShader() def loadSkybox(self): """ Loads the sample skybox. Will get replaced later """ self.skybox = self.loader.loadModel( "../../Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def reloadShader(self): self.renderPipeline.reloadShaders() render.setShader(self.renderPipeline.getDefaultObjectShader()) self.skybox.setShader( BetterShader.load("Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # tex.setMinfilter(Texture.FTLinearMipmapLinear) # tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. base.camera.lookAt(self.ralph) if (self.keyMap["cam-left"] != 0): base.camera.setX(base.camera, -20 * globalClock.getDt()) if (self.keyMap["cam-right"] != 0): base.camera.setX(base.camera, +20 * globalClock.getDt()) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if (self.keyMap["left"] != 0): self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt()) if (self.keyMap["right"] != 0): self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt()) if (self.keyMap["forward"] != 0): self.ralph.setY(self.ralph, -25 * globalClock.getDt()) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if (self.keyMap["forward"] != 0) or (self.keyMap["left"] != 0) or (self.keyMap["right"] != 0): if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - base.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if (camdist > 7.0): base.camera.setPos(base.camera.getPos() + camvec * (camdist - 7)) camdist = 7.0 if (camdist < 5.0): base.camera.setPos(base.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 # Now check for collisions. self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = [] for i in range(self.ralphGroundHandler.getNumEntries()): entry = self.ralphGroundHandler.getEntry(i) entries.append(entry) entries.sort(lambda x, y: cmp( y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if (len(entries) > 0) and (entries[0].getIntoNode().getName() == "terrain"): self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = [] for i in range(self.camGroundHandler.getNumEntries()): entry = self.camGroundHandler.getEntry(i) entries.append(entry) entries.sort(lambda x, y: cmp( y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if (len(entries) > 0) and (entries[0].getIntoNode().getName() == "terrain"): base.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if (base.camera.getZ() < self.ralph.getZ() + 5.0): base.camera.setZ(self.ralph.getZ() + 5.0) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.floater.setPos(self.ralph.getPos()) self.floater.setZ(self.ralph.getZ() + 2.0) base.camera.lookAt(self.floater) return task.cont
class Main(ShowBase, DebugObject): """ This is the render pipeline testing showbase """ def __init__(self): DebugObject.__init__(self, "Main") self.debug("Bit System =", 8 * struct.calcsize("P")) # Load engine configuration self.debug("Loading panda3d configuration from configuration.prc ..") loadPrcFile("Config/configuration.prc") # Init the showbase ShowBase.__init__(self) # Create the render pipeline self.debug("Creating pipeline") self.renderPipeline = RenderingPipeline(self) self.renderPipeline.loadSettings("Config/pipeline.ini") # Uncomment to use temp directory # writeDirectory = tempfile.mkdtemp(prefix='Shader-tmp') # writeDirectory = "Temp/" # Clear write directory when app exits # atexit.register(os.remove, writeDirectory) # Set a write directory, where the shader cache and so on is stored # self.renderPipeline.getMountManager().setWritePath(writeDirectory) self.renderPipeline.getMountManager().setBasePath(".") ####### END OF RENDER PIPELINE SETUP ####### # Load some demo source # self.sceneSource = "Demoscene.ignore/sponza.egg.bam" # self.sceneSource = "Demoscene.ignore/occlusionTest/Model.egg" # self.sceneSource = "Demoscene.ignore/lost-empire/Model.egg" # self.sceneSource = "Models/PSSMTest/Model.egg.bam" # self.sceneSource = "Scene.ignore/Car.bam" # self.sceneSource = "Demoscene.ignore/GITest/Model.egg" # self.sceneSource = "Demoscene.ignore/PSSMTest/Model.egg.bam" # self.sceneSource = "Models/Raventon/Model.egg" # self.sceneSource = "Demoscene.ignore/Room/LivingRoom.egg.bam" self.sceneSource = "Toolkit/Blender Material Library/MaterialLibrary.egg" # If global illumination is enabled, load the voxel grid GlobalIllumination.setSceneRoot( "Toolkit/Blender Material Library/voxelized/") # Create the pipeline, and enable scattering self.renderPipeline.create() self.renderPipeline.enableDefaultEarthScattering() # Load scene from disk self.debug("Loading Scene '" + self.sceneSource + "'") self.scene = self.loader.loadModel(self.sceneSource) # Wheter to use a ground floor self.usePlane = False self.sceneWireframe = False # Flatten scene? self.scene.flattenStrong() # Load ground plane if configured if self.usePlane: self.groundPlane = self.loader.loadModel( "Models/Plane/Model.egg.bam") self.groundPlane.setPos(0, 0, -0.01) self.groundPlane.setScale(2.0) self.groundPlane.setTwoSided(True) self.groundPlane.flattenStrong() self.groundPlane.reparentTo(self.scene) # Some artists really don't know about backface culling # self.scene.setTwoSided(True) # Required for tesselation # self.convertToPatches(self.scene) self.scene.reparentTo(self.render) # Prepare textures with SRGB format self.prepareSRGB(self.scene) # Create movement controller (Freecam) self.controller = MovementController(self) self.controller.setInitialPosition( Vec3(0.422895, -6.49557, 4.72692), Vec3(0, 0, 3)) self.controller.setup() # Hotkey for wireframe self.accept("f3", self.toggleSceneWireframe) # Hotkey to reload all shaders self.accept("r", self.setShaders) # Create a sun light dPos = Vec3(60, 30, 100) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(4096) dirLight.setAmbientColor(Vec3(0.5, 0.5, 0.5)) dirLight.setCastsShadows(True) dirLight.setPos(dPos) dirLight.setColor(Vec3(4)) self.renderPipeline.addLight(dirLight) self.dirLight = dirLight sunPos = Vec3(56.7587, -31.3601, 189.196) self.dirLight.setPos(sunPos) self.dirLight.setDirection(sunPos) # Slider to move the sun if self.renderPipeline.settings.displayOnscreenDebugger: self.renderPipeline.guiManager.demoSlider.node[ "command"] = self.setSunPos self.lastSliderValue = 0.0 # Load skybox self.skybox = None self.loadSkybox() # Set default object shaders self.setShaders(refreshPipeline=False) def setSunPos(self): """ Sets the sun position based on the debug slider """ radial = True rawValue = self.renderPipeline.guiManager.demoSlider.node["value"] diff = self.lastSliderValue - rawValue self.lastSliderValue = rawValue if radial: rawValue = rawValue / 100.0 * 2.0 * math.pi dPos = Vec3( math.sin(rawValue) * 100.0, math.cos(rawValue) * 100.0, 100) # dPos = Vec3(100, 100, (rawValue - 50) * 10.0) else: dPos = Vec3(30, (rawValue - 50), 100) if abs(diff) > 0.0001: self.dirLight.setPos(dPos) self.dirLight.setDirection(dPos) def toggleSceneWireframe(self): """ Toggles the scene rendermode """ self.sceneWireframe = not self.sceneWireframe if self.sceneWireframe: self.scene.setRenderModeWireframe() else: self.scene.clearRenderMode() def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() # Only diffuse textures should be SRGB if "diffuse" in tex.getName().lower(): print "Preparing texture", tex.getName() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # All textures should have the correct filter modes tex.setMinfilter(Texture.FTLinearMipmapLinear) tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) def loadLights(self, scene): """ Loads lights from a .egg. Lights should be empty objects (blender) """ model = self.loader.loadModel(scene) lights = model.findAllMatches("**/PointLight*") for prefab in lights: light = PointLight() light.setRadius(prefab.getScale().x) light.setColor(Vec3(2)) light.setPos(prefab.getPos()) light.setShadowMapResolution(2048) light.setCastsShadows(False) self.renderPipeline.addLight(light) print "Adding Light:", prefab.getPos(), prefab.getScale() self.lights.append(light) self.initialLightPos.append(prefab.getPos()) self.test = light def loadSkybox(self): """ Loads the skybox """ self.skybox = self.loader.loadModel("Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def setShaders(self, refreshPipeline=True): """ Sets all shaders """ self.debug("Reloading Shaders ..") if self.renderPipeline: self.scene.setShader( self.renderPipeline.getDefaultObjectShader(False)) if refreshPipeline: self.renderPipeline.reloadShaders() if self.skybox: self.skybox.setShader(BetterShader.load( "Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) def convertToPatches(self, model): """ Converts a model to patches. This is REQUIRED before beeing able to use it with tesselation shaders """ self.debug("Converting model to patches ..") for node in model.find_all_matches("**/+GeomNode"): geom_node = node.node() num_geoms = geom_node.get_num_geoms() for i in range(num_geoms): geom_node.modify_geom(i).make_patches_in_place()
class Main(ShowBase, DebugObject): """ This is the render pipeline testing showbase """ def __init__(self): DebugObject.__init__(self, "Main") self.debug("Bit System =", 8 * struct.calcsize("P")) # Load engine configuration self.debug("Loading panda3d configuration from configuration.prc ..") loadPrcFile("Config/configuration.prc") # Init the showbase ShowBase.__init__(self) # Create the render pipeline self.debug("Creating pipeline") self.renderPipeline = RenderingPipeline(self) # Uncomment to use temp directory # writeDirectory = tempfile.mkdtemp(prefix='Shader-tmp') # writeDirectory = "Temp/" # Clear write directory when app exits # atexit.register(os.remove, writeDirectory) # Set a write directory, where the shader cache and so on is stored # self.renderPipeline.getMountManager().setWritePath(writeDirectory) self.renderPipeline.getMountManager().setBasePath(".") ####### END OF RENDER PIPELINE SETUP ####### # Load some demo source # self.sceneSource = "Demoscene.ignore/sponza.egg.bam" # self.sceneSource = "Demoscene.ignore/occlusionTest/Model.egg" # self.sceneSource = "Demoscene.ignore/lost-empire/Model.egg" # self.sceneSource = "Models/PSSMTest/Model.egg.bam" # self.sceneSource = "Demoscene.ignore/GITest/Model.egg" # self.sceneSource = "Demoscene.ignore/PSSMTest/Model.egg.bam" # self.sceneSource = "Demoscene.ignore/Room/LivingRoom.egg" # self.sceneSource = "Models/CornelBox/Model.egg" # self.sceneSource = "Models/HouseSet/Model.egg" self.sceneSource = "Toolkit/Blender Material Library/MaterialLibrary.egg" self.renderPipeline.loadSettings("Config/pipeline.ini") # Create the pipeline, and enable scattering self.renderPipeline.create() self.renderPipeline.enableDefaultEarthScattering() # Load scene from disk self.debug("Loading Scene '" + self.sceneSource + "'") self.scene = self.loader.loadModel(self.sceneSource) # Wheter to use a ground floor self.usePlane = False self.sceneWireframe = False # Flatten scene? self.scene.flattenStrong() self.scene.analyze() # Load ground plane if configured if self.usePlane: self.groundPlane = self.loader.loadModel( "Models/Plane/Model.egg.bam") self.groundPlane.setPos(0, 0, -0.01) self.groundPlane.setScale(2.0) self.groundPlane.setTwoSided(True) self.groundPlane.flattenStrong() self.groundPlane.reparentTo(self.scene) # Some artists really don't know about backface culling # self.scene.setTwoSided(True) # Required for tesselation # self.convertToPatches(self.scene) self.scene.reparentTo(self.render) # Prepare textures with SRGB format self.prepareSRGB(self.scene) # Create movement controller (Freecam)wwww self.controller = MovementController(self) self.controller.setInitialPosition(Vec3(0, -5, 5.0), Vec3(0, 0, 5)) self.controller.setup() # Hotkey for wireframe self.accept("f3", self.toggleSceneWireframe) # Hotkey to reload all shaders self.accept("r", self.setShaders) # for i in xrange(1): # pointLight = PointLight() # pointLight.setPos(Vec3( (i-1)*3, 0, 7)) # pointLight.setColor(Vec3(0.1)) # pointLight.setShadowMapResolution(1024) # pointLight.setRadius(50) # pointLight.setCastsShadows(True) # # pointLight.attachDebugNode(render) # self.renderPipeline.addLight(pointLight) # Create a sun light dPos = Vec3(60, 30, 100) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(2048) dirLight.setAmbientColor(Vec3(0.0, 0.0, 0.0)) dirLight.setPos(dPos) dirLight.setColor(Vec3(3)) dirLight.setPssmTarget(base.cam, base.camLens) dirLight.setCastsShadows(True) self.renderPipeline.addLight(dirLight) self.dirLight = dirLight sunPos = Vec3(56.7587, -31.3601, 189.196) self.dirLight.setPos(sunPos) self.dirLight.setDirection(sunPos) # Tell the GI which light casts the GI self.renderPipeline.setGILightSource(dirLight) # Slider to move the sun if self.renderPipeline.settings.displayOnscreenDebugger: self.renderPipeline.guiManager.demoSlider.node[ "command"] = self.setSunPos self.renderPipeline.guiManager.demoSlider.node["value"] = 20 self.lastSliderValue = 0.0 # Load skyboxn self.skybox = None self.loadSkybox() # Set default object shaders self.setShaders(refreshPipeline=False) # Show windows # for window in base.graphicsEngine.getWindows(): # print window.getName(), window.getSort() def setSunPos(self): """ Sets the sun position based on the debug slider """ radial = True rawValue = self.renderPipeline.guiManager.demoSlider.node["value"] diff = self.lastSliderValue - rawValue self.lastSliderValue = rawValue if radial: rawValue = rawValue / 100.0 * 2.0 * math.pi dPos = Vec3( math.sin(rawValue) * 100.0, math.cos(rawValue) * 100.0, 100) # dPos = Vec3(100, 100, (rawValue - 50) * 10.0) else: dPos = Vec3(30, (rawValue - 50) * 1.5, 100) if abs(diff) > 0.0001: self.dirLight.setPos(dPos) self.dirLight.setDirection(dPos) def toggleSceneWireframe(self): """ Toggles the scene rendermode """ self.sceneWireframe = not self.sceneWireframe if self.sceneWireframe: self.scene.setRenderModeWireframe() else: self.scene.clearRenderMode() def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() # Only diffuse textures should be SRGB if "diffuse" in tex.getName().lower(): print "Preparing texture", tex.getName() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) elif baseFormat == Texture.FSrgb or baseFormat == Texture.FSrgbAlpha: # Format is okay already pass else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # All textures should have the correct filter modes tex.setMinfilter(Texture.FTLinearMipmapLinear) tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) def loadLights(self, scene): """ Loads lights from a .egg. Lights should be empty objects (blender) """ model = self.loader.loadModel(scene) lights = model.findAllMatches("**/PointLight*") for prefab in lights: light = PointLight() light.setRadius(prefab.getScale().x) light.setColor(Vec3(2)) light.setPos(prefab.getPos()) light.setShadowMapResolution(2048) light.setCastsShadows(False) self.renderPipeline.addLight(light) print "Adding Light:", prefab.getPos(), prefab.getScale() self.lights.append(light) self.initialLightPos.append(prefab.getPos()) self.test = light def loadSkybox(self): """ Loads the skybox """ self.skybox = self.loader.loadModel("Models/Skybox/Model.egg.bam") self.skybox.setScale(40000) self.skybox.reparentTo(self.render) def setShaders(self, refreshPipeline=True): """ Sets all shaders """ self.debug("Reloading Shaders ..") if self.renderPipeline: self.scene.setShader( self.renderPipeline.getDefaultObjectShader(False)) if refreshPipeline: self.renderPipeline.reloadShaders() if self.skybox: self.skybox.setShader( BetterShader.load("Shader/DefaultObjectShader/vertex.glsl", "Shader/Skybox/fragment.glsl")) def convertToPatches(self, model): """ Converts a model to patches. This is REQUIRED before beeing able to use it with tesselation shaders """ self.debug("Converting model to patches ..") for node in model.find_all_matches("**/+GeomNode"): geom_node = node.node() num_geoms = geom_node.get_num_geoms() for i in range(num_geoms): geom_node.modify_geom(i).make_patches_in_place()
class Main(ShowBase, DebugObject): """ This is the material explorer. You can try different materials""" def __init__(self): DebugObject.__init__(self, "Main") self.debug("Bit System =", 8 * struct.calcsize("P")) # Load engine configuration self.debug("Loading panda3d configuration from configuration.prc ..") loadPrcFile("../../Config/configuration.prc") # Init the showbase ShowBase.__init__(self) # Create the render pipeline self.debug("Creating pipeline") self.renderPipeline = RenderingPipeline(self) # Set a write directory, where the shader cache and so on is stored # self.renderPipeline.getMountManager().setWritePath(writeDirectory) self.renderPipeline.getMountManager().setBasePath("../../") self.renderPipeline.loadSettings("../../Config/pipeline.ini") # Create the pipeline, and enable scattering self.renderPipeline.create() self.renderPipeline.enableDefaultEarthScattering() # Load some demo source self.sceneSource = "Models/SmoothCube/Cube.bam" # Load scene from disk self.debug("Loading Scene '" + self.sceneSource + "'") self.model = self.loader.loadModel(self.sceneSource) self.scene = render.attachNewNode("Scene") self.model.reparentTo(self.scene) self.model.setZ(1.0) # Wheter to use a ground floor self.usePlane = True self.sceneWireframe = False # Flatten scene self.scene.flattenStrong() # Load ground plane if configured if self.usePlane: self.groundPlane = self.loader.loadModel( "Models/Plane/Model.egg.bam") self.groundPlane.setPos(0, 0, 0) self.groundPlane.setScale(2.0) self.groundPlane.setTwoSided(True) self.groundPlane.flattenStrong() self.groundPlane.reparentTo(self.scene) # Prepare textures with SRGB format self.prepareSRGB(self.scene) # Create movement controller (Freecam) self.controller = MovementController(self) self.controller.setInitialPosition( Vec3(0, -5, 5.0), Vec3(0, 0, 5)) self.controller.setup() # Hotkey for wireframe self.accept("f3", self.toggleSceneWireframe) # Hotkey to reload all shaders self.accept("r", self.setShaders) # Create a sun light dPos = Vec3(60, 30, 100) dirLight = DirectionalLight() dirLight.setDirection(dPos) dirLight.setShadowMapResolution(2048) dirLight.setAmbientColor(Vec3(0.0, 0.0, 0.0)) dirLight.setPos(dPos) dirLight.setColor(Vec3(3)) dirLight.setPssmTarget(base.cam, base.camLens) dirLight.setPssmDistance(50.0) dirLight.setCastsShadows(True) self.renderPipeline.addLight(dirLight) self.dirLight = dirLight sunPos = Vec3(56.7587, -31.3601, 189.196) self.dirLight.setPos(sunPos) self.dirLight.setDirection(sunPos) # Tell the GI which light casts the GI self.renderPipeline.setGILightSource(dirLight) # Slider to move the sun if self.renderPipeline.settings.displayOnscreenDebugger: self.renderPipeline.guiManager.demoSlider.node[ "command"] = self.setSunPos self.renderPipeline.guiManager.demoSlider.node[ "value"] = 20 self.lastSliderValue = 0.0 # Load skyboxn self.skybox = self.renderPipeline.getDefaultSkybox() self.skybox.reparentTo(render) # Set default object shaders self.setShaders(refreshPipeline=False) self.createGUI() def createGUI(self): self.slider_opts = { "roughness": { "name": "Roughness", "min": 0.0001, "max": 1.0, "default": 0.4, }, "metallic": { "name": "Metallic", "min": 0.0001, "max": 1.0, "default": 0.0, }, "specular": { "name": "Specular", "min": 0.0001, "max": 1.0, "default": 0.5, }, "basecolor_r": { "name": "Base Color [Red]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(1,0.2,0.2) }, "basecolor_g": { "name": "Base Color [Green]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(0.6,1.0,0.2) }, "basecolor_b": { "name": "Base Color [Blue]", "min": 0.0001, "max": 1.0, "default": 1.0, "color": Vec3(0.2,0.6,1) }, } self.sliderOrder = ["roughness", "metallic", "specular", "", "basecolor_r", "basecolor_g", "basecolor_b"] self.guiParent = UIWindow( "Material Explorer", 280, 400) self.guiParent.getNode().setPos(self.win.getXSize() - 340, 0, -120) self.windowNode = self.guiParent.getContentNode() currentY = 5 for name in self.sliderOrder: if name == "": currentY += 30 continue opts = self.slider_opts[name] opts["slider"] = BetterSlider( x=20, y=currentY+20, size=230, minValue=opts["min"],maxValue=opts["max"], value=opts["default"], parent=self.windowNode, callback=self.materialOptionChanged) col = Vec3(1.0) if "color" in opts: col = opts["color"] opts["label"] = BetterOnscreenText(x=20, y=currentY, text=opts["name"], align="left", parent=self.windowNode, size=15, color=col) opts["value_label"] = BetterOnscreenText(x=250, y=currentY, text=str(opts["default"]), align="right", parent=self.windowNode, size=15, color=Vec3(0.6),mayChange=True) currentY += 50 def materialOptionChanged(self): container = self.model for name, opt in self.slider_opts.items(): container.setShaderInput("opt_" + name, opt["slider"].getValue()) opt["value_label"].setText("{:0.4f}".format(opt["slider"].getValue())) def setSunPos(self): """ Sets the sun position based on the debug slider """ radial = True rawValue = self.renderPipeline.guiManager.demoSlider.node["value"] diff = self.lastSliderValue - rawValue self.lastSliderValue = rawValue if radial: rawValue = rawValue / 100.0 * 2.0 * math.pi dPos = Vec3( math.sin(rawValue) * 100.0, math.cos(rawValue) * 100.0, 100) # dPos = Vec3(100, 100, (rawValue - 50) * 10.0) else: dPos = Vec3(30, (rawValue - 50) * 1.5, 100) if abs(diff) > 0.0001: self.dirLight.setPos(dPos) self.dirLight.setDirection(dPos) def toggleSceneWireframe(self): """ Toggles the scene rendermode """ self.sceneWireframe = not self.sceneWireframe if self.sceneWireframe: self.scene.setRenderModeWireframe() else: self.scene.clearRenderMode() def prepareSRGB(self, np): """ Sets the correct texture format for all textures found in <np> """ for tex in np.findAllTextures(): baseFormat = tex.getFormat() # Only diffuse textures should be SRGB if "diffuse" in tex.getName().lower(): print "Preparing texture", tex.getName() if baseFormat == Texture.FRgb: tex.setFormat(Texture.FSrgb) elif baseFormat == Texture.FRgba: tex.setFormat(Texture.FSrgbAlpha) elif baseFormat == Texture.FSrgb or baseFormat == Texture.FSrgbAlpha: # Format is okay already pass else: print "Unkown texture format:", baseFormat print "\tTexture:", tex # All textures should have the correct filter modes tex.setMinfilter(Texture.FTLinearMipmapLinear) tex.setMagfilter(Texture.FTLinear) tex.setAnisotropicDegree(16) def setShaders(self, refreshPipeline=True): """ Sets all shaders """ self.debug("Reloading Shaders ..") if self.renderPipeline: self.scene.setShader( self.renderPipeline.getDefaultObjectShader(False)) self.model.setShader(Shader.load(Shader.SLGLSL, "DefaultObjectShader/vertex.glsl", "dynamicMaterialFragment.glsl")) if refreshPipeline: self.renderPipeline.reloadShaders() if self.skybox: self.skybox.setShader(Shader.load(Shader.SLGLSL, "DefaultObjectShader/vertex.glsl", "Skybox/fragment.glsl")) def convertToPatches(self, model): """ Converts a model to patches. This is REQUIRED before beeing able to use it with tesselation shaders """ self.debug("Converting model to patches ..") for node in model.find_all_matches("**/+GeomNode"): geom_node = node.node() num_geoms = geom_node.get_num_geoms() for i in range(num_geoms): geom_node.modify_geom(i).make_patches_in_place()