source: trunk/VUT/GtpVisibilityPreprocessor/src/Polygon3.cpp @ 314

Revision 314, 7.7 KB checked in by mattausch, 19 years ago (diff)

added ray intersection for polygons

Line 
1#include "Polygon3.h"
2#include "Mesh.h"
3#include "ViewCellBsp.h" // TODO: erase this
4#include "Mesh.h"
5#include "AxisAlignedBox3.h"
6#include "Ray.h"
7
8Polygon3::Polygon3(): mMaterial(NULL), mParent(NULL)
9{}
10
11Polygon3::Polygon3(const VertexContainer &vertices): mVertices(vertices), mMaterial(NULL), mParent(NULL)
12{}
13
14Polygon3::Polygon3(MeshInstance *parent): mMaterial(NULL), mParent(parent)
15{}
16
17
18Polygon3::Polygon3(Face *face, Mesh *parentMesh)
19{       
20        VertexIndexContainer::iterator it = face->mVertexIndices.begin();
21        for (; it != face->mVertexIndices.end();  ++it)
22        {
23                mVertices.push_back(parentMesh->mVertices[*it]);
24                mMaterial = parentMesh->mMaterial;
25        }
26}
27
28Plane3 Polygon3::GetSupportingPlane() const
29{
30        Vector3 v1 = mVertices[0] - mVertices[1];
31        Vector3 v2 = mVertices[2] - mVertices[1];
32#ifdef _DEBUG
33        Debug << "plane spanned by " <<  v1 << ", " << v2  << endl;
34#endif
35        return Plane3(mVertices[0], mVertices[1], mVertices[2]);
36}
37
38Vector3 Polygon3::GetNormal() const
39{
40    return Normalize(CrossProd(mVertices[0] - mVertices[1],
41                                                           mVertices[2] - mVertices[1]));
42}
43
44void Polygon3::Split(const Plane3 &partition,
45                                         Polygon3 &front,
46                                         Polygon3 &back,
47                                         VertexContainer &splitPts)
48{
49        Vector3 ptA = mVertices.back();
50       
51        int sideA = partition.Side(ptA, SIDE_TOLERANCE);
52       
53        VertexContainer::const_iterator it;
54       
55        // find line - plane intersections
56        for (it = mVertices.begin(); it != mVertices.end(); ++ it)
57        {
58                Vector3 ptB = *it;
59                int sideB = partition.Side(ptB, SIDE_TOLERANCE);
60       
61                // vertices on different sides => split
62            if (sideB > 0)
63                {
64                        if (sideA < 0)
65                        {
66                                //-- plane - line intersection
67                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
68                       
69                                // test if split point not too close to previous split point
70                                if ((splitPts.size() == 0) ||
71                                        (SqrDistance(splitPt, splitPts.back()) > SIDE_TOLERANCE_SQRD))
72                                {
73                                        // add vertex to both polygons
74                                        front.mVertices.push_back(splitPt);
75                                        back.mVertices.push_back(splitPt);
76                                       
77                                        splitPts.push_back(splitPt);
78                                }
79                        }
80                        front.mVertices.push_back(ptB);
81                }
82                else if (sideB < 0)
83                {
84                        if (sideA > 0)
85                        {
86                                //-- plane - line intersection
87                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
88                                // test if split point not too close to other split point
89                                        // test if split point not too close to previous split point
90                                if ((splitPts.size() == 0) ||
91                                        (SqrDistance(splitPt, splitPts.back()) > SIDE_TOLERANCE_SQRD))
92                                {
93                                        // add vertex to both polygons
94                                        front.mVertices.push_back(splitPt);
95                                        back.mVertices.push_back(splitPt);
96
97                                        splitPts.push_back(splitPt);
98                                }       
99                        }
100                        back.mVertices.push_back(ptB);
101                }
102                else
103                {
104                        // vertex on plane => add vertex to both polygons
105                        front.mVertices.push_back(ptB);
106                        back.mVertices.push_back(ptB);
107                }
108       
109                ptA = ptB;
110                sideA = sideB;
111        }
112}
113
114float Polygon3::GetArea() const
115{
116        Vector3 v = CrossProd(mVertices.back(), mVertices.front());
117   
118    for (int i=0; i < mVertices.size() - 1; ++i)
119                v += CrossProd(mVertices[i], mVertices[i+1]);
120   
121    //Debug << "area2: " << 0.5f * fabs(DotProd(GetNormal(), v)) << endl; 
122
123        return 0.5f * fabs(DotProd(GetNormal(), v));
124}
125
126int Polygon3::Side(const Plane3 &plane) const
127{
128        int classification = ClassifyPlane(plane);
129       
130        if (classification == BACK_SIDE)
131                return -1;
132        else if (classification == FRONT_SIDE)
133                return 1;
134
135        return 0;
136}
137
138int Polygon3::ClassifyPlane(const Plane3 &plane) const
139{
140        VertexContainer::const_iterator it;
141
142        bool onFrontSide = false;
143        bool onBackSide = false;
144       
145        int count = 0;
146        // find possible line-plane intersections
147        for (it = mVertices.begin(); it != mVertices.end(); ++ it)
148        {
149                int side = plane.Side(*it, SIDE_TOLERANCE);
150               
151                if (side > 0)
152                        onFrontSide = true;
153                else if (side < 0)
154                        onBackSide = true;
155               
156                //TODO: check if split goes through vertex
157                if (onFrontSide && onBackSide) // split
158                {
159                        return SPLIT;
160                }
161                // 3 vertices enough to decide coincident
162                else if (((++ count) >= 3) && !onFrontSide && !onBackSide)
163                        return COINCIDENT;
164        }
165
166        if (onBackSide)
167        {
168                return BACK_SIDE;
169        }
170        else if (onFrontSide)
171        {
172                return FRONT_SIDE;
173        }
174
175        return COINCIDENT; // plane and polygon are coincident
176}
177
178
179Vector3
180Polygon3::Center() const
181{
182        int i;
183        Vector3 sum = mVertices[0];
184        for (i=1; i < mVertices.size(); i++)
185                sum += mVertices[i];
186       
187        return sum/(float)i;
188}
189
190
191void
192Polygon3::Scale(const float scale)
193{
194        int i;
195        Vector3 center = Center();
196        for (i=0; i < mVertices.size(); i++) {
197                mVertices[i] = center + scale*(mVertices[i] - center);
198        }
199}
200
201bool Polygon3::Valid() const
202{
203        if (mVertices.size() < 3)
204                return false;
205
206#if 1
207        // check if area exceeds certain size
208        if (AREA_LIMIT > GetArea())
209        {
210                //Debug << "area too small: " << GetArea() << endl;
211                return false;
212        }
213#else
214        Vector3 vtx = mVertices.back();
215        VertexContainer::const_iterator it, it_end = mVertices.end();
216
217        for (it = mVertices.begin(); it != it_end; ++it)
218        {
219                if (!(SqrDistance(vtx, *it) > SIDE_TOLERANCE_SQRD))
220                {
221                        Debug << "Malformed vertices:\n" << *this << endl;
222                        return false;
223                }
224                vtx = *it;
225        }
226#endif 
227        return true;
228}
229
230void Polygon3::IncludeInBox(const PolygonContainer &polys, AxisAlignedBox3 &box)
231{
232        PolygonContainer::const_iterator it, it_end = polys.end();
233
234        for (it = polys.begin(); it != it_end; ++ it)
235                box.Include(*(*it));
236}
237
238// int_lineseg returns 1 if the given line segment intersects a 2D
239// ray travelling in the positive X direction.  This is used in the
240// Jordan curve computation for polygon intersection.
241inline int
242int_lineseg(float px,
243            float py,
244            float u1,
245            float v1,
246            float u2,
247            float v2)
248{
249        float t;
250        float ydiff;
251
252        u1 -= px; u2 -= px;       // translate line
253        v1 -= py; v2 -= py;
254
255        if ((v1 > 0 && v2 > 0) ||
256                (v1 < 0 && v2 < 0) ||
257                (u1 < 0 && u2 < 0))
258        return 0;
259
260        if (u1 > 0 && u2 > 0)
261        return 1;
262
263        ydiff = v2 - v1;
264
265        if (fabs(ydiff) < Limits::Small)
266        {         // denominator near 0
267                if (((fabs(v1) > Limits::Small) || (u1 > 0) || (u2 > 0)))
268                        return 0;
269                return 1;
270        }
271
272        t = -v1 / ydiff;                  // Compute parameter
273
274        return (u1 + t * (u2 - u1)) > 0;
275}
276
277int Polygon3::CastRay(const Ray &ray, float &t, const float nearestT)
278{
279        Plane3 plane = GetSupportingPlane();
280        float dot = DotProd(plane.mNormal, ray.GetDir());
281
282        // Watch for near-zero denominator
283        // ONLY single sided polygons!!!!!
284        if (dot > -Limits::Small)
285        //  if (fabs(dot) < Limits::Small)
286        return Ray::NO_INTERSECTION;
287
288        t = (-plane.mD - DotProd(plane.mNormal, ray.GetLoc())) / dot;
289
290        if (t <= Limits::Small)
291        return Ray::INTERSECTION_OUT_OF_LIMITS;
292
293        if (t >= nearestT) {
294        return Ray::INTERSECTION_OUT_OF_LIMITS; // no intersection was found
295        }
296
297        int count = 0;
298        float u, v, u1, v1, u2, v2;
299        int i;
300
301        int paxis = plane.mNormal.DrivingAxis();
302
303        // Project the intersection point onto the coordinate plane
304        // specified by which.
305        ray.Extrap(t).ExtractVerts(&u, &v, paxis);
306
307        int size = (int)mVertices.size();
308
309        mVertices.back().ExtractVerts(&u1, &v1, paxis );
310
311        if (0 && size <= 4)
312        {
313                // assume a convex face
314                for (i = 0; i < size; i++)
315                {
316                mVertices[i].ExtractVerts(&u2, &v2, paxis);
317           
318                        // line u1, v1, u2, v2
319           
320                        if ((v2 - v1)*(u1 - u) > (u2 - u1)*(v1 - v))
321                                return Ray::NO_INTERSECTION;
322
323                        u1 = u2;
324                        v1 = v2;
325        }
326
327        return Ray::INTERSECTION;
328        }
329
330        // We're stuck with the Jordan curve computation.  Count number
331        // of intersections between the line segments the polygon comprises
332        // with a ray originating at the point of intersection and
333        // travelling in the positive X direction.
334        for (i = 0; i < size; i++)
335        {
336                mVertices[i].ExtractVerts(&u2, &v2, paxis);
337
338                count += (int_lineseg(u, v, u1, v1, u2, v2) != 0);
339
340                u1 = u2;
341                v1 = v2;
342        }
343
344        // We hit polygon if number of intersections is odd.
345        return (count & 1) ? Ray::INTERSECTION : Ray::NO_INTERSECTION;
346}
347
Note: See TracBrowser for help on using the repository browser.