def toggleToonShading(self, checked): filters = CommonFilters(base.win, base.cam) if checked == True: filters.setCartoonInk() else: filters.delCartoonInk()
class filters(): def __init__(self): self.filters = CommonFilters(base.win, base.cam) self.initCartoonInk() def initCartoonInk(self): self.filters.setCartoonInk(separation=-1)
def __init__(self): ShowBase.__init__(self) # Gui #gui = GUI(self) render.setShaderAuto() self.game = Game(self) filters = CommonFilters(base.win, base.cam) filters.setBloom() filters.setCartoonInk()
def __init__(self): super().__init__(self) self.var1 = 0 self.scene = loader.loadModel("models/world") playerTexture = loader.loadTexture("models/starfoxShip.jpg") self.player = self.scene.find("player") self.player.setTexture(playerTexture) base.setBackgroundColor(0.1, 0.1, 0.1, 1) enemyTexture = loader.loadTexture("models/enemyShip.jpg") self.enemy = self.scene.find("enemy1") self.enemy.setTexture(enemyTexture) self.basePlane = self.scene.find("basePlane") self.scene.reparentTo(self.render) self.player.setPos(50, 50, 3) self.enemy.setPos(50, 55, 0) self.ambient = AmbientLight("ambient") self.ambient.color = (0.1, 0.1, 0.1, 1) self.ambientPath = self.render.attachNewNode(self.ambient) render.setLight(self.ambientPath) self.dirLight = DirectionalLight("dir light") self.dirLight.color = (1, 1, 1, 1) self.dirLightPath = self.render.attachNewNode(self.dirLight) self.dirLightPath.setHpr(0, -90, 0) self.dirLight.setShadowCaster(True, 512, 512) render.setLight(self.dirLightPath) self.pointLight = PointLight("point light") self.pointLight.color = (1, 1, 1, 1) self.pointLightPath = self.render.attachNewNode(self.pointLight) self.pointLightPath.setPos(50, 52.5, 4) self.pointLight.attenuation = (.5, 0, 0) self.pointLight.setShadowCaster(True, 1024, 1024) self.render.setLight(self.pointLightPath) self.fog = Fog("fog") self.fog.setColor(0.1, 0.1, 0.1) self.fog.setExpDensity(.3) self.fog.setLinearRange(150, 300) self.fog.setLinearFallback(45, 160, 320) self.render.setFog(self.fog) self.render.setShaderAuto() self.render.setAntialias(AntialiasAttrib.MAuto) filters = CommonFilters(base.win, base.cam) filters.setBloom(size="large") filters.setAmbientOcclusion(strength=0.6, falloff=0.0005, radius=0.1) filters.setCartoonInk(separation=2, color=(0, 0, 0, 1)) self.taskMgr.add(self.update, "update")
class filters(): def __init__(self): self.filters = CommonFilters(base.win, base.cam) self.initCartoonInk() def initCartoonInk(self): self.filters.setCartoonInk(separation= -1)
class ToonMaker(DirectObject): def __init__(self): base.disableMouse() base.cam.node().getLens().setNear(10.0) base.cam.node().getLens().setFar(9999999) camera.setPos(0, -50, 0) # Check video card capabilities. if (base.win.getGsg().getSupportsBasicShaders() == 0): addTitle("Toon Shader: Video driver reports that shaders are not supported.") return # Enable a 'light ramp' - this discretizes the lighting, # which is half of what makes a model look like a cartoon. # Light ramps only work if shader generation is enabled, # so we call 'setShaderAuto'. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) tempnode.setShaderAuto() base.cam.node().setInitialState(tempnode.getState()) # Use class 'CommonFilters' to enable a cartoon inking filter. # This can fail if the video card is not powerful enough, if so, # display an error and exit. self.separation = 1 # Pixels self.filters = CommonFilters(base.win, base.cam) filterok = self.filters.setCartoonInk(separation=self.separation) if (filterok == False): addTitle("Toon Shader: Video card not powerful enough to do image postprocessing") return # Create a non-attenuating point light and an ambient light. plightnode = PointLight("point light") plightnode.setAttenuation(Vec3(1,0,0)) plight = render.attachNewNode(plightnode) plight.setPos(30,-50,0) alightnode = AmbientLight("ambient light") alightnode.setColor(Vec4(0.8,0.8,0.8,1)) alight = render.attachNewNode(alightnode) render.setLight(alight) render.setLight(plight) # Panda contains a built-in viewer that lets you view the # results of all render-to-texture operations. This lets you # see what class CommonFilters is doing behind the scenes. self.accept("v", base.bufferViewer.toggleEnable) self.accept("V", base.bufferViewer.toggleEnable) base.bufferViewer.setPosition("llcorner") self.accept("s", self.filters.manager.resizeBuffers) # These allow you to change cartooning parameters in realtime self.accept("escape", sys.exit, [0])
def setupLighting(activeScreen): #add one light per face, so each face is nicely illuminated filters = CommonFilters(base.win, base.cam) filters.setCartoonInk(.4) plight1 = PointLight('plight') plight1.setColor(VBase4(1, 1, 1, 1)) plight1NodePath = activeScreen.attachNewNode(plight1) plight1NodePath.setPos(0, 0, 500) activeScreen.setLight(plight1NodePath) plight2 = PointLight('plight') plight2.setColor(VBase4(1, 1, 1, 1)) plight2NodePath = activeScreen.attachNewNode(plight2) plight2NodePath.setPos(0, 0, -500) activeScreen.setLight(plight2NodePath) plight3 = PointLight('plight') plight3.setColor(VBase4(1, 1, 1, 1)) plight3NodePath = activeScreen.attachNewNode(plight3) plight3NodePath.setPos(0, -500, 0) activeScreen.setLight(plight3NodePath) plight4 = PointLight('plight') plight4.setColor(VBase4(1, 1, 1, 1)) plight4NodePath = activeScreen.attachNewNode(plight4) plight4NodePath.setPos(0, 500, 0) activeScreen.setLight(plight4NodePath) plight5 = PointLight('plight') plight5.setColor(VBase4(1, 1, 1, 1)) plight5NodePath = activeScreen.attachNewNode(plight5) plight5NodePath.setPos(500,0, 0) activeScreen.setLight(plight5NodePath) plight6 = PointLight('plight') plight6.setColor(VBase4(1, 1, 1, 1)) plight6NodePath = activeScreen.attachNewNode(plight6) plight6NodePath.setPos(-500,0, 0) activeScreen.setLight(plight6NodePath)
class World(ShowBase, object): def __init__(self, camp=[2000, 500, 2000], lookatp=[0, 0, 250], up=[0, 0, 1], fov=40, w=2000, h=1500): """ :param camp: :param lookatp: :param fov: :param w: width of window :param h: height of window """ super(self.__class__, self).__init__() self.setBackgroundColor(1, 1, 1) # set up cartoon effect self.__separation = 1 self.filters = CommonFilters(self.win, self.cam) self.filters.setCartoonInk(separation=self.__separation) # set up lens lens = PerspectiveLens() lens.setFov(fov) lens.setNearFar(1, 50000) self.disableMouse() self.cam.setPos(camp[0], camp[1], camp[2]) self.cam.lookAt(Point3(lookatp[0], lookatp[1], lookatp[2]), Vec3(up[0], up[1], up[2])) self.cam.node().setLens(lens) # set up slight ablight = AmbientLight("ambientlight") ablight.setColor(Vec4(0.2, 0.2, 0.2, 1)) ablightnode = self.cam.attachNewNode(ablight) self.render.setLight(ablightnode) ptlight0 = PointLight("pointlight1") ptlight0.setColor(VBase4(1, 1, 1, 1)) ptlightnode0 = self.cam.attachNewNode(ptlight0) ptlightnode0.setPos(0, 0, 0) self.render.setLight(ptlightnode0) ptlight1 = PointLight("pointlight1") ptlight1.setColor(VBase4(.4, .4, .4, 1)) ptlightnode1 = self.cam.attachNewNode(ptlight1) ptlightnode1.setPos(self.cam.getPos().length(), 0, self.cam.getPos().length()) self.render.setLight(ptlightnode1) ptlight2 = PointLight("pointlight2") ptlight2.setColor(VBase4(.3, .3, .3, 1)) ptlightnode2 = self.cam.attachNewNode(ptlight2) ptlightnode2.setPos(-self.cam.getPos().length(), 0, base.cam.getPos().length()) self.render.setLight(ptlightnode2) # set up inputmanager self.inputmgr = im.InputManager(self, lookatp) taskMgr.add(self.cycleUpdate, "cycle update") # set up rotational cam # self.lookatp = lookatp # taskMgr.doMethodLater(.1, self.rotateCam, "rotate cam") # set window size props = WindowProperties() props.setSize(w, h) self.win.requestProperties(props) def cycleUpdate(self, task): # reset aspect ratio aspectRatio = self.getAspectRatio() self.cam.node().getLens().setAspectRatio(aspectRatio) self.inputmgr.checkMouse1Drag() self.inputmgr.checkMouse2Drag() self.inputmgr.checkMouseWheel() return task.cont def rotateCam(self, task): campos = self.cam.getPos() camangle = math.atan2(campos[1], campos[0]) # print camangle if camangle < 0: camangle += math.pi * 2 if camangle >= math.pi * 2: camangle = 0 else: camangle += math.pi / 180 camradius = math.sqrt(campos[0] * campos[0] + campos[1] * campos[1]) camx = camradius * math.cos(camangle) camy = camradius * math.sin(camangle) self.cam.setPos(camx, camy, campos[2]) self.cam.lookAt(self.lookatp[0], self.lookatp[1], self.lookatp[2]) return task.cont def changeLookAt(self, lookatp): self.cam.lookAt(lookatp[0], lookatp[1], lookatp[2]) self.inputmgr = im.InputManager(base, lookatp) # def setRenderEffect(base): # """ # Set a cartoonink shader and the background color etc to base # # ## input: # a showbase object # # author: weiwei # date: 20160616 # """ # try: # # setbgcolor must be done before setting shader # base.setBackgroundColor(1, 1, 1) # # # base.render.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) # # base.render.setShaderAuto() # base.separation = 1 # Pixels # base.filters = CommonFilters(base.win, base.cam) # base.filters.setCartoonInk(separation=base.separation) # # # base.loadPrcFileData("", "framebuffer-multisample 2") # # base.loadPrcFileData('', 'multisamples 8') # # base.render.setAntialias(AntialiasAttrib.MMultisample+AntialiasAttrib.MAuto) # except: # pass # # # def setNodeCartoon(nodepath): # """ # Set a cartoon-style shader, set nodepath # TODO: not working now (you have to implement an independant shader # # ## input: # base # a showbase object # nodepath # a pand3d nodepath # # author: weiwei # date: 20160620 # """ # try: # # setbgcolor must be done before setting shader # nodepath.setBackgroundColor(1, 1, 1) # # # nodepath.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) # # nodepath.setShaderAuto(Glow=True) # # nodepath.filters = CommonFilters(base.win, base.cam) # # nodepath.filters.setCartoonInk(separation=1) # except: # pass # # # def setLight(base): # """ # Set simple light style # background: white # ambient light: 0.7, 0.7, 0.7 # # ## input: # a showbase object # # author: weiwei # date: 20160616 # """ # # try: # ablight = AmbientLight("ambientlight") # ablight.setColor(Vec4(0.2, 0.2, 0.2, 1)) # ablightnode = base.render.attachNewNode(ablight) # base.render.setLight(ablightnode) # # # dlight0 = DirectionalLight("dlight0") # # dlight0.setColor(VBase4(.6, .6, .6, 1)) # # dlightnp0 = base.render.attachNewNode(dlight0) # # dlightnp0.setHpr(600,0,900) # # base.render.setLight(dlight0) # # # # dlight1 = DirectionalLight("dlight1") # # dlight1.setColor(VBase4(.6, .6, .6, 1)) # # dlightnp1 = base.render.attachNewNode(dlight1) # # dlightnp1.setHpr(0,0,150) # # base.render.setLight(dlight1) # # # slight = Spotlight('slight') # # slight.setColor(VBase4(1, 1, 1, 1)) # # lens = PerspectiveLens() # # slight.setLens(lens) # # slnp = base.render.attachNewNode(slight) # # slnp.setPos(100, 200, 0) # # slnp.lookAt([0,0,0]) # # base.render.setLight(slnp) # # ptlight0 = PointLight("pointlight1") # ptlight0.setColor(VBase4(1, 1, 1, 1)) # ptlightnode0 = base.render.attachNewNode(ptlight0) # ptlightnode0.setPos(0, 5000, 2500) # base.render.setLight(ptlightnode0) # # ptlight1 = PointLight("pointlight1") # ptlight1.setColor(VBase4(1, 1, 1, 1)) # ptlightnode1 = base.render.attachNewNode(ptlight1) # ptlightnode1.setPos(5000, 0, 2500) # base.render.setLight(ptlightnode1) # except: # pass # # # def setCam(base, posx, posy, posz, view=None): # """ # Set the position of cam # set the cam lookAt 0,0,0 # # ## input # base:s # a showbase object # posx, posy, posz: # the position of the cam # # note: # the operation should be done on base.cam, not base.camera # # author: weiwei # date: 20160618 # """ # # def setAspect(base, task): # aspectRatio = base.getAspectRatio() # base.cam.node().getLens().setAspectRatio(aspectRatio) # return task.cont # # def mouseMove(base, params, task): # if params['m1down']: # mw = base.mouseWatcherNode # hasMouse = mw.hasMouse() # if hasMouse: # m2downmpos = Vec2(mw.getMouseX()*2*base.cam.getPos().length(),mw.getMouseY()*2*base.cam.getPos().length()) # m2downmposworld = Vec3(base.render.getRelativePoint(base.cam, Vec3(m2downmpos[0], 0, m2downmpos[1]))) # m2downmposworld.normalize() # rotatevec = m2downmposworld.cross(params['m1downposinworld']) # rotateangle = m2downmposworld.signedAngleDeg(params['m1downposinworld'], rotatevec) # if rotateangle > 2 or rotateangle < -2: # # print base.camera.getHpr() # base.camera.setPos(Mat3.rotateMat(rotateangle, rotatevec).xform(base.camera.getPos())) # base.camera.lookAt(0,0,250) # return task.cont # # def mouse1Down(ispressdown, base, params): # if ispressdown: # params['m1down'] = True # mw = base.mouseWatcherNode # hasMouse = mw.hasMouse() # if hasMouse: # params['m1downpos'] = Vec2(mw.getMouseX()*2*base.camera.getPos().length(), mw.getMouseY()*2*base.camera.getPos().length()) # params['m1downposinworld'] = Vec3(base.render.getRelativePoint(base.camera, Vec3(params['m1downpos'][0], 0, params['m1downpos'][1]))) # params['m1downposinworld'].normalize() # # print params['m2downposinworld'] # else: # params['m1down'] = False # # def mouseWheel(isrollup, base): # """ # The mouse wheel moved. If direction is True, then it went up. # """ # forward = base.camera.getNetTransform().getMat().getRow3(1) # forward.normalize() # if isrollup: # # zoom in\ # if base.camera.getPos().length() > 1000: # newpos = base.camera.getPos() + forward*35 # base.camera.setPos(newpos[0], newpos[1], newpos[2]) # pass # else: # # zoom out # if base.camera.getPos().length() < 5000: # newpos = base.camera.getPos() - forward*35 # base.camera.setPos(newpos[0], newpos[1], newpos[2]) # # try: # lens = None # if view is None or view is 'orthogonal': # lens = OrthographicLens() # elif view is 'perspective': # lens = PerspectiveLens() # lens.setFilmSize(1500, 1500) # lens.setNearFar(1, 50000) # # # base.cam.setPos(posx, posy, posz) # # base.cam.lookAt(0,0,0) # # base.cam.node().setLens(lens) # # base.taskMgr.add(setaspect, "setaspect") # # own implementation # base.disableMouse() # base.camera.setPos(posx, posy, posz) # base.camera.lookAt(0,0,250) # base.cam.node().setLens(lens) # # params = {'m1down':False, 'm1downpos':Vec2(0,0), 'm1downposinworld':Vec3(0,0,0)} # base.accept("mouse1", mouse1Down, [True, base, params]) # base.accept("mouse1-up", mouse1Down, [False, base, params]) # base.accept("wheel_up", mouseWheel, [True, base]) # base.accept("wheel_down", mouseWheel, [False, base]) # base.taskMgr.add(mouseMove, "mousemove", extraArgs=[base, params], appendTask=True) # # TODO: check if there is an accept event for window-event # base.taskMgr.add(setAspect, "setaspect", extraArgs=[base], appendTask=True) # except: # print "Error Set cam" # pass # def onWindowEvent(window): # width = base.win.getProperties().getXSize() # height = base.win.getProperties().getYSize() # base.cam.node().getLens().setFilmSize(width, height)
class ToontownShaderManager(DirectFrame): def __init__(self, parent): self._parent = parent DirectFrame.__init__(self, parent=self._parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) self.filter = CommonFilters(base.win, base.cam) # Only affects primary window # Ambient Occlusion self.samples = 0 self.radius = 0.0 self.amount = 0.0 self.strength = 0.0 # Blur/Sharpen self.blur = 1.0 # this is normal value, 0.0 blurs it # Cartoon Ink self.cartoonSep = 0.0 self.cartoonR = 0.0 self.cartoonB = 0.0 self.cartoonG = 0.0 # todo: Add bloom # Boolean Filters self.HDREnabled = False self.invertedEnabled = False self.sRGBEnabled = False self.halfPixelShiftEnabled = False self.viewGlowEnabled = False # Other Filters self.exposure = 0.0 self.SAMPLES_MAX = 128 self.RAD_MAX = 1.0 self.AMT_MAX = 64.0 self.STR_MAX = 0.01 self.INCREMENT = 1 self.numSamples = None self.numRadius = None self.circleModel = loader.loadModel( 'phase_3/models/gui/tt_m_gui_mat_nameShop') self.barTexture = loader.loadTexture('phase_3/maps/slider.png') self.loadGUI() # self.newWindow() # Disabled for now def loadGUI(self): self.textRowHeight = 0.2 self.buttonbase_xcoord = 1.4 self.buttonbase_ycoord = 0.45 self.loadAmbientOcclusionGUI() self.loadBlurGUI() self.loadExposureGUI() self.loadCartoonInkGUI() self.loadHotkeys() def loadHotkeys(self): # for now instead of gui buttons i'ma just put the bool filters as hotkeys self.accept('4', self.__toggleHDR) self.accept('5', self.__toggleInverted) self.accept('6', self.__toggleSRGB) self.accept('7', self.__toggleHalfPixelShift) self.accept('8', self.__toggleViewGlow) def loadAmbientOcclusionGUI(self): self.numSamples = DirectSlider( parent=self, value=self.samples, pos=(self.buttonbase_xcoord + 0.1, 0.0, self.buttonbase_ycoord - self.textRowHeight * 3.5), thumb_relief=None, range=(0, 32), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeAOValue) # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength)) self.numSamples.setScale(0.5) self.numSamples.setTransparency(True) self.numSamplesText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 3.2), scale=0.05, text="AO Sample Count = {}".format(self.samples), style=5, mayChange=True) self.numRadius = DirectSlider( parent=self, value=self.radius, pos=(self.buttonbase_xcoord + 0.1, 0.0, (self.buttonbase_ycoord - self.textRowHeight * 4.5)), thumb_relief=None, range=(0, 1), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeAOValue) self.numRadius.setScale(0.5) self.numRadius.setTransparency(True) self.numRadiusText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 4.2), scale=0.05, text="AO Radius = {}".format(str(self.radius)), style=5, mayChange=True) self.numAmount = DirectSlider( parent=self, value=self.amount, pos=(self.buttonbase_xcoord + 0.1, 0.0, (self.buttonbase_ycoord - self.textRowHeight * 5.5)), thumb_relief=None, range=(0, 64), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeAOValue) self.numAmount.setScale(0.5) self.numAmount.setTransparency(True) self.numAmountText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 5.2), scale=0.05, text="AO Amount = {}".format(self.amount), style=5, mayChange=True) self.numStrength = DirectSlider( parent=self, value=self.strength, pos=(self.buttonbase_xcoord + 0.1, 0.0, (self.buttonbase_ycoord - self.textRowHeight * 6.5)), thumb_relief=None, range=(0, 0.1), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeAOValue) self.numStrength.setScale(0.5) self.numStrength.setTransparency(True) self.numStrengthText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 6.2), scale=0.05, text="AO Strength = {}".format(self.strength), style=5, mayChange=True) def loadCartoonInkGUI(self): self.cSep = DirectSlider( parent=self, value=self.cartoonSep, pos=(-self.buttonbase_xcoord + 0.1, 0.0, self.buttonbase_ycoord - self.textRowHeight * 2.5), thumb_relief=None, range=(0, 32), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeCartoon) # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength)) self.cSep.setScale(0.5) self.cSep.setTransparency(True) self.cRed = DirectSlider( parent=self, value=self.cartoonR, pos=(-self.buttonbase_xcoord + 0.1, 0.0, self.buttonbase_ycoord - self.textRowHeight * 3.5), thumb_relief=None, range=(0, 1), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeCartoon) # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength)) self.cRed.setScale(0.5) self.cRed.setTransparency(True) self.cBlue = DirectSlider( parent=self, value=self.cartoonB, pos=(-self.buttonbase_xcoord + 0.1, 0.0, self.buttonbase_ycoord - self.textRowHeight * 4.5), thumb_relief=None, range=(0, 1), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeCartoon) # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength)) self.cBlue.setScale(0.5) self.cBlue.setTransparency(True) self.cGreen = DirectSlider( parent=self, value=self.cartoonG, pos=(-self.buttonbase_xcoord + 0.1, 0.0, self.buttonbase_ycoord - self.textRowHeight * 5.5), thumb_relief=None, range=(0, 1), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeCartoon) # command=self.__changeAOValue(self.samples, self.radius, self.amount, self.strength)) self.cGreen.setScale(0.5) self.cGreen.setTransparency(True) def loadBlurGUI(self): self.numBlur = DirectSlider( parent=self, value=self.blur, # pos=(0.0, 0.0, 0.0), # for new window pos=(self.buttonbase_xcoord + 0.1, 0.0, (self.buttonbase_ycoord - self.textRowHeight * 0.5)), thumb_relief=None, range=(-10, 10), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeBlur) self.numBlur.setScale(0.5) self.numBlur.setTransparency(True) self.numBlurText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 0.2), scale=0.05, text="Blur Amount = {}".format(self.blur), style=5, mayChange=True) def loadExposureGUI(self): self.numExposure = DirectSlider( parent=self, value=self.exposure, pos=(self.buttonbase_xcoord + 0.1, 0.0, (self.buttonbase_ycoord - self.textRowHeight * 2.2)), thumb_relief=None, range=(0, 5), # self.SAMPLES_MAX thumb_geom=self.circleModel.find( '**/tt_t_gui_mat_namePanelCircle'), frameTexture=self.barTexture, frameSize=(-0.5, 0.5, -0.08, 0.08), command=self.__changeExposure) self.numExposure.setScale(0.5) self.numExposure.setTransparency(True) self.numExposureText = OnscreenText( pos=(self.buttonbase_xcoord + 0.1, self.buttonbase_ycoord - self.textRowHeight * 1.9), scale=0.05, text="Exposure Amount = {}".format(self.blur), style=5, mayChange=True) def printValue(self): print(self.numSamples['value']) def __changeCartoon(self): self.filter.delCartoonInk() s = self.cSep['value'] self.cartoonSep = s r = self.cRed['value'] self.cartoonR = r b = self.cBlue['value'] self.cartoonB = b g = self.cGreen['value'] self.cartoonG = g self.filter.setCartoonInk(s, (r, g, b, 1)) # a doesn't change def __changeBlur(self): self.filter.delBlurSharpen() b = self.numBlur['value'] self.blur = b self.numBlurText.setText("Blur amount = {}".format(b)) self.filter.setBlurSharpen(b) def __changeAOValue(self): if self.numSamples is None: print("NONE") return self.filter.delAmbientOcclusion() s = self.numSamples['value'] self.samples = s self.numSamplesText.setText("AO Sample Count = {}".format(s)) if (s == 0): return r = self.numRadius['value'] self.radius = r self.numRadiusText.setText("AO Radius = {}".format(r)) if (r == 0): return a = self.numAmount['value'] self.amount = a self.numAmountText.setText("AO Amount = {}".format(a)) if (a == 0): return st = self.numStrength['value'] self.strength = st self.numStrengthText.setText("AO Strength = {}".format(st)) if (st == 0): return self.filter.setAmbientOcclusion(numsamples=s, radius=r, amount=a, strength=st) print( "sample count: {} | radius count: {} | amount count: {} | strength count: {}" .format(self.samples, self.radius, self.amount, self.strength)) # WARNING: Won't work with relatively older Panda versions because this is new def __changeExposure(self): self.filter.delExposureAdjust() e = self.numExposure['value'] self.exposure = e self.numExposureText.setText("Exposure amount = {}".format(e)) self.filter.setExposureAdjust(e) def __toggleInverted(self): if not self.invertedEnabled: self.filter.setInverted() self.invertedEnabled = True else: self.filter.delInverted() self.invertedEnabled = False def __toggleHDR(self): if not self.HDREnabled: self.filter.setHighDynamicRange() self.HDREnabled = True else: self.filter.delHighDynamicRange() self.HDREnabled = False def __toggleSRGB(self): if not self.sRGBEnabled: self.filter.setSrgbEncode(True) self.sRGBEnabled = True else: self.filter.delSrgbEncode() self.sRGBEnabled = False def __toggleHalfPixelShift(self): if not self.halfPixelShiftEnabled: self.filter.setHalfPixelShift() self.halfPixelShiftEnabled = True else: self.filter.delHalfPixelShift() self.halfPixelShiftEnabled = False def __toggleViewGlow(self): if not self.viewGlowEnabled: self.filter.setViewGlow() self.viewGlowEnabled = True else: self.filter.delViewGlow() self.viewGlowEnabled = False def getValue(self, item, item_MAX): return item / item_MAX # Not gonna use this feature since it makes it very laggy. I will have to figure out # a better way to layout UI some other time. # https://discourse.panda3d.org/t/how-to-open-a-new-window/23929/4 def newWindow(self): self.wp = WindowProperties() self.wp.setSize(700, 500) self.wp.setRawMice(True) print(self.wp.getMouseMode()) win2mouseWatcher = MouseWatcher() ar = 1 self.win2 = base.openWindow(props=self.wp, aspectRatio=ar) self.window2render2d = NodePath('window2render2d') self.window2render2d.setDepthTest(0) self.window2render2d.setDepthWrite(0) self.window2camera2d = base.makeCamera2d(self.win2) self.window2camera2d.reparentTo(self.window2render2d) # Parent gui to this self.window2aspect2d = self.window2render2d.attachNewNode( PGTop('window2aspect2d')) self.window2aspect2d.setScale(1.0 / ar, 1.0, 1.0) name = self.win2.getInputDeviceName(0) mk = base.dataRoot.attachNewNode(MouseAndKeyboard(self.win2, 0, name)) mw = mk.attachNewNode(MouseWatcher(name)) self.window2aspect2d.node().setMouseWatcher(mw.node())
class world(ShowBase): def __init__(self): try: ShowBase.__init__(self) except: sys.exit("[WARNING]: unknown error: Showbase initialisation failed") # ------------------------------- Begin of parameter variables (pretty messy actually) ------------------------------------ #debug ------ self.debug=False #[ /!\ IMPORTANT /!\ ] do not leave this value to True when you commit the code to github, as it is impossible to change once ingame, and modifies quite a lot of graphical parameters #debug ------ self.stored_collisions=[] # this counts the amount of collisions and allows us to detect the income of a new collision in order to create a new particle effect when it appears self.timescale=5 # this can be changed at any moment, it represents the speed of the ingame time self.worldscale=0.1 # currently useless self.camera_delta=0.5 # camera delta displacement self.sensitivity_x,self.sensitivity_y=20,20 self.watched=None # watched object (object focused by the cursor) self.state=['paused','free',None] # state of things: [simulation paused/running,camera following object/free,followed object/None] print('free mode on') self.iteration=0 #iteration for the menu to be drawn once self.collision_status=False # Keep this on False, that's definitely not a setting # currently useless self.u_constant=6.67408*10**(-11) #just a quick reminder self.u_radius=5 #just what I said earlier # ------------------------------- End of parameter variables (sry for the mess) -------------------------------------------- self.Game_state=state() self.Game_state.cleanup() # better safe than sorry self.taskMgr.add(self.wait,'wait_task') self.setBackgroundColor(0,0,0) def wait(self,task): if not(task.time): self.screen_fill=OnscreenImage(image=str(MAINDIR)+"/Engine/main_page.png",pos = (0, 0, 0),scale=(1.77777778,1,1)) elif task.time>4: self.taskMgr.remove('wait_task') self.screen_fill.destroy() self.menu() return None return task.cont def menu(self): # loading time # music self.menu_music=self.loader.loadSfx(str(MAINDIR)+'/Sound/deadmau5-cabin.mp3') self.menu_music.setLoop(True) self.menu_music.play() # filters try: self.filters = CommonFilters(self.win, self.cam) except: pass self.Game_state.root_node.setAntialias(AntialiasAttrib.MAuto) def quit(): sys.exit(0) button_block_pos=(-1.2,0.35,0.25) maps_start=loader.loadModel(str(MAINDIR)+'/Engine/start.egg') maps_quit=loader.loadModel(str(MAINDIR)+'/Engine/quit.egg') maps_set=loader.loadModel(str(MAINDIR)+'/Engine/set.egg') self.start_button=DirectButton(pos=button_block_pos,frameColor=(0,0,0,0),scale=(0.717,0.4,0.221),geom=(maps_start.find('**/Start'),maps_start.find('**/Start_push'),maps_start.find('**/Start_on'),maps_start.find('**/Start')),command=self.loadgame) self.quit_button=DirectButton(pos=(button_block_pos[0]+0.135,button_block_pos[1],button_block_pos[2]-0.4),frameColor=(0,0,0,0),scale=(1,0.4,0.221),geom=(maps_quit.find('**/Quit'),maps_quit.find('**/Quit_push'),maps_quit.find('**/Quit_on'),maps_quit.find('**/Quit')),command=quit) self.settings_button=DirectButton(pos=(button_block_pos[0]-0.075,button_block_pos[1],button_block_pos[2]-0.2),frameColor=(0,0,0,0),scale=(0.56,0.4,0.221),geom=(maps_set.find('**/Set'),maps_set.find('**/Set_push'),maps_set.find('**/Set_on'),maps_set.find('**/Set')),command=self.not_implemented_yet) # settings not implemented yet self.title_pic=OnscreenImage(image=str(MAINDIR)+'/Engine/title.png',pos=(0,0.35,0), scale=(0.51,1,0.5)) self.title_pic.setTransparency(TransparencyAttrib.MAlpha) pannel_pos_x=1.25 self.activity_log=OnscreenImage(image=str(MAINDIR)+'/Engine/activity_log.png',pos=(pannel_pos_x,0.35,0.30),scale=(0.375,0.75,0.086775)) self.activity_log.setTransparency(TransparencyAttrib.MAlpha) #self.activity_log_bg=OnscreenImage(image=str(MAINDIR)+'/Engine/activity_log_bg.png',pos=(pannel_pos_x,-0.35,-0.3),scale=(0.5,0.4,0.675)) #self.activity_log_bg.setTransparency(TransparencyAttrib.MAlpha) #spaces compensate the center text effect self.logs=OnscreenText(text=' PyOS v0.11 \n\n Added main menu in last update\n Particle support has now become reality\n but still needs some improvement\n\n\n Feature in progress:\n collision animation\n\n\nRelease date >>',pos=(pannel_pos_x-0.3,0.11,0.2), scale=(0.05,0.05,0.05),fg=(1,1,1,1)) self.shrug=OnscreenImage(image=str(MAINDIR)+'/Engine/shrug.png',pos=(pannel_pos_x-0.35,0.35,-0.48),scale=(0.1,1,0.0317)) self.shrug.setTransparency(TransparencyAttrib.MAlpha) self.backgrnd=OnscreenImage(image=str(MAINDIR)+'/Engine/Stars.png',scale=(85.44,1,48),pos=(0,60,0)) self.backgrnd.reparentTo(self.Game_state.root_node) #self.filters.set_gamma_adjust(0.9) self.moon=self.loader.loadModel(str(MAINDIR)+"/Engine/Icy.egg") self.moon.setScale(0.2,0.2,0.2) self.moon.setPos(0,50,0) # radius of orbit equals 50 units self.moon.reparentTo(self.Game_state.root_node) self.intro_planet=self.loader.loadModel(str(MAINDIR)+"/Engine/tessena.egg") self.intro_planet_atm=self.loader.loadModel(str(MAINDIR)+"/Engine/tessena_atm.egg") self.intro_planet.setPos(0,-35,0) self.intro_planet_atm.setPos(0,-35,0) self.intro_planet.reparentTo(self.Game_state.root_node) self.intro_planet_atm.reparentTo(self.Game_state.root_node) self.intro_planet.setHpr(-110,0,0) self.intro_planet_atm.setHpr(-110,0,0) self.cam.setPos(0,-70,0) self.disable_mouse() # lighting d=DirectionalLight('menu_dlight') d.setColor(VBase4(0.631,0.369,1,1)) dlight=self.Game_state.root_node.attachNewNode(d) dlight.setHpr(60,-30,0) e=DirectionalLight('menu_dlight2') e.setColor(VBase4(1,1,1,1)) elight=self.Game_state.root_node.attachNewNode(e) elight.setHpr(60,-30,0) self.moon.setLight(elight) self.intro_planet.setLight(dlight) self.intro_planet_atm.setLight(dlight) self.task_mgr.add(self.rotate,'rotationtask') # penser a l'enlever def rotate(self,task): self.intro_planet.setHpr(self.intro_planet,(0.1,0,0)) self.intro_planet_atm.setHpr(self.intro_planet_atm,(0.07,0,0)) self.moon.setHpr(self.moon,(0.2,0,0)) self.moon.setPos(50*sin(task.time*0.02),50*cos(task.time*0.02),0) return task.cont # end of menu subfunctions def loadgame(self): # transition phase self.menu_music.setLoop(False) self.menu_music.stop() self.taskMgr.remove('rotationtask') self.cam.setPos(0,0,0) self.Game_state.cleanup() self.activity_log.hide() #self.activity_log_bg.hide() self.logs.hide() self.shrug.hide() self.start_button.hide() self.quit_button.hide() self.settings_button.hide() self.title_pic.hide() # end of transition phase # Mouse parameters self.hidden_mouse=True wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) self.win.requestProperties(wp) # preparing the menu text list: quit_to_desk=self.loader.loadModel(str(MAINDIR)+"/Engine/quit_to_desktop.egg") quit_to_menu=self.loader.loadModel(str(MAINDIR)+"/Engine/quit_to_menu.egg") resume=self.loader.loadModel(str(MAINDIR)+"/Engine/resume.egg") self.paused_menu_text=[] global_tempPosx=-1 self.paused_menu_text.append(DirectButton(pos=(global_tempPosx-0.065,0,-0.2),frameColor=(0,0,0,0),scale=(1,0.4,0.211),geom=(quit_to_desk.find('**/quit_to_desktop'),quit_to_desk.find('**/quit_to_desktop_push'),quit_to_desk.find('**/quit_to_desktop_on'),quit_to_desk.find('**/quit_to_desktop')),command=self.system_break)) self.paused_menu_text.append(DirectButton(pos=(global_tempPosx,0,0),frameColor=(0,0,0,0),scale=(1.12,0.4,0.211),geom=(quit_to_menu.find('**/quit_to_menu'),quit_to_menu.find('**/quit_to_menu_push'),quit_to_menu.find('**/quit_to_menu_on'),quit_to_menu.find('**/quit_to_menu')),command=self.ingame_back_to_menu)) self.paused_menu_text.append(DirectButton(pos=(global_tempPosx-0.325,0,0.2),frameColor=(0,0,0,0),scale=(0.47,0.4,0.211),geom=(resume.find('**/resume'),resume.find('**/resume_push'),resume.find('**/resume_on'),resume.find('**/resume')),command=self.toggle_pause)) # btw I found something about energy transmission through thermal radiation. I think it uses some Boltzmann formula stuff. Link here: # https://fr.wikibooks.org/wiki/Plan%C3%A9tologie/La_temp%C3%A9rature_de_surface_des_plan%C3%A8tes#Puissance_re%C3%A7ue_par_la_Terre # Defining important data lists # music imports (paths) self.sounds=[str(MAINDIR)+"/Sound/001.mp3", str(MAINDIR)+"/Sound/Blazing-Stars.mp3", str(MAINDIR)+"/Sound/Cold-Moon.mp3", str(MAINDIR)+"/Sound/Light-Years_v001.mp3", str(MAINDIR)+"/Sound/The-Darkness-Below.mp3", str(MAINDIR)+"/Sound/Retro-Sci-Fi-Planet.mp3", str(MAINDIR)+"/Sound/droid-bishop-nightland.mp3", str(MAINDIR)+"/Sound/interstellar-ost-03-dust-by-hans-zimmer.mp3", str(MAINDIR)+"/Sound/interstellar-ost-04-day-one-by-hans-zimmer.mp3", str(MAINDIR)+"/Sound/ascendant-remains-2015.mp3", str(MAINDIR)+"/Sound/droid-bishop-nightland.mp3", str(MAINDIR)+"/Sound/john-carpenter-utopian-facade-official-music-video.mp3", str(MAINDIR)+"/Sound/stranger-things-2-eulogy.mp3", str(MAINDIR)+"/Sound/interstellar-ost-07-the-wormhole-by-hans-zimmer.mp3"] self.collision_solids=[] #collision related stuff - comments are useless - just RTFM self.light_Mngr=[] self.data=[ [0,0,0,-0.00,0.003,0,0.30,0.30,0.30,100000.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/lp_planet_0.egg"),(0.1,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/lp_planet_1.egg"),(0.14,0,0)],"low_poly_planet01",False,0.1] ,[10,0,0,0,0.003,0,0.05,0.05,0.05,20.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/Icy.egg"),(0.05,0,0)],"Ottilia_modified",False,0.1] ,[0,70,10,0,0.005,0,0.1,0.1,0.1,40.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/asteroid_1.egg"),(0,0,0.2)],"Selena",False,1] ,[100,0,10,0,0,0,5,5,5,1000000,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/sun1.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/sun1_atm.egg"),(0.01,0,0)],"Sun",True,0.1] ,[-100,50,70,0,0,0.003,0.15,0.15,0.15,1000.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/Earth2.egg"),(-0.1,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/Earth2_atm.egg"),(-0.15,0,0)],"big_fucking_planet",False,0.1] ,[200,0,0,-0.001,0,0.01,0.1,0.1,0.1,100000,False,[self.loader.loadModel(MAINDIR+"/Engine/realistic_asteroid.egg"),(0,0.01,0)],"spaceship",False,0] ,[0,-120,0,0.004,0,0,0.3,0.3,0.3,100000,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/FG1.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG2.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG3.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG4.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG5.egg"),(0.01,0,0)],"Frozen_giant",False,0.1] # insert your 3d models here, following the syntax (this is the default scene that will be loaded on startup) ] # the correct reading syntax is [x,y,z,l,m,n,scale1,scale2,scale3,mass,static,[file,(H,p,r),file,(H,p,r)...],id,lightsource,brakeforce] for each body - x,y,z: position - l,m,n: speed - scale1,scale2,scale3: obvious (x,y,z) - mass: kg - static: boolean - [files]: panda3d readfiles list (first file must be the ground, the others are atmosphere models) #id: str - lightsource: boolean - #if you want the hitbox to be correctly scaled,and your body to have reasonable proportions, your 3d model must be a 5*5 sphere, or at least have these proportions # create the real data list, the one used by the program self.bodies=[] for c in self.data: temp=body() temp.fill_entries(c) self.bodies.append(temp) temp=None #self.bodies.reverse() # Quick presetting self.setBackgroundColor(0,0,0,True) # enable particles self.enableParticles() self.toggle_particles() # debug # create particle class object self.particle=particle() # intialize object: self.particle.config_path='/Engine/destruction_sphere.ptf' # the MAINDIR is already included inside the class definition # non-body type structures loading if SKYBOX=='sky': self.isphere=self.loader.loadModel(str(MAINDIR)+"/Engine/InvertedSphere.egg") #loading skybox structure self.tex=loader.loadCubeMap(str(MAINDIR)+'/Engine/Skybox4/skybox_#.png') elif SKYBOX=='arena': self.box=self.loader.loadModel(str(MAINDIR)+"/Engine/arena.egg") #load shaders (optionnal) ''' sun_shader=Shader.load(Shader.SLGLSL,MAINDIR+'/Engine/Shaders/flare_v.glsl',MAINDIR+'/Engine/Shaders/flare_f.glsl') ''' self.camLens.setNearFar(0.5, 100000) self.orbit_lines=[] #under developement # see https://www.panda3d.org/manual/?title=Collision_Solids for further collision interaction informations base.graphicsEngine.openWindows() try: print('\n[Loader manager]:\n') ''' self.filters.setBlurSharpen(amount=0) # just messing around ''' if not self.debug: self.filters.set_gamma_adjust(1.0) # can be usefull self.filters.set_bloom(intensity=1,size="medium") for c in self.bodies: # loading and displaying the preloaded planets and bodies if c.is_lightSource and not self.debug: # VM filtering self.filters.setVolumetricLighting(c.filelist[0],numsamples=50,density=0.5,decay=0.95,exposure=0.035) #c.filelist[u].set_shader(sun_shader) if BLUR: self.filters.setCartoonInk() for u in range(0,len(c.filelist),2): # loading each sub-file c.filelist[u].reparentTo(self.Game_state.root_node) c.filelist[u].setScale(tuple(c.scale)) c.filelist[u].setPos(tuple(c.position)) if u==0 and not(c.is_lightSource): c.filelist[u].setShaderAuto() #activate auto shading for compact, non translucent bodies #setting the collision solid up temp=hitbox() temp.Volume=CollisionSphere(0,0,0,self.u_radius) temp.NodePath=c.filelist[0].attachNewNode(CollisionNode(c.id)) temp.CollisionNode=temp.NodePath.node() self.collision_solids.append(temp) #the radius is calculated by using the average scale + the self.u_radius # the structure of the collision_solids list will be: [temp1,temp2,...] # asteroids and irregular shapes must be slightly bigger than their hitbox in order to avoid visual glitches self.collision_solids[len(self.collision_solids)-1].CollisionNode.addSolid(self.collision_solids[len(self.collision_solids)-1].Volume) #I am definitely not explaining that temp=None if self.debug: loadPrcFileData("", "show-frame-rate-meter 1") self.collision_solids[len(self.collision_solids)-1].NodePath.show() # debugging purposes only print("collision: ok") print("placing body: done") if c.is_lightSource: self.light_Mngr.append([PointLight(c.id+"_other")]) self.light_Mngr[len(self.light_Mngr)-1].append(self.Game_state.root_node.attachNewNode(self.light_Mngr[len(self.light_Mngr)-1][0])) self.light_Mngr[len(self.light_Mngr)-1][1].setPos(tuple(c.position)) # shadow stuff self.light_Mngr[len(self.light_Mngr)-1][1].node().setShadowCaster(True) ''' self.light_Mngr[len(self.light_Mngr)-1][1].node().getLens().setFov(40) self.light_Mngr[len(self.light_Mngr)-1][1].node().getLens().setNearFar(10, 100) ''' self.Game_state.root_node.setLight(self.light_Mngr[len(self.light_Mngr)-1][1]) self.light_Mngr.append([AmbientLight(c.id+"_self")]) self.light_Mngr[len(self.light_Mngr)-1][0].setColorTemperature(3000) self.light_Mngr[len(self.light_Mngr)-1].append(self.Game_state.root_node.attachNewNode(self.light_Mngr[len(self.light_Mngr)-1][0])) for u in range(0,len(c.filelist),2): c.filelist[u].setLight(self.light_Mngr[len(self.light_Mngr)-1][1]) print("lights: done") else: self.light_Mngr.append([]) #create an empty list, so that the coordinates of the data in the list is the same as in self.bodies (easier for further analysis and potential deletion) self.light_Mngr.append([]) print("loaded new body, out: done") if SKYBOX=='sky': self.isphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldCubeMap) # *takes a deep breath* cubemap stuff ! self.isphere.setTexProjector(TextureStage.getDefault(), self.Game_state.root_node, self.isphere) self.isphere.setTexPos(TextureStage.getDefault(), 0, 0, 0) self.isphere.setTexScale(TextureStage.getDefault(), .5) # that's a thing... self.isphere.setTexture(self.tex)# Create some 3D texture coordinates on the sphere. For more info on this, check the Panda3D manual. self.isphere.setLightOff() self.isphere.setScale(10000) #hope this is enough self.isphere.reparentTo(self.Game_state.root_node) elif SKYBOX=='arena': self.box.setPos(0,0,0) self.box.setScale(100) self.box.reparentTo(self.Game_state.root_node) # collision traverser and other collision stuff # that's super important, and super tricky to explain so just check the wiki self.ctrav = CollisionTraverser() self.queue = CollisionHandlerQueue() for n in self.collision_solids: self.ctrav.add_collider(n.NodePath,self.queue) # the traverser will be automatically updated, no need to repeat this every frame # debugging only if self.debug: self.ctrav.showCollisions(self.Game_state.root_node) # play a random music self.current_playing=random.randint(0,len(self.sounds)-1) self.current_song=self.loader.loadSfx(self.sounds[self.current_playing]) self.current_song.play() # task manager stuff comes here self.intro_loop() except: sys.exit(":( something went wrong: files could not be loaded") # key bindings self.accept('escape',self.toggle_pause) self.accept('mouse1',self.handle_select,[True]) self.accept('wheel_up',self.handle_scrolling,[True]) # center button (just a quick test) self.accept('wheel_down',self.handle_scrolling,[False]) self.accept('z',self.move_camera,[0,True]) self.accept('q',self.move_camera,[1,True]) self.accept('s',self.move_camera,[2,True]) self.accept('d',self.move_camera,[3,True]) self.accept('a',self.move_camera,[4,True]) self.accept('e',self.move_camera,[5,True]) self.accept('arrow_right',self.time_change,[True]) self.accept('arrow_left',self.time_change,[False]) self.accept('z-up',self.move_camera,[0,False]) self.accept('q-up',self.move_camera,[1,False]) self.accept('s-up',self.move_camera,[2,False]) self.accept('d-up',self.move_camera,[3,False]) self.accept('a-up',self.move_camera,[4,False]) self.accept('e-up',self.move_camera,[5,False]) self.keymap=['z',0,'q',0,'s',0,'d',0,'a',0,'e',0,'mouse1',0] if self.debug: # draw axis coord=[(1,0,0),(0,1,0),(0,0,1)] axis=[] for c in range(3): axis.append(LineSegs()) axis[c].moveTo(0,0,0) axis[c].drawTo(coord[c]) axis[c].setThickness(3) axis[c].setColor(tuple([coord[c][u]*255 for u in range(len(coord[c]))] +[True])) NodePath(axis[c].create()).reparent_to(self.Game_state.root_node) # camera positionning ------- self.focus_point=[0,0,0] # point focused: can become a body's coordinates if the user tells the program to do so self.zoom_distance=30 # distance to the focus point in common 3D units (can be modified by scrolling) self.cam_Hpr=[0,0,0] # phi, alpha, theta - aka yaw, pitch, roll self.cam_Hpr=[self.cam_Hpr[n]*pi/180 for n in range(len(self.cam_Hpr))] # convert to rad phi,alpha,theta,zoom,object=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.zoom_distance,self.state[2] # temporary vars if self.state[1]=='free': self.camera_pos=[0,0,0] self.camera.setPos(tuple(self.camera_pos)) elif self.state[1]=='linked': # find the object (self.state[2]) in the data list list_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(object.getParent()) self.focus_point=self.bodies[list_pos].position # take the focused object's coordinates self.camera_pos=[self.focus_point[0]+sin(phi)*cos(-alpha)*zoom,self.focus_point[1]-cos(phi)*cos(-alpha)*zoom,self.focus_point[2]+sin(-alpha)*zoom] #keep it up to date so that it's not hard to find whend switching modes self.camera.setPos(tuple(self.camera_pos)) self.camera.setHpr(self.cam_Hpr) # cursor self.cursor=self.showsimpletext('.',(0,0),(0.08,0.08),None,(1,1,1,True)) # yeah, you can laugh, but this still works so I don't care self.pointerNode=CollisionNode('cursor') self.pointerNP=camera.attachNewNode(self.pointerNode) self.pointerNode.setFromCollideMask(BitMask32.bit(1)) # separate collisions (in order to avoid mistakes during physical calculations) self.cursor_ray=CollisionRay() # create the mouse control ray self.pointerNode.addSolid(self.cursor_ray) self.ctrav.add_collider(self.pointerNP,self.queue) #self.screen_fill.destroy() # delete the displayed picture return None def showsimpletext(self,content,pos,scale,bg,fg): #shows a predefined, basic text on the screen (variable output only) return OnscreenText(text=content,pos=pos,scale=scale,bg=bg,fg=fg) def intro_loop(self): self.taskMgr.add(self.mouse_check,'mousePositionTask') self.taskMgr.add(self.placement_Mngr,'frameUpdateTask') self.taskMgr.add(self.Sound_Mngr,'MusicHandle') self.taskMgr.add(self.camera_update,'cameraPosition') self.taskMgr.remove('showIntroPic') return None def placement_Mngr(self,task): # void main = main game mechanics, frame updating function (kinda, all pausing and menu functions must be applied here) if self.state[0]=='running' or not task.time: self.ctrav.traverse(self.Game_state.root_node) #self.queue = CollisionHandlerQueue() # update the collision queue brakeforce=[0 for n in range(len(self.bodies))] # create an empty brakeforce list if self.queue.getNumEntries(): if self.debug: print(self.queue.getNumEntries()) # debug, shows only one digit most of the time # now we have to create a temp list containing only the Entries that refer to collisions between bodies, # not cursor-type collisions: temp1,temp2=[],[] for count in range(len(self.queue.getEntries())): if self.queue.getEntries()[count].getFromNodePath()!=self.pointerNP: temp1.append(self.queue.getEntries()[count]) else: temp2.append(self.queue.getEntries()[count]) # the temp1 and temp2 lists have been created # run the check for the body-with-body collisions if len(temp1)>len(self.stored_collisions): print('[WARNING]: New collision') # debugging , detects when a collision occurs AND IT F*****G WORKS BITCH !! (actually I created this system so that the particles linked to the collision are only created once) self.particle.add_particle(temp1[len(self.stored_collisions):]) #b=len(temp1[self.stored_collisions:len(temp1)]) for x in temp1[len(self.stored_collisions):]: self.particle.activate(x.getIntoNodePath().getParent(),self.Game_state.root_node) elif len(temp1)<len(self.stored_collisions): print('Collision ended') a=self.stored_collisions[len(temp1):] for x in a: self.particle.deactivate(x.getIntoNodePath().getParent()) self.particle.deactivate(x.getFromNodePath().getParent()) # at this point we should probably delete the particle object, as if we don't, it might still be updated, even if the collision doesn't exist in the self.queue anymore self.stored_collisions=temp1 #else do nothing for c in range(0,len(temp1),2): entry=temp1[c] brakeforce=self.collision_log(entry,brakeforce) # run the check for the cursor-with-body collisions for c in range(len(temp2)): entry=temp2[c] self.watched=entry.getIntoNodePath() # print "out" # update the collider list self.ctrav.clear_colliders() self.queue = CollisionHandlerQueue() for n in self.collision_solids: self.ctrav.add_collider(n.NodePath,self.queue) self.ctrav.add_collider(self.pointerNP,self.queue) # add the cursor ray again else: self.watched=None # collision events are now under constant surveillance acceleration=[] for c in range(len(self.bodies)): #selects the analysed body var=self.bodies[c] Bdf=[0,0,0] #Bdf stands for 'bilan des forces' in french, it's the resulting acceleration for d in self.bodies[0:c]+self.bodies[c+1:len(self.bodies)-1]: #selects the body which action on the analysed body we're studying...not sure about that english sentence though S,M=[d.mass]+d.position,[var.mass]+var.position temp=self.dual_a(S,M) Bdf=[Bdf[x]+temp[x] for x in range(3)] # list sum # add the result to the global save list acceleration.append(Bdf) #update the bodies' position self.speed_update(acceleration,brakeforce) self.pos_update() self.apply_update() elif self.state[0]=='paused': self.handle_menu(self.iteration) self.iteration+=1 return task.cont def speed_update(self,a,brakeforce): for c in range(len(self.bodies)): #the function updates the speed tuple accordingly self.bodies[c].speed[0]+=self.timescale*a[c][0] #self.bodies[c].speed[0]/=brakeforce[c]+1 # aero/lytho braking has to be applied to the colliding object # actually, speed isn't applied that way self.bodies[c].speed[1]+=self.timescale*a[c][1] #self.bodies[c].speed[1]/=brakeforce[c]+1 self.bodies[c].speed[2]+=self.timescale*a[c][2] #self.bodies[c].speed[2]/=brakeforce[c]+1 return 0 def pos_update(self): #updates the positional coordinates for c in range(len(self.bodies)): self.bodies[c].position[0]+=self.timescale*self.bodies[c].speed[0] self.bodies[c].position[1]+=self.timescale*self.bodies[c].speed[1] self.bodies[c].position[2]+=self.timescale*self.bodies[c].speed[2] return 0 def apply_update(self): #actually moves the hole 3d stuff around count=0 #local counter for c in self.bodies: for u in range(len(c.filelist)): if u%2!=0: c.filelist[u-1].setHpr(c.filelist[u-1],tuple([self.timescale*i for i in c.filelist[u]])) else: c.filelist[u].setPos(tuple(c.position)) if c.is_lightSource: self.light_Mngr[count][1].setPos(tuple(c.position)) count+=2 return 0 def camera_update(self,task): phi,alpha,theta,zoom,object=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.zoom_distance,self.state[2] if self.state[1]=='free': self.camera.setPos(tuple(self.camera_pos)) elif self.state[1]=='linked': # find the object (self.state[2]) in the data list list_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(object.getParent()) self.focus_point=self.bodies[list_pos].position # take the focused object's coordinates self.camera_pos=[self.focus_point[0]+sin(phi)*cos(-alpha)*zoom,self.focus_point[1]-cos(phi)*cos(-alpha)*zoom,self.focus_point[2]+sin(-alpha)*zoom] self.camera.setPos(tuple(self.camera_pos)) self.camera.setHpr(tuple(self.cam_Hpr)) # collision cursor stuff goes here: self.cursor_ray.setFromLens(self.camNode,0,0) # relatively to the camera, the cursor position will always be 0,0 which is the position of the # white point on the screen if self.keymap!=['z',0,'q',0,'s',0,'d',0]: for x in range(1,len(self.keymap),2): if self.keymap[x]: self.move_camera(int((x-1)/2),True) # why (x-1)/2 ? because we have to make the tow readable as a key number, like 0,1,2,3 return task.cont def dual_a(self,S,M): #S is the "static object", the one that applies the force to the "moving" object M O=[] #This will be the list with the accelerations for an object d=sqrt((S[1]-M[1])**2+(S[2]-M[2])**2+(S[3]-M[3])**2) x=(self.u_constant*S[0]*(S[1]-M[1]))/d**2 y=(self.u_constant*S[0]*(S[2]-M[2]))/d**2 z=(self.u_constant*S[0]*(S[3]-M[3]))/d**2 O.append(x) O.append(y) O.append(z) return O def collision_log(self,entry,brakeforce): from_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(entry.getFromNodePath().getParent()) into_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(entry.getIntoNodePath().getParent()) #find the nodepath in the list f_radius=sum(self.bodies[from_pos].scale)*self.u_radius/3 i_radius=sum(self.bodies[into_pos].scale)*self.u_radius/3 if max(f_radius,i_radius)==f_radius: inverted=True into_pos,from_pos=from_pos,into_pos else: inverted=False # currently useless brakeforce[from_pos]=self.bodies[from_pos].brakeforce # get the force given in the data list # those are the two positions of the nodepaths, now we need to know which one is bigger, in order to obtain the fusion effect # from_pos is the smaller body, into_pos is the bigger one self.collision_gfx(self.momentum_transfer(from_pos,into_pos,entry,inverted),f_radius,i_radius) #some useless data remains from version 0.9 return brakeforce def momentum_transfer(self,f_pos,i_pos,entry,inverted): if self.debug: print("colliding") # debug, makes the game laggy (only activated when the self.debug var is on) interior = entry.getInteriorPoint(self.Game_state.root_node) # default surface = entry.getSurfacePoint(self.Game_state.root_node) if self.debug: print((interior - surface).length()) # debug, doesn't slow the game down too much so I haven't removed it if (interior - surface).length() >= self.u_radius*2*sum(self.bodies[f_pos].scale)/3: # this is the body deletion routine if self.state[2]==self.collision_solids[f_pos].NodePath: self.state[1]='free' self.state[2]=None # Lighting if self.bodies[f_pos].is_lightSource: self.Game_state.root_node.clearLight(self.light_Mngr[2*f_pos][1]) self.Game_state.root_node.clearLight(self.light_Mngr[2*f_pos][1]) self.filters.delVolumetricLighting() #temp self.ctrav.remove_collider(self.collision_solids[f_pos].NodePath) self.bodies[f_pos].delete_body() self.bodies[i_pos].scale[0]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass self.bodies[i_pos].scale[1]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass self.bodies[i_pos].scale[2]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass self.bodies[i_pos].mass+=self.bodies[f_pos].mass # particle deletion self.particle.deactivate(self.collision_solids[f_pos].NodePath.getParent()) self.particle.deactivate(self.collision_solids[i_pos].NodePath.getParent()) # scale updating () ''' temporarly removed for c in range(0,len(self.bodies[i_pos].filelist),2): self.bodies[i_pos].filelist[c].setScale(tuple(self.bodies[i_pos].scale)) ''' # deleting the destroyed planet's data self.bodies=self.bodies[:f_pos]+self.bodies[f_pos+1:len(self.bodies)] self.collision_solids=self.collision_solids[:f_pos]+self.collision_solids[f_pos+1:len(self.collision_solids)] # update the light list self.light_Mngr=self.light_Mngr[:2*f_pos]+self.light_Mngr[2*f_pos+2:len(self.light_Mngr)] # just a quick test if self.debug: self.ctrav.showCollisions(self.Game_state.root_node) if self.debug: print("planet destroyed") return interior,surface # used for the collision gfx calculations def printScene(self): #debug file=open("scenegraph.txt","a") ls = LineStream() self.Game_state.root_node.ls(ls) while ls.isTextAvailable(): file.write(ls.getLine()) file.write("\n") file.write("\n") file.write("END\n") file.write("\n") file.close() def Sound_Mngr(self,task): if self.current_song.length()-self.current_song.getTime()==0: #could have just used not() self.current_playing=random.choice(list(range(0,self.current_playing))+list(range(self.current_playing+1,len(self.sounds)))) self.current_song=self.loader.loadSfx(self.sounds[self.current_playing]) self.current_song.play() print(self.current_playing) return task.cont def collision_gfx(self,points,Rf,Ri): # collision animation calculations # section size calculation # we know the depth of penetration (no silly jokes please), which allows us, knowing the radius of each body, # to calculate the radius of the section (I've got no idea how to say that in correct english) # the display of the particles all over this circle will be a piece of cake (at least I hope so) - edit - it wasn't # see documents in the screenshot folder for more informations about the maths ''' interior,surface=points[0],points[1] p=(interior - surface).length() p2=(p**2-2*Ri*p)/(2*Ri-2*p-2*Rf) p1=p-p2 ''' #currently useless self.update_particle_pos() # now we know everything about our impact section (the circle that defines the contact between the two bodies) # we just have to find the coord of the circle's center: we will take the surfacepoint impact point return 0 def create_crater(self): # see project for more informations return None def toggle_pause(self): self.toggle_particles() # pause the particles temp=['paused','running'] self.state[0]=temp[self.state[0]==temp[0]] # switches between paused and running self.iteration=0 if self.state[0]=='paused': # discord rpc updating try: RPC.update(state="Version: 0.11", details="In the menus",large_image="logo",small_image=None) except: pass self.handle_menu(self.iteration) else: # discord RPC updating try: RPC.update(state="Version: 0.11", details="In a simulation",large_image="logo",small_image=None) except: pass self.filters.del_blur_sharpen() self.filters.set_gamma_adjust(1) # make the mouse invisible self.hidden_mouse=True wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) # set the mouse pos to 0 self.center_mouse() self.win.requestProperties(wp) for u in self.paused_menu_text: u.hide() return None def handle_menu(self,iteration): if not iteration: self.accept('escape',self.toggle_pause) self.draw_menu() # make the mouse visible self.hidden_mouse=False wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) self.win.requestProperties(wp) else: a=1 # indentation (temporary) #put your mouse detection stuff here # menu stuff # menu stuff # please use directGui for that purpose (better rendering performances) # the menu doesn't actually work, as the whole clicking reaction routine is not implemented return None def draw_menu(self): self.filters.setBlurSharpen(amount=0) self.filters.set_gamma_adjust(1.7) for u in self.paused_menu_text: u.show() return None def show_credits(self): print("created by l3alr0g, zudo and Fang (at least this part) --> I'll do something better later") return None def system_break(self): # place your data saving routines here print("system exit successful, data saved") print("executing sys.exit()") print("out: done") sys.exit(0) return None def handle_scrolling(self,up): # up is a boolean: up=True means up, up=False means down if up and self.state[0]=='running': if self.state[1]=='linked': self.zoom_distance*=0.95 else: self.camera_delta*=1.1 elif not up and self.state[0]=='running': if self.state[1]=='linked': self.zoom_distance/=0.95 else: self.camera_delta/=1.1 return None def rotate_camera(self): self.camera.setHpr(tuple(self.cam_Hpr)) return None def move_camera(self,tow,pressed): # tow stands for towards, pressed is a boolean which indicates the state of the key if pressed: self.keymap[2*tow+1]=1 self.state[1]='free' #print('free mode on') self.state[2]=None else: self.keymap[2*tow+1]=0 if self.keymap[2*tow+1]: phi,alpha,theta,delta,zoom=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.camera_delta,self.zoom_distance if self.keymap[2*tow]=='q': if self.state[1]=='free': self.camera_pos=[self.camera_pos[0]-cos(phi)*cos(theta)*delta,self.camera_pos[1]-sin(phi)*cos(theta)*delta,self.camera_pos[2]+sin(theta)*delta] # moving the camera if self.keymap[2*tow]=='z': if self.state[1]=='free': self.camera_pos=[self.camera_pos[0]-sin(phi)*cos(alpha)*delta,self.camera_pos[1]+cos(phi)*cos(alpha)*delta,self.camera_pos[2]+sin(alpha)*delta] if self.keymap[2*tow]=='s': if self.state[1]=='free': self.camera_pos=[self.camera_pos[0]+sin(phi)*cos(alpha)*delta,self.camera_pos[1]-cos(phi)*cos(alpha)*delta,self.camera_pos[2]-sin(alpha)*delta] if self.keymap[2*tow]=='d': if self.state[1]=='free': self.camera_pos=[self.camera_pos[0]+cos(phi)*cos(theta)*delta,self.camera_pos[1]+sin(phi)*cos(theta)*delta,self.camera_pos[2]-sin(theta)*delta] if self.keymap[2*tow]=='a': self.cam_Hpr[2]-=1 if self.keymap[2*tow]=='e': self.cam_Hpr[2]+=1 return None def mouse_check(self,task): # gets the mouse's coordinates mwn = self.mouseWatcherNode if mwn.hasMouse(): x,y=mwn.getMouseX(),mwn.getMouseY() #print(x,y) # debug # focus_point coordinates modifier code here: if self.state==['running','free',None]: self.cam_Hpr[0]-=x*self.sensitivity_x # the - fixes a bug I can't solve # sensitivity is a coefficient used for mouse displacement routines self.cam_Hpr[1]+=y*self.sensitivity_y # those formulas do not work when theta (self.cam_Hpr[2]) changes self.rotate_camera() self.center_mouse() elif self.state[0]=='running' and self.state[1]=='linked': self.cam_Hpr[0]-=x*self.sensitivity_x self.cam_Hpr[1]-=y*self.sensitivity_y self.rotate_camera() self.center_mouse() ''' if self.debug: print(self.cam_Hpr,self.camera_pos) # debug ''' return task.cont def center_mouse(self): self.win.movePointer(0, int(self.win.getProperties().getXSize() / 2), int(self.win.getProperties().getYSize() / 2)) # move mouse back to center --> careful ! this makes the delta calculation code buggy def handle_select(self,is_clicked): if is_clicked and self.watched!=None: self.state[1]='linked' # toggle following mode self.state[2]=self.watched print('linked mode on, focusing: ',self.watched) #else: # do nothing actually return None def update_particle_pos(self): # harder than I thought for x in range(len(self.particle.particle_list)): a=[i.getIntoNodePath() for i in self.queue.getEntries()].index(self.particle.particle_list[x][1]) # finding the intonodepath inside self.queue.getEntries() self.particle.particle_list[x][0].setPos(self.queue.getEntries()[a].getSurfacePoint(self.Game_state.root_node)) # all particles are being displaced to the position of the surface impact point tempvar=self.queue.getEntries()[a].getSurfacePoint(self.Game_state.root_node) - self.queue.getEntries()[a].getIntoNodePath().getParent().getPos() H,P,R=-atan(tempvar[0]/tempvar[1])*180/pi+180,(-atan(tempvar[2]/tempvar[1])+pi/2)*180/pi,0 self.particle.particle_list[x][0].setHpr(H,P,R) # self.queue.getEntries()[a].getIntoNodePath().getParent().getPos() # +self.queue.getEntries()[a].getSurfacePoint(self.queue.getEntries()[a].getIntoNodePath()) return None def time_change(self,income): # income is a boolean, True means speed up, False means speed down if income: self.timescale*=1.2 else: self.timescale*=0.80 return None def not_implemented_yet(self): # comes with self.follow function self.sign=OnscreenImage(image=str(MAINDIR)+"/Engine/not_implemented_yet.png",pos=(0,0,0),scale=(0.5,0.5,0.5)) # scale is useless: already at scale (the pic is square shaped) self.sign.setTransparency(TransparencyAttrib.MAlpha) self.accept("escape",self.follow) self.quit_button['state']=DGG.DISABLED self.settings_button['state']=DGG.DISABLED self.start_button['state']=DGG.DISABLED return None def follow(self): # dependencies of the not_implemented_yet function self.ignore("escape") self.sign.destroy() self.quit_button['state']=DGG.NORMAL self.settings_button['state']=DGG.NORMAL self.start_button['state']=DGG.NORMAL return None def easter_egg(self): return "please be patient, our hens are working on it" # I am not responsible for the bad quality of my humor def ingame_back_to_menu(self): # large name, I know, I had no ideas self.filters.set_gamma_adjust(1) for u in self.paused_menu_text: u.hide() self.filters.del_blur_sharpen() self.Game_state.cleanup() # we have to delete all the taskManager routines we implemented during the simulation # here comes the whole stuff: self.taskMgr.remove('mousePositionTask') self.taskMgr.remove('frameUpdateTask') self.taskMgr.remove('MusicHandle') self.taskMgr.remove('cameraPosition') # end of task manager stuff self.stored_collisions=[] self.watched=None self.state=['paused','free',None] self.iteration=0 self.filters.delVolumetricLighting() # temporary, as I have to implement multiple light source handling # music self.current_song.stop() self.menu() return None
class World(object): def __init__(self, base, numberOfPlayers): self.base = base self.render = base.render self.taskMgr = base.taskMgr self.loader = base.loader self.windowEventSetup() # Create separate nodes so that shaders apply to # stuff in the worldRender but not the outsideWorldRender self.worldRender = render.attachNewNode("world") self.outsideWorldRender = render.attachNewNode("world") self.base.setFrameRateMeter(True) self.globalClock = ClockObject.getGlobalClock() self.globalClock.setMode(ClockObject.MLimited) self.globalClock.setFrameRate(60) self.base.disableMouse() if not 0 < numberOfPlayers < 5: raise ValueError("Number of players must be from 1 to 4") self.numberOfPlayers = numberOfPlayers self.createLights() self.initialisePhysics(debug=False) # Load the default arena self.level = Level("01.json", self) # disable default cam so that we can have multiplayer base.camNode.setActive(False) # moved player setup out to its own method self.setupPlayers(self.numberOfPlayers) # Set up shaders for shadows and cartoon outline self.setupShaders() # Create an audio manager. This is attached to the camera for # player 1, so sounds close to other players might not be very # loud self.audioManager = Audio3DManager.Audio3DManager(self.base.sfxManagerList[0], self.playerCameras[0]) # Distance should be in m, not feet self.audioManager.setDistanceFactor(3.28084) # Now initialise audio for all vehicles that were # previously set up. We can't just create the audio manager # first because we need the player 1 camera for vehicle in self.playerVehicles: vehicle.initialiseSound(self.audioManager) self.initialiseCollisionInfo() def run(self): """ Start running the game loop by adding it to the task manager """ self.taskMgr.add(self.gameLoop, "update") def setupPlayers(self, numberOfPlayers): """ Method to set up player, camera and skybox Sets up two player splitscreen TODO: Needs reorganising, removal of hardcoding """ self.playerCameras = [] self.playerVehicles = [] self.playerControllers = [] self.playerInputKeys = range(numberOfPlayers) # default camera position bound to each car cameraPosition = (0, -8, 3) vehiclePositions = self.level.spawnPositions if numberOfPlayers > len(vehiclePositions): raise ValueError( "Number of players (%d) requested is greater " "than the number of spawn positions (%d)" % (numberOfPlayers, len(vehiclePositions)) ) # single player display setup - default displayXStart = 0.0 displayXEnd = 1.0 displayYStart = 0.0 displayYEnd = 1.0 # I am not proud of myself for this... There has to be a better way using maths # But it's nearly 2am and I'll figure it out tomorrow for i in xrange(numberOfPlayers): vehiclePosition = vehiclePositions[i] if numberOfPlayers == 2: if i == 0: # player 1, indexes from 0 displayXStart = 0.0 # don't need this but it's safer to have it' displayXEnd = 1.0 displayYStart = 0.5 displayYEnd = 1.0 else: displayXStart = 0.0 # don't need this but it's safer to have it' displayXEnd = 1.0 displayYStart = 0.0 displayYEnd = 0.5 elif numberOfPlayers == 3: if i == 0: # top of screen displayXStart = 0.0 # don't need this but it's safer to have it' displayXEnd = 1.0 displayYStart = 0.5 displayYEnd = 1.0 elif i == 1: # p2, bottom left displayXStart = 0.0 displayXEnd = 0.5 displayYStart = 0.0 displayYEnd = 0.5 else: # bottom right displayXStart = 0.5 displayXEnd = 1.0 displayYStart = 0.0 displayYEnd = 0.5 elif numberOfPlayers == 4: if i == 0: # p1, top left displayXStart = 0.0 # don't need this but it's safer to have it' displayXEnd = 0.5 displayYStart = 0.5 displayYEnd = 1.0 elif i == 1: # p2, top right displayXStart = 0.5 displayXEnd = 1.0 displayYStart = 0.5 displayYEnd = 1.0 elif i == 2: # p3, bottom left displayXStart = 0.0 displayXEnd = 0.5 displayYStart = 0.0 displayYEnd = 0.5 else: # else p4, bottom right displayXStart = 0.5 displayXEnd = 1.0 displayYStart = 0.0 displayYEnd = 0.5 # setup display area for this player's camera displayBox = (displayXStart, displayXEnd, displayYStart, displayYEnd) # set up camera with display area above and default camera position camera = self.createCamera(displayBox, cameraPosition) self.playerCameras.append(camera) # set up player car vehicle = Vehicle(vehiclePosition, self.worldRender, self.world, self.base) self.playerVehicles.append(vehicle) # Create a 2D display region for showing the player's HUD playerRender2d = self.createPlayerDisplay(displayBox) # set up player controller with car, camera and keyset playerController = PlayerControl( self.playerVehicles[i], self.playerCameras[i], self.playerInputKeys[i], playerRender2d ) self.playerControllers.append(playerController) # set up skybox for this particular camera set and hide from other cameras self.createSkybox( self.playerCameras[i], self.playerInputKeys[i] ) # can use inputkey code for bitmaskCode as it will be unique def createPlayerDisplay(self, displayBox): """Create a 2D display region with camera to be used to show things like the speedo and points """ displayRegion = self.base.win.makeDisplayRegion(*displayBox) displayRegion.setSort(20) camera2d = NodePath(Camera("player2dcam")) lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setNearFar(-1000, 1000) camera2d.node().setLens(lens) render2d = NodePath("render2d") render2d.setDepthTest(False) render2d.setDepthWrite(False) camera2d.reparentTo(render2d) displayRegion.setCamera(camera2d) return render2d def windowEventSetup(self): """ Method to bind window events (resizing etc) to windowEventHandler method """ self.base.accept("window-event", self.windowEventHandler) def windowEventHandler(self, window=None): """ Called when the panda window is modified to update FOV and aspect ratio TODO fix hardcoding for camera names """ if window.isClosed(): sys.exit() wp = window.getProperties() windowWidth = wp.getXSize() windowHeight = wp.getYSize() for i in self.playerCameras: # added to allow for changing player number # since window size has changed we need to update the aspect ratio and FOV i.node().getLens().setAspectRatio(windowWidth / (windowHeight / 2)) i.node().getLens().setFov(60) def createCamera(self, dispRegion, pos): """ Method to create a camera. Takes displayRegion and a position tuple. Sets aspect ratio based on window properties and also sets FOV """ camera = base.makeCamera(base.win, displayRegion=dispRegion) windowWidth = base.win.getXSize() windowHeight = base.win.getYSize() camera.node().getLens().setAspectRatio(windowWidth / (windowHeight / 2)) camera.node().getLens().setFov(60) camera.setPos(pos) return camera def initialisePhysics(self, debug=False): """ Create Bullet world for physics objects """ self.world = BulletWorld() self.world.setGravity(Vec3(0, 0, -9.81)) if debug: self.debugNP = self.outsideWorldRender.attachNewNode(BulletDebugNode("Debug")) self.debugNP.show() self.debugNP.node().showWireframe(True) self.debugNP.node().showConstraints(True) self.debugNP.node().showBoundingBoxes(False) self.debugNP.node().showNormals(True) self.world.setDebugNode(self.debugNP.node()) def createSkybox(self, currentCamera, bitmaskCode): """ Create a skybox linked to the current camera setup. Skybox will only be shown to camera with this bitmaskCode TODO: remove bitmaskCode or make it more modular to reduce coupling """ sky = self.loader.loadModel("data/models/sky/cube.egg") diffuse = self.loader.loadTexture(self.level.skyTexture) sky.setTexture(diffuse) sky.setScale(270) # Get it to follow the camera so it feels as if it's infinitely # far away, but call setCompass so it rotates with the world sky.reparentTo(currentCamera) sky.setCompass() # hide skybox from other players sky.hide(BitMask32.allOn()) # show only to this player's camera currentCamera.node().setCameraMask(BitMask32.bit(bitmaskCode)) sky.show(BitMask32.bit(bitmaskCode)) def createLights(self): """Create an ambient light and a spotlight for shadows""" self.render.clearLight() alight = AmbientLight("ambientLight") alight.setColor(Vec4(0.7, 0.7, 0.7, 1)) alightNP = self.worldRender.attachNewNode(alight) self.worldRender.setLight(alightNP) # Create a directional light for shadows dlight = DirectionalLight("dLight") dlight.setColor(Vec4(0.6, 0.6, 0.6, 1)) dlight.setShadowCaster(True, 1024, 1024) dlight.getLens().setNearFar(1, 15) dlight.getLens().setFilmSize(128, 128) dlightNP = self.worldRender.attachNewNode(dlight) dlightNP.setPos(0, 0, 10) dlightNP.lookAt(0, 0, 0) self.worldRender.setLight(dlightNP) def setupShaders(self): """ Creates shaders for cartoon outline and enables shaders for shadows """ if self.base.win.getGsg().getSupportsBasicShaders() == 0: return thickness = 1.0 for camera in self.playerCameras: self.filters = CommonFilters(self.base.win, camera) filterEnabled = self.filters.setCartoonInk(separation=thickness) if filterEnabled == False: # Couldn't enable filter, video card probably # doesn't support filter return self.worldRender.setShaderAuto() def initialiseCollisionInfo(self): """ Sets up information required to later check for collisions """ self.previousContactForce = defaultdict(float) self.collisionDetected = defaultdict(bool) # List of node paths that we want to check for collisions on, # with information about the collision sounds to play self.collisionObjects = dict((v.rigidNode, v.collisionSound) for v in self.playerVehicles) self.collisionObjects.update(self.level.collisionObjects) # For assigning points etc, keep track of the player that # controls each vehicle self.vehicleControllers = dict( (vehicle.rigidNode, player) for vehicle, player in zip(self.playerVehicles, self.playerControllers) ) def checkCollisions(self, dt): """ Check to see what objects have collided and take actions Eg. play sounds, update player points """ # Use the change in the sum of contact force magnitudes to # check for collisions. # Go though contact points, calculating the total contact # force on all nodes of interest totalContactForce = defaultdict(float) for manifold in self.world.getManifolds(): nodes = (manifold.getNode0(), manifold.getNode1()) # Sum all points to get total impulse. Not sure if this is a # good idea or we should use individual points, and if we # need to use force direction rather than just magnitude points = manifold.getManifoldPoints() totalImpulse = sum((p.getAppliedImpulse() for p in points)) # Use force to get a more frame-rate independent measure of # collision magnitude force = totalImpulse / dt for node in nodes: if node in self.collisionObjects: totalContactForce[node] += force # If both objects are vehicles, then update points if all(n in self.vehicleControllers for n in nodes): self.calculateCollisionPoints( manifold, self.vehicleControllers[nodes[0]], self.vehicleControllers[nodes[1]] ) for node, force in totalContactForce.iteritems(): forceChange = force - self.previousContactForce[node] if self.collisionDetected[node]: # If a collision was recently detected, don't keep checking # This avoids sounds repeatedly starting to play continue soundInfo = self.collisionObjects[node] if forceChange > soundInfo.thresholdForce: self.playCollisionSound(soundInfo, forceChange) # Set collision detected, and then set a time out # so that it is later reset to False self.collisionDetected[node] = True self.taskMgr.doMethodLater( 0.2, self.collisionDetected.update, "clearColision", extraArgs=[{node: False}] ) # Want to reset anything not touching to zero, so replace # previous contact forces with current ones rather than updating self.previousContactForce = totalContactForce def playCollisionSound(self, soundInfo, magnitude): """Play a sound when an object is impacted Selects from a list of sounds depending on the collision magnitude, and scales the sound volume by the magnitude """ relativeMagnitude = (magnitude - soundInfo.thresholdForce) / (soundInfo.maxForce - soundInfo.thresholdForce) soundIndex = min(int(relativeMagnitude * len(soundInfo.sounds)), len(soundInfo.sounds) - 1) collisionSound = self.audioManager.loadSfx(soundInfo.sounds[soundIndex]) self.audioManager.attachSoundToObject(collisionSound, soundInfo.nodePath) collisionSound.setVolume(min(0.2 + magnitude / soundInfo.maxForce, 1.0)) collisionSound.play() def calculateCollisionPoints(self, manifold, player1, player2): """Calculate points to give players when vehicles collide """ # Todo: Make this actually assign points based on the vehicle # travelling towards the collision location manifoldPoints = manifold.getManifoldPoints() totalImpulse = sum((p.getAppliedImpulse() for p in manifoldPoints)) player1.add_points(int(totalImpulse) // 100) player2.add_points(int(totalImpulse) // 100) def gameLoop(self, task): dt = self.globalClock.getDt() for i in self.playerControllers: # added to allow for changing player number i.updatePlayer(dt) self.checkCollisions(dt) self.world.doPhysics(dt) return task.cont
class ToonMaker(ShowBase): def __init__(self): # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) self.disableMouse() self.cam.node().getLens().setNear(10.0) self.cam.node().getLens().setFar(200.0) camera.setPos(0, -50, 0) # Check video card capabilities. if not self.win.getGsg().getSupportsBasicShaders(): addTitle( "Toon Shader: Video driver reports that Cg shaders are not supported." ) return # Enable a 'light ramp' - this discretizes the lighting, # which is half of what makes a model look like a cartoon. # Light ramps only work if shader generation is enabled, # so we call 'setShaderAuto'. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) tempnode.setShaderAuto() self.cam.node().setInitialState(tempnode.getState()) # Use class 'CommonFilters' to enable a cartoon inking filter. # This can fail if the video card is not powerful enough, if so, # display an error and exit. self.separation = 1 # Pixels self.filters = CommonFilters(self.win, self.cam) filterok = self.filters.setCartoonInk(separation=self.separation) if (filterok == False): addTitle( "Toon Shader: Video card not powerful enough to do image postprocessing" ) return # Show instructions in the corner of the window. self.title = addTitle( "Panda3D: Tutorial - Toon Shading with Normals-Based Inking") self.inst1 = addInstructions(0.06, "ESC: Quit") self.inst2 = addInstructions( 0.12, "Up/Down: Increase/Decrease Line Thickness") self.inst3 = addInstructions(0.18, "V: View the render-to-texture results") # Load a dragon model and animate it. self.character = Actor() self.character.loadModel('models/nik-dragon') self.character.reparentTo(render) self.character.loadAnims({'win': 'models/nik-dragon'}) self.character.loop('win') self.character.hprInterval(15, (360, 0, 0)).loop() # Create a non-attenuating point light and an ambient light. plightnode = PointLight("point light") plightnode.setAttenuation((1, 0, 0)) plight = render.attachNewNode(plightnode) plight.setPos(30, -50, 0) alightnode = AmbientLight("ambient light") alightnode.setColor((0.8, 0.8, 0.8, 1)) alight = render.attachNewNode(alightnode) render.setLight(alight) render.setLight(plight) # Panda contains a built-in viewer that lets you view the # results of all render-to-texture operations. This lets you # see what class CommonFilters is doing behind the scenes. self.accept("v", self.bufferViewer.toggleEnable) self.accept("V", self.bufferViewer.toggleEnable) self.bufferViewer.setPosition("llcorner") self.accept("s", self.filters.manager.resizeBuffers) # These allow you to change cartooning parameters in realtime self.accept("escape", sys.exit, [0]) self.accept("arrow_up", self.increaseSeparation) self.accept("arrow_down", self.decreaseSeparation) def increaseSeparation(self): self.separation = self.separation * 1.11111111 print("separation: %f" % (self.separation)) self.filters.setCartoonInk(separation=self.separation) def decreaseSeparation(self): self.separation = self.separation * 0.90000000 print("separation: %f" % (self.separation)) self.filters.setCartoonInk(separation=self.separation)
class ArcnsApp(DirectObject): #class ArcnsApp, main class def __init__(self): #basic init start base.disableMouse() self.wp = WindowProperties() self.wp.setCursorHidden(True) base.win.requestProperties(self.wp) cm = CardMaker("cursor") cm.setFrame(0, 0.1, -0.13, 0) self.cust_mouse = render.attachNewNode(cm.generate()) self.cust_mouse_tex = [] self.cust_mouse_tex.append( loader.loadTexture("models/cursors/blank_cursor.png")) self.cust_mouse_tex.append( loader.loadTexture("models/cursors/main_cursor.png")) self.cust_mouse.setTexture(self.cust_mouse_tex[0]) self.cust_mouse.setTransparency(TransparencyAttrib.MAlpha) self.cust_mouse.reparentTo(render2d) self.cust_mouse.setBin("gui-popup", 100) base.mouseWatcherNode.setGeometry(self.cust_mouse.node()) #text and background textVersion = OnscreenText(text="v0.0", font=arcFont, pos=(1.15, -0.95), fg=(0, 0, 0, 1), bg=(1, 1, 1, 0.8)) base.setBackgroundColor(1, 1, 1) self.curdir = (appRunner.p3dFilename.getDirname() if appRunner else "..") self.main_config = json.loads("".join([ line.rstrip().lstrip() for line in file(self.curdir + "/config.json", "rb") ])) #arrows (GENERAL) self.arrow = loader.loadModel("models/static/arrow") self.lst_arrows = [] self.c_arr = CardMaker("arrow_hide") self.c_arr.setFrame(-1, 1, -0.8, 0.6) #light ramp self.rampnode = NodePath(PandaNode("temp node")) self.rampnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.1, 0.7)) self.rampnode.setShaderAuto() base.cam.node().setInitialState(self.rampnode.getState()) #ink filter self.filters = CommonFilters(base.win, base.cam) self.filterok = self.filters.setCartoonInk(separation=1) #keyboard inputs self.accept("escape", sys.exit, [0]) #lights self.lst_lghts = [] #ambient light (permanent) self.alghtnode = AmbientLight("amb light") self.alghtnode.setColor(Vec4(0.4, 0.4, 0.4, 1)) self.alght = render.attachNewNode(self.alghtnode) render.setLight(self.alght) #language recup self.langtab = self.main_config["lang"][self.main_config["lang_chx"]] self.lang = json.loads("".join([ line.rstrip().lstrip() for line in file("misc/lang/" + self.langtab[0] + ".json", "rb") ])) #common gui elements self.voile = [] self.voile.append( DirectFrame(frameSize=(-2, 2, -2, 2), frameColor=(0, 0, 0, 0.8))) self.voile[0].setBin("gui-popup", 1) self.voile[0].hide() self.voile.append(arcLabel("", (0, 0, 0.3), txtalgn=TextNode.ACenter)) self.voile[1].setBin("gui-popup", 1) self.voile[1].reparentTo(self.voile[0]) self.voile.append(arcLabel("", (0, 0, 0.17), txtalgn=TextNode.ACenter)) self.voile[2].setBin("gui-popup", 1) self.voile[2].reparentTo(self.voile[0]) self.voile.append( arcButton(self.lang["main_dialog"]["valid"], (-0.2, 0, 0), None, txtalgn=TextNode.ACenter)) self.voile[3].setBin("gui-popup", 1) self.voile[3].reparentTo(self.voile[0]) self.voile.append( arcButton(self.lang["main_dialog"]["cancel"], (0.2, 0, 0), None, txtalgn=TextNode.ACenter)) self.voile[4].setBin("gui-popup", 1) self.voile[4].reparentTo(self.voile[0]) #mouse handler self.mouse_trav = CollisionTraverser() self.mouse_hand = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.mouse_trav.addCollider(self.pickerNP, self.mouse_hand) self.pickly_node = render.attachNewNode("pickly_node") #init main scene self.scene = mainScene(self) def change_cursor(self, chx): self.cust_mouse.setTexture(self.cust_mouse_tex[chx]) def change_screen(self): # #TODO : changement de la résolution #TODO : ouverture d'une nouvelle fenêtre (uniquement accessible avec l'option "windowed" activée) # wp = WindowProperties() # wp.setSize(200, 200) # base.win.requestProperties(wp)
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) # Disable the camera trackball controls. self.disableMouse() # Load the environment model. self.environ = self.loader.loadModel("models/environment") # Reparent the model to render. self.environ.reparentTo(self.render) # Apply scale and position transforms on the model. self.environ.setScale(0.25, 0.25, 0.25) self.environ.setPos(-8, 42, 0) # Add the spinCameraTask procedure to the task manager. self.taskMgr.add(self.spinCameraTask, "SpinCameraTask") # Load and transform the panda actor. self.pandaActor = Actor(models="models/panda-model", anims={"walk": "models/panda-walk4"}) self.pandaActor.setScale(0.005, 0.005, 0.005) self.pandaActor.reparentTo(self.render) # Loop its animation. self.pandaActor.loop("walk") # Create the four lerp intervals needed for the panda to # walk back and forth. A = Point3(0, 10, 0) B = Point3(0, -10, 0) pandaPosInterval1 = self.pandaActor.posInterval(13, B, startPos=A) pandaPosInterval2 = self.pandaActor.posInterval(13, A, startPos=B) pandaHprInterval1 = self.pandaActor.hprInterval(3, Point3(180, 0, 0), startHpr=Point3( 0, 0, 0)) pandaHprInterval2 = self.pandaActor.hprInterval(3, Point3(0, 0, 0), startHpr=Point3( 180, 0, 0)) # Create and play the sequence that coordinates the intervals. self.pandaPace = Sequence(pandaPosInterval1, pandaHprInterval1, pandaPosInterval2, pandaHprInterval2, name="pandaPace") self.pandaPace.loop() self.teapot = self.loader.loadModel('models/teapot') self.teapot.reparentTo(self.render) self.console = Console(self) self.filters = CommonFilters(self.win, self.cam) self.filters.setCartoonInk() self.filters.setAmbientOcclusion() self.filters.setBloom() # Define a procedure to move the camera. def spinCameraTask(self, task): angleDegrees = task.time * 6.0 angleRadians = angleDegrees * (pi / 180.0) self.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3) self.camera.setHpr(angleDegrees, 0, 0) return Task.cont
class World(ShowBase, object): def __init__(self, camp=[2000,500,2000], lookatp=[0,0,250], up = [0,0,1], fov = 40, w = 2000, h = 1500): """ :param camp: :param lookatp: :param fov: :param w: width of window :param h: height of window """ super(World, self).__init__() self.setBackgroundColor(1, 1, 1) # set up lens lens = PerspectiveLens() lens.setFov(fov) lens.setNearFar(1, 50000) self.disableMouse() self.cam.setPos(camp[0], camp[1], camp[2]) self.cam.lookAt(Point3(lookatp[0], lookatp[1], lookatp[2]), Vec3(up[0], up[1], up[2])) self.cam.node().setLens(lens) # set up slight ablight = AmbientLight("ambientlight") ablight.setColor(Vec4(0.2, 0.2, 0.2, 1)) self.__ablightnode = self.cam.attachNewNode(ablight) self.render.setLight(self.__ablightnode) ptlight0 = PointLight("pointlight1") ptlight0.setColor(VBase4(1, 1, 1, 1)) self.__ptlightnode0 = self.cam.attachNewNode(ptlight0) self.__ptlightnode0.setPos(0, 0, 0) self.render.setLight(self.__ptlightnode0) ptlight1 = PointLight("pointlight1") ptlight1.setColor(VBase4(.4, .4, .4, 1)) self.__ptlightnode1 = self.cam.attachNewNode(ptlight1) self.__ptlightnode1.setPos(self.cam.getPos().length(), 0, self.cam.getPos().length()) self.render.setLight(self.__ptlightnode1) ptlight2 = PointLight("pointlight2") ptlight2.setColor(VBase4(.3, .3, .3, 1)) self.__ptlightnode2 = self.cam.attachNewNode(ptlight2) self.__ptlightnode2.setPos(-self.cam.getPos().length(), 0, base.cam.getPos().length()) self.render.setLight(self.__ptlightnode2) self.pg = pg self.pggen = pg.PandaGeomGen() # set up inputmanager self.inputmgr = im.InputManager(self, lookatp, self.pggen) thepot = [] taskMgr.add(self.cycleUpdate, "cycle update", extraArgs=[thepot], appendTask=True) # set up rotational cam self.lookatp = lookatp # taskMgr.doMethodLater(.1, self.rotateCam, "rotate cam") # set window size props = WindowProperties() props.setSize(w, h) self.win.requestProperties(props) # set up cartoon effect self.__separation = 1 self.filters = CommonFilters(self.win, self.cam) self.filters.setCartoonInk(separation=self.__separation) # self.setCartoonShader(True) def cycleUpdate(self, thepot, task): # reset aspect ratio aspectRatio = self.getAspectRatio() self.cam.node().getLens().setAspectRatio(aspectRatio) self.inputmgr.checkMouse1Drag() self.inputmgr.checkMouse2Drag() self.inputmgr.checkMouseWheel() # if len(thepot) > 0: # for x in thepot: # x.removeNode() # center = self.inputmgr.aimSphereCN.getSolid(0).getCenter() # radius = self.inputmgr.aimSphereCN.getSolid(0).getRadius() # thepot.append(self.pggen.plotSphere(self.render, center, radius, rgba = (1,0,0,0.2))) # normal = self.inputmgr.aimPlaneCN.getSolid(0).getPlane(4).getNormal() # thepot.append(self.pggen.plotArrow(self.render, spos=(0,0,0), epos=normal*100, thickness = 20, rgba = (1,0,0,0.2))) return task.cont def rotateCam(self, task): campos = self.cam.getPos() camangle = math.atan2(campos[1], campos[0]) # print camangle if camangle < 0: camangle += math.pi*2 if camangle >= math.pi*2: camangle = 0 else: camangle += math.pi/180 camradius = math.sqrt(campos[0]*campos[0]+campos[1]*campos[1]) camx = camradius*math.cos(camangle) camy= camradius*math.sin(camangle) self.cam.setPos(camx, camy, campos[2]) self.cam.lookAt(self.lookatp[0], self.lookatp[1], self.lookatp[2]) return task.cont def changeLookAt(self, lookatp): """ This function is questionable as lookat changes the rotation of the camera :param lookatp: :return: author: weiwei date: 20180606 """ self.cam.lookAt(lookatp[0], lookatp[1], lookatp[2]) self.inputmgr = im.InputManager(base, lookatp, self.pggen) def setCartoonShader(self, switchtoon = False): """ set cartoon shader, the following program is a reference https://github.com/panda3d/panda3d/blob/master/samples/cartoon-shader/advanced.py :return: author: weiwei date: 20180601 """ this_dir, this_filename = os.path.split(__file__) if switchtoon: lightinggen = Filename.fromOsSpecific(os.path.join(this_dir, "shaders", "lightingGen.sha")) tempnode = NodePath(PandaNode("temp node")) tempnode.setShader(loader.loadShader(lightinggen)) self.cam.node().setInitialState(tempnode.getState()) # self.render.setShaderInput("light", self.cam) self.render.setShaderInput("light", self.__ptlightnode0) # normalsBuffer = self.win.makeTextureBuffer("normalsBuffer", 0, 0) normalsBuffer.setClearColor(LVecBase4(0.5, 0.5, 0.5, 1)) normalsCamera = self.makeCamera( normalsBuffer, lens=self.cam.node().getLens(), scene = self.render) normalsCamera.reparentTo(self.cam) normalgen = Filename.fromOsSpecific(os.path.join(this_dir, "shaders", "normalGen.sha")) tempnode = NodePath(PandaNode("temp node")) tempnode.setShader(loader.loadShader(normalgen)) normalsCamera.node().setInitialState(tempnode.getState()) drawnScene = normalsBuffer.getTextureCard() drawnScene.setTransparency(1) drawnScene.setColor(1, 1, 1, 0) drawnScene.reparentTo(render2d) self.drawnScene = drawnScene self.separation = 0.0007 self.cutoff = 0.05 normalgen = Filename.fromOsSpecific(os.path.join(this_dir, "shaders", "inkGen.sha")) drawnScene.setShader(loader.loadShader(normalgen)) drawnScene.setShaderInput("separation", LVecBase4(self.separation, 0, self.separation, 0)) drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
class World(ShowBase, object): def __init__(self, camp=[2000, 500, 2000], lookatpos=[0, 0, 250], up=[0, 0, 1], fov=40, w=2000, h=1500, toggledebug=False, autocamrotate=False): """ :param camp: :param lookatpos: :param fov: :param w: width of window :param h: height of window """ # the taskMgr, loader, render2d, etc. are added to builtin after initializing the showbase parental class super().__init__() self.setBackgroundColor(1, 1, 1) # set up lens lens = PerspectiveLens() lens.setFov(fov) lens.setNearFar(1, 50000) # disable the default mouse control self.disableMouse() self.cam.setPos(camp[0], camp[1], camp[2]) self.cam.lookAt(Point3(lookatpos[0], lookatpos[1], lookatpos[2]), Vec3(up[0], up[1], up[2])) self.cam.node().setLens(lens) # set up slight ablight = AmbientLight("ambientlight") ablight.setColor(Vec4(0.2, 0.2, 0.2, 1)) self.__ablightnode = self.cam.attachNewNode(ablight) self.render.setLight(self.__ablightnode) ptlight0 = PointLight("pointlight1") ptlight0.setColor(Vec4(1, 1, 1, 1)) self.__ptlightnode0 = self.cam.attachNewNode(ptlight0) self.__ptlightnode0.setPos(0, 0, 0) self.render.setLight(self.__ptlightnode0) ptlight1 = PointLight("pointlight1") ptlight1.setColor(Vec4(.4, .4, .4, 1)) self.__ptlightnode1 = self.cam.attachNewNode(ptlight1) self.__ptlightnode1.setPos(self.cam.getPos().length(), 0, self.cam.getPos().length()) self.render.setLight(self.__ptlightnode1) ptlight2 = PointLight("pointlight2") ptlight2.setColor(Vec4(.3, .3, .3, 1)) self.__ptlightnode2 = self.cam.attachNewNode(ptlight2) self.__ptlightnode2.setPos(-self.cam.getPos().length(), 0, base.cam.getPos().length()) self.render.setLight(self.__ptlightnode2) # helpers # use pg to access the util functions; use pggen to generate the geometries for decoration # for back-compatibility purpose self.pg = pg self.pggen = pg.PandaDecorator() self.p3dh = p3dhelper self.o3dh = o3dhelper self.rm = rm # set up inputmanager self.inputmgr = im.InputManager(self, lookatpos, self.pggen) taskMgr.add(self.__interactionUpdate, "interaction", appendTask=True) # set up rotational cam self.lookatp = lookatpos if autocamrotate: taskMgr.doMethodLater(.1, self.__rotatecamUpdate, "rotate cam") # set window size props = WindowProperties() props.setSize(w, h) self.win.requestProperties(props) # set up cartoon effect self.__separation = 1 self.filters = CommonFilters(self.win, self.cam) self.filters.setCartoonInk(separation=self.__separation) # self.setCartoonShader(False) # set up physics world self.physicsworld = BulletWorld() self.physicsworld.setGravity(Vec3(0, 0, -981)) taskMgr.add(self.__physicsUpdate, "physics", appendTask=True) if toggledebug: globalbprrender = base.render.attachNewNode("globalbpcollider") debugNode = BulletDebugNode('Debug') debugNode.showWireframe(True) debugNode.showConstraints(True) debugNode.showBoundingBoxes(False) debugNode.showNormals(True) self._debugNP = globalbprrender.attachNewNode(debugNode) self._debugNP.show() self.physicsworld.setDebugNode(self._debugNP.node()) # set up render update self.__objtodraw = [ ] # the nodepath, collision model, or bullet dynamics model to be drawn taskMgr.add(self.__renderUpdate, "render", appendTask=True) def __interactionUpdate(self, task): # reset aspect ratio aspectRatio = self.getAspectRatio() self.cam.node().getLens().setAspectRatio(aspectRatio) self.inputmgr.check_mouse1drag() self.inputmgr.check_mouse2drag() self.inputmgr.check_mouse3click() self.inputmgr.check_mousewheel() self.inputmgr.check_resetcamera() return task.cont def __physicsUpdate(self, task): self.physicsworld.doPhysics(globalClock.getDt(), 20, 1.0 / 1200.0) return task.cont def __renderUpdate(self, task): for otdele in self.__objtodraw: otdele.detachNode() otdele.reparentTo(base.render) return task.cont def __rotatecamUpdate(self, task): campos = self.cam.getPos() camangle = math.atan2(campos[1], campos[0]) # print camangle if camangle < 0: camangle += math.pi * 2 if camangle >= math.pi * 2: camangle = 0 else: camangle += math.pi / 180 camradius = math.sqrt(campos[0] * campos[0] + campos[1] * campos[1]) camx = camradius * math.cos(camangle) camy = camradius * math.sin(camangle) self.cam.setPos(camx, camy, campos[2]) self.cam.lookAt(self.lookatp[0], self.lookatp[1], self.lookatp[2]) return task.cont def attachRUD(self, *args): """ add to the render update list *args,**kwargs :param obj: nodepath, collision model, or bullet dynamics model :return: author: weiwei date: 20190627 """ for obj in args: self.__objtodraw.append(obj) def detachRUD(self, *args): """ remove from the render update list :param obj: nodepath, collision model, or bullet dynamics model :return: author: weiwei date: 20190627 """ for obj in args: self.__objtodraw.remove(obj) def removeFromRUD(self, obj): """ add to render with update :param obj: nodepath, collision model, or bullet dynamics model :return: author: weiwei date: 20190627 """ self.__objtodraw.append(obj) def changeLookAt(self, lookatp): """ This function is questionable as lookat changes the rotation of the camera :param lookatp: :return: author: weiwei date: 20180606 """ self.cam.lookAt(lookatp[0], lookatp[1], lookatp[2]) self.inputmgr = im.InputManager(self, lookatp, self.pggen) def setCartoonShader(self, switchtoon=False): """ set cartoon shader, the following program is a reference https://github.com/panda3d/panda3d/blob/master/samples/cartoon-shader/advanced.py :return: author: weiwei date: 20180601 """ this_dir, this_filename = os.path.split(__file__) if switchtoon: lightinggen = Filename.fromOsSpecific( os.path.join(this_dir, "shaders", "lightingGen.sha")) tempnode = NodePath("temp") tempnode.setShader(loader.loadShader(lightinggen)) self.cam.node().setInitialState(tempnode.getState()) # self.render.setShaderInput("light", self.cam) self.render.setShaderInput("light", self.__ptlightnode0) # normalsBuffer = self.win.makeTextureBuffer("normalsBuffer", 0, 0) normalsBuffer.setClearColor(Vec4(0.5, 0.5, 0.5, 1)) normalsCamera = self.makeCamera(normalsBuffer, lens=self.cam.node().getLens(), scene=self.render) normalsCamera.reparentTo(self.cam) normalgen = Filename.fromOsSpecific( os.path.join(this_dir, "shaders", "normalGen.sha")) tempnode = NodePath("temp") tempnode.setShader(loader.loadShader(normalgen)) normalsCamera.node().setInitialState(tempnode.getState()) drawnScene = normalsBuffer.getTextureCard() drawnScene.setTransparency(1) drawnScene.setColor(1, 1, 1, 0) drawnScene.reparentTo(render2d) self.drawnScene = drawnScene self.separation = 0.0007 self.cutoff = 0.05 normalgen = Filename.fromOsSpecific( os.path.join(this_dir, "shaders", "inkGen.sha")) drawnScene.setShader(loader.loadShader(normalgen)) drawnScene.setShaderInput("separation", Vec4(self.separation, 0, self.separation, 0)) drawnScene.setShaderInput("cutoff", Vec4(self.cutoff))
class ToonMaker(ShowBase): def __init__(self): # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) self.disableMouse() self.cam.node().getLens().setNear(10.0) self.cam.node().getLens().setFar(200.0) camera.setPos(0, -50, 0) # Check video card capabilities. if not self.win.getGsg().getSupportsBasicShaders(): addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.") return # Enable a 'light ramp' - this discretizes the lighting, # which is half of what makes a model look like a cartoon. # Light ramps only work if shader generation is enabled, # so we call 'setShaderAuto'. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4)) tempnode.setShaderAuto() self.cam.node().setInitialState(tempnode.getState()) # Use class 'CommonFilters' to enable a cartoon inking filter. # This can fail if the video card is not powerful enough, if so, # display an error and exit. self.separation = 1 # Pixels self.filters = CommonFilters(self.win, self.cam) filterok = self.filters.setCartoonInk(separation=self.separation) if (filterok == False): addTitle( "Toon Shader: Video card not powerful enough to do image postprocessing") return # Show instructions in the corner of the window. self.title = addTitle( "Panda3D: Tutorial - Toon Shading with Normals-Based Inking") self.inst1 = addInstructions(0.06, "ESC: Quit") self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness") self.inst3 = addInstructions(0.18, "V: View the render-to-texture results") # Load a dragon model and animate it. self.character = Actor() self.character.loadModel('models/nik-dragon') self.character.reparentTo(render) self.character.loadAnims({'win': 'models/nik-dragon'}) self.character.loop('win') self.character.hprInterval(15, (360, 0, 0)).loop() # Create a non-attenuating point light and an ambient light. plightnode = PointLight("point light") plightnode.setAttenuation((1, 0, 0)) plight = render.attachNewNode(plightnode) plight.setPos(30, -50, 0) alightnode = AmbientLight("ambient light") alightnode.setColor((0.8, 0.8, 0.8, 1)) alight = render.attachNewNode(alightnode) render.setLight(alight) render.setLight(plight) # Panda contains a built-in viewer that lets you view the # results of all render-to-texture operations. This lets you # see what class CommonFilters is doing behind the scenes. self.accept("v", self.bufferViewer.toggleEnable) self.accept("V", self.bufferViewer.toggleEnable) self.bufferViewer.setPosition("llcorner") self.accept("s", self.filters.manager.resizeBuffers) # These allow you to change cartooning parameters in realtime self.accept("escape", sys.exit, [0]) self.accept("arrow_up", self.increaseSeparation) self.accept("arrow_down", self.decreaseSeparation) def increaseSeparation(self): self.separation = self.separation * 1.11111111 print("separation: %f" % (self.separation)) self.filters.setCartoonInk(separation=self.separation) def decreaseSeparation(self): self.separation = self.separation * 0.90000000 print("separation: %f" % (self.separation)) self.filters.setCartoonInk(separation=self.separation)
class WorldComposer: def __init__(self, render): self.render = render self.separation = .0 def setup_world_lightning(self): """ Sets up the ambient and specular lighting of the world :return: """ ambientLight = AmbientLight("ambientLight") ambientLight.setColor((2, 2, 2, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setShadowCaster(True) directionalLight.setDirection(LVector3(-1, -1, -1)) directionalLight.setColor((.5, .5, .5, 1)) dir_light_node = self.render.attachNewNode(directionalLight) # dir_light_node.setPos(10, 2, 7) # dir_light_node.lookAt(2, 2, 0) self.render.setLight(dir_light_node) self.render.setLight(self.render.attachNewNode(ambientLight)) spot = Spotlight("Spot") spot.setColorTemperature(9000) spot.setColor(LVector3(1, 1, 1)) light = self.render.attachNewNode(spot) light.node().setScene(self.render) light.node().setShadowCaster(True) # light.node().showFrustum() light.node().getLens().setFov(40) light.node().getLens().setNearFar(2, 100) # light.setPos(10, 2, 7) light.setPos(10, 20, 20) light.lookAt(2, 2, 0) self.render.setLight(light) # plight = PointLight('plight') # plight.setColor((1, 1, 1, 1)) # plight.setAttenuation(LVector3(0.7, 0.05, 0)) # # plnp = self.render.attachNewNode(plight) # plnp.setPos(0, 0, 5) # self.render.setShaderAuto() def compose_filters(self, win, cam, seperation=.6): """ function to handle the composing, which makes for the basic look and feel of the game also handles any filters which are attached to the cam :return: """ # set up the lightramp effect tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(LightRampAttrib.makeSingleThreshold( 0.5, 0.4)) # warning can be ignored.. tempnode.setShaderAuto() cam.node().setInitialState(tempnode.getState()) self.separation = seperation # Pixels self.filters = CommonFilters(win, cam) filterok = self.filters.setCartoonInk(separation=self.separation) if (filterok == False): return
def __init__(self): super().__init__(self) self.scene = loader.loadModel("models/world") self.player = self.scene.find("player") self.basePlane = self.scene.find("basePlane") self.player.reparentTo(self.render) self.basePlane.reparentTo(self.render) self.scene.remove_node() self.taskMgr.add(self.update, "update") self.camera.setPos(self.render, 0, -100, 70) base.setBackgroundColor(0.1, 0.1, 0.1, 1) self.dirLight = DirectionalLight("dir light") self.dirLight.setShadowCaster(True, 512, 512) self.dirLight.color = (1, 0, 1, 1) self.dirLightPath = self.render.attachNewNode(self.dirLight) self.dirLightPath.setHpr(45, -60, 0) render.setLight(self.dirLightPath) self.angleTime = 0.0 self.totalAngleTime = 10.0 self.hAngle = 0 self.ambientLight = AmbientLight("ambient") self.ambientLight.color = (0.1, 0.1, 0.1, 1) self.ambLightPath = self.render.attachNewNode(self.ambientLight) render.setLight(self.ambLightPath) self.pointLight = PointLight("point") self.pointLight.color = (1, 1, 1, 1) self.pointLightPath = self.render.attachNewNode(self.pointLight) self.pointLightPath.setPos(0, 5, 5) self.pointLight.setShadowCaster(True, 512, 512) self.render.setLight(self.pointLightPath) self.fog = Fog("fog") self.fog.setColor(.1, .1, .1) self.fog.setExpDensity(.3) self.fog.setLinearRange(150, 200) self.fog.setLinearFallback(45, 160, 320) render.setFog(self.fog) self.render.setShaderAuto() self.p = self.render.attachNewNode("particles") base.enableParticles() p = ParticleEffect() p.loadConfig('./mysmoke.ptf') p.start(parent=self.p, renderParent=render) self.p.setPos(self.player, 0, 0, 2) self.font = loader.loadFont('./fonts/Magenta.ttf') self.sceneName = DirectLabel(text="Starfox visual test", parent=self.aspect2d, scale=0.07, pos=(-1.2, 0, 0.85), text_font=self.font, relief=None, text_fg=(1, 1, 1, 1), textMayChange=True, text_align=TextNode.ALeft) self.foxy = OnscreenImage(image='./UI/fox-icon-png-8.png', pos=(1.2, 9, 0.85), scale=0.1) self.foxy.setTransparency(True) self.controlsPanel = DirectDialog(frameSize=(-1.1, 1.1, -0.9, -0.7), relief=DGG.FLAT) btn = DirectButton(text="Rotate", command=self.doRotate, image='./UI/fox-icon-png-8.png', pos=(-0.9, 0, -0.8), parent=self.controlsPanel, scale=0.07, relief=None) btn2 = DirectButton(text="Anin Light", command=self.doLight, image='./UI/fox-icon-png-8.png', pos=(-0.7, 0, -0.8), parent=self.controlsPanel, scale=0.07, relief=None) self.camera.lookAt(self.player) self.makeRotation = False self.rotateAngles = 0 self.animLight = False filter = CommonFilters(base.win, base.cam) filter.setBloom(size="large", intensity=2) #filter.setAmbientOcclusion(strength = 5, radius = 3) filter.setCartoonInk(separation=4)
class world(ShowBase): def __init__(self): try: ShowBase.__init__(self) except: sys.exit("something went wrong: error while loading OpenGL") # ------------------------------- Begin of parameter variables (pretty messy actually) ------------------------------------ #debug self.debug = False #REMEMBER TO TURN THIS OFF WHEN COMMITTING THIS TO GITHUB YOU GODDAM MORRON !!! #debug self.dir = Filename.fromOsSpecific(os.getcwd()) self.timescale = 5 self.worldscale = 0.1 # currently unused self.camera_delta = 0.5 # camera delta displacement self.sensitivity_x, self.sensitivity_y = 20, 20 self.watched = None # watched object (object focused by the cursor) self.state = [ 'paused', 'free', None ] # state of things: [simulation paused/running,camera following object/free,followed object/None] print('free mode on') self.iteration = 0 #iteration for the menu to be drawn once self.collision_status = False # Keep this on False, that's definitely not a setting # currently unused self.u_constant = 6.67408 * 10**(-11) #just a quick reminder self.u_radius = 5.25 #just what I said earlier self.u_radius_margin = 0.1 #a margin added to the generic radius as a safety feature (mountains and stuff, atmosphere) # ------------------------------- End of parameter variables (sry for the mess) -------------------------------------------- # Mouse parameters self.hidden_mouse = True wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) self.win.requestProperties(wp) # preparing the menu text list: self.menu_text = [] self.menu_text.append( self.showsimpletext('The PyOS project V0.10', (0, 0.4), (0.07, 0.07), None, (1, 1, 1, True))) self.menu_text.append( self.showsimpletext('Resume', (0, 0.3), (0.06, 0.06), None, (1, 1, 1, True))) self.menu_text.append( self.showsimpletext('Quit', (0, 0.2), (0.06, 0.06), None, (1, 1, 1, True))) # btw I found something about energy transmition through thermal radiation. I think it uses some Boltzmann formula stuff. Link here: # https://fr.wikibooks.org/wiki/Plan%C3%A9tologie/La_temp%C3%A9rature_de_surface_des_plan%C3%A8tes#Puissance_re%C3%A7ue_par_la_Terre # Defining important data lists self.sounds = [ self.loader.loadSfx(self.dir + "/Sound/001.mp3"), self.loader.loadSfx(self.dir + "/Sound/002.mp3"), self.loader.loadSfx(self.dir + "/Sound/003.mp3"), self.loader.loadSfx(self.dir + "/Sound/004.mp3"), self.loader.loadSfx(self.dir + "/Sound/005.mp3") ] #buggy self.collision_solids = [ ] #collision related stuff - comments are useless - just RTFM self.light_Mngr = [] self.data = [ [ 0, 0, 0, 0, 0.003, 0, 0.15, 0.15, 0.15, 100000.00, True, [ self.loader.loadModel(self.dir + "/Engine/lp_planet_0.egg"), (0.1, 0, 0), self.loader.loadModel(self.dir + "/Engine/lp_planet_1.egg"), (0.14, 0, 0) ], "lp_planet", False, 0.1 ], [ 40, 0, 0, 0, 0.003, 0, 0.05, 0.05, 0.05, 20.00, True, [ self.loader.loadModel(self.dir + "/Engine/Icy.egg"), (0.05, 0, 0) ], "Ottilia", False, 0.1 ], [ 0, 70, 10, 0, 0.005, 0, 0.1, 0.1, 0.1, 40.00, True, [ self.loader.loadModel(self.dir + "/Engine/asteroid_1.egg"), (0, 0, 0.2) ], "Selena", False, 1 ], [ 100, 0, 10, 0, 0, 0, 5, 5, 5, 1000000, True, [ self.loader.loadModel(self.dir + "/Engine/sun1.egg"), (0.01, 0, 0), self.loader.loadModel(self.dir + "/Engine/sun1_atm.egg"), (0.01, 0, 0) ], "Sun", True, 0.1 ], [ -100, 50, 70, 0, 0, 0.002, 0.15, 0.15, 0.15, 1000.00, True, [ self.loader.loadModel(self.dir + "/Engine/Earth2.egg"), (-0.1, 0, 0), self.loader.loadModel(self.dir + "/Engine/Earth2_atm.egg"), (-0.15, 0, 0) ], "Julius_planet", False, 0.1 ] # insert your 3d models here, following the syntax ] # the correct reading syntax is [x,y,z,l,m,n,scale1,scale2,scale3,mass,static,[file,(H,p,r),file,(H,p,r)...],id,lightsource,brakeforce] for each body - x,y,z: position - l,m,n: speed - scale1,scale2,scale3: obvious (x,y,z) - mass: kg - static: boolean - [files]: panda3d readfiles list - id: str - lightsource: boolean - #if you want the hitbox to be correctly scaled, and your body to have reasonable proportions, your 3d model must be a 5*5 sphere, or at least have these proportions # create the real data list, the one used by the program self.bodies = [] for c in self.data: temp = body() temp.fill_entries(c) self.bodies.append(temp) temp = None #self.bodies.reverse() # Quick presetting self.setBackgroundColor(0, 0, 0, True) # non-body type structures loading if SKYBOX == 'sky': self.isphere = self.loader.loadModel( self.dir + "/Engine/InvertedSphere.egg") #loading skybox structure self.tex = loader.loadCubeMap(self.dir + '/Engine/Skybox4/skybox_#.png') elif SKYBOX == 'arena': self.box = self.loader.loadModel(self.dir + "/Engine/arena.egg") #load shaders (optionnal) ''' sun_shader=Shader.load(Shader.SLGLSL,self.dir+'/Engine/Shaders/flare_v.glsl',self.dir+'/Engine/Shaders/flare_f.glsl') ''' self.orbit_lines = [] #under developement # see https://www.panda3d.org/manual/?title=Collision_Solids for further collision interaction informations base.graphicsEngine.openWindows() try: # filters predefining self.filters = CommonFilters(base.win, base.cam) ''' self.filters.setBlurSharpen(amount=0) # just messing around ''' if not self.debug: self.filters.set_gamma_adjust(1.0) # can be usefull self.filters.set_bloom(intensity=1, size="medium") render.setAntialias(AntialiasAttrib.MAuto) for c in self.bodies: # loading and displaying the preloaded planets and bodies if c.is_lightSource and not self.debug: # VM filtering self.filters.setVolumetricLighting(c.filelist[u], numsamples=50, density=0.5, decay=0.95, exposure=0.035) #c.filelist[u].set_shader(sun_shader) if BLUR: self.filters.setCartoonInk() for u in range(0, len(c.filelist), 2): # loading each sub-file c.filelist[u].reparentTo(self.render) c.filelist[u].setScale(tuple(c.scale)) c.filelist[u].setPos(tuple(c.position)) #setting the collision solid up temp = hitbox() temp.Volume = CollisionSphere(0, 0, 0, self.u_radius) temp.NodePath = c.filelist[0].attachNewNode(CollisionNode( c.id)) temp.CollisionNode = temp.NodePath.node() self.collision_solids.append( temp ) #the radius is calculated by using the average scale + the u_radius # the structure of the collision_solids list will be: [temp1,temp2,...] # asteroids and irregular shapes must be slightly bigger than their hitbox in order to avoid visual glitches self.collision_solids[ len(self.collision_solids) - 1].CollisionNode.addSolid( self.collision_solids[ len(self.collision_solids) - 1].Volume) #I am definitely not explaining that temp = None if self.debug: loadPrcFileData('', 'show-frame-rate-meter true') self.collision_solids[ len(self.collision_solids) - 1].NodePath.show() # debugging purposes only print("collision: ok") print("placing body: done") if c.is_lightSource: self.light_Mngr.append([PointLight(c.id + "_other")]) self.light_Mngr[len(self.light_Mngr) - 1].append( render.attachNewNode( self.light_Mngr[len(self.light_Mngr) - 1][0])) self.light_Mngr[len(self.light_Mngr) - 1][1].setPos( tuple(c.position)) render.setLight(self.light_Mngr[len(self.light_Mngr) - 1][1]) self.light_Mngr.append([AmbientLight(c.id + "_self")]) self.light_Mngr[len(self.light_Mngr) - 1][0].setColorTemperature(3000) self.light_Mngr[len(self.light_Mngr) - 1].append( render.attachNewNode( self.light_Mngr[len(self.light_Mngr) - 1][0])) for u in range(0, len(c.filelist), 2): c.filelist[u].setLight( self.light_Mngr[len(self.light_Mngr) - 1][1]) print("lights: done") print("loaded new body, out: done") if SKYBOX == 'sky': self.isphere.setTexGen( TextureStage.getDefault(), TexGenAttrib.MWorldCubeMap ) # *takes a deep breath* cubemap stuff ! self.isphere.setTexProjector(TextureStage.getDefault(), render, self.isphere) self.isphere.setTexPos(TextureStage.getDefault(), 0, 0, 0) self.isphere.setTexScale(TextureStage.getDefault(), .5) # that's a thing... self.isphere.setTexture( self.tex ) # Create some 3D texture coordinates on the sphere. For more info on this, check the Panda3D manual. self.isphere.setLightOff() self.isphere.setScale(10000) #hope this is enough self.isphere.reparentTo(self.render) elif SKYBOX == 'arena': self.box.setPos(0, 0, 0) self.box.setScale(100) self.box.reparentTo(self.render) # collision traverser and other collision stuff # that's super important, and super tricky to explain so just check the wiki self.ctrav = CollisionTraverser() self.queue = CollisionHandlerQueue() for n in self.collision_solids: self.ctrav.add_collider(n.NodePath, self.queue) # the traverser will be automatically updated, no need to repeat this every frame # debugging only if self.debug: self.ctrav.showCollisions(render) # play a random music self.current_playing = random.randint(0, len(self.sounds) - 1) self.sounds[self.current_playing].play() # task manager stuff comes here self.taskMgr.add(self.intro_loop, 'showIntroPic') except: sys.exit(":( something went wrong: files could not be loaded") ''' self.showsimpletext("All modules loaded, simulation running",(-1.42,0.95),(0.04,0.04),None,(1,1,1,True)) self.showsimpletext("PyOS build V0.10",(-1.5,0.90),(0.04,0.04),None,(1,1,1,True)) self.showsimpletext("By l3alr0g",(-1.68,0.85),(0.04,0.04),None,(1,1,1,True)) ''' # key bindings self.accept('backspace', self.system_break) self.accept('escape', self.toggle_pause) self.accept('mouse1', self.handle_select, [True]) self.accept('wheel_up', self.handle_scrolling, [True]) # center button (just a quick test) self.accept('wheel_down', self.handle_scrolling, [False]) self.accept('z', self.move_camera, [0, True]) self.accept('q', self.move_camera, [1, True]) self.accept('s', self.move_camera, [2, True]) self.accept('d', self.move_camera, [3, True]) self.accept('a', self.move_camera, [4, True]) self.accept('e', self.move_camera, [5, True]) self.accept('z-up', self.move_camera, [0, False]) self.accept('q-up', self.move_camera, [1, False]) self.accept('s-up', self.move_camera, [2, False]) self.accept('d-up', self.move_camera, [3, False]) self.accept('a-up', self.move_camera, [4, False]) self.accept('e-up', self.move_camera, [5, False]) self.keymap = [ 'z', 0, 'q', 0, 's', 0, 'd', 0, 'a', 0, 'e', 0, 'mouse1', 0 ] self.disable_mouse() if self.debug: # draw axis coord = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] axis = [] for c in range(3): axis.append(LineSegs()) axis[c].moveTo(0, 0, 0) axis[c].drawTo(coord[c]) axis[c].setThickness(3) axis[c].setColor( tuple([coord[c][u] * 255 for u in range(len(coord[c]))] + [True])) NodePath(axis[c].create()).reparent_to(render) # camera positionning ------- self.focus_point = [ 0, 0, 0 ] # point focused: can become a body's coordinates if the user tells the program to do so self.zoom_distance = 30 # distance to the focus point in common 3D units (can be modified by scrolling) self.cam_Hpr = [0, 0, 0] # phi, alpha, theta - aka yaw, pitch, roll self.cam_Hpr = [ self.cam_Hpr[n] * pi / 180 for n in range(len(self.cam_Hpr)) ] # convert to rad phi, alpha, theta, zoom, object = self.cam_Hpr[ 0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[ 2] * pi / 180, self.zoom_distance, self.state[ 2] # temporary vars if self.state[1] == 'free': self.camera_pos = [0, 0, 0] self.camera.setPos(tuple(self.camera_pos)) elif self.state[1] == 'linked': # find the object (self.state[2]) in the data list list_pos = [ self.bodies[n].filelist[0] for n in range(len(self.bodies)) ].index(object.getParent()) self.focus_point = self.bodies[ list_pos].position # take the focused object's coordinates self.camera_pos = [ self.focus_point[0] + sin(phi) * cos(-alpha) * zoom, self.focus_point[1] - cos(phi) * cos(-alpha) * zoom, self.focus_point[2] + sin(-alpha) * zoom ] #keep it up to date so that it's not hard to find whend switching modes self.camera.setPos(tuple(self.camera_pos)) self.camera.setHpr(self.cam_Hpr) # cursor self.cursor = self.showsimpletext('.', (0, 0), (0.08, 0.08), None, ( 1, 1, 1, True)) # yeah, you can laugh, but this still works so I don't care self.pointerNode = CollisionNode('cursor') self.pointerNP = camera.attachNewNode(self.pointerNode) self.pointerNode.setFromCollideMask( BitMask32.bit(1) ) # separate collisions (in order to avoid mistakes during physical calculations) self.cursor_ray = CollisionRay() # create the mouse control ray self.pointerNode.addSolid(self.cursor_ray) self.ctrav.add_collider(self.pointerNP, self.queue) return None def showsimpletext( self, content, pos, scale, bg, fg ): #shows a predefined, basic text on the screen (variable output only) return OnscreenText(text=content, pos=pos, scale=scale, bg=bg, fg=fg) def intro_loop(self, task): if not (task.time): self.screen_fill = OnscreenImage(image=str(self.dir) + "/Engine/main_page.png", pos=(0, 0, 0), scale=(1.77777778, 1, 1)) elif task.time > 3: self.screen_fill.destroy() self.taskMgr.add(self.mouse_check, 'mousePositionTask') self.taskMgr.add(self.placement_Mngr, 'frameUpdateTask') self.taskMgr.add(self.Sound_Mngr, 'MusicHandle') self.taskMgr.add(self.camera_update, 'cameraPosition') self.taskMgr.remove('showIntroPic') return None return task.cont def placement_Mngr( self, task ): # main game mechanics, frame updating function (kinda, all pausing and menu functions must be applied here if self.state[0] == 'running' or not task.time: self.ctrav.traverse(render) #self.queue = CollisionHandlerQueue() # update the collision queue brakeforce = [0 for n in range(len(self.bodies)) ] # create an empty brakeforce list if self.queue.getNumEntries(): if self.debug: print(self.queue.getNumEntries()) # debug # now we have to create a temp list containing only the Entries that refer to collisions between bodies, # not cursor-type collisions: temp1, temp2 = [], [] for count in range(len(self.queue.getEntries())): if self.queue.getEntries()[count].getFromNodePath( ) != self.pointerNP: temp1.append(self.queue.getEntries()[count]) else: temp2.append(self.queue.getEntries()[count]) # the temp1 and temp2 lists have been created # run the check for the body-with-body collisions for c in range(0, len(temp1), 2): entry = temp1[c] brakeforce = self.collision_log(entry, brakeforce) # run the check for the cursor-with-body collisions for c in range(len(temp2)): entry = temp2[c] self.watched = entry.getIntoNodePath() # print "out" # update the collider list self.ctrav.clear_colliders() self.queue = CollisionHandlerQueue() for n in self.collision_solids: self.ctrav.add_collider(n.NodePath, self.queue) self.ctrav.add_collider(self.pointerNP, self.queue) # add the cursor ray again else: self.watched = None # collision events are now under constant surveillance acceleration = [] for c in range(len(self.bodies)): #selects the analysed body var = self.bodies[c] Bdf = [ 0, 0, 0 ] #Bdf stands for 'bilan des forces' in french, it's the resulting acceleration for d in self.bodies[0:c] + self.bodies[c + 1:len( self.bodies ) - 1]: #selects the body which action on the analysed body we're studying...not sure about that english sentence though S, M = [d.mass] + d.position, [var.mass] + var.position temp = self.dual_a(S, M) Bdf = [Bdf[x] + temp[x] for x in range(3)] # list sum # add the result to the global save list acceleration.append(Bdf) #update the bodies' position self.speed_update(acceleration, brakeforce) self.pos_update() self.apply_update() elif self.state[0] == 'paused': self.handle_menu(self.iteration) self.iteration += 1 return task.cont def speed_update(self, a, brakeforce): for c in range(len(self.bodies) ): #the function updates the speed tuple accordingly self.bodies[c].speed[0] += self.timescale * a[c][0] #self.bodies[c].speed[0]/=brakeforce[c]+1 # aero/lytho braking has to be applied to the colliding object # actually, speed isn't applied that way self.bodies[c].speed[1] += self.timescale * a[c][1] #self.bodies[c].speed[1]/=brakeforce[c]+1 self.bodies[c].speed[2] += self.timescale * a[c][2] #self.bodies[c].speed[2]/=brakeforce[c]+1 return 0 def pos_update(self): #updates the positional coordinates for c in range(len(self.bodies)): self.bodies[c].position[ 0] += self.timescale * self.bodies[c].speed[0] self.bodies[c].position[ 1] += self.timescale * self.bodies[c].speed[1] self.bodies[c].position[ 2] += self.timescale * self.bodies[c].speed[2] return 0 def apply_update(self): #actually moves the hole 3d stuff around count = 0 #local counter for c in self.bodies: for u in range(len(c.filelist)): if u % 2 != 0: c.filelist[u - 1].setHpr(c.filelist[u - 1], c.filelist[u]) else: c.filelist[u].setPos(tuple(c.position)) if c.is_lightSource: self.light_Mngr[count][1].setPos(tuple(c.position)) count += 2 #we have to change the position of the pointlight, not the ambientlight return 0 def camera_update(self, task): phi, alpha, theta, zoom, object = self.cam_Hpr[ 0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[ 2] * pi / 180, self.zoom_distance, self.state[2] if self.state[1] == 'free': self.camera.setPos(tuple(self.camera_pos)) elif self.state[1] == 'linked': # find the object (self.state[2]) in the data list list_pos = [ self.bodies[n].filelist[0] for n in range(len(self.bodies)) ].index(object.getParent()) self.focus_point = self.bodies[ list_pos].position # take the focused object's coordinates self.camera_pos = [ self.focus_point[0] + sin(phi) * cos(-alpha) * zoom, self.focus_point[1] - cos(phi) * cos(-alpha) * zoom, self.focus_point[2] + sin(-alpha) * zoom ] self.camera.setPos(tuple(self.camera_pos)) self.camera.setHpr(tuple(self.cam_Hpr)) ''' # not finished yet self.camera.setPos(self.focus_point[0]+cos(self.cam_Hpr[0])*self.zoom_distance,self.focus_point[1]+sin(self.cam_Hpr[0])*self.zoom_distance,self.focus_point[2]+sin(self.cam_Hpr[1])*self.zoom_distance) self.camera.lookAt(self.focus_point[0],self.focus_point[1],self.focus_point[2]) ''' # collision cursor stuff goes here: self.cursor_ray.setFromLens(self.camNode, 0, 0) # relatively to the camera, the cursor position will always be 0,0 which is the position of the # white point on the screen if self.keymap != ['z', 0, 'q', 0, 's', 0, 'd', 0]: for x in range(1, len(self.keymap), 2): if self.keymap[x]: self.move_camera( int((x - 1) / 2), True ) # why (x-1)/2 ? because we have to make the tow readable as a key number, like 0,1,2,3 return task.cont def dual_a( self, S, M ): #S is the "static object", the one that applies the force to the "moving" object M O = [] #This will be the list with the accelerations for an object d = sqrt((S[1] - M[1])**2 + (S[2] - M[2])**2 + (S[3] - M[3])**2) x = (self.u_constant * S[0] * (S[1] - M[1])) / d**2 y = (self.u_constant * S[0] * (S[2] - M[2])) / d**2 z = (self.u_constant * S[0] * (S[3] - M[3])) / d**2 O.append(x) O.append(y) O.append(z) return O def collision_log(self, entry, brakeforce): from_pos = [ self.bodies[n].filelist[0] for n in range(len(self.bodies)) ].index(entry.getFromNodePath().getParent()) into_pos = [ self.bodies[n].filelist[0] for n in range(len(self.bodies)) ].index(entry.getIntoNodePath().getParent() ) #find the nodepath in the list f_radius = sum(self.bodies[from_pos].scale) * self.u_radius / 3 i_radius = sum(self.bodies[into_pos].scale) * self.u_radius / 3 if max(f_radius, i_radius) == f_radius: inverted = True into_pos, from_pos = from_pos, into_pos else: inverted = False # currently unused brakeforce[from_pos] = self.bodies[ from_pos].brakeforce # get the force given in the data list # those are the two positions of the nodepaths, now we need to know which one is bigger, in order to obtain the fusion effect # from_pos is the smaller body, into_pos is the bigger one self.collision_gfx( self.momentum_transfer(from_pos, into_pos, entry, inverted), f_radius, i_radius) return brakeforce def momentum_transfer(self, f_pos, i_pos, entry, inverted): if self.debug: print("colliding") # debug, makes the game laggy interior = entry.getInteriorPoint(entry.getIntoNodePath()) # default surface = entry.getSurfacePoint(entry.getIntoNodePath()) print((interior - surface).length()) # debug if (interior - surface).length() >= 2 * sum( self.bodies[f_pos].scale) * self.u_radius / 3: if self.state[2] == self.collision_solids[f_pos].NodePath: self.state[1] = 'free' self.state[2] = None self.ctrav.remove_collider(self.collision_solids[f_pos].NodePath) self.bodies[f_pos].delete_body() self.bodies[i_pos].scale[0] *= ( self.bodies[i_pos].mass + self.bodies[f_pos].mass) / self.bodies[i_pos].mass self.bodies[i_pos].scale[1] *= ( self.bodies[i_pos].mass + self.bodies[f_pos].mass) / self.bodies[i_pos].mass self.bodies[i_pos].scale[2] *= ( self.bodies[i_pos].mass + self.bodies[f_pos].mass) / self.bodies[i_pos].mass self.bodies[i_pos].mass += self.bodies[f_pos].mass # scale updating () ''' temporarly removed for c in range(0,len(self.bodies[i_pos].filelist),2): self.bodies[i_pos].filelist[c].setScale(tuple(self.bodies[i_pos].scale)) ''' # deleting the destroyed planet's data self.bodies = self.bodies[:f_pos] + self.bodies[f_pos + 1:len(self.bodies)] self.collision_solids = self.collision_solids[:f_pos] + self.collision_solids[ f_pos + 1:len(self.collision_solids)] # just a quick test if self.debug: self.ctrav.showCollisions(render) if self.debug: print("planet destroyed") return interior, surface # used for the collision gfx calculations def printScene(self): #debug file = open("scenegraph.txt", "a") ls = LineStream() render.ls(ls) while ls.isTextAvailable(): file.write(ls.getLine()) file.write("\n") file.write("\n") file.write("END\n") file.write("\n") file.close() def Sound_Mngr(self, task): if self.sounds[self.current_playing].length() - self.sounds[ self.current_playing].getTime( ) == 0: #could have just used not() self.current_playing = random.choice( list(range(0, self.current_playing)) + list(range(self.current_playing + 1, len(self.sounds)))) self.sounds[self.current_playing].play() return task.cont def collision_gfx(self, points, Rf, Ri): # collision animation calculations # section size calculation # we know the depth of penetration (no silly jokes please), which allows us, knowing the radius of each body, # to calculate the radius of the section (I've got no idea how to say that in correct english) # the display of the particles all over this circle will be a piece of cake (at least I hope so) # see documents in the screenshot folder for more informations about the maths interior, surface = points[0], points[1] p = (interior - surface).length() p2 = (p**2 - 2 * Ri * p) / (2 * Ri - 2 * p - 2 * Rf) p1 = p - p2 # now we know everything about our impact section (the circle that defines the contact between the two bodies) # we just have to find the coord of the circle's center return 0 def create_crater(self): # see project for more informations return None def toggle_pause(self): temp = ['paused', 'running'] self.state[0] = temp[self.state[0] == temp[0]] # switches between paused and running self.iteration = 0 if self.state[0] == 'paused': self.handle_menu(self.iteration) else: self.filters.del_blur_sharpen() # make the mouse invisible self.hidden_mouse = True wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) # set the mouse pos to 0 self.center_mouse() self.win.requestProperties(wp) for u in self.menu_text: u.hide() return None def handle_menu(self, iteration): if not iteration: self.accept('escape', self.toggle_pause) self.draw_menu() # make the mouse visible self.hidden_mouse = False wp = WindowProperties() wp.setCursorHidden(self.hidden_mouse) self.win.requestProperties(wp) else: a = 1 # indentation (temporary) #put your mouse detection stuff here return None def draw_menu(self): self.filters.setBlurSharpen(amount=0) for u in self.menu_text: u.show() return None def show_credits(self): print( "created by l3alr0g (at least this part, I'll do something better at the end)" ) return None def system_break(self): # place your data saving routines here print("system exit successful, data saved") print("executing sys.exit()") print("out: done") sys.exit(0) return None def handle_scrolling( self, up): # up is a boolean: up=True means up, up=False means down if up and self.state[0] == 'running': if self.state[1] == 'linked': self.zoom_distance *= 0.95 else: self.camera_delta *= 1.1 elif not up and self.state[0] == 'running': if self.state[1] == 'linked': self.zoom_distance /= 0.95 else: self.camera_delta /= 1.1 return None def rotate_camera(self): self.camera.setHpr(tuple(self.cam_Hpr)) return None def move_camera( self, tow, pressed ): # tow stands for towards, pressed is a boolean which indicates the state of the key if pressed: self.keymap[2 * tow + 1] = 1 self.state[1] = 'free' #print('free mode on') self.state[2] = None else: self.keymap[2 * tow + 1] = 0 if self.keymap[2 * tow + 1]: phi, alpha, theta, delta, zoom = self.cam_Hpr[ 0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[ 2] * pi / 180, self.camera_delta, self.zoom_distance if self.keymap[2 * tow] == 'q': if self.state[1] == 'free': self.camera_pos = [ self.camera_pos[0] - cos(phi) * cos(theta) * delta, self.camera_pos[1] - sin(phi) * cos(theta) * delta, self.camera_pos[2] + sin(theta) * delta ] # moving the camera if self.keymap[2 * tow] == 'z': if self.state[1] == 'free': self.camera_pos = [ self.camera_pos[0] - sin(phi) * cos(alpha) * delta, self.camera_pos[1] + cos(phi) * cos(alpha) * delta, self.camera_pos[2] + sin(alpha) * delta ] if self.keymap[2 * tow] == 's': if self.state[1] == 'free': self.camera_pos = [ self.camera_pos[0] + sin(phi) * cos(alpha) * delta, self.camera_pos[1] - cos(phi) * cos(alpha) * delta, self.camera_pos[2] - sin(alpha) * delta ] if self.keymap[2 * tow] == 'd': if self.state[1] == 'free': self.camera_pos = [ self.camera_pos[0] + cos(phi) * cos(theta) * delta, self.camera_pos[1] + sin(phi) * cos(theta) * delta, self.camera_pos[2] - sin(theta) * delta ] if self.keymap[2 * tow] == 'a': self.cam_Hpr[2] -= 1 if self.keymap[2 * tow] == 'e': self.cam_Hpr[2] += 1 return None def mouse_check(self, task): # gets the mouse's coordinates mwn = self.mouseWatcherNode if mwn.hasMouse(): x, y = mwn.getMouseX(), mwn.getMouseY() #print(x,y) # debug # focus_point coordinates modifier code here: if self.state == ['running', 'free', None]: self.cam_Hpr[ 0] -= x * self.sensitivity_x # the - fixes a bug I can't solve self.cam_Hpr[ 1] += y * self.sensitivity_y # those formulas do not work when theta (self.cam_Hpr[2]) changes self.rotate_camera() self.center_mouse() elif self.state[0] == 'running' and self.state[1] == 'linked': self.cam_Hpr[0] -= x * self.sensitivity_x self.cam_Hpr[1] -= y * self.sensitivity_y self.rotate_camera() self.center_mouse() ''' if self.debug: print(self.cam_Hpr,self.camera_pos) # debug ''' return task.cont def center_mouse(self): self.win.movePointer( 0, int(self.win.getProperties().getXSize() / 2), int(self.win.getProperties().getYSize() / 2) ) # move mouse back to center --> careful ! this makes the delta calculation code buggy def handle_select(self, is_clicked): if is_clicked and self.watched != None: self.state[1] = 'linked' # toggle following mode self.state[2] = self.watched print('linked mode on, focusing: ', self.watched) #else: # do nothing actually return None def easter_egg(self): return "please be patient, our hens are working on it"
class Dockit(DirectObject): ''' Main class of the Dockit! game ''' # VdW radius scale constant radius_scale = 1.5 # Step to move ligand move_step = 50 # Step to rotate ligand rotate_step = 90 #Instructions instructions = ['Dock it!','', 'X: a/d','Y: w/s','Z: q/e', 'Heading: r/f','Pitch: t/g','Roll: h/y', 'Cartoon: c','Control: x','Center: z', 'Help: v','Exit: escape'] def __init__(self, width, height, pdb_file1, pdb_file2, full_atom=True, debug=False): ''' Build Dockit! ''' self.multisamples = base.win.getFbProperties().getMultisamples() self.cartoon = False self.structures = False self.help = False self.text_on_screen = [] self.width = width self.height = height # Keyboard events self.accept('c', self.toggle_cartoon) self.accept('x', self.toggle_control_structures) self.accept('z', self.center_camera) self.accept('v', self.help_on_screen) self.accept('escape', sys.exit) self.keyMap = {"a":0, "d":0, "w":0, "s":0, "q":0, "e":0, "r":0, "f":0, "g":0, "t":0, "h":0, "y":0} # Load PDB structures receptor = PDBReader.read_pdb_from_file(pdb_file1) ligand = PDBReader.read_pdb_from_file(pdb_file2) receptor.move_to_origin() ligand.move_to_origin() # Background base.setBackgroundColor(0.7, 0.7, 0.7, 1.0) # Load Color maps color_map_cpk = CPK() color_map_bfactor = BFactor(min_value=-14.0, max_value=8.0, middle_value=0.0) # Load 3D model self.receptor_node = render.attachNewNode("Receptor") if full_atom: self.load_protein_full_atom(receptor, self.receptor_node, color_map_bfactor) else: self.load_protein_residue(receptor, self.receptor_node) self.receptor_node.reparentTo(render) self.ligand_node = render.attachNewNode("Ligand") if full_atom: self.load_protein_full_atom(ligand, self.ligand_node, color_map_cpk) else: self.load_protein_residue(ligand, self.ligand_node) self.ligand_node.reparentTo(render) # Ambient lights self.alight = AmbientLight('alight') self.alight.setColor(LVecBase4f(0.162679, 0.162679, 0.169059, 1.0)) self.alnp = render.attachNewNode(self.alight) render.setLight(self.alnp) # Center receptor and ligand self.center_proteins() # Center camera on complexes self.center = loader.loadModel("models/atom_sphere") self.center.setColor(0.0, 0.53, 0.0, 1.0) self.center_camera() # DirectionalLight self.dlight = DirectionalLight('dlight') self.dlight.setColor(LVecBase4f(0.797448, 0.660287, 0.743222, 1.0)) self.dlight.setShadowCaster(True, 512, 512) render.setShaderAuto() self.dlnp = render.attachNewNode(self.dlight) self.dlnp.setPos(0,-50,0) render.setLight(self.dlnp) self.dlnp.lookAt(self.center) l1 = loader.loadModel("models/Dirlight") l1.setColor(1.0, 1.0, 0.0, 1.0) l1.setPos(0, -50, 0) l1.setScale(1) self.lights = [l1] # Post processing render.setAntialias(AntialiasAttrib.MAuto) # Show control structures if required if self.structures: for light in self.lights: light.reparentTo(render) self.center.reparentTo(render) # Movement functions taskMgr.add(self.ligand_movement, 'ligand_movement') # Key mapping self.key_bindings() # Show frame rate if debug: base.setFrameRateMeter(True) # Create two windows from cameras pointing to each molecule wx = base.win.getProperties().getXOrigin() wy = base.win.getProperties().getYOrigin() # Ligand window wp = WindowProperties() wp.setSize(300,300) wp.setOrigin(self.width + wx + 10, wy - 50) wp.setTitle('From Ligand') self.ligand_view = base.openWindow(props=wp) self.cam_ligand = base.camList[1] self.cam_ligand.setPos(self.center.getX(), self.center.getY(), self.center.getZ()) self.cam_ligand.lookAt(self.receptor_node) # Receptor window wp = WindowProperties() wp.setSize(300,300) wp.setOrigin(self.width + wx + 10, wy + 300) wp.setTitle('From Receptor') self.receptor_view = base.openWindow(props=wp) self.cam_receptor = base.camList[2] self.cam_receptor.setPos(self.center.getX(), self.center.getY(), self.center.getZ()) self.cam_receptor.lookAt(self.ligand_node) def key_bindings(self): ''' Define key bindings ''' base.accept('a', self.setKey, ["a",1]) base.accept('a-up', self.setKey, ["a",0]) base.accept('d', self.setKey, ["d",1]) base.accept('d-up', self.setKey, ["d",0]) base.accept('w', self.setKey, ["w",1]) base.accept('w-up', self.setKey, ["w",0]) base.accept('s', self.setKey, ["s",1]) base.accept('s-up', self.setKey, ["s",0]) base.accept('q', self.setKey, ["q",1]) base.accept('q-up', self.setKey, ["q",0]) base.accept('e', self.setKey, ["e",1]) base.accept('e-up', self.setKey, ["e",0]) base.accept('f', self.setKey, ["f",1]) base.accept('f-up', self.setKey, ["f",0]) base.accept('r', self.setKey, ["r",1]) base.accept('r-up', self.setKey, ["r",0]) base.accept('g', self.setKey, ["g",1]) base.accept('g-up', self.setKey, ["g",0]) base.accept('t', self.setKey, ["t",1]) base.accept('t-up', self.setKey, ["t",0]) base.accept('h', self.setKey, ["h",1]) base.accept('h-up', self.setKey, ["h",0]) base.accept('y', self.setKey, ["y",1]) base.accept('y-up', self.setKey, ["y",0]) def ligand_movement(self, task): ''' Compute ligand movement depending on key bindings ''' if (self.keyMap["a"]!=0): self.ligand_node.setX(self.ligand_node.getX()-Dockit.move_step * globalClock.getDt()) if (self.keyMap["d"]!=0): self.ligand_node.setX(self.ligand_node.getX()+Dockit.move_step * globalClock.getDt()) if (self.keyMap["q"]!=0): self.ligand_node.setY(self.ligand_node.getY()-Dockit.move_step * globalClock.getDt()) if (self.keyMap["e"]!=0): self.ligand_node.setY(self.ligand_node.getY()+Dockit.move_step * globalClock.getDt()) if (self.keyMap["w"]!=0): self.ligand_node.setZ(self.ligand_node.getZ()-Dockit.move_step * globalClock.getDt()) if (self.keyMap["s"]!=0): self.ligand_node.setZ(self.ligand_node.getZ()+Dockit.move_step * globalClock.getDt()) if (self.keyMap["r"]!=0): self.ligand_node.setH(self.ligand_node.getH()-Dockit.rotate_step * globalClock.getDt()) if (self.keyMap["f"]!=0): self.ligand_node.setH(self.ligand_node.getH()+Dockit.rotate_step * globalClock.getDt()) if (self.keyMap["g"]!=0): self.ligand_node.setP(self.ligand_node.getP()-Dockit.rotate_step * globalClock.getDt()) if (self.keyMap["t"]!=0): self.ligand_node.setP(self.ligand_node.getP()+Dockit.rotate_step * globalClock.getDt()) if (self.keyMap["h"]!=0): self.ligand_node.setR(self.ligand_node.getR()-Dockit.rotate_step * globalClock.getDt()) if (self.keyMap["y"]!=0): self.ligand_node.setR(self.ligand_node.getR()+Dockit.rotate_step * globalClock.getDt()) return task.cont def setKey(self, key, value): ''' Record the state of the arrow keys ''' self.keyMap[key] = value def load_protein_full_atom(self, protein, node, color_map): ''' Display a given protein using spheres models for each atom ''' atoms = protein.get_atoms() self.atom_objects = [] for atom in atoms: a = loader.loadModel("models/atom_sphere") a.setPos(atom.get_x(), atom.get_y(), atom.get_z()) a.setScale(atom.get_radius() / Dockit.radius_scale) self.apply_color(a, atom, color_map) a.reparentTo(node) node.flattenStrong() def load_protein_residue(self, protein, node): ''' Display a given protein using spheres models for each atom ''' residues = protein.get_residues() self.residues_objects = [] for residue in residues: r = loader.loadModel("models/atom_sphere") x,y,z = residue.get_center_coordinates() r.setPos(x, y, z) r.setScale(4.0 / Dockit.radius_scale) r.setColor(LVecBase4f(1.0, 0.59, 0.0, 1.0)) r.setColorScale(LVecBase4f(1, 1, 1, 1)) r.reparentTo(node) node.flattenStrong() def apply_color(self, a3d, atom, color_map): ''' Apply a color map to the element ''' if isinstance(color_map, BFactor): color = color_map.get_color_by_bfactor(atom.get_b_factor()) else: color = color_map.get_color_by_element(atom.get_element()) red, green, blue, alpha = color.get_rgba() a3d.setColor(LVecBase4f(red, green, blue, alpha)) a3d.setColorScale(LVecBase4f(1, 1, 1, 1)) def toggle_cartoon(self): ''' Use Cartoon ink filter ''' self.cartoon = not self.cartoon if self.cartoon: tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.4, 0.6)) tempnode.setShaderAuto() base.cam.node().setInitialState(tempnode.getState()) self.separation = 1.3 # Pixels self.filters = CommonFilters(base.win, base.cam) self.filters.setCartoonInk(separation=self.separation) # Currently using MAuto antialias, uncomment to use different #render.setAntialias(AntialiasAttrib.MBetter) #self.filters.finalQuad.setAntialias(AntialiasAttrib.MBetter) else: self.filters.cleanup() base.cam.node().setInitialState(self.alight.getState()) def toggle_control_structures(self): ''' Show control structures as lights and center nodes and bounds if required ''' self.structures = not self.structures for light in self.lights: if not self.structures: light.detachNode() else: light.reparentTo(render) if self.structures: self.receptor_node.showBounds() self.ligand_node.showBounds() self.center.reparentTo(render) print "Number of multisamples available: %d" % self.multisamples else: self.receptor_node.hideBounds() self.ligand_node.hideBounds() self.center.detachNode() def center_proteins(self): ''' Move receptor and ligand to a centered position depending on its size ''' # Center the receptor receptor_radius = self.receptor_node.getBounds().getRadius() receptor_center = self.receptor_node.getBounds().getCenter() self.receptor_node.setPos(0,receptor_radius,0) # Center the ligand ligand_radius = self.ligand_node.getBounds().getRadius() self.ligand_node.setPos(receptor_center[0]-receptor_radius-ligand_radius, receptor_center[1], receptor_center[2]) self.ligand_node.lookAt(self.receptor_node) def center_camera(self): ''' Center camera on scene ''' xc, yc, zc = (self.receptor_node.getBounds().getCenter() + self.ligand_node.getBounds().getCenter()) / 2.0 self.center.setPos(xc, yc, zc) ligand_radius = self.ligand_node.getBounds().getRadius() receptor_radius = self.receptor_node.getBounds().getRadius() scene_center = self.center.getPos() base.cam.setPos(scene_center[0], -10-scene_center[1]-2*ligand_radius-2*receptor_radius, scene_center[2]) base.cam.lookAt(self.center) def help_on_screen(self): ''' Show commands on screen ''' self.help = not self.help if self.help: i = 0 for instruction in Dockit.instructions: self.text_on_screen.append(self.__gen_label_text(instruction,i)) i += 1 else: for text in self.text_on_screen: text.destroy() del self.text_on_screen[:] def __gen_label_text(self, text, i): ''' Auxiliar function to display text line by line ''' return OnscreenText(text = text, pos = (-1.6, .9-.06*i), fg=(1,1,1,1), align = TextNode.ALeft, scale=0.06, mayChange = 0)