Files
2025-09-29 00:52:08 +02:00

1133 lines
34 KiB
Scheme
Executable File

//-------------------------------------------------
// File: ggsm_arcade_maths.sch
// Purpose: general functions required by system
//-------------------------------------------------
//-------------------------------------------------
// INCLUDES
//-------------------------------------------------
USING "ggsm_structs.sch"
//-------------------------------------------------
// MISC FUNCTIONS
//-------------------------------------------------
FUNC INT GGSM_GET_NUMBER_OF_DIGITS_IN_NUMBER(INT iNumber)
RETURN FLOOR(LOG10(TO_FLOAT(iNumber))) + 1
ENDFUNC
//-------------------------------------------------
// COLOR FUNCTIONS
//-------------------------------------------------
PROC GGSM_SET_PACKED_R_COLOR(INT &col, INT c)
SET_BITS_IN_RANGE(col, 24, 31, c & 255)
ENDPROC
PROC GGSM_SET_PACKED_G_COLOR(INT &col, INT c)
SET_BITS_IN_RANGE(col, 16, 23, c & 255)
ENDPROC
PROC GGSM_SET_PACKED_B_COLOR(INT &col, INT c)
SET_BITS_IN_RANGE(col, 8, 15, c & 255)
ENDPROC
PROC GGSM_SET_PACKED_A_COLOR(INT &col, INT c)
SET_BITS_IN_RANGE(col, 0, 7, c & 255)
ENDPROC
FUNC INT GGSM_PACK_RGBA_COLOUR(INT r, INT g, INT b, INT a = 255)
RETURN (SHIFT_LEFT(r & 255, 24) | SHIFT_LEFT(g & 255, 16) | SHIFT_LEFT(b & 255, 8) | (a & 255))
ENDFUNC
FUNC INT GGSM_PACK_RGBA_COLOUR_STRUCT(RGBA_COLOUR_STRUCT &col)
RETURN (SHIFT_LEFT(col.iR & 255, 24) | SHIFT_LEFT(col.iG & 255, 16) | SHIFT_LEFT(col.iB & 255, 8) | (col.iA & 255))
ENDFUNC
PROC GGSM_UNPACK_RGBA_COLOUR(INT iCol, RGBA_COLOUR_STRUCT &out)
out.iA = GET_BITS_IN_RANGE(iCol, 0, 7)
out.iB = GET_BITS_IN_RANGE(iCol, 8, 15)
out.iG = GET_BITS_IN_RANGE(iCol, 16, 23)
out.iR = GET_BITS_IN_RANGE(iCol, 24, 31)
ENDPROC
//-------------------------------------------------
// DIRECTION FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// This packed 2d direction to int
/// Only holds values between -1.0 an 1.0
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC INT GGSM_PACK_DIRECTION_2D_TO_INT(VECTOR_2D vec)
INT ix = FLOOR(vec.x * 32767.0) + 32767
INT iy = FLOOR(vec.y * 32767.0) + 32767
RETURN ix + SHIFT_LEFT(iy, 16)
ENDFUNC
/// PURPOSE:
/// This packed 2d direction to int
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC INT GGSM_PACK_DIRECTION_2D_TO_INT_EXPLICIT(FLOAT x, FLOAT y)
INT ix = FLOOR(x * 32767.0) + 32767
INT iy = FLOOR(y * 32767.0) + 32767
RETURN ix + SHIFT_LEFT(iy, 16)
ENDFUNC
/// PURPOSE:
/// This unpacks 2d direction from int
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC VECTOR_2D GGSM_UNPACK_DIRECTION_2D(INT i)
VECTOR_2D v
v.x = TO_FLOAT(GET_BITS_IN_RANGE(i, 0, 15) - 32767) / 32767.0
v.y = TO_FLOAT(GET_BITS_IN_RANGE(i, 16, 31) - 32767) / 32767.0
RETURN v
ENDFUNC
//-------------------------------------------------
// VECTOR FUNCTIONS
//-------------------------------------------------
FUNC VECTOR_2D GGSM_FLOOR_VECTOR_2D_FOR_RENDERING(VECTOR_2D vec)
VECTOR_2D out
out.x = TO_FLOAT(FLOOR(vec.x))
out.y = TO_FLOAT(FLOOR(vec.y))
RETURN out
ENDFUNC
/// PURPOSE:
/// This packed 2d vector to int
/// It doesn't care about decimal points and only holds up to -32767 to +32767
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC INT GGSM_PACK_VECTOR_2D_TO_INT(VECTOR_2D vec)
INT ix = FLOOR(vec.x) + 32767
INT iy = FLOOR(vec.y) + 32767
RETURN ix + SHIFT_LEFT(iy, 16)
ENDFUNC
/// PURPOSE:
/// This packed 2d vector to int
/// It doesn't care about decimal points and only holds up to -32767 to +32767
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC INT GGSM_PACK_VECTOR_2D_TO_INT_EXPLICIT(FLOAT x, FLOAT y)
INT ix = FLOOR(x) + 32767
INT iy = FLOOR(y) + 32767
RETURN ix + SHIFT_LEFT(iy, 16)
ENDFUNC
/// PURPOSE:
/// This unpacks 2d vector to int
/// It doesn't care about decimal points and only holds up to -32767 to +32767
/// PARAMS:
/// vec -
/// RETURNS:
///
FUNC VECTOR_2D GGSM_UNPACK_VECTOR_2D(INT i)
VECTOR_2D v
v.x = TO_FLOAT(GET_BITS_IN_RANGE(i, 0, 15) - 32767)
v.y = TO_FLOAT(GET_BITS_IN_RANGE(i, 16, 31) - 32767)
RETURN v
ENDFUNC
/// PURPOSE:
/// Given a vector return the value of the largest axis
FUNC FLOAT GGSM_GET_DOMINANT_AXIS(VECTOR_2D v)
IF (ABSF(v.x) > ABSF(v.y))
RETURN v.x
ENDIF
RETURN v.y
ENDFUNC
/// PURPOSE:
/// Given a rectangle as topleft corner and size work out the center
/// PARAMS:
/// tlx - top left x
/// tly - top left y
/// w - width
/// h - height
/// RETURNS:
/// Center as vector 2D
FUNC VECTOR_2D GGSM_GET_CENTER_FROM_CORNER_RECT(FLOAT tlx, FLOAT tly, FLOAT w, FLOAT h)
RETURN INIT_VECTOR_2D(tlx + (w / 2.0), tly + (h / 2.0))
ENDFUNC
/// PURPOSE:
/// Returns a vector from a heading - 0 is south, 90 is west, 180 is north and 270 is east
/// PARAMS:
/// fHeading -
/// RETURNS:
///
FUNC VECTOR_2D GGSM_GET_VECTOR_FROM_HEADING(FLOAT fHeading)
VECTOR_2D new
IF (fHeading = 0.0)
new.x = 0.0
new.y = 1.0
ELIF (fHeading = 180.0)
new.x = 0.0
new.y = -1.0
ELIF (fHeading = 90.0)
new.x = -1.0
new.y = 0.0
ELIF (fHeading = 270.0)
new.x = 1.0
new.y = 0.0
ELSE
new.x = -SIN(fHeading)
new.y = COS(fHeading)
ENDIF
RETURN new
ENDFUNC
/// PURPOSE:
/// Rotates a vector in 3D space about the world z-axis.
/// It is assumed here that the axis of rotation passes through the origin.
/// If you are rotating by an increment of 90 degrees, see next function.
/// If you are rotating lots of things by the same angle and want to precompute the trig values, see next next function.
/// PARAMS:
/// vToRotate - Should be orientation only (should come from origin)
/// fZRot - Rotation in degrees
/// RETURNS:
/// Vector rotated locally about the world z axis by fZRot degrees - should really put an optimization in here
FUNC VECTOR_2D GGSM_ROTATE_VECTOR_2D_ABOUT_Z(VECTOR_2D vToRotate, FLOAT fZRot)
VECTOR_2D vNew
FLOAT fSin = SIN(fZRot)
FLOAT fCos = COS(fZRot)
vNew.x = vToRotate.x * fCos - vToRotate.y * fSin
vNew.y = vToRotate.x * fSin + vToRotate.y * fCos
RETURN vNew
ENDFUNC
/// PURPOSE:
/// Rotates a vector in 3D space about the world z-axis.
/// It is assumed here that the axis of rotation passes through the origin.
/// If you are rotating by an increment of 90 degrees, see next function.
/// If you are rotating lots of things by the same angle and want to precompute the trig values, see next next function.
/// PARAMS:
/// vToRotate - Should be orientation only (should come from origin)
/// fZRot - Rotation in degrees
/// RETURNS:
/// Vector rotated locally about the world z axis by fZRot degrees
FUNC VECTOR_2D GGSM_ROTATE_VECTOR_2D_ABOUT_Z_PRECOMPUTE(VECTOR_2D vToRotate, FLOAT fSin, FLOAT fCos)
VECTOR_2D vNew
vNew.x = vToRotate.x * fCos - vToRotate.y * fSin
vNew.y = vToRotate.x * fSin + vToRotate.y * fCos
RETURN vNew
ENDFUNC
/// PURPOSE:
/// Get offset relative to a screen coord
/// PARAMS:
/// vPos - position to offset around
/// fRotation - rotation
/// vOffset - offset desired
/// RETURNS:
/// Offset Vector
FUNC VECTOR_2D GGSM_GET_OFFSET_FROM_POSITION(VECTOR_2D vPos, FLOAT fRotation, VECTOR_2D vOffset)
VECTOR_2D v
v = GGSM_ROTATE_VECTOR_2D_ABOUT_Z(vOffset, fRotation)
v.x += vPos.x
v.y += vPos.y
RETURN v
ENDFUNC
/// PURPOSE:
/// Translates rotation by a given direction and speed
/// This is just a wrapper as I got sick of writing this all the time
PROC GGSM_TRANSLATE_VECTOR_BY_DIRECTION(VECTOR_2D &vec, VECTOR_2D dir, FLOAT spd)
vec.x += (dir.x * spd)
vec.y += (dir.y * spd)
ENDPROC
//-------------------------------------------------
// TRANSFORM FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Resets an transformation to identity (zero pos, zero rot, 1:1 scale)
PROC GGSM_IDENTITY_TRANSFORM(GGSM_TRANSFORM &trans)
trans.vPosition.x = 0
trans.vPosition.y = 0
trans.fRotation = 0
trans.vScale.x = 1
trans.vScale.y = 1
ENDPROC
/// PURPOSE:
/// Transforms a local vector into world space given a parent
FUNC VECTOR_2D GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(GGSM_TRANSFORM &parent, VECTOR_2D vLocalPos)
vLocalPos.x *= parent.vScale.x
vLocalPos.y *= parent.vScale.y
VECTOR_2D vWorldPos = GGSM_ROTATE_VECTOR_2D_ABOUT_Z(vLocalPos, parent.fRotation)
vWorldPos.x += parent.vPosition.x
vWorldPos.y += parent.vPosition.y
RETURN vWorldPos
ENDFUNC
/// PURPOSE:
/// Transforms a world vector into local space given a parent
FUNC VECTOR_2D GGSM_TRANSFORM_WORLD_POS_TO_LOCAL_POS(GGSM_TRANSFORM &parent, VECTOR_2D vWorldPos)
VECTOR_2D rel = SUBTRACT_VECTOR_2D(vWorldPos, parent.vPosition)
VECTOR_2D pos = GGSM_ROTATE_VECTOR_2D_ABOUT_Z(rel, -parent.fRotation)
RETURN pos
ENDFUNC
/// PURPOSE:
/// Transforms a local transform into world space given a parent
PROC GGSM_TRANSFORM_LOCAL_TO_WORLD(GGSM_TRANSFORM &parent, GGSM_TRANSFORM &local, GGSM_TRANSFORM &world)
VECTOR_2D vLocal = local.vPosition
vLocal.x *= parent.vScale.x
vLocal.y *= parent.vScale.y
world.vPosition = GGSM_ROTATE_VECTOR_2D_ABOUT_Z(vLocal, parent.fRotation)
world.vPosition.x += parent.vPosition.x
world.vPosition.y += parent.vPosition.y
world.fRotation = parent.fRotation + local.fRotation
world.vScale.x = parent.vScale.x * local.vScale.x
world.vScale.y = parent.vScale.y * local.vScale.y
ENDPROC
/// PURPOSE:
/// Transforms a world transform into local space given a parent
PROC GGSM_TRANSFORM_WORLD_TO_LOCAL(GGSM_TRANSFORM &parent, GGSM_TRANSFORM &world, GGSM_TRANSFORM &local)
VECTOR_2D rel = SUBTRACT_VECTOR_2D(world.vPosition, parent.vPosition)
rel.x *= world.vScale.x
rel.y *= world.vScale.y
local.vPosition = GGSM_ROTATE_VECTOR_2D_ABOUT_Z(rel, -parent.fRotation)
local.fRotation = world.fRotation - parent.fRotation
local.vScale = INIT_VECTOR_2D(1, 1)
ENDPROC
//-------------------------------------------------
// PLANE FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Tells us what side of a plane a point is on given the plane origin and normal
FUNC GGSM_PLANESIDE GGSM_GET_SIDE_OF_PLANE(VECTOR_2D vPlanePos, VECTOR_2D vPlaneNormal, VECTOR_2D vTestPoint)
FLOAT dot = DOT_PRODUCT_VECTOR_2D(SUBTRACT_VECTOR_2D(vTestPoint, vPlanePos), vPlaneNormal)
IF (dot < 0)
RETURN GGSM_PLANESIDE_BEHIND
ELIF (dot > 0)
RETURN GGSM_PLANESIDE_FRONT
ENDIF
RETURN GGSM_PLANESIDE_ON
ENDFUNC
/// PURPOSE:
/// Tells us what side of a plane a point is on given the plane origin and norma
FUNC BOOL GGSM_IS_POINT_BEHIND_PLANE(VECTOR_2D vPlanePos, VECTOR_2D vPlaneNormal, VECTOR_2D vTestPoint)
RETURN (DOT_PRODUCT_VECTOR_2D(SUBTRACT_VECTOR_2D(vTestPoint, vPlanePos), vPlaneNormal) <= 0.0)
ENDFUNC
/// PURPOSE:
/// 2D Wrapper for Get Closest Point On Line
FUNC VECTOR_2D GGSM_GET_CLOSEST_POINT_ON_LINE(VECTOR_2D vTestPoint, VECTOR_2D vStart, VECTOR_2D vEnd, BOOL bClampLine = TRUE)
VECTOR out = GET_CLOSEST_POINT_ON_LINE(<<vTestPoint.x, vTestPoint.y, 0>>, <<vStart.x, vStart.y, 0>>, <<vEnd.x, vEnd.y, 0>>, bClampLine)
RETURN INIT_VECTOR_2D(out.x, out.y)
ENDFUNC
/// PURPOSE:
/// Checks if 2 lines intersect
/// PARAMS:
/// p1 - line 1 point 0
/// p2 - line 1 point 1
/// p3 - line 2 point 0
/// p4 - line 2 point 1
/// pa - intersect point on line 1
/// pb - intersect point on line 2
/// RETURNS:
/// TRUE if intersect
FUNC BOOL GGSM_LINE_LINE_INTERSECT(VECTOR_2D p1, VECTOR_2D p2, VECTOR_2D p3, VECTOR_2D p4, VECTOR_2D &pa, VECTOR_2D &pb)
VECTOR_2D p13, p43, p21
FLOAT d1343, d4321, d4343, d2121
FLOAT numer, denom
FLOAT mua, mub
p43 = SUBTRACT_VECTOR_2D(p4, p3)
IF (ABSF(p43.x) < GGSM_EPSILON AND ABSF(p43.y) < GGSM_EPSILON)
RETURN FALSE
ENDIF
p21 = SUBTRACT_VECTOR_2D(p2, p1)
IF (ABSF(p21.x) < GGSM_EPSILON AND ABSF(p21.y) < GGSM_EPSILON)
RETURN FALSE
ENDIF
p13 = SUBTRACT_VECTOR_2D(p1, p3)
d1343 = DOT_PRODUCT_VECTOR_2D(p13, p43)
d4321 = DOT_PRODUCT_VECTOR_2D(p43, p21)
d4343 = DOT_PRODUCT_VECTOR_2D(p43, p43)
d2121 = DOT_PRODUCT_VECTOR_2D(p21, p21)
denom = d2121 * d4343 - d4321 * d4321
IF (ABSF(denom) < GGSM_EPSILON)
RETURN FALSE
ENDIF
numer = d1343 * d4343 - d4321 * d4321
mua = numer / denom
mub = (d1343 + d4321 * mua) / d4343
pa.x = p1.x + mua * p21.x
pa.y = p1.y + mua * p21.y
pb.x = p3.x + mub * p43.x
pb.y = p3.y + mua * p43.y
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Checks if 2 lines intersect
/// PARAMS:
/// p1 - line 1 point 0
/// p2 - line 1 point 1
/// p3 - line 2 point 0
/// p4 - line 2 point 1
/// pa - intersect point on line 1
/// pb - intersect point on line 2
/// RETURNS:
/// TRUE if intersect
FUNC BOOL GGSM_LINE_LINE_INTERSECT_QUICK(VECTOR_2D p1, VECTOR_2D p2, VECTOR_2D p3, VECTOR_2D p4)
VECTOR_2D p43, p21
FLOAT d4321, d4343, d2121
FLOAT denom
p43 = SUBTRACT_VECTOR_2D(p4, p3)
IF (ABSF(p43.x) < GGSM_EPSILON AND ABSF(p43.y) < GGSM_EPSILON)
RETURN FALSE
ENDIF
p21 = SUBTRACT_VECTOR_2D(p2, p1)
IF (ABSF(p21.x) < GGSM_EPSILON AND ABSF(p21.y) < GGSM_EPSILON)
RETURN FALSE
ENDIF
d4321 = DOT_PRODUCT_VECTOR_2D(p43, p21)
d4343 = DOT_PRODUCT_VECTOR_2D(p43, p43)
d2121 = DOT_PRODUCT_VECTOR_2D(p21, p21)
denom = d2121 * d4343 - d4321 * d4321
IF (ABSF(denom) < GGSM_EPSILON)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Returns squared distance between point c and segment ab
/// PARAMS:
/// a - line point 1
/// b - line point 2
/// c - point to rest
/// RETURNS:
/// Squared Distance for point to closest point on line ab
FUNC FLOAT GGSM_SQDIST_POINT_SEGMENT(VECTOR_2D a, VECTOR_2D b, VECTOR_2D c)
VECTOR_2D ab = SUBTRACT_VECTOR_2D(b, a)
FLOAT u = GET_RATIO_OF_CLOSEST_POINT_ON_LINE(c, a, b, TRUE)
VECTOR_2D p
p.x = a.x + u * ab.x
p.y = a.y + u * ab.y
RETURN DOT_PRODUCT_VECTOR_2D(a, p)
ENDFUNC
/// PURPOSE:
/// Gets the closest squared distance between two lines
/// PARAMS:
/// p1 - line 1 point 1
/// p2 - line 1 point 2
/// q1 - line 2 point 1
/// q2 - line 2 point 2
/// RETURNS:
/// Squared distance
FUNC FLOAT GGSM_SEGMENT_DISTANCE_SQUARED(VECTOR_2D p1, VECTOR_2D p2, VECTOR_2D q1, VECTOR_2D q2)
VECTOR_2D u = SUBTRACT_VECTOR_2D(p2, p1)
VECTOR_2D v = SUBTRACT_VECTOR_2D(q2, q1)
VECTOR_2D w = SUBTRACT_VECTOR_2D(p1, q1)
FLOAT a = DOT_PRODUCT_VECTOR_2D(u, u)
FLOAT b = DOT_PRODUCT_VECTOR_2D(u, v)
FLOAT c = DOT_PRODUCT_VECTOR_2D(v, v)
FLOAT d = DOT_PRODUCT_VECTOR_2D(u, w)
FLOAT e = DOT_PRODUCT_VECTOR_2D(v, w)
FLOAT disc = a * c - b * b
FLOAT sc, sN, sD = disc
FLOAT tc, tN, tD = disc
// compute the line parameters of the two closest points
IF (disc < GGSM_EPSILON) // the lines are almost parallel
sN = 0.0 // force using point P0 on segment S1
sD = 1.0 // to prevent possible division by 0.0 later
tN = e
tD = c
ELSE
sN = (b * e - c * d)
IF (sN < 0.0) // sc < 0 => the s=0 edge is visible
sN = 0.0
tN = e
tD = c
ELIF (sN > sD) // sc > 1 => the s=1 edge is visible
sN = sD
tN = e + b
tD = c
ELSE
tN = (a * e - b * d)
ENDIF
ENDIF
IF (tN < 0.0) // tc < 0 => the t=0 edge is visible
tN = 0.0
// recompute sc for this edge
IF (-d < 0.0)
sN = 0.0
ELIF (-d > a)
sN = sD
ELSE
sN = -d
sD = a
ENDIF
ELIF (tN > tD) // tc > 1 => the t=1 edge is visible
tN = tD
// recompute sc for this edge
IF ((-d + b) < 0.0)
sN = 0
ELIF ((-d + b) > a)
sN = sD
ELSE
sN = (-d + b)
sD = a
ENDIF
ENDIF
// finally do the division to get sc and tc
IF (ABSF(sN) < GGSM_EPSILON)
sc = 0.0
ELSE
sc = sN / sD
ENDIF
IF (ABSF(tN) < GGSM_EPSILON)
tc = 0.0
ELSE
tc = tN / tD
ENDIF
// get the difference of the two closest points
VECTOR_2D dP
dp.x = w.x + (sc * u.x) - (tc * v.x) // = S1(sc) - S2(tc)
dp.y = w.y + (sc * u.y) - (tc * v.y)
RETURN DOT_PRODUCT_VECTOR_2D(dP, dP)
ENDFUNC
/// PURPOSE:
/// Tells us if a point is in a polygon
/// PARAMS:
/// p - point to check
/// points - array of points
/// iNumPoints - num of points in array
/// RETURNS:
///
FUNC BOOL GGSM_IS_POINT_IN_POLYGON(VECTOR_2D p, VECTOR_2D &points[], INT iNumPoints)
INT i, j
BOOL bInside = FALSE
i = 0
j = iNumPoints - 1
WHILE (i < iNumPoints)
IF ( ((points[i].y > p.y) <> (points[j].y > p.y)) AND
(p.x < (points[j].x - points[i].x) * (p.y - points[i].y) / (points[j].y - points[i].y) + points[i].x) )
bInside = NOT bInside
ENDIF
j = i
i ++
ENDWHILE
RETURN bInside
ENDFUNC
/// PURPOSE:
/// Tells us if a sprite is within the draw area
/// PARAMS:
/// vPos - Sprite Pos
/// vSize - Sprite Size
/// RETURNS:
/// TRUE if it is on screen
FUNC BOOL GGSM_IS_POINT_ON_SCREEN(VECTOR_2D vPos, VECTOR_2D vSize)
VECTOR_2D halfScale
halfScale.x = vSize.x / 2.0
halfScale.y = vSize.y / 2.0
IF (vPos.x < (GGSM_PX_OVERSCAN_MIN_X - halfScale.x)) OR (vPos.x > (GGSM_PX_OVERSCAN_MAX_X + halfScale.x))
RETURN FALSE
ENDIF
IF (vPos.y < (GGSM_PX_OVERSCAN_MIN_Y - halfScale.y)) OR (vPos.y > (GGSM_PX_OVERSCAN_MAX_Y + halfScale.y))
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL GGSM_IS_POINT_FULLY_ON_SCREEN(VECTOR_2D vPos, VECTOR_2D vSize, FLOAT fBorder = 30.0)
VECTOR_2D halfScale
vSize.x = ABSF(vSize.x) + fBorder
vSize.y = ABSF(vSize.y) + fBorder
halfScale.x = vSize.x / 2.0
halfScale.y = vSize.y / 2.0
IF (vPos.x <= (GGSM_PX_OVERSCAN_MIN_X + halfScale.x)) OR (vPos.x >= (GGSM_PX_OVERSCAN_MAX_X - halfScale.x))
RETURN FALSE
ENDIF
IF (vPos.y <= (GGSM_PX_OVERSCAN_MIN_Y + halfScale.y)) OR (vPos.y >= (GGSM_PX_OVERSCAN_MAX_Y - halfScale.y))
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
//-------------------------------------------------
// SPLINE FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// 2D Catmull Rom Spline - Will give an interpolation between p1 and p2
/// PARAMS:
/// p0 - point 0
/// p1 - point 1
/// p2 - point 2
/// p3 - point 3
/// t - interpolate value
/// RETURNS:
/// Interpolated Point
FUNC VECTOR_2D GMMR_CALCULATE_SPLINE_POINT(VECTOR_2D& p0, VECTOR_2D& p1, VECTOR_2D& p2, VECTOR_2D& p3, FLOAT t)
VECTOR_2D ret
FLOAT t2 = t * t
FLOAT t3 = t2 * t
ret.x = 0.5 * ((2.0 * p1.x) +
(-p0.x + p2.x) * t +
(2.0 * p0.x - 5.0 * p1.x + 4 * p2.x - p3.x) * t2 +
(-p0.x + 3.0 * p1.x - 3.0 * p2.x + p3.x) * t3)
ret.y = 0.5 * ((2.0 * p1.y) +
(-p0.y + p2.y) * t +
(2.0 * p0.y - 5.0 * p1.y + 4 * p2.y - p3.y) * t2 +
(-p0.y + 3.0 * p1.y - 3.0 * p2.y + p3.y) * t3)
RETURN ret
ENDFUNC
/// PURPOSE:
/// Gets the point on a spline
/// PARAMS:
/// vPoints - array of points on the spline (this will break if there are less than 2)
/// iTotalPointsInSpline - # of points in the spline
/// t - interpolate value (ranges from 0 to iTotalPoints
/// RETURNS:
///
FUNC VECTOR_2D GGMR_GET_SPLINE_POINT(VECTOR_2D &vPoints[], INT iTotalPointsInSpline, FLOAT t)
VECTOR_2D v[4]
INT ti = FLOOR(t)
INT ind, i
IF (iTotalPointsInSpline < 2)
RETURN 0
ENDIF
// get the points - such that v1 and v2 are the ones we need
REPEAT 4 i
ind = CLAMP_INT(ti - 1, 0, iTotalPointsInSpline - 1)
v[i] = vPoints[ind]
i ++
ti ++
ENDREPEAT
// get the section we need
ti = FLOOR(t)
t -= TO_FLOAT(ti)
RETURN GMMR_CALCULATE_SPLINE_POINT(v[0], v[1], v[2], v[3], t)
ENDFUNC
/// PURPOSE:
/// Get Spline Segment Length
/// PARAMS:
/// vPoints - Array of Points
/// iTotalPointsInSpline - Total # of Spline Points
/// iNode - Node To Check
/// fStep -
/// RETURNS:
///
FUNC FLOAT GGMR_GET_SPLINE_SEGMENT_LENGTH(VECTOR_2D &vPoints[], INT iTotalPointsInSpline, INT iNode, FLOAT fStep = 0.005)
FLOAT t
FLOAT len = 0.0
VECTOR_2D newPoint
VECTOR_2D oldPoint = GGMR_GET_SPLINE_POINT(vPoints, iTotalPointsInSpline, TO_FLOAT(iNode))
WHILE (t < 1.0)
newPoint = GGMR_GET_SPLINE_POINT(vPoints, iTotalPointsInSpline, TO_FLOAT(iNode) + t)
len += VECTOR_2D_DIST(newPoint, oldPoint)
oldPoint = newPoint
t += fStep
ENDWHILE
RETURN len
ENDFUNC
//-------------------------------------------------
// COLLISION DATA FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Sets Collsion Data as a circle
/// PARAMS:
/// data - data
/// vCenter - center circle
/// fRadius - radius
PROC GGSM_COLLISION_DATA_SET_AS_CIRCLE(GGSM_COLLISION_DATA &data, VECTOR_2D vCenter, FLOAT fRadius)
data.eType = GGSM_COLLISION_CIRCLE
data.vCollisionVectors[0] = vCenter
data.vCollisionVectors[1] = vCenter
data.fColRadius = fRadius
ENDPROC
/// PURPOSE:
/// Set Collision Data as a capsule
/// PARAMS:
/// data - data
/// v1 - point 1 (this generally should be the leading point)
/// v2 - point 2
/// fRadius - radius
PROC GGSM_COLLISION_DATA_SET_AS_CAPSULE(GGSM_COLLISION_DATA &data, VECTOR_2D v1, VECTOR_2D v2, FLOAT fRadius)
data.eType = GGSM_COLLISION_CAPSULE
data.vCollisionVectors[0] = v1
data.vCollisionVectors[1] = v2
data.fColRadius = fRadius
ENDPROC
//-------------------------------------------------
// COLLIDER DATA FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Generate AABB from Collision Data -
/// PARAMS:
/// d1 -
PROC GGSM_COLLIDER_CALCULATE_AABB(GGSM_COLLIDER &d1)
VECTOR_2D vMin, vMax
IF (d1.sData.eType = GGSM_COLLISION_CIRCLE)
d1.sAABB.vCenter = d1.sData.vCollisionVectors[0]
d1.sAABB.vHalfSize.x = d1.sData.fColRadius
d1.sAABB.vHalfSize.y = d1.sData.fColRadius
EXIT
ENDIF
IF (d1.sData.eType = GGSM_COLLISION_CAPSULE)
// get min and max
vMin.x = FMIN(d1.sData.vCollisionVectors[0].x, d1.sData.vCollisionVectors[1].x) - d1.sData.fColRadius
vMin.y = FMIN(d1.sData.vCollisionVectors[0].y, d1.sData.vCollisionVectors[1].y) - d1.sData.fColRadius
vMax.x = FMAX(d1.sData.vCollisionVectors[0].x, d1.sData.vCollisionVectors[1].x) + d1.sData.fColRadius
vMax.y = FMAX(d1.sData.vCollisionVectors[0].y, d1.sData.vCollisionVectors[1].y) + d1.sData.fColRadius
// calculate half widths
d1.sAABB.vHalfSize.x = (vMax.x - vMin.x) / 2.0
d1.sAABB.vHalfSize.y = (vMax.y - vMin.y) / 2.0
// calculate center
d1.sAABB.vCenter.x = vMin.x + d1.sAABB.vHalfSize.x
d1.sAABB.vCenter.y = vMin.y + d1.sAABB.vHalfSize.y
ENDIF
ENDPROC
/// PURPOSE:
/// Transforms a collider
/// PARAMS:
/// col -
/// vPos - translation
/// fRot - rotation
/// baseData - base collider data
PROC GGSM_COLLIDER_TRANSFORM(GGSM_COLLIDER &col, GGSM_TRANSFORM &trans, GGSM_COLLISION_DATA &baseData)
col.sData = baseData
col.sData.fColRadius *= ABSF(GGSM_GET_DOMINANT_AXIS(trans.vScale))
IF (col.sData.eType = GGSM_COLLISION_CIRCLE)
col.sData.vCollisionVectors[0] = GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(trans, baseData.vCollisionVectors[0])
ELSE
col.sData.vCollisionVectors[0] = GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(trans, baseData.vCollisionVectors[0])
col.sData.vCollisionVectors[1] = GGSM_TRANSFORM_LOCAL_POS_TO_WORLD_POS(trans, baseData.vCollisionVectors[1])
ENDIF
GGSM_COLLIDER_CALCULATE_AABB(col)
ENDPROC
/// PURPOSE:
/// Determines if two circles have collided
/// Note - Only use this with transformed collision data
/// PARAMS:
/// d1 - Collision Data 1
/// d2 - Collision Data 2
/// RETURNS:
/// TRUE if collision occurs
FUNC BOOL GGSM_COLLIDER_CIRCLE_CIRCLE_INTERSECT(GGSM_COLLIDER &d1, GGSM_COLLIDER &d2)
FLOAT fRad
VECTOR_2D vec
// get distance between both circle centers squared
fRad = (d1.sData.fColRadius + d2.sData.fColRadius) * (d1.sData.fColRadius + d2.sData.fColRadius)
// get the vector between the 2 centers
vec.x = d2.sData.vCollisionVectors[0].x - d1.sData.vCollisionVectors[0].x
vec.y = d2.sData.vCollisionVectors[0].y - d1.sData.vCollisionVectors[0].y
// do a distance check
RETURN ((vec.x * vec.x) + (vec.y * vec.y)) < fRad
ENDFUNC
/// PURPOSE:
/// Determines if two rects have collided
/// Note - Only use this with transformed collision data
/// PARAMS:
/// d1 - Collision Data 1
/// d2 - Collision Data 2
/// RETURNS:
/// TRUE if collision occurs
FUNC BOOL GGSM_COLLIDER_AABB_AABB_INTERSECT(GGSM_COLLIDER &d1, GGSM_COLLIDER &d2)
IF (ABSF(d1.sAABB.vCenter.x - d2.sAABB.vCenter.x) > (d1.sAABB.vHalfSize.x + d2.sAABB.vHalfSize.x))
RETURN FALSE
ENDIF
IF (ABSF(d1.sAABB.vCenter.y - d2.sAABB.vCenter.y) > (d1.sAABB.vHalfSize.y + d2.sAABB.vHalfSize.y))
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Tells us if a sprite is within the draw area
/// PARAMS:
/// vPos - Sprite Pos
/// vSize - Sprite Size
/// RETURNS:
/// TRUE if it is on screen
FUNC BOOL GGSM_IS_COLLIDER_AABB_ON_SCREEN(GGSM_COLLIDER &d1)
IF (d1.sAABB.vCenter.x < (GGSM_PX_OVERSCAN_MIN_X - d1.sAABB.vHalfSize.x)) OR (d1.sAABB.vCenter.x > (GGSM_PX_OVERSCAN_MAX_X + d1.sAABB.vHalfSize.x))
RETURN FALSE
ENDIF
IF (d1.sAABB.vCenter.y < (GGSM_PX_OVERSCAN_MIN_Y - d1.sAABB.vHalfSize.y)) OR (d1.sAABB.vCenter.y > (GGSM_PX_OVERSCAN_MAX_Y + d1.sAABB.vHalfSize.y))
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Returns true is capsule and circle intersect
/// PARAMS:
/// ca1 -
/// c2 -
/// RETURNS:
///
FUNC BOOL GGSM_COLLIDER_CAPSULE_CIRCLE_INTERSECT(GGSM_COLLIDER &ca1, GGSM_COLLIDER &c2)
FLOAT r = c2.sData.fColRadius + ca1.sData.fColRadius
VECTOR v = GET_CLOSEST_POINT_ON_LINE(<<c2.sData.vCollisionVectors[0].x, c2.sData.vCollisionVectors[0].y, 0>>, <<ca1.sData.vCollisionVectors[0].x, ca1.sData.vCollisionVectors[0].y, 0>>, <<ca1.sData.vCollisionVectors[1].x, ca1.sData.vCollisionVectors[1].y, 0>>, TRUE)
RETURN VECTOR_2D_DIST2(INIT_VECTOR_2D(v.x, v.y), c2.sData.vCollisionVectors[0]) <= (r * r)
ENDFUNC
/// PURPOSE:
/// Determines if two rects have collided
/// Note - Only use this with transformed collision data
/// PARAMS:
/// d1 - Collision Data 1
/// d2 - Collision Data 2
/// RETURNS:
/// TRUE if collision occurs
FUNC BOOL GGSM_COLLIDER_CAPSULE_CAPSULE_INTERSECT(GGSM_COLLIDER &d1, GGSM_COLLIDER &d2)
FLOAT distSQ = GGSM_SEGMENT_DISTANCE_SQUARED(d1.sData.vCollisionVectors[0], d1.sData.vCollisionVectors[1], d2.sData.vCollisionVectors[0], d2.sData.vCollisionVectors[1])
IF (distSQ < GGSM_EPSILON)
RETURN TRUE
ENDIF
FLOAT rsq = (d1.sData.fColRadius + d2.sData.fColRadius)
RETURN (distSQ <= (rsq * rsq))
ENDFUNC
/// PURPOSE:
/// Determines if two collision datas have collided
/// Note - Only use this with transformed collision data
/// PARAMS:
/// d1 - Collision Data 1
/// d2 - Collision Data 2
/// RETURNS:
/// TRUE if collision occurs
FUNC BOOL GGSM_COLLIDER_INTERSECT(GGSM_COLLIDER &d1, GGSM_COLLIDER &d2)
IF NOT GGSM_COLLIDER_AABB_AABB_INTERSECT(d1, d2)
RETURN FALSE
ENDIF
// d1 is a circle
IF (d1.sData.eType = GGSM_COLLISION_CIRCLE)
IF (d2.sData.eType = GGSM_COLLISION_CIRCLE)
RETURN GGSM_COLLIDER_CIRCLE_CIRCLE_INTERSECT(d1, d2)
ELIF (d2.sData.eType = GGSM_COLLISION_CAPSULE)
RETURN GGSM_COLLIDER_CAPSULE_CIRCLE_INTERSECT(d2, d1)
ENDIF
RETURN FALSE
ENDIF
// d1 is a capsule
IF (d1.sData.eType = GGSM_COLLISION_CAPSULE)
IF (d2.sData.eType = GGSM_COLLISION_CIRCLE)
RETURN GGSM_COLLIDER_CAPSULE_CIRCLE_INTERSECT(d1, d2)
ELIF (d2.sData.eType = GGSM_COLLISION_CAPSULE)
RETURN GGSM_COLLIDER_CAPSULE_CAPSULE_INTERSECT(d1, d2)
ENDIF
RETURN FALSE
ENDIF
RETURN FALSE
ENDFUNC
//-------------------------------------------------
// TILE COLLISION DATA FUNCTIONS
//-------------------------------------------------
/// PURPOSE:
/// Wrapper function as at a some point I'll need to pack these
/// PARAMS:
/// data -
/// i -
/// fx -
/// fy -
PROC GGSM_TILE_COLLISION_DATA_SET_COLLISION_VECTOR(GGSM_TILE_COLLISION_DATA &data, INT i, FLOAT fx, FLOAT fy)
data.vCollisionVectors[i].x = fx
data.vCollisionVectors[i].y = fy
ENDPROC
/// PURPOSE:
/// Wrapper function as at a some point I'll need to pack these
/// PARAMS:
/// data -
/// i -
/// RETURNS:
///
FUNC VECTOR_2D GGSM_TILE_COLLISION_DATA_GET_COLLISION_VECTOR(GGSM_TILE_COLLISION_DATA &data, INT i)
RETURN data.vCollisionVectors[i]
ENDFUNC
/// PURPOSE:
///
/// PARAMS:
/// data -
/// vCenter - This center is relative to the top left corner of the tile
/// fRadius - Radius
PROC GGSM_TILE_COLLISION_DATA_SET_AS_BOX(GGSM_TILE_COLLISION_DATA &data, VECTOR_2D vCenter, VECTOR_2D vSize)
data.vCollisionVectors[0] = vCenter
data.vCollisionVectors[1].x = vSize.x / 2.0
data.vCollisionVectors[1].y = vSize.y / 2.0
data.eType = GGSM_TILE_COLLISION_BOX
data.vCollisionVectors[2].x = vCenter.x - data.vCollisionVectors[1].x
data.vCollisionVectors[2].y = vCenter.y - data.vCollisionVectors[1].y
data.vCollisionVectors[3].x = vCenter.x + data.vCollisionVectors[1].x
data.vCollisionVectors[3].y = vCenter.y + data.vCollisionVectors[1].y
data.iNumPoints = 1
ENDPROC
/// PURPOSE:
///
/// PARAMS:
/// data -
/// vMin - Min Corner Of Box
/// vMax - Max Corner of Box
PROC GGSM_TILE_COLLISION_DATA_SET_AS_BOX_MIN_MAX(GGSM_TILE_COLLISION_DATA &data, VECTOR_2D vMin, VECTOR_2D vMax)
VECTOR_2D vSize = INIT_VECTOR_2D((vMax.x - vMin.x) / 2.0, (vMax.y - vMin.y) / 2.0)
data.eType = GGSM_TILE_COLLISION_BOX
data.vCollisionVectors[0] = vMin
data.vCollisionVectors[0].x += vSize.x
data.vCollisionVectors[0].y += vSize.y
data.vCollisionVectors[1] = vSize
data.vCollisionVectors[2] = vMin
data.vCollisionVectors[3] = vMax
data.iNumPoints = 1
ENDPROC
/// PURPOSE:
/// Wrapper to Set Tile Collision as Line
/// PARAMS:
/// data -
/// vPoint1 - Position is relative to the top left corner of tile
/// vPoint2 - Position is relative to the top left corner of tile
PROC GGSM_TILE_COLLISION_DATA_SET_AS_LINE(GGSM_TILE_COLLISION_DATA &data, VECTOR_2D vPoint1, VECTOR_2D vPoint2)
data.eType = GGSM_TILE_COLLISION_LINE
data.vCollisionVectors[0] = vPoint1
data.vCollisionVectors[1] = vPoint2
data.iNumPoints = 2
ENDPROC
/// PURPOSE:
/// Wrapper to Set Tile Collision as Triangle
/// PARAMS:
/// data -
/// vPoint1 - Position is relative to the top left corner of tile
/// vPoint2 - Position is relative to the top left corner of tile
/// vPoint3 - Position is relative to the top left corner of tile
PROC GGSM_TILE_COLLISION_DATA_SET_AS_TRIANGLE(GGSM_TILE_COLLISION_DATA &data, VECTOR_2D vPoint1, VECTOR_2D vPoint2, VECTOR_2D vPoint3)
data.eType = GGSM_TILE_COLLISION_POLYGON
data.vCollisionVectors[0] = vPoint1
data.vCollisionVectors[1] = vPoint2
data.vCollisionVectors[2] = vPoint3
data.iNumPoints = 3
ENDPROC
/// PURPOSE:
/// Wrapper to Set Tile Collision as Polygon
/// PARAMS:
/// data -
/// vPoints - Position is relative to the top left corner of tile
/// iNumPoints - Num of Points
PROC GGSM_TILE_COLLISION_DATA_SET_AS_POLYGON(GGSM_TILE_COLLISION_DATA &data, VECTOR_2D &vPoints[], INT iNumPoints)
IF (iNumPoints > GGSM_MAX_TILE_POLYPOINTS)
SCRIPT_ASSERT("TOO MANY POINTS MAX IS 6")
EXIT
ENDIF
INT i
REPEAT iNumPoints i
data.vCollisionVectors[i] = vPoints[i]
ENDREPEAT
data.eType = GGSM_TILE_COLLISION_POLYGON
data.iNumPoints = iNumPoints
ENDPROC
/// PURPOSE:
/// Flips Tile Collision and
/// PARAMS:
/// tm -
/// iTileIndex -
/// dest -
/// bHFlip -
/// bVFlip -
PROC GGSM_TILEMAP_FLIP_COLLISION(GGSM_TILEMAP &tm, INT iTileIndex, GGSM_TILE_COLLISION_DATA &dest, BOOL bHFlip, BOOL bVFlip)
INT i
dest = tm.sCollisionData[iTileIndex]
REPEAT dest.iNumPoints i
IF (bHFlip)
dest.vCollisionVectors[i].x = tm.fTileSize - dest.vCollisionVectors[i].x
ENDIF
IF (bVFlip)
dest.vCollisionVectors[i].y = tm.fTileSize - dest.vCollisionVectors[i].y
ENDIF
ENDREPEAT
ENDPROC
FUNC BOOL GGSM_TILE_COLLISION_IN_POINT_IN_TILE(VECTOR_2D vTestPoint, VECTOR_2D vTileTopLeft, GGSM_TILE_COLLISION_DATA &data)
VECTOR_2D vCenter
SWITCH (data.eType)
CASE GGSM_TILE_COLLISION_BOX
vCenter = ADD_VECTOR_2D(vTileTopLeft, data.vCollisionVectors[0])
IF (ABSF(vCenter.x - vTestPoint.x) > data.vCollisionVectors[1].x)
RETURN FALSE
ENDIF
IF (ABSF(vCenter.y - vTestPoint.y) > data.vCollisionVectors[1].y)
RETURN FALSE
ENDIF
RETURN TRUE
BREAK
CASE GGSM_TILE_COLLISION_POLYGON
vTestPoint = SUBTRACT_VECTOR_2D(vTestPoint, vTileTopLeft) // move test point in tile space
RETURN GGSM_IS_POINT_IN_POLYGON(vTestPoint, data.vCollisionVectors, data.iNumPoints)
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
/*
/// PURPOSE:
/// Calculate which tile a particular position in screen coords is over
/// PARAMS:
/// cam - camera struct
/// vScreenPos - screen pos
/// tx - tile x
/// ty - tile y
PROC GGSM_CAMERA_GET_SCREEN_POSITION_IN_TILE_MAP(GGSM_CAMERA &cam, GGSM_TILEMAP &tm, VECTOR_2D vScreenPos, INT &tx, INT &ty)
tx = FLOOR((vScreenPos.x + cam.vWorldPosition.x) / tm.fTileSize)
ty = FLOOR((vScreenPos.y + cam.vWorldPosition.y) / tm.fTileSize)
ENDPROC
/// PURPOSE:
/// Calculate which tile a particular position in screen coords is over
/// PARAMS:
/// cam - camera struct
/// vScreenPos - screen pos
/// tx - tile x
/// ty - tile y
PROC GGSM_WORLD_POSITION_IN_TILE_MAP(GGSM_TILEMAP &tm, VECTOR_2D vPos, INT &tx, INT &ty)
tx = FLOOR(vPos.x / tm.fTileSize)
ty = FLOOR(vPos.y / tm.fTileSize)
ENDPROC
/// PURPOSE:
/// Give a tilemap coord give us the top left pos in world sapce
/// PARAMS:
/// tm - tilemap
/// iMx - map coord x
/// iMy - map coord y
/// RETURNS:
/// World Position of Top Left Corner of tile
FUNC VECTOR_2D GGSM_TILEMAP_GET_TOP_LEFT_WORLD_POSITION_FOR_TILE(GGSM_TILEMAP &tm, INT iMx, INT iMy)
VECTOR_2D vec
vec.x = iMx * tm.fTileSize
vec.y = iMy * tm.fTileSize
RETURN vec
ENDFUNC
*/