Esempio n. 1
0
 def createRays(self):
     self.notify.debug('createRays')
     body = OdeBody(self.world)
     self.ballRay = OdeRayGeom(self.space, 50.0)
     self.ballRay.setBody(body)
     self.ballRay.setOffsetRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1))
     self.ballRay.setOffsetPosition(0, 0, 0.0)
     self.ballRay.setCollideBits(BitMask32(16773375))
     self.ballRay.setCategoryBits(BitMask32(4278190080L))
     self.ballRayBody = body
     self.space.setCollideId(self.ballRay, GolfGlobals.OOB_RAY_COLLIDE_ID)
     self.rayList.append(self.ballRay)
     self.rayList.append(self.ballRayBody)
     self.skyRay = OdeRayGeom(self.space, 100.0)
     self.skyRay.setCollideBits(BitMask32(240))
     self.skyRay.setCategoryBits(BitMask32(0))
     self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1))
     self.space.setCollideId(self.skyRay, GolfGlobals.SKY_RAY_COLLIDE_ID)
     self.rayList.append(self.skyRay)
Esempio n. 2
0
def rayhit_closet(pfrom, pto, objcm):
    """
    :param pfrom:
    :param pto:
    :param objcm:
    :return:
    author: weiwei
    date: 20190805
    """
    tgt_cdmesh = gen_cdmesh_vvnf(*objcm.extract_rotated_vvnf())
    ray = OdeRayGeom(length=1)
    length, dir = rm.unit_vector(pto - pfrom, toggle_length=True)
    ray.set(pfrom[0], pfrom[1], pfrom[2], dir[0], dir[1], dir[2])
    ray.setLength(length)
    contact_entry = OdeUtil.collide(ray, tgt_cdmesh, max_contacts=10)
    contact_points = [
        da.pdv3_to_npv3(point) for point in contact_entry.getContactPoints()
    ]
    min_id = np.argmin(np.linalg.norm(pfrom - np.array(contact_points),
                                      axis=1))
    contact_normals = [
        da.pdv3_to_npv3(contact_entry.getContactGeom(i).getNormal())
        for i in range(contact_entry.getNumContacts())
    ]
    return contact_points[min_id], contact_normals[min_id]
Esempio n. 3
0
def rayhit_all(pfrom, pto, objcm):
    """
    :param pfrom:
    :param pto:
    :param objcm:
    :return:
    author: weiwei
    date: 20190805
    """
    tgt_cdmesh = gen_cdmesh_vvnf(*objcm.extract_rotated_vvnf())
    ray = OdeRayGeom(length=1)
    length, dir = rm.unit_vector(pto-pfrom, toggle_length=True)
    ray.set(pfrom[0], pfrom[1], pfrom[2], dir[0], dir[1], dir[2])
    ray.setLength(length)
    hit_entry = OdeUtil.collide(ray, tgt_cdmesh)
    hit_points = [da.pdv3_to_npv3(point) for point in hit_entry.getContactPoints()]
    hit_normals = [da.pdv3_to_npv3(hit_entry.getContactGeom(i).getNormal()) for i in range(hit_entry.getNumContacts())]
    return hit_points, hit_normals
Esempio n. 4
0
class GolfHoleBase:

    def __init__(self, canRender = 0):
        self.canRender = canRender
        self.recording = []
        self.aVRecording = []
        self.holePositions = []
        self.grayCount = 0
        self.skyContact = None
        self.lastSkyContactPoint = None
        self.doingRecording = 0
        self.backAmount = 270
        self.ballRocket = 0
        self.inCount = 0
        self.frame = 0
        self.onSlick = 0
        self.didHoleBreak = 0

    def loadLevel(self):
        tm = self.holeInfo['terrainModel']
        self.terrainModel = loader.loadModel(tm)
        td = self.holeInfo['physicsData']
        if self.canRender:
            self.terrainModel.reparentTo(render)
        
        if self.canRender:
            self.terrainModel.find('**/softSurface').setBin('ground', 0)
        
        terrainData = self.terrainModel.find('**/softSurface')
        grassData = terrainData.findAllMatches('**/grass*')
        self.terrainData = []
        for index in xrange(grassData.getNumPaths()):
            someTerrainData = grassData[index]
            terrainDataOde = OdeTriMeshData(someTerrainData)
            self.meshDataList.append(terrainDataOde)
            terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde)
            self.geomDataList.append(terrainGeomOde)
            terrainGeomOde.setCollideBits(BitMask32(4026531840L))
            terrainGeomOde.setCategoryBits(BitMask32(240))
            self.space.setSurfaceType(terrainGeomOde, GolfGlobals.GRASS_SURFACE)
            self.space.setCollideId(terrainGeomOde, 2)

        slickData = terrainData.findAllMatches('**/slick*')
        self.terrainData = []
        for index in xrange(slickData.getNumPaths()):
            someTerrainData = slickData[index]
            terrainDataOde = OdeTriMeshData(someTerrainData)
            self.meshDataList.append(terrainDataOde)
            terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde)
            self.geomDataList.append(terrainGeomOde)
            terrainGeomOde.setCollideBits(BitMask32(4026531840L))
            terrainGeomOde.setCategoryBits(BitMask32(240))
            self.space.setSurfaceType(terrainGeomOde, GolfGlobals.SLICK_SURFACE)
            self.space.setCollideId(terrainGeomOde, GolfGlobals.SLICK_COLLIDE_ID)

        cupData = terrainData.find('**/hole*')
        cupData = OdeTriMeshData(cupData)
        self.meshDataList.append(cupData)
        cupGeom = OdeTriMeshGeom(self.space, cupData)
        self.geomDataList.append(cupGeom)
        cupGeom.setCollideBits(BitMask32(4026531840L))
        cupGeom.setCategoryBits(BitMask32(240))
        self.space.setSurfaceType(cupGeom, GolfGlobals.HOLE_SURFACE)
        self.space.setCollideId(cupGeom, GolfGlobals.HOLE_CUP_COLLIDE_ID)
        if self.canRender:
            self.golfBarrier = self.terrainModel.find('**/collision1')
            if not self.golfBarrier.isEmpty():
                golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?')
                for i in xrange(golfBarrierCollection.getNumPaths()):
                    oneBarrier = golfBarrierCollection.getPath(i)
                    if oneBarrier != self.golfBarrier:
                        oneBarrier.wrtReparentTo(self.golfBarrier)

                self.golfBarrier.hide()
            else:
                self.notify.warning('Could not find collision1 node ---------')
        self.hardSurfaceNodePath = self.terrainModel.find('**/hardSurface')
        if self.canRender:
            self.terrainModel.find('**/hardSurface').setBin('ground', 0)
        
        self.loadBlockers()
        hardData = OdeTriMeshData(self.hardSurfaceNodePath)
        self.meshDataList.append(hardData)
        hardGeom = OdeTriMeshGeom(self.space, hardData)
        self.geomDataList.append(hardGeom)
        hardGeom.setCollideBits(BitMask32(4026531840L))
        hardGeom.setCategoryBits(BitMask32(240))
        self.space.setCollideId(hardGeom, 3)
        hardSurface = self.space.getSurfaceType(hardGeom)
        self.notify.debug('hardSurface = %s' % hardSurface)
        if self.notify.getDebug():
            self.notify.debug('self.hardGeom')
            hardGeom.write()
            self.notify.debug(' -')
        
        self.holeBottomNodePath = self.terrainModel.find('**/holebottom0')
        if self.holeBottomNodePath.isEmpty():
            self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0])
        else:
            self.holeBottomPos = self.holeBottomNodePath.getPos()
        
        self.holePositions.append(self.holeBottomPos)

    def isBallInHole(self, ball):
        retval = False
        for holePos in self.holePositions:
            displacement = ball.getPosition() - holePos
            length = displacement.length()
            self.notify.debug('hole %s length=%s' % (holePos, length))
            if length <= GolfGlobals.DistanceToBeInHole * 0.5:
                retval = True
                break

        return retval

    def createRays(self):
        self.notify.debug('createRays')
        body = OdeBody(self.world)
        self.ballRay = OdeRayGeom(self.space, 50.0)
        self.ballRay.setBody(body)
        self.ballRay.setOffsetRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1))
        self.ballRay.setOffsetPosition(0, 0, 0.0)
        self.ballRay.setCollideBits(BitMask32(16773375))
        self.ballRay.setCategoryBits(BitMask32(4278190080L))
        self.ballRayBody = body
        self.space.setCollideId(self.ballRay, GolfGlobals.OOB_RAY_COLLIDE_ID)
        self.rayList.append(self.ballRay)
        self.rayList.append(self.ballRayBody)
        self.skyRay = OdeRayGeom(self.space, 100.0)
        self.skyRay.setCollideBits(BitMask32(240))
        self.skyRay.setCategoryBits(BitMask32(0))
        self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1))
        self.space.setCollideId(self.skyRay, GolfGlobals.SKY_RAY_COLLIDE_ID)
        self.rayList.append(self.skyRay)

    def delete(self):
        self.ballRay = None
        self.skyRay = None
        self.recording = None
        self.avRecording = None
        self.llv = None

    def initRecord(self):
        del self.recording
        self.recording = []
        del self.aVRecording
        self.aVRecording = []
        self.skipFrame = 0.0
        self.frame = 0
        self.tXYMax = 1.0
        self.tZMax = 1.0
        self.tXYMin = 0.1
        self.tZMin = 0.1
        self.skyContact = 1
        self.doingRecording = 1
        self.ballRocket = 0
        self.inCount = 0
        self.ballInHoleFrame = 0
        self.ballTouchedHoleFrame = 0
        self.ballFirstTouchedHoleFrame = 0
        self.ballLastTouchedGrass = 0
        self.hasReset = 0
        self.resetAt = 100000
        self.greenIn = 0
        for key in self.commonObjectDict:
            self.commonObjectDict[key][2].enable()

    def checkCommonObjectsNeedPass(self):
        for index in self.commonObjectDict:
            if self.commonObjectDict[index][1] in [4]:
                return 1

        return 0

    def checkInRadius(self, ball):
        smallestDist = None
        for index in self.commonObjectDict:
            if self.commonObjectDict[index][1] in [4]:
                radius = self.commonObjectDict[index][8]
                mover = self.commonObjectDict[index][2]
                diffX = ball.getPosition()[0] - mover.getPosition()[0]
                diffY = ball.getPosition()[1] - mover.getPosition()[1]
                diffZ = ball.getPosition()[2] - mover.getPosition()[2]
                dist = math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ)
                if dist < radius:
                    if not smallestDist or smallestDist[1] > dist:
                        smallestDist = [radius, dist]
                        self.notify.debug('Ball Pos %s\nMover Pos %s' % (ball.getPosition(), mover.getPosition()))

        return smallestDist

    def trackRecordBodyFlight(self, ball, cycleTime, power, startPos, dirX, dirY):
        self.notify.debug('trackRecordBodyFlight')
        self.ballInHoleFrame = 0
        self.ballTouchedHoleFrame = 0
        self.ballFirstTouchedHoleFrame = 0
        self.ballLastTouchedGrass = 0
        startTime = globalClock.getRealTime()
        self.notify.debug('start position %s' % startPos)
        self.swingTime = cycleTime
        frameCount = 0
        lift = 0
        startTime = GolfGlobals.BALL_CONTACT_FRAME / 24
        startFrame = int(startTime * self.FPS)
        for frame in xrange(int(startFrame)):
            self.simulate()
            self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep)
            frameCount += 1

        forceMove = 1500
        if power > 50:
            lift = 0
        
        self.didHoleBreak = 0
        ball.setPosition(startPos)
        ball.setLinearVel(0.0, 0.0, 0.0)
        ball.setAngularVel(0.0, 0.0, 0.0)
        ball.enable()
        self.preStep()
        self.simulate()
        self.postStep()
        ball.enable()
        ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift))
        self.initRecord()
        self.llv = None
        self.lastSkyContactPoint = None
        ran = 0
        self.record(ball)
        self.comObjNeedPass = self.checkCommonObjectsNeedPass()
        self.notify.debug('self.comObjNeedPass %s' % self.comObjNeedPass)
        firstDisabled = -1
        reEnabled = 0
        lastFrameEnabled = 0
        checkFrames = self.FPS * (self.timingCycleLength + 1.0)
        hasPrinted = 0
        while ball.isEnabled() and len(self.recording) < 2100 or self.comObjNeedPass or len(self.recording) < 10:
            ran = 1
            if len(self.recording) > 2100 and not hasPrinted:
                self.notify.debug('recording too long %s' % len(self.recording))
                hasPrinted = 1
                ball.disable()
            
            self.preStep()
            self.simulate()
            self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep)
            frameCount += 1
            self.postStep()
            self.record(ball)
            if self.comObjNeedPass:
                if firstDisabled == -1 and not ball.isEnabled():
                    firstDisabled = self.frame
                    self.notify.debug('firstDisabled %s' % firstDisabled)
                    check = self.checkInRadius(ball)
                    if check == None:
                        self.comObjNeedPass = 0
                        self.notify.debug('out radius')
                    else:
                        self.notify.debug('in radius %s dist %s' % (check[0], check[1]))
                elif ball.isEnabled() and firstDisabled != -1 and not reEnabled:
                    reEnabled = self.frame
                    self.notify.debug('reEnabled %s' % reEnabled)
                if reEnabled:
                    if self.frame > reEnabled + checkFrames:
                        self.comObjNeedPass = 0
                        self.notify.debug('renable limit passed')
                elif self.frame > 2100 + checkFrames:
                    self.comObjNeedPass = 0
                    print 'recording limit passed comObj'
            if ball.isEnabled():
                lastFrameEnabled = self.frame

        self.notify.debug('lastFrameEnabled %s' % lastFrameEnabled)
        if lastFrameEnabled < 3:
            lastFrameEnabled = 3
        
        self.record(ball)
        self.notify.debug('Frames %s' % self.frame)
        midTime = globalClock.getRealTime()
        self.recording = self.recording[:lastFrameEnabled]
        self.aVRecording = self.aVRecording[:lastFrameEnabled]
        self.frame = lastFrameEnabled
        self.processRecording()
        self.processAVRecording()
        self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime,
         self.getCycleTime(),
         len(self.recording),
         len(self.aVRecording)))
        length = len(self.recording) - 1
        x = self.recording[length][1]
        y = self.recording[length][2]
        z = self.recording[length][3]
        endTime = globalClock.getRealTime()
        diffTime = endTime - startTime
        self.doingRecording = 0
        fpsTime = self.frame / diffTime
        self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime,
         midTime,
         endTime,
         diffTime,
         fpsTime,
         self.frame))
        return Vec3(x, y, z)

    def record(self, ball):
        self.recording.append((self.frame,
         ball.getPosition()[0],
         ball.getPosition()[1],
         ball.getPosition()[2]))
        self.aVRecording.append((self.frame,
         ball.getAngularVel()[0],
         ball.getAngularVel()[1],
         ball.getAngularVel()[2]))
        if self.frame > 50 and not self.frame % 13:
            curFrame = self.recording[self.frame]
            pastFrame5 = self.recording[self.frame - 11]
            pastFrame10 = self.recording[self.frame - 34]
            currPosA = Vec3(curFrame[1], curFrame[2], curFrame[3])
            past5PosA = Vec3(pastFrame5[1], pastFrame5[2], pastFrame5[3])
            past10PosA = Vec3(pastFrame10[1], pastFrame10[2], pastFrame10[3])
            displacement1 = currPosA - past5PosA
            displacement2 = currPosA - past10PosA
            if displacement1.lengthSquared() < 0.002 and displacement2.lengthSquared() < 0.002 and not self.grayCount and not self.onSlick:
                ball.disable()
        
        self.frame += 1

    def preStep(self):
        if hasattr(self, 'ballRay'):
            bp = self.curGolfBall().getPosition()
            self.ballRayBody.setPosition(bp[0], bp[1], bp[2])
            self.skyRay.setPosition(bp[0], bp[1], 50.0)

    def getOrderedContacts(self, entry):
        c0 = self.space.getCollideId(entry.getGeom1())
        c1 = self.space.getCollideId(entry.getGeom2())
        if c0 > c1:
            return (c1, c0)
        else:
            return (c0, c1)

    def postStep(self):
        if self.canRender:
            self.translucentLastFrame = self.translucentCurFrame[:]
            self.translucentCurFrame = []
        
        self.onSlick = 0
        rayCount = 0
        skyRayHitPos = None
        ballRayHitPos = None
        bp = self.curGolfBall().getPosition()
        for entry in self.colEntries:
            c0, c1 = self.getOrderedContacts(entry)
            x, y, z = entry.getContactPoint(0)
            if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID:
                rayCount += 1
                if self.canRender:
                    if self.currentGolfer:
                        self.ballShadowDict[self.currentGolfer].setPos(x, y, z + 0.1)
                
                if c1 == GolfGlobals.GRASS_COLLIDE_ID or c1 == GolfGlobals.HARD_COLLIDE_ID:
                    if self.curGolfBall().getPosition()[2] < z + 0.2:
                        ballRayHitPos = Vec3(x, y, z)
            if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.SLICK_COLLIDE_ID:
                self.onSlick = 1
            elif c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.HARD_COLLIDE_ID:
                self.onSlick = 1
            if c0 == GolfGlobals.GRASS_COLLIDE_ID and c1 == GolfGlobals.SKY_RAY_COLLIDE_ID:
                self.lastSkyContactPoint = (x, y, z)
                if self.curGolfBall().getPosition()[2] < z + 0.2 and rayCount == 0:
                    if self.skyContact in [1, 2]:
                        skyRayHitPos = Vec3(x, y, z)
                        self.skyContact += 1
            if self.doingRecording:
                if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID:
                    rayCount += 1
                    if c1 == GolfGlobals.GRASS_COLLIDE_ID:
                        self.greenIn = self.frame
                        self.llv = self.curGolfBall().getLinearVel()
                elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.HOLE_CUP_COLLIDE_ID in [c0, c1]:
                    self.ballTouchedHoleFrame = self.frame
                    ballUndersideZ = self.curGolfBall().getPosition()[2] - 0.05
                    if z < ballUndersideZ:
                        if not self.ballInHoleFrame:
                            self.ballInHoleFrame = self.frame
                   
                    if self.ballFirstTouchedHoleFrame < self.ballLastTouchedGrass:
                        self.ballFirstTouchedHoleFrame = self.frame
                    
                    if self.isBallInHole(self.curGolfBall()) and self.didHoleBreak == 0:
                        self.comObjNeedPass = 0
                        ballLV = self.curGolfBall().getLinearVel()
                        ballAV = self.curGolfBall().getAngularVel()
                        self.curGolfBall().setLinearVel(0.5 * ballLV[0], 0.5 * ballLV[1], 0.5 * ballLV[2])
                        self.curGolfBall().setAngularVel(0.5 * ballAV[0], 0.5 * ballAV[1], 0.5 * ballAV[2])
                        self.notify.debug('BALL IN THE HOLE!!! FOO!')
                        self.didHoleBreak = 1
                        return
                elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID in [c0, c1]:
                    if self.ballInHoleFrame:
                        self.ballInHoleFrame = 0
                        self.notify.debug('setting ballInHoleFrame=0')
                    
                    self.ballLastTouchedGrass = self.frame
            elif self.canRender:
                if c0 == GolfGlobals.TOON_RAY_COLLIDE_ID or c1 == GolfGlobals.TOON_RAY_COLLIDE_ID:
                    self.toonRayCollisionCallback(x, y, z)
                
                if GolfGlobals.CAMERA_RAY_COLLIDE_ID in [c0, c1] and GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]:
                    self.translucentCurFrame.append(self.windmillFanNodePath)
                    self.translucentCurFrame.append(self.windmillBaseNodePath)
                
                if GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID not in [c0, c1]:
                    self.handleBallHitNonGrass(c0, c1)

        if not self.curGolfBall().isEnabled():
            return
        
        if rayCount == 0:
            self.notify.debug('out of bounds detected!')
            self.grayCount += 1
            self.outCommon = self.getCommonObjectData()
            self.inCount = 0
            if skyRayHitPos:
                self.curGolfBall().setPosition(skyRayHitPos[0], skyRayHitPos[1], skyRayHitPos[2] + 0.27)
                self.notify.debug('SKY RAY ADJUST?')
        else:
            if self.grayCount > 1:
                self.notify.debug('Back in bounds')
            
            self.grayCount = 0
            self.inCount += 1
            if ballRayHitPos:
                self.curGolfBall().setPosition(ballRayHitPos[0], ballRayHitPos[1], ballRayHitPos[2] + 0.245)
                ballRayHitPos = None
                if self.doingRecording:
                    self.notify.debug('BALL RAY ADJUST!')
                    self.notify.debug('%s' % self.curGolfBall().getLinearVel())
        
        if self.ballRocket > 0 and self.inCount > 1:
            self.ballRocket -= 1
            rocketVel = self.curGolfBall().getLinearVel()
            self.curGolfBall().setLinearVel(2.0 * rocketVel[0], 2.0 * rocketVel[1], 2.0 * rocketVel[2])
            self.notify.debug('ROCKET!!!!')
        
        if self.grayCount > self.backAmount and self.doingRecording:
            if self.greenIn > 2:
                self.greenIn -= 2
            
            if self.greenIn > self.resetAt:
                self.greenIn = self.resetAt - 10
            
            if self.greenIn < 0 or self.hasReset > 3:
                self.greenIn = 0
            
            self.hasReset += 1
            self.notify.debug('BALL RESET frame %s greenIn %s resetAt %s' % (self.frame, self.greenIn, self.resetAt))
            self.useCommonObjectData(self.outCommon)
            self.curGolfBall().setPosition(self.recording[self.greenIn][1], self.recording[self.greenIn][2], self.recording[self.greenIn][3] + 0.27)
            self.curGolfBall().setAngularVel(0, 0, 0)
            if self.hasReset < 3 and self.llv:
                self.ballRocket += 1
                self.notify.debug(' BRAKE!!!!')
                self.curGolfBall().setLinearVel(0.5 * self.llv[0], 0.5 * self.llv[1], 0.5 * self.llv[2])
            else:
                self.notify.debug('back disable %s' % self.frame)
                if self.lastSkyContactPoint:
                    self.curGolfBall().setPosition(self.lastSkyContactPoint[0], self.lastSkyContactPoint[1], self.lastSkyContactPoint[2] + 0.27)
                
                self.curGolfBall().setLinearVel(0, 0, 0)
                self.curGolfBall().disable()
            
            self.recording = self.recording[:self.greenIn]
            self.aVRecording = self.aVRecording[:self.greenIn]
            self.frame = self.greenIn
            self.resetAt = self.greenIn
            self.grayCount = 0
            if self.ballFirstTouchedHoleFrame > self.frame:
                self.notify.debug('reseting first touched hole, self.frame=%d self.ballFirstTouchedHoleFrame=%d' % (self.frame, self.ballFirstTouchedHoleFrame))
                self.ballFirstTouchedHoleFrame = 0
            
            if self.ballLastTouchedGrass > self.frame:
                self.ballLastTouchedGrass = 0

    def processRecording(self, errorMult = 1.0):
        self.notify.debug('processRecording')
        lastFrame = self.recording[len(self.recording) - 1][0]
        countRemovals = 0
        for frame in self.recording:
            if frame[0] == 0 or frame[0] == lastFrame:
                pass
            else:
                index = self.recording.index(frame)
                prevFrame = self.recording[index - 1]
                nextFrame = self.recording[index + 1]
                if self.predict(frame, prevFrame, nextFrame, errorMult):
                    self.recording.remove(frame)
                    countRemovals += 1

        if countRemovals > 5:
            self.processRecording()
        elif len(self.recording) > 120:
            self.processRecording(errorMult * 1.25)
        else:
            for frame in self.recording:
                pass

    def processAVRecording(self, errorMult = 1.0, trials = 0):
        self.notify.debug('processAVRecording')
        lastFrame = self.recording[len(self.recording) - 1][0]
        countRemovals = 0
        countTrials = trials
        for frame in self.aVRecording:
            if frame[0] == 0 or frame[0] == lastFrame:
                pass
            else:
                index = self.aVRecording.index(frame)
                prevFrame = self.aVRecording[index - 1]
                nextFrame = self.aVRecording[index + 1]
                if self.predictAV(frame, prevFrame, nextFrame, errorMult):
                    self.aVRecording.remove(frame)
                    countRemovals += 1
                else:
                    countTrials += 1

        if countRemovals > 5:
            self.processAVRecording(errorMult, countTrials)
        elif len(self.aVRecording) > 80:
            self.processAVRecording(errorMult * 1.25, countTrials)
        else:
            for frame in self.aVRecording:
                pass

    def predict(self, frame, sourceFrame, destFrame, errorMult = 1.0):
        tXY = 0.05 * errorMult
        tZ = 0.05 * errorMult
        projLength = destFrame[0] - sourceFrame[0]
        projPen = destFrame[0] - frame[0]
        propSource = float(projPen) / float(projLength)
        propDest = 1.0 - propSource
        projX = sourceFrame[1] * propSource + destFrame[1] * propDest
        projY = sourceFrame[2] * propSource + destFrame[2] * propDest
        projZ = sourceFrame[3] * propSource + destFrame[3] * propDest
        varX = abs(projX - frame[1])
        varY = abs(projY - frame[2])
        varZ = abs(projZ - frame[3])
        
        if varX > tXY or varY > tXY or varZ > tZ:
            return 0
        else:
            return 1

    def predictAV(self, frame, sourceFrame, destFrame, errorMult = 1.0):
        tXYZ = 1.5 * errorMult
        projLength = destFrame[0] - sourceFrame[0]
        projPen = destFrame[0] - frame[0]
        propSource = float(projPen) / float(projLength)
        propDest = 1.0 - propSource
        projX = sourceFrame[1] * propSource + destFrame[1] * propDest
        projY = sourceFrame[2] * propSource + destFrame[2] * propDest
        projZ = sourceFrame[3] * propSource + destFrame[3] * propDest
        varX = abs(projX - frame[1])
        varY = abs(projY - frame[2])
        varZ = abs(projZ - frame[3])
        
        if varX > tXYZ or varY > tXYZ or varZ > tXYZ:
            return 0
        else:
            return 1

    def handleBallHitNonGrass(self, c0, c1):
        pass