1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2011 by the OpenSG Forum *
7 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
9 \*---------------------------------------------------------------------------*/
10 /*---------------------------------------------------------------------------*\
13 * This library is free software; you can redistribute it and/or modify it *
14 * under the terms of the GNU Library General Public License as published *
15 * by the Free Software Foundation, version 2. *
17 * This library is distributed in the hope that it will be useful, but *
18 * WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
20 * Library General Public License for more details. *
22 * You should have received a copy of the GNU Library General Public *
23 * License along with this library; if not, write to the Free Software *
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
26 \*---------------------------------------------------------------------------*/
28 #include "OSGConfig.h"
29 #include "OSGGeoOptimization.h"
30 #include "OSGGeometry.h"
31 #include "OSGTypedGeoIntegralProperty.h"
32 #include "OSGTypedGeoVectorProperty.h"
36 // #include <boost/functional/hash/hash.hpp>
37 // #include <tr1/unordered_set>
39 #define OSG_GEOOPT_VERBOSE 1
40 #undef OSG_GEOOPT_VERBOSE
42 #define OSG_GEOOPT_STATISTICS 1
43 #undef OSG_GEOOPT_STATISTICS
47 #if __GNUC__ >= 4 || __GNUC_MINOR__ >=3
48 #pragma GCC diagnostic push
49 #pragma GCC diagnostic ignored "-Wunused-function"
54 // Index Tuple (IT) comparison function objects.
55 // There are ITLess, ITHash, ITEqual to allow using
56 // std::set<> or std::tr1::unordered_set<>
60 typedef std::vector
<GeoIntegralProperty
*> IndexStore
;
62 explicit ITLess(Geometry
*geo
);
63 ITLess(const ITLess
&other
);
65 bool operator()(UInt32 lhs
, UInt32 rhs
) const;
67 std::ostream
&print(std::ostream
&os
, UInt32 idx
);
74 void operator =(const ITLess
&rhs
);
78 ITLess::ITLess(Geometry
*geo
)
82 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
84 GeoIntegralProperty
*idx
= _geo
->getIndex(i
);
89 for(UInt32 j
= 0; add
== true && j
< _istore
.size(); ++j
)
96 _istore
.push_back(idx
);
101 ITLess::ITLess(const ITLess
&other
)
103 _istore(other
._istore
)
109 ITLess::operator()(UInt32 lhs
, UInt32 rhs
) const
113 IndexStore::const_iterator isIt
= _istore
.begin();
114 IndexStore::const_iterator isEnd
= _istore
.end ();
116 for(; isIt
!= isEnd
; ++isIt
)
120 (*isIt
)->getValue(valL
, lhs
);
121 (*isIt
)->getValue(valR
, rhs
);
126 retVal
= valL
< valR
;
134 ITLess::print(std::ostream
&os
, UInt32 idx
)
136 IndexStore::const_iterator isIt
= _istore
.begin();
137 IndexStore::const_iterator isEnd
= _istore
.end ();
140 for(; isIt
!= isEnd
; ++isIt
)
143 (*isIt
)->getValue(val
, idx
);
151 /* ==================================================================== */
153 #if 0 // disabled for now
156 typedef std::vector
<GeoIntegralProperty
*> IndexStore
;
158 explicit ITHash(Geometry
*geo
);
160 std::size_t operator()(UInt32 idx
) const;
167 ITHash::ITHash(Geometry
*geo
)
171 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
173 GeoIntegralProperty
*idx
= _geo
->getIndex(i
);
178 for(UInt32 j
= 0; add
== true && j
< _istore
.size(); ++j
)
180 if(idx
== _istore
[j
])
185 _istore
.push_back(idx
);
191 ITHash::operator()(UInt32 idx
) const
193 std::size_t retVal
= 0;
195 IndexStore::const_iterator isIt
= _istore
.begin();
196 IndexStore::const_iterator isEnd
= _istore
.end ();
198 for(; isIt
!= isEnd
; ++isIt
)
201 (*isIt
)->getValue(val
, idx
);
203 boost::hash_combine(retVal
, val
);
209 /* ==================================================================== */
213 typedef std::vector
<GeoIntegralProperty
*> IndexStore
;
215 explicit ITEqual(Geometry
*geo
);
217 bool operator()(UInt32 lhs
, UInt32 rhs
) const;
224 ITEqual::ITEqual(Geometry
*geo
)
228 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
230 GeoIntegralProperty
*idx
= _geo
->getIndex(i
);
235 for(UInt32 j
= 0; add
== true && j
< _istore
.size(); ++j
)
237 if(idx
== _istore
[j
])
242 _istore
.push_back(idx
);
248 ITEqual::operator()(UInt32 lhs
, UInt32 rhs
) const
252 IndexStore::const_iterator isIt
= _istore
.begin();
253 IndexStore::const_iterator isEnd
= _istore
.end ();
255 for(; isIt
!= isEnd
; ++isIt
)
259 (*isIt
)->getValue(valL
, lhs
);
260 (*isIt
)->getValue(valR
, rhs
);
271 #endif // disabled for now
273 typedef std::set
<UInt32
, ITLess
> ITSet
;
274 //typedef std::tr1::unordered_set<UInt32, ITHash, ITEqual> ITSet;
276 /* ==================================================================== */
278 typedef std::vector
<OSG::GeoVectorPropertyUnrecPtr
> PropertyStore
;
279 typedef std::vector
<OSG::GeoIntegralPropertyUnrecPtr
> PropIndexStore
;
280 typedef std::vector
<std::pair
<UInt32
, UInt32
> > IndexVec
;
282 void copyIndices(const PropIndexStore
&srcIdx
,
284 const PropIndexStore
&dstIdx
,
288 OSG_ASSERT(srcIdx
.size() == dstIdx
.size());
290 for(UInt32 i
= 0; i
< srcIdx
.size(); ++i
)
292 if(dstIdx
[i
]->size() < (dstOffset
+ count
))
293 dstIdx
[i
]->resize(dstOffset
+ count
);
295 for(UInt32 j
= 0; j
< count
; ++j
)
297 GeoIntegralProperty::MaxTypeT val
;
298 srcIdx
[i
]->getValue(val
, srcOffset
+ j
);
299 dstIdx
[i
]->setValue(val
, dstOffset
+ j
);
304 void copyIndexSet(const PropIndexStore
&srcIdx
,
305 const std::vector
<UInt32
> &srcIndices
,
306 const PropIndexStore
&dstIdx
,
309 OSG_ASSERT(srcIdx
.size() == dstIdx
.size());
311 UInt32 count
= srcIndices
.size();
313 for(UInt32 i
= 0; i
< srcIdx
.size(); ++i
)
315 if(dstIdx
[i
]->size() < (dstOffset
+ count
))
316 dstIdx
[i
]->resize(dstOffset
+ count
);
318 for(UInt32 j
= 0; j
< count
; ++j
)
320 GeoIntegralProperty::MaxTypeT val
;
322 srcIdx
[i
]->getValue(val
, srcIndices
[j
]);
323 dstIdx
[i
]->setValue(val
, dstOffset
+ j
);
328 Real32
polyArea(GeoVectorProperty
*pPos
,
329 IndexVec
&vPolyIndex
,
339 for(Int32 p
= len
- 1, q
= 0; q
< len
; p
= q
++)
341 pPos
->getValue(oPntP
, vPolyIndex
[p
].first
);
342 pPos
->getValue(oPntQ
, vPolyIndex
[q
].first
);
344 A
+= oPntP
[uiXIdx
] * oPntQ
[uiYIdx
] - oPntQ
[uiXIdx
] * oPntP
[uiYIdx
];
350 /* InsideTriangle decides if a point P is Inside of the triangle
354 bool InsideTriangle(Real32 Ax
, Real32 Ay
,
355 Real32 Bx
, Real32 By
,
356 Real32 Cx
, Real32 Cy
,
357 Real32 Px
, Real32 Py
)
360 Real32 ax
, ay
, bx
, by
, cx
, cy
, apx
, apy
, bpx
, bpy
, cpx
, cpy
;
361 Real32 cCROSSap
, bCROSScp
, aCROSSbp
;
363 ax
= Cx
- Bx
; ay
= Cy
- By
;
364 bx
= Ax
- Cx
; by
= Ay
- Cy
;
365 cx
= Bx
- Ax
; cy
= By
- Ay
;
366 apx
= Px
- Ax
; apy
= Py
- Ay
;
367 bpx
= Px
- Bx
; bpy
= Py
- By
;
368 cpx
= Px
- Cx
; cpy
= Py
- Cy
;
370 aCROSSbp
= ax
* bpy
- ay
* bpx
;
371 cCROSSap
= cx
* apy
- cy
* apx
;
372 bCROSScp
= bx
* cpy
- by
* cpx
;
374 return ((aCROSSbp
>= 0.0f
) && (bCROSScp
>= 0.0f
) && (cCROSSap
>= 0.0f
));
377 static const float EPSILON
= 0.0000000001f
;
379 bool Snip(GeoVectorProperty
*pPos
,
380 IndexVec
&vPolyIndex
,
389 Real32 Ax
, Ay
, Bx
, By
, Cx
, Cy
, Px
, Py
;
393 pPos
->getValue(oPnt
, vPolyIndex
[u
].first
);
398 pPos
->getValue(oPnt
, vPolyIndex
[v
].first
);
403 pPos
->getValue(oPnt
, vPolyIndex
[w
].first
);
408 if(EPSILON
> (((Bx
- Ax
) * (Cy
- Ay
)) - ((By
- Ay
) * (Cx
- Ax
))))
411 for(p
= 0; p
< n
; ++p
)
413 if((p
== u
) || (p
== v
) || (p
== w
))
416 pPos
->getValue(oPnt
, vPolyIndex
[p
].first
);
421 if(InsideTriangle(Ax
, Ay
, Bx
, By
, Cx
, Cy
, Px
, Py
))
428 // Simple polygon triangulation, base on
429 // http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
430 // from John W. Ratcliff
432 UInt32
triangulatePoly( IndexVec
&vPolyIndex
,
433 const PropIndexStore
&srcIdx
,
434 const PropIndexStore
&dstIdx
,
439 OSG_ASSERT(len
>= 3 && pGeo
!= NULL
);
441 GeoVectorProperty
*pPos
= pGeo
->getProperty(Geometry::PositionsIndex
);
443 // figure out the coordinate plane to use:
449 pPos
->getValue(oP0
, vPolyIndex
[0].first
);
450 pPos
->getValue(oP1
, vPolyIndex
[1].first
);
451 pPos
->getValue(oP2
, vPolyIndex
[2].first
);
453 Vec3f oE0
= oP1
- oP0
;
454 Vec3f oE1
= oP2
- oP0
;
456 Vec3f oN
= oE0
.cross(oE1
);
457 UInt32 uiMaxIdx
= oN
.maxDimAbs();
467 else if(uiMaxIdx
== 1)
472 Real32 fArea
= polyArea(pPos
,
478 bool bReversed
= false;
482 std::reverse(vPolyIndex
.begin(), vPolyIndex
.end());
489 /* remove nv-2 Vertices, creating 1 triangle every time */
490 Int32 count
= 2 * nv
; /* error detection */
492 std::vector
<UInt32
> vTriIndices
;
494 vTriIndices
.resize(3);
496 UInt32 returnValue
= 0;
498 for(Int32 m
= 0, v
= nv
- 1; nv
> 2;)
501 /* if we loop, it is probably a non-simple polygon */
504 //** Triangulate: ERROR - probable bad polygon!
508 /* three consecutive vertices in current polygon, <u,v,w> */
512 u
= 0; /* previous */
531 vTriIndices
[0] = vPolyIndex
[u
].second
;
533 if(bReversed
== false)
535 vTriIndices
[1] = vPolyIndex
[v
].second
;
536 vTriIndices
[2] = vPolyIndex
[w
].second
;
540 vTriIndices
[1] = vPolyIndex
[w
].second
;
541 vTriIndices
[2] = vPolyIndex
[v
].second
;
554 /* remove v from remaining polygon */
555 for(Int32 s
= v
, t
= v
+ 1; t
< nv
; ++s
, ++t
)
556 vPolyIndex
[s
] = vPolyIndex
[t
];
560 /* resest error detection counter */
571 /* ======================================================================== */
573 /*! Modifies the Geometry \a geo to use a single index for all
576 This creates new GeoVectorProperties (of the same type) for all
577 properties and creates a new index. The geometry must be (multi) indexed.
581 makeSingleIndexed(Geometry
*geo
)
583 if(geo
->isSingleIndex() == true)
585 SINFO
<< "Geometry is already single indexed, nothing to do."
590 bool hasIndex
= false;
592 for(UInt32 i
= 0; i
<= Geometry::LastIndex
; ++i
)
594 if(geo
->getIndex(i
) != NULL
)
601 if(hasIndex
== false)
603 SWARNING
<< "No index found, aborting."
608 // 1) compute the set of unique index tuples
609 // itSet contains the offset into the geo's indices
610 // for unique index tuples
611 // index contains the new offset for a index tuple
613 // 0* 1* 2* 3 4 5* 6 7 8* *'ed elements are in itSet
615 // x x x x x x x x x idx for prop 0
616 // 0 1 2 2 1 3 2 3 4 idx for prop 1
617 // x x x x x x x x x idx for prop 2
619 // 0 1 2 2 1 3 2 3 4 contents of index
621 // the comparison function object used for itSet is what
622 // enables the detection of duplicate index tuples
624 #if defined(OSG_GEOOPT_STATISTICS)
625 Time start1
= getSystemTime();
628 typedef std::vector
<UInt32
> IndexUI32
;
630 ITSet
itSet((ITLess(geo
)));
631 //ITSet itSet(10, ITHash(geo), ITEqual(geo));
634 GeoIntegralProperty
*lengths
= geo
->getLengths();
635 UInt32 lengthsN
= lengths
->size32();
638 for(UInt32 i
= 0; i
< lengthsN
; ++i
)
641 lengths
->getValue(len
, i
);
643 for(UInt32 j
= 0; j
< len
; ++j
)
645 ITSet::const_iterator isIt
= itSet
.find(k
);
647 if(isIt
!= itSet
.end())
649 // already encountered index tuple
650 index
.push_back(index
[*isIt
]);
654 // new unique index tuple
655 index
.push_back(UInt32(itSet
.size()));
663 #if defined(OSG_GEOOPT_STATISTICS)
664 SLOG
<< "time to compute unique index tuples: " << (getSystemTime() - start1
)
667 Time start2
= getSystemTime();
670 // 2) rewrite properties
672 // For existing propery propN and its index propNIdx we
673 // fill the new proprty newPropN.
674 // index[*isIt] is the unique index tuple number for *isIt
676 // newPropN[index[*isIt]] = propN[propNIdx[*isIt]]
679 PropertyStore newProp
;
680 PropIndexStore propIdx
;
682 // create new properties
683 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
685 if(geo
->getProperty(i
) != NULL
)
689 for(UInt32 j
= 0; add
== true && j
< prop
.size(); ++j
)
691 if(prop
[j
] == geo
->getProperty(i
))
697 // property present, but no index?
698 OSG_ASSERT(geo
->getIndex(i
) != NULL
);
700 prop
.push_back(geo
->getProperty(i
));
701 propIdx
.push_back(geo
->getIndex (i
));
703 // create new propery of same type
704 GeoVectorPropertyUnrecPtr np
=
705 dynamic_pointer_cast
<GeoVectorProperty
>(
706 geo
->getProperty(i
)->getType().createContainer());
707 np
->resize(itSet
.size());
709 newProp
.push_back(np
);
714 // fill new properties
715 UInt32 propCount
= UInt32(prop
.size());
717 ITSet::const_iterator isIt
= itSet
.begin();
718 ITSet::const_iterator isEnd
= itSet
.end ();
720 for(; isIt
!= isEnd
; ++isIt
)
722 for(UInt32 i
= 0; i
< propCount
; ++i
)
724 GeoIntegralProperty::MaxTypeT idx
;
725 GeoVectorProperty ::MaxTypeT val
;
727 propIdx
[i
]->getValue(idx
, *isIt
);
728 prop
[i
] ->getValue(val
, idx
);
730 newProp
[i
]->setValue(val
, index
[*isIt
]);
735 GeoUInt32PropertyUnrecPtr newIdx
= GeoUInt32Property::create();
736 newIdx
->editFieldPtr()->insert(
737 newIdx
->editFieldPtr()->begin(),
738 index
.begin(), index
.end() );
740 // set new properties and index on geometry
741 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
743 if(geo
->getProperty(i
) != NULL
)
745 for(UInt32 j
= 0; j
< prop
.size(); ++j
)
747 if(prop
[j
] == geo
->getProperty(i
))
749 geo
->setProperty(newProp
[j
], i
);
754 geo
->setIndex(newIdx
, i
);
758 geo
->setIndex(NULL
, i
);
762 #if defined(OSG_GEOOPT_STATISTICS)
763 SLOG
<< "time to rewrite properties: " << (getSystemTime() - start2
)
768 /* ======================================================================== */
770 /*! Converts all "face" primitives (triangles strips/fans, quads, quad strips,
771 polygons) to triangle lists.
773 This modifies the types and lengths of the geometry and replaces indices
776 Assumes valid OpenGL conforming convex primitives
779 void makeIndexedTriangles(Geometry
*geo
,
780 bool bKeepLowerDimPrimitives
)
782 #if defined(OSG_GEOOPT_STATISTICS)
783 Time start
= getSystemTime();
786 // 1) find unique index properties used by the geometry and create
787 // new ones of the same size (if necessary the new indices are
788 // grown by the copyIndices() helper function)
790 PropIndexStore oldIdx
;
791 PropIndexStore newIdx
;
793 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
795 if(geo
->getIndex(i
) != NULL
)
799 for(UInt32 j
= 0; add
== true && j
< oldIdx
.size(); ++j
)
801 if(geo
->getIndex(i
) == oldIdx
[j
])
807 oldIdx
.push_back(geo
->getIndex(i
));
809 newIdx
.push_back(GeoUInt32Property::create());
810 newIdx
.back()->resize(oldIdx
.back()->size());
815 GeoIntegralProperty
*oldTypes
= geo
->getTypes ();
816 GeoIntegralProperty
*oldLengths
= geo
->getLengths();
818 if(oldTypes
->size() != oldLengths
->size())
820 SWARNING
<< "Types and Lengths have inconsistent size, aborting."
825 // 2a) loop over all primitives first copying those that can
826 // not be converted to GL_TRIANGLES (e.g. lines, points,
827 // unknown primitives).
828 // This allows group all primitives that can be converted
829 // to be grouped into one GL_TRIANGLES primitive at
832 GeoIntegralPropertyUnrecPtr newTypes
=
833 dynamic_pointer_cast
<GeoIntegralProperty
>(
834 oldTypes
->getType().createContainer());
835 GeoIntegralPropertyUnrecPtr newLengths
=
836 dynamic_pointer_cast
<GeoIntegralProperty
>(
837 oldLengths
->getType().createContainer());
839 bool doSecondPass
= false;
840 UInt32 primCount
= oldTypes
->size32();
841 UInt32 srcOffset
= 0;
842 UInt32 dstOffset
= 0;
844 if(bKeepLowerDimPrimitives
== true)
846 for(UInt32 i
= 0; i
< primCount
; ++i
)
848 UInt32 type
= oldTypes
->getValue(i
);
849 UInt32 len
= oldLengths
->getValue(i
);
858 // not a "face" primitive, copy indices unchanged
859 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
863 newTypes
->push_back(type
);
864 newLengths
->push_back(len
);
869 case GL_TRIANGLE_STRIP
:
870 case GL_TRIANGLE_FAN
:
875 // these can be converted, we handle them later
882 // unknown primitive, copy indices unchanged
883 SWARNING
<< "Unknown primitive type " << type
884 << " copying indices unchanged." << std::endl
;
885 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
889 newTypes
->push_back(type
);
890 newLengths
->push_back(len
);
901 // 2b) if we encountered any primitive that can be converted,
902 // do a second pass over all primitives handling only the
903 // ones that can be converted.
905 if(doSecondPass
== true)
907 GLenum lastPrim
= GL_NONE
;
909 for(UInt32 i
= 0; i
< primCount
; ++i
)
911 UInt32 lengthsCount
= newLengths
->size32();
912 UInt32 type
= oldTypes
->getValue(i
);
913 UInt32 len
= oldLengths
->getValue(i
);
919 // already tris, copy indices unchanged
920 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
924 if(lastPrim
== GL_TRIANGLES
)
926 newLengths
->setValue(
927 newLengths
->getValue(lengthsCount
- 1) + len
,
932 newTypes
->push_back(GL_TRIANGLES
);
933 newLengths
->push_back(len
);
934 lastPrim
= GL_TRIANGLES
;
938 case GL_TRIANGLE_STRIP
:
942 SWARNING
<< "Encountered degenerate TRIANGLE_STRIP,"
947 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
951 for(UInt32 j
= 3; j
< len
; ++j
)
955 copyIndices(oldIdx
, srcOffset
-2, newIdx
, dstOffset
);
956 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
+1);
957 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+2);
961 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
);
962 copyIndices(oldIdx
, srcOffset
-2, newIdx
, dstOffset
+1);
963 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+2);
970 if(lastPrim
== GL_TRIANGLES
)
972 newLengths
->setValue(
973 newLengths
->getValue(lengthsCount
- 1) + 3 * (len
- 2),
978 newTypes
->push_back(GL_TRIANGLES
);
979 newLengths
->push_back(3 * (len
- 2));
980 lastPrim
= GL_TRIANGLES
;
984 case GL_TRIANGLE_FAN
:
989 SWARNING
<< "Encountered degenerate TRIANGLE_FAN "
990 << "or POLYGON, aborting."
994 UInt32 firstSrcOffset
= srcOffset
;
995 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
999 for(UInt32 j
= 3; j
< len
; ++j
)
1001 copyIndices(oldIdx
, firstSrcOffset
, newIdx
, dstOffset
);
1002 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
+1);
1003 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+2);
1009 if(lastPrim
== GL_TRIANGLES
)
1011 newLengths
->setValue(
1012 newLengths
->getValue(lengthsCount
- 1) + 3 * (len
- 2),
1017 newTypes
->push_back(GL_TRIANGLES
);
1018 newLengths
->push_back(3 * (len
- 2));
1019 lastPrim
= GL_TRIANGLES
;
1025 if(len
< 4 || len
% 4 != 0)
1027 SWARNING
<< "Encountered degenerate QUADS, aborting."
1031 for(UInt32 j
= 0; j
< len
/ 4; ++j
)
1033 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
1036 copyIndices(oldIdx
, srcOffset
-3, newIdx
, dstOffset
);
1037 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
+1);
1038 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+2);
1043 if(lastPrim
== GL_TRIANGLES
)
1045 newLengths
->setValue(
1046 newLengths
->getValue(lengthsCount
- 1) + 3 * (len
/ 2),
1051 newTypes
->push_back(GL_TRIANGLES
);
1052 newLengths
->push_back(3 * (len
/ 2));
1053 lastPrim
= GL_TRIANGLES
;
1059 if(len
< 4 || len
% 2 != 0)
1061 SWARNING
<< "Encountered degenerate QUAD_STRIP, aborting."
1065 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
);
1066 copyIndices(oldIdx
, srcOffset
+2, newIdx
, dstOffset
+1);
1067 copyIndices(oldIdx
, srcOffset
+1, newIdx
, dstOffset
+2);
1070 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
1074 for(UInt32 j
= 4; j
< len
; j
+= 2)
1076 copyIndices(oldIdx
, srcOffset
-2, newIdx
, dstOffset
);
1077 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+1);
1078 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
+2);
1080 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
, 3);
1085 if(lastPrim
== GL_TRIANGLES
)
1087 newLengths
->setValue(
1088 newLengths
->getValue(lengthsCount
- 1) + 3 * (len
- 2),
1093 newTypes
->push_back(GL_TRIANGLES
);
1094 newLengths
->push_back(3 * (len
- 2));
1095 lastPrim
= GL_TRIANGLES
;
1103 geo
->setTypes (newTypes
);
1104 geo
->setLengths(newLengths
);
1106 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
1108 if(geo
->getIndex(i
) != NULL
)
1110 for(UInt32 j
= 0; j
< oldIdx
.size(); ++j
)
1112 if(geo
->getIndex(i
) == oldIdx
[j
])
1114 geo
->setIndex(newIdx
[j
], i
);
1121 #if defined(OSG_GEOOPT_STATISTICS)
1122 SLOG
<< "time to rewrite types/len/indices: " << (getSystemTime() - start
)
1127 /* checks and creates explicit index properties if not present */
1129 void createExplicitIndex(Geometry
*geo
)
1131 bool hasIndex
= false;
1133 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
1135 if(geo
->getIndex(i
) != NULL
)
1142 if(hasIndex
== false)
1144 GeoVectorProperty
*pPos
= geo
->getProperty(Geometry::PositionsIndex
);
1148 fprintf(stderr
, "no positions, abort\n");
1152 GeoUInt32PropertyUnrecPtr pNewIdx
= GeoUInt32Property::create();
1154 pNewIdx
->editField().reserve(pPos
->size());
1156 for(SizeT i
= 0; i
< pPos
->size(); ++i
)
1158 pNewIdx
->push_back(i
);
1161 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
1163 if(geo
->getProperty(i
) != NULL
)
1165 if(geo
->getProperty(i
)->size() != pPos
->size())
1167 SWARNING
<< "inconsitent property sizes, interesting "
1168 << "things might happen ;)"
1172 geo
->setIndex(pNewIdx
, i
);
1179 /*! Converts all "face" primitives (triangles strips/fans, quads, quad strips,
1180 polygons) to triangle lists.
1182 This modifies the types and lengths of the geometry and replaces indices
1185 Does not assume valid, OpenGL conforming convex GL_POLYGON and GL_QUADS
1188 GL_QUAD_STRIPs are assumed to contain convex primitives.
1191 void makeIndexedTrianglesConcave(Geometry
*geo
,
1192 bool bKeepLowerDimPrimitives
)
1194 #if defined(OSG_GEOOPT_STATISTICS)
1195 Time start
= getSystemTime();
1198 // 1) find unique index properties used by the geometry and create
1199 // new ones of the same size (if necessary the new indices are
1200 // grown by the copyIndices() helper function)
1202 PropIndexStore oldIdx
;
1203 PropIndexStore newIdx
;
1205 createExplicitIndex(geo
);
1207 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
1209 if(geo
->getIndex(i
) != NULL
)
1213 for(UInt32 j
= 0; add
== true && j
< oldIdx
.size(); ++j
)
1215 if(geo
->getIndex(i
) == oldIdx
[j
])
1221 oldIdx
.push_back(geo
->getIndex(i
));
1223 newIdx
.push_back(GeoUInt32Property::create());
1224 newIdx
.back()->resize(oldIdx
.back()->size());
1229 GeoIntegralProperty
*oldTypes
= geo
->getTypes ();
1230 GeoIntegralProperty
*oldLengths
= geo
->getLengths();
1232 if(oldTypes
== NULL
|| oldLengths
== NULL
)
1234 SWARNING
<< "Either no Types and Lengths, aborting."
1239 if(oldTypes
->size() != oldLengths
->size())
1241 SWARNING
<< "Types and Lengths have inconsistent size, aborting."
1246 // 2a) loop over all primitives first copying those that can
1247 // not be converted to GL_TRIANGLES (e.g. lines, points,
1248 // unknown primitives).
1249 // This allows group all primitives that can be converted
1250 // to be grouped into one GL_TRIANGLES primitive at
1253 GeoIntegralPropertyUnrecPtr newTypes
=
1254 dynamic_pointer_cast
<GeoIntegralProperty
>(
1255 oldTypes
->getType().createContainer());
1256 GeoIntegralPropertyUnrecPtr newLengths
=
1257 dynamic_pointer_cast
<GeoIntegralProperty
>(
1258 oldLengths
->getType().createContainer());
1260 bool doSecondPass
= false;
1261 UInt32 primCount
= oldTypes
->size32();
1262 UInt32 srcOffset
= 0;
1263 UInt32 dstOffset
= 0;
1265 if(bKeepLowerDimPrimitives
== true)
1267 for(UInt32 i
= 0; i
< primCount
; ++i
)
1269 UInt32 type
= oldTypes
->getValue(i
);
1270 UInt32 len
= oldLengths
->getValue(i
);
1279 // not a "face" primitive, copy indices unchanged
1280 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
1284 newTypes
->push_back(type
);
1285 newLengths
->push_back(len
);
1290 case GL_TRIANGLE_STRIP
:
1291 case GL_TRIANGLE_FAN
:
1296 // these can be converted, we handle them later
1297 doSecondPass
= true;
1303 // unknown primitive, copy indices unchanged
1304 SWARNING
<< "Unknown primitive type " << type
1305 << " copying indices unchanged." << std::endl
;
1306 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
1310 newTypes
->push_back(type
);
1311 newLengths
->push_back(len
);
1319 doSecondPass
= true;
1322 // 2b) if we encountered any primitive that can be converted,
1323 // do a second pass over all primitives handling only the
1324 // ones that can be converted.
1326 if(doSecondPass
== true)
1328 GLenum lastPrim
= GL_NONE
;
1330 IndexVec vPolyIndex
;
1332 for(UInt32 i
= 0; i
< primCount
; ++i
)
1334 UInt32 lengthsCount
= newLengths
->size32();
1335 UInt32 type
= oldTypes
->getValue(i
);
1336 UInt32 len
= oldLengths
->getValue(i
);
1342 // already tris, copy indices unchanged
1343 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, len
);
1347 if(lastPrim
== GL_TRIANGLES
)
1349 newLengths
->setValue(
1350 newLengths
->getValue(lengthsCount
- 1) + len
,
1355 newTypes
->push_back(GL_TRIANGLES
);
1356 newLengths
->push_back(len
);
1357 lastPrim
= GL_TRIANGLES
;
1362 case GL_TRIANGLE_STRIP
:
1366 SWARNING
<< "Encountered degenerate TRIANGLE_STRIP,"
1371 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
1375 for(UInt32 j
= 3; j
< len
; ++j
)
1379 copyIndices(oldIdx
, srcOffset
-2,
1380 newIdx
, dstOffset
);
1381 copyIndices(oldIdx
, srcOffset
-1,
1382 newIdx
, dstOffset
+1);
1383 copyIndices(oldIdx
, srcOffset
,
1384 newIdx
, dstOffset
+2);
1388 copyIndices(oldIdx
, srcOffset
-1,
1389 newIdx
, dstOffset
);
1390 copyIndices(oldIdx
, srcOffset
-2,
1391 newIdx
, dstOffset
+1);
1392 copyIndices(oldIdx
, srcOffset
,
1393 newIdx
, dstOffset
+2);
1400 if(lastPrim
== GL_TRIANGLES
)
1402 newLengths
->setValue(
1403 newLengths
->getValue(
1404 lengthsCount
- 1) + 3 * (len
- 2),
1409 newTypes
->push_back(GL_TRIANGLES
);
1410 newLengths
->push_back(3 * (len
- 2));
1411 lastPrim
= GL_TRIANGLES
;
1416 case GL_TRIANGLE_FAN
:
1420 SWARNING
<< "Encountered degenerate TRIANGLE_FAN, "
1426 UInt32 firstSrcOffset
= srcOffset
;
1427 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
1431 for(UInt32 j
= 3; j
< len
; ++j
)
1433 copyIndices(oldIdx
, firstSrcOffset
,
1434 newIdx
, dstOffset
);
1435 copyIndices(oldIdx
, srcOffset
-1,
1436 newIdx
, dstOffset
+1);
1437 copyIndices(oldIdx
, srcOffset
,
1438 newIdx
, dstOffset
+2);
1444 if(lastPrim
== GL_TRIANGLES
)
1446 newLengths
->setValue(
1447 newLengths
->getValue(
1448 lengthsCount
- 1) + 3 * (len
- 2),
1453 newTypes
->push_back(GL_TRIANGLES
);
1454 newLengths
->push_back(3 * (len
- 2));
1455 lastPrim
= GL_TRIANGLES
;
1464 SWARNING
<< "Encountered degenerate "
1465 << "POLYGON, aborting."
1470 vPolyIndex
.resize(len
);
1472 for(UInt32 j
= 0; j
< len
; ++j
)
1474 oldIdx
[0]->getValue(vPolyIndex
[j
].first
, srcOffset
+ j
);
1476 vPolyIndex
[j
].second
= srcOffset
+ j
;
1479 UInt32 res
= triangulatePoly(vPolyIndex
,
1491 if(lastPrim
== GL_TRIANGLES
)
1493 newLengths
->setValue(
1494 newLengths
->getValue(
1495 lengthsCount
- 1) + 3 * res
,
1500 newTypes
->push_back(GL_TRIANGLES
);
1501 newLengths
->push_back(3 * res
);
1502 lastPrim
= GL_TRIANGLES
;
1509 if((len
< 4 || len
% 4 != 0))
1511 SWARNING
<< "Encountered degenerate QUADS, aborting."
1516 vPolyIndex
.resize(4);
1518 for(UInt32 k
= 0; k
< len
; k
+= 4)
1520 for(UInt32 j
= 0; j
< 4; ++j
)
1522 oldIdx
[0]->getValue(vPolyIndex
[j
].first
,
1525 vPolyIndex
[j
].second
= srcOffset
+ j
;
1528 UInt32 res
= triangulatePoly(vPolyIndex
,
1540 if(lastPrim
== GL_TRIANGLES
)
1542 newLengths
->setValue(
1543 newLengths
->getValue(
1544 lengthsCount
- 1) + 3 * res
,
1549 newTypes
->push_back(GL_TRIANGLES
);
1550 newLengths
->push_back(3 * res
);
1551 lastPrim
= GL_TRIANGLES
;
1554 lengthsCount
= newLengths
->size32();
1561 if(len
< 4 || len
% 2 != 0)
1563 SWARNING
<< "Encountered degenerate QUAD_STRIP, "
1569 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
);
1570 copyIndices(oldIdx
, srcOffset
+2, newIdx
, dstOffset
+1);
1571 copyIndices(oldIdx
, srcOffset
+1, newIdx
, dstOffset
+2);
1574 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
, 3);
1578 for(UInt32 j
= 4; j
< len
; j
+= 2)
1580 copyIndices(oldIdx
, srcOffset
-2, newIdx
, dstOffset
);
1581 copyIndices(oldIdx
, srcOffset
, newIdx
, dstOffset
+1);
1582 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
+2);
1584 copyIndices(oldIdx
, srcOffset
-1, newIdx
, dstOffset
, 3);
1589 if(lastPrim
== GL_TRIANGLES
)
1591 newLengths
->setValue(
1592 newLengths
->getValue(
1593 lengthsCount
- 1) + 3 * (len
- 2),
1598 newTypes
->push_back(GL_TRIANGLES
);
1599 newLengths
->push_back(3 * (len
- 2));
1600 lastPrim
= GL_TRIANGLES
;
1608 geo
->setTypes (newTypes
);
1609 geo
->setLengths(newLengths
);
1611 for(UInt16 i
= 0; i
<= Geometry::LastIndex
; ++i
)
1613 if(geo
->getIndex(i
) != NULL
)
1615 for(UInt32 j
= 0; j
< oldIdx
.size(); ++j
)
1617 if(geo
->getIndex(i
) == oldIdx
[j
])
1619 geo
->setIndex(newIdx
[j
], i
);
1626 #if defined(OSG_GEOOPT_STATISTICS)
1627 SLOG
<< "time to rewrite types/len/indices: " << (getSystemTime() - start
)
1632 /* ======================================================================== */
1636 // Helper types and functions for makeOptimizedIndex().
1638 const UInt32 ModelledVertexCacheSize
= 32;
1639 const Real32 CachePosScorePower
= 1.5f
;
1640 const Real32 ValenceScoreScale
= 2.0f
;
1641 const Real32 ValenceScorePower
= -0.5f
;
1645 typedef std::vector
<UInt32
> IndexStore
;
1651 UInt32 _totalValence
;
1653 IndexStore _triIndices
;
1656 VertexInfo::VertexInfo(void)
1657 : _cachePos (TypeTraits
<UInt32
>::getMax()),
1665 typedef std::vector
<VertexInfo
> VertexInfoStore
;
1667 /* ==================================================================== */
1674 UInt32 _vertIndices
[3];
1678 TriangleInfo::TriangleInfo(void)
1685 typedef std::vector
<TriangleInfo
> TriangleInfoStore
;
1687 /* ==================================================================== */
1689 // Calculate a vertex' score, based on _remValence
1691 // Then update triangles that use the vertex, by adding
1692 // the change in score to their score and keeping track
1693 // of the one with the highest score.
1695 updateVertexScore(VertexInfo
&vi
,
1696 TriangleInfoStore
&tis
,
1697 Real32
&bestTriScore
,
1698 UInt32
&bestTriIndex
)
1700 Real32 oldScore
= vi
._score
;
1702 if(vi
._remValence
> 0)
1704 // base score based on position in cache
1705 if(vi
._cachePos
== TypeTraits
<UInt32
>::getMax())
1709 else if(vi
._cachePos
< 3)
1715 const Real32 scale
= 1.f
/ (ModelledVertexCacheSize
- 3);
1716 vi
._score
= scale
* (vi
._cachePos
- 3);
1717 vi
._score
= std::pow(1.f
- vi
._score
, CachePosScorePower
);
1720 // boost for having low valence
1721 vi
._score
+= ValenceScoreScale
* std::pow(vi
._remValence
,
1729 Real32 deltaScore
= vi
._score
- oldScore
;
1731 #if defined(OSG_GEOOPT_VERBOSE)
1732 SLOG
<< "update score: score " << vi
._score
1733 << " cache pos " << vi
._cachePos
1734 << " totalValence " << vi
._totalValence
1735 << " remValence " << vi
._remValence
1740 for(UInt32 i
= 0; i
< vi
._remValence
; ++i
)
1742 UInt32 triIdx
= vi
._triIndices
[i
];
1744 tis
[triIdx
]._score
+= deltaScore
;
1746 #if defined(OSG_GEOOPT_VERBOSE)
1747 SLOG
<< " updating tri " << triIdx
1748 << " score " << tis
[triIdx
]._score
;
1751 // we don't need to check tis[triIdx]._added
1752 // because the vertex we are updating has
1753 // it among the first _remValence ones,
1754 // so it was not added yet.
1755 if(tis
[triIdx
]._score
> bestTriScore
)
1757 #if defined(OSG_GEOOPT_VERBOSE)
1758 PLOG
<< " new best";
1761 bestTriScore
= tis
[triIdx
]._score
;
1762 bestTriIndex
= triIdx
;
1765 #if defined(OSG_GEOOPT_VERBOSE)
1771 /* ==================================================================== */
1773 // updates vertex info when triangle usedTriIndex is
1774 // added - reduces _remValence by one and shuffles
1775 // _triIndices around to ensure the ones belonging
1776 // to not-yet-added triangles come first
1778 updateUsedVertex(VertexInfo
&vi
, UInt32 usedTriIndex
)
1780 for(UInt32 j
= 0; j
< vi
._remValence
; ++j
)
1782 if(vi
._triIndices
[j
] == usedTriIndex
)
1784 // move tri index back, so first vi._remValence many
1785 // are the ones that still need to be added
1786 std::swap(vi
._triIndices
[j
],
1787 vi
._triIndices
[vi
._remValence
-1] );
1792 vi
._remValence
-= 1;
1795 /* ==================================================================== */
1797 // Comparison function object for the LRU cache,
1798 // sorts VertexInfos by making NULL less than every non-NULL
1799 // one, and sort non-NULL ones by their _cachePos.
1800 // This ensures we move the 3 NULL slots for the current
1801 // triangles vertices to the front.
1804 bool operator()(VertexInfo
*lhs
, VertexInfo
*rhs
);
1808 LRULess::operator()(VertexInfo
*lhs
, VertexInfo
*rhs
)
1810 bool retVal
= false;
1816 else if(lhs
== NULL
)
1820 else if(rhs
== NULL
)
1826 retVal
= lhs
->_cachePos
< rhs
->_cachePos
;
1832 /* ==================================================================== */
1834 #if defined(OSG_GEOOPT_STATISTICS)
1836 calcCacheMissCount(Geometry
*geo
, UInt32
&missCount
, UInt32
&hitCount
)
1838 typedef std::list
<UInt32
> LRUCache
;
1840 GeoIntegralProperty
*types
= geo
->getTypes ();
1841 GeoIntegralProperty
*lengths
= geo
->getLengths();
1842 GeoIntegralProperty
*propIdx0
= geo
->getIndex(0);
1848 for(UInt32 i
= 0; i
< types
->size(); ++i
)
1850 GLenum type
= types
->getValue(i
);
1851 UInt32 len
= lengths
->getValue(i
);
1853 if(type
!= GL_TRIANGLES
)
1859 for(UInt32 j
= 0; j
< len
; ++j
, ++k
)
1861 UInt32 idx
= propIdx0
->getValue(k
);
1863 LRUCache::iterator lruIt
= std::find(lru
.begin(), lru
.end(), idx
);
1865 if(lruIt
!= lru
.end())
1876 lru
.push_front(idx
);
1878 while(lruSize
> ModelledVertexCacheSize
)
1889 /*! Optimize the index of triangle lists for the post-transform
1891 Based on "Post-Transform Cache Optimization for Indexed Triangle
1892 Lists" by Tom Forsyth, see
1893 http://home.comcast.net/~tom_forsyth/papers/fast_vertex_cache_opt.html
1895 Only works on GL_TRIANGLES primitives in the geometry and requires
1896 the geometry to be single indexed.
1900 makeOptimizedIndex(Geometry
*geo
)
1902 if(geo
->isSingleIndex() == false)
1904 SWARNING
<< "Only single indexed geometries can be optimized."
1909 GeoIntegralProperty
*types
= geo
->getTypes ();
1910 GeoIntegralProperty
*lengths
= geo
->getLengths();
1912 if(types
->size() != lengths
->size())
1914 SWARNING
<< "Types and Lengths have inconsistent size, aborting."
1919 VertexInfoStore vis
;
1920 TriangleInfoStore tis
;
1922 GeoVectorProperty
*prop0
= geo
->getProperty(0);
1923 GeoIntegralPropertyUnrecPtr propIdx0
= geo
->getIndex (0);
1924 GeoIntegralPropertyUnrecPtr newPropIdx
= GeoUInt32Property::create();
1926 if(prop0
== NULL
|| propIdx0
== NULL
)
1928 SWARNING
<< "No property 0 (positions) or index on geometry, "
1934 newPropIdx
->resize(propIdx0
->size());
1936 // 1a) Initialize VertexInfos and TriangleInfos
1937 // First go over geometry and find all GL_TRIANGLES primitives.
1938 // For each triangle store the 3 vertices it uses,
1939 // increment the valence counters of the vertices and
1940 // add the triangle to the list of triangles the vertex belongs to
1942 vis
.resize(prop0
->size());
1944 UInt32 triCount
= 0;
1947 // loop over all primitives
1948 for(UInt32 i
= 0; i
< types
->size(); ++i
)
1950 UInt32 type
= types
->getValue(i
);
1951 UInt32 len
= lengths
->getValue(i
);
1953 // skip non triangles
1954 if(type
!= GL_TRIANGLES
)
1962 SWARNING
<< "Encountered degenerate GL_TRIANGLES, aborting."
1967 // loop over triangles
1968 tis
.resize(triCount
+ len
/ 3);
1970 for(UInt32 j
= 0; j
< len
; j
+= 3)
1972 // update vertices used by triangle
1973 UInt32 idx0
= propIdx0
->getValue(k
++);
1974 UInt32 idx1
= propIdx0
->getValue(k
++);
1975 UInt32 idx2
= propIdx0
->getValue(k
++);
1977 if(idx0
== idx1
|| idx0
== idx2
|| idx1
== idx2
)
1980 vis
[idx0
]._totalValence
+= 1;
1981 vis
[idx0
]._remValence
+= 1;
1982 vis
[idx0
]._triIndices
.push_back(triCount
);
1984 vis
[idx1
]._totalValence
+= 1;
1985 vis
[idx1
]._remValence
+= 1;
1986 vis
[idx1
]._triIndices
.push_back(triCount
);
1988 vis
[idx2
]._totalValence
+= 1;
1989 vis
[idx2
]._remValence
+= 1;
1990 vis
[idx2
]._triIndices
.push_back(triCount
);
1992 // record vertices used by triangle
1993 tis
[triCount
]._vertIndices
[0] = idx0
;
1994 tis
[triCount
]._vertIndices
[1] = idx1
;
1995 tis
[triCount
]._vertIndices
[2] = idx2
;
2003 SINFO
<< "Geometry does not contain any GL_TRIANGLES, ignoring."
2008 #if defined(OSG_GEOOPT_STATISTICS)
2009 UInt32 preMissCount
= 0;
2010 UInt32 preHitCount
= 0;
2011 calcCacheMissCount(geo
, preMissCount
, preHitCount
);
2014 // 1b) Complete initialization by going over VertexInfos
2015 // calculating the initial score and update triangles scores
2017 // Also keep track of the triangle with the highest score
2018 // to have a starting point for the main loop.
2020 Real32 bestTriScore
= 0.f
;
2021 UInt32 bestTriIndex
= 0;
2023 VertexInfoStore::iterator viIt
= vis
.begin();
2024 VertexInfoStore::iterator viEnd
= vis
.end ();
2026 for(; viIt
!= viEnd
; ++viIt
)
2028 updateVertexScore(*viIt
, tis
, bestTriScore
, bestTriIndex
);
2031 #if defined(OSG_GEOOPT_VERBOSE)
2032 SLOG
<< "post init: bestTriScore " << bestTriScore
2033 << " bestTriIndex " << bestTriIndex
2038 // Add triangles to index based on their score, update
2039 // the LRU cache and affected scores after each step
2040 // while keeping track of the triangle with the highest score.
2041 // The triangle with highest score is only an approximation,
2042 // because only tris with vertices in the LRU are considered.
2043 // See the paper for more info.
2045 typedef std::vector
<VertexInfo
*> LRUType
;
2046 LRUType
lru(ModelledVertexCacheSize
+ 3, NULL
);
2049 for(UInt32 i
= 0; i
< triCount
; ++i
)
2051 #if defined(OSG_GEOOPT_VERBOSE)
2052 SLOG
<< "=== tri i " << i
<< " of " << triCount
<< " ==="
2056 // it's possible that all vertices in the cache belong
2057 // to triangles that are already added (very rare case).
2058 // Scan all triangles to find the best.
2059 if(bestTriScore
== 0.f
||
2060 tis
[bestTriIndex
]._added
== true )
2062 #if defined(OSG_GEOOPT_VERBOSE)
2063 SLOG
<< " no valid triangle with vertices in cache"
2064 << " performing full search." << std::endl
;
2070 for(UInt32 j
= 0; j
< triCount
; ++j
)
2072 if(tis
[j
]._added
== false &&
2073 tis
[j
]._score
> bestTriScore
)
2075 bestTriScore
= tis
[j
]._score
;
2081 // Pick the triangle with the highest score and add it to the index
2082 TriangleInfo
&bestTri
= tis
[bestTriIndex
];
2084 newPropIdx
->setValue(bestTri
._vertIndices
[0], i
* 3 );
2085 newPropIdx
->setValue(bestTri
._vertIndices
[1], i
* 3 + 1);
2086 newPropIdx
->setValue(bestTri
._vertIndices
[2], i
* 3 + 2);
2088 VertexInfo
&v0
= vis
[bestTri
._vertIndices
[0]];
2089 VertexInfo
&v1
= vis
[bestTri
._vertIndices
[1]];
2090 VertexInfo
&v2
= vis
[bestTri
._vertIndices
[2]];
2092 updateUsedVertex(v0
, bestTriIndex
);
2093 updateUsedVertex(v1
, bestTriIndex
);
2094 updateUsedVertex(v2
, bestTriIndex
);
2096 bestTri
._added
= true;
2098 #if defined(OSG_GEOOPT_VERBOSE)
2099 SLOG
<< " adding best tri " << bestTriIndex
2100 << " with score " << bestTriScore
;
2103 // if vertices of the added triangle are already
2104 // in the cache we clear out their entries and
2105 // keep track of how much the cache grows
2108 if(v0
._cachePos
!= TypeTraits
<UInt32
>::getMax())
2110 #if defined(OSG_GEOOPT_VERBOSE)
2111 PLOG
<< " v0 " << bestTri
._vertIndices
[0]
2112 << " in cache (" << v0
._cachePos
<< ")";
2114 lru
[v0
._cachePos
] = NULL
;
2119 #if defined(OSG_GEOOPT_VERBOSE)
2120 PLOG
<< " v0 " << bestTri
._vertIndices
[0];
2124 if(v1
._cachePos
!= TypeTraits
<UInt32
>::getMax())
2126 #if defined(OSG_GEOOPT_VERBOSE)
2127 PLOG
<< " v1 " << bestTri
._vertIndices
[1]
2128 << " in cache (" << v1
._cachePos
<< ")";
2130 lru
[v1
._cachePos
] = NULL
;
2135 #if defined(OSG_GEOOPT_VERBOSE)
2136 PLOG
<< " v1 " << bestTri
._vertIndices
[1];
2140 if(v2
._cachePos
!= TypeTraits
<UInt32
>::getMax())
2142 #if defined(OSG_GEOOPT_VERBOSE)
2143 PLOG
<< " v2 " << bestTri
._vertIndices
[2]
2144 << " in cache (" << v2
._cachePos
<< ")";
2146 lru
[v2
._cachePos
] = NULL
;
2151 #if defined(OSG_GEOOPT_VERBOSE)
2152 PLOG
<< " v2 " << bestTri
._vertIndices
[2];
2156 #if defined(OSG_GEOOPT_VERBOSE)
2160 // make sure there are lruGrow NULL entries after the
2162 for(UInt32 j
= lruSize
; j
< lruSize
+ lruGrow
; ++j
)
2165 // sort cache entries, moving NULL entries to the front,
2166 // other entries remain sorted by their _cachePos.
2167 std::sort(lru
.begin(), lru
.begin() + lruSize
+ lruGrow
,
2170 OSG_ASSERT(lru
[0] == NULL
&& lru
[1] == NULL
&& lru
[2] == NULL
);
2172 // put the three used vertices at front of cache
2177 // update cache, setting correct position for entries
2178 // and recalculate score, keeping track of best triangle
2182 for(UInt32 j
= 0; j
< lruSize
+ lruGrow
; ++j
)
2184 if(j
< ModelledVertexCacheSize
)
2186 #if defined(OSG_GEOOPT_VERBOSE)
2187 SLOG
<< " update cache j " << j
<< std::endl
;
2190 lru
[j
]->_cachePos
= j
;
2194 #if defined(OSG_GEOOPT_VERBOSE)
2195 SLOG
<< " update cache j " << j
2196 << " -> evicted" << std::endl
;
2199 lru
[j
]->_cachePos
= TypeTraits
<UInt32
>::getMax();
2202 updateVertexScore(*lru
[j
], tis
, bestTriScore
, bestTriIndex
);
2205 lruSize
= osgMin(lruSize
+ lruGrow
, ModelledVertexCacheSize
);
2207 #if defined(OSG_GEOOPT_VERBOSE)
2208 SLOG
<< " new cache size " << lruSize
2209 << " lruGrow " << lruGrow
2214 // set the new index on the geometry
2215 for(UInt32 i
= 0; i
<= Geometry::LastIndex
; ++i
)
2217 if(geo
->getIndex(i
) == propIdx0
)
2219 geo
->setIndex(newPropIdx
, i
);
2223 #if defined(OSG_GEOOPT_STATISTICS)
2224 UInt32 postMissCount
= 0;
2225 UInt32 postHitCount
= 0;
2226 calcCacheMissCount(geo
, postMissCount
, postHitCount
);
2228 SLOG
<< "preMissCount " << preMissCount
2229 << " preHitCount " << preHitCount
2230 << " pre ACMR " << (Real32(preMissCount
) / triCount
)
2231 << " triCount " << triCount
2233 SLOG
<< "postMissCount " << postMissCount
2234 << " postHitCount " << postHitCount
2235 << " post ACMR " << (Real32(postMissCount
) / triCount
)
2236 << " triCount " << triCount
2238 SLOG
<< "old idx size " << propIdx0
->size()
2239 << " new idx size " << newPropIdx
->size()
2244 /* ======================================================================== */
2248 // Helper types and functions for makeOptimizedProperties().
2250 // A pair of indices. Used to record a change from oldIdx to newIdx.
2253 explicit IndexRemap(UInt32 oldIdx
, UInt32 newIdx
);
2260 IndexRemap::IndexRemap(UInt32 oldIdx
, UInt32 newIdx
)
2266 // Comparison function object for IndexRemap, allows them to be used in
2267 // associative containers (std::set, std::map).
2268 // Only the old index is used for comparison.
2269 struct IndexRemapLess
2271 bool operator()(const IndexRemap
&lhs
, const IndexRemap
&rhs
) const;
2275 IndexRemapLess::operator()(const IndexRemap
&lhs
,
2276 const IndexRemap
&rhs
) const
2278 return (lhs
._oldIdx
< rhs
._oldIdx
);
2281 typedef std::set
<IndexRemap
, IndexRemapLess
> IndexRemapSet
;
2283 // Copies property values from srcOffset in srcProps to destOffset
2284 // in the corresponding dstProps.
2286 copyProperties(const PropertyStore
&srcProps
,
2288 const PropertyStore
&dstProps
,
2291 OSG_ASSERT(srcProps
.size() == dstProps
.size());
2293 for(UInt32 i
= 0; i
< srcProps
.size(); ++i
)
2295 GeoVectorProperty::MaxTypeT val
;
2296 srcProps
[i
]->getValue(val
, srcOffset
);
2297 dstProps
[i
]->setValue(val
, dstOffset
);
2303 /*! Optimize properties of the geometry for the pre-transform vertex
2305 Creates new properties that store values in the order referenced
2306 by the index (without duplicates) and creates a new index for
2307 the reordered properties. This does not change the order
2308 primitives are drawn in, so previos post-transform vertex cache
2309 optimizations are preserved.
2311 Requires the geometry to be single indexed.
2315 makeOptimizedProperties(Geometry
*geo
)
2317 if(geo
->isSingleIndex() == false)
2319 SWARNING
<< "Only single indexed geometries can be optimized."
2324 GeoIntegralProperty
*types
= geo
->getTypes ();
2325 GeoIntegralProperty
*lengths
= geo
->getLengths();
2327 if(types
->size() != lengths
->size())
2329 SWARNING
<< "Types and Lengths have inconsistent size, aborting."
2334 GeoIntegralPropertyUnrecPtr propIdx0
= geo
->getIndex(0);
2336 if(propIdx0
== NULL
)
2338 SWARNING
<< "No index for property 0 (positions), aborting."
2343 // create new properties of the same type as existing ones
2346 PropertyStore newProp
;
2347 GeoIntegralPropertyUnrecPtr newPropIdx
= GeoUInt32Property::create();
2349 newPropIdx
->resize(propIdx0
->size());
2351 for(UInt32 i
= 0; i
<= Geometry::LastIndex
; ++i
)
2353 if(geo
->getProperty(i
) != NULL
)
2357 for(UInt32 j
= 0; add
== true && j
< prop
.size(); ++j
)
2359 if(prop
[j
] == geo
->getProperty(i
))
2365 // create new propery of same type
2366 GeoVectorPropertyUnrecPtr np
=
2367 dynamic_pointer_cast
<GeoVectorProperty
>(
2368 geo
->getProperty(i
)->getType().createContainer());
2369 np
->resize(geo
->getProperty(i
)->size());
2371 prop
.push_back(geo
->getProperty(i
));
2372 newProp
.push_back(np
);
2377 // reorder properties
2378 // go through the index, filling the properties in the order
2379 // of access through the index.
2380 // idxSet is used to keep track of already added properties
2381 // and the new value to be stored in the index for accessing it.
2383 IndexRemapSet idxSet
;
2385 UInt32 idxOffset
= 0;
2386 UInt32 propOffset
= 0;
2388 for(UInt32 i
= 0; i
< lengths
->size(); ++i
)
2390 UInt32 len
= lengths
->getValue(i
);
2392 for(UInt32 j
= 0; j
< len
; ++j
, ++idxOffset
)
2394 UInt32 idx
= propIdx0
->getValue(idxOffset
);
2396 IndexRemap
ir(idx
, propOffset
);
2397 IndexRemapSet::const_iterator isIt
= idxSet
.find(ir
);
2399 if(isIt
== idxSet
.end())
2401 // first use of this idx
2404 // copy properties to new position
2405 copyProperties(prop
, idx
, newProp
, propOffset
);
2407 // write new position to index
2408 newPropIdx
->setValue(propOffset
, idxOffset
);
2414 // idx occured previously, only record
2415 // new position in index
2416 newPropIdx
->setValue((*isIt
)._newIdx
, idxOffset
);
2421 // set new properties and index on geometry
2423 for(UInt32 i
= 0; i
<= Geometry::LastIndex
; ++i
)
2425 if(geo
->getProperty(i
) != NULL
)
2427 for(UInt32 j
= 0; j
< prop
.size(); ++j
)
2429 if(geo
->getProperty(i
) == prop
[j
])
2431 geo
->setProperty(newProp
[j
], i
);
2432 geo
->setIndex (newPropIdx
, i
);
2439 #if __GNUC__ >= 4 || __GNUC_MINOR__ >=3
2440 #pragma GCC diagnostic pop