Raised This Month: $51 Target: $400
 12% 

[SNIPPET] Polygon Zones


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Deathknife
Senior Member
Join Date: Aug 2014
Old 01-08-2017 , 13:33   [SNIPPET] Polygon Zones
Reply With Quote #1

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(pointzone)) 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 xintyint;
    
    
//Intersections for base bottom and top(2)
    
float baseYbaseZ;
    
float baseY2baseZ2;
    
    
//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 0gZonePointsNum[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] == 1) {
            
currentpoint gZonePoints[zone][i];
            
nextpoint gZonePoints[zone][0];
        }else {
            
currentpoint gZonePoints[zone][i];
            
nextpoint gZonePoints[zone][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], xintyint);
        
        
//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], baseYbaseZ);
        
        
//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], baseY2baseZ2);
        
        
//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(
y2 && 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 0lIntNumk++) {
        for(
int l 1lIntNuml++) {
            if(
== k) continue;
            
int i lIntersected[k];
            
int j lIntersected[l];
            if(
== j) continue;
            
            
float currentpoint[2][3];
            
float nextpoint[2][3];
            
            if(
gZonePointsNum[zone] == 1) {
                
currentpoint[0] = gZonePoints[zone][i];
                
nextpoint[0] = gZonePoints[zone][0];
            }else {
                
currentpoint[0] = gZonePoints[zone][i];
                
nextpoint[0] = gZonePoints[zone][1];
            }
            
            if(
gZonePointsNum[zone] == 1) {
                
currentpoint[1] = gZonePoints[zone][j];
                
nextpoint[1] = gZonePoints[zone][0];
            }else {
                
currentpoint[1] = gZonePoints[zone][j];
                
nextpoint[1] = gZonePoints[zone][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 0lIntNumTk++) {
        for(
int l 1lIntNumTl++) {
            if(
== k) continue;
            
int i lIntersectedT[k];
            
int j lIntersectedT[l];
            if(
== j) continue;
            
            
float currentpoint[2][3];
            
float nextpoint[2][3];
            
            if(
gZonePointsNum[zone] == 1) {
                
currentpoint[0] = gZonePoints[zone][i];
                
nextpoint[0] = gZonePoints[zone][0];
            }else {
                
currentpoint[0] = gZonePoints[zone][i];
                
nextpoint[0] = gZonePoints[zone][1];
            }
            
            if(
gZonePointsNum[zone] == 1) {
                
currentpoint[1] = gZonePoints[zone][j];
                
nextpoint[1] = gZonePoints[zone][0];
            }else {
                
currentpoint[1] = gZonePoints[zone][j];
                
nextpoint[1] = gZonePoints[zone][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 <= || intersections == 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_xfloat p0_yfloat p1_xfloat p1_yfloat p2_xfloat p2_yfloat p3_xfloat p3_yfloat &i_xfloat &i_y) {
    
float s1_xs1_ys2_xs2_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 st;
    
= (-s1_y * (p0_x p2_x) + s1_x * (p0_y p2_y)) / (-s2_x s1_y s1_x s2_y);
    
= ( s2_x * (p0_y p2_y) - s2_y * (p0_x p2_x)) / (-s2_x s1_y s1_x s2_y);

    if (
>= && <= && >= && <= 1) {
        
// Collision detected
        
i_x p0_x + (s1_x);
        
i_y p0_y + (s1_y);
        return 
true;
    }

    return 
false// No collision

Simple function that will create zone from an ArrayList
PHP Code:
public void CreateZoneFromArray(ArrayList pointsfloat height) {
    
float greatdiff 0.0;
    
float tempMin[3];
    
float tempMax[3];
    
    
gZoneHeight[gZonesNum] = height;
    
gZonePointsNum[gZonesNum] = 0;
    
    for(
int i 0points.Lengthi++) {
        
gZonePoints[gZonesNum][i][0] = points.Get(i0);
        
gZonePoints[gZonesNum][i][1] = points.Get(i1);
        
gZonePoints[gZonesNum][i][2] = points.Get(i2);
        
gZonePointsNum[gZonesNum]++;
        
        
//Calculate min/max 
        
for(int j=3j++) {
            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 03y++) {
        
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.
Attached Files
File Type: sp Get Plugin or Get Source (poly-zones.sp - 205 views - 15.5 KB)
__________________

Last edited by Deathknife; 01-08-2017 at 17:57. Reason: Accidently attached the .smx instead of the .sp my bad
Deathknife is offline
shavit
AlliedModders Donor
Join Date: Dec 2011
Location: Israel
Old 01-09-2017 , 07:29   Re: [SNIPPET] Polygon Zones
Reply With Quote #2

pretty damn cool
is performance any good?
__________________
retired
shavit is offline
Deathknife
Senior Member
Join Date: Aug 2014
Old 01-09-2017 , 12:10   Re: [SNIPPET] Polygon Zones
Reply With Quote #3

Quote:
Originally Posted by shavit View Post
pretty damn cool
is performance any good?
Yeah it shouldn't have any notable performance impact. By checking if the point is in the "box" the polygon is, it helps to reduce usage greatly. It won't be doing too much checking.

You can also check if player moved from his last position before checking all the polygons again, this will reduce checking for AFK players.

To put this into perspective; Surf server, ~40people online, multiple polygons, checking 4 points(edges) of players and no notable decrease in performance or any notable increase in CPU usage. It should be perfectly fine.
__________________
Deathknife is offline
404UserNotFound
BANNED
Join Date: Dec 2011
Old 01-09-2017 , 16:05   Re: [SNIPPET] Polygon Zones
Reply With Quote #4

I always wondered if this was possible with that zones plugin that could only do boxes, and it could only handle one "custom" output (i.e. giving/removing godmode upon entry/leaving). I'd love a system that would do a normal box if you declare 4 points, or a polygon if you go above 4, and that would allow for multiple different custom effects to be coded in by other developers/server owners.
404UserNotFound is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 10:25.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode