Ejemplo n.º 1
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.set_angular_damping(.9)
     node_shape = BulletSphereShape(.08)
     node.add_shape(node_shape)
     node.set_mass(.5)
     return node
Ejemplo n.º 2
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(
         BulletBoxShape(
             Vec3(self.size[0] / 2.0, self.size[1] / 2.0,
                  self.size[2] / 2.0)))
     return node
Ejemplo n.º 3
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.set_angular_damping(.9)
     node_shape = BulletSphereShape(.08)
     node.add_shape(node_shape)
     node.set_mass(.5)
     return node
Ejemplo n.º 4
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node_shape = BulletBoxShape(Vec3(.01, self.size, self.size))
     node.add_shape(node_shape)
     node.set_mass(3)
     node.set_angular_damping(.7)
     return node
Ejemplo n.º 5
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node_shape = BulletBoxShape(Vec3(.01, self.size, self.size))
     node.add_shape(node_shape)
     node.set_mass(3)
     node.set_angular_damping(.7)
     return node
Ejemplo n.º 6
0
    def update_root_nodepath(self, nodepath):
        entity = self._entity
        component = self._class_component

        # Find appropriate mesh source
        mesh_name = component.mesh_name
        if mesh_name is None:
            physics_node = BulletRigidBodyNode()

            # Load mesh from first MeshComponent
            for cls_component in entity.components.values():
                if isinstance(cls_component, MeshComponent):
                    shape = self._shape_from_mesh_component(
                        entity, cls_component)
                    physics_node.add_shape(shape)
                    break

        else:
            physics_node = self._node_from_bam_name(entity, mesh_name)

        # Set mass
        if component.mass is not None:
            physics_node.set_mass(component.mass)

        physics_node.notify_collisions(True)
        physics_node.set_deactivation_enabled(False)

        physics_nodepath = NodePath(physics_node)
        nodepath.reparent_to(physics_nodepath)

        self.body = physics_node
        self._nodepath = physics_nodepath

        return physics_nodepath
Ejemplo n.º 7
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(
         BulletBoxShape(
             Vec3(self.thickness / 2.0, self.width / 2.0,
                  self.length / 2.0)))
     return node
Ejemplo n.º 8
0
class WeaponPhys(PhysColleague):

    def __init__(self, mediator, car, cars, players):  # unused players
        PhysColleague.__init__(self, mediator)
        self.parent, self.car, self.cars = car.gfx.nodepath, car, cars
        self.n_p = self.node = None

    def fire(self):
        self.node = BulletRigidBodyNode(self.coll_name)
        self.node.set_mass(50)
        self.node.add_shape(self.coll_mesh_cls(.5))
        self.n_p = self.parent.attach_node(self.node)
        self.n_p.set_pos(Vec(0, 0, self.joint_z))
        self.n_p.wrt_reparent_to(self.eng.gfx.root)
        launch_dist = self.car.logic.car_vec * self.launch_dist
        pos = self.n_p.get_pos()
        pos = Vec(pos.x, pos.y, pos.z)
        self.n_p.set_pos(pos + launch_dist)
        self.eng.phys_mgr.attach_rigid_body(self.node)
        self.mediator.gfx.gfx_np.reparent_to(self.n_p)
        self.mediator.gfx.gfx_np.set_pos(Vec(0, 0, self.gfx_dz))

    def destroy(self):
        if self.node:  # has not been fired
            self.eng.phys_mgr.remove_rigid_body(self.node)
            try:
                self.n_p = self.n_p.remove_node()
            except AttributeError:
                info('n_p: %s' % self.n_p)
                # it may happen on pause/menu
        self.parent = None
        PhysColleague.destroy(self)
Ejemplo n.º 9
0
    def add_ball(task):
        bullet_body = BulletRigidBodyNode()
        bullet_body.set_linear_sleep_threshold(0)
        bullet_body.set_angular_sleep_threshold(0)
        bullet_body.set_mass(1.0)

        bullet_body.add_shape(BulletSphereShape(ball_size))

        func = random.choice([
            add_smiley,
            add_panda,
            add_mr_man_static,
            add_mr_man_dynamic,
        ])
        func(world, bullet_body)

        return task.again
Ejemplo n.º 10
0
    def __init__(self, app, map_name):
        map_file = 'assets/maps/{}/{}.bam'.format(map_name, map_name)
        self.app = app

        self.physics_world = BulletWorld()

        node = BulletRigidBodyNode('Ground')
        self.np = self.app.render.attach_new_node(node)
        self.np.setPos(0, 0, 0)
        self.physics_world.attachRigidBody(node)

        self.model = loader.load_model(map_file)
        self.model.reparent_to(self.np)

        self.env_data = EnvironmentData(self.model, map_name, 'maps')

        self.physics_world.setGravity(self.env_data.gravity)

        sky = self.model.find(SKYSPHERE)
        sky.reparent_to(base.cam)
        sky.set_bin('background', 0)
        sky.set_depth_write(False)
        sky.set_compass()
        sky.set_light_off()

        # Bullet collision mesh
        collision_solids = self.model.find_all_matches(
            '{}*'.format(TERRAIN_COLLIDER)
        )

        #collision_solids.hide()

        for collision_solid in collision_solids:
            collision_solid.flatten_strong()
            for geom_node in collision_solid.find_all_matches('**/+GeomNode'):
                mesh = BulletTriangleMesh()
                for geom in geom_node.node().get_geoms():
                    mesh.addGeom(geom)
                shape = BulletTriangleMeshShape(mesh, dynamic=False)
                terrain_node = BulletRigidBodyNode('terrain')
                terrain_node.add_shape(shape)
                terrain_node.set_friction(self.env_data.friction)
                terrain_np = geom_node.attach_new_node(terrain_node)
                terrain_np.set_collide_mask(CM_TERRAIN | CM_COLLIDE)
                self.physics_world.attach_rigid_body(terrain_node)
 def drop_boxes(self):
     """
     Puts a stack of boxes at a distance in front of the vehicle.
     The boxes will not necessarily spawn above the terrain and will not
     be cleaned up.
     """ 
     model = loader.load_model('models/box.egg')
     model.set_pos(-0.5, -0.5, -0.5)
     model.flatten_light()
     shape = BulletBoxShape(LVector3(0.5, 0.5, 0.5))
     ahead = self.vehicleNP.get_pos() + self.vehicle.get_forward_vector()*15
     
     for i in range(6):
         node = BulletRigidBodyNode('Box')
         node.set_mass(5.0)
         node.add_shape(shape)
         node.set_deactivation_enabled(False)
         np = render.attach_new_node(node)
         np.set_pos(ahead.x, ahead.y, ahead.z + i*2)
         self.world.attach(node)
         model.copy_to(np)
Ejemplo n.º 12
0
 def generate_spheroid(self):
     bounding_box = Vec3(*[random.uniform(5.0, 30.0) for _ in range(3)])
     pos = Vec3(
         *[random.uniform(-100.0, 100.0) for _ in range(2)],
         random.uniform(50.0, 400.0)
     )
     hpr = Vec3(*[random.random() * 360 for _ in range(3)])
     s = Spheroid(bounding_box)
     s.subdivide_dist(4)
     s.geom_store.normals_as_colors()
     geom_node = s.geom_node
     spheroid_np = self.render.attach_new_node(geom_node)
     shape = get_bullet_shape(geom_node)
     node = BulletRigidBodyNode('Cuboid')
     node.set_mass(random.uniform(0.1, 1000.0))
     node.add_shape(shape)
     visual_np = self.render.attach_new_node(node)
     self.world.attach(node)
     spheroid_np.reparent_to(visual_np)
     self.follow_np.reparent_to(visual_np)
     visual_np.set_pos(pos)
     visual_np.set_hpr(hpr)
Ejemplo n.º 13
0
 def generate_cylinder(self):
     start_point = Vec3(*[random.uniform(-30.0, 30.0) for _ in range(3)])
     end_point = -start_point
     radii = [random.randint(0, 10) for _ in range(2)]
     pos = Vec3(
         *[random.uniform(-100.0, 100.0) for _ in range(2)],
         random.uniform(50.0, 400.0)
     )
     c = SingleCylinder(12, start_point, radii[0], end_point, radii[1])
     c.subdivide_dist(4)
     c.geom_store.normals_as_colors()
     geom_node = c.geom_node
     cylinder_np = self.render.attach_new_node(geom_node)
     shape = get_bullet_shape(geom_node)
     node = BulletRigidBodyNode('Cuboid')
     node.set_mass(random.uniform(0.1, 1000.0))
     node.add_shape(shape)
     visual_np = self.render.attach_new_node(node)
     self.world.attach(node)
     cylinder_np.reparent_to(visual_np)
     self.follow_np.reparent_to(visual_np)
     visual_np.set_pos(pos)
Ejemplo n.º 14
0
 def generate_plane(self):
     bounds = Vec3(*[random.uniform(5.0, 30.0) for _ in range(2)])
     pos = Vec3(
         *[random.uniform(-100.0, 100.0) for _ in range(2)],
         random.uniform(50.0, 400.0)
     )
     normal = Vec3(
         *[random.uniform(-1.0, 1.0) for _ in range(3)],
     )
     color = tuple(normal * 0.5 + 0.5) + (1.,)
     p = Plane((0., 0., 0.), bounds, normal, two_sided=True)
     p.subdivide_dist(4)
     p.geom_store.set_color(color)
     geom_node = p.geom_node
     plane_np = self.render.attach_new_node(geom_node)
     shape = get_bullet_shape(geom_node)
     node = BulletRigidBodyNode('Plane')
     node.set_mass(random.uniform(0.1, 1000.0))
     node.add_shape(shape)
     visual_np = self.render.attach_new_node(node)
     self.world.attach(node)
     plane_np.reparent_to(visual_np)
     self.follow_np.reparent_to(visual_np)
     visual_np.set_pos(pos)
Ejemplo n.º 15
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(BulletBoxShape(Vec3(self.size[0] / 2.0, self.size[1] / 2.0, self.size[2] / 2.0)))
     return node
Ejemplo n.º 16
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(BulletBoxShape(Vec3(self.thickness / 2.0, self.width / 2.0, self.length / 2.0)))
     return node
    def setup_terrain(self):
        """
        Terrain info
        Units are meters, which is preferable when working with Bullet.
        """
        self.terrain_scale = LVector3(512, 512, 100)
        self.terrain_pos = LVector3(-256, -256, -70)
        # sample values for a 4096 x 4096px heightmap.
        #self.terrain_scale = LVector3(4096, 4096, 1000)
        #self.terrain_pos = LVector3(-2048, -2048, -70)
        """
        Diamond_subdivision is an alternating triangulation scheme and may
        produce better results.
        """
        use_diamond_subdivision = True
        
        """
        Construct the terrain
        Without scaling, any ShaderTerrainMesh is 1x1x1 units.
        """
        self.terrain_node = ShaderTerrainMesh()
        """
        Set a heightfield, the heightfield should be a 16-bit png and
        have a quadratic size of a power of two.
        """
        heightfield = Texture()
        heightfield.read(self.heightfield_fn)
        heightfield.set_keep_ram_image(True)        
        self.terrain_node.heightfield = heightfield
        
        # Display characteristic values of the heightfield texture
        #minpoint, maxpoint, avg = LPoint3(), LPoint3(), LPoint3()
        #heightfield.calc_min_max(minpoint, maxpoint)
        #heightfield.calc_average_point(avg, 0.5, 0.5, 0.5)
        #print("avg: {} min: {} max: {}".format(avg.x, minpoint.x, maxpoint.x))

        """
        Set the target triangle width. For a value of 10.0 for example,
        the ShaderTerrainMesh will attempt to make every triangle 10 pixels
        wide on screen.
        """
        self.terrain_node.target_triangle_width = 10.0
        if use_diamond_subdivision:
            """
            This has to be specified before calling .generate()
            The default is false.
            """
            load_prc_file_data("", "stm-use-hexagonal-layout true")
        
        self.terrain_node.generate()
        """
        Attach the terrain to the main scene and set its scale. With no scale
        set, the terrain ranges from (0, 0, 0) to (1, 1, 1)
        """
        self.terrain = self.render.attach_new_node(self.terrain_node)
        self.terrain.set_scale(self.terrain_scale)
        self.terrain.set_pos(self.terrain_pos)
        """
        Set a vertex and a fragment shader on the terrain. The
        ShaderTerrainMesh only works with an applied shader.
        """
        terrain_shader = Shader.load(Shader.SL_GLSL, 
            "samples/shader-terrain/terrain.vert.glsl", 
            "samples/shader-terrain/terrain.frag.glsl")
        self.terrain.set_shader(terrain_shader)
        self.terrain.set_shader_input("camera", base.camera)
        # Set some texture on the terrain
        grass_tex = self.loader.load_texture(
            "samples/shader-terrain/textures/grass.png")
        grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear)
        grass_tex.set_anisotropic_degree(16)
        self.terrain.set_texture(grass_tex)

        """
        Set up the DynamicHeightfield (it's a type of PfmFile). We load the
        same heightfield image as with ShaderTerrainMesh.
        """
        self.DHF = DynamicHeightfield()
        self.DHF.read(self.heightfield_fn)
        """
        Set up empty PfmFiles to prepare stuff in that is going to
        dynamically modify our terrain.
        """
        self.StagingPFM = PfmFile()
        self.RotorPFM = PfmFile()
        
        """
        Set up the BulletHeightfieldShape (=collision terrain) and give it
        some sensible physical properties.
        """
        self.HFS = BulletHeightfieldShape(self.DHF, self.terrain_scale.z,
                                          STM=True)
        if use_diamond_subdivision:
            self.HFS.set_use_diamond_subdivision(True)
        HFS_rigidbody = BulletRigidBodyNode("BulletTerrain")
        HFS_rigidbody.set_static(True)
        friction = 2.0
        HFS_rigidbody.set_anisotropic_friction(
            LVector3(friction, friction, friction/1.3))
        HFS_rigidbody.set_restitution(0.3)
        HFS_rigidbody.add_shape(self.HFS)
        self.world.attach(HFS_rigidbody)
        
        HFS_NP = NodePath(HFS_rigidbody)
        HFS_NP.reparent_to(self.worldNP)
        """
        This aligns the Bullet terrain with the ShaderTerrainMesh rendered
        terrain. It will be exact as long as the terrain vertex shader from
        the STM sample is used and no additional tessellation shader.
        For Bullet (as for other physics engines) the origin of objects is at
        the center.
        """
        HFS_NP.set_pos(self.terrain_pos + self.terrain_scale/2)
        HFS_NP.set_sx(self.terrain_scale.x / heightfield.get_x_size())
        HFS_NP.set_sy(self.terrain_scale.y / heightfield.get_y_size())
        
        # Disables Bullet debug rendering for the terrain, because it is slow.
        #HFS_NP.node().set_debug_enabled(False)
        
        """
        Finally, link the ShaderTerrainMesh and the BulletHeightfieldShape to
        the DynamicHeightfield. From now on changes to the DynamicHeightfield
        will propagate to the (visible) ShaderTerrainMesh and the (collidable)
        BulletHeightfieldShape.
        """
        self.HFS.set_dynamic_heightfield(self.DHF)
        self.terrain_node.set_dynamic_heightfield(self.DHF)
Ejemplo n.º 18
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)
def make_node(name, BulletShape, *args):
    # Returns a BulletRigidBodyNode for the given shape
    shape = BulletShape(*args)
    node = BulletRigidBodyNode(name)
    node.add_shape(shape)
    return node
Ejemplo n.º 20
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     mesh = BulletConvexHullShape()
     mesh.add_geom(self.geom.get_geom(0))
     node.add_shape(mesh)
     return node
Ejemplo n.º 21
0
class BlockTutorial(ShowBase):

    def __init__(self):
        # Step 2: Set up graphics
        ShowBase.__init__(self)
        self.setup_camera()
        self.load_block_model()

        # Step 3: Set up physics
        self.create_physics()
        self.load_block_physics()

        # Step 4: Link graphics and physics
        self.link()

        # This lets us see the debug skeletons for the physics objects.
        self.setup_debug()

        # Setup keybindings
        print
        print "Keybindings"
        print "-----------"

        # Turn on/off the debug skeletons by pressing 'd'
        self.accept('d', self.toggle_debug)
        print "d\t: toggle debug mode"

        # Turn on/off physics simulation by pressing 'space'
        self.accept('space', self.toggle_physics)
        print "space\t: toggle physics"

        # Exit the application by pressing 'esc'
        self.accept('escape', sys.exit)
        print "esc\t: exit"

    def setup_camera(self):
        """Position the camera so we can see the objects in the scene"""

        self.cam.set_pos(-8, -6, 2.75)
        self.cam.look_at((0, 0, 0))

    def load_block_model(self):
        """Load the 3D model of a block, and tell Panda3D to render it"""

        self.block_graphics = self.loader.loadModel("wood_block.egg")
        self.block_graphics.reparent_to(self.render)
        self.block_graphics.set_scale(0.2, 0.2, 0.2)

    def create_physics(self):
        """Create the physical world, and start a task to simulate physics"""

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

        self.physics_on = False
        self.taskMgr.add(self.step_physics, "physics")

    def step_physics(self, task):
        """Get the amount of time that has elapsed, and simulate physics for
        that amount of time"""

        if self.physics_on:
            dt = globalClock.get_dt()
            self.world.do_physics(dt)
        return task.cont

    def toggle_physics(self):
        """Turn physics on or off."""

        self.physics_on = not(self.physics_on)

    def load_block_physics(self):
        """Create collision geometry and a physical body for the block."""

        self.block_body = BulletRigidBodyNode('block-physics')
        self.block_body.add_shape(BulletBoxShape((0.2, 0.6, 0.2)))
        self.block_body.set_mass(1.0)
        self.world.attach_rigid_body(self.block_body)

    def link(self):
        """Tell Panda3D that the block's physics and graphics should be
        linked, by making the physics NodePath be the parent of the
        graphics NodePath.

        """

        self.block_physics = self.render.attach_new_node(self.block_body)
        self.block_graphics.reparent_to(self.block_physics)

    def setup_debug(self):
        """Set up a debug node, which will render skeletons for all the
        physics objects in the physics world."""

        debug_node = BulletDebugNode('Debug')
        debug_node.show_wireframe(True)
        debug_node.show_constraints(True)
        debug_node.show_bounding_boxes(True)
        debug_node.show_normals(True)
        self.world.set_debug_node(debug_node)
        self.debug_np = self.render.attach_new_node(debug_node)

    def toggle_debug(self):
        """Turn off/on rendering of the physics skeletons."""
        if self.debug_np.is_hidden():
            self.debug_np.show()
            # simulate physics for a tiny amount of time, because the
            # skeletons won't actually be rendered until physics has
            # started
            self.world.do_physics(0.0000001)
        else:
            self.debug_np.hide()
Ejemplo n.º 22
0
debug_node = BulletDebugNode('Debug')
debug_node.showWireframe(True)
debug_node.showConstraints(True)
debug_node.showBoundingBoxes(False)
debug_node.showNormals(False)
debug_np = s.render.attach_new_node(debug_node)
bullet_world.set_debug_node(debug_node)
debug_np.show()

# The object in question
mass = BulletRigidBodyNode()
mass.set_mass(1)
mass.setLinearSleepThreshold(0)
mass.setAngularSleepThreshold(0)
shape = BulletSphereShape(1)
mass.add_shape(shape)
mass_node = s.render.attach_new_node(mass)
mass_node.set_hpr(1, 1, 1)
bullet_world.attach_rigid_body(mass)
model = s.loader.load_model('models/smiley')
model.reparent_to(mass_node)
model_axis = loader.load_model('models/zup-axis')
model_axis.reparent_to(model)
model_axis.set_pos(0, 0, 0)
model_axis.set_scale(0.2)

# The orientation to reach
target_node = s.loader.load_model('models/smiley')
target_node.reparent_to(s.render)
target_node.set_hpr(0, 0, 0)
target_node.set_scale(1.5)
Ejemplo n.º 23
0
class Vehicle:
    def __init__(self, app, model_name):
        model_file_name = 'assets/cars/{}/{}.bam'.format(
            model_name, model_name)
        self.app = app

        def animation_path(model, animation):
            base_path = 'assets/cars/animations/{}/{}-{}.bam'
            return base_path.format(model, model, animation)

        self.model = Actor(
            model_file_name,
            {
                #    AIRBRAKE: 'assets/cars/animations/{}-{}.bam'.format(model_name, AIRBRAKE),
                #    AIRBRAKE: animation_path(model_name, AIRBRAKE),
                #    STABILIZER_FINS: animation_path(model_name, STABILIZER_FINS),
            })
        self.model.enableBlend()
        self.model.setControlEffect(AIRBRAKE, 1)
        self.model.setControlEffect(STABILIZER_FINS, 1)
        # FIXME: This code fails due to a bug in Actor
        # airbrake_joints = [joint.name
        #                    for joint in self.model.getJoints()
        #                    if joint.name.startswith(AIRBRAKE)
        # ]
        # self.model.makeSubpart(AIRBRAKE, airbrake_joints)
        # stabilizer_joints = [joint.name
        #                      for joint in self.model.getJoints()
        #                      if joint.name.startswith(STABILIZER_FINS)
        # ]
        # self.model.makeSubpart(STABILIZER_FINS, stabilizer_joints)

        puppet = self.app.loader.load_model(model_file_name)
        puppet.find("**/armature").hide()
        puppet.reparentTo(self.model)

        # Get the vehicle data
        self.vehicle_data = VehicleData(puppet, model_name, 'cars')

        # Configure the physics node
        self.physics_node = BulletRigidBodyNode('vehicle')
        self.physics_node.set_friction(self.vehicle_data.friction)
        self.physics_node.set_linear_sleep_threshold(0)
        self.physics_node.set_angular_sleep_threshold(0)
        self.physics_node.setCcdMotionThreshold(1e-7)
        self.physics_node.setCcdSweptSphereRadius(0.5)
        self.physics_node.setMass(self.vehicle_data.mass)
        shape = BulletConvexHullShape()
        for geom_node in self.model.find_all_matches("**/+GeomNode"):
            for geom in geom_node.node().get_geoms():
                vertices = GeomVertexReader(geom.get_vertex_data(), 'vertex')
                while not vertices.is_at_end():
                    v_geom = vertices.getData3f()
                    v_model = self.model.get_relative_point(geom_node, v_geom)
                    shape.add_point(v_model)
        self.physics_node.add_shape(shape)
        self.vehicle = NodePath(self.physics_node)
        self.vehicle.set_collide_mask(CM_VEHICLE | CM_COLLIDE)
        self.model.reparent_to(self.vehicle)

        # Navigational aids
        self.target_node = self.app.loader.load_model('models/zup-axis')
        self.target_node.reparent_to(self.model)
        self.target_node.set_scale(1)
        self.target_node.set_render_mode_wireframe()
        self.target_node.hide()

        self.delta_node = self.app.loader.load_model('models/smiley')
        self.delta_node.set_pos(1, 10, 1)
        self.delta_node.reparent_to(base.cam)
        self.delta_node.hide()

        self.airbrake_state = 0.0
        self.airbrake_factor = 0.5
        self.airbrake_speed = 1 / self.vehicle_data.airbrake_duration
        self.stabilizer_fins_state = 0.0
        self.stabilizer_fins_speed = 1 / self.vehicle_data.stabilizer_fins_duration

        self.centroid = base.loader.load_model('models/smiley')
        self.centroid.reparent_to(self.vehicle)
        self.centroid.hide()

        # Gyro sound
        sound_file = 'assets/cars/{}/{}.wav'.format(
            model_name,
            GYROSCOPE_SOUND,
        )
        sound = base.audio3d.load_sfx(sound_file)
        self.model.set_python_tag(GYROSCOPE_SOUND, sound)
        base.audio3d.attach_sound_to_object(sound, self.model)
        sound.set_volume(0)
        sound.set_play_rate(0)
        sound.set_loop(True)
        sound.play()

        # Thruster limiting
        self.thruster_state = 0.0
        self.thruster_heat = 0.0

        for repulsor in self.vehicle_data.repulsor_nodes:
            self.add_repulsor(repulsor, model_name)
        for thruster in self.vehicle_data.thruster_nodes:
            self.add_thruster(thruster, model_name)

        # ECU data storage from frame to frame
        self.last_flight_height = None

        # FIXME: Move into a default controller
        self.inputs = {
            # Repulsors
            REPULSOR_ACTIVATION: 0.0,
            ACCELERATE: 0.0,
            TURN: 0.0,
            STRAFE: 0.0,
            HOVER: 0.0,
            FULL_REPULSORS: False,
            # Gyro
            ACTIVE_STABILIZATION_ON_GROUND: PASSIVE,
            ACTIVE_STABILIZATION_CUTOFF_ANGLE: PASSIVE,
            ACTIVE_STABILIZATION_IN_AIR: PASSIVE,
            TARGET_ORIENTATION: Vec3(0, 0, 0),
            # Thrust
            THRUST: 0.0,
            # Air foils
            AIRBRAKE: 0.0,
            STABILIZER_FINS: 0.0,
        }
        self.sensors = {}
        self.commands = {}

    def np(self):
        return self.vehicle

    def place(self, spawn_point):
        # FIXME: Pass a root node to __init__ instead
        self.vehicle.reparent_to(self.app.environment.model)
        connector = self.model.find("**/" + SPAWN_POINT_CONNECTOR)
        self.vehicle.set_hpr(-connector.get_hpr(spawn_point))
        self.vehicle.set_pos(-connector.get_pos(spawn_point))
        self.app.environment.add_physics_node(self.physics_node)

    def set_inputs(self, inputs):
        self.inputs = inputs

    def add_repulsor(self, node, model_name):
        ground_contact = self.app.loader.load_model(
            "assets/effect/repulsorhit.egg")
        ground_contact.set_scale(1)
        ground_contact.reparent_to(self.app.render)
        node.set_python_tag('ray_end', ground_contact)

        sound_file = 'assets/cars/{}/{}.wav'.format(
            model_name,
            REPULSOR_SOUND,
        )
        sound = base.audio3d.load_sfx(sound_file)
        node.set_python_tag(REPULSOR_SOUND, sound)
        base.audio3d.attach_sound_to_object(sound, node)
        sound.set_volume(0)
        sound.set_play_rate(0)
        sound.set_loop(True)
        sound.play()

    def add_thruster(self, node, model_name):
        node.set_python_tag(THRUSTER_POWER, 0.0)
        node.set_python_tag(THRUSTER_OVERHEATED, False)

        # Basic jet sound
        sound_file = 'assets/cars/{}/{}.wav'.format(
            model_name,
            THRUSTER_SOUND,
        )
        sound = base.audio3d.load_sfx(sound_file)
        node.set_python_tag(THRUSTER_SOUND, sound)
        base.audio3d.attach_sound_to_object(sound, node)
        sound.set_volume(0)
        sound.set_play_rate(0)
        sound.set_loop(True)
        sound.play()

        # Overheating sound
        sound_file = 'assets/cars/{}/{}.wav'.format(
            model_name,
            THRUSTER_OVERHEAT_SOUND,
        )
        sound = base.audio3d.load_sfx(sound_file)
        node.set_python_tag(THRUSTER_OVERHEAT_SOUND, sound)
        base.audio3d.attach_sound_to_object(sound, node)

    def game_loop(self):
        self.gather_sensors()
        self.ecu()
        self.apply_air_drag()
        self.apply_repulsors()
        self.apply_gyroscope()
        self.apply_thrusters()
        self.apply_airbrake()
        self.apply_stabilizer_fins()

    def gather_sensors(self):
        # Gather data repulsor ray collisions with ground
        repulsor_data = []
        for node in self.vehicle_data.repulsor_nodes:
            data = RepulsorData()
            repulsor_data.append(data)
            max_distance = node.get_python_tag(ACTIVATION_DISTANCE)
            data.position = node.get_pos(self.vehicle)
            data.direction = self.app.render.get_relative_vector(
                node,
                Vec3(0, 0, -max_distance),
            )
            # FIXME: `self.app.environment.physics_world` is ugly.
            feeler = self.app.environment.physics_world.ray_test_closest(
                base.render.get_relative_point(self.vehicle, data.position),
                base.render.get_relative_point(self.vehicle,
                                               data.position + data.direction),
                CM_TERRAIN,
            )
            if feeler.has_hit():
                data.active = True
                data.fraction = feeler.get_hit_fraction()
                data.contact = self.vehicle.get_relative_point(
                    self.app.render,
                    feeler.get_hit_pos(),
                )
            else:
                data.active = False
                data.fraction = 1.0

        # Find the local ground's normal vector
        local_up = False
        contacts = [
            data.contact for data in repulsor_data
            if data.active and self.inputs[REPULSOR_ACTIVATION]
        ]
        if len(contacts) > 2:
            local_up = True
            target_mode = self.inputs[ACTIVE_STABILIZATION_ON_GROUND]
            # We can calculate a local up as the smallest base vector of the
            # point cloud of contacts.
            centroid = reduce(lambda a, b: a + b, contacts) / len(contacts)
            covariance = [
                [c.x, c.y, c.z]
                for c in [contact - centroid for contact in contacts]
            ][0:3]  # We need exactly 3, no PCA yet. :(
            eigenvalues, eigenvectors = numpy.linalg.eig(
                numpy.array(covariance))
            # FIXME: These few lines look baaaad...
            indexed_eigenvalues = enumerate(eigenvalues)

            def get_magnitude(indexed_element):
                index, value = indexed_element
                return abs(value)

            sorted_indexed_eigenvalues = sorted(
                indexed_eigenvalues,
                key=get_magnitude,
            )
            # The smallest eigenvalue leads to the ground plane's normal
            up_vec_idx, _ = sorted_indexed_eigenvalues[0]
            up_vec = VBase3(*eigenvectors[:, up_vec_idx])
            # Point into the upper half-space
            if up_vec.z < 0:
                up_vec *= -1
            # Calculate the forward of the centroid
            centroid_forward = Vec3(0, 1,
                                    0) - up_vec * (Vec3(0, 1, 0).dot(up_vec))
            # FIXME: Huh?
            forward_planar = centroid_forward - up_vec * (
                centroid_forward.dot(up_vec))
            # Now let's orient and place the centroid
            self.centroid.set_pos(self.vehicle, (0, 0, 0))
            self.centroid.heads_up(forward_planar, up_vec)
            self.centroid.set_pos(self.vehicle, centroid)

            # Flight height for repulsor attenuation
            flight_height = -self.centroid.get_z(self.vehicle)
            if self.last_flight_height is not None:
                climb_speed = (flight_height -
                               self.last_flight_height) / globalClock.dt
            else:
                climb_speed = 0
            self.last_flight_height = flight_height
        else:
            local_up = False
            self.last_flight_height = None
            flight_height = 0.0
            climb_speed = 0.0

        # Air drag
        movement = self.physics_node.get_linear_velocity()

        drag_coefficient = self.vehicle_data.drag_coefficient
        drag_area = self.vehicle_data.drag_area + \
                    self.vehicle_data.airbrake_area * self.airbrake_state + \
                    self.vehicle_data.stabilizer_fins_area * self.stabilizer_fins_state
        air_density = self.app.environment.env_data.air_density
        air_speed = -self.vehicle.get_relative_vector(
            base.render,
            movement,
        )

        # drag_force = 1/2*drag_coefficient*mass_density*area*flow_speed**2
        result = Vec3(air_speed)
        result = (result**3) / result.length()
        result.componentwise_mult(drag_area)
        result.componentwise_mult(self.vehicle_data.drag_coefficient)
        drag_force = result * 1 / 2 * air_density

        self.sensors = {
            CURRENT_ORIENTATION: self.vehicle.get_hpr(self.app.render),
            CURRENT_MOVEMENT: movement,
            CURRENT_ROTATION: self.physics_node.get_angular_velocity(),
            LOCAL_DRAG_FORCE: drag_force,
            REPULSOR_DATA: repulsor_data,
            LOCAL_UP: local_up,
            FLIGHT_HEIGHT: flight_height,
            CLIMB_SPEED: climb_speed,
        }

    def ecu(self):
        repulsor_target_angles = self.ecu_repulsor_reorientation()
        repulsor_activation, delta_height, projected_delta_height, \
            power_frac_needed = self.ecu_repulsor_activation()
        gyro_rotation = self.ecu_gyro_stabilization()
        thruster_activation = [
            self.inputs[THRUST] for _ in self.vehicle_data.thruster_nodes
        ]
        airbrake = self.inputs[AIRBRAKE]
        stabilizer_fins = self.inputs[STABILIZER_FINS]

        self.commands = {
            # Steering commands
            REPULSOR_TARGET_ORIENTATIONS: repulsor_target_angles,
            REPULSOR_ACTIVATION: repulsor_activation,
            GYRO_ROTATION: gyro_rotation,
            THRUSTER_ACTIVATION: thruster_activation,
            AIRBRAKE: airbrake,
            STABILIZER_FINS: stabilizer_fins,
            # ECU data output; Interesting numbers we found along the way.
            HEIGHT_OVER_TARGET: delta_height,
            HEIGHT_OVER_TARGET_PROJECTED: projected_delta_height,
            REPULSOR_POWER_FRACTION_NEEDED: power_frac_needed,
        }

    def ecu_repulsor_reorientation(self):
        # Calculate effective repulsor motion blend values
        accelerate = self.inputs[ACCELERATE]
        turn = self.inputs[TURN]
        strafe = self.inputs[STRAFE]
        hover = self.inputs[HOVER]
        length = sum([abs(accelerate), abs(turn), abs(strafe), hover])
        if length > 1:
            accelerate /= length
            turn /= length
            strafe /= length
            hover /= length
        # Split the turn signal into animation blend factors
        if turn > 0.0:
            turn_left = 0.0
            turn_right = turn
        else:
            turn_left = -turn
            turn_right = 0.0
        # Blend the repulsor angle
        repulsor_target_angles = []
        for node in self.vehicle_data.repulsor_nodes:
            acc_angle = -(node.get_python_tag(ACCELERATE)) * accelerate
            turn_left_angle = node.get_python_tag(TURN_LEFT) * turn_left
            turn_right_angle = node.get_python_tag(TURN_RIGHT) * turn_right
            strafe_angle = node.get_python_tag(STRAFE) * strafe
            hover_angle = node.get_python_tag(HOVER) * hover
            angle = acc_angle + turn_left_angle + turn_right_angle + \
                    strafe_angle + hover_angle
            repulsor_target_angles.append(angle)
        return repulsor_target_angles

    def ecu_repulsor_activation(self):
        # Do we know how high we are flying?
        if self.sensors[LOCAL_UP]:
            tau = self.inputs[TARGET_FLIGHT_HEIGHT_TAU]
            # What would we be at in tau seconds if we weren't using repulsors?
            flight_height = self.sensors[FLIGHT_HEIGHT]
            target_flight_height = self.inputs[TARGET_FLIGHT_HEIGHT]
            delta_height = flight_height - target_flight_height
            gravity_z = self.centroid.get_relative_vector(
                self.app.render,
                self.app.environment.physics_world.get_gravity(),
            ).get_z()
            # Since gravity is an acceleration
            gravity_h = 1 / 2 * gravity_z * tau**2
            climb_rate = self.sensors[CLIMB_SPEED] * tau
            projected_delta_height = delta_height + gravity_h + climb_rate
            # Are we sinking?
            if projected_delta_height <= 0:
                # Our projected height will be under our target height, so we
                # will need to apply the repulsors to make up the difference.
                # How much climb can each repulsor provide at 100% power right
                # now?
                max_powers = [
                    node.get_python_tag(FORCE)
                    for node in self.vehicle_data.repulsor_nodes
                ]
                transferrable_powers = [
                    max_power * cos(0.5 * pi * data.fraction)
                    for max_power, data in zip(
                        max_powers,
                        self.sensors[REPULSOR_DATA],
                    )
                ]
                angle_ratios = [
                    cos(node.get_quat(self.vehicle).get_angle_rad())
                    for node in self.vehicle_data.repulsor_nodes
                ]
                angled_powers = [
                    power * ratio
                    for power, ratio in zip(transferrable_powers, angle_ratios)
                ]
                # We don't want to activate the repulsors unevenly, so we'll
                # have to go by the weakest link.
                total_angled_power = min(angled_powers) * len(angled_powers)
                # How high can we climb under 100% repulsor power?
                max_climb = 1 / 2 * total_angled_power * tau**2 / self.vehicle_data.mass
                # The fraction of power needed to achieve the desired climb
                power_frac_needed = -projected_delta_height / max_climb
                # ...and store it.
                repulsor_activation = [
                    power_frac_needed for _ in self.vehicle_data.repulsor_nodes
                ]
            else:
                # We're not sinking.
                repulsor_activation = [
                    0.0 for _ in self.vehicle_data.repulsor_nodes
                ]
                power_frac_needed = 0.0
        else:  # We do not have ground contact.
            repulsor_activation = [
                0.0 for _ in self.vehicle_data.repulsor_nodes
            ]
            delta_height = 0.0
            projected_delta_height = 0.0
            power_frac_needed = 0.0
        # The driver gives 100% repulsor power, no matter how high we are, or
        # whether we even have ground contact.
        if self.inputs[FULL_REPULSORS]:
            repulsor_activation = [
                self.inputs[REPULSOR_ACTIVATION]
                for _ in self.vehicle_data.repulsor_nodes
            ]
        return repulsor_activation, delta_height, projected_delta_height,\
            power_frac_needed

    def ecu_gyro_stabilization(self):
        # Active stabilization and angular dampening
        # FIXME: Get from self.inputs
        tau = 0.2  # Seconds until target orientation is reached

        if self.sensors[LOCAL_UP]:
            target_mode = self.inputs[ACTIVE_STABILIZATION_ON_GROUND]
        else:
            target_mode = self.inputs[ACTIVE_STABILIZATION_IN_AIR]

        if target_mode == TO_HORIZON:
            # Stabilize to the current heading, but in a horizontal
            # orientation
            self.target_node.set_hpr(
                self.app.render,
                self.vehicle.get_h(),
                0,
                0,
            )
        elif target_mode == TO_GROUND:
            # Stabilize towards the local up
            self.target_node.set_hpr(self.centroid, (0, 0, 0))

        if target_mode != PASSIVE:
            xyz_driver_modification = self.inputs[TARGET_ORIENTATION]
            hpr_driver_modification = VBase3(
                xyz_driver_modification.z,
                xyz_driver_modification.x,
                xyz_driver_modification.y,
            )
            self.target_node.set_hpr(
                self.target_node,
                hpr_driver_modification,
            )

            # Now comes the math.
            orientation = self.vehicle.get_quat(self.app.render)
            target_orientation = self.target_node.get_quat(self.app.render)
            delta_orientation = target_orientation * invert(orientation)
            self.delta_node.set_quat(invert(delta_orientation))

            delta_angle = delta_orientation.get_angle_rad()
            if abs(delta_angle) < (pi / 360 * 0.1) or isnan(delta_angle):
                delta_angle = 0
                axis_of_torque = VBase3(0, 0, 0)
            else:
                axis_of_torque = delta_orientation.get_axis()
                axis_of_torque.normalize()
                axis_of_torque = self.app.render.get_relative_vector(
                    self.vehicle,
                    axis_of_torque,
                )
            if delta_angle > pi:
                delta_angle -= 2 * pi

            # If the mass was standing still, this would be the velocity that
            # has to be reached to achieve the targeted orientation in tau
            # seconds.
            target_angular_velocity = axis_of_torque * delta_angle / tau
        else:  # `elif target_mode == PASSIVE:`, since we might want an OFF mode
            # Passive stabilization, so this is the pure commanded impulse
            target_angular_velocity = self.app.render.get_relative_vector(
                self.vehicle,
                self.inputs[TARGET_ORIENTATION] * tau / pi,
            )

        # But we also have to cancel out the current velocity for that.
        angular_velocity = self.physics_node.get_angular_velocity()
        countering_velocity = -angular_velocity

        # An impulse of 1 causes an angular velocity of 2.5 rad on a unit mass,
        # so we have to adjust accordingly.
        target_impulse = target_angular_velocity / 2.5 * self.vehicle_data.mass
        countering_impulse = countering_velocity / 2.5 * self.vehicle_data.mass

        # Now just sum those up, and we have the impulse that needs to be
        # applied to steer towards target.
        impulse = target_impulse + countering_impulse
        return impulse

    def apply_air_drag(self):
        drag_force = self.sensors[LOCAL_DRAG_FORCE]
        global_drag_force = base.render.get_relative_vector(
            self.vehicle,
            drag_force,
        )
        if not isnan(global_drag_force.x):
            self.physics_node.apply_central_impulse(
                global_drag_force * globalClock.dt, )

    def apply_repulsors(self):
        dt = globalClock.dt
        repulsor_data = zip(self.vehicle_data.repulsor_nodes,
                            self.sensors[REPULSOR_DATA],
                            self.commands[REPULSOR_ACTIVATION],
                            self.commands[REPULSOR_TARGET_ORIENTATIONS])
        for node, data, activation, angle in repulsor_data:
            active = data.active
            frac = data.fraction
            # Repulse in current orientation
            if active and activation:
                if activation > 1.0:
                    activation = 1.0
                if activation < 0.0:
                    activation = 0.0
                # Repulsor power at zero distance
                base_strength = node.get_python_tag(FORCE)
                # Effective fraction of repulsors force
                transfer_frac = cos(0.5 * pi * frac)
                # Effective repulsor force
                strength = base_strength * activation * transfer_frac
                # Resulting impulse
                impulse_dir = Vec3(0, 0, 1)
                impulse_dir_world = self.app.render.get_relative_vector(
                    node,
                    impulse_dir,
                )
                impulse = impulse_dir_world * strength
                # Apply
                repulsor_pos = node.get_pos(self.vehicle)
                # FIXME! The position at which an impulse is applied seems to be
                # centered around node it is applied to, but offset in the world
                # orientation. So, right distance, wrong angle. This is likely a
                # bug in Panda3D's Bullet wrapper. Or an idiosyncracy of Bullet.
                self.physics_node.apply_impulse(
                    impulse * dt,
                    self.app.render.get_relative_vector(
                        self.vehicle,
                        repulsor_pos,
                    ),
                )

                # Contact visualization node
                max_distance = node.get_python_tag(ACTIVATION_DISTANCE)
                contact_distance = -impulse_dir_world * max_distance * frac
                contact_node = node.get_python_tag('ray_end')
                contact_node.set_pos(
                    node.get_pos(self.app.render) + contact_distance, )
                #contact_node.set_hpr(node, 0, -90, 0) # Look towards repulsor
                #contact_node.set_hpr(0, -90, 0) # Look up
                contact_node.set_hpr(0, -90,
                                     contact_node.getR() +
                                     4)  # Look up and rotate
                contact_node.set_scale(1 - frac)

                contact_node.show()
                # Sound
                sound = node.get_python_tag(REPULSOR_SOUND)
                sound.set_volume(activation * 20)
                sound.set_play_rate((1 + activation / 2) * 2)
            else:
                node.get_python_tag('ray_end').hide()
                sound = node.get_python_tag(REPULSOR_SOUND)
                sound.set_volume(0.0)
                sound.set_play_rate(0.0)
            # Reorient
            old_hpr = node.get_python_tag(REPULSOR_OLD_ORIENTATION)
            want_hpr = VBase3(angle.z, angle.x, angle.y)
            delta_hpr = want_hpr - old_hpr
            max_angle = node.get_python_tag(REPULSOR_TURNING_ANGLE) * dt
            if delta_hpr.length() > max_angle:
                delta_hpr = delta_hpr / delta_hpr.length() * max_angle
            new_hpr = old_hpr + delta_hpr
            node.set_hpr(new_hpr)
            node.set_python_tag(REPULSOR_OLD_ORIENTATION, new_hpr)

    def apply_gyroscope(self):
        impulse = self.commands[GYRO_ROTATION]
        # Clamp the impulse to what the "motor" can produce.
        max_impulse = self.vehicle_data.max_gyro_torque
        if impulse.length() > max_impulse:
            clamped_impulse = impulse / impulse.length() * max_impulse
        else:
            clamped_impulse = impulse

        self.physics_node.apply_torque_impulse(clamped_impulse)

        impulse_ratio = clamped_impulse.length() / max_impulse
        sound = self.model.get_python_tag(GYROSCOPE_SOUND)
        sound.set_volume(0.5 * impulse_ratio)
        sound.set_play_rate(0.9 + 0.1 * impulse_ratio)

    def apply_thrusters(self):
        dt = globalClock.dt
        thruster_data = zip(
            self.vehicle_data.thruster_nodes,
            self.commands[THRUSTER_ACTIVATION],
        )
        for node, thrust in thruster_data:
            if self.thruster_heat >= 1.0:
                thrust = 0.0
            current_power = node.get_python_tag(THRUSTER_POWER)
            # Adjust thrust to be within bounds of thruster's ability to adjust
            # thrust.
            ramp_time = node.get_python_tag(THRUSTER_RAMP_TIME)
            ramp_step = (1 / ramp_time) * globalClock.dt
            thrust = adjust_within_limit(thrust, current_power, ramp_step)
            node.set_python_tag(THRUSTER_POWER, thrust)
            max_force = node.get_python_tag(FORCE)
            real_force = max_force * thrust
            # FIXME: See repulsors above for the shortcoming that this suffers
            thruster_pos = base.render.get_relative_vector(
                self.vehicle,
                node.get_pos(self.vehicle),
            )
            thrust_direction = self.app.render.get_relative_vector(
                node, Vec3(0, 0, 1))
            self.physics_node.apply_impulse(
                thrust_direction * real_force * dt,
                thruster_pos,
            )
            heating = node.get_python_tag(THRUSTER_HEATING)
            cooling = node.get_python_tag(THRUSTER_COOLING)
            effective_heating = -cooling + (heating - cooling) * thrust
            self.thruster_heat += effective_heating * dt
            if self.thruster_heat < 0.0:
                self.thruster_heat = 0.0

            # Sound
            sound = node.get_python_tag(THRUSTER_SOUND)
            sound.set_volume(thrust * 5)
            sound.set_play_rate((1 + thrust / 20) / 3)

            was_overheated = node.get_python_tag(THRUSTER_OVERHEATED)
            is_overheated = self.thruster_heat > 1.0
            if is_overheated and not was_overheated:
                sound = node.get_python_tag(THRUSTER_OVERHEAT_SOUND)
                sound.set_volume(5)
                sound.play()
                node.set_python_tag(THRUSTER_OVERHEATED, True)
            if not is_overheated:
                node.set_python_tag(THRUSTER_OVERHEATED, False)

    def apply_airbrake(self):
        # Animation and state update only, since the effect is in air drag
        dt = globalClock.dt
        target = self.commands[AIRBRAKE]
        max_movement = self.airbrake_speed * dt
        # Clamp change to available speed
        delta = target - self.airbrake_state
        if abs(delta) > max_movement:
            self.airbrake_state += copysign(max_movement, delta)
        else:
            self.airbrake_state = target
        if self.airbrake_state > 1.0:
            self.airbrake_state = 1.0
        if self.airbrake_state < 0.0:
            self.airbrake_state = 0.0
        #self.model.pose(AIRBRAKE, self.airbrake_state, partName=AIRBRAKE)
        self.model.pose(AIRBRAKE, self.airbrake_state)

    def apply_stabilizer_fins(self):
        # Animation and state update only, since the effect is in air drag
        dt = globalClock.dt
        target = self.commands[STABILIZER_FINS]
        max_movement = self.stabilizer_fins_speed * dt
        # Clamp change to available speed
        delta = target - self.stabilizer_fins_state
        if abs(delta) > max_movement:
            self.stabilizer_fins_state += copysign(max_movement, delta)
        else:
            self.stabilizer_fins_state = target
        if self.stabilizer_fins_state > 1.0:
            self.stabilizer_fins_state = 1.0
        if self.stabilizer_fins_state < 0.0:
            self.stabilizer_fins_state = 0.0
        self.model.pose(
            STABILIZER_FINS,
            self.stabilizer_fins_state,
            #partName=STABILIZER_FINS,
        )
        # FIXME: Implement stabilizing effect

    def shock(self, x=0, y=0, z=0):
        self.physics_node.apply_impulse(
            Vec3(0, 0, 0),
            Vec3(random(), random(), random()) * 10,
        )
        self.physics_node.apply_torque_impulse(Vec3(x, y, z))
Ejemplo n.º 24
0
class BlockTutorial(ShowBase):
    def __init__(self):
        # Step 2: Set up graphics
        ShowBase.__init__(self)
        self.setup_camera()
        self.load_block_model()

        # Step 3: Set up physics
        self.create_physics()
        self.load_block_physics()

        # Step 4: Link graphics and physics
        self.link()

        # This lets us see the debug skeletons for the physics objects.
        self.setup_debug()

        # Setup keybindings
        print
        print "Keybindings"
        print "-----------"

        # Turn on/off the debug skeletons by pressing 'd'
        self.accept('d', self.toggle_debug)
        print "d\t: toggle debug mode"

        # Turn on/off physics simulation by pressing 'space'
        self.accept('space', self.toggle_physics)
        print "space\t: toggle physics"

        # Exit the application by pressing 'esc'
        self.accept('escape', sys.exit)
        print "esc\t: exit"

    def setup_camera(self):
        """Position the camera so we can see the objects in the scene"""

        self.cam.set_pos(-8, -6, 2.75)
        self.cam.look_at((0, 0, 0))

    def load_block_model(self):
        """Load the 3D model of a block, and tell Panda3D to render it"""

        self.block_graphics = self.loader.loadModel("wood_block.egg")
        self.block_graphics.reparent_to(self.render)
        self.block_graphics.set_scale(0.2, 0.2, 0.2)

    def create_physics(self):
        """Create the physical world, and start a task to simulate physics"""

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

        self.physics_on = False
        self.taskMgr.add(self.step_physics, "physics")

    def step_physics(self, task):
        """Get the amount of time that has elapsed, and simulate physics for
        that amount of time"""

        if self.physics_on:
            dt = globalClock.get_dt()
            self.world.do_physics(dt)
        return task.cont

    def toggle_physics(self):
        """Turn physics on or off."""

        self.physics_on = not (self.physics_on)

    def load_block_physics(self):
        """Create collision geometry and a physical body for the block."""

        self.block_body = BulletRigidBodyNode('block-physics')
        self.block_body.add_shape(BulletBoxShape((0.2, 0.6, 0.2)))
        self.block_body.set_mass(1.0)
        self.world.attach_rigid_body(self.block_body)

    def link(self):
        """Tell Panda3D that the block's physics and graphics should be
        linked, by making the physics NodePath be the parent of the
        graphics NodePath.

        """

        self.block_physics = self.render.attach_new_node(self.block_body)
        self.block_graphics.reparent_to(self.block_physics)

    def setup_debug(self):
        """Set up a debug node, which will render skeletons for all the
        physics objects in the physics world."""

        debug_node = BulletDebugNode('Debug')
        debug_node.show_wireframe(True)
        debug_node.show_constraints(True)
        debug_node.show_bounding_boxes(True)
        debug_node.show_normals(True)
        self.world.set_debug_node(debug_node)
        self.debug_np = self.render.attach_new_node(debug_node)

    def toggle_debug(self):
        """Turn off/on rendering of the physics skeletons."""
        if self.debug_np.is_hidden():
            self.debug_np.show()
            # simulate physics for a tiny amount of time, because the
            # skeletons won't actually be rendered until physics has
            # started
            self.world.do_physics(0.0000001)
        else:
            self.debug_np.hide()
Ejemplo n.º 25
0
def main():
    ECSShowBase()
    base.disable_mouse()
    base.accept('escape', sys.exit)
    base.cam.set_pos(4, -5, 2)
    base.cam.look_at(0, 0, 0)

    system_types = [
        prototype.ManageModels,
        clock.DetermineTimestep,
        prototype.DeterminePhysicsTimestep,
        prototype.DoPhysics,
    ]
    for sort, system_type in enumerate(system_types):
        base.add_system(system_type(), sort)

    # Bullet world
    bullet_world = BulletWorld()
    bullet_world.set_gravity(Vec3(0, 0, -9.81))

    if debug:
        debugNode = BulletDebugNode('Debug')
        debugNode.showWireframe(True)
        debugNode.showConstraints(True)
        debugNode.showBoundingBoxes(False)
        debugNode.showNormals(False)
        debugNP = render.attachNewNode(debugNode)
        debugNP.show()
        bullet_world.setDebugNode(debugNP.node())

    world = base.ecs_world.create_entity(
        clock.Clock(clock=clock.panda3d_clock),
        prototype.PhysicsWorld(world=bullet_world, timestep=1 / 30),
    )
    base.ecs_world._flush_component_updates()

    # Something for the models to fall on
    bullet_body = BulletRigidBodyNode()
    bullet_body.set_mass(0.0)
    bullet_body.add_shape(
        BulletBoxShape(Vec3(base_size, base_size, 0.1)),
        TransformState.makePos(Point3(0, 0, 0)),
    )
    bullet_body.add_shape(
        BulletBoxShape(Vec3(base_size, 0.1, wall_height)),
        TransformState.makePos(Point3(0, -base_size, wall_height)),
    )
    bullet_body.add_shape(
        BulletBoxShape(Vec3(base_size, 0.1, wall_height)),
        TransformState.makePos(Point3(0, base_size, wall_height)),
    )
    bullet_body.add_shape(
        BulletBoxShape(Vec3(0.1, base_size, wall_height)),
        TransformState.makePos(Point3(-base_size, 0, wall_height)),
    )
    bullet_body.add_shape(
        BulletBoxShape(Vec3(0.1, base_size, wall_height)),
        TransformState.makePos(Point3(base_size, 0, wall_height)),
    )

    base.ecs_world.create_entity(
        prototype.Model(post_attach=prototype.transform(pos=Vec3(0, 0, -1)), ),
        prototype.PhysicsBody(
            body=bullet_body,
            world=world._uid,
        ),
    )

    # Regularly add a ball

    def add_ball(task):
        bullet_body = BulletRigidBodyNode()
        bullet_body.set_linear_sleep_threshold(0)
        bullet_body.set_angular_sleep_threshold(0)
        bullet_body.set_mass(1.0)

        bullet_body.add_shape(BulletSphereShape(ball_size))

        func = random.choice([
            add_smiley,
            add_panda,
            add_mr_man_static,
            add_mr_man_dynamic,
        ])
        func(world, bullet_body)

        return task.again

    base.do_method_later(creation_interval, add_ball, 'add ball')

    # Start
    base.run()
Ejemplo n.º 26
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(BulletPlaneShape(Vec3(0, 1, 0), 1))
     return node
debug_node.showWireframe(True)
debug_node.showConstraints(True)
debug_node.showBoundingBoxes(False)
debug_node.showNormals(False)
debug_np = s.render.attach_new_node(debug_node)
bullet_world.set_debug_node(debug_node)
debug_np.show()


# The object in question
mass = BulletRigidBodyNode()
mass.set_mass(1)
mass.setLinearSleepThreshold(0)
mass.setAngularSleepThreshold(0)
shape = BulletSphereShape(1)
mass.add_shape(shape)
mass_node = s.render.attach_new_node(mass)
mass_node.set_hpr(1, 1, 1)
bullet_world.attach_rigid_body(mass)
model = s.loader.load_model('models/smiley')
model.reparent_to(mass_node)
model_axis = loader.load_model('models/zup-axis')
model_axis.reparent_to(model)
model_axis.set_pos(0, 0, 0)
model_axis.set_scale(0.2)


# The orientation to reach
target_node = s.loader.load_model('models/smiley')
target_node.reparent_to(s.render)
target_node.set_hpr(0,0,0)
Ejemplo n.º 28
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     mesh = BulletConvexHullShape()
     mesh.add_geom(self.geom.get_geom(0))
     node.add_shape(mesh)
     return node
Ejemplo n.º 29
0
 def create_solid(self):
     node = BulletRigidBodyNode(self.name)
     node.add_shape(BulletPlaneShape(Vec3(0, 1, 0), 1))
     return node