Exemple #1
0
 def getTile(self, xStart, yStart, scale):
     """
     returns a tile for the specified positions and size
     """
     sizeY = tileMapSize
     sizeX = tileMapSize
     getHeight = self.getHeight
     
     noiseTex=Texture("NoiseTex")
     noiseTex.setup2dTexture(sizeX, sizeY, Texture.TUnsignedByte, Texture.FRgb)
     p=noiseTex.modifyRamImage()
     step=noiseTex.getNumComponents()*noiseTex.getComponentWidth()
     scalar=.4
     for y in range(sizeY):
         yPos=scalar*(1.0*y*scale/(sizeY-1)+yStart)
         for x in range(sizeX):
             height = getHeight(scalar*(1.0*x*scale/(sizeX-1) + xStart), yPos)
             r=min(255,max(0,height*256))
             g=r*256
             b=g*256
             index = (sizeX * y + x)*step
             p.setElement(index, b%256)#Blue
             p.setElement(index+1, g%256)#Green
             p.setElement(index+2, r)#Red
     
     return Tile({"height":Map("height", noiseTex)},[], xStart, yStart, scale)
class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # Load the environment model.
        self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        self.scene.setScale(0.25, 0.25, 0.25)
        self.scene.setPos(-8, 42, 0)

        # Needed for camera image
        self.dr = self.camNode.getDisplayRegion(0)

        # Needed for camera depth image
        winprops = WindowProperties.size(self.win.getXSize(),
                                         self.win.getYSize())
        fbprops = FrameBufferProperties()
        fbprops.setDepthBits(1)
        self.depthBuffer = self.graphicsEngine.makeOutput(
            self.pipe, "depth buffer", -2, fbprops, winprops,
            GraphicsPipe.BFRefuseWindow, self.win.getGsg(), self.win)
        self.depthTex = Texture()
        self.depthTex.setFormat(Texture.FDepthComponent)
        self.depthBuffer.addRenderTexture(self.depthTex,
                                          GraphicsOutput.RTMCopyRam,
                                          GraphicsOutput.RTPDepth)
        lens = self.cam.node().getLens()
        # the near and far clipping distances can be changed if desired
        # lens.setNear(5.0)
        # lens.setFar(500.0)
        self.depthCam = self.makeCamera(self.depthBuffer,
                                        lens=lens,
                                        scene=render)
        self.depthCam.reparentTo(self.cam)

        # TODO: Scene is rendered twice: once for rgb and once for depth image.
        # How can both images be obtained in one rendering pass?

    def get_camera_image(self, requested_format=None):
        """
        Returns the camera's image, which is of type uint8 and has values
        between 0 and 255.
        The 'requested_format' argument should specify in which order the
        components of the image must be. For example, valid format strings are
        "RGBA" and "BGRA". By default, Panda's internal format "BGRA" is used,
        in which case no data is copied over.
        """
        tex = self.dr.getScreenshot()
        if requested_format is None:
            data = tex.getRamImage()
        else:
            data = tex.getRamImageAs(requested_format)
        image = np.frombuffer(
            data, np.uint8)  # use data.get_data() instead of data in python 2
        image.shape = (tex.getYSize(), tex.getXSize(), tex.getNumComponents())
        image = np.flipud(image)
        return image

    def get_camera_depth_image(self):
        """
        Returns the camera's depth image, which is of type float32 and has
        values between 0.0 and 1.0.
        """
        data = self.depthTex.getRamImage()
        depth_image = np.frombuffer(data, np.float32)
        depth_image.shape = (self.depthTex.getYSize(),
                             self.depthTex.getXSize(),
                             self.depthTex.getNumComponents())
        depth_image = np.flipud(depth_image)
        return depth_image
Exemple #3
0
class Panda3dCameraSensor(object):
    def __init__(self,
                 base,
                 color=True,
                 depth=False,
                 size=None,
                 near_far=None,
                 hfov=None,
                 title=None):
        if size is None:
            size = (640, 480)
        if near_far is None:
            near_far = (0.01, 10000.0)
        if hfov is None:
            hfov = 60
        winprops = WindowProperties.size(*size)
        winprops.setTitle(title or 'Camera Sensor')
        fbprops = FrameBufferProperties()
        # Request 8 RGB bits, 8 alpha bits, and a depth buffer.
        fbprops.setRgbColor(True)
        fbprops.setRgbaBits(8, 8, 8, 8)
        fbprops.setDepthBits(24)
        self.graphics_engine = GraphicsEngine(base.pipe)

        window_type = base.config.GetString('window-type', 'onscreen')
        flags = GraphicsPipe.BFFbPropsOptional
        if window_type == 'onscreen':
            flags = flags | GraphicsPipe.BFRequireWindow
        elif window_type == 'offscreen':
            flags = flags | GraphicsPipe.BFRefuseWindow

        self.buffer = self.graphics_engine.makeOutput(base.pipe,
                                                      "camera sensor buffer",
                                                      -100, fbprops, winprops,
                                                      flags)

        if not color and not depth:
            raise ValueError("At least one of color or depth should be True")
        if color:
            self.color_tex = Texture("color_texture")
            self.buffer.addRenderTexture(self.color_tex,
                                         GraphicsOutput.RTMCopyRam,
                                         GraphicsOutput.RTPColor)
        else:
            self.color_tex = None
        if depth:
            self.depth_tex = Texture("depth_texture")
            self.buffer.addRenderTexture(self.depth_tex,
                                         GraphicsOutput.RTMCopyRam,
                                         GraphicsOutput.RTPDepth)
        else:
            self.depth_tex = None

        self.cam = base.makeCamera(self.buffer,
                                   scene=base.render,
                                   camName='camera_sensor')
        self.lens = self.cam.node().getLens()
        self.lens.setFov(hfov)
        self.lens.setFilmSize(
            *size)  # this also defines the units of the focal length
        self.lens.setNearFar(*near_far)

    def observe(self):
        for _ in range(self.graphics_engine.getNumWindows()):
            self.graphics_engine.renderFrame()
        self.graphics_engine.syncFrame()

        images = []

        if self.color_tex:
            data = self.color_tex.getRamImageAs('RGBA')
            if sys.version_info < (3, 0):
                data = data.get_data()
            image = np.frombuffer(data, np.uint8)
            image.shape = (self.color_tex.getYSize(),
                           self.color_tex.getXSize(),
                           self.color_tex.getNumComponents())
            image = np.flipud(image)
            image = image[
                ..., :
                -1]  # remove alpha channel; if alpha values are needed, set alpha bits to 8
            images.append(image)

        if self.depth_tex:
            depth_data = self.depth_tex.getRamImage()
            if sys.version_info < (3, 0):
                depth_data = depth_data.get_data()
            depth_image_size = self.depth_tex.getYSize(
            ) * self.depth_tex.getXSize() * self.depth_tex.getNumComponents()
            if len(depth_data) == 2 * depth_image_size:
                dtype = np.float16
            elif len(depth_data) == 3 * depth_image_size:
                dtype = np.float24
            elif len(depth_data) == 4 * depth_image_size:
                dtype = np.float32
            else:
                raise ValueError(
                    "Depth data has %d bytes but the size of the depth image is %d"
                    % (len(depth_data), depth_image_size))
            depth_image = np.frombuffer(depth_data, dtype)
            depth_image.shape = (self.depth_tex.getYSize(),
                                 self.depth_tex.getXSize(),
                                 self.depth_tex.getNumComponents())
            depth_image = np.flipud(depth_image)
            depth_image = depth_image.astype(
                np.float32, copy=False)  # copy only if necessary
            images.append(depth_image)

        return tuple(images)
Exemple #4
0
class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # Load the environment model.
        self.setup_environment()
        #self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        #self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        #self.scene.setScale(0.25, 0.25, 0.25)
        #self.scene.setPos(-8, 42, 0)

        # Needed for camera image
        self.dr = self.camNode.getDisplayRegion(0)

        # Needed for camera depth image
        winprops = WindowProperties.size(self.win.getXSize(),
                                         self.win.getYSize())
        fbprops = FrameBufferProperties()
        fbprops.setDepthBits(1)
        self.depthBuffer = self.graphicsEngine.makeOutput(
            self.pipe, "depth buffer", -2, fbprops, winprops,
            GraphicsPipe.BFRefuseWindow, self.win.getGsg(), self.win)
        self.depthTex = Texture()
        self.depthTex.setFormat(Texture.FDepthComponent)
        self.depthBuffer.addRenderTexture(self.depthTex,
                                          GraphicsOutput.RTMCopyRam,
                                          GraphicsOutput.RTPDepth)
        lens = self.cam.node().getLens()
        lens.setFov(90.0, 90.0)
        # the near and far clipping distances can be changed if desired
        # lens.setNear(5.0)
        # lens.setFar(500.0)
        self.depthCam = self.makeCamera(self.depthBuffer,
                                        lens=lens,
                                        scene=self.render)
        self.depthCam.reparentTo(self.cam)

        # TODO: Scene is rendered twice: once for rgb and once for depth image.
        # How can both images be obtained in one rendering pass?
        self.render.setAntialias(AntialiasAttrib.MAuto)

    def setup_environment(self):
        # encapsulate some stuff

        # set up ambient lighting
        self.alight = AmbientLight('alight')
        self.alight.setColor(VBase4(0.1, 0.1, 0.1, 1))
        self.alnp = self.render.attachNewNode(self.alight)
        self.render.setLight(self.alnp)

        # set up a point light
        self.plight = PointLight('plight')
        self.plight.setColor(VBase4(0.8, 0.8, 0.8, 1))
        self.plnp = self.render.attachNewNode(self.plight)
        self.plnp.setPos(0, 0, 100)
        self.render.setLight(self.plnp)

        # set up terrain model
        self.terr_material = Material()
        self.terr_material.setShininess(1.0)
        self.terr_material.setAmbient(VBase4(0, 0, 0, 0))
        self.terr_material.setDiffuse(VBase4(1, 1, 1, 1))
        self.terr_material.setEmission(VBase4(0, 0, 0, 0))
        self.terr_material.setSpecular(VBase4(0, 0, 0, 0))

        # general scaling
        self.trrHorzSc = 4.0
        self.trrVertSc = 4.0  # was 4.0

        # Create sky
        #terrctr = self.trrHorzSc*65.0
        #self.setup_skybox(terrctr,800.0,2.0,0.3)

        self.skysphere = self.loader.loadModel("sky-forest/SkySphere.bam")
        self.skysphere.setBin('background', 1)
        self.skysphere.setDepthWrite(0)
        self.skysphere.reparentTo(self.render)

        # Load some textures
        self.grsTxtSc = 5
        self.numTreeTexts = 7

        # ground texture
        self.txtGrass = self.loader.loadTexture('tex/ground005.png')
        self.txtGrass.setWrapU(Texture.WM_mirror)
        self.txtGrass.setWrapV(Texture.WM_mirror)
        self.txtGrass.setMagfilter(Texture.FTLinear)
        self.txtGrass.setMinfilter(Texture.FTLinearMipmapLinear)

        # set up terrain texture stages
        self.TS1 = TextureStage('terrtext')
        self.TS1.setSort(0)
        self.TS1.setMode(TextureStage.MReplace)

        # Set up the GeoMipTerrain
        self.terrain = GeoMipTerrain("myDynamicTerrain")
        img = PNMImage(Filename('tex/bowl_height_map.png'))
        self.terrain.setHeightfield(img)
        self.terrain.setBruteforce(0)
        self.terrain.setAutoFlatten(GeoMipTerrain.AFMMedium)

        # Set terrain properties
        self.terrain.setBlockSize(32)
        self.terrain.setNear(50)
        self.terrain.setFar(500)
        self.terrain.setFocalPoint(self.camera)

        # Store the root NodePath for convenience
        self.root = self.terrain.getRoot()
        self.root.clearTexture()
        self.root.setTwoSided(0)
        self.root.setCollideMask(BitMask32.bit(0))
        self.root.setSz(self.trrVertSc)
        self.root.setSx(self.trrHorzSc)
        self.root.setSy(self.trrHorzSc)
        self.root.setMaterial(self.terr_material)
        self.root.setTexture(self.TS1, self.txtGrass)
        self.root.setTexScale(self.TS1, self.grsTxtSc, self.grsTxtSc)
        offset = 0.5 * img.getXSize() * self.trrHorzSc - 0.5
        self.root.setPos(-offset, -offset, 0)

        self.terrain.generate()
        self.root.reparentTo(self.render)

        # load tree billboards
        self.txtTreeBillBoards = []
        for a in range(self.numTreeTexts):
            fstr = 'trees/tree' + '%03d' % (a + 991)
            self.txtTreeBillBoards.append( \
                self.loader.loadTexture(fstr + '-color.png', fstr + '-opacity.png'))
            self.txtTreeBillBoards[a].setMinfilter(
                Texture.FTLinearMipmapLinear)

        #self.placePlantOnTerrain('trees',300,0,20,20,self.trrHorzSc,self.trrVertSc, \
        #    self.numTreeTexts,self.txtTreeBillBoards,'scene-def/trees.txt')
        self.setup_house()
        self.setup_vehicle()

        self.taskMgr.add(self.skysphereTask, "SkySphere Task")

    def setup_house(self):
        # place farmhouse on terrain
        self.house = ModelNode('house1')
        self.loadModelOntoTerrain(self.render, self.terrain, self.house, 43.0,
                                  0.275, 0.0, 0.0, self.trrHorzSc,
                                  self.trrVertSc, 'models/FarmHouse',
                                  Vec3(0, 0, 0),
                                  Point3(-12.0567, -29.1724, 0.0837742),
                                  Point3(12.2229, 21.1915, 21.3668))

    def setup_vehicle(self):
        # place HMMWV on terrain
        self.hmmwv = ModelNode('hmmwv1')
        self.loadModelOntoTerrain(self.render, self.terrain, self.hmmwv, 33.0,
                                  1.0, 20.0, 24.0, self.trrHorzSc,
                                  self.trrVertSc, 'models/hmmwv',
                                  Vec3(0, -90, 0),
                                  Point3(-1.21273, -2.49153, -1.10753),
                                  Point3(1.21273, 2.49153, 1.10753))

    def setup_skybox(self,
                     terrctr=645.0,
                     boxsz=1000.0,
                     aspect=1.0,
                     uplift=0.0):
        vsz = boxsz / aspect
        self.bckgtx = []
        self.bckgtx.append(self.loader.loadTexture('sky/Back2.png'))
        self.bckgtx.append(self.loader.loadTexture('sky/Right2.png'))
        self.bckgtx.append(self.loader.loadTexture('sky/Front2.png'))
        self.bckgtx.append(self.loader.loadTexture('sky/Left2.png'))
        self.bckgtx.append(self.loader.loadTexture('sky/Up.png'))
        for a in range(4):
            self.bckg = CardMaker('bkcard')
            lr = Point3(0.5 * boxsz, 0.5 * boxsz, -0.5 * vsz)
            ur = Point3(0.5 * boxsz, 0.5 * boxsz, 0.5 * vsz)
            ul = Point3(-0.5 * boxsz, 0.5 * boxsz, 0.5 * vsz)
            ll = Point3(-0.5 * boxsz, 0.5 * boxsz, -0.5 * vsz)
            self.bckg.setFrame(ll, lr, ur, ul)
            self.bckg.setHasNormals(0)
            self.bckg.setHasUvs(1)
            #self.bckg.setUvRange(self.bckgtx[a])
            bkcrd = self.render.attachNewNode(self.bckg.generate())
            bkcrd.setTexture(self.bckgtx[a])
            self.bckgtx[a].setWrapU(Texture.WMClamp)
            self.bckgtx[a].setWrapV(Texture.WMClamp)
            bkcrd.setLightOff()
            bkcrd.setFogOff()
            bkcrd.setHpr(90.0 * a, 0, 0)
            cz = 0.5 * boxsz * uplift
            #print 'set card at:', terrctr,terrctr,cz, ' with points: ', lr,ur,ul,ll
            bkcrd.setPos(terrctr, terrctr, cz)
            self.top = CardMaker('bkcard')
            lr = Point3(0.5 * boxsz, -0.5 * boxsz, 0)
            ur = Point3(0.5 * boxsz, 0.5 * boxsz, 0)
            ul = Point3(-0.5 * boxsz, 0.5 * boxsz, 0)
            ll = Point3(-0.5 * boxsz, -0.5 * boxsz, 0)
            self.top.setFrame(ll, lr, ur, ul)
            self.top.setHasNormals(0)
            self.top.setHasUvs(1)
            #self.top.setUvRange(self.bckgtx[4])
            bkcrd = self.render.attachNewNode(self.bckg.generate())
            bkcrd.setTexture(self.bckgtx[4])
            self.bckgtx[4].setWrapU(Texture.WMClamp)
            self.bckgtx[4].setWrapV(Texture.WMClamp)
            bkcrd.setLightOff()
            bkcrd.setFogOff()
            bkcrd.setHpr(0, 90, 90)
            bkcrd.setPos(terrctr, terrctr, 0.5 * vsz + 0.5 * boxsz * uplift)

    def placePlantOnTerrain(self, itemStr, itemCnt, Mode, typItemWidth,
                            typItemHeight, trrHorzSc, trrVertSc, numTxtTypes,
                            txtList, planFileName):
        # Billboarding plants
        crd = CardMaker('mycard')
        crd.setColor(0.5, 0.5, 0.5, 1)
        ll = Point3(-0.5 * typItemWidth, 0, 0)
        lr = Point3(0.5 * typItemWidth, 0, 0)
        ur = Point3(0.5 * typItemWidth, 0, typItemHeight)
        ul = Point3(-0.5 * typItemWidth, 0, typItemHeight)
        crd.setFrame(ll, lr, ur, ul)
        crd.setHasNormals(False)
        crd.setHasUvs(True)
        # generate/save/load locations
        try:
            plan_data_fp = open(planFileName, 'r')
            item_list = []
            for line in plan_data_fp:
                toks = line.split(',')
                px = float(toks[0].strip(' '))
                py = float(toks[1].strip(' '))
                ang = float(toks[2].strip(' '))
                dht = float(toks[3].strip(' '))
                scl = float(toks[4].strip(' '))
                idx = int(toks[5].strip(' '))
                item_list.append((px, py, ang, dht, scl, idx))
            plan_data_fp.close()
            print 'loaded ', itemStr, ' data file of size:', len(item_list)
        except IOError:
            # generate list and try to save
            item_list = []
            for a in range(itemCnt):
                px = random.randrange(-self.trrHorzSc * 64,
                                      self.trrHorzSc * 64)
                py = random.randrange(-self.trrHorzSc * 64,
                                      self.trrHorzSc * 64)
                ang = 180 * random.random()
                dht = 0.0
                scl = 0.75 + 0.25 * (random.random() + random.random())
                idx = random.randrange(0, numTxtTypes)
                item_list.append([px, py, ang, dht, scl, idx])
            try:
                plan_data_fp = open(planFileName, 'w')
                for c in item_list:
                    print >> plan_data_fp, c[0], ',', c[1], ',', c[2], ',', c[
                        3], ',', c[4], ',', c[5]
                plan_data_fp.close()
                print 'saved ', itemStr, ' data of size: ', len(item_list)
            except IOError:
                print 'unable to store ', itemStr, ' data of size: ', len(
                    item_list)
        # define each plant
        for c in item_list:
            px = c[0]
            py = c[1]
            ang = c[2]
            dht = c[3]
            scl = c[4]
            idx = c[5]
            if idx >= numTxtTypes:
                idx = 0
            if Mode > 0:
                for b in range(Mode):
                    crdNP = self.render.attachNewNode(crd.generate())
                    crdNP.setTexture(txtList[idx])
                    crdNP.setScale(scl)
                    crdNP.setTwoSided(True)
                    ht = self.terrain.getElevation(px / trrHorzSc,
                                                   py / trrHorzSc)
                    crdNP.setPos(px, py, ht * trrVertSc + dht)
                    crdNP.setHpr(ang + (180 / Mode) * b, 0, 0)
                    crdNP.setTransparency(TransparencyAttrib.MAlpha)
                    crdNP.setLightOff()
            else:
                # set up item as defined
                crd.setUvRange(txtList[idx])
                crdNP = self.render.attachNewNode(crd.generate())
                crdNP.setBillboardAxis()
                crdNP.setTexture(txtList[idx])
                crdNP.setScale(scl)
                ht = self.terrain.getElevation(px / trrHorzSc, py / trrHorzSc)
                crdNP.setPos(px, py, ht * trrVertSc)
                crdNP.setTransparency(TransparencyAttrib.MAlpha)
                crdNP.setLightOff()

    def loadModelOntoTerrain(self, render_node, terr_obj, model_obj, hdg, scl,
                             xctr, yctr, terr_horz_sc, terr_vert_sc,
                             model_path, rotA, minP, maxP):
        # load model onto terrain
        hdg_rads = hdg * math.pi / 180.0
        model_obj = self.loader.loadModel(model_path)
        rotAll = rotA
        rotAll.setX(rotAll.getX() + hdg)
        model_obj.setHpr(rotA)
        model_obj.setLightOff()
        # if model changes, these will have to be recomputed
        # minP = Point3(0,0,0)
        # maxP = Point3(0,0,0)
        # model_obj.calcTightBounds(minP,maxP)
        print minP
        print maxP
        htl = []
        maxzofs = -1000.0
        for xi in [minP[0], maxP[0]]:
            for yi in [minP[1], maxP[1]]:
                tx = xctr + scl * xi * math.cos(hdg_rads)
                ty = yctr + scl * yi * math.sin(hdg_rads)
                tht = self.terrain.getElevation(tx / terr_horz_sc,
                                                ty / terr_horz_sc)
                print 'tx=', tx, ', ty=', ty, ', tht=', tht
                htl.append(tht * terr_vert_sc - minP.getZ())
        for hi in htl:
            if hi > maxzofs:
                maxzofs = hi
        print maxzofs
        model_obj.setPos(xctr, yctr, maxzofs)
        model_obj.setHpr(rotAll)
        model_obj.setScale(scl)
        model_obj.reparentTo(render_node)
        return maxzofs, minP, maxP

    def get_camera_image(self, requested_format=None):
        """
        Returns the camera's image, which is of type uint8 and has values
        between 0 and 255.
        The 'requested_format' argument should specify in which order the
        components of the image must be. For example, valid format strings are
        "RGBA" and "BGRA". By default, Panda's internal format "BGRA" is used,
        in which case no data is copied over.
        """
        tex = self.dr.getScreenshot()
        if requested_format is None:
            data = tex.getRamImage()
        else:
            data = tex.getRamImageAs(requested_format)
        image = np.frombuffer(
            data.get_data(),
            np.uint8)  # use data.get_data() instead of data in python 2
        image.shape = (tex.getYSize(), tex.getXSize(), tex.getNumComponents())
        image = np.flipud(image)
        return image

    def get_camera_depth_image(self):
        """
        Returns the camera's depth image, which is of type float32 and has
        values between 0.0 and 1.0.
        """
        data = self.depthTex.getRamImage()
        depth_image = np.frombuffer(data.get_data(), np.float32)
        depth_image.shape = (self.depthTex.getYSize(),
                             self.depthTex.getXSize(),
                             self.depthTex.getNumComponents())
        depth_image = np.flipud(depth_image)
        '''
        
        Surface position can be inferred by calculating backward from the
        depth buffer. Each pixel on the screen represents a ray from the
        camera into the scene, and the depth value in the pixel indicates a
        distance along the ray. Because of this, it is not actually necessary
        to store surface position explicitly - it is only necessary to store
        depth values. Of course, OpenGL does that for free.

        So the framebuffer now needs to store surface normal, diffuse color,
        and depth value (to infer surface position). In practice, most
        ordinary framebuffers can only store color and depth - they don't have
        any place to store a third value. So we need to use a special
        offscreen buffer with an "auxiliary" bitplane. The auxiliary bitplane
        stores the surface normal.

        So then, there's the final postprocessing pass. This involves
        combining the diffuse color texture, the surface normal texture, the
        depth texture, and the light parameters into a final rendered output.
        The light parameters are passed into the postprocessing shader as
        constants, not as textures.

        If there are a lot of lights, things get interesting. You use one
        postprocessing pass per light. Each pass only needs to scan those
        framebuffer pixels that are actually in range of the light in
        question. To traverse only the pixels that are affected by the light,
        just render the illuminated area's convex bounding volume.

        The shader to store the diffuse color and surface normal is trivial.
        But the final postprocessing shader is a little complicated. What
        makes it tricky is that it needs to regenerate the original surface
        position from the screen position and depth value. The math for that
        deserves some explanation.

        We need to take a clip-space coordinate and depth-buffer value
        (ClipX,ClipY,ClipZ,ClipW) and unproject it back to a view-space
        (ViewX,ViewY,ViewZ) coordinate. Lighting is then done in view-space.

        Okay, so here's the math. Panda uses the projection matrix to
        transform view-space into clip-space. But in practice, the projection
        matrix for a perspective camera always contains four nonzero
        constants, and they're always in the same place:
            
        -- here are the non-zero elements of the projection matrix --
        
        A	0	0	0
        0	0	B	1
        0	C	0	0
        0	0	D	0
        
        -- precompute these from above projection matrix --
        '''
        proj = self.cam.node().getLens().getProjectionMat()
        proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0)
        proj_y = 0.5 * proj.getCell(3, 2)
        proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1)
        proj_w = -0.5 - 0.5 * proj.getCell(1, 2)
        '''
        -- now for each pixel compute viewpoint coordinates --
        
        viewx = (screenx * projx) / (depth + projw)
        viewy = (1 * projy) / (depth + projw)
        viewz = (screeny * projz) / (depth + projw)
        '''
        grid = np.mgrid[0:depth_image.shape[0], 0:depth_image.shape[1]]
        ygrid = np.float32(np.squeeze(
            grid[0, :, :])) / float(depth_image.shape[0] - 1)
        ygrid -= 0.5
        xgrid = np.float32(np.squeeze(
            grid[1, :, :])) / float(depth_image.shape[1] - 1)
        xgrid -= 0.5
        xview = 2.0 * xgrid * proj_x
        zview = 2.0 * ygrid * proj_z
        denom = np.squeeze(depth_image) + proj_w
        xview = xview / denom
        yview = proj_y / denom
        zview = zview / denom
        sqrng = xview**2 + yview**2 + zview**2
        range_image = np.sqrt(sqrng)
        range_image_1 = np.expand_dims(range_image, axis=2)

        return depth_image, range_image_1

    def compute_sample_pattern(self, limg_shape, res_factor):
        # assume velocity is XYZ and we are looking +X up and towards -Z
        pattern = []
        lens = self.cam.node().getLens()
        sx = self.win.getXSize()
        sy = self.win.getYSize()
        ifov_vert = 2.0 * math.tan(
            0.5 * math.radians(lens.getVfov())) / float(sy - 1)
        ifov_horz = 2.0 * math.tan(
            0.5 * math.radians(lens.getHfov())) / float(sx - 1)
        #ifov_vert = lens.getVfov() / float(sy-1)
        #ifov_horz = lens.getHfov() / float(sy-1)
        for ldr_row in range(limg_shape[0]):
            theta = -10.0 - 41.33 * (
                float(ldr_row) / float(limg_shape[0] - 1) - 0.5)
            for ldr_col in range(limg_shape[1]):
                psi = 60.0 * (float(ldr_col) / float(limg_shape[1] - 1) - 0.5)
                cpsi = math.cos(math.radians(psi))
                vert_ang = theta / cpsi
                img_row_flt = (0.5 * float(sy - 1) -
                               (math.tan(math.radians(vert_ang)) / ifov_vert))
                #img_row_flt = 0.5*(sy-1) - (vert_ang / ifov_vert)
                if img_row_flt < 0:
                    print('img_row_flt=%f' % img_row_flt)
                    img_row_flt = 0.0
                if img_row_flt >= sy:
                    print('img_row_flt=%f' % img_row_flt)
                    img_row_flt = float(sy - 1)
                img_col_flt = (0.5 * float(sx - 1) +
                               (math.tan(math.radians(psi)) / ifov_horz))
                #img_col_flt = 0.5*(sx-1) + (psi / ifov_horz)
                if img_col_flt < 0:
                    print('img_col_flt=%f' % img_col_flt)
                    img_col_flt = 0.0
                if img_col_flt >= sx:
                    print('img_col_flt=%f' % img_col_flt)
                    img_col_flt = float(sx - 1)
                pattern.append((ldr_row, ldr_col, img_row_flt, img_col_flt))
        return pattern

    def find_sorted_ladar_returns(self, rangearr, intensarr, ks_m):
        my_range = rangearr.copy()
        my_inten = intensarr.copy()
        '''
        pixels data is organized by:
           [0] starting range of this return
           [1] ending range of this return
           [2] peak range of this return
           [3] total intensity of this return
        '''
        int_mult = len(my_inten)
        pixels = map(
            list,
            zip(my_range.tolist(), my_range.tolist(), my_range.tolist(),
                my_inten.tolist()))
        spix = sorted(pixels, key=lambda x: x[0])
        done = False
        while not done:
            mxpi = len(spix)
            if mxpi > 2:
                mindel = 1e20
                mnidx = None
                for pidx in range(mxpi - 1):
                    rdel = spix[pidx + 1][0] - spix[pidx][1]
                    # must be within ks_m meters in range to merge
                    if (rdel < ks_m) and (rdel < mindel):
                        mindel = rdel
                        mnidx = pidx
                # merge best two returns
                if mnidx is not None:
                    # new range span for testing against neighbors
                    spix[mnidx][1] = spix[mnidx + 1][1]
                    # new peak range is range of max contributor
                    if spix[mnidx + 1][3] > spix[mnidx][3]:
                        spix[mnidx][2] = spix[mnidx + 1][2]
                    # intensity of return is sum of contributors
                    spix[mnidx][3] += spix[mnidx + 1][3]
                    # remove one of the two merged
                    del spix[mnidx + 1]
                else:
                    done = True
            else:
                done = True
        # now eliminate all but max and last returns
        max_idx = None
        max_val = 0.0
        for ci, pix in enumerate(spix):
            if pix[3] > max_val:
                max_val = pix[3] / int_mult
                max_idx = ci
        # if they are the same, return only one
        if spix[-1][3] >= spix[max_idx][3]:
            return [spix[-1]]
        else:
            return [spix[max_idx], spix[-1]]

    def sample_range_image(self, rng_img, int_img, limg_shape, vel_cam, pps,
                           ldr_err, pattern):
        # depth image is set up as 512 x 512 and is 62.5 degrees vertical FOV
        # the center row is vertical, but we want to sample from the
        # region corresponding to HDL-32 FOV: from +10 to -30 degrees
        detailed_sensor_model = False
        fwd_vel = vel_cam[1]
        beam_div = 0.002
        lens = self.cam.node().getLens()
        #sx = self.win.getXSize()
        sy = self.win.getYSize()
        ifov_vert = 2.0 * math.tan(
            0.5 * math.radians(lens.getVfov())) / float(sy - 1)
        #ifov_horz = 2.0*math.tan(0.5*math.radians(lens.getHfov()))/float(sx-1)
        #ifov = math.radians(self.cam.node().getLens().getVfov() / self.win.getYSize())
        sigma = beam_div / ifov_vert
        hs = int(2.0 * sigma + 1.0)
        gprof = gauss_kern(sigma, hs, normalize=False)
        rimg = np.zeros(limg_shape, dtype=np.float32)
        iimg = np.zeros(limg_shape, dtype=np.float32)
        margin = 10.0

        for pidx, relation in enumerate(pattern):

            # get the usual scan pattern sample
            ldr_row, ldr_col, img_row_flt, img_col_flt = relation

            if ((img_row_flt > -margin) and (img_col_flt > -margin)
                    and (img_row_flt < rng_img.shape[0] + margin)
                    and (img_col_flt < rng_img.shape[1] + margin)):

                # within reasonable distance from image limits
                img_row = int(round(img_row_flt))
                img_col = int(round(img_col_flt))

                # motion compensation
                trng = np.float32(rng_img[img_row, img_col])
                if trng > 0.0:
                    # TODO: change this back to False
                    done = True
                    ic = 0
                    while not done:
                        old_trng = trng
                        del_row = pidx * fwd_vel / (ifov_vert * trng * pps)
                        if (abs(del_row) > 1e-1) and (ic < 10):
                            img_row_f = img_row_flt + del_row
                            img_row = int(round(img_row_f))
                            trng = np.float32(rng_img[img_row, img_col])
                            ic += 1
                            if abs(trng - old_trng) < 0.5:
                                done = True
                        else:
                            done = True

                    # simple sensor processing: just sample from large images
                    rimg[ldr_row, ldr_col] = np.float32(rng_img[img_row,
                                                                img_col])
                    iimg[ldr_row, ldr_col] = np.float32(int_img[img_row,
                                                                img_col])

                    if detailed_sensor_model:
                        # detailed model subsamples whole beam width
                        gpatch = copy_patch_centered((img_row, img_col), hs,
                                                     int_img, 0.0)
                        gpatch = np.float32(gpatch)
                        gpatch *= gprof
                        rpatch = copy_patch_centered((img_row, img_col), hs,
                                                     rng_img, 0.0)
                        rpatch = np.squeeze(rpatch)
                        valid = rpatch > 1e-3
                        if np.count_nonzero(valid) > 0:
                            rpatch_ts = rpatch[valid]
                            gpatch_ts = gpatch[valid]
                            returns = self.find_sorted_ladar_returns(
                                rpatch_ts, gpatch_ts, 2.5)
                            # for now we just take first return
                            rimg[ldr_row, ldr_col] = returns[0][2]
                            iimg[ldr_row, ldr_col] = returns[0][3]
                else:
                    rimg[ldr_row, ldr_col] = 0.0
                    iimg[ldr_row, ldr_col] = np.float32(int_img[img_row,
                                                                img_col])

        rimg += ldr_err * np.random.standard_normal(rimg.shape)
        return rimg, iimg

    def skysphereTask(self, task):
        if self.base is not None:
            self.skysphere.setPos(self.base.camera, 0, 0, 0)
            self.terrain.generate()
        return task.cont
Exemple #5
0
class TextureHeightmapBase(Heightmap):
    def __init__(self, name, width, height, height_scale, u_scale, v_scale,
                 median, interpolator):
        Heightmap.__init__(self, name, width, height, height_scale, u_scale,
                           v_scale, median, interpolator)
        self.texture = None
        self.texture_offset = LVector2()
        self.texture_scale = LVector2(1, 1)
        self.tex_id = str(width) + ':' + str(height)

    def reset(self):
        self.texture = None

    def get_texture_offset(self, patch):
        return self.texture_offset

    def get_texture_scale(self, patch):
        return self.texture_scale

        return self.heightmap_ready

    def set_height(self, x, y, height):
        pass

    def get_height(self, x, y):
        if self.texture_peeker is None:
            print("No peeker")
            traceback.print_stack()
            return 0.0
        new_x = x * self.texture_scale[0] + self.texture_offset[0] * self.width
        new_y = y * self.texture_scale[1] + self.texture_offset[1] * self.height
        new_x = min(new_x, self.width - 1)
        new_y = min(new_y, self.height - 1)
        height = self.interpolator.get_value(self.texture_peeker, new_x, new_y)
        return height * self.height_scale  # + self.offset

    def create_heightmap(self, shape, callback=None, cb_args=()):
        return self.load(shape, callback, cb_args)

    def load(self, shape, callback, cb_args=()):
        if self.texture is None:
            self.texture = Texture()
            self.texture.set_wrap_u(Texture.WMClamp)
            self.texture.set_wrap_v(Texture.WMClamp)
            self.interpolator.configure_texture(self.texture)
            self.do_load(shape, self.heightmap_ready_cb, (callback, cb_args))
        else:
            if callback is not None:
                callback(self, *cb_args)

    def heightmap_ready_cb(self, texture, callback, cb_args):
        #print("READY", self.patch.str_id())
        self.texture_peeker = self.texture.peek()
        #         if self.texture_peeker is None:
        #             print("NOT READY !!!")
        self.heightmap_ready = True
        data = self.texture.getRamImage()
        if sys.version_info[0] < 3:
            buf = data.getData()
            np_buffer = numpy.fromstring(buf, dtype=numpy.float32)
        else:
            np_buffer = numpy.frombuffer(data, numpy.float32)
        np_buffer.shape = (self.texture.getYSize(), self.texture.getXSize(),
                           self.texture.getNumComponents())
        self.min_height = np_buffer.min()
        self.max_height = np_buffer.max()
        self.mean_height = np_buffer.mean()
        if callback is not None:
            callback(self, *cb_args)

    def do_load(self, shape, callback, cb_args):
        pass
Exemple #6
0
class HeightmapPatch:
    cachable = True

    def __init__(self,
                 parent,
                 x0,
                 y0,
                 x1,
                 y1,
                 width,
                 height,
                 scale=1.0,
                 coord=TexCoord.Cylindrical,
                 face=-1,
                 border=1):
        self.parent = parent
        self.x0 = x0
        self.y0 = y0
        self.x1 = x1
        self.y1 = y1
        self.scale = scale
        self.width = width
        self.height = height
        self.coord = coord
        self.face = face
        self.border = border
        self.r_width = self.width + self.border * 2
        self.r_height = self.height + self.border * 2
        self.r_x0 = self.x0 - float(
            self.border) / self.width * (self.x1 - self.x0)
        self.r_x1 = self.x1 + float(
            self.border) / self.width * (self.x1 - self.x0)
        self.r_y0 = self.y0 - float(
            self.border) / self.height * (self.y1 - self.y0)
        self.r_y1 = self.y1 + float(
            self.border) / self.height * (self.y1 - self.y0)
        self.dx = self.r_x1 - self.r_x0
        self.dy = self.r_y1 - self.r_y0
        self.lod = None
        self.patch = None
        self.heightmap_ready = False
        self.texture = None
        self.texture_peeker = None
        self.callback = None
        self.cloned = False
        self.texture_offset = LVector2()
        self.texture_scale = LVector2(1, 1)
        self.min_height = None
        self.max_height = None
        self.mean_height = None

    @classmethod
    def create_from_patch(cls,
                          noise,
                          parent,
                          x,
                          y,
                          scale,
                          lod,
                          density,
                          coord=TexCoord.Cylindrical,
                          face=-1):
        #TODO: Should be move to Patch/Tile
        if coord == TexCoord.Cylindrical:
            r_div = 1 << lod
            s_div = 2 << lod
            x0 = float(x) / s_div
            y0 = float(y) / r_div
            x1 = float(x + 1) / s_div
            y1 = float(y + 1) / r_div
        elif coord == TexCoord.NormalizedCube or coord == TexCoord.SqrtCube:
            div = 1 << lod
            r_div = div
            s_div = div
            x0 = float(x) / div
            y0 = float(y) / div
            x1 = float(x + 1) / div
            y1 = float(y + 1) / div
        else:
            div = 1 << lod
            r_div = div
            s_div = div
            size = 1.0 / (1 << lod)
            x0 = (x) * scale
            y0 = (y) * scale
            x1 = (x + size) * scale
            y1 = (y + size) * scale
        patch = cls(noise,
                    parent,
                    x0,
                    y0,
                    x1,
                    y1,
                    width=density,
                    height=density,
                    scale=scale,
                    coord=coord,
                    face=face,
                    border=1)
        patch.patch_lod = lod
        patch.lod = lod
        patch.lod_scale_x = scale / s_div
        patch.lod_scale_y = scale / r_div
        patch.density = density
        patch.x = x
        patch.y = y
        return patch

    def copy_from(self, heightmap_patch):
        self.cloned = True
        self.lod = heightmap_patch.lod
        self.texture = heightmap_patch.texture
        self.texture_peeker = heightmap_patch.texture_peeker
        self.heightmap_ready = heightmap_patch.heightmap_ready
        self.min_height = heightmap_patch.min_height
        self.max_height = heightmap_patch.max_height
        self.mean_height = heightmap_patch.mean_height

    def calc_sub_patch(self):
        self.copy_from(self.parent_heightmap)
        delta = self.patch.lod - self.lod
        scale = 1 << delta
        if self.patch.coord != TexCoord.Flat:
            x_tex = int(self.x / scale) * scale
            y_tex = int(self.y / scale) * scale
            x_delta = float(self.x - x_tex) / scale
            y_delta = float(self.y - y_tex) / scale
        else:
            x_tex = int(self.x * scale) / scale
            y_tex = int(self.y * scale) / scale
            x_delta = float(self.x - x_tex)
            y_delta = float(self.y - y_tex)
        self.texture_offset = LVector2(x_delta, y_delta)
        self.texture_scale = LVector2(1.0 / scale, 1.0 / scale)

    def is_ready(self):
        return self.heightmap_ready

    def set_height(self, x, y, height):
        pass

    def get_height(self, x, y):
        if self.texture_peeker is None:
            print("No peeker", self.patch.str_id(), self.patch.instance_ready)
            traceback.print_stack()
            return 0.0
        new_x = x * self.texture_scale[0] + self.texture_offset[0] * self.width
        new_y = y * self.texture_scale[1] + self.texture_offset[1] * self.height
        new_x = min(new_x, self.width - 1)
        new_y = min(new_y, self.height - 1)
        height = self.parent.interpolator.get_value(self.texture_peeker, new_x,
                                                    new_y)
        #TODO: This should be done in PatchedHeightmap.get_height()
        return height * self.parent.height_scale  # + self.parent.offset

    def get_height_uv(self, u, v):
        return self.get_height(u * self.width, v * self.height)

    def load(self, patch, callback, cb_args=()):
        if self.texture is None:
            self.texture = Texture()
            self.texture.set_wrap_u(Texture.WMClamp)
            self.texture.set_wrap_v(Texture.WMClamp)
            self.parent.interpolator.configure_texture(self.texture)
            self.do_load(patch, callback, cb_args)
        else:
            if callback is not None:
                callback(self, *cb_args)

    def heightmap_ready_cb(self, texture, callback, cb_args):
        if texture is not None:
            self.texture = texture
            #print("READY", self.patch.str_id(), texture, self.texture)
            self.texture_peeker = texture.peek()
            #           if self.texture_peeker is None:
            #               print("NOT READY !!!")
            self.heightmap_ready = True
            data = self.texture.getRamImage()
            #TODO: should be completed and refactored
            signed = False
            component_type = texture.getComponentType()
            if component_type == Texture.T_float:
                buffer_type = numpy.float32
                scale = 1.0
            elif component_type == Texture.T_unsigned_byte:
                if signed:
                    buffer_type = numpy.int8
                    scale = 128.0
                else:
                    buffer_type = numpy.uint8
                    scale = 255.0
            elif component_type == Texture.T_unsigned_short:
                if signed:
                    buffer_type = numpy.int16
                    scale = 32768.0
                else:
                    buffer_type = numpy.uint16
                    scale = 65535.0
            if sys.version_info[0] < 3:
                buf = data.getData()
                np_buffer = numpy.fromstring(buf, dtype=buffer_type)
            else:
                np_buffer = numpy.frombuffer(data, buffer_type)
            np_buffer.shape = (self.texture.getYSize(),
                               self.texture.getXSize(),
                               self.texture.getNumComponents())
            self.min_height = np_buffer.min() / scale
            self.max_height = np_buffer.max() / scale
            self.mean_height = np_buffer.mean() / scale
        else:
            if self.parent_heightmap is not None:
                self.calc_sub_patch()
            else:
                print("Make default texture for heightmap")
                texture = Texture()
                texture.setup_2d_texture(1, 1, Texture.T_float, Texture.F_r32)
                texture.set_clear_color(LColor(0, 0, 0, 0))
                texture.make_ram_image()
                self.heightmap_ready_cb(texture, None, None)
        if callback is not None:
            callback(self, *cb_args)

    def do_load(self, patch, callback, cb_args):
        pass
class ShaderHeightmapPatch(HeightmapPatch):
    tex_generators = {}
    cachable = False

    def __init__(self,
                 noise,
                 parent,
                 x0,
                 y0,
                 x1,
                 y1,
                 width,
                 height,
                 scale=1.0,
                 coord=TexCoord.Cylindrical,
                 face=0,
                 border=1):
        HeightmapPatch.__init__(self, parent, x0, y0, x1, y1, width, height,
                                scale, coord, face, border)
        self.shader = None
        self.texture = None
        self.texture_peeker = None
        self.noise = noise
        self.tex_generator = None
        self.callback = None
        self.cloned = False
        self.texture_offset = LVector2()
        self.texture_scale = LVector2(1, 1)
        self.min_height = None
        self.max_height = None
        self.mean_height = None

    def copy_from(self, heightmap_patch):
        self.cloned = True
        self.lod = heightmap_patch.lod
        self.texture = heightmap_patch.texture
        self.texture_peeker = heightmap_patch.texture_peeker
        self.heightmap_ready = heightmap_patch.heightmap_ready
        self.min_height = heightmap_patch.min_height
        self.max_height = heightmap_patch.max_height
        self.mean_height = heightmap_patch.mean_height

    def get_height(self, x, y):
        if self.texture_peeker is None:
            print("No peeker", self.patch.str_id(), self.patch.instance_ready)
            traceback.print_stack()
            return 0.0
        new_x = x * self.texture_scale[0] + self.texture_offset[0] * self.width
        new_y = (
            (self.height - 1) -
            y) * self.texture_scale[1] + self.texture_offset[1] * self.height
        new_x = min(new_x, self.width - 1)
        new_y = min(new_y, self.height - 1)
        height = self.parent.interpolator.get_value(self.texture_peeker, new_x,
                                                    new_y)
        #TODO: This should be done in PatchedHeightmap.get_height()
        return height * self.parent.height_scale  # + self.parent.offset

    def generate(self, callback, cb_args=()):
        if self.texture is None:
            self.texture = Texture()
            self.texture.set_wrap_u(Texture.WMClamp)
            self.texture.set_wrap_v(Texture.WMClamp)
            self.parent.interpolator.configure_texture(self.texture)
            self._make_heightmap(callback, cb_args)
        else:
            if callback is not None:
                callback(self, *cb_args)

    def heightmap_ready_cb(self, texture, callback, cb_args):
        #print("READY", self.patch.str_id())
        self.texture_peeker = self.texture.peek()
        #         if self.texture_peeker is None:
        #             print("NOT READY !!!")
        self.heightmap_ready = True
        data = self.texture.getRamImage()
        if sys.version_info[0] < 3:
            buf = data.getData()
            np_buffer = numpy.fromstring(buf, dtype=numpy.float32)
        else:
            np_buffer = numpy.frombuffer(data, numpy.float32)
        np_buffer.shape = (self.texture.getYSize(), self.texture.getXSize(),
                           self.texture.getNumComponents())
        self.min_height = np_buffer.min()
        self.max_height = np_buffer.max()
        self.mean_height = np_buffer.mean()
        if callback is not None:
            callback(self, *cb_args)

    def _make_heightmap(self, callback, cb_args):
        if not self.width in ShaderHeightmapPatch.tex_generators:
            ShaderHeightmapPatch.tex_generators[self.width] = GeneratorPool(
                settings.patch_pool_size)
            if settings.encode_float:
                texture_format = Texture.F_rgba
            else:
                texture_format = Texture.F_r32
            ShaderHeightmapPatch.tex_generators[self.width].make_buffer(
                self.width, self.height, texture_format)
        tex_generator = ShaderHeightmapPatch.tex_generators[self.width]
        if self.shader is None:
            self.shader = NoiseShader(coord=self.coord,
                                      noise_source=self.noise,
                                      noise_target=FloatTarget(),
                                      offset=(self.x0, self.y0, 0.0),
                                      scale=(self.lod_scale_x,
                                             self.lod_scale_y, 1.0))
            self.shader.global_frequency = self.parent.global_frequency
            self.shader.global_scale = self.parent.global_scale
            self.shader.create_and_register_shader(None, None)
        tex_generator.generate(self.shader, self.face, self.texture,
                               self.heightmap_ready_cb, (callback, cb_args))