def toggleToonShading(self, checked):
		filters = CommonFilters(base.win, base.cam)
		
		if checked == True:
			filters.setCartoonInk()
		else:
			filters.delCartoonInk()
예제 #2
0
    def toggleToonShading(self, checked):
        filters = CommonFilters(base.win, base.cam)

        if checked == True:
            filters.setCartoonInk()
        else:
            filters.delCartoonInk()
예제 #3
0
class filters():
    def __init__(self):

        self.filters = CommonFilters(base.win, base.cam)

        self.initCartoonInk()

    def initCartoonInk(self):

        self.filters.setCartoonInk(separation=-1)
예제 #4
0
    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()
예제 #5
0
    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")
예제 #6
0
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)
예제 #9
0
파일: pandactrl.py 프로젝트: xwavex/pyhiro
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)
예제 #10
0
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())
예제 #11
0
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
예제 #12
0
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
예제 #13
0
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)
예제 #14
0
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)
예제 #15
0
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
예제 #16
0
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))
예제 #17
0
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))
예제 #18
0
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)
예제 #19
0
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)
예제 #21
0
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"
예제 #22
0
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)