1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
|
//-----------------------------------------------------------------------------
// BSPLIB MODULE: Polygon.cpp
//
// Copyright (c) 1996-1998 by Markus Hadwiger
// All Rights Reserved.
//-----------------------------------------------------------------------------
// bsplib headers
#include "BspObject.h"
#include "Polygon.h"
#include "BoundingBox.h"
#include "BSPNode.h"
#include "BspTool.h"
#include "Vertex.h"
BSPLIB_NAMESPACE_BEGIN
// construct a Polygon --------------------------------------------------------
//
Polygon::Polygon( BspObject *bobj, int pno, int fno, Polygon *next, int num )
{
baseobject = bobj;
polygonno = pno;
faceno = fno;
nextpolygon = next;
numpolygons = num;
numvertindxs = 0;
vertindxs = NULL;
vindxinsertpos = NULL;
}
// get vertexlist of object polygon belongs to --------------------------------
//
VertexChunk& Polygon::getVertexList()
{
CHECK_DEREFERENCING(
if ( baseobject == NULL )
Error();
);
return baseobject->getVertexList();
}
// get facelist of object polygon belongs to ----------------------------------
//
FaceChunk& Polygon::getFaceList()
{
CHECK_DEREFERENCING(
if ( baseobject == NULL )
Error();
);
return baseobject->getFaceList();
}
// create new polygon and prepend it to list (append list to new polygon) -----
//
Polygon *Polygon::NewPolygon()
{
//CAVEAT:
// the automatical assignment of polygon and face number
// must be used judiciously! it's very easy to get the
// numbering wrong. also, a single polygon cannot be
// used as "polygon-factory" since it will always assign
// the same numbers to the new polygons, based on its own.
//NOTE:
// normally, this function should only be used like this:
// poly = poly->NewPolygon();
// automatically assign sequential polygon- and face-number
return new Polygon( baseobject, numpolygons, numpolygons, this, numpolygons + 1 );
}
// prepend existing polygon to list (polygon- and face id are not changed!) ---
//
Polygon *Polygon::InsertPolygon( Polygon *poly )
{
poly->nextpolygon = this;
poly->numpolygons = numpolygons + 1;
poly->baseobject = baseobject;
return poly;
}
// delete head of polygon list; return rest of list ---------------------------
//
Polygon *Polygon::DeleteHead()
{
Polygon *temp = nextpolygon;
nextpolygon = NULL;
delete this;
//CAVEAT:
// this can create problems if function not used correctly!
// INVOKE ONLY LIKE THIS: poly = poly->DeleteHead();
// otherwise a dangling pointer will be created!
// THIS FUNCTION MAY ALSO NEVER BE INVOKED ON STATICALLY
// ALLOCATED POLYGONS!!
return temp;
}
// find polygon with specific id ----------------------------------------------
//
Polygon *Polygon::FindPolygon( int id )
{
// scan entire list and compare ids
for ( Polygon *scanpo = this; scanpo; scanpo = scanpo->getNext() ) {
if ( scanpo->getId() == id )
return scanpo;
}
// no polygon with this id found
return NULL;
}
// sum up vertex numbers for all polygons of list -----------------------------
//
int Polygon::SumVertexNumsEntireList()
{
// scan entire list and compare ids
int count = 0;
for ( Polygon *scanpo = this; scanpo; scanpo = scanpo->getNext() )
count += scanpo->getNumVertices();
return count;
}
// correct relative values to refer to new base -------------------------------
//
void Polygon::CorrectBase( BspObject *newbaseobj, int vertexindxbase, int faceidbase, int polygonidbase )
{
baseobject = newbaseobj;
faceno += faceidbase;
polygonno += polygonidbase;
// correct all vertex indexes by just adding offset value
for ( VIndx *vertlist = vertindxs; vertlist; vertlist = vertlist->getNext() )
vertlist->setIndx( vertlist->getIndx() + vertexindxbase );
}
// correct relative values to refer to new base using vertex index map --------
//
void Polygon::CorrectBaseByTable( BspObject *newbaseobj, int *vtxindxmap, int faceidbase, int polygonidbase )
{
baseobject = newbaseobj;
faceno += faceidbase;
polygonno += polygonidbase;
// correct all vertex indexes using mapping table
for ( VIndx *vertlist = vertindxs; vertlist; vertlist = vertlist->getNext() )
vertlist->setIndx( vtxindxmap[ vertlist->getIndx() ] );
}
// write list of vertices to text-file ----------------------------------------
//
void Polygon::FillVertexIndexArray( dword *arr ) const
{
// scan list of vertex indexes and write them into the int array
for ( VIndx *vertlist = vertindxs; vertlist; vertlist = vertlist->getNext() )
*arr++ = vertlist->getIndx();
}
// write list of vertices to text-file ----------------------------------------
//
void Polygon::WriteVertexList( FILE *fp, int cr ) const
{
// scan list of vertex indexes and write them to text file
for ( VIndx *vertlist = vertindxs; vertlist; vertlist = vertlist->getNext() )
fprintf( fp, vertlist->getNext() ? "%d, " : "%d", vertlist->getIndx() + 1 );
if ( cr )
fprintf( fp, "\n" );
}
// write list of polygon numbers to text-file (reverse ordering) --------------
//
void Polygon::WritePolyList( FILE *fp ) const
{
// recurse to achieve reverse ordering
if ( nextpolygon )
nextpolygon->WritePolyList( fp );
fprintf( fp, "%d, ", getId() + 1 );
}
// add new VIndx for polygon at head of vertindxs -----------------------------
//
void Polygon::PrependNewVIndx( int indx )
{
// prepend new VIndx
VIndx *oldhead = vertindxs;
vertindxs = new VIndx( indx, oldhead );
// remember last node in list
if ( vindxinsertpos == NULL )
vindxinsertpos = vertindxs;
// update number of vertex indexes
numvertindxs++;
}
// add new VIndx for polygon at tail of vertindxs -----------------------------
//
void Polygon::AppendNewVIndx( int indx )
{
// append new VIndx
if ( vindxinsertpos != NULL ) {
vindxinsertpos->setNext( new VIndx( indx ) );
vindxinsertpos = vindxinsertpos->getNext();
} else {
vertindxs = new VIndx( indx );
vindxinsertpos = vertindxs;
}
// update number of vertex indexes
numvertindxs++;
}
// append existing VIndx at tail of vertindxs ---------------------------------
//
void Polygon::AppendVIndx( VIndx *vindx )
{
// append passed in VIndx
if ( vindxinsertpos ) {
vindxinsertpos->setNext( vindx );
} else {
vertindxs = vindx;
}
vindxinsertpos = vindx;
//CAVEAT:
// if passed in VIndx is the head of a list
// that list is unlinked when appending!
vindx->setNext( NULL );
// update number of vertex indexes
numvertindxs++;
}
// unlink last node in list of vertex indexes ---------------------------------
//
VIndx *Polygon::UnlinkLastVIndx()
{
VIndx *vlist = vertindxs;
if ( vlist ) {
numvertindxs--;
if ( vlist->getNext() ) {
for ( ; vlist->getNext()->getNext(); vlist = vlist->getNext() ) ;
vindxinsertpos = vlist;
VIndx *node = vlist->getNext();
vlist->setNext( NULL );
return node;
} else {
// return one and only node in list
vindxinsertpos = NULL;
vertindxs = NULL;
return vlist;
}
} else {
// list was already empty
return NULL;
}
}
// calculate bounding box encompassing all polygons in list -------------------
//
void Polygon::CalcBoundingBox( BoundingBox* &boundingbox )
{
VertexChunk& vtxlist = getVertexList();
VIndx *polyvertlist = getVList();
Vertex3 minvertex = vtxlist[ polyvertlist->getIndx() ];
Vertex3 maxvertex = vtxlist[ polyvertlist->getIndx() ];
polyvertlist = polyvertlist->getNext();
for ( Polygon *curpoly = this; ; ) {
for ( ; polyvertlist; polyvertlist = polyvertlist->getNext() ) {
Vertex3& testvertex = vtxlist[ polyvertlist->getIndx() ];
if ( testvertex.getX() < minvertex.getX() )
minvertex.setX( testvertex.getX() );
if ( testvertex.getY() < minvertex.getY() )
minvertex.setY( testvertex.getY() );
if ( testvertex.getZ() < minvertex.getZ() )
minvertex.setZ( testvertex.getZ() );
if ( testvertex.getX() > maxvertex.getX() )
maxvertex.setX( testvertex.getX() );
if ( testvertex.getY() > maxvertex.getY() )
maxvertex.setY( testvertex.getY() );
if ( testvertex.getZ() > maxvertex.getZ() )
maxvertex.setZ( testvertex.getZ() );
}
if ( ( curpoly = curpoly->getNext() ) != NULL )
polyvertlist = curpoly->getVList();
else
break;
}
//CAVEAT:
// boundingbox MUST NOT be initialized, since it
// is overwritten without deletion!
boundingbox = new BoundingBox( minvertex, maxvertex );
}
// check which class with respect to the splitter a given polygon belongs to --
//
int Polygon::CheckIntersection( Polygon *testpoly )
{
VertexChunk& vtxlist = getVertexList();
Plane splitterplane = getPlane();
int numneg = 0, numpos = 0, numzero = 0;
// classify all vertices of polygon with respect to splitting plane
VIndx *polyvertlist = testpoly->getVList();
for ( ; polyvertlist; polyvertlist = polyvertlist->getNext() ) {
Vertex3 curvertex = vtxlist[ polyvertlist->getIndx() ];
if ( splitterplane.PointInPositiveHalfspace( curvertex ) )
numpos++;
else if ( splitterplane.PointInNegativeHalfspace( curvertex ) )
numneg++;
else
numzero++;
}
// determine polygon's classification from vertex classifications
if ( ( numneg == 0 ) && ( numpos == 0 ) )
return POLY_IN_SAME_PLANE;
else if ( numneg == 0 )
return POLY_IN_FRONT_SUBSPACE;
else if ( numpos == 0 )
return POLY_IN_BACK_SUBSPACE;
else
return POLY_STRADDLES_SPLITTER;
}
// check which halfspace another polygon's normal's tip lies in ---------------
//
int Polygon::NormalDirectionSimilar( Polygon *testpoly )
{
// calculate position of normal's tip
Vertex3 normalstip = testpoly->getFirstVertex() + testpoly->getPlaneNormal();
// check position of tip with respect to this polygon's plane
return !getPlane().PointInNegativeHalfspace( normalstip );
}
// split polygon straddling two subspaces -------------------------------------
//
void Polygon::SplitPolygon( Polygon *poly, Polygon* &frontsubspace, Polygon* &backsubspace )
{
VertexChunk& vtxlist = getVertexList();
FaceChunk& facelist = getFaceList();
if ( display_messages & MESSAGE_SPLITTING_POLYGON ) {
sprintf( str_scratchpad, "--- Splitting polygon %d\n", poly->getId() + 1 );
InfoMessage( str_scratchpad );
}
// create the two new polygons: one for each subspace.
// the original polygon (this) will not be deleted by this function.
Polygon *frontpoly = new Polygon( baseobject,
poly->getId(),
poly->getFaceId(),
frontsubspace );
Polygon *backpoly = new Polygon( baseobject,
poly->getId(),
poly->getFaceId(),
backsubspace );
// fetch normal vector and arbitrary point on splitter plane
Vector3 splitternormal = getPlaneNormal();
Vertex3 splittervertex = getFirstVertex();
// init pointer to vertex-list of source polygon
VIndx *polyvertlist = poly->getVList();
// check location of first vertex and set starting subspace accordingly
Vector3 directionvec( splittervertex, poly->getFirstVertex() );
if ( normalize_vectors )
directionvec.Normalize();
double scalprod( splitternormal.DotProduct( directionvec ) );
// check if first vertex lies in splitter plane
if ( fabs( scalprod ) < EPS_SCALAR ) {
if ( display_messages & MESSAGE_STARTVERTEX_IN_SPLITTER_PLANE ) {
sprintf( str_scratchpad, "Startvertex in splitter-plane: %d\n",
polyvertlist->getIndx() + 1 );
InfoMessage( str_scratchpad );
}
VIndx *scan = NULL;
// scan vertex list for vertex not contained in splitter plane
for ( scan = polyvertlist->getNext(); scan; scan = scan->getNext() ) {
directionvec.CreateDirVec( splittervertex, vtxlist[ scan->getIndx() ] );
if ( normalize_vectors )
directionvec.Normalize();
// set scalprod such that the start-subspace is opposite
// the first non-contained vertex's subspace
scalprod = -splitternormal.DotProduct( directionvec );
// exit loop if non-contained vertex found
if ( fabs( scalprod ) >= EPS_SCALAR )
break;
}
if ( display_messages & MESSAGE_STARTVERTEX_IN_SPLITTER_PLANE ) {
if ( scan == NULL ) {
sprintf( str_scratchpad, "WARNING: all vertices contained in splitter-plane!\n" );
} else {
sprintf( str_scratchpad, "Using vertex %d for halfspace classification.\n",
scan->getIndx() + 1 );
}
InfoMessage( str_scratchpad );
}
}
/*
// check if polygon to split is non-convex
if ( [non-convex polygon] ) {
ErrorMessage( "***ERROR*** Non-convex polygon encountered while splitting." );
HandleCriticalError();
}
*/
// select subspace to start in
int cursubspace = ( scalprod > 0.0 ) ? FRONT_SUBSPACE : BACK_SUBSPACE;
// split polygon into two polygons; one for each subspace ---------------
int twas1 = 0;
for ( ; polyvertlist; polyvertlist = polyvertlist->getNext() ) {
// copy current vertex from source polygon to its respective subspace
if ( cursubspace == FRONT_SUBSPACE )
frontpoly->AppendNewVIndx( polyvertlist->getIndx() );
else
backpoly->AppendNewVIndx( polyvertlist->getIndx() );
// check intersection of splitter plane with next lineseg -----
int nextvertex = polyvertlist->getNext() ?
polyvertlist->getNext()->getIndx() : poly->getVList()->getIndx();
// denominator is length of projection of lineseg onto plane normal
Vector3 linevec( vtxlist[ polyvertlist->getIndx() ], vtxlist[ nextvertex ] );
double denominator = splitternormal.DotProduct( linevec );
// numerator is distance from splittervertex to startvertex of lineseg
Vector3 directionvec( splittervertex, vtxlist[ polyvertlist->getIndx() ] );
double numerator = splitternormal.DotProduct( directionvec );
// if lineseg isn't parallel to splitter plane, check for intersection
if ( fabs( denominator ) >= EPS_DENOM_ZERO ) {
double t = - numerator / denominator;
if ( twas1 || ( fabs( t ) < EPS_POINT_ON_LINESEG ) ) {
if ( display_messages & MESSAGE_VERTEX_IN_SPLITTER_PLANE ) {
sprintf( str_scratchpad, "Vertex in splitter-plane! (%d)\n", polyvertlist->getIndx() + 1 );
InfoMessage( str_scratchpad );
}
// reset flag
twas1 = 0;
// switch subspace and insert same vertex again
cursubspace = -cursubspace;
if ( cursubspace == FRONT_SUBSPACE )
frontpoly->AppendNewVIndx( polyvertlist->getIndx() );
else
backpoly->AppendNewVIndx( polyvertlist->getIndx() );
// check if next vertex is contained in the other subspace
Vector3 directionvec( splittervertex, vtxlist[ nextvertex ] );
if ( normalize_vectors )
directionvec.Normalize();
double scalprod( splitternormal.DotProduct( directionvec ) );
if ( ( ( scalprod < 0.0 ) && ( cursubspace == FRONT_SUBSPACE ) ) ||
( ( scalprod > 0.0 ) && ( cursubspace == BACK_SUBSPACE ) ) ) {
// switch back again
cursubspace = -cursubspace;
if ( display_messages & MESSAGE_VERTEX_IN_SPLITTER_PLANE ) {
sprintf( str_scratchpad, "Vertex alone in %s halfspace!\n",
( cursubspace == BACK_SUBSPACE ) ? "front" : "back" );
InfoMessage( str_scratchpad );
}
}
//NOTE:
// the above check is necessary to avoid problems with polygons actually
// entirely contained in a single subspace but nevertheless classified as
// straddling. in this case the implicit subspace-switching for t=0 vertices
// would cause these vertices to be inserted into the wrong subspace!
} else if ( ( t >= EPS_POINT_ON_LINESEG ) && ( t <= 1.0 - EPS_POINT_ON_LINESEG ) ) {
// insert intersection point into both subspaces and switch
cursubspace = -cursubspace;
Vertex3 newvertex( linevec * t + vtxlist[ polyvertlist->getIndx() ] );
int newvertindx = vtxlist.FindCloseVertex( newvertex );
if ( newvertindx == -1 ) {
newvertindx = vtxlist.AddVertex( newvertex );
if ( display_messages & MESSAGE_NEW_SPLITVERTEX ) {
sprintf( str_scratchpad, "Inserting new split-vertex (%d)\n", vtxlist.getNumElements() );
InfoMessage( str_scratchpad );
}
} else if ( display_messages & MESSAGE_REUSING_SPLITVERTEX ) {
sprintf( str_scratchpad, "Reusing split-vertex (%d)\n", newvertindx + 1 );
InfoMessage( str_scratchpad );
}
frontpoly->AppendNewVIndx( newvertindx );
backpoly->AppendNewVIndx( newvertindx );
} else if ( fabs( t - 1.0 ) < EPS_POINT_ON_LINESEG ) {
// set flag
twas1 = 1;
//NOTE:
// this flag is necessary to avoid numerical problems when
// a vertex is classified as t=1 for one edge but is not
// classified as t=0 for the next edge.
// if this is not the last vertex it will be inserted
// during the next iteration. if not, the insertion
// must be done here, otherwise a vertex will be missing
// for the current polygon!
if ( polyvertlist->getNext() == NULL ) {
// determine vertex's subspace
Polygon *testpoly = ( cursubspace == FRONT_SUBSPACE ) ? frontpoly : backpoly;
// check if not already inserted as first vertex
if ( ( testpoly->getVList() != NULL ) &&
( testpoly->getVList()->getIndx() == nextvertex ) )
continue;
// insert last vertex into its subspace
testpoly->AppendNewVIndx( nextvertex );
}
}
} else if ( polyvertlist->getNext() == NULL ) {
// determine last vertex's subspace
Polygon *testpoly = ( cursubspace == FRONT_SUBSPACE ) ? frontpoly : backpoly;
// check if not already inserted as first vertex
if ( ( testpoly->getVList() != NULL ) &&
( testpoly->getVList()->getIndx() == nextvertex ) )
continue;
// insert last vertex into its subspace
testpoly->AppendNewVIndx( nextvertex );
}
}
// check generated polygons for degeneration
if ( frontpoly->HasArea() ) {
// insert front poly into front subspace
frontsubspace = frontpoly;
// check back poly
if ( backpoly->HasArea() ) {
// insert back poly into back subspace
backsubspace = backpoly;
// set new id in sequence
backpoly->setId( baseobject->getNumPolygons() );
// this poly has not been counted yet
baseobject->numpolygons++;
} else {
// remove degenerated back poly
backpoly->setNext( NULL );
delete backpoly;
if ( display_messages & MESSAGE_SPLITTING_POLYGON )
InfoMessage( "Split not carried out due to boundary case: polygon in front halfspace.\n" );
}
} else {
// remove degenerated front poly
frontpoly->setNext( NULL );
delete frontpoly;
if ( display_messages & MESSAGE_SPLITTING_POLYGON )
InfoMessage( "Split not carried out due to boundary case: polygon in back halfspace.\n" );
// check back poly
if ( backpoly->HasArea() ) {
// insert back poly into back subspace
backsubspace = backpoly;
} else {
// both polygons degenerated: should be impossible!
Error();
}
}
}
// partition list of polygons into two halfspaces (heavily recursive!) --------
//
BSPNode *Polygon::PartitionSpace()
{
// display invocation message if flag set
if ( display_messages & MESSAGE_INVOCATION ) {
sprintf( str_scratchpad, "PartitionSpace() called: #%d polygon %d\n",
partition_callcount, getId() + 1 );
InfoMessage( str_scratchpad );
partition_callcount++;
}
// only one polygon in this halfspace?
if ( nextpolygon == NULL ) {
if ( display_messages & MESSAGE_INVOCATION ) {
sprintf( str_scratchpad, "Leaf: polygon %d\n", getId() + 1 );
InfoMessage( str_scratchpad );
}
return new BSPNode( NULL, NULL, this, NULL );
}
// used after recursive calls: has to be automatic!!
Polygon *splitter = this;
// take first polygon in list without testing?
if ( splitter_crit == SPLITTERCRIT_FIRST_POLY )
goto splitter_selected;
// find best candidate for splitter -----------------------------
static int bestnumsplitted;
static int bestnumcontained;
bestnumsplitted = INT_MAX;
bestnumcontained = 0;
static int samplecount;
samplecount = 0;
static Polygon *currentsplitter;
static Polygon *precpolygon;
static Polygon *splitterpred;
currentsplitter = this;
precpolygon = NULL;
splitterpred = NULL;
// walk polygon list trying to find appropriate splitter
for ( ; currentsplitter; precpolygon = currentsplitter, currentsplitter = currentsplitter->getNext() ) {
if ( splitter_crit == SPLITTERCRIT_RANDOM_SAMPLE ) {
//TODO: don't use rtl rand() as pseudo random number generator
//NOTE: assumes that RAND_MAX is at least 10000
if ( ( rand() % 10000 ) > test_probability )
continue;
}
static int numsplitted;
static int numcontained;
numsplitted = 0;
numcontained = 0;
// determine number of polygons splitted and contained by *currentsplitter, respectively
static Polygon *scanpo;
for ( scanpo = this; scanpo; scanpo = scanpo->getNext() ) {
if ( scanpo != currentsplitter ) {
switch ( currentsplitter->CheckIntersection( scanpo ) ) {
case POLY_STRADDLES_SPLITTER:
numsplitted++;
break;
case POLY_IN_SAME_PLANE:
numcontained++;
break;
default:
// *currentsplitter neither splits nor contains *scanpo
break;
}
}
}
if ( numsplitted == 0 ) {
// if nothing splitted at all, skip all other tests
splitter = currentsplitter;
splitterpred = precpolygon;
break;
} else if ( numsplitted < bestnumsplitted ) {
splitter = currentsplitter;
splitterpred = precpolygon;
bestnumsplitted = numsplitted;
bestnumcontained = numcontained;
} else if ( ( numsplitted == bestnumsplitted ) && ( numcontained > bestnumcontained ) ) {
splitter = currentsplitter;
splitterpred = precpolygon;
bestnumcontained = numcontained;
}
// test sample size for SPLITTERCRIT_SAMPLE_FIRST_N and SPLITTERCRIT_RANDOM_SAMPLE
if ( splitter_crit & SPLITTERCRITMASK_SAMPLESIZ ) {
// desired sample size already reached?
if ( ++samplecount >= sample_size )
break;
}
}
splitter_selected:
// build polygon list without splitter
static Polygon *polylist;
polylist = this;
if ( splitter == polylist ) {
polylist = splitter->getNext();
} else {
splitterpred->setNext( splitter->getNext() );
}
splitter->setNext( NULL ); // unlink splitter from rest of list
splitter->setNumPolygons( 1 ); // splitter contains only itself
// display splitter info
if ( display_messages & MESSAGE_INVOCATION ) {
sprintf( str_scratchpad, "Splitter: polygon %d\n", splitter->getId() + 1 );
InfoMessage( str_scratchpad );
}
// partition space; create subspace lists -----------------------
Polygon *frontsubspace = NULL;
Polygon *backsubspace = NULL;
Polygon *backlist = NULL; // list of backfacing polygons
static Polygon *containlist; // append position for contained polygons
containlist = splitter;
while ( polylist != NULL ) {
static Polygon *temppoly;
switch ( splitter->CheckIntersection( polylist ) ) {
// polygon completely contained in front-subspace
case POLY_IN_FRONT_SUBSPACE:
temppoly = polylist->getNext();
polylist->setNext( frontsubspace );
frontsubspace = polylist;
polylist = temppoly;
break;
// polygon completely contained in back-subspace
case POLY_IN_BACK_SUBSPACE:
temppoly = polylist->getNext();
polylist->setNext( backsubspace );
backsubspace = polylist;
polylist = temppoly;
break;
// polygon straddles plane of splitter
case POLY_STRADDLES_SPLITTER:
// split polygon and add split pieces to the front- and back-subspace, respectively
splitter->SplitPolygon( polylist, frontsubspace, backsubspace );
// delete split polygon
polylist = polylist->DeleteHead();
break;
// polygon lies in same plane as splitter
case POLY_IN_SAME_PLANE:
temppoly = polylist->getNext();
if ( splitter->NormalDirectionSimilar( polylist ) ) {
// insert frontfacing polygon into contained list (at tail)
containlist->setNext( polylist );
containlist = polylist;
containlist->setNext( NULL );
containlist->setNumPolygons( 1 );
//CAVEAT:
// numpolygons is only correct for first polygon in contained-list (splitter)!!
splitter->numpolygons++;
} else {
// insert backfacing polygon into backlist (at head)
polylist->setNext( backlist );
polylist->setNumPolygons( backlist ? backlist->getNumPolygons() + 1 : 1 );
backlist = polylist;
}
polylist = temppoly;
break;
}
}
// allocate new root; partition halfspaces recursively and return root
BSPNode *front = frontsubspace ? frontsubspace->PartitionSpace() : NULL;
BSPNode *back = backsubspace ? backsubspace->PartitionSpace() : NULL;
return new BSPNode( front, back, splitter, backlist );
}
// calculate plane normals for planes of all polygons -------------------------
//
void Polygon::CalcPlaneNormals()
{
// scan entire polygon list
FaceChunk& facelist = getFaceList();
for ( Polygon *polyscan = this; polyscan; polyscan = polyscan->getNext() ) {
// calculate plane (and normal) if not valid yet
Face& curface = facelist[ polyscan->getFaceId() ];
if ( !curface.PlaneValid() )
curface.CalcPlane( polyscan->getFirstVertex(),
polyscan->getSecondVertex(),
polyscan->getThirdVertex() );
}
}
// check all edges for t-vertices and insert trace-vertices as appropriate ----
//
void Polygon::CheckEdges()
{
//NOTE:
// this function is terribly slow, because for every polygon
// every single edge is checked whether it contains any vertices.
// thus, overall every edge is checked twice for containment
// of all vertices and no topological information is used at all!
VertexChunk& vtxlist = getVertexList();
// scan all polygons
for ( Polygon *poly = this; poly; poly = poly->getNext() ) {
// just to be sure
int numvtxs = vtxlist.getNumElements();
if ( numvtxs < 3 )
continue;
// create vertex mask array
char *vmask = new char[ numvtxs ];
memset( vmask, 0, sizeof( char ) * numvtxs );
// scan all edges: init mask
VIndx *polyvertlist = poly->getVList();
for ( ; polyvertlist; polyvertlist = polyvertlist->getNext() )
vmask[ polyvertlist->getIndx() ] = 1;
// scan all edges: check t-junctions
polyvertlist = poly->getVList();
for ( ; polyvertlist; polyvertlist = polyvertlist->getNext() ) {
// determine next vertex with wrap-around
int nextvertex = polyvertlist->getNext() ?
polyvertlist->getNext()->getIndx() : poly->getVList()->getIndx();
// create directed edge
Vector3 linevec( vtxlist[ polyvertlist->getIndx() ], vtxlist[ nextvertex ] );
// check edge against all vertices not masked out
for ( int i = 0; i < numvtxs; i++ )
if ( vmask[ i ] == 0 ) {
LineSeg3 lineseg( vtxlist[ polyvertlist->getIndx() ], linevec );
if ( lineseg.PointOnLineSeg( vtxlist[ i ] ) ) {
// create new vertex-index
VIndx *tempvindx = new VIndx( i, polyvertlist->getNext() );
polyvertlist->setNext( tempvindx );
if ( tempvindx->getNext() == NULL ) {
//NOTE:
// if tracevertex is inserted into last edge, AppendNewVIndx() and AppendVIndx()
// wouldn't work anymore if the insertposition in the polygon isn't
// updated correctly; normally this isn't done anyway, though
vindxinsertpos = tempvindx;
}
// new endpoint for lineseg to check
nextvertex = i;
vmask[ i ] = 1;
linevec.CreateDirVec( vtxlist[ polyvertlist->getIndx() ], vtxlist[ nextvertex ] );
baseobject->numtracevertices++;
if ( display_messages & MESSAGE_TRACEVERTEX_INSERTED ) {
sprintf( str_scratchpad, "Trace vertex inserted: polygon %d, vertex %d\n",
poly->getId() + 1, i + 1 );
InfoMessage( str_scratchpad );
}
}
}
}
// remove mask array
delete vmask;
}
}
// split polygons with vertices not contained in the same plane ---------------
//
Polygon *Polygon::CheckPlanesAndMappings()
{
//NOTE:
// for historical reasons this function checks only quadrilaterals
// for planarity! i.e., if a polygon has more than 4 vertices no
// check is done at all whether all vertices are contained in the
// same plane. quadrilaterals, however, are checked and split into
// two triangles if nonplanar.
// explicit triangulation of quadrilaterals can be forced by using
// the static Polygon::triangulate_all flag.
// fetch vertex and face lists
VertexChunk& vtxlist = getVertexList();
FaceChunk& facelist = getFaceList();
if ( display_messages & MESSAGE_CHECKING_POLYGON_PLANES )
InfoMessage( "\nChecking polygon planes and mappings...\n" );
// scan entire polygon list
Polygon *polylist = this;
for ( Polygon *polyscan = polylist; polyscan; polyscan = polyscan->getNext() ) {
Face& curface = facelist[ polyscan->getFaceId() ];
int vertindx1 = polyscan->getFirstVertexIndx();
int vertindx2 = polyscan->getSecondVertexIndx();
int vertindx3 = polyscan->getThirdVertexIndx();
// calculate face plane/normal if not already done
if ( !curface.PlaneValid() )
curface.CalcPlane( vtxlist[ vertindx1 ], vtxlist[ vertindx2 ], vtxlist[ vertindx3 ] );
// convert correspondences to face mapping if not already done
if ( curface.FaceTexMapped() && !curface.MappingAttached() ) {
// calc correspondence number
int corrno = 0;
for ( int i = 0; i < polyscan->getFaceId(); i++ )
if ( facelist[ i ].FaceTexMapped() )
corrno++;
// set projective space coordinates
curface.MapXY( 0 ).InitFromVertex3( vtxlist[ vertindx1 ] );
curface.MapXY( 1 ).InitFromVertex3( vtxlist[ vertindx2 ] );
curface.MapXY( 2 ).InitFromVertex3( vtxlist[ vertindx3 ] );
// set (u,v)-space coordinates
Vertex2 corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx1 );
curface.MapUV( 0 ) = corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx2 );
curface.MapUV( 1 ) = corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx3 );
curface.MapUV( 2 ) = corrpoint;
}
// check fourth vertex if face is quadrilateral
if ( polyscan->getNumVertices() == 4 ) {
VIndx *vindxscan = polyscan->getVList()->getNext()->getNext(); // pointer to third VIndx
int testvertindx = vindxscan->getNext()->getIndx(); // fourth vertindx
if ( !curface.getPlane().PointContained( vtxlist[ testvertindx ] ) || triangulate_all ) {
// split quadrilateral into triangles
polylist = polylist->NewPolygon(); // create second triangle
polylist->AppendNewVIndx( vertindx3 ); // append VIndx for third vertex
polylist->AppendVIndx( polyscan->UnlinkLastVIndx() ); // append existing VIndx for fourth vertex
polylist->AppendNewVIndx( vertindx1 ); // append VIndx for first vertex
if ( display_messages & MESSAGE_SPLITTING_QUADRILATERAL ) {
sprintf( str_scratchpad, "Splitting quadrilateral: polygon %d\n",
polyscan->getId() + 1 );
InfoMessage( str_scratchpad );
sprintf( str_scratchpad, "-->Creating new triangle: polygon %d\n",
polylist->getId() + 1 );
InfoMessage( str_scratchpad );
}
// update count of split quadrilaterals
baseobject->numsplitquadrilaterals++;
// create new face
int newfaceno = facelist.AddElement( curface );
Face& newface = facelist[ newfaceno ];
// update face id of new polygon
polylist->setFaceId( newfaceno );
// fetch first three vertices
vertindx1 = polylist->getFirstVertexIndx();
vertindx2 = polylist->getSecondVertexIndx();
vertindx3 = polylist->getThirdVertexIndx();
// calculate face normal
newface.CalcPlane( vtxlist[ vertindx1 ], vtxlist[ vertindx2 ], vtxlist[ vertindx3 ] );
// transfer mapping into face
if ( curface.FaceTexMapped() ) {
// calc correspondence number
int corrno = 0;
for ( int i = 0; i < polyscan->getFaceId(); i++ )
if( facelist[ i ].FaceTexMapped() )
corrno++;
// set projective space coordinates
newface.MapXY( 0 ).InitFromVertex3( vtxlist[ vertindx1 ] );
newface.MapXY( 1 ).InitFromVertex3( vtxlist[ vertindx2 ] );
newface.MapXY( 2 ).InitFromVertex3( vtxlist[ vertindx3 ] );
// set (u,v)-space coordinates
Vertex2 corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx1 );
newface.MapUV( 0 ) = corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx2 );
newface.MapUV( 1 ) = corrpoint;
corrpoint = baseobject->mappinglist[ corrno ].FetchMapPoint( vertindx3 );
newface.MapUV( 2 ) = corrpoint;
}
}
}
}
return polylist;
}
// calculate probability with which to test a single polygon ------------------
//
int Polygon::CalcSplitterTestProbability()
{
double numerator = sample_size;
double denominator = numpolygons;
double prob = ( denominator > 0.0 ) ? numerator / denominator : 1.0;
prob = ( prob < 1.0 ) ? prob * 10000.0 : 10000.0;
test_probability = (int) floor( prob + 0.5 );
if ( test_probability < 1 ) test_probability = 1;
return ( numpolygons > 0 );
}
// unspecified error encountered ----------------------------------------------
//
void Polygon::Error() const
{
ErrorMessage( "\n***ERROR*** in object of class BspLib::Polygon." );
HandleCriticalError();
}
// splitter selection ---------------------------------------------------------
//
int Polygon::splitter_crit = Polygon::SPLITTERCRIT_SAMPLE_ALL;
int Polygon::sample_size = 20;
int Polygon::test_probability = 10000; // means 100.00%
// static flags ---------------------------------------------------------------
//
int Polygon::triangulate_all = FALSE;
int Polygon::normalize_vectors = FALSE;
int Polygon::display_messages = Polygon::MESSAGEMASK_DISPLAY_ALL;
// counter for invocations of PartitionSpace() --------------------------------
//
int Polygon::partition_callcount = 0;
// string scratch pad ---------------------------------------------------------
//
char Polygon::str_scratchpad[ 256 ] = "";
BSPLIB_NAMESPACE_END
//-----------------------------------------------------------------------------
|