1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/3d/stripifier.h"
20 // For now, don't use NVidia stripifier.
21 //#include "nv_tri_strip_objects.h"
36 // ***************************************************************************
37 CStripifier::CStripifier()
42 // ***************************************************************************
44 NVidia(tm) 's method get better performance (8ms on 50K faces meshe, instead of 8.9ms), but
45 precomputing is much slower (1'40 instead of 0'?? :) ).
47 /*void CStripifier::optimizeTriangles(const CIndexBuffer &in, CIndexBuffer &out, uint cacheSize)
49 NvStripifier stripifier;
51 NvStripInfoVec outStrips;
55 inIndices.resize(in.getNumTri()*3);
56 for(i=0;i< (sint)inIndices.size(); i++)
58 inIndices[i]= in.getPtr()[i];
62 stripifier.Stripify(inIndices, cacheSize, outStrips);
64 // build triangles from strips, and release memory
66 out.reserveTri(in.getNumTri());
67 for(i= 0;i< (sint)outStrips.size(); i++)
69 NvStripInfo *stripInfo= outStrips[i];
71 // build triangle from the strip.
72 for(uint j= 0;j< stripInfo->m_faces.size(); j++)
74 NvFaceInfo *face= stripInfo->m_faces[j];
75 out.addTri(face->m_v0, face->m_v1, face->m_v2);
81 // Unref first the edges touched by this strip.
82 NvEdgeInfo *edgeInfo = stripInfo->m_startInfo.m_startEdge;
85 NvEdgeInfo *edgeInfoNext= edgeInfo->m_nextV1;
87 edgeInfo= edgeInfoNext;
95 // ***************************************************************************
98 CVertexCache(sint cacheSize
, sint nbVerts
)
100 _VertexInCache
.resize(nbVerts
, 0);
101 _Cache
.resize(cacheSize
, 0xFFFFFFFF);
104 void touchVertex(uint vert
)
106 if(isVertexInCache(vert
))
108 // do nothing ?????? depends of vcache implementation
113 uint removed
= _Cache
.front();
114 if(removed
!=0xFFFFFFFF)
115 _VertexInCache
[removed
]= 0;
118 _VertexInCache
[vert
]= 3;
119 _Cache
.push_back(vert
);
123 bool isVertexInCache(uint vert
)
125 return _VertexInCache
[vert
]==3;
128 // return which vertex is at which place in the cache. 0xFFFFFFFF if the entry is empty
129 uint
getVertexInCache(uint vertIdInCache
)
131 return _Cache
[vertIdInCache
];
134 void tempTouchVertex(uint vert
, bool inCache
)
136 if( _VertexInCache
[vert
]&1 )
139 _VertexInCache
[vert
]|= 2;
141 _VertexInCache
[vert
]&= 1;
146 // 0 if not in the cache
147 vector
<uint8
> _VertexInCache
;
148 deque
<uint32
> _Cache
;
153 // ***************************************************************************
159 void insertInPB(CIndexBuffer
&out
, CVertexCache
&vertexCache
)
161 uint index
= out
.getNumIndexes ();
162 out
.setNumIndexes (index
+3);
163 CIndexBufferReadWrite ibaWrite
;
165 ibaWrite
.setTri(index
, v
[0], v
[1], v
[2]);
166 vertexCache
.touchVertex(v
[0]);
167 vertexCache
.touchVertex(v
[1]);
168 vertexCache
.touchVertex(v
[2]);
172 sint
countCacheMiss(CVertexCache
&vertexCache
)
175 if(!vertexCache
.isVertexInCache(v
[0])) ret
++;
176 if(!vertexCache
.isVertexInCache(v
[1])) ret
++;
177 if(!vertexCache
.isVertexInCache(v
[2])) ret
++;
183 // ***************************************************************************
188 // corner == tuple face/vertex.
194 // ***************************************************************************
195 void CStripifier::optimizeTriangles(const CIndexBuffer
&in
, CIndexBuffer
&out
, uint cacheSize
)
197 vector
<COrderFace
> inFaces
;
199 sint numTris
= in
.getNumIndexes()/3;
201 // TestYoyo: All the same tri => perfect vertex caching...
202 /*out.setNumTri(numTris);
203 for(i=0;i< numTris; i++)
205 uint32 v0= *(in.getPtr()+0);
206 uint32 v1= *(in.getPtr()+1);
207 uint32 v2= *(in.getPtr()+2);
208 out.setTri(i, v0, v1, v2);
213 // prepare inIndices.
214 //--------------------
216 CIndexBufferRead ibaRead
;
218 inFaces
.resize(numTris
);
219 if (ibaRead
.getFormat() == CIndexBuffer::Indices32
)
221 for(i
=0;i
< numTris
; i
++)
223 const uint32
*ibaPtr
= (const uint32
*) ibaRead
.getPtr();
224 inFaces
[i
].v
[0]= ibaPtr
[i
*3 + 0];
225 inFaces
[i
].v
[1]= ibaPtr
[i
*3 + 1];
226 inFaces
[i
].v
[2]= ibaPtr
[i
*3 + 2];
227 inFaces
[i
].Inserted
= false;
232 nlassert(ibaRead
.getFormat() == CIndexBuffer::Indices16
);
233 for(i
=0;i
< numTris
; i
++)
235 const uint16
*ibaPtr
= (const uint16
*) ibaRead
.getPtr();
236 inFaces
[i
].v
[0]= ibaPtr
[i
*3 + 0];
237 inFaces
[i
].v
[1]= ibaPtr
[i
*3 + 1];
238 inFaces
[i
].v
[2]= ibaPtr
[i
*3 + 2];
239 inFaces
[i
].Inserted
= false;
245 // build our cache, and compute max number of vertices.
246 //--------------------
248 for (i
= 0; i
< numTris
; i
++)
250 numVerts
= max(numVerts
, (int)inFaces
[i
].v
[0]);
251 numVerts
= max(numVerts
, (int)inFaces
[i
].v
[1]);
252 numVerts
= max(numVerts
, (int)inFaces
[i
].v
[2]);
255 CVertexCache
vertexCache(cacheSize
, numVerts
);
258 // Compute vertex connectivity.
259 //--------------------
260 vector
<CCornerNode
*> vertexConnectivity
;
261 vector
<CCornerNode
> cornerAllocator
;
262 cornerAllocator
.resize(numTris
* 3);
263 vertexConnectivity
.resize(numVerts
, NULL
);
264 // For all triangles.
265 for (i
= 0; i
< numTris
; i
++)
267 COrderFace
*ordFace
= &inFaces
[i
];
268 // For each corner, allocate and fill
269 for(sint j
=0; j
<3;j
++)
271 sint vertexId
= ordFace
->v
[j
];
274 CCornerNode
*corner
= &cornerAllocator
[i
*3 + j
];
278 corner
->VertexId
= vertexId
;
279 // Link it to the vertex list of faces.
280 corner
->Next
= vertexConnectivity
[vertexId
];
281 vertexConnectivity
[vertexId
]= corner
;
286 // build output optimized triangles
287 //--------------------
288 out
.setFormat(in
.getFormat());
289 out
.setNumIndexes(0);
290 out
.reserve(3*numTris
);
292 for(i
=0; i
<numTris
; i
++)
294 // force insertion of the ith face.
295 sint nextToInsert
= i
;
296 bool nextToInsertFound
= true;
297 while( nextToInsertFound
)
299 nextToInsertFound
= false;
301 // if the face is not yet inserted.
302 if(!inFaces
[nextToInsert
].Inserted
)
304 // must insert this face.
305 inFaces
[nextToInsert
].insertInPB(out
, vertexCache
);
309 // look only for faces which use vertices in VertexCache, to get a face with at least one vertex.
310 for(uint j
=0; j
<cacheSize
; j
++)
312 // get a vertex from the vertex cache.
313 uint vertexId
= vertexCache
.getVertexInCache(j
);
315 if(vertexId
==0xFFFFFFFF)
318 // parse list of faces which use this vertex.
319 CCornerNode
*corner
= vertexConnectivity
[vertexId
];
322 uint faceId
= corner
->FaceId
;
324 // if the face is not yet inserted.
325 if(!inFaces
[faceId
].Inserted
)
327 sint c
= inFaces
[faceId
].countCacheMiss(vertexCache
);
328 // insert first any face which don't add any vertex in the cache.
331 inFaces
[faceId
].insertInPB(out
, vertexCache
);
333 // else the one which add the minimum of vertex possible: nextToInsert
336 // Add cost of faces that use vertices pushed out (better results...)
339 for(k
=cacheSize
-numVOut
; k
<cacheSize
; k
++)
341 uint vertexOutId
= vertexCache
.getVertexInCache(k
);
342 if(vertexOutId
==0xFFFFFFFF)
344 // TempRemove the vertex from the cache
345 vertexCache
.tempTouchVertex(vertexOutId
, false);
347 // parse all faces that still use those out vertices.
348 for(k
=cacheSize
-numVOut
; k
<cacheSize
; k
++)
350 uint vertexOutId
= vertexCache
.getVertexInCache(k
);
351 if(vertexOutId
==0xFFFFFFFF)
353 CCornerNode
*cornerOut
= vertexConnectivity
[vertexOutId
];
356 uint faceOutId
= cornerOut
->FaceId
;
358 // if the face is not yet inserted AND not the one treated
359 if(!inFaces
[faceOutId
].Inserted
&& faceOutId
!=faceId
)
361 // Add cache miss of this face
362 c
+= inFaces
[faceOutId
].countCacheMiss(vertexCache
);
366 cornerOut
= cornerOut
->Next
;
370 for(k
=cacheSize
-numVOut
; k
<cacheSize
; k
++)
372 uint vertexOutId
= vertexCache
.getVertexInCache(k
);
373 if(vertexOutId
==0xFFFFFFFF)
375 // restore TempTouch the vertex from the cache
376 vertexCache
.tempTouchVertex(vertexOutId
, true);
380 // take the minimum cost
383 nextToInsert
= faceId
;
384 nextToInsertFound
= true;
391 corner
= corner
->Next
;
396 // if nextToInsertFound, then nextToInsert has the face which add the minimum of vertex possible in the cache