예제 #1
0
  def __init__(self,manager,xml):
    self.surface = getWaterSurface(manager)
    self.surface.reparentTo(render)
    self.surface.hide(BitMask32.bit(1)) # Invisible to reflection camera (speedup)
    self.surface.hide(BitMask32.bit(2)) # Invisible to volumetric lighting camera (speedup)
    self.surface.hide(BitMask32.bit(3)) # Invisible to shadow cameras (speedup)
    self.surface.setShader(loader.loadShader(manager.get('paths').getConfig().find('shaders').get('path')+'/water.cg'))
    self.surface.setShaderInput('time', 0.0, 0.0, 0.0, 0.0)
    ntex = loader.loadTexture(manager.get('paths').getConfig().find('textures').get('path')+'/water-normal.png')
    ntex.setMinfilter(Texture.FTLinearMipmapLinear)
    self.surface.setShaderInput('normal', ntex)
    self.surface.setShaderInput('camera', base.cam)
    self.surface.setTransparency(TransparencyAttrib.MDual, 10)
    self.surface.setTwoSided(True)

    self.surface.setShaderInput('waveInfo', Vec4(0.4, 0.4, 0.4, 0))
    self.surface.setShaderInput('param2', Vec4(-0.015,0.005, 0.05, 0.05))
    self.surface.setShaderInput('param3', Vec4(0.7, 0.3, 0, 0))
    self.surface.setShaderInput('param4', Vec4(2.0, 0.5, 0.5, 0.0))
    #self.surface.setShaderInput('speed', Vec4(-.8, -.4, -.9, .3))
    self.surface.setShaderInput('speed', Vec4(0.2, -1.2, -0.2, -0.7))
    self.surface.setShaderInput('deepcolor', Vec4(0.0,0.3,0.5,1.0))
    self.surface.setShaderInput('shallowcolor', Vec4(0.0,1.0,1.0,1.0))
    self.surface.setShaderInput('reflectioncolor', Vec4(0.95,1.0,1.0,1.0))
    self.surface.hide()

    self.wbuffer = base.win.makeTextureBuffer('water', 512, 512)
    self.wbuffer.setClearColorActive(True)
    self.wbuffer.setClearColor(base.win.getClearColor())
    self.wcamera = base.makeCamera(self.wbuffer)
    if manager.get('sky') != None and manager.get('sky').model != None:
      self.sky = manager.get('sky').model.copyTo(self.wcamera)
      self.sky.setTwoSided(True)
      self.sky.setSz(self.sky, -1)
      self.sky.setClipPlaneOff(1)
      self.sky.show()
      self.sky.hide(BitMask32.bit(0)) # Hide for normal camera
      self.sky.hide(BitMask32.bit(2)) # Hide for volumetric lighting camera
      self.sky.hide(BitMask32.bit(3)) # Hide for shadow camera(s), if any
    else:
      self.sky = None
    self.wcamera.reparentTo(render)
    self.wcamera.node().setLens(base.camLens)
    self.wcamera.node().setCameraMask(BitMask32.bit(1))
    self.surface.hide(BitMask32.bit(1))
    wtexture = self.wbuffer.getTexture()
    wtexture.setWrapU(Texture.WMClamp)
    wtexture.setWrapV(Texture.WMClamp)
    wtexture.setMinfilter(Texture.FTLinearMipmapLinear)
    self.surface.setShaderInput('reflection', wtexture)
    self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))
    self.wplanenp = render.attachNewNode(PlaneNode('water', self.wplane))
    tmpnp = NodePath('StateInitializer')
    tmpnp.setClipPlane(self.wplanenp)
    tmpnp.setAttrib(CullFaceAttrib.makeReverse())
    self.wcamera.node().setInitialState(tmpnp.getState())

    #self.fog = Fog('UnderwaterFog')
    #self.fog.setColor(0.0,0.3,0.5)
    self.fogEnabled = False
    def renderSceneInto(self,
                        depthtex=None,
                        colortex=None,
                        auxtex=None,
                        auxbits=0,
                        textures=None):
        if textures:
            colortex = textures.get('color', None)
            depthtex = textures.get('depth', None)
            auxtex = textures.get('aux', None)

        if colortex == None:
            colortex = Texture('filter-base-color')
            colortex.setWrapU(Texture.WMClamp)
            colortex.setWrapV(Texture.WMClamp)

        texgroup = (depthtex, colortex, auxtex, None)
        (winx, winy) = self.getScaledSize(1, 1, 1)
        buffer = self.createBuffer('filter-base', winx, winy, texgroup)
        if buffer == None:
            return None

        cm = CardMaker('filter-base-quad')
        cm.setFrameFullscreenQuad()
        quad = NodePath(cm.generate())
        quad.setDepthTest(0)
        quad.setDepthWrite(0)
        quad.setTexture(colortex)
        quad.setColor(Vec4(1, 0.5, 0.5, 1))
        cs = NodePath('dummy')
        cs.setState(self.camstate)
        if auxbits:
            cs.setAttrib(AuxBitplaneAttrib.make(auxbits))

        self.camera.node().setInitialState(cs.getState())
        quadcamnode = Camera('filter-quad-cam')
        lens = OrthographicLens()
        lens.setFilmSize(2, 2)
        lens.setFilmOffset(0, 0)
        lens.setNearFar(-1000, 1000)
        quadcamnode.setLens(lens)
        quadcam = quad.attachNewNode(quadcamnode)
        self.region.setCamera(quadcam)
        dr = buffer.getDisplayRegion(0)
        self.setStackedClears(dr, self.rclears, self.wclears)
        if auxtex:
            dr.setClearActive(GraphicsOutput.RTPAuxRgba0, 1)
            dr.setClearValue(GraphicsOutput.RTPAuxRgba0,
                             Vec4(0.5, 0.5, 1.0, 0.0))

        self.region.disableClears()
        if self.isFullscreen():
            self.win.disableClears()

        dr.setCamera(self.camera)
        dr.setActive(1)
        self.buffers.append(buffer)
        self.sizes.append((1, 1, 1))
        return quad
예제 #3
0
class LightBase(object):
    
    """ LightBase is a lightweight interface for rendering with
    Panda3d. It contains several key Panda3d objects that are required
    for rendering, like GraphicsEngine, GraphicsPipe, GraphicsOutput,
    a root NodePath (rootnode), cameras and lights. It provides also
    methods for manipulating and reading them out them."""

    def __init__(self):
        self.init_graphics()
        self.setup_rootnode()
        self.output_list = []
        self.gsg_list = []
        self.cameras = None
        
    def init_graphics(self):
        """ Creates GraphicsEngine, GraphicsPipe, and loader """
        # Get a handle to the graphics pipe selector
        selection = GraphicsPipeSelection.getGlobalPtr()
        # Check for DISPLAY
        if "DISPLAY" in os.environ:
            # Use the first option (should be glx)
            pipe_type = selection.getPipeTypes()[0]
        else:
            # Use the last option (should be some fallback module)
            pipe_type = selection.getPipeTypes()[-1]
        # Create the graphics pipe
        self.pipe = selection.makePipe(pipe_type)
        # Get the graphics engine
        self.engine = GraphicsEngine.getGlobalPtr()
        # Get the model loader object and assign it to the engine
        self.loader = Loader.Loader(self)
        self.engine.setDefaultLoader(self.loader.loader)

    @staticmethod
    def init_fbp():
        """ Initial / default FrameBufferProperties """
        fbp = FrameBufferProperties()
        fbp.setRgbColor(1) 
        fbp.setColorBits(1)
        fbp.setAlphaBits(1)
        fbp.setDepthBits(1)
        return fbp

    @staticmethod
    def init_wp(window_type, size):
        """ Initial / default WindowProperties """
        if window_type == 'onscreen':
            wp = WindowProperties.getDefault()
            wp.setSize(size[0], size[1])
        elif window_type == 'offscreen':
            wp = WindowProperties.size(size[0], size[1])
        return wp

    def make_window(self, size=None, name=None, sort=0, xflags=0,
                    host_out=None):
        """ Create an onscreen window -- high-level interface for
        makeOutput"""

        # Handle size
        if size is None and host_out is not None:
            size = (host_out.getFbXSize(), host_out.getFbYSize())
        elif size is None:
            raise ValueError("Size not available.")

        # Initialize WindowProperties
        wp = self.init_wp('onscreen', size)

        # Create window
        output = self.make_output('onscreen', name, sort, wp=wp,
                                  xflags=xflags, host_out=host_out)

        # Handle failure to create window (returns None)
        if output is None:
            raise StandardError("Window creation failed, not sure why.")

        return output

    def make_buffer(self, size=None, name=None, sort=0, xflags=0,
                    host_out=None):
        """ Makes an offscreen buffer -- high-level interface for
        make_output"""

        # Handle size
        if size is None and host_out is not None:
            size = (host_out.getFbXSize(), host_out.getFbYSize())
        elif size is None:
            raise ValueError("Size not available.")

        # Initialize WindowProperties
        wp = self.init_wp('offscreen', size)
        
        # Create the buffer
        output = self.make_output('offscreen', name, sort, wp=wp,
                                  xflags=xflags, host_out=host_out)

        # Handle case when offscreen buffer cannot be directly created
        # (currently this is what happens)
        if output is None:
            print("Direct offscreen buffer creation failed...")
            print("... falling back to onscreen --> offscreen method.")
            
            # Open an onscreen window first, just to get a GraphicsOutput
            # object which is needed to open an offscreen buffer.
            dummywin = self.make_window(size, 'dummy_onscreen_win', sort,
                                        xflags, host_out)
            # Now, make offscreen buffer through win
            output = self.make_output('offscreen', name, sort, xflags=xflags,
                                      host_out=dummywin)

            # Handle failure to create window (returns None)
            if output is None:
                raise StandardError("Failed to create offscreen buffer.")

        return output

    def make_texture_buffer(self, size=None, name=None, sort=0,
                            xflags=0, mode=None, bitplane=None, host_out=None):
        """ Makes an offscreen buffer and adds a render texture """

        # Make the offscreen buffer
        output = self.make_buffer(size, name, sort, xflags, host_out)
        # Add the texture
        tex = self.add_render_texture(output, mode, bitplane)
        # Cause necessary stuff to be created (buffers, textures etc)
        self.render_frame()
        
        return output, tex

    def make_output(self, window_type, name=None, sort=0, fbp=None, wp=None,
                    xflags=0, host_out=None):
        """ Makes a GraphicsOutput object and stores it in
        self.output_list. This is the low-level interface."""
        
        # Input handling / defaults
        if name is None:
            name = window_type + '_win'
        if fbp is None:
            # Initialize FramebufferProperties
            fbp = self.init_fbp()
        if wp is None and host_out is not None:
            # Initialize WindowProperties
            wp = self.init_wp(window_type, (host_out.getFbXSize(),
                                            host_out.getFbYSize()))
        elif wp is None:
            raise ValueError("Size not available in either wp or win.")
        flags = xflags | GraphicsPipe.BFFbPropsOptional
        # flags' window_type switch
        if window_type == 'onscreen':
            flags = flags | GraphicsPipe.BFRequireWindow
        elif window_type == 'offscreen':
            flags = flags | GraphicsPipe.BFRefuseWindow
            
        # Make the window / buffer
        engine = self.engine
        pipe = self.pipe
        if host_out is None:
            output = engine.makeOutput(pipe, name, sort, fbp, wp, flags)
        else:
            output = engine.makeOutput(pipe, name, sort, fbp, wp, flags,
                                       host_out.getGsg(), host_out)

        # Add output to this instance's list
        if output is not None:
            self.add_to_output_gsg_lists(output)

            # Set background color to black by default
            output.setClearColor((0.0, 0.0, 0.0, 0.0))

            # Cause necessary stuff to be created (buffers, textures etc)
            self.render_frame()
            
        return output

    def add_to_output_gsg_lists(self, output):
        """ Adds the output and Gsg to the instance's running list. """
        self.output_list.append(output)
        self.gsg_list.append(output.getGsg())

    def remove_from_output_gsg_lists(self, output):
        """ Removes the output and Gsg from the instance's running list. """
        self.output_list.remove(output)
        self.gsg_list.remove(output.getGsg())

    @staticmethod
    def add_render_texture(output, mode=None, bitplane=None):
        """ Similar to GraphicsOutput's addRenderTexture. 

        ** Possible mode values **
        GraphicsOutput.RTMNone
        GraphicsOutput.RTMBindOrCopy
        GraphicsOutput.RTMCopyTexture
        GraphicsOutput.RTMCopyRam
        GraphicsOutput.RTMTriggeredCopyTexture
        GraphicsOutput.RTMTriggeredCopyRam

        ** Possible bitplane values **
        GraphicsOutput.RTPStencil 	
        GraphicsOutput.RTPDepthStencil 	
        GraphicsOutput.RTPColor 	
        GraphicsOutput.RTPAuxRgba0 	
        GraphicsOutput.RTPAuxRgba1 	
        GraphicsOutput.RTPAuxRgba2 	
        GraphicsOutput.RTPAuxRgba3 	
        GraphicsOutput.RTPAuxHrgba0 	
        GraphicsOutput.RTPAuxHrgba1 	
        GraphicsOutput.RTPAuxHrgba2 	
        GraphicsOutput.RTPAuxHrgba3 	
        GraphicsOutput.RTPAuxFloat0 	
        GraphicsOutput.RTPAuxFloat1 	
        GraphicsOutput.RTPAuxFloat2 	
        GraphicsOutput.RTPAuxFloat3 	
        GraphicsOutput.RTPDepth 	
        GraphicsOutput.RTPCOUNT

        """

        if mode is None:
            mode = GraphicsOutput.RTMBindOrCopy
        elif isinstance(mode, str):
            mode = getattr(GraphicsOutput, mode)

        if bitplane is None:
            bitplane = GraphicsOutput.RTPColor
        elif isinstance(bitplane, str):
            bitplane = getattr(GraphicsOutput, bitplane)

        tex = Texture()
        tex.setFormat(Texture.FLuminance)
            
        # Add the texture to the buffer
        output.addRenderTexture(tex, mode, bitplane)
        # Get a handle to the texture
        assert tex == output.getTexture(), "Texture wasn't created properly."

        return tex

    def close_output(self, output):
        """
        Closes the indicated output and removes it from the list. 
        """
        output.setActive(False)
        
        # First, remove all of the cameras associated with display
        # regions on the window.
        num_regions = output.getNumDisplayRegions()        
        for i in range(num_regions):
            dr = output.getDisplayRegion(i)                   
            dr.setCamera(NodePath())

        # Remove this output from the list
        self.remove_from_output_gsg_lists(output)
        # Now we can actually close the window.
        engine = output.getEngine()
        engine.removeWindow(output)
        # Give the window a chance to actually close before continuing.
        engine.renderFrame()   

    def close_all_outputs(self):
        """ Closes all of this instance's outputs """

        # Close them each
        for output in self.output_list:
            self.close_output(output)
        # Clear the output list
        self.output_list = []
        
    def make_camera(self, output, sort=0, dr_dims=(0, 1, 0, 1),
                    aspect_ratio=None, clear_depth=False, clear_color=None,
                    lens=None, cam_name='camera0', mask=None):
        """
        Makes a new 3-d camera associated with the indicated window,
        and creates a display region in the indicated subrectangle.

        If stereo is True, then a stereo camera is created, with a
        pair of DisplayRegions.  If stereo is False, then a standard
        camera is created.  If stereo is None or omitted, a stereo
        camera is created if the window says it can render in stereo.

        If useCamera is not None, it is a NodePath to be used as the
        camera to apply to the window, rather than creating a new
        camera.
        """
        
        # self.cameras is the parent node of all cameras: a node that
        # we can move around to move all cameras as a group.
        if self.cameras is None:
            # We make it a ModelNode with the PTLocal flag, so that a
            # wayward flatten operations won't attempt to mangle the
            # camera.
            self.cameras = self.rootnode.attachNewNode(ModelNode('cameras'))
            self.cameras.node().setPreserveTransform(ModelNode.PTLocal)

        # Make a new Camera node.
        cam_node = Camera(cam_name)
        if lens is None:
            lens = PerspectiveLens()
            if aspect_ratio is None:
                aspect_ratio = self.get_aspect_ratio(output)
            lens.setAspectRatio(aspect_ratio)
            lens.setNear(0.1)
            lens.setFar(1000.0)
        if lens is not None:
            cam_node.setLens(lens)
        camera = self.cameras.attachNewNode(cam_node)

        # Masks out part of scene from camera
        if mask is not None:
            if (isinstance(mask, int)):
                mask = BitMask32(mask)
            cam_node.setCameraMask(mask)

        # Make a display region
        dr = output.makeDisplayRegion(*dr_dims)
        # By default, we do not clear 3-d display regions (the entire
        # window will be cleared, which is normally sufficient).  But
        # we will if clearDepth is specified.
        if clear_depth:
            dr.setClearDepthActive(1)          
        if clear_color:
            dr.setClearColorActive(1)
            dr.setClearColor(clear_color)
        dr.setSort(sort)
        dr.setCamera(camera)
        dr.setActive(True)
        return camera

    @staticmethod
    def get_aspect_ratio(output):
        """ Returns the actual aspect ratio of the indicated (or main
         window), or the default aspect ratio if there is not yet a
         main window."""

        aspect_ratio = 1
        if output.hasSize():
            aspect_ratio = float(output.getSbsLeftXSize()) / \
                           float(output.getSbsLeftYSize())
        else:
            wp = output.getRequestedProperties()
            if not wp.hasSize():
                wp = WindowProperties.getDefault()
            aspect_ratio = float(wp.getXSize()) / float(wp.getYSize())
        return aspect_ratio

    @staticmethod
    def make_lights(light_spec=None, lname=None):
        """ Create one point light and an ambient light."""
        if lname is None:
            lname = 'lights'
        lights = NodePath(lname)

        if light_spec is None:
            # Create point lights
            plight = PointLight('plight1')
            light = lights.attachNewNode(plight)
            plight.setColor((0.1, 0.1, 0.1, 1.0))
            light.setPos((-2, -6, 4))
            light.lookAt(0, 0, 0)

        # Create ambient light
            alight = AmbientLight('alight')
            alight.setColor((1, 1, 1, 1.0))
            lights.attachNewNode(alight)
        else:
            for lspec in light_spec:
                light_type = lspec['type']
                light_name = lspec['name']
                assert light_type in ['AmbientLight', 'PointLight',  'DirectionalLight', 'SpotLight'], light_type
                if light_type == 'AmbientLight':
                    light = AmbientLight(light_name)
                    light.setColor(lspec['color'])
                    lights.attachNewNode(light)
                elif light_type == 'PointLight':
                    light = PointLight(light_name)
                    lights.attachNewNode(light)
                    light.setColor(lspec['color'])
                    light.setPoint(lspec['pos'])
                elif light_type == 'DirectionalLight':
                    light = DirectionalLight(light_name)
                    lights.attachNewNode(light)
                    light.setColor(lspec['color'])
                    light.setPoint(lspec['pos'])
                    light.setDirection(lspec['direction'])
                    

        return lights

    def setup_rootnode(self):
        """
        Creates the rootnode scene graph, the primary scene graph for
        rendering 3-d geometry.
        """
        self.rootnode = NodePath('rootnode')
        self.rootnode.setAttrib(RescaleNormalAttrib.makeDefault())
        self.rootnode.setTwoSided(0)
        self.backface_culling_enabled = 0
        self.texture_enabled = 1
        self.wireframe_enabled = 0

    def wireframe_on(self):
        """ Toggle wireframe mode on."""
        self.rootnode.setRenderModeWireframe(100)
        self.rootnode.setTwoSided(1)
        self.wireframe_enabled = 1

    def wireframe_off(self):
        """ Toggle wireframe mode off."""
        self.rootnode.clearRenderMode()
        self.rootnode.setTwoSided(not self.backface_culling_enabled)
        self.wireframe_enabled = 0

    @staticmethod
    def trigger_copy(output):
        """ This signals the texture to be pushed to RAM after the
        next renderFrame"""
        output.trigger_copy()

    def render_frame(self):
        """ Render the frame."""
        #self.engine.readyFlip()
        self.engine.renderFrame()
        # If you're trying to read the texture buffer, remember to
        # call self.triggerCopy()

    @staticmethod
    def get_tex_image(tex, freshape=True):
        """ Returns numpy arr containing image in tex """
        
        # Remember to call self.triggerCopy() before
        # self.renderFrame(), or the next frame won't be pushed to RAM

        if not tex.hasRamImage():
            arr = None
        else:
            size = (tex.getXSize(), tex.getYSize())
            texdata = tex.getRamImage().getData()
            arr = np.fromstring(texdata, 'u1')

            if freshape:
                arr = np.reshape(arr, list(size) + [-1])[:, :, [2, 1, 0, 3]]

        return arr

    @staticmethod
    def screenshot(output, pth=None):
        """ Similar to ShowBase's screenshot """
        if pth is None:
            filename = GraphicsOutput.makeScreenshotFilename()
        else:
            filename = Filename(pth)

        if isinstance(output, GraphicsOutput):
            f_success = output.saveScreenshot(filename)
        elif isinstance(output, Texture):
            if output.getZSize() > 1:
                f_success = output.write(filename, 0, 0, 1, 0)
            else:
                f_success = output.write(filename)
        else:
            raise TypeError('Unhandled output type: ' + type(output))

        return f_success
    
    def destroy(self):
        """ self.__exitfunc() calls this automatically """
        self.close_all_outputs()
        if getattr(self, 'loader', None):
            self.loader.destroy()
            self.loader = None
        if getattr(self, 'engine', None):
            self.engine.removeAllWindows()
            self.engine = None
        if getattr(self, 'pipe', None):
            self.pipe = None
            
    def __exitfunc(self):
        """ Customize pre-exit commands."""
        self.destroy()

    def user_exit(self):
        """ The user has requested we exit the program. """
        self.__exitfunc()
        sys.exit()
        
    @classmethod
    def destroy_windows(cls):
        """ General destroy windows method."""
        GraphicsEngine.getGlobalPtr().removeAllWindows()
예제 #4
0
파일: water.py 프로젝트: figo-sh/naith
    def __init__(self, manager, xml):
        self.surface = getWaterSurface(manager)
        self.surface.reparentTo(render)
        self.surface.hide(BitMask32.bit(1))  # Invisible to reflection camera (speedup)
        self.surface.hide(BitMask32.bit(2))  # Invisible to volumetric lighting camera (speedup)
        self.surface.hide(BitMask32.bit(3))  # Invisible to shadow cameras (speedup)
        self.surface.setShader(
            loader.loadShader(manager.get("paths").getConfig().find("shaders").get("path") + "/water.cg")
        )
        self.surface.setShaderInput("time", 0.0, 0.0, 0.0, 0.0)
        ntex = loader.loadTexture(manager.get("paths").getConfig().find("textures").get("path") + "/water-normal.png")
        ntex.setMinfilter(Texture.FTLinearMipmapLinear)
        self.surface.setShaderInput("normal", ntex)
        self.surface.setShaderInput("camera", base.cam)
        self.surface.setTransparency(TransparencyAttrib.MDual, 10)
        self.surface.setTwoSided(True)

        self.surface.setShaderInput("waveInfo", Vec4(0.4, 0.4, 0.4, 0))
        self.surface.setShaderInput("param2", Vec4(-0.015, 0.005, 0.05, 0.05))
        self.surface.setShaderInput("param3", Vec4(0.7, 0.3, 0, 0))
        self.surface.setShaderInput("param4", Vec4(2.0, 0.5, 0.5, 0.0))
        # self.surface.setShaderInput('speed', Vec4(-.8, -.4, -.9, .3))
        self.surface.setShaderInput("speed", Vec4(0.2, -1.2, -0.2, -0.7))
        self.surface.setShaderInput("deepcolor", Vec4(0.0, 0.3, 0.5, 1.0))
        self.surface.setShaderInput("shallowcolor", Vec4(0.0, 1.0, 1.0, 1.0))
        self.surface.setShaderInput("reflectioncolor", Vec4(0.95, 1.0, 1.0, 1.0))
        self.surface.hide()

        self.wbuffer = base.win.makeTextureBuffer("water", 512, 512)
        self.wbuffer.setClearColorActive(True)
        self.wbuffer.setClearColor(base.win.getClearColor())
        self.wcamera = base.makeCamera(self.wbuffer)
        if manager.get("sky") != None and manager.get("sky").model != None:
            self.sky = manager.get("sky").model.copyTo(self.wcamera)
            self.sky.setTwoSided(True)
            self.sky.setSz(self.sky, -1)
            self.sky.setClipPlaneOff(1)
            self.sky.show()
            self.sky.hide(BitMask32.bit(0))  # Hide for normal camera
            self.sky.hide(BitMask32.bit(2))  # Hide for volumetric lighting camera
            self.sky.hide(BitMask32.bit(3))  # Hide for shadow camera(s), if any
        else:
            self.sky = None
        self.wcamera.reparentTo(render)
        self.wcamera.node().setLens(base.camLens)
        self.wcamera.node().setCameraMask(BitMask32.bit(1))
        self.surface.hide(BitMask32.bit(1))
        wtexture = self.wbuffer.getTexture()
        wtexture.setWrapU(Texture.WMClamp)
        wtexture.setWrapV(Texture.WMClamp)
        wtexture.setMinfilter(Texture.FTLinearMipmapLinear)
        self.surface.setShaderInput("reflection", wtexture)
        self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))
        self.wplanenp = render.attachNewNode(PlaneNode("water", self.wplane))
        tmpnp = NodePath("StateInitializer")
        tmpnp.setClipPlane(self.wplanenp)
        tmpnp.setAttrib(CullFaceAttrib.makeReverse())
        self.wcamera.node().setInitialState(tmpnp.getState())

        # self.fog = Fog('UnderwaterFog')
        # self.fog.setColor(0.0,0.3,0.5)
        self.fogEnabled = False
class FishManager:
    notify = DirectNotifyGlobal.directNotify.newCategory("FishManager")

    def __init__(self, gameObject=None):
        base.loadingScreen.beginStep("FishManager", 2, 20)
        self.gameObject = gameObject
        if self.gameObject.distributedFishingSpot.onABoat:
            self.location = "ship"
        else:
            self.location = "dock"
        self.uncaughtFish = []
        self.caughtFish = 0
        self.deadFish = []
        self.activeFish = None
        self.specialFishIndex = 0
        self.fishHistogram = {}
        for fish in FishingGlobals.allFishData:
            self.fishHistogram[fish["id"]] = [0, fish["maxPossiblePerScene"]]

        self.hiddenFish = []
        self.legendaryFishPool = []
        self.hideNormalFishSequence = Sequence()
        self.objectsWithCaustics = NodePath("objectsWithCaustics")
        self.objectsWithCaustics.reparentTo(self.gameObject.fishingSpot)
        self.causticsTexture = loader.loadTexture("maps/pir_t_gam_fsh_caustics.jpg")
        self.causticsShader = loader.loadShader("models/shaders/fishingCausticsShader.sha")
        if base.win and base.win.getGsg():
            intShaders = base.win.getGsg().getSupportsBasicShaders()
        else:
            intShaders = 0
        if intShaders > 0:
            self.doShaders()
        else:
            self.doFixedFunction()
        if base.win and base.win.getGsg():
            shaderModel = base.win.getGsg().getShaderModel()

        base.loadingScreen.tick()
        self.loadFish()
        base.loadingScreen.endStep("FishManager")

    def loadFish(self):
        rodSize = self.gameObject.getAvatarsBestRod()
        for depth in range(len(FishingGlobals.fishCountRangePerRodPerLevel[rodSize])):
            numberOfFishForThisLevel = random.randint(
                FishingGlobals.fishCountRangePerRodPerLevel[rodSize][depth][0],
                FishingGlobals.fishCountRangePerRodPerLevel[rodSize][depth][1],
            )
            for fishIndex in range(numberOfFishForThisLevel):
                fishData = FishingGlobals.giveMeAFish(self.location, depth, self.fishHistogram)
                f = Fish(self, fishData, len(self.uncaughtFish))
                self.fishHistogram[f.myData["id"]][0] += 1
                f.fsm.request("Offscreen")
                self.uncaughtFish.append(f)

            self.legendaryFish = None

    def doShaders(self):
        self.objectsWithCaustics.setShader(self.causticsShader)
        self.objectsWithCaustics.setShaderInput("caustics", self.causticsTexture)
        self.objectsWithCaustics.setShaderInput(
            "causticsParams", Vec4(5.3300000000000001, 0.0234325112498, 0.010589092533, 0.20000000000000001)
        )
        self.objectsWithCaustics.setShaderInput(
            "causticsParams2",
            Vec4(2.8795310000000001, -0.035686130000000003, 0.027589099999999998, 0.20000000000000001),
        )
        self.objectsWithCaustics.setShaderInput(
            "causticsParams3", Vec4(3.7854611, 0.037037029999999999, 0.047619047619047616, 0.20000000000000001)
        )
        self.objectsWithCaustics.setShaderInput(
            "fogInfo", Vec4(0.0, FishingGlobals.fogDarkness, 0.69999999999999996, 0.0)
        )
        self.objectsWithCaustics.setShaderInput("fogColor", FishingGlobals.waterFogColor)
        self.objectsWithCaustics.setShaderInput(
            "causticsToWorldScale", Vec4(1.0 / 30.100000000000001, 1.0 / 30.100000000000001, 0.0, 0.0)
        )
        self.objectsWithCaustics.setShaderInput("sunWorldLocation", Vec4(0.0, 0.0, 100.0, 1.0))

    def doFixedFunction(self):
        fishFog = Fog("fishFogNode")
        fishFog.setMode(Fog.MLinear)
        fishFog.setColor(FishingGlobals.waterFogColor)
        fishFog.setLinearRange(0.0, 200.0)
        fishFogAttrib = FogAttrib.make(fishFog)
        self.objectsWithCaustics.setAttrib(fishFogAttrib)

    def codeReload(self):
        for fish in self.uncaughtFish:
            print fish.codeReload()

    def scareAwayNormalFish(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != "ScareAway":
                if fish.movingRight and fish.fsm.getCurrentOrNextState() != "TurnAround":
                    fish.fsm.request("TurnAround", "ScareAway", False)
                else:
                    fish.fsm.request("ScareAway")
            fish.fsm.getCurrentOrNextState() != "TurnAround"

    def startLegendaryFish(self, whichFish=None):
        self.notify.debug("start LegendaryFish %s" % str(whichFish))
        fishData = FishingGlobals.getALegendaryFish(whichFish)
        self.legendaryFish = LegendaryFish(self, fishData, 0)
        self.activeFish = self.legendaryFish
        self.legendaryFish.fsm.request("AboutToBiteLure")

    def replaceFish(self, fish):
        depth = fish.myData["depth"]
        index = fish.index
        self.fishHistogram[fish.myData["id"]][0] -= 1
        self.uncaughtFish.remove(fish)
        fish.destroy()
        fishData = FishingGlobals.giveMeAFish(self.location, depth, self.fishHistogram)
        f = Fish(self, fishData, index)
        f.fsm.request("Offscreen")
        self.fishHistogram[f.myData["id"]][0] += 1
        f.pickPositionAndSwim()
        self.uncaughtFish.append(f)

    def destroy(self):
        for fish in self.uncaughtFish:
            fish.destroy()

        self.uncaughtFish = []
        if self.legendaryFish:
            self.legendaryFish.destroy()

        self.legendaryFish = None

    def moveFish(self, direction):
        self.shutdown()
        self.specialFishIndex += direction
        self.specialFishIndex = max(min(len(FishingGlobals.allFishData) - 1, self.specialFishIndex), 0)
        self.startup()

    def startup(self):
        for fish in self.uncaughtFish:
            fish.pickPositionAndSwim()

    def shutdown(self):
        self.activeFish = None
        self.caughtFish = 0
        self.deadFish = []
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != "Offscreen":
                fish.fsm.request("Offscreen")
                continue

    def reset(self):
        if self.activeFish and self.activeFish.fsm and self.activeFish.fsm.getCurrentOrNextState() != "Offscreen":
            self.activeFish.fsm.request("Offscreen")

        self.activeFish = None
        self.deadFish = []
        self.loseInterest()

    def update(self, dt):
        lurePos = self.gameObject.lure.getPos()
        for i in range(len(self.uncaughtFish)):
            self.uncaughtFish[i].update(dt, i, lurePos)

        for fish in self.deadFish:
            self.uncaughtFish.remove(fish)
            fish.destroy()

        self.deadFish = []

    def turnAllFish(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != "TurnAround":
                fish.fsm.request("TurnAround", "Swimming", not (fish.movingRight))
                continue

    def enterReeling(self):
        pass

    def reloadCollisions(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                for fishData in FishingGlobals.allFishData:
                    if fish.myData["model"] == fishData["model"]:
                        fish.myData = fishData
                        fish.reloadCollisions()
                        continue
                        continue

    def loseInterest(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() == "Biting":
                fish.fsm.request("Swimming")
                continue

    def showAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.showAvoidanceCollisionVisuals()

    def hideAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.hideAvoidanceCollisionVisuals()

    def showAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.showAttractionCollisionVisuals()

    def hideAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.hideAttractionCollisionVisuals()
예제 #6
0
    def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None):

        """ Causes the scene to be rendered into the supplied textures
        instead of into the original window.  Puts a fullscreen quad
        into the original window to show the render-to-texture results.
        Returns the quad.  Normally, the caller would then apply a
        shader to the quad.

        To elaborate on how this all works:

        * An offscreen buffer is created.  It is set up to mimic
          the original display region - it is the same size,
          uses the same clear colors, and contains a DisplayRegion
          that uses the original camera.

        * A fullscreen quad and an orthographic camera to render
          that quad are both created.  The original camera is
          removed from the original window, and in its place, the
          orthographic quad-camera is installed.

        * The fullscreen quad is textured with the data from the
          offscreen buffer.  A shader is applied that tints the
          results pink.

        * Automatic shader generation NOT enabled.
          If you have a filter that depends on a render target from
          the auto-shader, you either need to set an auto-shader
          attrib on the main camera or scene, or, you need to provide
          these outputs in your own shader.

        * All clears are disabled on the original display region.
          If the display region fills the whole window, then clears
          are disabled on the original window as well.  It is
          assumed that rendering the full-screen quad eliminates
          the need to do clears.

        Hence, the original window which used to contain the actual
        scene, now contains a pink-tinted quad with a texture of the
        scene.  It is assumed that the user will replace the shader
        on the quad with a more interesting filter. """

        if (textures):
            colortex = textures.get("color", None)
            depthtex = textures.get("depth", None)
            auxtex = textures.get("aux", None)

        if (colortex == None):
            colortex = Texture("filter-base-color")
            colortex.setWrapU(Texture.WMClamp)
            colortex.setWrapV(Texture.WMClamp)

        texgroup = (depthtex, colortex, auxtex, None)

        # Choose the size of the offscreen buffer.

        (winx, winy) = self.getScaledSize(1,1,1)
        buffer = self.createBuffer("filter-base", winx, winy, texgroup)

        if (buffer == None):
            return None

        cm = CardMaker("filter-base-quad")
        cm.setFrameFullscreenQuad()
        quad = NodePath(cm.generate())
        quad.setDepthTest(0)
        quad.setDepthWrite(0)
        quad.setTexture(colortex)
        quad.setColor(Vec4(1,0.5,0.5,1))

        cs = NodePath("dummy")
        cs.setState(self.camstate)
        # Do we really need to turn on the Shader Generator?
        #cs.setShaderAuto()
        if (auxbits):
            cs.setAttrib(AuxBitplaneAttrib.make(auxbits))
        self.camera.node().setInitialState(cs.getState())

        quadcamnode = Camera("filter-quad-cam")
        lens = OrthographicLens()
        lens.setFilmSize(2, 2)
        lens.setFilmOffset(0, 0)
        lens.setNearFar(-1000, 1000)
        quadcamnode.setLens(lens)
        quadcam = quad.attachNewNode(quadcamnode)
        
        self.region.setCamera(quadcam)

        dr = buffer.getDisplayRegion(0)
        self.setStackedClears(dr, self.rclears, self.wclears)
        if (auxtex):
            dr.setClearActive(GraphicsOutput.RTPAuxRgba0, 1)
            dr.setClearValue(GraphicsOutput.RTPAuxRgba0, Vec4(0.5,0.5,1.0,0.0))
        self.region.disableClears()
        if (self.isFullscreen()):
            self.win.disableClears()
        dr.setCamera(self.camera)
        dr.setActive(1)

        self.buffers.append(buffer)
        self.sizes.append((1, 1, 1))

        return quad
예제 #7
0
    def renderSceneInto(self, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None):

        """ Causes the scene to be rendered into the supplied textures
        instead of into the original window.  Puts a fullscreen quad
        into the original window to show the render-to-texture results.
        Returns the quad.  Normally, the caller would then apply a
        shader to the quad.

        To elaborate on how this all works:

        * An offscreen buffer is created.  It is set up to mimic
          the original display region - it is the same size,
          uses the same clear colors, and contains a DisplayRegion
          that uses the original camera.

        * A fullscreen quad and an orthographic camera to render
          that quad are both created.  The original camera is
          removed from the original window, and in its place, the
          orthographic quad-camera is installed.

        * The fullscreen quad is textured with the data from the
          offscreen buffer.  A shader is applied that tints the
          results pink.

        * Automatic shader generation NOT enabled.
          If you have a filter that depends on a render target from
          the auto-shader, you either need to set an auto-shader
          attrib on the main camera or scene, or, you need to provide
          these outputs in your own shader.

        * All clears are disabled on the original display region.
          If the display region fills the whole window, then clears
          are disabled on the original window as well.  It is
          assumed that rendering the full-screen quad eliminates
          the need to do clears.

        Hence, the original window which used to contain the actual
        scene, now contains a pink-tinted quad with a texture of the
        scene.  It is assumed that the user will replace the shader
        on the quad with a more interesting filter. """

        if (textures):
            colortex = textures.get("color", None)
            depthtex = textures.get("depth", None)
            auxtex = textures.get("aux", None)
            auxtex0 = textures.get("aux0", auxtex)
            auxtex1 = textures.get("aux1", None)
        else:
            auxtex0 = auxtex
            auxtex1 = None

        if (colortex == None):
            colortex = Texture("filter-base-color")
            colortex.setWrapU(Texture.WMClamp)
            colortex.setWrapV(Texture.WMClamp)

        texgroup = (depthtex, colortex, auxtex0, auxtex1)

        # Choose the size of the offscreen buffer.

        (winx, winy) = self.getScaledSize(1,1,1)
        buffer = self.createBuffer("filter-base", winx, winy, texgroup)

        if (buffer == None):
            return None

        cm = CardMaker("filter-base-quad")
        cm.setFrameFullscreenQuad()
        quad = NodePath(cm.generate())
        quad.setDepthTest(0)
        quad.setDepthWrite(0)
        quad.setTexture(colortex)
        quad.setColor(Vec4(1,0.5,0.5,1))

        cs = NodePath("dummy")
        cs.setState(self.camstate)
        # Do we really need to turn on the Shader Generator?
        #cs.setShaderAuto()
        if (auxbits):
            cs.setAttrib(AuxBitplaneAttrib.make(auxbits))
        self.camera.node().setInitialState(cs.getState())

        quadcamnode = Camera("filter-quad-cam")
        lens = OrthographicLens()
        lens.setFilmSize(2, 2)
        lens.setFilmOffset(0, 0)
        lens.setNearFar(-1000, 1000)
        quadcamnode.setLens(lens)
        quadcam = quad.attachNewNode(quadcamnode)
        
        self.region.setCamera(quadcam)

        self.setStackedClears(buffer, self.rclears, self.wclears)
        if (auxtex0):
            buffer.setClearActive(GraphicsOutput.RTPAuxRgba0, 1)
            buffer.setClearValue(GraphicsOutput.RTPAuxRgba0, Vec4(0.5, 0.5, 1.0, 0.0))
        if (auxtex1):
            buffer.setClearActive(GraphicsOutput.RTPAuxRgba1, 1)
        self.region.disableClears()
        if (self.isFullscreen()):
            self.win.disableClears()

        dr = buffer.makeDisplayRegion()
        dr.disableClears()
        dr.setCamera(self.camera)
        dr.setActive(1)

        self.buffers.append(buffer)
        self.sizes.append((1, 1, 1))

        return quad
예제 #8
0
class FishManager:
    __module__ = __name__
    notify = DirectNotifyGlobal.directNotify.newCategory('FishManager')

    def __init__(self, gameObject=None):
        base.loadingScreen.beginStep('FishManager', 2, 20)
        self.gameObject = gameObject
        if self.gameObject.distributedFishingSpot.onABoat:
            self.location = 'ship'
        else:
            self.location = 'dock'
        self.uncaughtFish = []
        self.caughtFish = 0
        self.deadFish = []
        self.activeFish = None
        self.specialFishIndex = 0
        self.fishHistogram = {}
        for fish in FishingGlobals.allFishData:
            self.fishHistogram[fish['id']] = [0, fish['maxPossiblePerScene']]

        self.hiddenFish = []
        self.legendaryFishPool = []
        self.hideNormalFishSequence = Sequence()
        self.objectsWithCaustics = NodePath('objectsWithCaustics')
        self.objectsWithCaustics.reparentTo(self.gameObject.fishingSpot)
        self.causticsTexture = loader.loadTexture(
            'maps/pir_t_gam_fsh_caustics.jpg')
        self.causticsShader = loader.loadShader(
            'models/shaders/fishingCausticsShader.sha')
        if base.win:
            if base.win.getGsg():
                intShaders = base.win.getGsg().getSupportsBasicShaders()
            else:
                intShaders = 0
            intShaders > 0 and self.doShaders()
        else:
            self.doFixedFunction()
        if base.win and base.win.getGsg():
            shaderModel = base.win.getGsg().getShaderModel()
        base.loadingScreen.tick()
        self.loadFish()
        base.loadingScreen.endStep('FishManager')
        return

    def loadFish(self):
        rodSize = self.gameObject.getAvatarsBestRod()
        for depth in range(
                len(FishingGlobals.fishCountRangePerRodPerLevel[rodSize])):
            numberOfFishForThisLevel = random.randint(
                FishingGlobals.fishCountRangePerRodPerLevel[rodSize][depth][0],
                FishingGlobals.fishCountRangePerRodPerLevel[rodSize][depth][1])
            for fishIndex in range(numberOfFishForThisLevel):
                fishData = FishingGlobals.giveMeAFish(self.location, depth,
                                                      self.fishHistogram)
                f = Fish(self, fishData, len(self.uncaughtFish))
                self.fishHistogram[f.myData['id']][0] += 1
                f.fsm.request('Offscreen')
                self.uncaughtFish.append(f)

            self.legendaryFish = None

        return

    def doShaders(self):
        self.objectsWithCaustics.setShader(self.causticsShader)
        self.objectsWithCaustics.setShaderInput('caustics',
                                                self.causticsTexture)
        self.objectsWithCaustics.setShaderInput(
            'causticsParams', Vec4(5.33, 0.0234325112498, 0.010589092533, 0.2))
        self.objectsWithCaustics.setShaderInput(
            'causticsParams2', Vec4(2.879531, -0.03568613, 0.0275891, 0.2))
        self.objectsWithCaustics.setShaderInput(
            'causticsParams3',
            Vec4(3.7854611, 0.03703703, 0.047619047619047616, 0.2))
        self.objectsWithCaustics.setShaderInput(
            'fogInfo', Vec4(0.0, FishingGlobals.fogDarkness, 0.7, 0.0))
        self.objectsWithCaustics.setShaderInput('fogColor',
                                                FishingGlobals.waterFogColor)
        self.objectsWithCaustics.setShaderInput(
            'causticsToWorldScale', Vec4(1.0 / 30.1, 1.0 / 30.1, 0.0, 0.0))
        self.objectsWithCaustics.setShaderInput('sunWorldLocation',
                                                Vec4(0.0, 0.0, 100.0, 1.0))

    def doFixedFunction(self):
        fishFog = Fog('fishFogNode')
        fishFog.setMode(Fog.MLinear)
        fishFog.setColor(FishingGlobals.waterFogColor)
        fishFog.setLinearRange(0.0, 200.0)
        fishFogAttrib = FogAttrib.make(fishFog)
        self.objectsWithCaustics.setAttrib(fishFogAttrib)

    def codeReload(self):
        for fish in self.uncaughtFish:
            print fish.codeReload()

    def scareAwayNormalFish(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != 'ScareAway':
                if fish.movingRight:
                    fish.fsm.getCurrentOrNextState(
                    ) != 'TurnAround' and fish.fsm.request(
                        'TurnAround', 'ScareAway', False)
                else:
                    fish.fsm.request('ScareAway')

    def startLegendaryFish(self, whichFish=None):
        self.notify.debug('start LegendaryFish %s' % str(whichFish))
        fishData = FishingGlobals.getALegendaryFish(whichFish)
        self.legendaryFish = LegendaryFish(self, fishData, 0)
        self.activeFish = self.legendaryFish
        self.legendaryFish.fsm.request('AboutToBiteLure')

    def replaceFish(self, fish):
        depth = fish.myData['depth']
        index = fish.index
        self.fishHistogram[fish.myData['id']][0] -= 1
        self.uncaughtFish.remove(fish)
        fish.destroy()
        fishData = FishingGlobals.giveMeAFish(self.location, depth,
                                              self.fishHistogram)
        f = Fish(self, fishData, index)
        f.fsm.request('Offscreen')
        self.fishHistogram[f.myData['id']][0] += 1
        f.pickPositionAndSwim()
        self.uncaughtFish.append(f)

    def destroy(self):
        for fish in self.uncaughtFish:
            fish.destroy()

        self.uncaughtFish = []
        if self.legendaryFish:
            self.legendaryFish.destroy()
        self.legendaryFish = None
        return

    def moveFish(self, direction):
        self.shutdown()
        self.specialFishIndex += direction
        self.specialFishIndex = max(
            min(len(FishingGlobals.allFishData) - 1, self.specialFishIndex), 0)
        self.startup()

    def startup(self):
        for fish in self.uncaughtFish:
            fish.pickPositionAndSwim()

    def shutdown(self):
        self.activeFish = None
        self.caughtFish = 0
        self.deadFish = []
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != 'Offscreen':
                fish.fsm.request('Offscreen')

        return

    def reset(self):
        if self.activeFish and self.activeFish.fsm and self.activeFish.fsm.getCurrentOrNextState(
        ) != 'Offscreen':
            self.activeFish.fsm.request('Offscreen')
        self.activeFish = None
        self.deadFish = []
        self.loseInterest()
        return

    def update(self, dt):
        lurePos = self.gameObject.lure.getPos()
        for i in range(len(self.uncaughtFish)):
            self.uncaughtFish[i].update(dt, i, lurePos)

        for fish in self.deadFish:
            self.uncaughtFish.remove(fish)
            fish.destroy()

        self.deadFish = []

    def turnAllFish(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() != 'TurnAround':
                fish.fsm.request('TurnAround', 'Swimming',
                                 not fish.movingRight)

    def enterReeling(self):
        pass

    def reloadCollisions(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                for fishData in FishingGlobals.allFishData:
                    if fish.myData['model'] == fishData['model']:
                        fish.myData = fishData
                        fish.reloadCollisions()
                        continue

    def loseInterest(self):
        for fish in self.uncaughtFish:
            if fish.fsm.getCurrentOrNextState() == 'Biting':
                fish.fsm.request('Swimming')

    def showAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.showAvoidanceCollisionVisuals()

    def hideAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.hideAvoidanceCollisionVisuals()

    def showAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.showAttractionCollisionVisuals()

    def hideAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            for fish in self.uncaughtFish:
                fish.hideAttractionCollisionVisuals()