Пример #1
0
def splatting(node, first, second, stencil, scale=None, offset=None):
    """Apply a texture splatting to the provided NodePath.
    """
    # Apply the first texture.
    ts1 = TextureStage("stage-first")
    ts1.setSort(0)
    ts1.setMode(TextureStage.MReplace)
    ts1.setSavedResult(True)
    node.setTexture(ts1, first)
    # Apply the second texture.
    ts2 = TextureStage("stage-second")
    ts2.setSort(1)
    ts2.setMode(TextureStage.MReplace)
    node.setTexture(ts2, second)
    # Apply the stencil.
    ts3 = TextureStage("stage-stencil")
    ts3.setSort(2)
    ts3.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSPrevious,
                      TextureStage.COSrcColor, TextureStage.CSLastSavedResult,
                      TextureStage.COSrcColor, TextureStage.CSTexture,
                      TextureStage.COSrcColor)
    node.setTexture(ts3, stencil)
    if scale: node.setTexScale(ts3, scale, scale)
    if offset is not None:
        node.setTexOffset(ts1, *offset)
        node.setTexOffset(ts2, *offset)
        node.setTexOffset(ts3, *offset)
Пример #2
0
    def apply_splatted_textures(self, tile: NodePath, first_tex, second_tex,
                                stencil_tex):
        # first = self.load("textures/sand_tex_1.png")
        # second = self.load("textures/grass_tex_1.png")
        # third = self.load("textures/water_tex_1.png")
        # stencil = self.load("textures/stencil_tex_1.png")
        # stencil_2 = self.load("textures/stencil_tex_2.png")
        # # normal = self.load("textures/sea-normal.jpg")
        # normal = self.loader.load_texture("textures/layingrock-n.jpg")

        # Apply the first texture.
        ts1 = TextureStage("stage-first")
        ts1.setSort(0)
        ts1.setMode(TextureStage.MReplace)
        ts1.setSavedResult(True)
        tile.setTexture(ts1, first_tex)
        # Apply the second texture.
        ts2 = TextureStage("stage-second")
        ts2.setSort(1)
        ts2.setMode(TextureStage.MReplace)
        tile.setTexture(ts2, second_tex)
        ts3 = TextureStage("stage-stencil")
        ts3.setSort(2)
        ts3.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSPrevious,
                          TextureStage.COSrcColor,
                          TextureStage.CSLastSavedResult,
                          TextureStage.COSrcColor, TextureStage.CSTexture,
                          TextureStage.COSrcColor)
        ts3.setSavedResult(True)
        tile.setTexture(ts3, stencil_tex)
Пример #3
0
    def __create_terrain(self):
        terrain = GeoMipTerrain("Terrain")
        terrain.setHeightfield(self.__texture_path(self.__scene.get("scene", "heightmap")))
        terrain.getRoot().reparentTo(self.render)
        terrain.generate()
        terrain.getRoot().setSx(1000.0 / 512)
        terrain.getRoot().setSy(1000.0 / 512)
        terrain.getRoot().setSz(74)
        terrain.getRoot().setPos(-500, -500, 0)

        black = self.loader.loadTexture(self.__texture_path(self.__scene.get("terrain", "black")))
        black.setMinfilter(Texture.FTLinearMipmapNearest)
        ts = TextureStage("stage-first")
        ts.setSort(0)
        ts.setMode(TextureStage.MReplace)
        ts.setSavedResult(True)
        terrain.getRoot().setTexture(ts, black)
        terrain.getRoot().setTexScale(ts, 250, 250)

        white = self.loader.loadTexture(self.__texture_path(self.__scene.get("terrain", "white")))
        white.setMinfilter(Texture.FTLinearMipmapNearest)
        ts = TextureStage("stage-second")
        ts.setSort(1)
        ts.setMode(TextureStage.MReplace)
        terrain.getRoot().setTexture(ts, white)
        terrain.getRoot().setTexScale(ts, 250, 250)

        stencil = self.loader.loadTexture(self.__texture_path(self.__scene.get("scene", "stencil")))
        ts = TextureStage("stage-stencil")
        ts.setSort(2)
        ts.setCombineRgb(TextureStage.CMInterpolate,
                         TextureStage.CSPrevious, TextureStage.COSrcColor,
                         TextureStage.CSLastSavedResult, TextureStage.COSrcColor,
                         TextureStage.CSTexture, TextureStage.COSrcColor)

        terrain.getRoot().setTexture(ts, stencil)

        ts = TextureStage("stage-vertexcolour")
        ts.setSort(3)
        ts.setCombineRgb(TextureStage.CMModulate, TextureStage.CSPrevious, TextureStage.COSrcColor,
                         TextureStage.CSPrimaryColor, TextureStage.COSrcColor)

        terrain.getRoot().setTexture(ts, "final")
Пример #4
0
    def __create_terrain(self):
        terrain = GeoMipTerrain("Terrain")
        terrain.setHeightfield(self.__texture_path(self.__scene.get("scene", "heightmap")))
        terrain.getRoot().reparentTo(self.render)
        terrain.generate()
        terrain.getRoot().setSx(1000.0 / 512)
        terrain.getRoot().setSy(1000.0 / 512)
        terrain.getRoot().setSz(74)
        terrain.getRoot().setPos(-500, -500, 0)

        black = self.loader.loadTexture(self.__texture_path(self.__scene.get("terrain", "black")))
        black.setMinfilter(Texture.FTLinearMipmapNearest)
        ts = TextureStage("stage-first")
        ts.setSort(0)
        ts.setMode(TextureStage.MReplace)
        ts.setSavedResult(True)
        terrain.getRoot().setTexture(ts, black)
        terrain.getRoot().setTexScale(ts, 250, 250)

        white = self.loader.loadTexture(self.__texture_path(self.__scene.get("terrain", "white")))
        white.setMinfilter(Texture.FTLinearMipmapNearest)
        ts = TextureStage("stage-second")
        ts.setSort(1)
        ts.setMode(TextureStage.MReplace)
        terrain.getRoot().setTexture(ts, white)
        terrain.getRoot().setTexScale(ts, 250, 250)

        stencil = self.loader.loadTexture(self.__texture_path(self.__scene.get("scene", "stencil")))
        ts = TextureStage("stage-stencil")
        ts.setSort(2)
        ts.setCombineRgb(TextureStage.CMInterpolate,
                         TextureStage.CSPrevious, TextureStage.COSrcColor,
                         TextureStage.CSLastSavedResult, TextureStage.COSrcColor,
                         TextureStage.CSTexture, TextureStage.COSrcColor)

        terrain.getRoot().setTexture(ts, stencil)

        ts = TextureStage("stage-vertexcolour")
        ts.setSort(3)
        ts.setCombineRgb(TextureStage.CMModulate, TextureStage.CSPrevious, TextureStage.COSrcColor,
                         TextureStage.CSPrimaryColor, TextureStage.COSrcColor)

        terrain.getRoot().setTexture(ts, "final")
class CombineStages(ShowBase):
    """Trying to draw a mask stage over a stim stage and combine them (modulate)
    Note debug variable is 1 to print vars, 2 to print vars and display mask from 
    matplotlib"""
    def __init__(self, texture_array, mask_position, mask_scale, debug):
        super().__init__()
        self.debug = debug
        if self.debug >= 1:
            import matplotlib.pyplot as plt
        texture_size = 1024
        self.mask_scale = mask_scale
        self.texture_array = texture_array
        self.mask_position_ndc = mask_position
        self.mask_position_uv = (self.ndc2uv(self.mask_position_ndc[0]), 
                                 self.ndc2uv(self.mask_position_ndc[1]))

        #CREATE MASK (zeros on left, 255s on right)
        self.right_mask = 255*np.ones((texture_size,texture_size), dtype=np.uint8)  #should set to 0/1 not 255?
        self.right_mask[:, texture_size//2: ] = 0   
        self.right_mask[texture_size//2 - 400:texture_size//2-300, -40:] = 120 #gray notch in RHS of zeros
        self.right_mask[texture_size//2 - 50:texture_size//2+50, 
                        texture_size//2: texture_size//2+80] = 180 #light notch in LHS of zeros
        
        if self.debug >= 2:
            plt.imshow(self.right_mask, cmap = 'gray')
            plt.show()

        #CREATE TEXTURE STAGES
        #Grating texture 
        self.grating_texture = Texture("Grating")  #T_unsigned_byte
        self.grating_texture.setup2dTexture(texture_size, texture_size, 
                                            Texture.T_unsigned_byte, Texture.F_luminance) 
        self.grating_texture.setRamImage(self.texture_array)   
        self.left_texture_stage = TextureStage('grating')
        #Mask
        self.right_mask_texture = Texture("right_mask")
        self.right_mask_texture.setup2dTexture(texture_size, texture_size, 
                                               Texture.T_unsigned_byte, Texture.F_luminance) 
        self.right_mask_texture.setRamImage(self.right_mask)  
        self.right_mask_stage = TextureStage('right_mask')
        #Multiply the texture stages together
        self.right_mask_stage.setCombineRgb(TextureStage.CMModulate, 
                                    TextureStage.CSTexture, 
                                    TextureStage.COSrcColor,
                                    TextureStage.CSPrevious, 
                                    TextureStage.COSrcColor)                         
        #CREATE SCENE GRAPH
        cm = CardMaker('card')
        cm.setFrameFullscreenQuad()
        self.left_card = self.aspect2d.attachNewNode(cm.generate())
        self.left_card.setTexture(self.left_texture_stage, self.grating_texture)  
        self.left_card.setTexture(self.right_mask_stage, self.right_mask_texture)
        
        #CREATE TASKS TO MOVE MASK, AND PRINT OUT INFO ABOUT MASK
        ShowBaseGlobal.base.taskMgr.add(self.update)
        if self.debug >= 1:
            display_period = 2  #seconds
            ShowBaseGlobal.base.taskMgr.doMethodLater(display_period,
                                                      self.print_updates, 'mask_display')
            
        #SCREEN TEXT FOR REFERENCE
        #Text mark the origin with green o
        self.title = OnscreenText("o", style = 1, fg = (1,1,0,1), bg = (0,1,0,0.8),
                          pos = (0,0), scale = 0.05)
        #Text mark the desired location with white x
        self.title = OnscreenText("x", style = 1, fg = (1,1,1,1), bg = (0,0,0,0.5),
                                  pos = self.mask_position_ndc, scale = 0.08)

    def update(self, task):
        update_transform = self.trs_transform(task.time)
        self.left_card.setTexTransform(self.right_mask_stage, update_transform)
        return task.cont
       
    def print_updates(self, task):
        self.print_ts_info()
        return task.again  #to keep going every whatever seconds

    def trs_transform(self, elapsed_time):
        """ trs = translate rotate scale: transform for mask stage """        
        pos = 0.5 + self.mask_position_uv[0], 0.5 + self.mask_position_uv[1]
        center_shift = TransformState.make_pos2d((-pos[0], -pos[1]))
        scale = TransformState.make_scale2d(1/self.mask_scale)
        rotate = TransformState.make_rotate2d(np.mod(elapsed_time*40, 360))
        translate = TransformState.make_pos2d((0.5, 0.5))
        return translate.compose(rotate.compose(scale.compose(center_shift)))
        

    
    def print_ts_info(self):
            print("\noffset: ", self.left_card.getTexOffset(self.right_mask_stage))
            print("rot: ", self.left_card.getTexRotate(self.right_mask_stage))
            #print("position: ", self.left_card.getTexPos(self.right_mask_stage))  #z is always 0
            return
        
    def ndc2uv(self, val):
        return 0.5*val
    
    def uv2ndc(self, val):
        return 2*val
class BinocularDrift(ShowBase):
    """
    Show binocular drifting textures forever.
    Takes in texture array and other parameters, and shows texture drifting indefinitely.
    Texture array can be grayscale or rgb, uint8 or uint16.
    
    Usage:
        BinocularDrift(texture_array, 
                        stim_angles = (0, 0), 
                        mask_angle = 0, 
                        position = (0,0),
                        velocity = 0.1,
                        band_radius = 3,
                        window_size = 512, 
                        texture_size = 512,
                        bgcolor = (0,0,0,1))
        
    Note(s): 
        angles are (left_texture_angle, right_texture_angle): >  is cw, < 0 cc2
        Velocity is in NDC, so 1.0 is the entire window width (i.e., super-fast).
        Make texture_size a power of 2: this makes the GPU happier.
        position is x,y in NDC (from [-1 1]), so (.5, .5) will be in top right quadrant of window.
        band_radius is just the half-width of the band in the middle. It can be 0.
        The texture array can be 2d (gray) or NxNx3 (rgb)
    """
    def __init__(self, texture_array, stim_angles = (0, 0), mask_angle = 0, position = (0, 0), velocity = 0,
                 band_radius = 3, window_size = 512, texture_size = 512, bgcolor = (0, 0, 0, 1)):
        super().__init__()

        self.mask_position_ndc = position
        self.mask_position_uv = (ndc2uv(self.mask_position_ndc[0]), 
                                 ndc2uv(self.mask_position_ndc[1]))
        self.scale = np.sqrt(8)
        self.texture_array = texture_array
        self.texture_dtype = type(self.texture_array.flat[0])
        self.ndims = self.texture_array.ndim
        self.left_texture_angle = stim_angles[0]
        self.right_texture_angle = stim_angles[1]
        self.velocity = velocity
        self.mask_angle = mask_angle #this will change fairly frequently
        
        #Set window title and size
        self.window_properties = WindowProperties()
        self.window_properties.setSize(window_size, window_size)
        self.window_properties.setTitle("BinocularStatic")
        ShowBaseGlobal.base.win.requestProperties(self.window_properties)  #base is a panda3d global
        
        #CREATE MASKS (right mask for left tex, left mask for right tex)
        self.right_mask = 255*np.ones((texture_size,texture_size), dtype=np.uint8)    
        self.right_mask[:, texture_size//2 - band_radius :] = 0  
        self.left_mask = 255*np.ones((texture_size,texture_size), dtype=np.uint8)    
        self.left_mask[:, : texture_size//2 + band_radius] = 0  
        
        
        if False:  #set to True to debug
            import matplotlib.pyplot as plt
            plt.imshow(self.left_mask, cmap = 'gray')
            plt.show()
    
        #CREATE TEXTURE STAGES
        #Grating texture 
        self.grating_texture = Texture("Grating")  #T_unsigned_byte
        self.grating_texture.setup2dTexture(texture_size, texture_size, 
                                            Texture.T_unsigned_byte, Texture.F_luminance) 
        self.grating_texture.setRamImage(self.texture_array)   
                   
        #TEXTURE STAGES FOR RIGHT CARD
        self.right_texture_stage = TextureStage('right_texture')       
        #Mask
        self.left_mask_texture = Texture("left_mask")
        self.left_mask_texture.setup2dTexture(texture_size, texture_size, 
                                               Texture.T_unsigned_byte, Texture.F_luminance) 
        self.left_mask_texture.setRamImage(self.left_mask)  
        self.left_mask_stage = TextureStage('left_mask')
        #Multiply the texture stages together
        self.left_mask_stage.setCombineRgb(TextureStage.CMModulate, 
                                    TextureStage.CSTexture, 
                                    TextureStage.COSrcColor,
                                    TextureStage.CSPrevious, 
                                    TextureStage.COSrcColor)    
                                             
        #CREATE CARDS/SCENEGRAPH
        cm = CardMaker('stimcard')
        cm.setFrameFullscreenQuad()
        self.right_card = self.aspect2d.attachNewNode(cm.generate())
        self.setBackgroundColor((0,0,0,1))  #set above
        self.right_card.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add))
        
        #SET TEXTURE STAGES
        self.right_card.setTexture(self.right_texture_stage, self.grating_texture)  
        self.right_card.setTexture(self.left_mask_stage, self.left_mask_texture)
        
        #SET static transforms
        #Left texture mask
        self.mask_transform = self.trs_transform()
        self.right_card.setTexTransform(self.left_mask_stage, self.mask_transform)
        #Right texture
        self.right_card.setTexScale(self.right_texture_stage, 1/self.scale)
        self.right_card.setTexRotate(self.right_texture_stage, self.right_texture_angle)

        #Set dynamic transforms
        if self.velocity != 0:
            self.taskMgr.add(self.texture_update, "moveTextureTask")


        self.title = OnscreenText("x",
                                  style = 1,
                                  fg = (1,1,1,1),
                                  bg = (0,0,0,.8),
                                  pos = self.mask_position_ndc, 
                                  scale = 0.05)
          
    #Procedure to move the camera
    def texture_update(self, task):
        #Move texture in whatever direction
        right_tex_position = task.time*self.velocity
        self.right_card.setTexPos(self.right_texture_stage, right_tex_position, 0, 0)
        return task.cont
    

    def trs_transform(self):
        """ trs = translate rotate scale: transform for mask stage """        
        pos = 0.5 + self.mask_position_uv[0], 0.5 + self.mask_position_uv[1]
        center_shift = TransformState.make_pos2d((-pos[0], -pos[1]))
        scale = TransformState.make_scale2d(1/self.scale)
        rotate = TransformState.make_rotate2d(self.mask_angle)
        translate = TransformState.make_pos2d((0.5, 0.5))
        return translate.compose(rotate.compose(scale.compose(center_shift)))
class Water(AssetBase):
    def __init__(self, name, size=10000, resolution=1024):
        """Arguments:
        size -- Edge length of the water square.
        resolution -- Texture size of the rendered reflection buffer.
        """
        # Uncomment to see the output of the refclection buffer.
        base.bufferViewer.toggleEnable()

        AssetBase.__init__(self)
        self.name = name

        self.cm = CardMaker("water surface")
        self.cm.setFrame(-0.5 * size, 0.5 * size, -0.5 * size, 0.5 * size)
        self.cm.setHasUvs(True)
        self.node = NodePath(self.cm.generate())

        self.node.setP(self.node, -90)
        self.node.flattenLight()
        self.node.hide(BitMask32.bit(1))
        # self.node.setTwoSided(True)
        self.node.setShaderOff()

        # size of one texture tile in meters
        self.tex_size = 100.0

        diffuse = TexturePool.loadTexture("textures/water.diffuse.png")
        diffuse.setWrapU(Texture.WMRepeat)
        diffuse.setWrapV(Texture.WMRepeat)
        diffuse.setMinfilter(Texture.FTLinearMipmapLinear)
        diffuse.setMagfilter(Texture.FTLinearMipmapLinear)
        self.diffuse_stage = TextureStage("diffuse")
        self.diffuse_stage.setSort(2)
        self.node.setTexture(self.diffuse_stage, diffuse)
        self.node.setTexScale(self.diffuse_stage, size / self.tex_size, size / self.tex_size)

        # Reflection camera renders to 'buffer' which is projected onto the
        # water surface.
        buffer = base.win.makeTextureBuffer("water reflection", resolution, resolution)
        buffer.setClearColor(Vec4(0, 0, 0, 1))

        self.refl_cam = base.makeCamera(buffer)
        self.refl_cam.reparentTo(self.node)
        self.refl_cam.node().setCameraMask(BitMask32.bit(1))
        self.refl_cam.node().getLens().setFov(base.camLens.getFov())
        self.refl_cam.node().getLens().setNearFar(1, 100000)

        plane = PlaneNode("water culling plane", Plane(Vec3(0, 0, 1), Point3(0, 0, 0)))
        cfa = CullFaceAttrib.makeReverse()
        cpa = ClipPlaneAttrib.make(PlaneNode.CEVisible, plane)
        rs = RenderState.make(cfa, cpa)
        self.refl_cam.node().setInitialState(rs)

        reflection = buffer.getTexture()
        reflection.setMinfilter(Texture.FTLinear)
        reflection.setMagfilter(Texture.FTLinear)
        self.refl_stage = TextureStage("reflection")
        self.refl_stage.setSort(1)
        self.node.projectTexture(self.refl_stage, reflection, base.cam)
        self.node.setTexture(self.refl_stage, reflection)

        # Blend between diffuse and reflection.
        self.diffuse_stage.setColor(VBase4(1, 1, 1, 0.2))  # opacity of 20%
        self.diffuse_stage.setCombineRgb(
            TextureStage.CMInterpolate,
            TextureStage.CSTexture,
            TextureStage.COSrcColor,
            TextureStage.CSPrevious,
            TextureStage.COSrcColor,
            TextureStage.CSConstant,
            TextureStage.COSrcAlpha,
        )

        self.addTask(self.update, name="water update", sort=1, taskChain="world")

    def update(self, task):
        """Updates position of the reflection camera and the water plane."""
        mc = base.cam.getMat(render)
        # mf = Plane(Vec3(0, 0, 1), Point3(0, 0, 0)).getReflectionMat()
        mf = Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)
        self.refl_cam.setMat(mc * mf)

        self.node.setX(camera.getX(render))
        self.node.setY(camera.getY(render))
        self.node.setTexOffset(self.diffuse_stage, self.node.getX() / self.tex_size, self.node.getY() / self.tex_size)

        return task.cont

    def destroy(self):
        self.removeAllTasks()
        self.node.removeNode()
        self.refl_cam.removeNode()
Пример #8
0
class InputControlStim(ShowBase):
    """
    Generic input-controll stimulus class: takes in list of texture classes, and stimulus parameters.
    Stimulus shown, in real-time, depends on events produced by utils.Monitor() class.
    
    Inputs:
        Positional
            tex_classes: m-element list of texture classes
            stim_params: m-element list of dictionaries: each contains parameters (e.g., velocity)
        
        Keyword 
            initial_tex_ind (0): index for first stim to show
            window_size (512): size of the panda3d window (pixels)
            window_name ('InputControlStim'): title of window in gui
            profile_on (False): will show actual fps, profiler, and little x at center if True
            fps (30): controls frame rate of display
            save_path (None): if set to a file path, will save data about stimuli, and time they are delivered
    """
    def __init__(self,
                 tex_classes,
                 stim_params,
                 initial_tex_ind=0,
                 window_size=512,
                 window_name="InputControlStim",
                 profile_on=False,
                 fps=30,
                 save_path=None):
        super().__init__()

        self.current_tex_num = initial_tex_ind
        self.previous_tex_num = None
        self.tex_classes = tex_classes
        self.stim_params = stim_params
        self.window_size = window_size
        self.stimulus_initialized = False  # for setting up first stim (don't clear cards they don't exist)
        self.fps = fps
        self.profile_on = profile_on
        self.save_path = save_path
        if self.save_path:
            self.filestream = utils.save_initialize(save_path, tex_classes,
                                                    stim_params)
        else:
            self.filestream = None
        self.scale = np.sqrt(
            8)  #so it can handle arbitrary rotations and shifts
        self.window_name = window_name

        #Window properties
        self.window_props = WindowProperties()
        self.window_props.setSize(self.window_size, self.window_size)
        self.set_title(self.window_name)

        # Set frame rate
        ShowBaseGlobal.globalClock.setMode(ClockObject.MLimited)
        ShowBaseGlobal.globalClock.setFrameRate(self.fps)

        #Set up profiling if desired
        if self.profile_on:
            PStatClient.connect()  # this will only work if pstats is running
            ShowBaseGlobal.base.setFrameRateMeter(True)  #Show frame rate

        #Set initial texture(s)
        self.set_stimulus(str(self.current_tex_num))

        # Set up event handlers (accept) and tasks (taskMgr) for dynamics
        self.accept('stim0', self.set_stimulus, ['0'])
        self.accept('stim1', self.set_stimulus, ['1'])
        self.accept('stim2', self.set_stimulus, ['2'])
        # Wrinkle: should we set this here or there?
        self.taskMgr.add(self.move_textures, "move textures")

    def set_tasks(self):
        if self.current_stim_params['stim_type'] == 'b':
            self.taskMgr.add(self.textures_update, "move_both")

    #Move textures
    def move_textures(self, task):
        if self.current_stim_params['stim_type'] == 'b':
            left_tex_position = -task.time * self.current_stim_params[
                'velocities'][0]  #negative b/c texture stage
            right_tex_position = -task.time * self.current_stim_params[
                'velocities'][1]
            try:
                self.left_card.setTexPos(self.left_texture_stage,
                                         left_tex_position, 0, 0)
                self.right_card.setTexPos(self.right_texture_stage,
                                          right_tex_position, 0, 0)
            except Exception as e:
                logger.error(e)
        elif self.current_stim_params['stim_type'] == 's':
            if self.current_stim_params['velocity'] == 0:
                pass
            else:
                new_position = -task.time * self.current_stim_params['velocity']
                # Sometimes setting position fails when the texture stage isn't fully set
                try:
                    self.card.setTexPos(self.texture_stage, new_position, 0,
                                        0)  #u, v, w
                except Exception as e:
                    logger.error(e)
        return task.cont

    @property
    def texture_size(self):
        return self.tex_classes[self.current_tex_num].texture_size

    @property
    def current_stim_params(self):
        """ 
        Parameters of current texture (e.g., velocity, stim_type) 
        """
        return self.stim_params[self.current_tex_num]

    def create_cards(self):
        """ 
        Create cards: these are panda3d objects that are required for displaying textures.
        You can't just have a disembodied texture. In pandastim (at least for now) we are
        only showing 2d projections of textures, so we use cards.       
        """
        cardmaker = CardMaker("stimcard")
        cardmaker.setFrameFullscreenQuad()
        #Binocular cards
        if self.current_stim_params['stim_type'] == 'b':
            self.setBackgroundColor(
                (0, 0, 0, 1))  # without this the cards will appear washed out
            self.left_card = self.aspect2d.attachNewNode(cardmaker.generate())
            self.left_card.setAttrib(
                ColorBlendAttrib.make(
                    ColorBlendAttrib.M_add))  # otherwise only right card shows

            self.right_card = self.aspect2d.attachNewNode(cardmaker.generate())
            self.right_card.setAttrib(
                ColorBlendAttrib.make(ColorBlendAttrib.M_add))
            if self.profile_on:
                self.center_indicator = OnscreenText(
                    "x",
                    style=1,
                    fg=(1, 1, 1, 1),
                    bg=(0, 0, 0, .8),
                    pos=self.current_stim_params['position'],
                    scale=0.05)
        # Tex card
        elif self.current_stim_params['stim_type'] == 's':
            self.card = self.aspect2d.attachNewNode(cardmaker.generate())
            self.card.setColor((1, 1, 1, 1))  #?
            self.card.setScale(self.scale)
        return

    def create_texture_stages(self):
        """
        Create the texture stages: these are basically textures that you can apply
        to cards (sometimes mulitple textures at the same time -- is useful with
        masks).
        
        For more on texture stages:
        https://docs.panda3d.org/1.10/python/programming/texturing/multitexture-introduction
        """
        #Binocular cards
        if self.current_stim_params['stim_type'] == 'b':
            #TEXTURE STAGES FOR LEFT CARD
            # Texture itself
            self.left_texture_stage = TextureStage('left_texture_stage')
            # Mask
            self.left_mask = Texture("left_mask_texture")
            self.left_mask.setup2dTexture(self.texture_size, self.texture_size,
                                          Texture.T_unsigned_byte,
                                          Texture.F_luminance)
            self.left_mask_stage = TextureStage('left_mask_array')

            #TEXTURE STAGES FOR RIGHT CARD
            self.right_texture_stage = TextureStage('right_texture_stage')
            #Mask
            self.right_mask = Texture("right_mask_texture")
            self.right_mask.setup2dTexture(self.texture_size,
                                           self.texture_size,
                                           Texture.T_unsigned_byte,
                                           Texture.F_luminance)
            self.right_mask_stage = TextureStage('right_mask_stage')
        # Tex card
        elif self.current_stim_params['stim_type'] == 's':
            self.texture_stage = TextureStage("texture_stage")
        return

    def set_stimulus(self, data):
        """ 
        Uses events from zmq to set the stimulus value. 
        """
        logger.debug("\tset_stimulus(%s)", data)
        if not self.stimulus_initialized:
            # If this is first stim, then toggle initialization to on, and
            # do not clear previous texture (there is no previous texture).
            self.stimulus_initialized = True
            self.data_previous = data
        elif data == self.data_previous:
            return
        else:
            self.data_previous = data
            self.clear_cards()  #clear the textures before adding new ones

        # This assumes data streaming is string numbers 0, 1, etc.
        self.current_tex_num = int(data)

        # Set new texture stages/cards etc
        self.tex = self.tex_classes[self.current_tex_num]

        logger.debug("\t%d: %s", self.current_tex_num, self.tex)
        self.create_texture_stages()
        self.create_cards()
        self.set_texture_stages()
        self.set_transforms()
        #Save stim to file (put this last as you want to set transforms quickly)
        if self.filestream:
            self.filestream.write(f"{str(datetime.now())}\t{data}\n")
            self.filestream.flush()
        return

    def clear_cards(self):
        """ 
        Clear cards when new stimulus: stim-class sensitive
        """
        if self.current_stim_params['stim_type'] == 'b':
            self.left_card.detachNode()
            self.right_card.detachNode()
            if self.profile_on:
                self.center_indicator.detachNode()
        elif self.current_stim_params['stim_type'] == 's':
            self.card.detachNode()
        return

    def set_transforms(self):
        """ 
        Set up the transforms to apply to textures/cards (e.g., rotations/scales)
        This is different from the framewise movement handled by the task manager
        """
        if self.current_stim_params['stim_type'] == 'b':
            #masks
            self.mask_transform = self.trs_transform()
            self.left_card.setTexTransform(self.left_mask_stage,
                                           self.mask_transform)
            self.right_card.setTexTransform(self.right_mask_stage,
                                            self.mask_transform)
            #Left texture
            self.left_card.setTexScale(self.left_texture_stage, 1 / self.scale)
            self.left_card.setTexRotate(self.left_texture_stage,
                                        self.current_stim_params['angles'][0])

            #Right texture
            self.right_card.setTexScale(self.right_texture_stage,
                                        1 / self.scale)
            self.right_card.setTexRotate(self.right_texture_stage,
                                         self.current_stim_params['angles'][1])

        if self.current_stim_params['stim_type'] == 's':
            self.card.setTexRotate(self.texture_stage,
                                   self.current_stim_params['angle'])
        return

    def set_texture_stages(self):
        """ 
        Add texture stages to cards
        """
        if self.current_stim_params['stim_type'] == 'b':
            self.mask_position_uv = (
                utils.card2uv(self.current_stim_params['position'][0]),
                utils.card2uv(self.current_stim_params['position'][1]))

            #CREATE MASK ARRAYS
            self.left_mask_array = 255 * np.ones(
                (self.texture_size, self.texture_size), dtype=np.uint8)
            self.left_mask_array[:, self.texture_size // 2 -
                                 self.current_stim_params['strip_width'] //
                                 2:] = 0
            self.right_mask_array = 255 * np.ones(
                (self.texture_size, self.texture_size), dtype=np.uint8)
            self.right_mask_array[:, :self.texture_size // 2 +
                                  self.current_stim_params['strip_width'] //
                                  2] = 0

            #ADD TEXTURE STAGES TO CARDS
            self.left_mask.setRamImage(self.left_mask_array)
            self.left_card.setTexture(self.left_texture_stage,
                                      self.tex.texture)
            self.left_card.setTexture(self.left_mask_stage, self.left_mask)
            #Multiply the texture stages together
            self.left_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                               TextureStage.CSTexture,
                                               TextureStage.COSrcColor,
                                               TextureStage.CSPrevious,
                                               TextureStage.COSrcColor)
            self.right_mask.setRamImage(self.right_mask_array)
            self.right_card.setTexture(self.right_texture_stage,
                                       self.tex.texture)
            self.right_card.setTexture(self.right_mask_stage, self.right_mask)
            #Multiply the texture stages together
            self.right_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                                TextureStage.CSTexture,
                                                TextureStage.COSrcColor,
                                                TextureStage.CSPrevious,
                                                TextureStage.COSrcColor)

        elif self.current_stim_params['stim_type'] == 's':
            self.card.setTexture(self.texture_stage, self.tex.texture)
        return

    def trs_transform(self):
        """ 
        trs = translate-rotate-scale transform for mask stage
        panda3d developer rdb contributed to this code
        """
        pos = 0.5 + self.mask_position_uv[0], 0.5 + self.mask_position_uv[1]
        center_shift = TransformState.make_pos2d((-pos[0], -pos[1]))
        scale = TransformState.make_scale2d(1 / self.scale)
        rotate = TransformState.make_rotate2d(
            self.current_stim_params['strip_angle'])
        translate = TransformState.make_pos2d((0.5, 0.5))
        return translate.compose(rotate.compose(scale.compose(center_shift)))

    def set_title(self, title):
        self.window_props.setTitle(title)
        ShowBaseGlobal.base.win.requestProperties(
            self.window_props)  #base is a panda3d global
Пример #9
0
class BinocularMoving(ShowBase):
    """
    Show binocular drifting textures forever.
    Takes in texture object and other parameters, and shows texture drifting indefinitely.

    Usage:
        BinocularDrift(texture_object,
                        stim_angles = (0, 0),
                        strip_angle = 0,
                        position = (0,0),
                        velocities = (0,0),
                        strip_width = 2,
                        window_size = 512,
                        window_name = 'FunStim',
                        profile_on  = False)

    Note(s):
        - angles are (left_texture_angle, right_texture_angle): >0 is cw, <0 ccw
        - Make texture_size a power of 2: this makes the GPU happier.
        - position is x,y in card-based coordinates (from [-1 1]), so (.5, .5) will be in middle of top right quadrant
        - Velocity is in card-based units, so 1.0 is the entire window width (i.e., super-fast).
        - strip_width is just the width of the strip down the middle. It can be 0. Even is better.
        - The texture array can be 2d (gray) or NxNx3 (rgb) with unit8 or uint16 elements.
    """
    def __init__(self,
                 tex,
                 stim_angles=(0, 0),
                 strip_angle=0,
                 position=(0, 0),
                 velocities=(0, 0),
                 strip_width=4,
                 fps=30,
                 window_size=None,
                 window_name='BinocularDrift',
                 profile_on=False):
        super().__init__()
        self.tex = tex
        if window_size == None:
            self.window_size = tex.texture_size
        else:
            self.window_size = window_size
        self.mask_position_card = position
        self.mask_position_uv = (utils.card2uv(self.mask_position_card[0]),
                                 utils.card2uv(self.mask_position_card[1]))
        self.scale = np.sqrt(
            8)  #so it can handle arbitrary rotations and shifts
        self.left_texture_angle = stim_angles[0]
        self.right_texture_angle = stim_angles[1]
        self.left_velocity = velocities[0]
        self.right_velocity = velocities[1]
        self.strip_angle = strip_angle  #this will change fairly frequently
        self.fps = fps
        self.window_name = window_name
        self.profile_on = profile_on

        #Set window title and size
        self.window_properties = WindowProperties()
        self.window_properties.setSize(self.window_size, self.window_size)
        self.window_properties.setTitle(self.window_name)
        ShowBaseGlobal.base.win.requestProperties(
            self.window_properties)  #base is a panda3d global

        #CREATE MASK ARRAYS
        self.left_mask_array = 255 * np.ones(
            (self.tex.texture_size, self.tex.texture_size), dtype=np.uint8)
        self.left_mask_array[:, self.tex.texture_size // 2 -
                             strip_width // 2:] = 0
        self.right_mask_array = 255 * np.ones(
            (self.tex.texture_size, self.tex.texture_size), dtype=np.uint8)
        self.right_mask_array[:, :self.tex.texture_size // 2 +
                              strip_width // 2] = 0

        #TEXTURE STAGES FOR LEFT CARD
        self.left_texture_stage = TextureStage('left_texture_stage')
        #Mask
        self.left_mask = Texture("left_mask_texture")
        self.left_mask.setup2dTexture(self.tex.texture_size,
                                      self.tex.texture_size,
                                      Texture.T_unsigned_byte,
                                      Texture.F_luminance)
        self.left_mask.setRamImage(self.left_mask_array)
        self.left_mask_stage = TextureStage('left_mask_array')
        #Multiply the texture stages together
        self.left_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                           TextureStage.CSTexture,
                                           TextureStage.COSrcColor,
                                           TextureStage.CSPrevious,
                                           TextureStage.COSrcColor)

        #TEXTURE STAGES FOR RIGHT CARD
        self.right_texture_stage = TextureStage('right_texture_stage')
        #Mask
        self.right_mask = Texture("right_mask_texture")
        self.right_mask.setup2dTexture(self.tex.texture_size,
                                       self.tex.texture_size,
                                       Texture.T_unsigned_byte,
                                       Texture.F_luminance)
        self.right_mask.setRamImage(self.right_mask_array)
        self.right_mask_stage = TextureStage('right_mask_stage')
        #Multiply the texture stages together
        self.right_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                            TextureStage.CSTexture,
                                            TextureStage.COSrcColor,
                                            TextureStage.CSPrevious,
                                            TextureStage.COSrcColor)

        #CREATE CARDS/SCENEGRAPH
        cm = CardMaker('stimcard')
        cm.setFrameFullscreenQuad()
        #self.setBackgroundColor((0,0,0,1))
        self.left_card = self.aspect2d.attachNewNode(cm.generate())
        self.right_card = self.aspect2d.attachNewNode(cm.generate())
        self.left_card.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add))
        self.right_card.setAttrib(ColorBlendAttrib.make(
            ColorBlendAttrib.M_add))

        #ADD TEXTURE STAGES TO CARDS
        self.left_card.setTexture(self.left_texture_stage, self.tex.texture)
        self.left_card.setTexture(self.left_mask_stage, self.left_mask)
        self.right_card.setTexture(self.right_texture_stage, self.tex.texture)
        self.right_card.setTexture(self.right_mask_stage, self.right_mask)
        self.setBackgroundColor(
            (0, 0, 0, 1))  # without this the cards will appear washed out

        #TRANSFORMS
        #Masks
        self.mask_transform = self.trs_transform()
        self.left_card.setTexTransform(self.left_mask_stage,
                                       self.mask_transform)
        self.right_card.setTexTransform(self.right_mask_stage,
                                        self.mask_transform)
        #Left texture
        self.left_card.setTexScale(self.left_texture_stage, 1 / self.scale)
        self.left_card.setTexRotate(self.left_texture_stage,
                                    self.left_texture_angle)
        #Right texture
        self.right_card.setTexScale(self.right_texture_stage, 1 / self.scale)
        self.right_card.setTexRotate(self.right_texture_stage,
                                     self.right_texture_angle)

        #Set dynamic transforms
        if self.left_velocity != 0 and self.right_velocity != 0:
            self.taskMgr.add(self.textures_update, "move_both")
        elif self.left_velocity != 0 and self.right_velocity == 0:
            self.taskMgr.add(self.left_texture_update, "move_left")
        elif self.left_velocity == 0 and self.right_velocity != 0:
            self.taskMgr.add(self.right_texture_update, "move_right")

        # Set frame rate
        ShowBaseGlobal.globalClock.setMode(ClockObject.MLimited)
        ShowBaseGlobal.globalClock.setFrameRate(
            self.fps)  #can lock this at whatever

        #Set up profiling if desired
        if profile_on:
            PStatClient.connect()  # this will only work if pstats is running
            ShowBaseGlobal.base.setFrameRateMeter(True)  #Show frame rate
            # Following will show a small x at the center
            self.title = OnscreenText("x",
                                      style=1,
                                      fg=(1, 1, 1, 1),
                                      bg=(0, 0, 0, .8),
                                      pos=self.mask_position_card,
                                      scale=0.05)

    #Move both textures
    def textures_update(self, task):
        left_tex_position = -task.time * self.left_velocity  #negative b/c texture stage
        right_tex_position = -task.time * self.right_velocity
        self.left_card.setTexPos(self.left_texture_stage, left_tex_position, 0,
                                 0)
        self.right_card.setTexPos(self.right_texture_stage, right_tex_position,
                                  0, 0)
        return task.cont

    def left_texture_update(self, task):
        left_tex_position = -task.time * self.left_velocity  #negative b/c texture stage
        self.left_card.setTexPos(self.left_texture_stage, left_tex_position, 0,
                                 0)
        return task.cont

    def right_texture_update(self, task):
        right_tex_position = -task.time * self.right_velocity
        self.right_card.setTexPos(self.right_texture_stage, right_tex_position,
                                  0, 0)
        return task.cont

    def trs_transform(self):
        """ 
        trs = translate rotate scale transform for mask stage
        rdb contributed to this code
        """
        pos = 0.5 + self.mask_position_uv[0], 0.5 + self.mask_position_uv[1]
        center_shift = TransformState.make_pos2d((-pos[0], -pos[1]))
        scale = TransformState.make_scale2d(1 / self.scale)
        rotate = TransformState.make_rotate2d(self.strip_angle)
        translate = TransformState.make_pos2d((0.5, 0.5))
        return translate.compose(rotate.compose(scale.compose(center_shift)))
Пример #10
0
class InputControlParams(ShowBase):
    """
    Input signal sends in x, y, theta values for binocular stimuli to control
    those parameters of the stim in real time. Need to expand to single texture.

    Usage:
        InputControlParams(texture_object,
                        stim_angles = (0, 0),
                        strip_angle = 0,
                        position = (0,0),
                        velocities = (0,0),
                        strip_width = 2,
                        window_size = 512,
                        window_name = 'FunStim',
                        profile_on  = False)

    Note(s):
        - angles are relative to strip angle
        - position is x,y in card-based coordinates (from [-1 1]), so (.5, .5) will be in middle of top right quadrant
        - Velocity is in same direction as angle, and units of window size (so 1 is super-fast)
        - strip_width is just the width of the strip down the middle. Can be 0.
    """
    def __init__(self,
                 tex,
                 stim_angles=(0, 0),
                 initial_angle=0,
                 initial_position=(0, 0),
                 velocities=(0, 0),
                 strip_width=4,
                 fps=30,
                 window_size=None,
                 window_name='position control',
                 profile_on=False,
                 save_path=None):
        super().__init__()
        self.render.setAntialias(AntialiasAttrib.MMultisample)
        self.aspect2d.prepareScene(
            ShowBaseGlobal.base.win.getGsg())  # pre-loads world
        self.tex = tex
        if window_size == None:
            self.window_size = tex.texture_size
        else:
            self.window_size = window_size
        self.mask_position_card = initial_position
        self.strip_width = strip_width
        self.scale = np.sqrt(
            8)  #so it can handle arbitrary rotations and shifts
        self.strip_angle = initial_angle  #this will change fairly frequently
        self.stim_angles = stim_angles
        self.left_texture_angle = self.stim_angles[
            0] + self.strip_angle  #make this a property
        self.right_texture_angle = self.stim_angles[1] + self.strip_angle
        self.left_velocity = velocities[0]
        self.right_velocity = velocities[1]
        self.fps = fps
        self.window_name = window_name
        self.profile_on = profile_on
        print(save_path)
        self.save_path = save_path
        if self.save_path:
            initial_params = {
                'angles': stim_angles,
                'initial_angle': self.strip_angle,
                'velocities': velocities,
                'strip_width': self.strip_width,
                'initial_position': initial_position
            }
            print(tex, initial_params)
            self.filestream = utils.save_initialize(self.save_path, [tex],
                                                    [initial_params])
            print(self.filestream)
        else:
            self.filestream = None

        #Set window title and size
        self.window_properties = WindowProperties()
        self.window_properties.setSize(self.window_size, self.window_size)
        self.window_properties.setTitle(self.window_name)
        ShowBaseGlobal.base.win.requestProperties(
            self.window_properties)  #base is a panda3d global

        # Set frame rate
        ShowBaseGlobal.globalClock.setMode(ClockObject.MLimited)
        ShowBaseGlobal.globalClock.setFrameRate(
            self.fps)  #can lock this at whatever

        #CREATE MASK ARRAYS
        self.left_mask_array = 255 * np.ones(
            (self.tex.texture_size, self.tex.texture_size), dtype=np.uint8)
        self.left_mask_array[:, self.tex.texture_size // 2 -
                             self.strip_width // 2:] = 0
        self.right_mask_array = 255 * np.ones(
            (self.tex.texture_size, self.tex.texture_size), dtype=np.uint8)
        self.right_mask_array[:, :self.tex.texture_size // 2 +
                              self.strip_width // 2] = 0

        #TEXTURE STAGES FOR LEFT CARD
        self.left_texture_stage = TextureStage('left_texture_stage')
        #Mask
        self.left_mask = Texture("left_mask_texture")
        self.left_mask.setup2dTexture(self.tex.texture_size,
                                      self.tex.texture_size,
                                      Texture.T_unsigned_byte,
                                      Texture.F_luminance)
        self.left_mask.setRamImage(self.left_mask_array)
        self.left_mask_stage = TextureStage('left_mask_array')
        #Multiply the texture stages together
        self.left_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                           TextureStage.CSTexture,
                                           TextureStage.COSrcColor,
                                           TextureStage.CSPrevious,
                                           TextureStage.COSrcColor)

        #TEXTURE STAGES FOR RIGHT CARD
        self.right_texture_stage = TextureStage('right_texture_stage')
        #Mask
        self.right_mask = Texture("right_mask_texture")
        self.right_mask.setup2dTexture(self.tex.texture_size,
                                       self.tex.texture_size,
                                       Texture.T_unsigned_byte,
                                       Texture.F_luminance)
        self.right_mask.setRamImage(self.right_mask_array)
        self.right_mask_stage = TextureStage('right_mask_stage')
        #Multiply the texture stages together
        self.right_mask_stage.setCombineRgb(TextureStage.CMModulate,
                                            TextureStage.CSTexture,
                                            TextureStage.COSrcColor,
                                            TextureStage.CSPrevious,
                                            TextureStage.COSrcColor)

        #CREATE CARDS/SCENEGRAPH
        cm = CardMaker('stimcard')
        cm.setFrameFullscreenQuad()
        #self.setBackgroundColor((0,0,0,1))
        self.left_card = self.aspect2d.attachNewNode(cm.generate())
        self.right_card = self.aspect2d.attachNewNode(cm.generate())
        self.left_card.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add))
        self.right_card.setAttrib(ColorBlendAttrib.make(
            ColorBlendAttrib.M_add))

        #ADD TEXTURE STAGES TO CARDS
        self.left_card.setTexture(self.left_texture_stage, self.tex.texture)
        self.left_card.setTexture(self.left_mask_stage, self.left_mask)
        self.right_card.setTexture(self.right_texture_stage, self.tex.texture)
        self.right_card.setTexture(self.right_mask_stage, self.right_mask)
        self.setBackgroundColor(
            (0, 0, 0, 1))  # without this the cards will appear washed out
        #self.left_card.setAntialias(AntialiasAttrib.MMultisample)
        #self.right_card.setAntialias(AntialiasAttrib.MMultisample)
        #TRANSFORMS
        # Masks
        self.mask_transform = self.trs_transform()
        self.left_card.setTexTransform(self.left_mask_stage,
                                       self.mask_transform)
        self.right_card.setTexTransform(self.right_mask_stage,
                                        self.mask_transform)

        # Textures
        # Left
        self.left_card.setTexScale(self.left_texture_stage, 1 / self.scale)
        self.left_card.setTexRotate(self.left_texture_stage,
                                    self.left_texture_angle)
        # Right
        self.right_card.setTexScale(self.right_texture_stage, 1 / self.scale)
        self.right_card.setTexRotate(self.right_texture_stage,
                                     self.right_texture_angle)

        #Set task manager(s) for textures
        if self.left_velocity != 0 and self.right_velocity != 0:
            self.taskMgr.add(self.textures_update, "move_both")
        elif self.left_velocity != 0 and self.right_velocity == 0:
            self.taskMgr.add(self.left_texture_update, "move_left")
        elif self.left_velocity == 0 and self.right_velocity != 0:
            self.taskMgr.add(self.right_texture_update, "move_right")

        # Event handler to process the messages
        self.accept("stim", self.process_stim, [])

        #Set up profiling if desired
        if profile_on:
            PStatClient.connect()  # this will only work if pstats is running
            ShowBaseGlobal.base.setFrameRateMeter(True)  #Show frame rate

    @property
    def mask_position_uv(self):
        return (utils.card2uv(self.mask_position_card[0]),
                utils.card2uv(self.mask_position_card[1]))

    def process_stim(self, x, y, theta):
        """
        Event handler method for processing message about current x,y, theta
        """
        # If new values are same as previous, return to caller. Otherwise, reset
        if (self.strip_angle, self.mask_position_card) == (theta, (x, y)):
            return
        else:
            self.strip_angle = theta
            self.right_texture_angle = self.stim_angles[1] + self.strip_angle
            self.left_texture_angle = self.stim_angles[0] + self.strip_angle
            #print(self.strip_angle, self.left_texture_angle, self.right_texture_angle)
            self.mask_position_card = (x, y)
            self.mask_transform = self.trs_transform()
            self.left_card.setTexTransform(self.left_mask_stage,
                                           self.mask_transform)
            self.right_card.setTexTransform(self.right_mask_stage,
                                            self.mask_transform)
            self.left_card.setTexRotate(self.left_texture_stage,
                                        self.left_texture_angle)
            self.right_card.setTexRotate(self.right_texture_stage,
                                         self.right_texture_angle)

        if self.filestream:
            self.filestream.write(
                f"{str(datetime.now())}\t{x}\t{y}\t{theta}\n")
            self.filestream.flush()
            return

    #Move both textures
    def textures_update(self, task):
        left_tex_position = -task.time * self.left_velocity  #negative b/c texture stage
        right_tex_position = -task.time * self.right_velocity
        self.left_card.setTexPos(self.left_texture_stage, left_tex_position, 0,
                                 0)
        self.right_card.setTexPos(self.right_texture_stage, right_tex_position,
                                  0, 0)
        return task.cont

    def left_texture_update(self, task):
        left_tex_position = -task.time * self.left_velocity  #negative b/c texture stage
        self.left_card.setTexPos(self.left_texture_stage, left_tex_position, 0,
                                 0)
        return task.cont

    def right_texture_update(self, task):
        right_tex_position = -task.time * self.right_velocity
        self.right_card.setTexPos(self.right_texture_stage, right_tex_position,
                                  0, 0)
        return task.cont

    def trs_transform(self):
        """ 
        trs = translate rotate scale transform for mask stage
        rdb contributed to this code
        """
        #print(self.strip_angle)
        pos = 0.5 + self.mask_position_uv[0], 0.5 + self.mask_position_uv[1]
        center_shift = TransformState.make_pos2d((-pos[0], -pos[1]))
        scale = TransformState.make_scale2d(1 / self.scale)
        rotate = TransformState.make_rotate2d(self.strip_angle)
        translate = TransformState.make_pos2d((0.5, 0.5))
        return translate.compose(rotate.compose(scale.compose(center_shift)))
Пример #11
0
class TerrainManager(DirectObject.DirectObject):
    def __init__(self):
        self.accept("mouse1", self.lclick)
        self.waterType = 2
        self.water = None
        self.citycolors = {0: VBase3D(1, 1, 1)}

        self.accept('generateRegion', self.generateWorld)
        self.accept('regenerateRegion', self.regenerateWorld)
        self.accept("regionView_normal", self.setSurfaceTextures)
        self.accept("regionView_owners", self.setOwnerTextures)
        self.accept("regionView_foundNew", self.regionViewFound)
        self.accept("updateRegion", self.updateRegion)
        self.accept("enterCityView", self.enterCity)

        # View: 0, region, # cityid
        self.view = 0
        self.ownerview = False

    def lclick(self):
        cell = picker.getMouseCell()
        print "Cell:", cell
        blockCoords = self.terrain.getBlockFromPos(cell[0], cell[1])
        block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1])
        print "Block coords:", blockCoords
        print "NodePath:", block
        print "Elevation:", self.terrain.getElevation(cell[0], cell[1])
        if not self.view:
            messenger.send("clickForCity", [cell])

    def switchWater(self):
        print "Switch Water"
        self.waterType += 1
        if self.waterType > 2:
            self.waterType = 0
        self.generateWater(self.waterType)

    def generateWorld(self, heightmap, tiles, cities, container):
        self.heightmap = heightmap
        self.terrain = PagedGeoMipTerrain("surface")
        #self.terrain = GeoMipTerrain("surface")
        self.terrain.setHeightfield(self.heightmap)
        #self.terrain.setFocalPoint(base.camera)
        self.terrain.setBruteforce(True)
        self.terrain.setBlockSize(64)
        self.terrain.generate()

        root = self.terrain.getRoot()
        root.reparentTo(render)
        #root.setSz(100)
        self.terrain.setSz(100)
        messenger.send('makePickable', [root])

        if self.heightmap.getXSize() > self.heightmap.getYSize():
            self.size = self.heightmap.getXSize() - 1
        else:
            self.size = self.heightmap.getYSize() - 1
        self.xsize = self.heightmap.getXSize() - 1
        self.ysize = self.heightmap.getYSize() - 1

        # Set multi texture
        # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536
        self.generateSurfaceTextures()
        self.generateWaterMap()
        self.generateOwnerTexture(tiles, cities)
        #self.terrain.makeTextureMap()
        colormap = PNMImage(heightmap.getXSize() - 1, heightmap.getYSize() - 1)
        colormap.addAlpha()
        slopemap = self.terrain.makeSlopeImage()
        for x in range(0, colormap.getXSize()):
            for y in range(0, colormap.getYSize()):
                # Else if statements used to make sure one channel is used per pixel
                # Also for some optimization
                # Snow. We do things funky here as alpha will be 1 already.
                if heightmap.getGrayVal(x, y) < 200:
                    colormap.setAlpha(x, y, 0)
                else:
                    colormap.setAlpha(x, y, 1)
                # Beach. Estimations from http://www.simtropolis.com/omnibus/index.cfm/Main.SimCity_4.Custom_Content.Custom_Terrains_and_Using_USGS_Data
                if heightmap.getGrayVal(x, y) < 62:
                    colormap.setBlue(x, y, 1)
                # Rock
                elif slopemap.getGrayVal(x, y) > 170:
                    colormap.setRed(x, y, 1)
                else:
                    colormap.setGreen(x, y, 1)
        self.colorTexture = Texture()
        self.colorTexture.load(colormap)
        self.colorTS = TextureStage('color')
        self.colorTS.setSort(0)
        self.colorTS.setPriority(1)

        self.setSurfaceTextures()
        self.generateWater(2)
        taskMgr.add(self.updateTerrain, "updateTerrain")
        print "Done with terrain generation"
        messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]])
        self.terrain.getRoot().analyze()
        self.accept("h", self.switchWater)

    def regenerateWorld(self):
        '''Regenerates world, often upon city exit.'''
        self.terrain.generate()
        root = self.terrain.getRoot()
        root.reparentTo(render)
        self.terrain.setSz(100)
        messenger.send('makePickable', [root])
        # Set multi texture
        # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536
        #self.generateSurfaceTextures()
        self.generateWaterMap()

        self.setSurfaceTextures()
        self.generateWater(2)
        print "Done with terrain regeneration"
        messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]])

    def generateWaterMap(self):
        ''' Iterate through every pix of color map. This will be very slow so until faster method is developed, use sparingly
        getXSize returns pixels length starting with 1, subtract 1 for obvious reasons
        We also slip in checking for the water card size, which should only change when the color map does
        '''
        print "GenerateWaterMap"
        self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax = -1, 0, -1, 0
        for x in range(0, self.heightmap.getXSize() - 1):
            for y in range(0, self.heightmap.getYSize() - 1):
                # Else if statements used to make sure one channel is used per pixel
                # Also for some optimization
                # Snow. We do things funky here as alpha will be 1 already.
                if self.heightmap.getGrayVal(x, y) < 62:
                    # Water card dimensions here
                    # Y axis flipped from texture space to world space
                    if self.waterXMin == -1 or x < self.waterXMin:
                        self.waterXMin = x
                    if not self.waterYMax:
                        self.waterYMax = y
                    if y < self.waterYMax:
                        self.waterYMax = y
                    if x > self.waterXMax:
                        self.waterXMax = x
                    if y > self.waterYMin:
                        self.waterYMin = y
        # Transform y coords
        self.waterYMin = self.size - 64 - self.waterYMin
        self.waterYMax = self.size - 64 - self.waterYMax

    def generateOwnerTexture(self, tiles, cities):
        '''Generates a simple colored texture to be applied to the city info region overlay.
        Due to different coordinate systems (terrain org bottom left, texture top left)
        some conversions are needed,
        
        Also creates and sends a citylabels dict for the region view
        '''
        self.citymap = PNMImage(self.xsize, self.ysize)
        citylabels = cities
        scratch = {}

        # Setup for city labels
        for ident in cities:
            scratch[ident] = []

        # conversion for y axis
        ycon = []
        s = self.ysize - 1
        for y in range(self.ysize):
            ycon.append(s)
            s -= 1
        for ident, city in cities.items():
            if ident not in self.citycolors:
                self.citycolors[ident] = VBase3D(random.random(),
                                                 random.random(),
                                                 random.random())
        for tile in tiles:
            self.citymap.setXel(tile.coords[0], ycon[tile.coords[1]],
                                self.citycolors[tile.cityid])
            # Scratch for labeling
            if tile.cityid:
                scratch[tile.cityid].append((tile.coords[0], tile.coords[1]))
        for ident, values in scratch.items():
            xsum = 0
            ysum = 0
            n = 0
            for coords in values:
                xsum += coords[0]
                ysum += coords[1]
                n += 1
            xavg = xsum / n
            yavg = ysum / n
            print "Elevation:", self.terrain.getElevation(xavg, yavg)
            z = self.terrain.getElevation(xavg, yavg) * 100
            citylabels[ident]["position"] = (xavg, yavg, z + 15)
            print "Citylabels:", citylabels
        messenger.send("updateCityLabels", [citylabels, self.terrain])

    def generateSurfaceTextures(self):
        # Textureize
        self.grassTexture = loader.loadTexture("Textures/grass.png")
        self.grassTS = TextureStage('grass')
        self.grassTS.setSort(1)

        self.rockTexture = loader.loadTexture("Textures/rock.jpg")
        self.rockTS = TextureStage('rock')
        self.rockTS.setSort(2)
        self.rockTS.setCombineRgb(TextureStage.CMAdd,
                                  TextureStage.CSLastSavedResult,
                                  TextureStage.COSrcColor,
                                  TextureStage.CSTexture,
                                  TextureStage.COSrcColor)

        self.sandTexture = loader.loadTexture("Textures/sand.jpg")
        self.sandTS = TextureStage('sand')
        self.sandTS.setSort(3)
        self.sandTS.setPriority(5)

        self.snowTexture = loader.loadTexture("Textures/ice.png")
        self.snowTS = TextureStage('snow')
        self.snowTS.setSort(4)
        self.snowTS.setPriority(0)

        # Grid for city placement and guide and stuff
        self.gridTexture = loader.loadTexture("Textures/grid.png")
        self.gridTexture.setWrapU(Texture.WMRepeat)
        self.gridTexture.setWrapV(Texture.WMRepeat)
        self.gridTS = TextureStage('grid')
        self.gridTS.setSort(5)
        self.gridTS.setPriority(10)

    def enterCity(self, ident, city, position, tiles):
        '''Identifies which terrain blocks city belogs to and disables those that are not
        A lot of uneeded for loops in here. Will need to find a better way later.'''
        #root = self.terrain.getRoot()
        children = []
        for terrain in self.terrain.terrains:
            root = terrain.getRoot()
            children += root.getChildren()
        keepBlocks = []
        # Reset water dimentions
        self.waterXMin = 0
        self.waterXMax = 0
        self.waterYMin = 0
        self.waterYMax = 0

        for tile in tiles:
            blockCoords = self.terrain.getBlockFromPos(tile.coords[0],
                                                       tile.coords[1])
            block = self.terrain.getBlockNodePath(blockCoords[0],
                                                  blockCoords[1])
            if block not in keepBlocks:
                keepBlocks.append(block)
            if self.heightmap.getGrayVal(tile.coords[0],
                                         self.size - tile.coords[1]) < 62:
                # Water card dimensions here
                # Y axis flipped from texture space to world space
                if not self.waterXMin:
                    self.waterXMin = tile.coords[0]
                if not self.waterYMin:
                    self.waterYMin = tile.coords[1]
                if tile.coords[1] > self.waterYMax:
                    self.waterYMax = tile.coords[1]
                if tile.coords[0] > self.waterXMax:
                    self.waterXMax = tile.coords[0]
                if tile.coords[0] < self.waterXMin:
                    self.waterXMin = tile.coords[0]
                if tile.coords[1] < self.waterYMin:
                    self.waterYMin = tile.coords[1]
        for child in children:
            if child not in keepBlocks:
                child.detachNode()
        self.view = ident
        self.generateWater(2)

    def newTerrainOverlay(self, task):
        root = self.terrain.getRoot()
        position = picker.getMouseCell()
        if position:
            # Check to make sure we do not go out of bounds
            if position[0] < 32:
                position = (32, position[1])
            elif position[0] > self.xsize - 32:
                position = (self.xsize - 32, position[1])
            if position[1] < 32:
                position = (position[0], 32)
            elif position[1] > self.ysize - 32:
                position = (position[0], self.size - 32)
            root.setTexOffset(self.tileTS, -(position[0] - 32) / 64,
                              -(position[1] - 32) / 64)
        return task.cont

    def regionViewFound(self):
        '''Gui for founding a new city!'''
        self.setOwnerTextures()
        root = self.terrain.getRoot()
        task = taskMgr.add(self.newTerrainOverlay, "newTerrainOverlay")
        tileTexture = loader.loadTexture("Textures/tile.png")
        tileTexture.setWrapU(Texture.WMClamp)
        tileTexture.setWrapV(Texture.WMClamp)
        self.tileTS = TextureStage('tile')
        self.tileTS.setSort(6)
        self.tileTS.setMode(TextureStage.MDecal)
        #self.tileTS.setColor(Vec4(1,0,1,1))
        root.setTexture(self.tileTS, tileTexture)
        root.setTexScale(self.tileTS, self.terrain.xchunks,
                         self.terrain.ychunks)
        self.acceptOnce("mouse1", self.regionViewFound2)
        self.acceptOnce("escape", self.cancelRegionViewFound)

    def regionViewFound2(self):
        '''Grabs cell location for founding.
        The texture coordinate is used as the mouse may enter an out of bounds area.
        '''
        root = self.terrain.getRoot()
        root_position = root.getTexOffset(self.tileTS)
        # We offset the position of the texture, so we will now put the origin of the new city not on mouse cursor but the "bottom left" of it. Just need to add 32 to get other edge
        position = [
            int(abs(root_position[0] * 64)),
            int(abs(root_position[1] * 64))
        ]
        self.cancelRegionViewFound()
        messenger.send("found_city_name", [position])

    def cancelRegionViewFound(self):
        taskMgr.remove("newTerrainOverlay")
        root = self.terrain.getRoot()
        root.clearTexture(self.tileTS)
        # Restore original mouse function
        self.accept("mouse1", self.lclick)
        messenger.send("showRegionGUI")

    def setSurfaceTextures(self):
        self.ownerview = False
        root = self.terrain.getRoot()
        root.clearTexture()
        #self.terrain.setTextureMap()
        root.setTexture(self.colorTS, self.colorTexture)
        root.setTexture(self.grassTS, self.grassTexture)
        root.setTexScale(self.grassTS, self.size / 8, self.size / 8)
        root.setTexture(self.rockTS, self.rockTexture)
        root.setTexScale(self.rockTS, self.size / 8, self.size / 8)
        root.setTexture(self.sandTS, self.sandTexture)
        root.setTexScale(self.sandTS, self.size / 8, self.size / 8)
        root.setTexture(self.snowTS, self.snowTexture)
        root.setTexScale(self.snowTS, self.size / 8, self.size / 8)
        root.setTexture(self.gridTS, self.gridTexture)
        root.setTexScale(self.gridTS, self.xsize, self.ysize)
        root.setShaderInput('size', self.xsize, self.ysize, self.size,
                            self.size)
        root.setShader(loader.loadShader('Shaders/terraintexture.sha'))

    def setOwnerTextures(self):
        self.ownerview = True
        root = self.terrain.getRoot()
        root.clearShader()
        root.clearTexture()
        cityTexture = Texture()
        cityTexture.load(self.citymap)
        cityTS = TextureStage('citymap')
        cityTS.setSort(0)
        root.setTexture(self.gridTS, self.gridTexture)
        root.setTexScale(self.gridTS, self.terrain.xchunks,
                         self.terrain.ychunks)
        root.setTexture(cityTS, cityTexture, 1)

    def updateRegion(self, heightmap, tiles, cities):
        self.generateOwnerTexture(tiles, cities)
        if self.ownerview:
            self.setOwnerTextures()

    def updateTerrain(self, task):
        '''Updates terrain and water'''
        self.terrain.update()
        # Water
        if self.waterType is 2:
            pos = base.camera.getPos()
            render.setShaderInput('time', task.time)
            mc = base.camera.getMat()
            self.water.changeCameraPos(pos, mc)
            self.water.changeCameraPos(pos, mc)
        #print "Render diagnostics"
        #render.analyze()
        #base.cTrav.showCollisions(render)
        return task.cont

    def generateWater(self, style):
        print "Generate Water:", self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax
        '''Generates water
        style 0: blue card
        style 1: reflective card
        style 2: reflective card with shaders
        '''
        self.waterHeight = 22.0
        if self.water:
            self.water.removeNode()
        if style is 0:
            cm = CardMaker("water")
            #cm.setFrame(-1, 1, -1, 1)
            cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin,
                        self.waterYMax)
            cm.setColor(0, 0, 1, 0.9)
            self.water = render.attachNewNode(cm.generate())
            if self.waterYMax > self.waterXMax:
                size = self.waterYMax
            else:
                size = self.waterXMax
            self.water.lookAt(0, 0, -1)
            self.water.setZ(self.waterHeight)
            messenger.send('makePickable', [self.water])
        elif style is 1:
            # From Prosoft's super awesome terrain demo
            cm = CardMaker("water")
            #cm.setFrame(-1, 1, -1, 1)
            cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin,
                        self.waterYMax)
            self.water = render.attachNewNode(cm.generate())
            if self.waterYMax > self.waterXMax:
                size = self.waterYMax
            else:
                size = self.waterXMax
            #self.water.setScale(size)
            self.water.lookAt(0, 0, -1)
            self.water.setZ(self.waterHeight)
            self.water.setShaderOff(1)
            self.water.setLightOff(1)
            self.water.setAlphaScale(0.5)
            self.water.setTransparency(TransparencyAttrib.MAlpha)
            wbuffer = base.win.makeTextureBuffer("water", 512, 512)
            wbuffer.setClearColorActive(True)
            wbuffer.setClearColor(base.win.getClearColor())
            self.wcamera = base.makeCamera(wbuffer)
            self.wcamera.reparentTo(render)
            self.wcamera.node().setLens(base.camLens)
            self.wcamera.node().setCameraMask(BitMask32.bit(1))
            self.water.hide(BitMask32.bit(1))
            wtexture = wbuffer.getTexture()
            wtexture.setWrapU(Texture.WMClamp)
            wtexture.setWrapV(Texture.WMClamp)
            wtexture.setMinfilter(Texture.FTLinearMipmapLinear)
            self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, self.water.getZ()))
            wplanenp = render.attachNewNode(PlaneNode("water", self.wplane))
            tmpnp = NodePath("StateInitializer")
            tmpnp.setClipPlane(wplanenp)
            tmpnp.setAttrib(CullFaceAttrib.makeReverse())
            self.wcamera.node().setInitialState(tmpnp.getState())
            self.water.projectTexture(TextureStage("reflection"), wtexture,
                                      self.wcamera)
            messenger.send('makePickable', [self.water])
        elif style is 2:
            # From Clcheung just as super awesome demomaster
            self.water_level = Vec4(0.0, 0.0, self.waterHeight, 1.0)
            self.water = water.WaterNode(self.waterXMin, self.waterYMin,
                                         self.waterXMax, self.waterYMax,
                                         self.water_level.getZ())
            self.water.setStandardControl()
            self.water.changeParams(None)
            wl = self.water_level
            wl.setZ(wl.getZ() - 0.05)
            #root.setShaderInput('waterlevel', self.water_level)
            render.setShaderInput('time', 0)
            messenger.send('makePickable', [self.water.waterNP])
Пример #12
0
class Water(AssetBase):
    def __init__(self, name, size=10000, resolution=1024):
        """Arguments:
        size -- Edge length of the water square.
        resolution -- Texture size of the rendered reflection buffer.
        """
        # Uncomment to see the output of the refclection buffer.
        base.bufferViewer.toggleEnable()

        AssetBase.__init__(self)
        self.name = name

        self.cm = CardMaker("water surface")
        self.cm.setFrame(-0.5 * size, 0.5 * size, -0.5 * size, 0.5 * size)
        self.cm.setHasUvs(True)
        self.node = NodePath(self.cm.generate())

        self.node.setP(self.node, -90)
        self.node.flattenLight()
        self.node.hide(BitMask32.bit(1))
        #self.node.setTwoSided(True)
        self.node.setShaderOff()

        # size of one texture tile in meters
        self.tex_size = 100.0

        diffuse = TexturePool.loadTexture("textures/water.diffuse.png")
        diffuse.setWrapU(Texture.WMRepeat)
        diffuse.setWrapV(Texture.WMRepeat)
        diffuse.setMinfilter(Texture.FTLinearMipmapLinear)
        diffuse.setMagfilter(Texture.FTLinearMipmapLinear)
        self.diffuse_stage = TextureStage("diffuse")
        self.diffuse_stage.setSort(2)
        self.node.setTexture(self.diffuse_stage, diffuse)
        self.node.setTexScale(self.diffuse_stage, size / self.tex_size,
                              size / self.tex_size)

        # Reflection camera renders to 'buffer' which is projected onto the
        # water surface.
        buffer = base.win.makeTextureBuffer("water reflection", resolution,
                                            resolution)
        buffer.setClearColor(Vec4(0, 0, 0, 1))

        self.refl_cam = base.makeCamera(buffer)
        self.refl_cam.reparentTo(self.node)
        self.refl_cam.node().setCameraMask(BitMask32.bit(1))
        self.refl_cam.node().getLens().setFov(base.camLens.getFov())
        self.refl_cam.node().getLens().setNearFar(1, 100000)

        plane = PlaneNode("water culling plane",
                          Plane(Vec3(0, 0, 1), Point3(0, 0, 0)))
        cfa = CullFaceAttrib.makeReverse()
        cpa = ClipPlaneAttrib.make(PlaneNode.CEVisible, plane)
        rs = RenderState.make(cfa, cpa)
        self.refl_cam.node().setInitialState(rs)

        reflection = buffer.getTexture()
        reflection.setMinfilter(Texture.FTLinear)
        reflection.setMagfilter(Texture.FTLinear)
        self.refl_stage = TextureStage("reflection")
        self.refl_stage.setSort(1)
        self.node.projectTexture(self.refl_stage, reflection, base.cam)
        self.node.setTexture(self.refl_stage, reflection)

        # Blend between diffuse and reflection.
        self.diffuse_stage.setColor(VBase4(1, 1, 1, 0.2))  # opacity of 20%
        self.diffuse_stage.setCombineRgb(
            TextureStage.CMInterpolate, TextureStage.CSTexture,
            TextureStage.COSrcColor, TextureStage.CSPrevious,
            TextureStage.COSrcColor, TextureStage.CSConstant,
            TextureStage.COSrcAlpha)

        self.addTask(self.update,
                     name="water update",
                     sort=1,
                     taskChain="world")

    def update(self, task):
        """Updates position of the reflection camera and the water plane."""
        mc = base.cam.getMat(render)
        #mf = Plane(Vec3(0, 0, 1), Point3(0, 0, 0)).getReflectionMat()
        mf = Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)
        self.refl_cam.setMat(mc * mf)

        self.node.setX(camera.getX(render))
        self.node.setY(camera.getY(render))
        self.node.setTexOffset(self.diffuse_stage,
                               self.node.getX() / self.tex_size,
                               self.node.getY() / self.tex_size)

        return task.cont

    def destroy(self):
        self.removeAllTasks()
        self.node.removeNode()
        self.refl_cam.removeNode()
    def setupHeightmap(self, name):

        # Automatically generate a heightmap mesh from a monochrome image.
        self.hmHeight = 120
        hmPath = "../maps/map" + name + "/map" + name + "-h.png"
        imPath = "../maps/map" + name + "/map" + name + "-i.png"
        smPath = "../maps/map" + name + "/map" + name + "-s.png"
        scmPath = "../maps/map" + name + "/map" + name + "-sc.png"
        print(hmPath)
        print(imPath)
        print(smPath)
        print(scmPath)
        hmImg = PNMImage(Filename(hmPath))
        hmShape = BulletHeightfieldShape(hmImg, self.hmHeight, ZUp)
        hmNode = BulletRigidBodyNode('Terrain')
        hmNode.addShape(hmShape)
        hmNode.setMass(0)
        self.hmNP = render.attachNewNode(hmNode)
        self.worldBullet.attachRigidBody(hmNode)
        self.hmOffset = hmImg.getXSize() / 2.0 - 0.5
        self.hmTerrain = GeoMipTerrain('gmTerrain')
        self.hmTerrain.setHeightfield(hmImg)

        # Optimizations and fixes
        self.hmTerrain.setBruteforce(
            True)  # I don't think this is actually needed.
        self.hmTerrain.setMinLevel(3)  # THIS is what triangulates the terrain.
        self.hmTerrain.setBlockSize(
            128)  # This does a pretty good job of raising FPS.
        # Level-of-detail (not yet working)
        # self.hmTerrain.setNear(40)
        # self.hmTerrain.setFar(200)

        self.hmTerrain.generate()

        self.hmTerrainNP = self.hmTerrain.getRoot()
        self.hmTerrainNP.setSz(self.hmHeight)
        self.hmTerrainNP.setPos(-self.hmOffset, -self.hmOffset,
                                -self.hmHeight / 2.0)
        self.hmTerrainNP.flattenStrong(
        )  # This only reduces the number of nodes; nothing to do with polys.
        self.hmTerrainNP.analyze()

        # Here begins the scenery mapping
        treeModel = loader.loadModel("../res/models/tree_1.egg")
        rockModel = loader.loadModel("../res/models/rock_1.egg")
        rock2Model = loader.loadModel("../res/models/rock_2.egg")
        rock3Model = loader.loadModel("../res/models/rock_3.egg")
        # caveModel = loader.loadModel("../res/models/cave_new.egg")
        # planeFrontModel = loader.loadModel("../res/models/plane_front.egg")
        # planeWingModel = loader.loadModel("../res/models/plane_wing.egg")
        texpk = loader.loadTexture(scmPath).peek()

        # GameObject nodepath for flattening
        self.objNP = render.attachNewNode("gameObjects")
        self.treeNP = self.objNP.attachNewNode("goTrees")
        self.rockNP = self.objNP.attachNewNode("goRocks")
        self.rock2NP = self.objNP.attachNewNode("goRocks2")
        self.rock3NP = self.objNP.attachNewNode("goRocks3")
        # self.caveNP = self.objNP.attachNewNode("goCave")
        # self.planeFrontNP = self.objNP.attachNewNode("goPlaneFront")
        # self.planeWingNP = self.objNP.attachNewNode("goPlaneWing")

        for i in range(0, texpk.getXSize()):
            for j in range(0, texpk.getYSize()):
                color = VBase4(0, 0, 0, 0)
                texpk.lookup(color,
                             float(i) / texpk.getXSize(),
                             float(j) / texpk.getYSize())
                if (int(color.getX() * 255.0) == 255.0):
                    newTree = self.treeNP.attachNewNode("treeNode")
                    treeModel.instanceTo(newTree)
                    newTree.setPos(
                        i - texpk.getXSize() / 2, j - texpk.getYSize() / 2,
                        self.hmTerrain.get_elevation(i, j) * self.hmHeight -
                        self.hmHeight / 2)
                    # newTree.setScale(randint(0,4))
                    newTree.setScale(2)

                if (int(color.getX() * 255.0) == 128):
                    newRock = self.rockNP.attachNewNode("newRock")
                    newRock.setPos(
                        i - texpk.getXSize() / 2, j - texpk.getYSize() / 2,
                        self.hmTerrain.get_elevation(i, j) * self.hmHeight -
                        self.hmHeight / 2)
                    rockModel.instanceTo(newRock)

                if (int(color.getX() * 255.0) == 77):
                    newRock2 = self.rock2NP.attachNewNode("newRock2")
                    newRock2.setPos(
                        i - texpk.getXSize() / 2, j - texpk.getYSize() / 2,
                        self.hmTerrain.get_elevation(i, j) * self.hmHeight -
                        self.hmHeight / 2)
                    rock2Model.instanceTo(newRock2)

                if (int(color.getX() * 255.0) == 102):
                    newRock3 = self.rock3NP.attachNewNode("newRock3")
                    newRock3.setPos(
                        i - texpk.getXSize() / 2, j - texpk.getYSize() / 2,
                        self.hmTerrain.get_elevation(i, j) * self.hmHeight -
                        self.hmHeight / 2)
                    rock3Model.instanceTo(newRock3)

                # if(int(color.getX() * 255.0) == 64):
                # newCave = self.caveNP.attachNewNode("newCave")
                # newCave.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
                # newCave.setScale(5)
                # newCave.setP(180)
                # caveModel.instanceTo(newCave)

                # if(int(color.getX() * 255.0) == 191):
                # newPlaneFront = self.planeFrontNP.attachNewNode("newPlaneFront")
                # newPlaneFront.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
                # newPlaneFront.setScale(6)
                # planeFrontModel.instanceTo(newPlaneFront)

                # if(int(color.getX() * 255.0) == 179):
                # newPlaneWing = self.planeWingNP.attachNewNode("newPlaneWing")
                # newPlaneWing.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
                # newPlaneWing.setScale(6)
                # newPlaneWing.setH(250)
                # newPlaneWing.setR(180)
                # newPlaneWing.setP(135)
                # planeWingModel.instanceTo(newPlaneWing)

        self.snowflakes = []

        for i in xrange(0, self.snowflakeCount):
            print("Call " + str(i))
            sf = SMCollect(self.worldBullet, self.worldObj,
                           self.snowflakePositions[i])
            self.snowflakes.append(sf)

        # render.flattenStrong()
        self.hmTerrainNP.reparentTo(render)

        # Here begins the attribute mapping
        ts = TextureStage("stage-alpha")
        ts.setSort(0)
        ts.setPriority(1)
        ts.setMode(TextureStage.MReplace)
        ts.setSavedResult(True)
        self.hmTerrainNP.setTexture(ts, loader.loadTexture(imPath, smPath))

        ts = TextureStage("stage-stone")
        ts.setSort(1)
        ts.setPriority(1)
        ts.setMode(TextureStage.MReplace)
        self.hmTerrainNP.setTexture(
            ts, loader.loadTexture("../res/textures/stone_tex.png"))
        self.hmTerrainNP.setTexScale(ts, 32, 32)

        ts = TextureStage("stage-ice")
        ts.setSort(2)
        ts.setPriority(1)
        ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSTexture,
                         TextureStage.COSrcColor, TextureStage.CSPrevious,
                         TextureStage.COSrcColor,
                         TextureStage.CSLastSavedResult,
                         TextureStage.COSrcColor)
        self.hmTerrainNP.setTexture(
            ts, loader.loadTexture("../res/textures/ice_tex.png"))
        self.hmTerrainNP.setTexScale(ts, 32, 32)

        ts = TextureStage("stage-snow")
        ts.setSort(3)
        ts.setPriority(0)
        ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSTexture,
                         TextureStage.COSrcColor, TextureStage.CSPrevious,
                         TextureStage.COSrcColor,
                         TextureStage.CSLastSavedResult,
                         TextureStage.COSrcAlpha)
        self.hmTerrainNP.setTexture(
            ts, loader.loadTexture("../res/textures/snow_tex_1.png"))
        self.hmTerrainNP.setTexScale(ts, 32, 32)

        # print(self.snowflakes)

        return hmNode
Пример #14
0
	def setupHeightmap(self, name):
		
		# Automatically generate a heightmap mesh from a monochrome image.
		self.hmHeight = 120
		hmPath = "../maps/map" + name + "/map" + name + "-h.png"
		imPath = "../maps/map" + name + "/map" + name + "-i.png"
		smPath = "../maps/map" + name + "/map" + name + "-s.png"
		scmPath = "../maps/map" + name + "/map" + name + "-sc.png"
		print(hmPath)
		print(imPath)
		print(smPath)
		print(scmPath)
		hmImg = PNMImage(Filename(hmPath))
		hmShape = BulletHeightfieldShape(hmImg, self.hmHeight, ZUp)
		hmNode = BulletRigidBodyNode('Terrain')
		hmNode.addShape(hmShape)
		hmNode.setMass(0)
		self.hmNP = render.attachNewNode(hmNode)
		self.worldBullet.attachRigidBody(hmNode)
		self.hmOffset = hmImg.getXSize() / 2.0 - 0.5
		self.hmTerrain = GeoMipTerrain('gmTerrain')
		self.hmTerrain.setHeightfield(hmImg)
		
		# Optimizations and fixes
		self.hmTerrain.setBruteforce(True) # I don't think this is actually needed. 
		self.hmTerrain.setMinLevel(3) # THIS is what triangulates the terrain.
		self.hmTerrain.setBlockSize(128)  # This does a pretty good job of raising FPS.
		# Level-of-detail (not yet working)
		# self.hmTerrain.setNear(40)
		# self.hmTerrain.setFar(200)
		
		self.hmTerrain.generate()
		
		self.hmTerrainNP = self.hmTerrain.getRoot()
		self.hmTerrainNP.setSz(self.hmHeight)
		self.hmTerrainNP.setPos(-self.hmOffset, -self.hmOffset, -self.hmHeight / 2.0)
		self.hmTerrainNP.flattenStrong() # This only reduces the number of nodes; nothing to do with polys.
		self.hmTerrainNP.analyze()

		# Here begins the scenery mapping
		treeModel = loader.loadModel("../res/models/tree_1.egg")
		rockModel = loader.loadModel("../res/models/rock_1.egg")
		rock2Model = loader.loadModel("../res/models/rock_2.egg")
		rock3Model = loader.loadModel("../res/models/rock_3.egg")
		# caveModel = loader.loadModel("../res/models/cave_new.egg")
		# planeFrontModel = loader.loadModel("../res/models/plane_front.egg")
		# planeWingModel = loader.loadModel("../res/models/plane_wing.egg")
		texpk = loader.loadTexture(scmPath).peek()
		
		# GameObject nodepath for flattening
		self.objNP = render.attachNewNode("gameObjects")
		self.treeNP = self.objNP.attachNewNode("goTrees")
		self.rockNP = self.objNP.attachNewNode("goRocks")
		self.rock2NP = self.objNP.attachNewNode("goRocks2")
		self.rock3NP = self.objNP.attachNewNode("goRocks3")
		# self.caveNP = self.objNP.attachNewNode("goCave")
		# self.planeFrontNP = self.objNP.attachNewNode("goPlaneFront")
		# self.planeWingNP = self.objNP.attachNewNode("goPlaneWing")
		
		for i in range(0, texpk.getXSize()):
			for j in range(0, texpk.getYSize()):
				color = VBase4(0, 0, 0, 0)
				texpk.lookup(color, float(i) / texpk.getXSize(), float(j) / texpk.getYSize())
				if(int(color.getX() * 255.0) == 255.0):
					newTree = self.treeNP.attachNewNode("treeNode")
					treeModel.instanceTo(newTree)
					newTree.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					# newTree.setScale(randint(0,4))
					newTree.setScale(2)
					
				if(int(color.getX() * 255.0) == 128):
					newRock = self.rockNP.attachNewNode("newRock")
					newRock.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					rockModel.instanceTo(newRock)
					
				if(int(color.getX() * 255.0) == 77):
					newRock2 = self.rock2NP.attachNewNode("newRock2")
					newRock2.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					rock2Model.instanceTo(newRock2)
					
				if(int(color.getX() * 255.0) == 102):
					newRock3 = self.rock3NP.attachNewNode("newRock3")
					newRock3.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					rock3Model.instanceTo(newRock3)
					
				# if(int(color.getX() * 255.0) == 64):
					# newCave = self.caveNP.attachNewNode("newCave")
					# newCave.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					# newCave.setScale(5)
					# newCave.setP(180)
					# caveModel.instanceTo(newCave)
				
				# if(int(color.getX() * 255.0) == 191):
					# newPlaneFront = self.planeFrontNP.attachNewNode("newPlaneFront")
					# newPlaneFront.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					# newPlaneFront.setScale(6)
					# planeFrontModel.instanceTo(newPlaneFront)
					
				# if(int(color.getX() * 255.0) == 179):
					# newPlaneWing = self.planeWingNP.attachNewNode("newPlaneWing")
					# newPlaneWing.setPos(i - texpk.getXSize() / 2, j - texpk.getYSize() / 2, self.hmTerrain.get_elevation(i, j) * self.hmHeight - self.hmHeight / 2)
					# newPlaneWing.setScale(6)
					# newPlaneWing.setH(250)
					# newPlaneWing.setR(180)
					# newPlaneWing.setP(135)
					# planeWingModel.instanceTo(newPlaneWing)

		self.snowflakes = []
		
		for i in xrange(0, self.snowflakeCount):
			print("Call " + str(i))
			sf = SMCollect(self.worldBullet, self.worldObj, self.snowflakePositions[i])
			self.snowflakes.append(sf)
					
		# render.flattenStrong()
		self.hmTerrainNP.reparentTo(render)
		
		# Here begins the attribute mapping
		ts = TextureStage("stage-alpha")
		ts.setSort(0)
		ts.setPriority(1)
		ts.setMode(TextureStage.MReplace)
		ts.setSavedResult(True)
		self.hmTerrainNP.setTexture(ts, loader.loadTexture(imPath, smPath))

		ts = TextureStage("stage-stone")
		ts.setSort(1)
		ts.setPriority(1)
		ts.setMode(TextureStage.MReplace)
		self.hmTerrainNP.setTexture(ts, loader.loadTexture("../res/textures/stone_tex.png"))
		self.hmTerrainNP.setTexScale(ts, 32, 32)

		ts = TextureStage("stage-ice")
		ts.setSort(2)
		ts.setPriority(1)
		ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSTexture, TextureStage.COSrcColor,
						 TextureStage.CSPrevious, TextureStage.COSrcColor,
						 TextureStage.CSLastSavedResult, TextureStage.COSrcColor)
		self.hmTerrainNP.setTexture(ts, loader.loadTexture("../res/textures/ice_tex.png"))
		self.hmTerrainNP.setTexScale(ts, 32, 32)

		ts = TextureStage("stage-snow")
		ts.setSort(3)
		ts.setPriority(0)
		ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSTexture, TextureStage.COSrcColor,
						 TextureStage.CSPrevious, TextureStage.COSrcColor,
						 TextureStage.CSLastSavedResult, TextureStage.COSrcAlpha)
		self.hmTerrainNP.setTexture(ts, loader.loadTexture("../res/textures/snow_tex_1.png"))
		self.hmTerrainNP.setTexScale(ts, 32, 32)

		# print(self.snowflakes)
		
		return hmNode
Пример #15
0
class TerrainManager(DirectObject.DirectObject):
    def __init__(self):
        self.accept("mouse1", self.lclick)
        self.waterType = 2
        self.water = None
        self.citycolors = {0: VBase3D(1, 1, 1)}
    
        self.accept('generateRegion', self.generateWorld)
        self.accept('regenerateRegion', self.regenerateWorld)
        self.accept("regionView_normal", self.setSurfaceTextures)
        self.accept("regionView_owners", self.setOwnerTextures)
        self.accept("regionView_foundNew", self.regionViewFound)
        self.accept("updateRegion", self.updateRegion)
        self.accept("enterCityView", self.enterCity)
        
        # View: 0, region, # cityid
        self.view = 0
        self.ownerview = False
    
    def lclick(self):
        cell = picker.getMouseCell()
        print "Cell:", cell
        blockCoords = self.terrain.getBlockFromPos(cell[0], cell[1])
        block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1])
        print "Block coords:", blockCoords
        print "NodePath:", block
        print "Elevation:", self.terrain.getElevation(cell[0], cell[1])
        if not self.view:
            messenger.send("clickForCity", [cell])
    
    def switchWater(self):
        print "Switch Water"
        self.waterType += 1
        if self.waterType > 2:
            self.waterType = 0
        self.generateWater(self.waterType)
    
    def generateWorld(self, heightmap, tiles, cities, container):
        self.heightmap = heightmap
        self.terrain = PagedGeoMipTerrain("surface")
        #self.terrain = GeoMipTerrain("surface")
        self.terrain.setHeightfield(self.heightmap)
        #self.terrain.setFocalPoint(base.camera)
        self.terrain.setBruteforce(True)
        self.terrain.setBlockSize(64)
        self.terrain.generate()

        root = self.terrain.getRoot()
        root.reparentTo(render)
        #root.setSz(100)
        self.terrain.setSz(100)
        messenger.send('makePickable', [root])   
        
        if self.heightmap.getXSize() > self.heightmap.getYSize():
            self.size = self.heightmap.getXSize()-1
        else:
            self.size = self.heightmap.getYSize()-1
        self.xsize = self.heightmap.getXSize()-1
        self.ysize = self.heightmap.getYSize()-1
                
        # Set multi texture
        # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536
        self.generateSurfaceTextures()
        self.generateWaterMap()
        self.generateOwnerTexture(tiles, cities)
        #self.terrain.makeTextureMap()
        colormap = PNMImage(heightmap.getXSize()-1, heightmap.getYSize()-1)
        colormap.addAlpha()
        slopemap = self.terrain.makeSlopeImage()
        for x in range(0, colormap.getXSize()):
            for y in range(0, colormap.getYSize()):
                # Else if statements used to make sure one channel is used per pixel
                # Also for some optimization
                # Snow. We do things funky here as alpha will be 1 already.
                if heightmap.getGrayVal(x, y) < 200:
                    colormap.setAlpha(x, y, 0)
                else:
                    colormap.setAlpha(x, y, 1)
                # Beach. Estimations from http://www.simtropolis.com/omnibus/index.cfm/Main.SimCity_4.Custom_Content.Custom_Terrains_and_Using_USGS_Data
                if heightmap.getGrayVal(x,y) < 62:
                    colormap.setBlue(x, y, 1)
                # Rock
                elif slopemap.getGrayVal(x, y) > 170:
                    colormap.setRed(x, y, 1)
                else:
                    colormap.setGreen(x, y, 1)
        self.colorTexture = Texture()
        self.colorTexture.load(colormap)
        self.colorTS = TextureStage('color')
        self.colorTS.setSort(0)
        self.colorTS.setPriority(1)
            
        self.setSurfaceTextures()
        self.generateWater(2)
        taskMgr.add(self.updateTerrain, "updateTerrain")
        print "Done with terrain generation"
        messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]])
        self.terrain.getRoot().analyze()
        self.accept("h", self.switchWater)
    
    def regenerateWorld(self):
        '''Regenerates world, often upon city exit.'''
        self.terrain.generate()
        root = self.terrain.getRoot()
        root.reparentTo(render)
        self.terrain.setSz(100)
        messenger.send('makePickable', [root])   
        # Set multi texture
        # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536
        #self.generateSurfaceTextures()
        self.generateWaterMap()
        
        self.setSurfaceTextures()
        self.generateWater(2)
        print "Done with terrain regeneration"
        messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]])
    
    def generateWaterMap(self):
        ''' Iterate through every pix of color map. This will be very slow so until faster method is developed, use sparingly
        getXSize returns pixels length starting with 1, subtract 1 for obvious reasons
        We also slip in checking for the water card size, which should only change when the color map does
        '''
        print "GenerateWaterMap"
        self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax = -1,0,-1,0
        for x in range(0, self.heightmap.getXSize()-1):
            for y in range(0, self.heightmap.getYSize()-1):
                # Else if statements used to make sure one channel is used per pixel
                # Also for some optimization
                # Snow. We do things funky here as alpha will be 1 already.
                if self.heightmap.getGrayVal(x,y) < 62:
                    # Water card dimensions here
                    # Y axis flipped from texture space to world space
                    if self.waterXMin == -1 or x < self.waterXMin:
                        self.waterXMin = x
                    if not self.waterYMax:
                        self.waterYMax = y
                    if y < self.waterYMax:
                        self.waterYMax = y
                    if x > self.waterXMax:
                        self.waterXMax = x
                    if y > self.waterYMin:
                        self.waterYMin = y
        # Transform y coords
        self.waterYMin = self.size-64 - self.waterYMin
        self.waterYMax = self.size-64 - self.waterYMax
    
    def generateOwnerTexture(self, tiles, cities):
        '''Generates a simple colored texture to be applied to the city info region overlay.
        Due to different coordinate systems (terrain org bottom left, texture top left)
        some conversions are needed,
        
        Also creates and sends a citylabels dict for the region view
        '''
        self.citymap = PNMImage(self.xsize, self.ysize)
        citylabels = cities
        scratch = {}
        
        # Setup for city labels
        for ident in cities:
            scratch[ident] = []
        
        # conversion for y axis
        ycon = []
        s = self.ysize - 1
        for y in range(self.ysize):
            ycon.append(s)
            s -= 1
        for ident, city in cities.items():
            if ident not in self.citycolors:
                self.citycolors[ident] = VBase3D(random.random(), random.random(), random.random())
        for tile in tiles:
            self.citymap.setXel(tile.coords[0], ycon[tile.coords[1]], self.citycolors[tile.cityid])
            # Scratch for labeling
            if tile.cityid:
                scratch[tile.cityid].append((tile.coords[0], tile.coords[1]))
        for ident, values in scratch.items():
            xsum = 0
            ysum = 0
            n = 0
            for coords in values:
                xsum += coords[0]
                ysum += coords[1]
                n += 1
            xavg = xsum/n
            yavg = ysum/n
            print "Elevation:", self.terrain.getElevation(xavg, yavg)
            z = self.terrain.getElevation(xavg, yavg)*100
            citylabels[ident]["position"] = (xavg, yavg, z+15)
            print "Citylabels:", citylabels
        messenger.send("updateCityLabels", [citylabels, self.terrain])
    
    def generateSurfaceTextures(self):
        # Textureize
        self.grassTexture = loader.loadTexture("Textures/grass.png")
        self.grassTS = TextureStage('grass')
        self.grassTS.setSort(1)
        
        self.rockTexture = loader.loadTexture("Textures/rock.jpg")
        self.rockTS = TextureStage('rock')
        self.rockTS.setSort(2)
        self.rockTS.setCombineRgb(TextureStage.CMAdd, TextureStage.CSLastSavedResult, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor)
        
        self.sandTexture = loader.loadTexture("Textures/sand.jpg")
        self.sandTS = TextureStage('sand')
        self.sandTS.setSort(3)
        self.sandTS.setPriority(5)
        
        self.snowTexture = loader.loadTexture("Textures/ice.png")
        self.snowTS = TextureStage('snow')
        self.snowTS.setSort(4)
        self.snowTS.setPriority(0)
        
        # Grid for city placement and guide and stuff
        self.gridTexture = loader.loadTexture("Textures/grid.png")
        self.gridTexture.setWrapU(Texture.WMRepeat)
        self.gridTexture.setWrapV(Texture.WMRepeat)
        self.gridTS = TextureStage('grid')
        self.gridTS.setSort(5)
        self.gridTS.setPriority(10)
    
    def enterCity(self, ident, city, position, tiles):
        '''Identifies which terrain blocks city belogs to and disables those that are not
        A lot of uneeded for loops in here. Will need to find a better way later.'''
        #root = self.terrain.getRoot()
        children = []
        for terrain in self.terrain.terrains:
            root = terrain.getRoot()
            children += root.getChildren()
        keepBlocks = []
        # Reset water dimentions
        self.waterXMin = 0
        self.waterXMax = 0
        self.waterYMin = 0
        self.waterYMax = 0
        
        for tile in tiles:
            blockCoords = self.terrain.getBlockFromPos(tile.coords[0], tile.coords[1])
            block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1])
            if block not in keepBlocks:
                keepBlocks.append(block)
            if self.heightmap.getGrayVal(tile.coords[0], self.size-tile.coords[1]) < 62:
                # Water card dimensions here
                # Y axis flipped from texture space to world space
                if not self.waterXMin:
                    self.waterXMin = tile.coords[0]
                if not self.waterYMin:
                    self.waterYMin = tile.coords[1]
                if tile.coords[1] > self.waterYMax:
                    self.waterYMax = tile.coords[1]
                if tile.coords[0] > self.waterXMax:
                    self.waterXMax = tile.coords[0]
                if tile.coords[0] < self.waterXMin:
                    self.waterXMin = tile.coords[0]
                if tile.coords[1] < self.waterYMin:
                    self.waterYMin = tile.coords[1]
        for child in children:
            if child not in keepBlocks:
                child.detachNode()
        self.view = ident
        self.generateWater(2)
    
    def newTerrainOverlay(self, task):
        root = self.terrain.getRoot()
        position = picker.getMouseCell()
        if position:
            # Check to make sure we do not go out of bounds
            if position[0] < 32:
                position = (32, position[1])
            elif position[0] > self.xsize-32:
                position = (self.xsize-32, position[1])
            if position[1] < 32:
                position = (position[0], 32)
            elif position [1] > self.ysize-32:
                position = (position[0], self.size-32)                
            root.setTexOffset(self.tileTS, -(position[0]-32)/64, -(position[1]-32)/64)
        return task.cont
    
    def regionViewFound(self):
        '''Gui for founding a new city!'''
        self.setOwnerTextures()
        root = self.terrain.getRoot()
        task = taskMgr.add(self.newTerrainOverlay, "newTerrainOverlay")
        tileTexture = loader.loadTexture("Textures/tile.png")
        tileTexture.setWrapU(Texture.WMClamp)
        tileTexture.setWrapV(Texture.WMClamp)
        self.tileTS = TextureStage('tile')
        self.tileTS.setSort(6)
        self.tileTS.setMode(TextureStage.MDecal)
        #self.tileTS.setColor(Vec4(1,0,1,1))
        root.setTexture(self.tileTS, tileTexture)
        root.setTexScale(self.tileTS, self.terrain.xchunks, self.terrain.ychunks)
        self.acceptOnce("mouse1", self.regionViewFound2)
        self.acceptOnce("escape", self.cancelRegionViewFound)
    
    def regionViewFound2(self):
        '''Grabs cell location for founding.
        The texture coordinate is used as the mouse may enter an out of bounds area.
        '''
        root = self.terrain.getRoot()
        root_position = root.getTexOffset(self.tileTS)
        # We offset the position of the texture, so we will now put the origin of the new city not on mouse cursor but the "bottom left" of it. Just need to add 32 to get other edge
        position = [int(abs(root_position[0]*64)), int(abs(root_position[1]*64))]
        self.cancelRegionViewFound()
        messenger.send("found_city_name", [position])
    
    def cancelRegionViewFound(self):
        taskMgr.remove("newTerrainOverlay")
        root = self.terrain.getRoot()
        root.clearTexture(self.tileTS)
        # Restore original mouse function
        self.accept("mouse1", self.lclick)
        messenger.send("showRegionGUI")
    
    def setSurfaceTextures(self):
        self.ownerview = False
        root = self.terrain.getRoot()
        root.clearTexture()
        #self.terrain.setTextureMap()
        root.setTexture( self.colorTS, self.colorTexture )
        root.setTexture( self.grassTS, self.grassTexture )
        root.setTexScale(self.grassTS, self.size/8, self.size/8) 
        root.setTexture( self.rockTS, self.rockTexture ) 
        root.setTexScale(self.rockTS, self.size/8, self.size/8) 
        root.setTexture( self.sandTS, self.sandTexture) 
        root.setTexScale(self.sandTS, self.size/8, self.size/8) 
        root.setTexture( self.snowTS, self.snowTexture ) 
        root.setTexScale(self.snowTS, self.size/8, self.size/8) 
        root.setTexture( self.gridTS, self.gridTexture ) 
        root.setTexScale(self.gridTS, self.xsize, self.ysize)
        root.setShaderInput('size', self.xsize, self.ysize, self.size, self.size)
        root.setShader(loader.loadShader('Shaders/terraintexture.sha'))
    
    def setOwnerTextures(self):
        self.ownerview = True
        root = self.terrain.getRoot()
        root.clearShader()
        root.clearTexture()
        cityTexture = Texture()
        cityTexture.load(self.citymap)
        cityTS = TextureStage('citymap')
        cityTS.setSort(0)
        root.setTexture( self.gridTS, self.gridTexture ) 
        root.setTexScale(self.gridTS, self.terrain.xchunks, self.terrain.ychunks)
        root.setTexture(cityTS, cityTexture, 1)
    
    def updateRegion(self, heightmap, tiles, cities):
        self.generateOwnerTexture(tiles, cities)
        if self.ownerview:
            self.setOwnerTextures()
    
    def updateTerrain(self, task):
        '''Updates terrain and water'''
        self.terrain.update()
        # Water
        if self.waterType is 2:
            pos = base.camera.getPos()
            render.setShaderInput('time', task.time)
            mc = base.camera.getMat( )
            self.water.changeCameraPos(pos,mc)
            self.water.changeCameraPos(pos,mc)
        #print "Render diagnostics"
        #render.analyze()
        #base.cTrav.showCollisions(render)
        return task.cont 
    
    def generateWater(self, style):
        print "Generate Water:", self.waterXMin, self.waterXMax, self.waterYMin,  self.waterYMax
        '''Generates water
        style 0: blue card
        style 1: reflective card
        style 2: reflective card with shaders
        '''
        self.waterHeight = 22.0
        if self.water:
            self.water.removeNode()
        if style is 0:
            cm = CardMaker("water")
            #cm.setFrame(-1, 1, -1, 1)
            cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin,  self.waterYMax)
            cm.setColor(0, 0, 1, 0.9)
            self.water = render.attachNewNode(cm.generate())
            if self.waterYMax > self.waterXMax:
                size = self.waterYMax
            else:
                size = self.waterXMax
            self.water.lookAt(0, 0, -1)
            self.water.setZ(self.waterHeight)
            messenger.send('makePickable', [self.water])
        elif style is 1:
            # From Prosoft's super awesome terrain demo
            cm = CardMaker("water")
            #cm.setFrame(-1, 1, -1, 1)
            cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin,  self.waterYMax)
            self.water = render.attachNewNode(cm.generate())
            if self.waterYMax > self.waterXMax:
                size = self.waterYMax
            else:
                size = self.waterXMax
            #self.water.setScale(size)
            self.water.lookAt(0, 0, -1)
            self.water.setZ(self.waterHeight)
            self.water.setShaderOff(1)
            self.water.setLightOff(1)
            self.water.setAlphaScale(0.5)
            self.water.setTransparency(TransparencyAttrib.MAlpha)
            wbuffer = base.win.makeTextureBuffer("water", 512, 512)
            wbuffer.setClearColorActive(True)
            wbuffer.setClearColor(base.win.getClearColor())
            self.wcamera = base.makeCamera(wbuffer)
            self.wcamera.reparentTo(render)
            self.wcamera.node().setLens(base.camLens)
            self.wcamera.node().setCameraMask(BitMask32.bit(1))
            self.water.hide(BitMask32.bit(1))
            wtexture = wbuffer.getTexture()
            wtexture.setWrapU(Texture.WMClamp)
            wtexture.setWrapV(Texture.WMClamp)
            wtexture.setMinfilter(Texture.FTLinearMipmapLinear)
            self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, self.water.getZ()))
            wplanenp = render.attachNewNode(PlaneNode("water", self.wplane))
            tmpnp = NodePath("StateInitializer")
            tmpnp.setClipPlane(wplanenp)
            tmpnp.setAttrib(CullFaceAttrib.makeReverse())
            self.wcamera.node().setInitialState(tmpnp.getState())
            self.water.projectTexture(TextureStage("reflection"), wtexture, self.wcamera)
            messenger.send('makePickable', [self.water])
        elif style is 2:
            # From Clcheung just as super awesome demomaster
            self.water_level = Vec4(0.0, 0.0, self.waterHeight, 1.0)
            self.water = water.WaterNode(self.waterXMin, self.waterYMin, self.waterXMax, self.waterYMax, self.water_level.getZ())
            self.water.setStandardControl()
            self.water.changeParams(None)
            wl=self.water_level
            wl.setZ(wl.getZ()-0.05)
            #root.setShaderInput('waterlevel', self.water_level)
            render.setShaderInput('time', 0)
            messenger.send('makePickable', [self.water.waterNP])
Пример #16
0
class LeftDrift(ShowBase):
    """
    Show drifting textures forever to left eye.
    """
    def __init__(self, texture_array, texture_angle = 0, mask_angle = 0, position = (0, 0), velocity = 0,
                 window_size = 512, texture_size = 512, bgcolor = (0, 0, 0, 1)):
        super().__init__()

        self.texture_array = texture_array
        self.texture_dtype = type(self.texture_array.flat[0])
        self.left_angle = texture_angle
        self.velocity = velocity
        self.mask_angle = mask_angle #this will change fairly frequently
        
        #Set window title and size
        self.window_properties = WindowProperties()
        self.window_properties.setSize(window_size, window_size)
        self.window_properties.setTitle("LeftDrift")
        ShowBaseGlobal.base.win.requestProperties(self.window_properties)  #base is a panda3d global
        
        #CREATE MASKS (right mask for left stim, and vice-versa)
        self.right_mask = np.ones((texture_size,texture_size), dtype=np.uint8)    #was 255* this but for modulate 
        self.right_mask[:, texture_size//2: ] = 0   #texture_size//2:] = 0  
        #print(self.right_mask)

        #CREATE TEXTURE STAGES
        #Stimuli
        self.grating_texture = Texture("Grating")  #T_unsigned_byte
        self.grating_texture.setup2dTexture(texture_size, texture_size, 
                                            Texture.T_unsigned_byte, Texture.F_luminance) 
        self.grating_texture.setRamImage(self.texture_array)   
        self.left_texture_stage = TextureStage('grating')
        #Mask to restrict stimulus to LHS
        self.right_mask_texture = Texture("right_mask")
        self.right_mask_texture.setup2dTexture(texture_size, texture_size, 
                                               Texture.T_unsigned_byte, Texture.F_luminance) 
        self.right_mask_texture.setRamImage(self.right_mask)  
        self.right_mask_stage = TextureStage('right_mask')
                                                                           
        #CREATE CARDS/SCENEGRAPH
        cm = CardMaker('left_stim_card')
        cm.setFrameFullscreenQuad()
        self.left_texture_card = self.aspect2d.attachNewNode(cm.generate())
        self.right_mask_card = self.aspect2d.attachNewNode(cm.generate())
        
        #SET TEXTURE STAGES
        self.left_texture_card.setTexture(self.left_texture_stage, self.grating_texture)  
        self.right_mask_card.setTexture(self.right_mask_stage, self.right_mask_texture)

        
        #BASIC TRANSFORMS to set up angles and position 
        #TEXTURE
        self.left_texture_card.setScale(np.sqrt(8))  #so it can handle arbitrary rotations and shifts    
        self.left_texture_card.setR(self.left_angle) 
        
        #MASK
        #self.right_card.setPos(position[0], 0, position[1])
        self.right_mask_card.setScale(np.sqrt(8))
        self.right_mask_card.setR(self.mask_angle)
        self.right_mask_card.setPos(position[0], 0, position[1] ) # ndc2uv(position[0]), ndc2uv(position[1]))

        self.right_mask_card.setTransparency(TransparencyAttrib.MAlpha)  #enable transparency
        #Combine the two cards (take product of mask and texture)
        operand = TextureStage.COSrcColor
        source0 = self.right_mask_stage
        source1 = self.left_texture_stage
        self.right_mask_stage.setCombineRgb(TextureStage.CMModulate, source0, operand, source1, operand)
        #self.left_texture_card.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add))
        #self.left_texture_card.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add))
        
        #This puts a marker where the stimulus should end
        self.title = OnscreenText("x",
                                  style = 1,
                                  fg = (1,1,1,1),
                                  bg = bgcolor,
                                  pos = (position[0], position[1]), 
                                  scale = 0.02)
        
        #Add texture move procedure to the task manager, if needed
        if self.velocity != 0:
            self.taskMgr.add(self.moveTextureTask, "moveTextureTask")
        
    #Procedure to move the texture
    def moveTextureTask(self, task):
        new_position = -task.time*self.velocity #not sure why negative
        self.left_texture_card.setTexPos(self.left_texture_stage, new_position, 0, 0) #u, v, w
        return Task.cont #as long as this is returned, the taskMgr will continue to call it