Ejemplo n.º 1
0
    def __init__(self):
        ShowBase.__init__(self)
        base.setFrameRateMeter(True)
        base.disableMouse() # Disable panda's default mouse control
        # Hide the cursor
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)

        # Disable sound for now
        base.disableAllAudio() # Doesn't work oO

        # Enable anti aliasing
        render.setAntialias(AntialiasAttrib.MAuto)

        # Set up our physics world
        self.world = BulletWorld()
        self.world.setGravity(Vec3(0, 0, -9.81))

        # We need some gui
        self.lblGearState = DirectLabel(text = "X", scale = .1, pos = Point3(1.2, 0, -.97))
        self.lblGear = DirectLabel(text = "X", scale = .1, pos = Point3(1., 0, -.97))
        self.lblRpmSlider = DirectSlider(scale = .5, pos = Point3(1, 0, -.8), range=(0,3000), value=0, pageSize=0)

        # speedometer
        self.lblSpeedo = TextNode("speedometer")
        self.lblSpeedo.setText("000")
        self.lblSpeedo.setFont(loader.loadFont(self.datadir + "gui/lcd-normal.ttf"))
        self.lblSpeedo.setSlant(-.1)
        self.lblSpeedo.setAlign(TextNode.ARight)
        self.lblSpeedo.setTextColor(0, 0, 0, 1)
        self.lblSpeedoNp = aspect2d.attachNewNode(self.lblSpeedo)
        self.lblSpeedoNp.setScale(0.08)
        self.lblSpeedoNp.setPos(Point3(1.25, 0, -.9))

        self.accept("f9", self.toggleDebug)
        self.accept("f10", base.toggleWireframe)
        self.accept("f11", base.toggleTexture)
        self.accept("f12", base.screenshot, ["40tons"])

        # render in wireframe by default for now
        #base.toggleWireframe()

        # Enable shad(er|ow) generation
        render.setShaderAuto()

        # Let there be light!
        base.setBackgroundColor(.67, .67, 1., 1.)

        sunlight = DirectionalLight('sun')
        sunlight.setPoint(Point3(0, 0, 100))
        sunlightNp = render.attachNewNode(sunlight)
        sunlightNp.setPos(0, 0, 100)
        sunlightNp.setP(-90)
        render.setLight(sunlightNp)

        amblight = AmbientLight('amblight')
        amblight.setColor(VBase4(0.4, 0.4, 0.4, 1))
        amblightNp = render.attachNewNode(amblight)
        render.setLight(amblightNp)

        #print "[msg] Shader lighting commented out"
        self.setupShaderLighting()

        # TODO Load the skybox
        # Proper sky_box_ with face normals inside is missing
        #tex = loader.loadCubeMap(self.datadir + "tex/skyrender#.png")
        #skyboxNp = self.camera.attachNewNode(loader.loadModel("box").node())
        #skyboxNp.setBin("background", 0);
        #skyboxNp.setDepthWrite(False);
        #skyboxNp.setCompass()
        #skyboxNp.setTexture(tex)
        #skyboxNp.setLightOff()
        #skyboxNp.setShaderOff()

        # skydome (from http://www.mygamefast.com/volume1/issue3/)
        skydome = loader.loadModel(self.datadir + "sky.egg")
        skydome.setEffect(CompassEffect.make(self.render))
        skydome.setBin("background", 0);
        skydome.setDepthWrite(False);
        skydome.setLightOff()
        skydome.setScale(200)
        skydome.setZ(-65) # sink it
        skydome.reparentTo(self.camera) # NOT render - we want it to always stay "far away"

        height = 10.0
        terrainFile = self.datadir + "tex/inclined.png"

        self.terBodyNp = render.attachNewNode(BulletRigidBodyNode("terrainBody"))
        img = PNMImage(Filename(terrainFile))
        offset = img.getXSize() / 2.0 - 0.5 # Used for the GeoMipTerrain
        self.terBodyNp.node().addShape(BulletHeightfieldShape(img, height, ZUp))
        self.terBodyNp.node().setDebugEnabled(False)
        self.terBodyNp.setPos(0,0, height / 2.0)
        self.world.attachRigidBody(self.terBodyNp.node())

        # Set up the GeoMipTerrain
        self.terrain = GeoMipTerrain("terrainNode")
        self.terrain.setHeightfield(terrainFile)
        self.terrain.setBlockSize(32)
        self.terrain.setNear(50)
        self.terrain.setFar(300)
        self.terrain.setFocalPoint(base.camera)
        #self.terrain.setBruteforce(True)
        self.terrain.getRoot().reparentTo(self.terBodyNp)

        self.terrainNp = self.terrain.getRoot()
        self.terrainNp.setSz(height)
        self.terrainNp.setPos(-offset, -offset, -height / 2.0)
        #self.terrainNp.setDepthOffset(1)
        # Generate it.
        self.terrain.generate()

        # Paint it
        terTex = loader.loadTexture(self.datadir + "tex/vegetati.png")
        terTex.setAnisotropicDegree(2)
        self.terrainNp.setTexture(terTex)
        self.terrainNp.setTexScale(TextureStage.getDefault(), 16., 16.)

        # Build our bridge
        p0 = Point3(-11, -6.5, 3.5 - height/2.)
        p2 = Point3(-8.5,  -6.5, 3.5 - height/2.)
        p1 = Point3(-10.5,7.5, 3.5 - height/2.)
        p3 = Point3(-7,   7.5, 3.5 - height/2.)
        mesh = BulletTriangleMesh()
        mesh.addTriangle(p0, p1, p2)
        mesh.addTriangle(p1, p2, p3)
        self.bridgeNp = self.terBodyNp.attachNewNode(BulletRigidBodyNode("bridgeBody"))
        self.bridgeNp.node().addShape(BulletTriangleMeshShape(mesh, dynamic=False))
        self.world.attachRigidBody(self.bridgeNp.node())

        # Configure the debug node
        self.debug = render.attachNewNode(BulletDebugNode('debug'))
        self.debug.node().showWireframe(True)
        self.debug.node().showConstraints(True)
        self.debug.node().showBoundingBoxes(False)
        self.debug.node().showNormals(False)
        self.world.setDebugNode(self.debug.node())
        #self.debug.show()

        self.vehicles.append(XMLTruck("vehicles/atego/vehicle.xml", self.datadir, Vec3(0,0,0), self.world))
        #self.vehicles.append(XMLTruck("vehicles/atego/vehicle.xml", self.datadir, Vec3(4,5,0), self.world))
        self.vehicles.append(XMLTrailer("vehicles/dumper trailer/vehicle.xml", self.datadir, Vec3(0,-6,0), self.world))

        # Register truck functions
        # Truck should do this by itself! (or advertise keys it wants to use? better approach...)
        self.keyconf = KeyConfig(self)
        self.keyconf.loadConfig("qwertz.conf")
        self.keyconf.setHook("gas", self.vehicles[0].setGas, [1.], [0.])
        self.keyconf.setHook("brake", self.vehicles[0].setBrake, [1.], [0.])
        self.keyconf.setHook("steerLeft", self.vehicles[0].steer, [1], [0])
        self.keyconf.setHook("steerRight", self.vehicles[0].steer, [-1], [0])
        self.keyconf.setHook("reset", self.vehicles[0].reset)
        self.keyconf.setHook("shiftPark", self.vehicles[0].shiftPark)
        self.keyconf.setHook("shiftReverse", self.vehicles[0].shiftReverse)
        self.keyconf.setHook("shiftNeutral", self.vehicles[0].shiftNeutral)
        self.keyconf.setHook("shiftDrive", self.vehicles[0].shiftDrive)
        self.keyconf.setHook("couple", self.vehicles[0].couple, [self.vehicles])
        self.keyconf.setHook("control0Up", self.vehicles[0].control, [0, 1.], [0, 0.])
        self.keyconf.setHook("control0Down", self.vehicles[0].control, [0, -1.], [0, 0.])
        self.keyconf.setHook("control1Up", self.vehicles[0].control, [1, 1.], [1, 0.])
        self.keyconf.setHook("control1Down", self.vehicles[0].control, [1, -1.], [1, 0.])
        self.keyconf.setHook("control2Up", self.vehicles[0].control, [2, 1.], [2, 0.])
        self.keyconf.setHook("control2Down", self.vehicles[0].control, [2, -1.], [2, 0.])
        self.keyconf.setHook("control3Up", self.vehicles[0].control, [3, 1.], [3, 0.])
        self.keyconf.setHook("control3Down", self.vehicles[0].control, [3, -1.], [3, 0.])
        self.keyconf.setHook("control4Up", self.vehicles[0].control, [4, 1.], [4, 0.])
        self.keyconf.setHook("control4Down", self.vehicles[0].control, [4, -1.], [4, 0.])

        self.camcon = FollowerCameraController(self.world, self.camera, self.vehicles[0].getChassis().getBodyNp())
        taskMgr.add(self.camcon.update, 'CameraController', priority=10)
        self.keyconf.setHook("switchCamera", self.switchCamera)
        self.accept("wheel_up", self.camcon.mwheelup)
        self.accept("wheel_down", self.camcon.mwheeldown)

        self.accept('escape', sys.exit)
        self.accept("i", base.bufferViewer.toggleEnable)

        # register the physics update task
        self.taskMgr.doMethodLater(1./60., self.physicsTask, "physicsTask", priority=5)
        # register the render task
        self.taskMgr.doMethodLater(1./60., self.renderTask, "renderTask", priority=9)
Ejemplo n.º 2
0
class Main(ShowBase):
    vehicles = []
    datadir = "../../data/"
    accel, brake, left, right = False, False, False, False

    def __init__(self):
        ShowBase.__init__(self)
        base.setFrameRateMeter(True)
        base.disableMouse() # Disable panda's default mouse control
        # Hide the cursor
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)

        # Disable sound for now
        base.disableAllAudio() # Doesn't work oO

        # Enable anti aliasing
        render.setAntialias(AntialiasAttrib.MAuto)

        # Set up our physics world
        self.world = BulletWorld()
        self.world.setGravity(Vec3(0, 0, -9.81))

        # We need some gui
        self.lblGearState = DirectLabel(text = "X", scale = .1, pos = Point3(1.2, 0, -.97))
        self.lblGear = DirectLabel(text = "X", scale = .1, pos = Point3(1., 0, -.97))
        self.lblRpmSlider = DirectSlider(scale = .5, pos = Point3(1, 0, -.8), range=(0,3000), value=0, pageSize=0)

        # speedometer
        self.lblSpeedo = TextNode("speedometer")
        self.lblSpeedo.setText("000")
        self.lblSpeedo.setFont(loader.loadFont(self.datadir + "gui/lcd-normal.ttf"))
        self.lblSpeedo.setSlant(-.1)
        self.lblSpeedo.setAlign(TextNode.ARight)
        self.lblSpeedo.setTextColor(0, 0, 0, 1)
        self.lblSpeedoNp = aspect2d.attachNewNode(self.lblSpeedo)
        self.lblSpeedoNp.setScale(0.08)
        self.lblSpeedoNp.setPos(Point3(1.25, 0, -.9))

        self.accept("f9", self.toggleDebug)
        self.accept("f10", base.toggleWireframe)
        self.accept("f11", base.toggleTexture)
        self.accept("f12", base.screenshot, ["40tons"])

        # render in wireframe by default for now
        #base.toggleWireframe()

        # Enable shad(er|ow) generation
        render.setShaderAuto()

        # Let there be light!
        base.setBackgroundColor(.67, .67, 1., 1.)

        sunlight = DirectionalLight('sun')
        sunlight.setPoint(Point3(0, 0, 100))
        sunlightNp = render.attachNewNode(sunlight)
        sunlightNp.setPos(0, 0, 100)
        sunlightNp.setP(-90)
        render.setLight(sunlightNp)

        amblight = AmbientLight('amblight')
        amblight.setColor(VBase4(0.4, 0.4, 0.4, 1))
        amblightNp = render.attachNewNode(amblight)
        render.setLight(amblightNp)

        #print "[msg] Shader lighting commented out"
        self.setupShaderLighting()

        # TODO Load the skybox
        # Proper sky_box_ with face normals inside is missing
        #tex = loader.loadCubeMap(self.datadir + "tex/skyrender#.png")
        #skyboxNp = self.camera.attachNewNode(loader.loadModel("box").node())
        #skyboxNp.setBin("background", 0);
        #skyboxNp.setDepthWrite(False);
        #skyboxNp.setCompass()
        #skyboxNp.setTexture(tex)
        #skyboxNp.setLightOff()
        #skyboxNp.setShaderOff()

        # skydome (from http://www.mygamefast.com/volume1/issue3/)
        skydome = loader.loadModel(self.datadir + "sky.egg")
        skydome.setEffect(CompassEffect.make(self.render))
        skydome.setBin("background", 0);
        skydome.setDepthWrite(False);
        skydome.setLightOff()
        skydome.setScale(200)
        skydome.setZ(-65) # sink it
        skydome.reparentTo(self.camera) # NOT render - we want it to always stay "far away"

        height = 10.0
        terrainFile = self.datadir + "tex/inclined.png"

        self.terBodyNp = render.attachNewNode(BulletRigidBodyNode("terrainBody"))
        img = PNMImage(Filename(terrainFile))
        offset = img.getXSize() / 2.0 - 0.5 # Used for the GeoMipTerrain
        self.terBodyNp.node().addShape(BulletHeightfieldShape(img, height, ZUp))
        self.terBodyNp.node().setDebugEnabled(False)
        self.terBodyNp.setPos(0,0, height / 2.0)
        self.world.attachRigidBody(self.terBodyNp.node())

        # Set up the GeoMipTerrain
        self.terrain = GeoMipTerrain("terrainNode")
        self.terrain.setHeightfield(terrainFile)
        self.terrain.setBlockSize(32)
        self.terrain.setNear(50)
        self.terrain.setFar(300)
        self.terrain.setFocalPoint(base.camera)
        #self.terrain.setBruteforce(True)
        self.terrain.getRoot().reparentTo(self.terBodyNp)

        self.terrainNp = self.terrain.getRoot()
        self.terrainNp.setSz(height)
        self.terrainNp.setPos(-offset, -offset, -height / 2.0)
        #self.terrainNp.setDepthOffset(1)
        # Generate it.
        self.terrain.generate()

        # Paint it
        terTex = loader.loadTexture(self.datadir + "tex/vegetati.png")
        terTex.setAnisotropicDegree(2)
        self.terrainNp.setTexture(terTex)
        self.terrainNp.setTexScale(TextureStage.getDefault(), 16., 16.)

        # Build our bridge
        p0 = Point3(-11, -6.5, 3.5 - height/2.)
        p2 = Point3(-8.5,  -6.5, 3.5 - height/2.)
        p1 = Point3(-10.5,7.5, 3.5 - height/2.)
        p3 = Point3(-7,   7.5, 3.5 - height/2.)
        mesh = BulletTriangleMesh()
        mesh.addTriangle(p0, p1, p2)
        mesh.addTriangle(p1, p2, p3)
        self.bridgeNp = self.terBodyNp.attachNewNode(BulletRigidBodyNode("bridgeBody"))
        self.bridgeNp.node().addShape(BulletTriangleMeshShape(mesh, dynamic=False))
        self.world.attachRigidBody(self.bridgeNp.node())

        # Configure the debug node
        self.debug = render.attachNewNode(BulletDebugNode('debug'))
        self.debug.node().showWireframe(True)
        self.debug.node().showConstraints(True)
        self.debug.node().showBoundingBoxes(False)
        self.debug.node().showNormals(False)
        self.world.setDebugNode(self.debug.node())
        #self.debug.show()

        self.vehicles.append(XMLTruck("vehicles/atego/vehicle.xml", self.datadir, Vec3(0,0,0), self.world))
        #self.vehicles.append(XMLTruck("vehicles/atego/vehicle.xml", self.datadir, Vec3(4,5,0), self.world))
        self.vehicles.append(XMLTrailer("vehicles/dumper trailer/vehicle.xml", self.datadir, Vec3(0,-6,0), self.world))

        # Register truck functions
        # Truck should do this by itself! (or advertise keys it wants to use? better approach...)
        self.keyconf = KeyConfig(self)
        self.keyconf.loadConfig("qwertz.conf")
        self.keyconf.setHook("gas", self.vehicles[0].setGas, [1.], [0.])
        self.keyconf.setHook("brake", self.vehicles[0].setBrake, [1.], [0.])
        self.keyconf.setHook("steerLeft", self.vehicles[0].steer, [1], [0])
        self.keyconf.setHook("steerRight", self.vehicles[0].steer, [-1], [0])
        self.keyconf.setHook("reset", self.vehicles[0].reset)
        self.keyconf.setHook("shiftPark", self.vehicles[0].shiftPark)
        self.keyconf.setHook("shiftReverse", self.vehicles[0].shiftReverse)
        self.keyconf.setHook("shiftNeutral", self.vehicles[0].shiftNeutral)
        self.keyconf.setHook("shiftDrive", self.vehicles[0].shiftDrive)
        self.keyconf.setHook("couple", self.vehicles[0].couple, [self.vehicles])
        self.keyconf.setHook("control0Up", self.vehicles[0].control, [0, 1.], [0, 0.])
        self.keyconf.setHook("control0Down", self.vehicles[0].control, [0, -1.], [0, 0.])
        self.keyconf.setHook("control1Up", self.vehicles[0].control, [1, 1.], [1, 0.])
        self.keyconf.setHook("control1Down", self.vehicles[0].control, [1, -1.], [1, 0.])
        self.keyconf.setHook("control2Up", self.vehicles[0].control, [2, 1.], [2, 0.])
        self.keyconf.setHook("control2Down", self.vehicles[0].control, [2, -1.], [2, 0.])
        self.keyconf.setHook("control3Up", self.vehicles[0].control, [3, 1.], [3, 0.])
        self.keyconf.setHook("control3Down", self.vehicles[0].control, [3, -1.], [3, 0.])
        self.keyconf.setHook("control4Up", self.vehicles[0].control, [4, 1.], [4, 0.])
        self.keyconf.setHook("control4Down", self.vehicles[0].control, [4, -1.], [4, 0.])

        self.camcon = FollowerCameraController(self.world, self.camera, self.vehicles[0].getChassis().getBodyNp())
        taskMgr.add(self.camcon.update, 'CameraController', priority=10)
        self.keyconf.setHook("switchCamera", self.switchCamera)
        self.accept("wheel_up", self.camcon.mwheelup)
        self.accept("wheel_down", self.camcon.mwheeldown)

        self.accept('escape', sys.exit)
        self.accept("i", base.bufferViewer.toggleEnable)

        # register the physics update task
        self.taskMgr.doMethodLater(1./60., self.physicsTask, "physicsTask", priority=5)
        # register the render task
        self.taskMgr.doMethodLater(1./60., self.renderTask, "renderTask", priority=9)

    def physicsTask(self, task):
        self.world.doPhysics(task.delayTime, 10, task.delayTime/10.)

        if len(self.vehicles) > 0:
            for truck in self.vehicles:
                if truck.getType() == "truck":
                    truck.update(task.delayTime)
        return task.again

    def renderTask(self, task):
        """ Do stuff. """
        self.terrain.update()

        # Update the truck's speedometer
        self.lblSpeedoNp.node().setText("%i" % abs(self.vehicles[0].getSpeed()))
        self.lblGearState["text"] = self.vehicles[0].getGbState()
        self.lblRpmSlider["value"] = self.vehicles[0].getRpm()

        if self.vehicles[0].getGear() == 0:
            self.lblGear["text"] = 'r'
        elif self.vehicles[0].getGear() == 1:
            self.lblGear["text"] = 'n'
        else:
            self.lblGear["text"] = "%i" % (self.vehicles[0].getGear() - 1)

        return Task.cont

    def toggleDebug(self):
        if self.debug.isHidden():
            self.debug.show()
        else:
            self.debug.hide()

    def switchCamera(self):
        if isinstance(self.camcon, CockpitCameraController):
            self.camcon = ManualCameraController(self.world, self.camera, self.vehicles[0].getChassis().getBodyNp())
            taskMgr.remove("CameraController")
            taskMgr.add(self.camcon.update, 'CameraController', priority=10)
            self.accept("wheel_up", self.camcon.mwheelup)
            self.accept("wheel_down", self.camcon.mwheeldown)
        elif isinstance(self.camcon, ManualCameraController):
            self.camcon = FollowerCameraController(self.world, self.camera, self.vehicles[0].getChassis().getBodyNp())
            taskMgr.remove("CameraController")
            taskMgr.add(self.camcon.update, 'CameraController', priority=10)
            self.accept("wheel_up", self.camcon.mwheelup)
            self.accept("wheel_down", self.camcon.mwheeldown)
        else:
            self.camcon = CockpitCameraController(self.world, self.camera, self.vehicles[0].getChassis().getBodyNp())
            taskMgr.remove("CameraController")
            taskMgr.add(self.camcon.update, 'CameraController', priority=10)
            self.accept("wheel_up", self.camcon.mwheelup)
            self.accept("wheel_down", self.camcon.mwheeldown)

    def createFrustumNode(self, lightNodeNp):
        gn = GeomNode("frustum")
        gn.addGeom(lightNodeNp.node().getLens().makeGeometry())
        gnnp = lightNodeNp.attachNewNode(gn)
        gnnp.setPos(lightNodeNp.getPos())
        gnnp.setHpr(lightNodeNp.getHpr())

    def adjustPushBias(self,inc):
        self.pushBias *= inc
        render.setShaderInput('push',self.pushBias,self.pushBias,self.pushBias,0)

    def setupShaderLighting(self):
        loadPrcFileData("", "prefer-parasite-buffer #f")

        # Preliminary capabilities check.
        if (base.win.getGsg().getSupportsBasicShaders()==0):
            print "[err] Video driver reports that shaders are not supported."
            return
        if (base.win.getGsg().getSupportsDepthTexture()==0):
            print "[err] Video driver reports that depth textures are not supported."
            return

        # creating the offscreen buffer.
        winprops = WindowProperties.size(2048,2048)
        props = FrameBufferProperties()
        props.setRgbColor(1)
        props.setAlphaBits(1)
        props.setDepthBits(1)
        LBuffer = base.graphicsEngine.makeOutput(
                 base.pipe, "offscreen buffer", -2,
                 props, winprops,
                 GraphicsPipe.BFRefuseWindow,
                 base.win.getGsg(), base.win)

        if (LBuffer == None):
           print "[err] Video driver cannot create an offscreen buffer."
           return

        # Adding a color texture is totally unnecessary, but it helps with debugging.
        Lcolormap = Texture()
        LBuffer.addRenderTexture(Lcolormap, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)

        self.LCam = base.makeCamera(LBuffer)
        self.LCam.reparentTo(render)
        self.LCam.node().setScene(render)
        self.LCam.node().getLens().setFov(30)
        self.LCam.node().getLens().setNearFar(277, 305)
        #self.LCam.node().getLens().setFilmSize(100, 100)
        self.LCam.setPos(0, 0, 300)
        self.LCam.setP(-90)
        self.LCam.lookAt(0, 0, 0)
        #self.createFrustumNode(self.LCam)

        # default values
        self.pushBias=0.50
        self.ambient=0.20
        self.cameraSelection = 0
        self.lightSelection = 0

        # Put a shader on the Main camera.
        # Some video cards have special hardware for shadow maps.
        # If the card has that, use it.  If not, use a different
        # shader that does not require hardware support.

        mci = NodePath(PandaNode("Main Camera Initializer"))
        if (base.win.getGsg().getSupportsShadowFilter()):
            mci.setShader(Shader.load(self.datadir + 'shaders/shadow.sha'))
        else:
            print "[wrn] No shadow shader support found, using shadow-nosupport.sha"
            mci.setShader(Shader.load(self.datadir + 'shaders/shadow-nosupport.sha'))
        base.cam.node().setInitialState(mci.getState())
    
        # setting up shader
        render.setShaderInput('light',self.LCam)
        render.setShaderInput('Ldepthmap',Lcolormap)
        render.setShaderInput('ambient',self.ambient,0,0,1.0)
        render.setShaderInput('texDisable',0,0,0,0)
        render.setShaderInput('scale',1,1,1,1)

        render.setShaderInput('push',0.20,0.20,0.20,0)

        # Put a shader on the Light camera.
        lci = NodePath(PandaNode("Light Camera Initializer"))
        lci.setShader(Shader.load(self.datadir + 'shaders/caster.sha'))
        self.LCam.node().setInitialState(lci.getState())

        self.adjustPushBias(self.pushBias)

    def shaderSupported(self):
        return base.win.getGsg().getSupportsBasicShaders() and \
               base.win.getGsg().getSupportsDepthTexture() and \
               base.win.getGsg().getSupportsShadowFilter()