Example #1
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"
Example #2
0
    def __init__(self):
        load_prc_file_data(
            "", """
            win-size 1920 1080
            window-title Panda3D Arena Sample FPS Bullet Auto Colliders PBR HW Skinning
            show-frame-rate-meter #t
            framebuffer-srgb #t
            framebuffer-multisample 1
            multisamples 4
            view-frustum-cull 0
            textures-power-2 none
            hardware-animated-vertices #t
            gl-depth-zero-to-one true
            clock-frame-rate 60
            interpolate-frames 1
            cursor-hidden #t
            fullscreen #f
        """)

        # Initialize the showbase
        super().__init__()
        gltf.patch_loader(self.loader)

        props = WindowProperties()
        props.set_mouse_mode(WindowProperties.M_relative)
        base.win.request_properties(props)
        base.set_background_color(0.5, 0.5, 0.8)

        self.camLens.set_fov(80)
        self.camLens.set_near_far(0.01, 90000)
        self.camLens.set_focal_length(7)
        # self.camera.set_pos(0, 0, 2)

        # ConfigVariableManager.getGlobalPtr().listVariables()

        # point light generator
        for x in range(0, 3):
            plight_1 = PointLight('plight')
            # add plight props here
            plight_1_node = self.render.attach_new_node(plight_1)
            # group the lights close to each other to create a sun effect
            plight_1_node.set_pos(random.uniform(-21, -20),
                                  random.uniform(-21, -20),
                                  random.uniform(20, 21))
            self.render.set_light(plight_1_node)

        # point light for volumetric lighting filter
        plight_1 = PointLight('plight')
        # add plight props here
        plight_1_node = self.render.attach_new_node(plight_1)
        # group the lights close to each other to create a sun effect
        plight_1_node.set_pos(random.uniform(-21, -20),
                              random.uniform(-21, -20), random.uniform(20, 21))
        self.render.set_light(plight_1_node)

        scene_filters = CommonFilters(base.win, base.cam)
        scene_filters.set_bloom()
        scene_filters.set_high_dynamic_range()
        scene_filters.set_exposure_adjust(0.6)
        scene_filters.set_gamma_adjust(1.1)
        # scene_filters.set_volumetric_lighting(plight_1_node, 32, 0.5, 0.7, 0.1)
        # scene_filters.set_blur_sharpen(0.9)
        # scene_filters.set_ambient_occlusion(32, 0.05, 2.0, 0.01, 0.000002)

        self.accept("f3", self.toggle_wireframe)
        self.accept("escape", sys.exit, [0])

        exponential_fog = Fog('world_fog')
        exponential_fog.set_color(0.6, 0.7, 0.7)
        # this is a very low fog value, set it higher for a greater effect
        exponential_fog.set_exp_density(0.00009)
        self.render.set_fog(exponential_fog)

        self.game_start = 0

        from panda3d.bullet import BulletWorld
        from panda3d.bullet import BulletCharacterControllerNode
        from panda3d.bullet import ZUp
        from panda3d.bullet import BulletCapsuleShape
        from panda3d.bullet import BulletTriangleMesh
        from panda3d.bullet import BulletTriangleMeshShape
        from panda3d.bullet import BulletBoxShape
        from panda3d.bullet import BulletGhostNode
        from panda3d.bullet import BulletRigidBodyNode
        from panda3d.bullet import BulletPlaneShape

        self.world = BulletWorld()
        self.world.set_gravity(Vec3(0, 0, -9.81))

        arena_1 = self.loader.load_model('models/arena_1.gltf')
        arena_1.reparent_to(self.render)
        arena_1.set_pos(0, 0, 0)

        def make_collision_from_model(input_model, node_number, mass, world,
                                      target_pos, h_adj):
            # tristrip generation from static models
            # generic tri-strip collision generator begins
            geom_nodes = input_model.find_all_matches('**/+GeomNode')
            geom_nodes = geom_nodes.get_path(node_number).node()
            # print(geom_nodes)
            geom_target = geom_nodes.get_geom(0)
            # print(geom_target)
            output_bullet_mesh = BulletTriangleMesh()
            output_bullet_mesh.add_geom(geom_target)
            tri_shape = BulletTriangleMeshShape(output_bullet_mesh,
                                                dynamic=False)
            print(output_bullet_mesh)

            body = BulletRigidBodyNode('input_model_tri_mesh')
            np = self.render.attach_new_node(body)
            np.node().add_shape(tri_shape)
            np.node().set_mass(mass)
            np.node().set_friction(0.01)
            np.set_pos(target_pos)
            np.set_scale(1)
            np.set_h(h_adj)
            # np.set_p(180)
            # np.set_r(180)
            np.set_collide_mask(BitMask32.allOn())
            world.attach_rigid_body(np.node())

        make_collision_from_model(arena_1, 0, 0, self.world,
                                  (arena_1.get_pos()), 0)

        # load the scene shader
        scene_shader = Shader.load(Shader.SL_GLSL,
                                   "shaders/simplepbr_vert_mod_1.vert",
                                   "shaders/simplepbr_frag_mod_1.frag")
        self.render.set_shader(scene_shader)
        self.render.set_antialias(AntialiasAttrib.MMultisample)
        scene_shader_attrib = ShaderAttrib.make(scene_shader)
        scene_shader_attrib = scene_shader_attrib.setFlag(
            ShaderAttrib.F_hardware_skinning, True)

        # initialize player character physics the Bullet way
        shape_1 = BulletCapsuleShape(0.75, 0.5, ZUp)
        player_node = BulletCharacterControllerNode(
            shape_1, 0.1, 'Player')  # (shape, mass, player name)
        # player_node.set_max_slope(0.1)
        # player_node.set_linear_movement(1, True)
        player_np = self.render.attach_new_node(player_node)
        player_np.set_pos(-20, -10, 10)
        player_np.set_collide_mask(BitMask32.allOn())
        self.world.attach_character(player_np.node())
        # cast player_np to self.player
        self.player = player_np

        # reparent player character to render node
        fp_character = actor_data.player_character
        fp_character.reparent_to(self.render)
        fp_character.set_scale(1)
        # set the actor skinning hardware shader
        fp_character.set_attrib(scene_shader_attrib)

        self.camera.reparent_to(self.player)
        # reparent character to FPS cam
        fp_character.reparent_to(self.player)
        fp_character.set_pos(0, 0, -0.95)
        # self.camera.set_x(self.player, 1)
        self.camera.set_y(self.player, 0.03)
        self.camera.set_z(self.player, 0.5)

        # player gun begins
        self.player_gun = actor_data.arm_handgun
        self.player_gun.reparent_to(self.render)
        self.player_gun.reparent_to(self.camera)
        self.player_gun.set_x(self.camera, 0.1)
        self.player_gun.set_y(self.camera, 0.4)
        self.player_gun.set_z(self.camera, -0.1)

        # directly make a text node to display text
        text_1 = TextNode('text_1_node')
        text_1.set_text("")
        text_1_node = self.aspect2d.attach_new_node(text_1)
        text_1_node.set_scale(0.05)
        text_1_node.set_pos(-1.4, 0, 0.92)
        # import font and set pixels per unit font quality
        nunito_font = loader.load_font('fonts/Nunito/Nunito-Light.ttf')
        nunito_font.set_pixels_per_unit(100)
        nunito_font.set_page_size(512, 512)
        # apply font
        text_1.set_font(nunito_font)
        # small caps
        # text_1.set_small_caps(True)

        # on-screen target dot for aiming
        target_dot = TextNode('target_dot_node')
        target_dot.set_text(".")
        target_dot_node = self.aspect2d.attach_new_node(target_dot)
        target_dot_node.set_scale(0.075)
        target_dot_node.set_pos(0, 0, 0)
        # target_dot_node.hide()
        # apply font
        target_dot.set_font(nunito_font)
        target_dot.set_align(TextNode.ACenter)
        # see the Task section for relevant dot update logic

        # directly make a text node to display text
        text_2 = TextNode('text_2_node')
        text_2.set_text("Neutralize the NPC by shooting the head." + '\n' +
                        "Press 'f' to toggle the flashlight.")
        text_2_node = self.aspect2d.attach_new_node(text_2)
        text_2_node.set_scale(0.04)
        text_2_node.set_pos(-1.4, 0, 0.8)
        # import font and set pixels per unit font quality
        nunito_font = self.loader.load_font('fonts/Nunito/Nunito-Light.ttf')
        nunito_font.set_pixels_per_unit(100)
        nunito_font.set_page_size(512, 512)
        # apply font
        text_2.set_font(nunito_font)
        text_2.set_text_color(0, 0.3, 1, 1)

        # print player position on mouse click
        def print_player_pos():
            print(self.player.get_pos())
            self.player.node().do_jump()

        self.accept('mouse3', print_player_pos)

        self.flashlight_state = 0

        def toggle_flashlight():
            current_flashlight = self.render.find_all_matches("**/flashlight")

            if self.flashlight_state == 0:
                if len(current_flashlight) == 0:
                    self.slight = 0
                    self.slight = Spotlight('flashlight')
                    self.slight.set_shadow_caster(True, 1024, 1024)
                    self.slight.set_color(VBase4(0.5, 0.6, 0.6,
                                                 1))  # slightly bluish
                    lens = PerspectiveLens()
                    lens.set_near_far(0.5, 5000)
                    self.slight.set_lens(lens)
                    self.slight.set_attenuation((0.5, 0, 0.0000005))
                    self.slight = self.render.attach_new_node(self.slight)
                    self.slight.set_pos(-0.1, 0.3, -0.4)
                    self.slight.reparent_to(self.camera)
                    self.flashlight_state = 1
                    self.render.set_light(self.slight)

                elif len(current_flashlight) > 0:
                    self.render.set_light(self.slight)
                    self.flashlight_state = 1

            elif self.flashlight_state > 0:
                self.render.set_light_off(self.slight)
                self.flashlight_state = 0

        self.accept('f', toggle_flashlight)

        # add a few random physics boxes
        for x in range(0, 40):
            # dynamic collision
            random_vec = Vec3(1, 1, 1)
            special_shape = BulletBoxShape(random_vec)
            # rigidbody
            body = BulletRigidBodyNode('random_prisms')
            d_coll = self.render.attach_new_node(body)
            d_coll.node().add_shape(special_shape)
            d_coll.node().set_mass(0.9)
            d_coll.node().set_friction(0.5)
            d_coll.set_collide_mask(BitMask32.allOn())
            # turn on Continuous Collision Detection
            d_coll.node().set_ccd_motion_threshold(0.000000007)
            d_coll.node().set_ccd_swept_sphere_radius(0.30)
            d_coll.node().set_deactivation_enabled(False)
            d_coll.set_pos(random.uniform(-60, -20), random.uniform(-60, -20),
                           random.uniform(5, 10))
            box_model = self.loader.load_model('models/1m_cube.gltf')
            box_model.reparent_to(self.render)
            box_model.reparent_to(d_coll)
            box_model.set_color(random.uniform(0, 1), random.uniform(0, 1),
                                random.uniform(0, 1), 1)
            self.world.attach_rigid_body(d_coll.node())

        # portal #1 begins
        # make a new texture buffer, render node, and attach a camera
        mirror_buffer = self.win.make_texture_buffer("mirror_buff", 4096, 4096)
        mirror_render = NodePath("mirror_render")
        mirror_render.set_shader(scene_shader)
        self.mirror_cam = self.make_camera(mirror_buffer)
        self.mirror_cam.reparent_to(mirror_render)
        self.mirror_cam.set_pos(0, -60, 5)
        self.mirror_cam.set_hpr(0, 25, 0)
        self.mirror_cam.node().get_lens().set_focal_length(10)
        self.mirror_cam.node().get_lens().set_fov(90)

        mirror_filters = CommonFilters(mirror_buffer, self.mirror_cam)
        # mirror_filters.set_high_dynamic_range()
        mirror_filters.set_exposure_adjust(1.1)
        # mirror_filters.set_gamma_adjust(1.3)

        # load in a mirror/display object model in normal render space
        self.mirror_model = self.loader.loadModel(
            'models/wide_screen_video_display.egg')
        self.mirror_model.reparent_to(self.render)
        self.mirror_model.set_pos(-20, 0, 1)
        self.mirror_model.set_sz(3)
        # self.mirror_model.flatten_strong()

        # mirror scene model load-in
        # reparent to mirror render node
        house_uv = self.loader.load_model('models/hangar_1.gltf')
        house_uv.reparent_to(mirror_render)
        windows = house_uv.find('**/clear_arches')
        windows.hide()
        house_uv.set_pos(0, 0, 0)
        house_uv.set_scale(1)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(mirror_render)
        house_uv.set_h(180)
        house_uv.set_scale(1.5)
        house_uv.set_pos(0, -50, 0)

        # mirror scene lighting
        # point light generator
        for x in range(0, 1):
            plight_1 = PointLight('plight')
            # add plight props here
            plight_1_node = mirror_render.attach_new_node(plight_1)
            # group the lights close to each other to create a sun effect
            plight_1_node.set_pos(random.uniform(-21, -20),
                                  random.uniform(-21, -20),
                                  random.uniform(20, 21))
            mirror_render.set_light(plight_1_node)

        # set the live buffer texture to the mirror/display model in normal render space
        self.mirror_model.set_texture(mirror_buffer.get_texture())

        # secret hangar far off somewhere on the graph
        house_uv = self.loader.load_model('models/hangar_1.gltf')
        house_uv.reparent_to(self.render)
        windows = house_uv.find('**/clear_arches')
        windows.hide()
        house_uv.set_pos(400, 400, -1)

        # the portal ring
        house_uv = self.loader.load_model('models/ring_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(90)
        house_uv.set_pos(-20, 0, -2)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(0)
        house_uv.set_pos(-20, -5.5, 0)
        r_pos = house_uv.get_pos()
        make_collision_from_model(house_uv, 0, 0, self.world,
                                  (r_pos[0], r_pos[1], r_pos[2] + 1), 0)

        self.count_frames_1 = 0
        self.screen_cap_num = 1

        def make_screenshot():
            # Ensure the frame is rendered.
            base.graphicsEngine.render_frame()

            # Grab the screenshot into a big image
            full = PNMImage()
            base.win.get_screenshot(full)

            # Now reduce it
            reduced = PNMImage(500, 300)
            reduced.gaussianFilterFrom(1, full)

            # And write it out.
            reduced.write(
                Filename('screen_cap_' + str(self.screen_cap_num) + '.jpg'))

            self.screen_cap_num += 1

        def update_portal_cam(Task):
            if self.count_frames_1 < 30:
                self.count_frames_1 += 1

            if self.count_frames_1 == 15:
                pass
                # make_screenshot()

            if self.count_frames_1 == 29:
                self.count_frames_1 = 0

            p_dist = (self.player.get_pos() -
                      self.mirror_model.get_pos(base.render)).length()
            target_fov = 115

            if p_dist < 2.25:
                target_fov = 145
                self.player.set_pos(400, 400, 3)
                # adjust the ground plane
                self.ground_plane.set_pos(0, 0, 0)
                # move the normal point lights
                lights = self.render.find_all_matches('**/plight*')
                for l in lights:
                    l.set_pos(400, 400, 21)

            player_h = self.player.get_h()
            self.mirror_cam.set_h(player_h)
            self.mirror_cam.node().get_lens().set_fov(target_fov)

            return Task.cont

        self.task_mgr.add(update_portal_cam)

        # portal #2 begins
        # the portal ring
        house_uv = self.loader.load_model('models/ring_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(90)
        house_uv.set_pos(400, 400, -2)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(180)
        house_uv.set_pos(400, 405.5, 0)
        r_pos = house_uv.get_pos()
        make_collision_from_model(house_uv, 0, 0, self.world,
                                  (r_pos[0], r_pos[1], r_pos[2] + 1), 180)

        # NPC_1 load-in
        comp_shape_1 = BulletCapsuleShape(0.05, 0.01, ZUp)
        npc_1_node = BulletCharacterControllerNode(
            comp_shape_1, 0.2, 'NPC_A_node')  # (shape, mass, character name)
        np = self.render.attach_new_node(npc_1_node)
        np.set_pos(-40, -40, 5)
        np.set_collide_mask(BitMask32.allOn())
        self.world.attach_character(np.node())
        np.set_h(random.randint(0, 180))
        npc_model_1 = actor_data.NPC_1
        npc_model_1.reparent_to(np)
        # set the actor skinning hardware shader
        npc_model_1.set_attrib(scene_shader_attrib)
        # get the separate head model
        npc_1_head = self.loader.load_model('models/npc_1_head.gltf')
        npc_1_head.reparent_to(actor_data.NPC_1.get_parent())

        # npc base animation loop
        npc_1_control = actor_data.NPC_1.get_anim_control('walking')
        if not npc_1_control.is_playing():
            actor_data.NPC_1.stop()
            actor_data.NPC_1.loop("walking", fromFrame=60, toFrame=180)
            actor_data.NPC_1.set_play_rate(6.0, 'walking')

        # create special hit areas
        # use Task section for npc collision movement logic
        # special head node size
        special_shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
        # ghost npc node
        body = BulletGhostNode('special_node_A')
        special_node = self.render.attach_new_node(body)
        special_node.node().add_shape(
            special_shape, TransformState.makePos(Point3(0, 0, 0.4)))
        # special_node.node().set_mass(0)
        # special_node.node().set_friction(0.5)
        special_node.set_collide_mask(BitMask32(0x0f))
        # turn on Continuous Collision Detection
        special_node.node().set_deactivation_enabled(False)
        special_node.node().set_ccd_motion_threshold(0.000000007)
        special_node.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_ghost(special_node.node())

        # dynamic collision
        special_shape = BulletBoxShape(Vec3(0.3, 0.15, 0.6))
        # rigidbody npc node
        body = BulletRigidBodyNode('d_coll_A')
        d_coll = self.render.attach_new_node(body)
        d_coll.node().add_shape(special_shape,
                                TransformState.makePos(Point3(0, 0, 0.7)))
        # d_coll.node().set_mass(0)
        d_coll.node().set_friction(0.5)
        d_coll.set_collide_mask(BitMask32.allOn())
        # turn on Continuous Collision Detection
        d_coll.node().set_deactivation_enabled(False)
        d_coll.node().set_ccd_motion_threshold(0.000000007)
        d_coll.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_rigid_body(d_coll.node())

        # npc state variables
        self.npc_1_is_dead = False
        self.npc_1_move_increment = Vec3(0, 0, 0)
        self.gun_anim_is_playing = False

        # npc movement timer
        def npc_1_move_gen():
            while not self.npc_1_is_dead:
                m_incs = []
                for x in range(0, 2):
                    m_incs.append(random.uniform(2, 5))

                print('NPC_1 movement increments this cycle: ' + str(m_incs))
                self.npc_1_move_increment[0] = m_incs[0]
                self.npc_1_move_increment[1] = m_incs[1]
                time.sleep(3)
                self.npc_1_move_increment[0] = m_incs[0]
                self.npc_1_move_increment[1] = m_incs[1]
                time.sleep(3)
                self.npc_1_move_increment[0] = (-1 * m_incs[0]) * 2
                self.npc_1_move_increment[1] = (-1 * m_incs[1]) * 2
                time.sleep(3)
                self.npc_1_move_increment[0] = 0
                self.npc_1_move_increment[1] = 0

        # activate the movement timer in a dedicated thread to prevent lockup with .sleep()
        threading2._start_new_thread(npc_1_move_gen, ())

        def is_npc_1_shot():
            # animate the gun
            gun_ctrl = actor_data.arm_handgun.get_anim_control('shoot')
            if not gun_ctrl.is_playing():
                actor_data.arm_handgun.stop()
                actor_data.arm_handgun.play("shoot")
                actor_data.arm_handgun.set_play_rate(15.0, 'shoot')

            # target dot ray test
            # get mouse data
            mouse_watch = base.mouseWatcherNode
            if mouse_watch.has_mouse():
                posMouse = base.mouseWatcherNode.get_mouse()
                posFrom = Point3()
                posTo = Point3()
                base.camLens.extrude(posMouse, posFrom, posTo)
                posFrom = self.render.get_relative_point(base.cam, posFrom)
                posTo = self.render.get_relative_point(base.cam, posTo)
                rayTest = self.world.ray_test_closest(posFrom, posTo)
                target = rayTest.get_node()
                target_dot = self.aspect2d.find_all_matches(
                    "**/target_dot_node")

                if 'special_node_A' in str(target):

                    def npc_cleanup():
                        # the head is hit, the npc is dead
                        self.npc_1_is_dead = True
                        text_2.set_text('Congrats, you have won!')
                        npc_1_control = actor_data.NPC_1.get_anim_control(
                            'walking')
                        if npc_1_control.is_playing():
                            actor_data.NPC_1.stop()
                        npc_1_control = actor_data.NPC_1.get_anim_control(
                            'death')
                        if not npc_1_control.is_playing():
                            actor_data.NPC_1.play('death')

                        # Bullet node removals
                        self.world.remove(target)
                        rigid_target = self.render.find('**/d_coll_A')
                        self.world.remove(rigid_target.node())

                    threading2._start_new_thread(npc_cleanup, ())

        self.accept('mouse1', is_npc_1_shot)

        def smooth_load_physics():
            # this is a patch to speed up the cold start hitch on success condition
            # Bullet node removals
            self.world.remove(special_node.node())
            rigid_target = self.render.find('**/d_coll_A')
            self.world.remove(rigid_target.node())
            print('NPC physics init removed.')

        smooth_load_physics()

        # repeat the NPC physics initialization after smooth_load_physics
        # create special hit areas
        # use Task section for npc collision movement logic
        # special head node size
        special_shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
        # ghost npc node
        body = BulletGhostNode('special_node_A')
        special_node = self.render.attach_new_node(body)
        special_node.node().add_shape(
            special_shape, TransformState.makePos(Point3(0, 0, 0.4)))
        # special_node.node().set_mass(0)
        # special_node.node().set_friction(0.5)
        special_node.set_collide_mask(BitMask32(0x0f))
        # turn on Continuous Collision Detection
        special_node.node().set_deactivation_enabled(False)
        special_node.node().set_ccd_motion_threshold(0.000000007)
        special_node.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_ghost(special_node.node())

        # dynamic collision
        special_shape = BulletBoxShape(Vec3(0.3, 0.15, 0.6))
        # rigidbody npc node
        body = BulletRigidBodyNode('d_coll_A')
        d_coll = self.render.attach_new_node(body)
        d_coll.node().add_shape(special_shape,
                                TransformState.makePos(Point3(0, 0, 0.7)))

        # d_coll.node().set_mass(0)
        d_coll.node().set_friction(0.5)
        d_coll.set_collide_mask(BitMask32.allOn())
        # turn on Continuous Collision Detection
        d_coll.node().set_deactivation_enabled(False)
        d_coll.node().set_ccd_motion_threshold(0.000000007)
        d_coll.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_rigid_body(d_coll.node())

        # 3D player movement system begins
        self.keyMap = {
            "left": 0,
            "right": 0,
            "forward": 0,
            "backward": 0,
            "run": 0,
            "jump": 0
        }

        def setKey(key, value):
            self.keyMap[key] = value

        # define button map
        self.accept("a", setKey, ["left", 1])
        self.accept("a-up", setKey, ["left", 0])
        self.accept("d", setKey, ["right", 1])
        self.accept("d-up", setKey, ["right", 0])
        self.accept("w", setKey, ["forward", 1])
        self.accept("w-up", setKey, ["forward", 0])
        self.accept("s", setKey, ["backward", 1])
        self.accept("s-up", setKey, ["backward", 0])
        self.accept("shift", setKey, ["run", 1])
        self.accept("shift-up", setKey, ["run", 0])
        self.accept("space", setKey, ["jump", 1])
        self.accept("space-up", setKey, ["jump", 0])
        # disable mouse
        self.disable_mouse()

        # the player movement speed
        self.movementSpeedForward = 5
        self.movementSpeedBackward = 5
        self.dropSpeed = -0.2
        self.striveSpeed = 6
        self.ease = -10.0
        self.static_pos_bool = False
        self.static_pos = Vec3()

        def move(Task):
            if self.game_start > 0:
                if not self.npc_1_is_dead:
                    npc_pos_1 = actor_data.NPC_1.get_parent().get_pos()
                    # place head hit box
                    special_node.set_pos(npc_pos_1[0], npc_pos_1[1],
                                         npc_pos_1[2] + 1)
                    special_node.set_h(actor_data.NPC_1.get_h())
                    # dynamic collision node
                    d_coll.set_pos(npc_pos_1[0], npc_pos_1[1], npc_pos_1[2])
                    d_coll.set_h(actor_data.NPC_1.get_h())
                    # make the npc look at the player continuously
                    actor_data.NPC_1.look_at(self.player)
                    npc_1_head.look_at(self.player)

                    if actor_data.NPC_1.get_p() > 3:
                        actor_data.NPC_1.set_p(3)

                    if npc_1_head.get_p() > 3:
                        npc_1_head.set_p(3)

                    m_inst = self.npc_1_move_increment
                    t_inst = globalClock.get_dt()
                    actor_data.NPC_1.get_parent().set_pos(
                        npc_pos_1[0] + (m_inst[0] * t_inst),
                        npc_pos_1[1] + (m_inst[1] * t_inst), npc_pos_1[2])

                if self.npc_1_is_dead:
                    npc_1_head.hide()
                    inst_h = actor_data.NPC_1.get_h()
                    inst_p = actor_data.NPC_1.get_p()
                    actor_data.NPC_1.set_hpr(inst_h, inst_p, 0)

                # target dot ray test
                # turns the target dot red
                # get mouse data
                mouse_watch = base.mouseWatcherNode
                if mouse_watch.has_mouse():
                    posMouse = base.mouseWatcherNode.get_mouse()
                    posFrom = Point3()
                    posTo = Point3()
                    base.camLens.extrude(posMouse, posFrom, posTo)
                    posFrom = self.render.get_relative_point(base.cam, posFrom)
                    posTo = self.render.get_relative_point(base.cam, posTo)
                    rayTest = self.world.ray_test_closest(posFrom, posTo)
                    target = rayTest.get_node()
                    target_dot = self.aspect2d.find_all_matches(
                        "**/target_dot_node")

                    if 'special_node_A' in str(target):
                        # the npc is recognized, make the dot red
                        for dot in target_dot:
                            dot.node().set_text_color(0.9, 0.1, 0.1, 1)

                    if 'd_coll_A' in str(target):
                        # the npc is recognized, make the dot red
                        for dot in target_dot:
                            dot.node().set_text_color(0.9, 0.1, 0.1, 1)

                    if 'special_node_A' not in str(target):
                        # no npc recognized, make the dot white
                        if 'd_coll_A' not in str(target):
                            for dot in target_dot:
                                dot.node().set_text_color(1, 1, 1, 1)

                # get mouse data
                mouse_watch = base.mouseWatcherNode
                if mouse_watch.has_mouse():
                    pointer = base.win.get_pointer(0)
                    mouseX = pointer.get_x()
                    mouseY = pointer.get_y()

                # screen sizes
                window_Xcoord_halved = base.win.get_x_size() // 2
                window_Ycoord_halved = base.win.get_y_size() // 2
                # mouse speed
                mouseSpeedX = 0.2
                mouseSpeedY = 0.2
                # maximum and minimum pitch
                maxPitch = 90
                minPitch = -50
                # cam view target initialization
                camViewTarget = LVecBase3f()

                if base.win.movePointer(0, window_Xcoord_halved,
                                        window_Ycoord_halved):
                    p = 0

                    if mouse_watch.has_mouse():
                        # calculate the pitch of camera
                        p = self.camera.get_p() - (
                            mouseY - window_Ycoord_halved) * mouseSpeedY

                    # sanity checking
                    if p < minPitch:
                        p = minPitch
                    elif p > maxPitch:
                        p = maxPitch

                    if mouse_watch.has_mouse():
                        # directly set the camera pitch
                        self.camera.set_p(p)
                        camViewTarget.set_y(p)

                    # rotate the self.player's heading according to the mouse x-axis movement
                    if mouse_watch.has_mouse():
                        h = self.player.get_h() - (
                            mouseX - window_Xcoord_halved) * mouseSpeedX

                    if mouse_watch.has_mouse():
                        # sanity checking
                        if h < -360:
                            h += 360

                        elif h > 360:
                            h -= 360

                        self.player.set_h(h)
                        camViewTarget.set_x(h)

                    # hide the gun if looking straight down
                    if p < -30:
                        self.player_gun.hide()
                    if p > -30:
                        self.player_gun.show()

                if self.keyMap["left"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_x(self.player,
                                      -self.striveSpeed * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if not self.keyMap["left"]:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["right"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_x(self.player,
                                      self.striveSpeed * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if not self.keyMap["right"]:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["forward"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_y(
                        self.player,
                        self.movementSpeedForward * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if self.keyMap["forward"] != 1:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["backward"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_y(
                        self.player,
                        -self.movementSpeedBackward * globalClock.get_dt())

                    myBackControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myBackControl.isPlaying():
                        myBackControl.stop()
                        actor_data.player_character.play('walking')
                        actor_data.player_character.set_play_rate(
                            -4.0, 'walking')

            return Task.cont

        # infinite ground plane
        # the effective world-Z limit
        ground_plane = BulletPlaneShape(Vec3(0, 0, 1), 0)
        node = BulletRigidBodyNode('ground')
        node.add_shape(ground_plane)
        node.set_friction(0.1)
        self.ground_plane = self.render.attach_new_node(node)
        self.ground_plane.set_pos(0, 0, -1)
        self.world.attach_rigid_body(node)

        # Bullet debugger
        from panda3d.bullet import BulletDebugNode
        debugNode = BulletDebugNode('Debug')
        debugNode.show_wireframe(True)
        debugNode.show_constraints(True)
        debugNode.show_bounding_boxes(False)
        debugNode.show_normals(False)
        debugNP = self.render.attach_new_node(debugNode)
        self.world.set_debug_node(debugNP.node())

        # debug toggle function
        def toggle_debug():
            if debugNP.is_hidden():
                debugNP.show()
            else:
                debugNP.hide()

        self.accept('f1', toggle_debug)

        def update(Task):
            if self.game_start < 1:
                self.game_start = 1
            return Task.cont

        def physics_update(Task):
            dt = globalClock.get_dt()
            self.world.do_physics(dt)
            return Task.cont

        self.task_mgr.add(move)
        self.task_mgr.add(update)
        self.task_mgr.add(physics_update)
Example #3
0
    def __init__(self):
        load_prc_file_data(
            "", """
            win-size 1680 1050
            window-title P3D Space Tech Demo Skybox Test 1
            show-frame-rate-meter #t
            framebuffer-multisample 1
            multisamples 4
            view-frustum-cull 0
            textures-power-2 none
            hardware-animated-vertices #t
            gl-depth-zero-to-one true
            clock-frame-rate 60
            interpolate-frames 1
            cursor-hidden #t
            fullscreen #f
        """)

        # Initialize the showbase
        super().__init__()
        gltf.patch_loader(self.loader)

        props = WindowProperties()
        props.set_mouse_mode(WindowProperties.M_relative)
        base.win.request_properties(props)
        base.set_background_color(0.5, 0.5, 0.8)

        self.camLens.set_fov(80)
        self.camLens.set_near_far(0.01, 90000)
        self.camLens.set_focal_length(7)
        self.camera.set_pos(-300, -300, 0)

        # ConfigVariableManager.getGlobalPtr().listVariables()

        # point light generator
        for x in range(0, 3):
            plight_1 = PointLight('plight')
            # add plight props here
            plight_1_node = self.render.attach_new_node(plight_1)
            # group the lights close to each other to create a sun effect
            plight_1_node.set_pos(random.uniform(-21, -20),
                                  random.uniform(-21, -20),
                                  random.uniform(20, 21))
            self.render.set_light(plight_1_node)

        # point light for volumetric lighting filter
        plight_1 = PointLight('plight')
        # add plight props here
        plight_1_node = self.render.attach_new_node(plight_1)
        # group the lights close to each other to create a sun effect
        plight_1_node.set_pos(random.uniform(-21, -20),
                              random.uniform(-21, -20), random.uniform(20, 21))
        self.render.set_light(plight_1_node)

        scene_filters = CommonFilters(base.win, base.cam)
        scene_filters.set_bloom()
        scene_filters.set_high_dynamic_range()
        scene_filters.set_exposure_adjust(0.6)
        scene_filters.set_gamma_adjust(1.1)
        # scene_filters.set_volumetric_lighting(plight_1_node, 32, 0.5, 0.7, 0.1)
        # scene_filters.set_blur_sharpen(0.9)
        # scene_filters.set_ambient_occlusion(32, 0.05, 2.0, 0.01, 0.000002)

        self.accept("f3", self.toggle_wireframe)
        self.accept("escape", sys.exit, [0])

        exponential_fog = Fog('world_fog')
        exponential_fog.set_color(0.6, 0.7, 0.7)
        # this is a very low fog value, set it higher for a greater effect
        exponential_fog.set_exp_density(0.00009)
        # self.render.set_fog(exponential_fog)

        self.game_start = 0

        skybox = self.loader.load_model('skyboxes/40k_test.gltf')
        skybox.reparent_to(self.camera)
        skybox.setCompass()
        skybox.setBin("background", 1)
        skybox.setDepthWrite(False)

        aster_bool = False

        # add some asteroids
        for x in range(100):
            ran_pos = Vec3(random.uniform(-300,
                                          300), random.uniform(-300, 300),
                           random.uniform(-300, 300))

            if not aster_bool:
                asteroid = self.loader.load_model('models/asteroid_1.gltf')
                aster_bool = True

            if aster_bool:
                asteroid = self.loader.load_model('models/asteroid_2.gltf')
                aster_bool = False

            asteroid.reparent_to(self.render)
            asteroid.set_pos(ran_pos)
            asteroid.set_scale(random.uniform(0.1, 10))
            a_pos = asteroid.get_pos()
            ran_inter = random.uniform(-20, 20)
            ran_h = random.uniform(-180, 180)
            asteroid.set_h(ran_h)
            a_rotate = LerpPosHprInterval(asteroid, 100,
                                          (a_pos[0] + ran_inter, a_pos[1] +
                                           ran_inter, a_pos[2] + ran_inter),
                                          (360, 360, 0)).loop()

        # load the scene shader
        scene_shader = Shader.load(Shader.SL_GLSL,
                                   "shaders/simplepbr_vert_mod_1.vert",
                                   "shaders/simplepbr_frag_mod_1.frag")
        self.render.set_shader(scene_shader)
        self.render.set_antialias(AntialiasAttrib.MMultisample)
        scene_shader = ShaderAttrib.make(scene_shader)
        scene_shader = scene_shader.setFlag(ShaderAttrib.F_hardware_skinning,
                                            True)

        # directly make a text node to display text
        text_2 = TextNode('text_2_node')
        text_2.set_text("P3D Space Tech Demo Skybox Test")
        text_2_node = self.aspect2d.attach_new_node(text_2)
        text_2_node.set_scale(0.04)
        text_2_node.set_pos(-1.4, 0, 0.8)
        # import font and set pixels per unit font quality
        nunito_font = self.loader.load_font('fonts/Nunito/Nunito-Light.ttf')
        nunito_font.set_pixels_per_unit(100)
        nunito_font.set_page_size(512, 512)
        # apply font
        text_2.set_font(nunito_font)
        text_2.set_text_color(0.1, 0.1, 0.1, 1)

        # 3D player movement system begins
        self.keyMap = {
            "left": 0,
            "right": 0,
            "forward": 0,
            "backward": 0,
            "run": 0,
            "jump": 0,
            "up": 0,
            "down": 0
        }

        def setKey(key, value):
            self.keyMap[key] = value

        # define button map
        self.accept("a", setKey, ["left", 1])
        self.accept("a-up", setKey, ["left", 0])
        self.accept("d", setKey, ["right", 1])
        self.accept("d-up", setKey, ["right", 0])
        self.accept("w", setKey, ["forward", 1])
        self.accept("w-up", setKey, ["forward", 0])
        self.accept("s", setKey, ["backward", 1])
        self.accept("s-up", setKey, ["backward", 0])
        self.accept("lshift", setKey, ["up", 1])
        self.accept("lshift-up", setKey, ["up", 0])
        self.accept("lcontrol", setKey, ["down", 1])
        self.accept("lcontrol-up", setKey, ["down", 0])
        self.accept("space", setKey, ["jump", 1])
        self.accept("space-up", setKey, ["jump", 0])
        # disable mouse
        self.disable_mouse()

        # the player movement speed
        self.inc_var = 1
        self.max_speed_inc = 0.02
        self.max_abs_speed = 0.2
        self.inertia_x = 0
        self.inertia_y = 0
        self.inertia_z = 0

        def move(Task):
            if self.game_start > 0:
                # get mouse data
                mouse_watch = base.mouseWatcherNode
                if mouse_watch.has_mouse():
                    pointer = base.win.get_pointer(0)
                    mouseX = pointer.get_x()
                    mouseY = pointer.get_y()

                # screen sizes
                window_Xcoord_halved = base.win.get_x_size() // 2
                window_Ycoord_halved = base.win.get_y_size() // 2
                # mouse speed
                mouseSpeedX = 0.2
                mouseSpeedY = 0.2
                # maximum and minimum pitch
                maxPitch = 90
                minPitch = -50
                # cam view target initialization
                camViewTarget = LVecBase3f()
                # clock
                dt = globalClock.get_dt()

                if base.win.movePointer(0, window_Xcoord_halved,
                                        window_Ycoord_halved):
                    p = 0

                    if mouse_watch.has_mouse():
                        # calculate the pitch of camera
                        p = self.camera.get_p() - (
                            mouseY - window_Ycoord_halved) * mouseSpeedY

                    # sanity checking
                    if p < minPitch:
                        p = minPitch
                    elif p > maxPitch:
                        p = maxPitch

                    if mouse_watch.has_mouse():
                        # directly set the camera pitch
                        self.camera.set_p(p)
                        camViewTarget.set_y(p)

                    # rotate the self.player's heading according to the mouse x-axis movement
                    if mouse_watch.has_mouse():
                        h = self.camera.get_h() - (
                            mouseX - window_Xcoord_halved) * mouseSpeedX

                    if mouse_watch.has_mouse():
                        # sanity checking
                        if h < -360:
                            h += 360

                        elif h > 360:
                            h -= 360

                        self.camera.set_h(h)
                        camViewTarget.set_x(h)

                if self.inertia_x > self.max_abs_speed:
                    self.inertia_x = self.max_abs_speed

                if self.inertia_y > self.max_abs_speed:
                    self.inertia_y = self.max_abs_speed

                if self.inertia_z > self.max_abs_speed:
                    self.inertia_z = self.max_abs_speed

                if self.keyMap["right"]:
                    if self.inertia_x > 0:
                        self.inertia_x += self.inc_var * dt
                    else:
                        self.inertia_x = self.max_speed_inc

                    self.camera.set_x(self.camera, self.inertia_x)

                if self.keyMap["left"]:
                    if self.inertia_x < 0:
                        self.inertia_x -= self.inc_var * dt

                    else:
                        self.inertia_x = -self.max_speed_inc

                    self.camera.set_x(self.camera, self.inertia_x)

                if self.keyMap["forward"]:
                    # print(self.inertia_y)
                    if self.inertia_y > 0:
                        self.inertia_y += self.inc_var * dt

                    else:
                        self.inertia_y = self.max_speed_inc

                    self.camera.set_y(self.camera, self.inertia_y)

                if self.keyMap["backward"]:
                    if self.inertia_y < 0:
                        self.inertia_y -= self.inc_var * dt

                    else:
                        self.inertia_y = -self.max_speed_inc

                    self.camera.set_y(self.camera, self.inertia_y)

                if self.keyMap["up"]:
                    if self.inertia_z > 0:
                        self.inertia_z += self.inc_var * dt

                    else:
                        self.inertia_z = self.max_speed_inc

                    self.camera.set_z(self.camera, self.inertia_z)

                if self.keyMap["down"]:
                    if self.inertia_z < 0:
                        self.inertia_z -= self.inc_var * dt

                    else:
                        self.inertia_z = -self.max_speed_inc

                    self.camera.set_z(self.camera, self.inertia_z)

                else:
                    self.camera.set_x(self.camera, self.inertia_x)
                    self.camera.set_y(self.camera, self.inertia_y)
                    self.camera.set_z(self.camera, self.inertia_z)

            return Task.cont

        def update(Task):
            if self.game_start < 1:
                self.game_start = 1
            return Task.cont

        self.task_mgr.add(move)
        self.task_mgr.add(update)
Example #4
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