Пример #1
0
class PbLightNode():
    """Pybullet-compatible light node wrapper
    """
    def __init__(self, render: NodePath):
        self._alight = AmbientLight('pb_alight')
        self._dlight = DirectionalLight('pb_dlight')
        self._anode = render.attach_new_node(self._alight)
        self._dnode = render.attach_new_node(self._dlight)
        self._render = render
        self._is_active = False
        self.set_active(True)

    def set_active(self, active: bool):
        if active and not self._is_active:
            self._render.set_light(self._anode)
            self._render.set_light(self._dnode)
        elif not active and self._is_active:
            self._render.clear_light(self._anode)
            self._render.clear_light(self._dnode)
        self._is_active = active

    def is_active(self):
        return self._is_active

    def update(self, pb_light):
        self._alight.set_color(Vec3(*pb_light.ambient_color))
        self._dlight.set_color(Vec3(*pb_light.diffuse_color))
        self._dlight.set_specular_color(Vec3(*pb_light.specular_color))
        self._dlight.set_shadow_caster(pb_light.shadow_caster)
        self._dnode.set_pos(Vec3(*pb_light.position))
        self._dnode.look_at(0, 0, 0)
Пример #2
0
class DirectionalLight(Light):
    def __init__(self, shadows=True, **kwargs):
        super().__init__()
        self._light = PandaDirectionalLight('directional_light')
        render.setLight(self.attachNewNode(self._light))
        self.shadow_map_resolution = Vec2(1024, 1024)

        for key, value in kwargs.items():
            setattr(self, key, value)

        invoke(setattr, self, 'shadows', shadows, delay=.1)

    @property
    def shadows(self):
        return self._shadows

    @shadows.setter
    def shadows(self, value):
        self._shadows = value
        if value:
            self._light.set_shadow_caster(True,
                                          int(self.shadow_map_resolution[0]),
                                          int(self.shadow_map_resolution[1]))
            bmin, bmax = scene.get_tight_bounds(self)
            lens = self._light.get_lens()
            lens.set_near_far(bmin.z * 2, bmax.z * 2)
            lens.set_film_offset((bmin.xy + bmax.xy) * .5)
            lens.set_film_size((bmax.xy - bmin.xy))
        else:
            self._light.set_shadow_caster(False)
Пример #3
0
    def __init__(self,
                 cad_file=None,
                 output_size=(512, 512),
                 light_on=True,
                 cast_shadow=True):
        # acquire lock since showbase cannot be created twice
        Panda3DRenderer.__lock.acquire()

        # set output size and init the base
        loadPrcFileData('', f'win-size {output_size[0]} {output_size[1]}')
        base = ShowBase(windowType='offscreen')

        # coordinate for normalized and centered object
        obj_node = base.render.attach_new_node('normalized_obj')

        # ambient
        alight = AmbientLight('alight')
        alight.set_color(VBase4(0.5, 0.5, 0.5, 1.0))
        alnp = base.render.attachNewNode(alight)
        base.render.setLight(alnp)

        # directional light for ambient
        dlight1 = DirectionalLight('dlight1')
        dlight1.set_color(VBase4(0.235, 0.235, 0.235, 1.0))
        dlnp1 = base.render.attach_new_node(dlight1)
        dlnp1.set_pos(-2, 3, 1)
        dlnp1.look_at(obj_node)
        base.render.set_light(dlnp1)

        # point light for ambient
        plight1 = PointLight('plight1')
        plight1.set_color(VBase4(1.75, 1.75, 1.75, 1.0))
        plight1.setAttenuation((1, 1, 1))
        plnp1 = base.render.attach_new_node(plight1)
        plnp1.set_pos(0, 0, 3)
        plnp1.look_at(obj_node)
        base.render.set_light(plnp1)

        plight2 = PointLight('plight2')
        plight2.set_color(VBase4(1.5, 1.5, 1.5, 1.0))
        plight2.setAttenuation((1, 0, 1))
        plnp2 = base.render.attach_new_node(plight2)
        plnp2.set_pos(0, -3, 0)
        plnp2.look_at(obj_node)
        base.render.set_light(plnp2)

        dlight2 = DirectionalLight('dlight2')
        dlight2.set_color(VBase4(0.325, 0.325, 0.325, 1.0))
        dlnp2 = base.render.attach_new_node(dlight2)
        dlnp2.set_pos(-1, 1, -1.65)
        dlnp2.look_at(obj_node)
        base.render.set_light(dlnp2)

        dlight3 = DirectionalLight('dlight3')
        dlight3.set_color(VBase4(0.15, 0.15, 0.15, 1.0))
        dlnp3 = base.render.attach_new_node(dlight3)
        dlnp3.set_pos(-2.5, 2.5, 2.0)
        dlnp3.look_at(obj_node)
        base.render.set_light(dlnp3)
        if cast_shadow:
            lens = PerspectiveLens()
            dlight3.set_lens(lens)
            dlight3.set_shadow_caster(True, 1024, 1024)

        dlight4 = DirectionalLight('dlight4')
        dlight4.set_color(VBase4(0.17, 0.17, 0.17, 1.0))
        dlnp4 = base.render.attach_new_node(dlight4)
        dlnp4.set_pos(1.2, -2.0, 2.5)
        dlnp4.look_at(obj_node)
        base.render.set_light(dlnp4)
        if cast_shadow:
            lens = PerspectiveLens()
            dlight4.set_lens(lens)
            dlight4.set_shadow_caster(True, 1024, 1024)

        self.direct_node = direct_node = base.render.attach_new_node(
            'direct_light')
        dlnp2.reparent_to(direct_node)
        dlnp3.reparent_to(direct_node)
        dlnp4.reparent_to(direct_node)

        # auto shader for shadow
        if cast_shadow:
            base.render.setShaderAuto()
        # no culling
        base.render.set_two_sided(True)
        # anti-alias
        base.render.setAntialias(AntialiasAttrib.MMultisample, 8)
        # init camera position
        self.coverage = 0.5
        # the default clear color
        self.clear_color = (0.0, 0.0, 0.0, 0.0)
        # translate in rendered image
        self.obj_translate = (0, 0)
        # light rotation
        self.light_hpr = (0, 0, 0)
        # object rotation
        self.obj_hpr = (0, 0, 0)

        self.base = base
        self.obj = None
        self.obj_node = obj_node
        self.cast_shadow = cast_shadow
        self.camera = base.camera
        if cad_file is not None:
            self.set_obj(cad_file)

        if not light_on:
            base.render.set_light_off()
Пример #4
0
class CastawayBase(ShowBase):
    """
    The Showbase instance for the castaway example.
    Handles window and scene management
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Position the camera.  Set a saner far distance.
        self.camera.set_pos(-35, -6, 6)
        self.camera.set_hpr(-74, -19, 91)
        self.camLens.set_far(250)
        self.disable_mouse()

        # Display instructions
        add_title("Panda3D Tutorial: Castaway Island")
        add_instructions(0.95, "[ESC]: Quit")
        add_instructions(0.90, '[TAB]: Toggle Buffer Viewer')
        add_instructions(0.85, '[F12]: Save Screenshot')
        add_instructions(0.80, '[F11]: Toggle Frame Rate Meter')
        add_instructions(0.75, '[F]: Toggle Sun Frustum')
        add_instructions(0.70, '[O]: Toggle OOBE mode')
        add_instructions(0.65, '[1]: Enable static adjustment mode')
        add_instructions(0.60, '[2]: Enable dynamic adjustment mode')
        add_instructions(0.55, '[3]: disable automatic adjustment')

        # Prepare scene graph
        self.scene_root = self.render.attach_new_node('castaway_scene')
        scene_shader = Shader.load(Shader.SL_GLSL, "resources/scene.vert",
                                   "resources/scene.frag")
        self.render.set_shader(scene_shader)
        self.render.set_shader_input('camera', self.camera)
        self.render.set_antialias(AntialiasAttrib.MAuto)

        # Load the island asset
        self.island = self.loader.load_model('resources/island')
        self.island.reparent_to(self.scene_root)
        self.island.set_p(90)
        self.island.flatten_strong()

        # Create water and fog instances
        self.load_water()
        self.load_fog()

        # Setup lighting
        self.load_lights()
        self.show_frustum = False
        self.taskMgr.add(self._adjust_lighting_bounds_task, sort=45)

        self.adjustment_mode = 0

        # Setup key bindings for debuging
        self.accept('tab', self.bufferViewer.toggleEnable)
        self.accept('f12', self.screenshot)
        self.accept('o', self.oobe)
        self.accept('f11', self.toggle_frame_rate_meter)
        self.accept('1', self.set_adjust_mode, [0])
        self.accept('2', self.set_adjust_mode, [1])
        self.accept('3', self.set_adjust_mode, [2])
        self.accept('f', self.toggle_frustum)
        self.accept('esc', sys.exit)

    def set_adjust_mode(self, mode):
        """
        Sets the currently enabled adjustment mode
        """

        if mode < 0 or mode > 2:
            mode = 0

        print('Setting adjustment mode: %s' % mode)
        self.adjustment_mode = mode

    def toggle_frustum(self):
        """
        Toggles the sun lights frustum viewer
        """

        if self.show_frustum:
            self.sun_light.hide_frustum()
        else:
            self.sun_light.show_frustum()

        self.show_frustum = not self.show_frustum

    def toggle_frame_rate_meter(self):
        """
        Toggles the frame rate meter's state
        """

        self.set_frame_rate_meter(self.frameRateMeter == None)

    def load_water(self):
        """
        Loads the islands psuedo infinite water plane
        """

        # Create a material for the PBR shaders
        water_material = Material()
        water_material.set_base_color(VBase4(0, 0.7, 0.9, 1))

        water_card_maker = CardMaker('water_card')
        water_card_maker.set_frame(-200, 200, -150, 150)
        self.water_path = self.render.attach_new_node(
            water_card_maker.generate())
        self.water_path.set_material(water_material, 1)
        self.water_path.set_scale(500)

    def load_fog(self):
        """
        Loads the fog seen in the distance from the island
        """

        self.world_fog = Fog('world_fog')
        self.world_fog.set_color(
            Vec3(SKY_COLOR.get_x(), SKY_COLOR.get_y(), SKY_COLOR.get_z()))
        self.world_fog.set_linear_range(0, 320)
        self.world_fog.set_linear_fallback(45, 160, 320)
        self.world_fog_path = self.render.attach_new_node(self.world_fog)
        self.render.set_fog(self.world_fog)

    def load_lights(self):
        """
        Loads the scene lighting objects
        """

        # Create a sun source
        self.sun_light = DirectionalLight('sun_light')
        self.sun_light.set_color_temperature(SUN_TEMPERATURE)
        self.sun_light.color = self.sun_light.color * 4
        self.sun_light_path = self.render.attach_new_node(self.sun_light)
        self.sun_light_path.set_pos(10, -10, -10)
        self.sun_light_path.look_at(0, 0, 0)
        self.sun_light_path.hprInterval(
            10.0,
            (self.sun_light_path.get_h(), self.sun_light_path.get_p() - 360,
             self.sun_light_path.get_r()),
            bakeInStart=True).loop()
        self.render.set_light(self.sun_light_path)

        self.sun_light.get_lens().set_near_far(1, 30)
        self.sun_light.get_lens().set_film_size(20, 40)
        self.sun_light.set_shadow_caster(True, 4096, 4096)

        # Create a sky light
        self.sky_light = AmbientLight('sky_light')
        self.sky_light.set_color(VBase4(SKY_COLOR * 0.04, 1))
        self.sky_light_path = self.render.attach_new_node(self.sky_light)
        self.render.set_light(self.sky_light_path)
        self.set_background_color(SKY_COLOR)

    def adjust_colors(self, color):
        """
        Adjusts the scene's current time of day
        """

        self.set_background_color(color)
        self.sky_light.set_color(color)
        self.world_fog.set_color(color)

    def adjust_lighting_static(self):
        """
        This method tightly fits the light frustum around the entire scene.
        Because it is computationally expensive for complex scenes, it is
        intended to be used for non-rotating light sources, and called once at
        loading time.
        """

        bmin, bmax = self.scene_root.get_tight_bounds(self.sun_light_path)
        lens = self.sun_light.get_lens()
        lens.set_film_offset((bmin.xz + bmax.xz) * 0.5)
        lens.set_film_size(bmax.xz - bmin.xz)
        lens.set_near_far(bmin.y, bmax.y)

    def adjust_lighting_dynamic(self):
        """
        This method is much faster, but not nearly as tightly fitting.  May (or
        may not) work better with "bounds-type best" in Config.prc.
        
        It will automatically try to reduce the shadow frustum size in order not
        to shadow objects that are out of view.  Additionally, it will disable
        the shadow camera if the scene bounds are completely out of view of the
        shadow camera.
        """

        # Get Panda's precomputed scene bounds.
        scene_bounds = self.scene_root.get_bounds()
        scene_bounds.xform(self.scene_root.get_mat(self.sun_light_path))

        # Also transform the bounding volume of the camera frustum to light space.
        lens_bounds = self.camLens.make_bounds()
        lens_bounds.xform(self.camera.get_mat(self.sun_light_path))

        # Does the lens bounds contain the scene bounds?
        intersection = lens_bounds.contains(scene_bounds)
        if not intersection:
            # No; deactivate the shadow camera.
            self.sun_light.set_active(False)
            return

        self.sun_light.set_active(True)
        bmin = scene_bounds.get_min()
        bmax = scene_bounds.get_max()

        if intersection & BoundingVolume.IF_all:
            # Completely contains the world volume; no adjustment necessary.
            pass
        else:
            # Adjust all dimensions to tighten around the view frustum bounds,
            # except for the near distance, because objects that are out of view
            # in that direction may still cast shadows.
            lmin = lens_bounds.get_min()
            lmax = lens_bounds.get_max()

            bmin[0] = min(max(bmin[0], lmin[0]), lmax[0])
            bmin[1] = min(bmin[1], lmax[1])
            bmin[2] = min(max(bmin[2], lmin[2]), lmax[2])

        lens = self.sun_light.get_lens()
        lens.set_film_offset((bmin.xz + bmax.xz) * 0.5)
        lens.set_film_size(bmax.xz - bmin.xz)
        lens.set_near_far(bmin.y, bmax.y)

    def _adjust_lighting_bounds_task(self, task):
        """
        Calls the adjust_lighting_bounds function between the ivalLoop(20) and the igLoop(50)
        """

        if self.adjustment_mode == 0:
            self.adjust_lighting_static()
        elif self.adjustment_mode == 1:
            self.adjust_lighting_dynamic()

        return task.cont