//------------------------------------------------- // 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(<>, <>, <>, 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(<>, <>, <>, 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 */