source: GTP/trunk/Lib/Vis/Preprocessing/src/Polygon3.cpp @ 1328

Revision 1328, 12.0 KB checked in by mattausch, 18 years ago (diff)
Line 
1#include "Polygon3.h"
2#include "Mesh.h"
3#include "AxisAlignedBox3.h"
4#include "Ray.h"
5#include "Triangle3.h"
6
7
8namespace GtpVisibilityPreprocessor {
9
10
11Polygon3::Polygon3():
12mMaterial(NULL), mParent(NULL), mPiercingRays(0)
13, mPlane(NULL)
14{
15        // mostly there will be triangles
16        //mVertices.reserve(3);
17}
18
19Polygon3::Polygon3(const VertexContainer &vertices):
20mMaterial(NULL), mParent(NULL), mPiercingRays(0)
21, mPlane(NULL)
22{
23        mVertices.reserve(vertices.size());
24        mVertices = vertices;
25}
26
27
28Polygon3::Polygon3(MeshInstance *parent):
29mMaterial(NULL), mParent(parent), mPiercingRays(0)
30, mPlane(NULL)
31{}
32
33
34Polygon3::Polygon3(Face *face, Mesh *parentMesh):
35mMaterial(NULL), mParent(NULL), mPiercingRays(0)
36, mPlane(NULL)
37{       
38        mVertices.reserve(face->mVertexIndices.size());
39       
40        VertexIndexContainer::iterator it = face->mVertexIndices.begin();
41        for (; it != face->mVertexIndices.end();  ++it)
42        {
43                mVertices.push_back(parentMesh->mVertices[*it]);
44        }
45
46        mMaterial = parentMesh->mMaterial;
47}
48
49
50Plane3 Polygon3::GetSupportingPlane()// const
51{
52#if 0
53        return Plane3(mVertices[0], mVertices[1], mVertices[2]);
54#else
55        if (!mPlane)
56                mPlane = new Plane3(mVertices[0], mVertices[1], mVertices[2]);
57        return *mPlane;
58#endif
59}
60
61
62Vector3 Polygon3::GetNormal() const
63{
64    return Normalize(CrossProd(mVertices[2] - mVertices[1],
65                                                           mVertices[0] - mVertices[1]));
66}
67
68void Polygon3::Split(const Plane3 &partition,
69                                         Polygon3 &front,
70                                         Polygon3 &back,
71                                         const float epsilon)
72{
73        Vector3 ptA = mVertices.back();
74       
75        int sideA = partition.Side(ptA, epsilon);
76       
77        VertexContainer::const_iterator it;
78       
79        Vector3 lastSplit;
80
81        bool foundSplit = false;
82
83        //-- find line - plane intersections
84        for (it = mVertices.begin(); it != mVertices.end(); ++ it)
85        {
86                Vector3 ptB = *it;
87                int sideB = partition.Side(ptB, epsilon);
88       
89                // vertices on different sides => split
90            if (sideB > 0)
91                {
92                        if (sideA < 0)
93                        {
94                                //-- plane - line intersection
95                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
96                       
97                                // test if split point not too close to previous split point
98                                if (!foundSplit || (!EpsilonEqualV3(splitPt, lastSplit, epsilon)))
99                                {
100                                        // add vertex to both polygons
101                                        front.mVertices.push_back(splitPt);
102                                        back.mVertices.push_back(splitPt);
103                                       
104                                        lastSplit = splitPt;
105                                        foundSplit = true;
106                                }
107                        }
108                        front.mVertices.push_back(ptB);
109                }
110                else if (sideB < 0)
111                {
112                        if (sideA > 0)
113                        {
114                                //-- plane - line intersection
115                                Vector3 splitPt = partition.FindIntersection(ptA, ptB);
116                                // test if split point not too close to other split point
117                                        // test if split point not too close to previous split point
118                                if (!foundSplit || (!EpsilonEqualV3(splitPt, lastSplit, epsilon)))
119                                {
120                                        // add vertex to both polygons
121                                        front.mVertices.push_back(splitPt);
122                                        back.mVertices.push_back(splitPt);
123
124                                        lastSplit = splitPt;
125                                        foundSplit = true;
126                                }       
127                        }
128                        back.mVertices.push_back(ptB);
129                }
130                else
131                {
132                        // vertex on plane => add vertex to both polygons
133                        front.mVertices.push_back(ptB);
134                        back.mVertices.push_back(ptB);
135                }
136       
137                ptA = ptB;
138                sideA = sideB;
139        }
140}
141
142
143float Polygon3::GetArea() const
144{
145        Vector3 v = CrossProd(mVertices.back(), mVertices.front());
146   
147    for (int i=0; i < mVertices.size() - 1; ++i)
148                v += CrossProd(mVertices[i], mVertices[i+1]);
149   
150    return 0.5f * fabs(DotProd(GetNormal(), v));
151}
152
153
154int Polygon3::Side(const Plane3 &plane,
155                                   const float epsilon) const
156{
157        int classification = ClassifyPlane(plane, epsilon);
158       
159        if (classification == BACK_SIDE)
160                return -1;
161        else if (classification == FRONT_SIDE)
162                return 1;
163        // plane splits polygon
164        return 0;
165}
166
167
168int Polygon3::ClassifyPlane(const Plane3 &plane,
169                                                        const float epsilon) const
170{
171        VertexContainer::const_iterator it, it_end = mVertices.end();
172
173        bool onFrontSide = false;
174        bool onBackSide = false;
175       
176        int count = 0;
177        // find possible line-plane intersections
178        for (it = mVertices.begin(); it != it_end; ++ it)
179        {
180                const int side = plane.Side(*it, epsilon);
181               
182                if (side > 0)
183                {
184                        onFrontSide = true;
185                }
186                else if (side < 0)
187                {
188                        onBackSide = true;
189                }
190
191                //TODO: check if split goes through vertex
192                if (onFrontSide && onBackSide) // split
193                {
194                        return SPLIT;
195                }
196                // 3 vertices enough to decide coincident
197                else if (((++ count) >= 3) && !onFrontSide && !onBackSide)
198                {   
199                        return COINCIDENT;
200                }
201        }
202
203        if (onBackSide)
204        {
205                return BACK_SIDE;
206        }
207        else if (onFrontSide)
208        {
209                return FRONT_SIDE;
210        }
211       
212        return COINCIDENT; // plane and polygon are coincident
213}
214
215
216Vector3
217Polygon3::Center() const
218{
219        int i;
220        Vector3 sum = mVertices[0];
221        for (i=1; i < mVertices.size(); i++)
222                sum += mVertices[i];
223       
224        return sum/(float)i;
225}
226
227
228void
229Polygon3::Scale(const float scale)
230{
231        int i;
232        Vector3 center = Center();
233        for (i=0; i < mVertices.size(); i++) {
234                mVertices[i] = center + scale*(mVertices[i] - center);
235        }
236}
237
238bool Polygon3::Valid(const float epsilon) const
239{
240        if (mVertices.size() < 3)
241                return false;
242
243        //TODO: remove for performance
244#if 0
245        if (1)
246        {
247                // check if area exceeds certain size
248                if (GetArea() < AREA_LIMIT)
249                {
250                        Debug << "area too small: " << GetArea() << endl;
251                        return false;
252                }
253        }
254       
255        if (1)
256        {
257                Vector3 vtx = mVertices.back();
258                VertexContainer::const_iterator it, it_end = mVertices.end();
259
260                for (it = mVertices.begin(); it != it_end; ++it)
261                {
262                        if (EpsilonEqualV3(vtx, *it, 0.0001))
263                        {
264                                //Debug << "Malformed vertices:\n" << *this << endl;
265                                return false;
266                        }
267                        vtx = *it;
268                }
269        }
270#endif
271
272        return true;
273}
274
275
276// int_lineseg returns 1 if the given line segment intersects a 2D
277// ray travelling in the positive X direction.  This is used in the
278// Jordan curve computation for polygon intersection.
279inline int
280int_lineseg(float px,
281            float py,
282            float u1,
283            float v1,
284            float u2,
285            float v2)
286{
287        float t;
288        float ydiff;
289
290        u1 -= px; u2 -= px;       // translate line
291        v1 -= py; v2 -= py;
292
293        if ((v1 > 0 && v2 > 0) ||
294                (v1 < 0 && v2 < 0) ||
295                (u1 < 0 && u2 < 0))
296        return 0;
297
298        if (u1 > 0 && u2 > 0)
299        return 1;
300
301        ydiff = v2 - v1;
302
303        if (fabs(ydiff) < Limits::Small)
304        {         // denominator near 0
305                if (((fabs(v1) > Limits::Small) || (u1 > 0) || (u2 > 0)))
306                        return 0;
307                return 1;
308        }
309
310        t = -v1 / ydiff;                  // Compute parameter
311
312        return (u1 + t * (u2 - u1)) > 0;
313}
314
315
316int Polygon3::CastRay(const Ray &ray, float &t, const float nearestT)
317{
318        const Plane3 plane = GetSupportingPlane();
319        const float dot = DotProd(plane.mNormal, ray.GetDir());
320
321        // Watch for near-zero denominator
322        // ONLY single sided polygons!!!!!
323        if (dot > -Limits::Small)
324        {
325                //  if (fabs(dot) < Limits::Small)
326                return Ray::NO_INTERSECTION;
327        }
328
329        t = (-plane.mD - DotProd(plane.mNormal, ray.GetLoc())) / dot;
330
331        if (t <= Limits::Small)
332        {
333                return Ray::INTERSECTION_OUT_OF_LIMITS;
334        }
335
336        if (t >= nearestT)
337        {
338                return Ray::INTERSECTION_OUT_OF_LIMITS; // no intersection was found
339        }
340
341        int count = 0;
342        float u, v, u1, v1, u2, v2;
343        int i;
344
345        const int paxis = plane.mNormal.DrivingAxis();
346
347        // Project the intersection point onto the coordinate plane
348        // specified by which.
349        ray.Extrap(t).ExtractVerts(&u, &v, paxis);
350
351        const int size = (int)mVertices.size();
352
353        mVertices.back().ExtractVerts(&u1, &v1, paxis );
354
355        if (0 && size <= 4)
356        {
357                // assume a convex face
358                for (i = 0; i < size; i++)
359                {
360                mVertices[i].ExtractVerts(&u2, &v2, paxis);
361           
362                        // line u1, v1, u2, v2
363                        if ((v2 - v1) * (u1 - u) > (u2 - u1)*(v1 - v))
364                                return Ray::NO_INTERSECTION;
365
366                        u1 = u2;
367                        v1 = v2;
368                }
369               
370                return Ray::INTERSECTION;
371        }
372
373        // We're stuck with the Jordan curve computation.  Count number
374        // of intersections between the line segments the polygon comprises
375        // with a ray originating at the point of intersection and
376        // travelling in the positive X direction.
377        for (i = 0; i < size; i++)
378        {
379                mVertices[i].ExtractVerts(&u2, &v2, paxis);
380
381                count += (int_lineseg(u, v, u1, v1, u2, v2) != 0);
382
383                u1 = u2;
384                v1 = v2;
385        }
386
387        // We hit polygon if number of intersections is odd.
388        return (count & 1) ? Ray::INTERSECTION : Ray::NO_INTERSECTION;
389}
390
391
392void Polygon3::InheritRays(Polygon3 &front_piece,
393                                               Polygon3 &back_piece) const
394{
395        if (mPiercingRays.empty())
396                return;
397
398        RayContainer::const_iterator it,
399                it_end = mPiercingRays.end();
400
401        for (it = mPiercingRays.begin(); it != it_end; ++ it)
402        {
403                switch((*it)->GetId())
404                {
405                case Ray::BACK:
406                        back_piece.mPiercingRays.push_back(*it);
407                        break;
408                case Ray::FRONT:
409                        front_piece.mPiercingRays.push_back(*it);
410                        break;
411                case Ray::FRONT_BACK:
412                        back_piece.mPiercingRays.push_back(*it);
413                        break;
414                case Ray::BACK_FRONT:
415                        front_piece.mPiercingRays.push_back(*it);
416                        break;
417                default:
418                        break;
419                }
420        }
421}
422
423
424int Polygon3::ClassifyPlane(const PolygonContainer &polys,
425                                                        const Plane3 &plane,
426                                                        const float epsilon)
427{
428        bool onFrontSide = false;
429        bool onBackSide = false;
430        PolygonContainer::const_iterator it;
431
432        // find intersections
433        for (it = polys.begin(); it != polys.end(); ++ it)
434        {
435        const int cf = (*it)->ClassifyPlane(plane, epsilon);
436               
437        if (cf == FRONT_SIDE)
438                        onFrontSide = true;
439                else if (cf == BACK_SIDE)
440                        onBackSide = true;
441               
442                if ((cf == SPLIT) || (cf == COINCIDENT) || (onFrontSide && onBackSide))
443                {
444                        return SPLIT;
445                }
446        }
447
448        if (onBackSide)
449        {
450                return BACK_SIDE;
451        }
452        else if (onFrontSide)
453        {
454                return FRONT_SIDE;
455        }
456       
457        return SPLIT;
458}
459
460
461int Polygon3::ParentObjectsSize(const PolygonContainer &polys)
462{
463        int count = 0;
464
465        PolygonContainer::const_iterator it, it_end = polys.end();
466
467        MeshInstance::NewMail();
468
469        for (it = polys.begin(); it != it_end; ++ it)
470        {
471                if ((*it)->mParent && !(*it)->mParent->Mailed())
472                {
473                        ++ count;
474                        (*it)->mParent->Mail();
475                }
476        }
477        return count;
478}
479
480
481float Polygon3::GetArea(const PolygonContainer &cell)
482{
483        float area = 0;
484        PolygonContainer::const_iterator pit;
485
486        for (pit = cell.begin(); pit != cell.end(); ++ pit)
487        {
488                area += (*pit)->GetArea();
489        }
490
491        return area;
492}
493
494
495Polygon3 *Polygon3::CreateReversePolygon() const
496{
497        Polygon3 *revPoly = new Polygon3();
498
499        VertexContainer::const_reverse_iterator rit,
500                        rit_end = mVertices.rend();
501
502        for(rit = mVertices.rbegin(); rit != rit_end; ++ rit)
503                revPoly->mVertices.push_back(*rit);
504
505        return revPoly;
506}
507
508
509void Polygon3::Triangulate(vector<Triangle3> &triangles) const
510{
511        int i = 1;
512        int j = 0;
513        int k = (int)mVertices.size() - 1;
514        int count = 0;
515
516        while (i < k)
517        {
518                triangles.push_back(Triangle3(mVertices[i], mVertices[j], mVertices[k]));
519
520                if ((count ++) % 2)
521                        j = i ++;
522                else
523                        j = k --;
524        }
525}
526
527
528void Polygon3::Triangulate(VertexIndexContainer &indices) const
529{
530        int i = 1;
531        int j = 0;
532        int k = (int)mVertices.size() - 1;
533
534        int count = 0;
535
536        while (i < k)
537        {
538                indices.push_back(k);
539                indices.push_back(j);
540                indices.push_back(i);
541
542                if ((count ++) % 2)
543                {
544                        j = i ++;
545                }
546                else
547                {
548                        j = k --;
549                }
550        }
551}
552
553
554void IncludePolyInMesh(const Polygon3 &poly, Mesh &mesh)
555{
556        const int n = (int)mesh.mVertices.size();
557
558    //-- add the vertices
559    VertexContainer::const_iterator vit, vit_end = poly.mVertices.end();
560        for (vit = poly.mVertices.begin(); vit != vit_end; ++ vit)
561        {
562                mesh.mVertices.push_back(*vit);
563        }
564
565        // one quad => no triangulation necessary
566        if (poly.mVertices.size() == 4)
567        {
568                mesh.AddFace(new Face(n, n + 1, n + 2, n + 3));
569                return;
570        }
571       
572        VertexIndexContainer indices;
573        poly.Triangulate(indices);
574
575        // add indices of triangle strip
576        for (int i = 0; i < (int)indices.size(); i += 3)
577        {
578                Face *face = new Face(n + indices[i],
579                                                          n + indices[i + 1],
580                                                          n + indices[i + 2]);
581                mesh.AddFace(face);
582        }
583}
584
585
586AxisAlignedBox3 Polygon3::GetBoundingBox() const
587{
588        VertexContainer::const_iterator vit, vit_end = mVertices.end();
589       
590        AxisAlignedBox3 box;
591        box.Initialize();
592
593        for (vit = mVertices.begin(); vit != vit_end; ++ vit)
594        {
595                box.Include(*vit);
596        }
597
598        return box;
599}
600
601
602}
Note: See TracBrowser for help on using the repository browser.