| Deathknife |
01-08-2017 13:33 |
[SNIPPET] Polygon Zones
1 Attachment(s)
Basically what title says. Works perfectly on planar, still a bit buggy on non-planar though.
Uses ray casting method.
PHP Code:
#define MAXZONES 100 #define MAXPOINTS 100
//Zone Variables float gZonePoints[MAXZONES][MAXPOINTS][3]; //Store points int gZonePointsNum[MAXZONES]; //Store amount of points float gZoneHeight[MAXZONES]; //The height of the zone float gZoneMaxDistance[MAXZONES]; //Max distance - used to get ray outside of zone
//Helps in optimization as we can check if client is inside this 'box'. float gZoneMin[MAXZONES][3]; float gZoneMax[MAXZONES][3];
//Amount of zones int gZonesNum = 0;
public bool IsPointInZone(float point[3], int zone) { //Check if point is in the zone if(!IsOriginInBox(point, zone)) return false; //Get a ray outside of the polygon float ray[3]; ray = point; ray[1] += gZoneMaxDistance[zone] + 50.0; ray[2] = point[2]; //Store the x and y intersections of where the ray hits the line float xint, yint; //Intersections for base bottom and top(2) float baseY, baseZ; float baseY2, baseZ2; //Calculate equation for x + y float eq[2]; eq[0] = point[0] - ray[0]; eq[1] = point[2] - ray[2]; //This is for checking if the line intersected the base //The method is messy, came up with it myself, and might not work 100% of the time. //Should work though. //Bottom int lIntersected[64]; float fIntersect[64][3]; //Top int lIntersectedT[64]; float fIntersectT[64][3]; //Count amount of intersetcions int intersections = 0; //Count amount of intersection for BASE int lIntNum = 0; int lIntNumT = 0; //Get slope float lSlope = (ray[2] - point[2]) / (ray[1] - point[1]); float lEq = (lSlope & ray[0]) - ray[2]; lEq = -lEq; //Get second slope //float lSlope2 = (ray[1] - point[1]) / (ray[0] - point[0]); //float lEq2 = (lSlope2 * point[0]) - point[1]; //lEq2 = -lEq2; //Loop through every point of the zone for(int i = 0; i < gZonePointsNum[zone]; i++) { //Get current & next point float currentpoint[3]; float nextpoint[3]; //Check if its the last point, if it is, join it with the first if(gZonePointsNum[zone] == i + 1) { currentpoint = gZonePoints[zone][i]; nextpoint = gZonePoints[zone][0]; }else { currentpoint = gZonePoints[zone][i]; nextpoint = gZonePoints[zone][i + 1]; } //Check if the ray intersects the point //Ignore the height parameter as we will check against that later bool didinter = get_line_intersection(ray[0], ray[1], point[0], point[1], currentpoint[0], currentpoint[1], nextpoint[0], nextpoint[1], xint, yint); //Get intersections of the bottom bool baseInter = get_line_intersection(ray[1], ray[2], point[1], point[2], currentpoint[1], currentpoint[2], nextpoint[1], nextpoint[2], baseY, baseZ); //Get intersections of the top bool baseInter2 = get_line_intersection(ray[1], ray[2], point[1], point[2], currentpoint[1] + gZoneHeight[zone], currentpoint[2] + gZoneHeight[zone], nextpoint[1] + gZoneHeight[zone], nextpoint[2] + gZoneHeight[zone], baseY2, baseZ2); //If base intersected, store the line for later if(baseInter && lIntNum < sizeof(fIntersect)) { lIntersected[lIntNum] = i; fIntersect[lIntNum][1] = baseY; fIntersect[lIntNum][2] = baseZ; lIntNum++; } if(baseInter2 && lIntNumT < sizeof(fIntersectT)) { lIntersectedT[lIntNumT] = i; fIntersectT[lIntNumT][1] = baseY2; fIntersectT[lIntNum][2] = baseZ2; lIntNumT++; } //If ray intersected line, check against height if(didinter) { //Get the height of intersection //Get slope of line it hit float m1 = (nextpoint[2] - currentpoint[2]) / (nextpoint[0] - currentpoint[0]); //Equation y = mx + c | mx - y = -c float l1 = (m1 * currentpoint[0]) - currentpoint[2]; l1 = -l1; float y2 = (m1 * xint) + l1; //Get slope of ray float y = (lSlope * xint) + lEq; if(y > y2 && y < y2 + 128.0 + gZoneHeight[zone]) { //The ray intersected the line and is within the height intersections++; } } } //Now we check for base hitting //This method is weird, but works most of the time for(int k = 0; k < lIntNum; k++) { for(int l = k + 1; l < lIntNum; l++) { if(l == k) continue; int i = lIntersected[k]; int j = lIntersected[l]; if(i == j) continue; float currentpoint[2][3]; float nextpoint[2][3]; if(gZonePointsNum[zone] == i + 1) { currentpoint[0] = gZonePoints[zone][i]; nextpoint[0] = gZonePoints[zone][0]; }else { currentpoint[0] = gZonePoints[zone][i]; nextpoint[0] = gZonePoints[zone][i + 1]; } if(gZonePointsNum[zone] == j + 1) { currentpoint[1] = gZonePoints[zone][j]; nextpoint[1] = gZonePoints[zone][0]; }else { currentpoint[1] = gZonePoints[zone][j]; nextpoint[1] = gZonePoints[zone][j + 1]; } //Get equation of both lines then find slope of them float m1 = (nextpoint[0][1] - currentpoint[0][1]) / (nextpoint[0][0] - currentpoint[0][0]); float m2 = (nextpoint[1][1] - currentpoint[1][1]) / (nextpoint[1][0] - currentpoint[1][0]); float lEq1 = (m1 * currentpoint[0][0]) - currentpoint[0][1]; float lEq2 = (m2 * currentpoint[1][0]) - currentpoint[1][1]; lEq1 = -lEq1; lEq2 = -lEq2; //Get x point of intersection float xPoint1 = ((fIntersect[k][1] - lEq1) / m1); float xPoint2 = ((fIntersect[l][1] - lEq2 / m2)); if(xPoint1 > point[0] > xPoint2 || xPoint1 < point[0] < xPoint2) { intersections++; } } } for(int k = 0; k < lIntNumT; k++) { for(int l = k + 1; l < lIntNumT; l++) { if(l == k) continue; int i = lIntersectedT[k]; int j = lIntersectedT[l]; if(i == j) continue; float currentpoint[2][3]; float nextpoint[2][3]; if(gZonePointsNum[zone] == i + 1) { currentpoint[0] = gZonePoints[zone][i]; nextpoint[0] = gZonePoints[zone][0]; }else { currentpoint[0] = gZonePoints[zone][i]; nextpoint[0] = gZonePoints[zone][i + 1]; } if(gZonePointsNum[zone] == j + 1) { currentpoint[1] = gZonePoints[zone][j]; nextpoint[1] = gZonePoints[zone][0]; }else { currentpoint[1] = gZonePoints[zone][j]; nextpoint[1] = gZonePoints[zone][j + 1]; } //Get equation of both lines then find slope of them float m1 = (nextpoint[0][1] - currentpoint[0][1]) / (nextpoint[0][0] - currentpoint[0][0]); float m2 = (nextpoint[1][1] - currentpoint[1][1]) / (nextpoint[1][0] - currentpoint[1][0]); float lEq1 = (m1 * currentpoint[0][0]) - currentpoint[0][1]; float lEq2 = (m2 * currentpoint[1][0]) - currentpoint[1][1]; lEq1 = -lEq1; lEq2 = -lEq2; //Get x point of intersection float xPoint1 = ((fIntersectT[k][1] - lEq1) / m1); float xPoint2 = ((fIntersectT[l][1] - lEq2 / m2)); if(xPoint1 > point[0] > xPoint2 || xPoint1 < point[0] < xPoint2) { intersections++; } } } if(intersections <= 0 || intersections % 2 == 0) { return false; }else { return true; } }
//Stock that checks if point is inside zone's min and max stock bool IsOriginInBox(float origin[3], int zone) { if(origin[0] >= gZoneMin[zone][0] && origin[1] >= gZoneMin[zone][1] && origin[2] >= gZoneMin[zone][2] && origin[0] <= gZoneMax[zone][0] + gZoneHeight[zone] && origin[1] <= gZoneMax[zone][1] + gZoneHeight[zone] && origin[2] <= gZoneMax[zone][2] + gZoneHeight[zone]) { return true; } return false; }
//Stolen from stackoverflow bool get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, float p2_x, float p2_y, float p3_x, float p3_y, float &i_x, float &i_y) { float s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
float s, t; s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected i_x = p0_x + (t * s1_x); i_y = p0_y + (t * s1_y); return true; }
return false; // No collision }
Simple function that will create zone from an ArrayList
PHP Code:
public void CreateZoneFromArray(ArrayList points, float height) { float greatdiff = 0.0; float tempMin[3]; float tempMax[3]; gZoneHeight[gZonesNum] = height; gZonePointsNum[gZonesNum] = 0; for(int i = 0; i < points.Length; i++) { gZonePoints[gZonesNum][i][0] = points.Get(i, 0); gZonePoints[gZonesNum][i][1] = points.Get(i, 1); gZonePoints[gZonesNum][i][2] = points.Get(i, 2); gZonePointsNum[gZonesNum]++; //Calculate min/max for(int j=0 ; j < 3; j++) { if(tempMin[j] == 0.0 || tempMin[j] > gZonePoints[gZonesNum][i][j]) { tempMin[j] = gZonePoints[gZonesNum][i][j]; } if(tempMax[j] == 0.0 || tempMax[j] < gZonePoints[gZonesNum][i][j]) { tempMax[j] = gZonePoints[gZonesNum][i][j]; } } float diff = CalculateHorizontalDistance(gZonePoints[gZonesNum][0], gZonePoints[gZonesNum][i], false); if(diff > greatdiff) { greatdiff = diff; } } for(int y = 0; y < 3; y++) { gZoneMin[gZonesNum][y] = tempMin[y]; gZoneMax[gZonesNum][y] = tempMax[y]; }
gZoneMaxDistance[gZonesNum] = greatdiff; gZonesNum++; }
I have attached an example plugin in which you can create zones to test them out. Fairly simple.
sm_startzone -> shoot to make points -> sm_endzone
sm_deletelast -> removes the last point
Code is quite messy and not necessarily optimized the best, I did it few months ago.
Here's it in:
https://youtu.be/Ywisr7911TA
The non-planar one is still a bit glitchy. Planar one works perfectly.
|