Example #1
0
float CGround::LineGroundCol(float3 from, float3 to)
{
    float savedLength = 0.0f;

    if (from.z > float3::maxzpos && to.z < float3::maxzpos) {
        // a special case since the camera in overhead mode can often do this
        float3 dir = to - from;
        float maxLength = dir.Length();
        dir /= maxLength;

        savedLength = -(from.z - float3::maxzpos) / dir.z;
        from += dir * savedLength;
    }

    from.CheckInBounds();

    float3 dir = to - from;
    float maxLength = dir.Length();
    dir /= maxLength;

    if (from.x + dir.x * maxLength < 1.0f)
        maxLength = (1.0f - from.x) / dir.x;
    else if (from.x + dir.x * maxLength > float3::maxxpos)
        maxLength = (float3::maxxpos - from.x) / dir.x;

    if (from.z + dir.z * maxLength < 1.0f)
        maxLength = (1.0f - from.z) / dir.z;
    else if (from.z + dir.z * maxLength > float3::maxzpos)
        maxLength = (float3::maxzpos - from.z) / dir.z;

    to = from + dir * maxLength;

    const float dx=to.x-from.x;
    const float dz=to.z-from.z;
    float xp=from.x;
    float zp=from.z;
    float ret;
    float xn,zn;

    bool keepgoing=true;

    if((floor(from.x/SQUARE_SIZE)==floor(to.x/SQUARE_SIZE)) && (floor(from.z/SQUARE_SIZE)==floor(to.z/SQUARE_SIZE))) {
        ret = LineGroundSquareCol(from,to,(int)floor(from.x/SQUARE_SIZE),(int)floor(from.z/SQUARE_SIZE));
        if(ret>=0) {
            return ret;
        }
    } else if(floor(from.x/SQUARE_SIZE)==floor(to.x/SQUARE_SIZE)) {
        while(keepgoing) {
            ret = LineGroundSquareCol(from,to,(int)floor(xp/SQUARE_SIZE),(int)floor(zp/SQUARE_SIZE));
            if(ret>=0) {
                return ret+savedLength;
            }
            keepgoing=fabs(zp-from.z)<fabs(dz);
            if(dz>0)
                zp+=SQUARE_SIZE;
            else
                zp-=SQUARE_SIZE;
        }
        // if you hit this the collision detection hit an infinite loop
        assert(!keepgoing);
    } else if(floor(from.z/SQUARE_SIZE)==floor(to.z/SQUARE_SIZE)) {
        while(keepgoing) {
            ret = LineGroundSquareCol(from,to,(int)floor(xp/SQUARE_SIZE),(int)floor(zp/SQUARE_SIZE));
            if(ret>=0) {
                return ret+savedLength;
            }
            keepgoing=fabs(xp-from.x)<fabs(dx);
            if(dx>0)
                xp+=SQUARE_SIZE;
            else
                xp-=SQUARE_SIZE;
        }
        // if you hit this the collision detection hit an infinite loop
        assert(!keepgoing);
    } else {
        while(keepgoing) {
            float xs, zs;

            // Push value just over the edge of the square
            // This is the best accuracy we can get with floats:
            // add one digit and (xp*constant) reduces to xp itself
            // This accuracy means that at (16384,16384) (lower right of 32x32 map)
            // 1 in every 1/(16384*1e-7f/8)=4883 clicks on the map will be ignored.
            if (dx>0) xs = floor(xp*1.0000001f/SQUARE_SIZE);
            else      xs = floor(xp*0.9999999f/SQUARE_SIZE);
            if (dz>0) zs = floor(zp*1.0000001f/SQUARE_SIZE);
            else      zs = floor(zp*0.9999999f/SQUARE_SIZE);

            ret = LineGroundSquareCol(from, to, (int)xs, (int)zs);
            if(ret>=0) {
                return ret+savedLength;
            }
            keepgoing=fabs(xp-from.x)<fabs(dx) && fabs(zp-from.z)<fabs(dz);

            if(dx>0) {
                // distance xp to right edge of square (xs,zs) divided by dx, xp += xn*dx puts xp on the right edge
                xn=(xs*SQUARE_SIZE+SQUARE_SIZE-xp)/dx;
            } else {
                // distance xp to left edge of square (xs,zs) divided by dx, xp += xn*dx puts xp on the left edge
                xn=(xs*SQUARE_SIZE-xp)/dx;
            }
            if(dz>0) {
                // distance zp to bottom edge of square (xs,zs) divided by dz, zp += zn*dz puts zp on the bottom edge
                zn=(zs*SQUARE_SIZE+SQUARE_SIZE-zp)/dz;
            } else {
                // distance zp to top edge of square (xs,zs) divided by dz, zp += zn*dz puts zp on the top edge
                zn=(zs*SQUARE_SIZE-zp)/dz;
            }
            // xn and zn are always positive, minus signs are divided out above

            // this puts (xp,zp) exactly on the first edge you see if you look from (xp,zp) in the (dx,dz) direction
            if(xn<zn) {
                xp+=xn*dx;
                zp+=xn*dz;
            } else {
                xp+=zn*dx;
                zp+=zn*dz;
            }
        }
    }
    return -1;
}
Example #2
0
float CGround::LineGroundCol(float3 from, float3 to, bool synced)
{
	const float* hm  = readMap->GetSharedCornerHeightMap(synced);
	const float3* nm = readMap->GetSharedFaceNormals(synced);

	const float3 pfrom = from;

	// only for performance -> skip part that can impossibly collide
	// with the terrain, cause it is above map's current max height
	ClampInMapHeight(from, to);

	// handle special cases where the ray origin is out of bounds:
	// need to move <from> to the closest map-edge along the ray
	// (if both <from> and <to> are out of bounds, the ray might
	// still hit)
	// clamping <from> naively would change the direction of the
	// ray, hence we save the distance along it that got skipped
	ClampLineInMap(from, to);

	if (from == to) {
		// ClampLineInMap & ClampInMapHeight set `from == to == vec(-1,-1,-1)`
		// in case the line is outside of the map
		return -1.0f;
	}

	const float skippedDist = pfrom.distance(from);

	if (synced) { //TODO do this in unsynced too once the map border rendering is finished?
		// check if our start position is underground (assume ground is unpassable for cannons etc.)
		const int sx = from.x / SQUARE_SIZE;
		const int sz = from.z / SQUARE_SIZE;

		if (from.y <= hm[sz * mapDims.mapxp1 + sx]) {
			return 0.0f + skippedDist;
		}
	}

	const float dx = to.x - from.x;
	const float dz = to.z - from.z;
	const int dirx = (dx > 0.0f) ? 1 : -1;
	const int dirz = (dz > 0.0f) ? 1 : -1;

	// Clamping is done cause LineGroundSquareCol() operates on the 2 triangles faces each heightmap
	// square is formed of.
	const float ffsx = Clamp(from.x / SQUARE_SIZE, 0.0f, (float)mapDims.mapx);
	const float ffsz = Clamp(from.z / SQUARE_SIZE, 0.0f, (float)mapDims.mapy);
	const float ttsx = Clamp(to.x / SQUARE_SIZE, 0.0f, (float)mapDims.mapx);
	const float ttsz = Clamp(to.z / SQUARE_SIZE, 0.0f, (float)mapDims.mapy);
	const int fsx = ffsx;
	const int fsz = ffsz;
	const int tsx = ttsx;
	const int tsz = ttsz;

	bool keepgoing = true;

	if ((fsx == tsx) && (fsz == tsz)) {
		// <from> and <to> are the same
		const float ret = LineGroundSquareCol(hm, nm,  from, to,  fsx, fsz);

		if (ret >= 0.0f) {
			return (ret + skippedDist);
		}
	} else if (fsx == tsx) {
		// ray is parallel to z-axis
		int zp = fsz;

		while (keepgoing) {
			const float ret = LineGroundSquareCol(hm, nm,  from, to,  fsx, zp);

			if (ret >= 0.0f) {
				return (ret + skippedDist);
			}

			keepgoing = (zp != tsz);
			zp += dirz;
		}
	} else if (fsz == tsz) {
		// ray is parallel to x-axis
		int xp = fsx;

		while (keepgoing) {
			const float ret = LineGroundSquareCol(hm, nm,  from, to,  xp, fsz);

			if (ret >= 0.0f) {
				return (ret + skippedDist);
			}

			keepgoing = (xp != tsx);
			xp += dirx;
		}
	} else {
		// general case
		const float rdsx = SQUARE_SIZE / dx; // := 1 / (dx / SQUARE_SIZE)
		const float rdsz = SQUARE_SIZE / dz;

		// we need to shift the `test`-point in case of negative directions
		// case: dir<0
		//  ___________
		// |   |   |   |
		// |___|___|___|
		//     ^cur
		// ^cur + dir
		// >   < range of int(cur + dir)
		//     ^wanted test point := cur - epsilon
		// you can set epsilon=0 and then handle the `beyond end`-case (xn >= 1.0f && zn >= 1.0f) separate
		// (we already need to do so cause of floating point precision limits, so skipping epsilon doesn't add
		// any additional performance cost nor precision issue)
		//
		// case : dir>0
		// in case of `dir>0` the wanted test point is idential with `cur + dir`
		const float testposx = (dx > 0.0f) ? 0.0f : 1.0f;
		const float testposz = (dz > 0.0f) ? 0.0f : 1.0f;

		int curx = fsx;
		int curz = fsz;

		while (keepgoing) {
			// do the collision test with the squares triangles
			const float ret = LineGroundSquareCol(hm, nm,  from, to,  curx, curz);

			if (ret >= 0.0f) {
				return (ret + skippedDist);
			}

			// check if we reached the end already and need to stop the loop
			const bool endReached = (curx == tsx && curz == tsz);
			const bool beyondEnd = ((curx - tsx) * dirx > 0) || ((curz - tsz) * dirz > 0);

			assert(!beyondEnd);
			keepgoing = !endReached && !beyondEnd;

			if (!keepgoing)
				 break;

			// calculate the `normalized position` of the next edge in x & z direction
			//  `normalized position`:=n :   x = from.x + n * (to.x - from.x)   (with 0<= n <=1)
			int nextx = curx + dirx;
			int nextz = curz + dirz;
			float xn = (nextx + testposx - ffsx) * rdsx;
			float zn = (nextz + testposz - ffsz) * rdsz;

			// handles the following 2 case:
			// case1: (floor(to.x) == to.x) && (to.x < from.x)
			//   In this case we calculate xn at to.x but set curx = to.x - 1,
			//   and so we would be beyond the end of the ray.
			// case2: floating point precision issues
			if ((nextx - tsx) * dirx > 0) { xn=1337.0f; nextx=tsx; }
			if ((nextz - tsz) * dirz > 0) { zn=1337.0f; nextz=tsz; }

			// advance to the next nearest edge in either x or z dir, or in the case we reached the end make sure
			// we set it to the exact square positions (floating point precision sometimes hinders us to hit it)
			if (xn >= 1.0f && zn >= 1.0f) {
				assert(curx != nextx || curz != nextz);
				curx = nextx;
				curz = nextz;
			} else if (xn < zn) {
				assert(curx != nextx);
				curx = nextx;
			} else {
				assert(curz != nextz);
				curz = nextz;
			}
		}
	}

	return -1.0f;
}