2016-08-03 00:34:54 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 Khaled Mammou - Advanced Micro Devices, Inc.
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
#ifndef O3DGC_TRIANGLE_LIST_ENCODER_INL
|
|
|
|
#define O3DGC_TRIANGLE_LIST_ENCODER_INL
|
|
|
|
|
|
|
|
namespace o3dgc
|
|
|
|
{
|
|
|
|
// extract opposite edge
|
|
|
|
template <class T>
|
|
|
|
inline void CompueOppositeEdge(const long focusVertex,
|
|
|
|
const T * triangle,
|
|
|
|
long & a, long & b)
|
|
|
|
{
|
|
|
|
if ((long) triangle[0] == focusVertex)
|
|
|
|
{
|
|
|
|
a = triangle[1];
|
|
|
|
b = triangle[2];
|
|
|
|
}
|
|
|
|
else if ((long) triangle[1] == focusVertex)
|
|
|
|
{
|
|
|
|
a = triangle[2];
|
|
|
|
b = triangle[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
a = triangle[0];
|
|
|
|
b = triangle[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline bool IsCase0(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 1000001 vertices: -1 -2
|
|
|
|
if ((numIndices != 2) || (degree < 2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -1) ||(indices[1] != -2) ||
|
|
|
|
(ops[0] != 1) ||(ops[degree-1] != 1) ) return false;
|
|
|
|
for (long u = 1; u < degree-1; u++) {
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase1(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 1xxxxxx1 indices: -1 x x x x x -2
|
|
|
|
if ((degree < 2) || (numIndices < 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -1) ||(indices[numIndices-1] != -2) ||
|
|
|
|
(ops[0] != 1) ||(ops[degree-1] != 1) ) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase2(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 00000001 indices: -1
|
|
|
|
if ((degree < 2) || (numIndices!= 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -1) || (ops[degree-1] != 1) ) return false;
|
|
|
|
for (long u = 0; u < degree-1; u++) {
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase3(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 00000001 indices: -2
|
|
|
|
if ((degree < 2) || (numIndices!= 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -2) || (ops[degree-1] != 1) ) return false;
|
|
|
|
for (long u = 0; u < degree-1; u++) {
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase4(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 10000000 indices: -1
|
|
|
|
if ((degree < 2) || (numIndices!= 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -1) || (ops[0] != 1) ) return false;
|
|
|
|
for (long u = 1; u < degree; u++)
|
|
|
|
{
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase5(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 10000000 indices: -2
|
|
|
|
if ((degree < 2) || (numIndices!= 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -2) || (ops[0] != 1) ) return false;
|
|
|
|
for (long u = 1; u < degree; u++) {
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2017-11-02 10:13:52 +00:00
|
|
|
inline bool IsCase6(long degree, long numIndices, const long * const ops, const long * const /*indices*/)
|
2016-08-03 00:34:54 +00:00
|
|
|
{
|
|
|
|
// ops: 0000000 indices:
|
|
|
|
if (numIndices!= 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (long u = 0; u < degree; u++)
|
|
|
|
{
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase7(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 1000001 indices: -2 -1
|
|
|
|
if ((numIndices!= 2) || (degree < 2))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -2) ||(indices[1] != -1) ||
|
|
|
|
(ops[0] != 1) ||(ops[degree-1] != 1) ) return false;
|
|
|
|
for (long u = 1; u < degree-1; u++)
|
|
|
|
{
|
|
|
|
if (ops[u] != 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline bool IsCase8(long degree, long numIndices, const long * const ops, const long * const indices)
|
|
|
|
{
|
|
|
|
// ops: 1xxxxxx1 indices: -1 x x x x x -2
|
|
|
|
if ((degree < 2) || (numIndices < 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((indices[0] != -2) ||(indices[numIndices-1] != -1) ||
|
|
|
|
(ops[0] != 1) ||(ops[degree-1] != 1) ) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
TriangleListEncoder<T>::TriangleListEncoder(void)
|
|
|
|
{
|
|
|
|
m_vtags = 0;
|
|
|
|
m_ttags = 0;
|
|
|
|
m_tmap = 0;
|
|
|
|
m_vmap = 0;
|
|
|
|
m_count = 0;
|
|
|
|
m_invVMap = 0;
|
|
|
|
m_invTMap = 0;
|
|
|
|
m_nonConqueredTriangles = 0;
|
|
|
|
m_nonConqueredEdges = 0;
|
|
|
|
m_visitedVertices = 0;
|
|
|
|
m_visitedVerticesValence = 0;
|
|
|
|
m_vertexCount = 0;
|
|
|
|
m_triangleCount = 0;
|
|
|
|
m_maxNumVertices = 0;
|
|
|
|
m_maxNumTriangles = 0;
|
|
|
|
m_numTriangles = 0;
|
|
|
|
m_numVertices = 0;
|
|
|
|
m_triangles = 0;
|
|
|
|
m_maxSizeVertexToTriangle = 0;
|
|
|
|
m_streamType = O3DGC_STREAM_TYPE_UNKOWN;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
TriangleListEncoder<T>::~TriangleListEncoder()
|
|
|
|
{
|
|
|
|
delete [] m_vtags;
|
|
|
|
delete [] m_vmap;
|
|
|
|
delete [] m_invVMap;
|
|
|
|
delete [] m_invTMap;
|
|
|
|
delete [] m_visitedVerticesValence;
|
|
|
|
delete [] m_visitedVertices;
|
|
|
|
delete [] m_ttags;
|
|
|
|
delete [] m_tmap;
|
|
|
|
delete [] m_count;
|
|
|
|
delete [] m_nonConqueredTriangles;
|
|
|
|
delete [] m_nonConqueredEdges;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::Init(const T * const triangles,
|
|
|
|
long numTriangles,
|
|
|
|
long numVertices)
|
|
|
|
{
|
|
|
|
assert(numVertices > 0);
|
|
|
|
assert(numTriangles > 0);
|
|
|
|
|
|
|
|
m_numTriangles = numTriangles;
|
|
|
|
m_numVertices = numVertices;
|
|
|
|
m_triangles = triangles;
|
|
|
|
m_vertexCount = 0;
|
|
|
|
m_triangleCount = 0;
|
|
|
|
|
|
|
|
if (m_numVertices > m_maxNumVertices)
|
|
|
|
{
|
|
|
|
delete [] m_vtags;
|
|
|
|
delete [] m_vmap;
|
|
|
|
delete [] m_invVMap;
|
|
|
|
delete [] m_visitedVerticesValence;
|
|
|
|
delete [] m_visitedVertices;
|
|
|
|
m_maxNumVertices = m_numVertices;
|
|
|
|
m_vtags = new long [m_numVertices];
|
|
|
|
m_vmap = new long [m_numVertices];
|
|
|
|
m_invVMap = new long [m_numVertices];
|
|
|
|
m_visitedVerticesValence = new long [m_numVertices];
|
|
|
|
m_visitedVertices = new long [m_numVertices];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_numTriangles > m_maxNumTriangles)
|
|
|
|
{
|
|
|
|
delete [] m_ttags;
|
|
|
|
delete [] m_tmap;
|
|
|
|
delete [] m_invTMap;
|
|
|
|
delete [] m_nonConqueredTriangles;
|
|
|
|
delete [] m_nonConqueredEdges;
|
|
|
|
delete [] m_count;
|
|
|
|
m_maxNumTriangles = m_numTriangles;
|
|
|
|
m_ttags = new long [m_numTriangles];
|
|
|
|
m_tmap = new long [m_numTriangles];
|
|
|
|
m_invTMap = new long [m_numTriangles];
|
|
|
|
m_count = new long [m_numTriangles+1];
|
|
|
|
m_nonConqueredTriangles = new long [m_numTriangles];
|
|
|
|
m_nonConqueredEdges = new long [2*m_numTriangles];
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(m_vtags , 0x00, sizeof(long) * m_numVertices );
|
|
|
|
memset(m_vmap , 0xFF, sizeof(long) * m_numVertices );
|
|
|
|
memset(m_invVMap, 0xFF, sizeof(long) * m_numVertices );
|
|
|
|
memset(m_ttags , 0x00, sizeof(long) * m_numTriangles);
|
|
|
|
memset(m_tmap , 0xFF, sizeof(long) * m_numTriangles);
|
|
|
|
memset(m_invTMap, 0xFF, sizeof(long) * m_numTriangles);
|
|
|
|
memset(m_count , 0x00, sizeof(long) * (m_numTriangles+1));
|
|
|
|
|
|
|
|
m_vfifo.Allocate(m_numVertices);
|
|
|
|
m_ctfans.SetStreamType(m_streamType);
|
|
|
|
m_ctfans.Allocate(m_numVertices, m_numTriangles);
|
|
|
|
|
|
|
|
// compute vertex-to-triangle adjacency information
|
|
|
|
m_vertexToTriangle.AllocateNumNeighborsArray(numVertices);
|
|
|
|
m_vertexToTriangle.ClearNumNeighborsArray();
|
|
|
|
long * numNeighbors = m_vertexToTriangle.GetNumNeighborsBuffer();
|
|
|
|
for(long i = 0, t = 0; i < m_numTriangles; ++i, t+=3)
|
|
|
|
{
|
|
|
|
++numNeighbors[ triangles[t ] ];
|
|
|
|
++numNeighbors[ triangles[t+1] ];
|
|
|
|
++numNeighbors[ triangles[t+2] ];
|
|
|
|
}
|
|
|
|
m_maxSizeVertexToTriangle = 0;
|
|
|
|
for(long i = 0; i < numVertices; ++i)
|
|
|
|
{
|
|
|
|
if (m_maxSizeVertexToTriangle < numNeighbors[i])
|
|
|
|
{
|
|
|
|
m_maxSizeVertexToTriangle = numNeighbors[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_vertexToTriangle.AllocateNeighborsArray();
|
|
|
|
m_vertexToTriangle.ClearNeighborsArray();
|
|
|
|
for(long i = 0, t = 0; i < m_numTriangles; ++i, t+=3)
|
|
|
|
{
|
|
|
|
m_vertexToTriangle.AddNeighbor(triangles[t ], i);
|
|
|
|
m_vertexToTriangle.AddNeighbor(triangles[t+1], i);
|
|
|
|
m_vertexToTriangle.AddNeighbor(triangles[t+2], i);
|
|
|
|
}
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::Encode(const T * const triangles,
|
|
|
|
const unsigned long * const indexBufferIDs,
|
|
|
|
const long numTriangles,
|
|
|
|
const long numVertices,
|
|
|
|
BinaryStream & bstream)
|
|
|
|
{
|
|
|
|
assert(numVertices > 0);
|
|
|
|
assert(numTriangles > 0);
|
|
|
|
|
|
|
|
Init(triangles, numTriangles, numVertices);
|
|
|
|
unsigned char mask = 0;
|
|
|
|
bool encodeTrianglesOrder = (indexBufferIDs != 0);
|
|
|
|
|
|
|
|
|
|
|
|
if (encodeTrianglesOrder)
|
|
|
|
{
|
|
|
|
long numBufferIDs = 0;
|
|
|
|
for (long t = 0; t < numTriangles; t++)
|
|
|
|
{
|
|
|
|
if (numBufferIDs <= (long) indexBufferIDs[t])
|
|
|
|
{
|
|
|
|
++numBufferIDs;
|
|
|
|
assert(numBufferIDs <= numTriangles);
|
|
|
|
}
|
|
|
|
++m_count[indexBufferIDs[t]+1];
|
|
|
|
}
|
|
|
|
for (long i = 2; i <= numBufferIDs; i++)
|
|
|
|
{
|
|
|
|
m_count[i] += m_count[i-1];
|
|
|
|
}
|
|
|
|
mask += 2; // preserved triangles order
|
|
|
|
}
|
|
|
|
bstream.WriteUChar(mask, m_streamType);
|
|
|
|
bstream.WriteUInt32(m_maxSizeVertexToTriangle, m_streamType);
|
|
|
|
|
|
|
|
long v0;
|
|
|
|
for (long v = 0; v < m_numVertices; v++)
|
|
|
|
{
|
|
|
|
if (!m_vtags[v])
|
|
|
|
{
|
|
|
|
m_vfifo.PushBack(v);
|
|
|
|
m_vtags[v] = 1;
|
|
|
|
m_vmap[v] = m_vertexCount++;
|
|
|
|
m_invVMap[m_vmap[v]] = v;
|
|
|
|
while (m_vfifo.GetSize() > 0 )
|
|
|
|
{
|
|
|
|
v0 = m_vfifo.PopFirst();
|
|
|
|
ProcessVertex(v0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (encodeTrianglesOrder)
|
|
|
|
{
|
|
|
|
long t, prev = 0;
|
|
|
|
long pred;
|
|
|
|
for (long i = 0; i < numTriangles; ++i)
|
|
|
|
{
|
|
|
|
t = m_invTMap[i];
|
|
|
|
m_tmap[t] = m_count[ indexBufferIDs[t] ]++;
|
|
|
|
pred = m_tmap[t] - prev;
|
|
|
|
m_ctfans.PushTriangleIndex(pred);
|
|
|
|
prev = m_tmap[t] + 1;
|
|
|
|
}
|
|
|
|
for (long t = 0; t < numTriangles; ++t)
|
|
|
|
{
|
|
|
|
m_invTMap[m_tmap[t]] = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_ctfans.Save(bstream, encodeTrianglesOrder, m_streamType);
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::CompueLocalConnectivityInfo(const long focusVertex)
|
|
|
|
{
|
|
|
|
long t, v, p;
|
|
|
|
m_numNonConqueredTriangles = 0;
|
|
|
|
m_numConqueredTriangles = 0;
|
|
|
|
m_numVisitedVertices = 0;
|
|
|
|
for(long i = m_vertexToTriangle.Begin(focusVertex); i < m_vertexToTriangle.End(focusVertex); ++i)
|
|
|
|
{
|
|
|
|
t = m_vertexToTriangle.GetNeighbor(i);
|
|
|
|
|
|
|
|
if ( m_ttags[t] == 0) // non-processed triangle
|
|
|
|
{
|
|
|
|
m_nonConqueredTriangles[m_numNonConqueredTriangles] = t;
|
|
|
|
CompueOppositeEdge( focusVertex,
|
|
|
|
m_triangles + (3*t),
|
|
|
|
m_nonConqueredEdges[m_numNonConqueredTriangles*2],
|
|
|
|
m_nonConqueredEdges[m_numNonConqueredTriangles*2+1]);
|
|
|
|
++m_numNonConqueredTriangles;
|
|
|
|
}
|
|
|
|
else // triangle already processed
|
|
|
|
{
|
|
|
|
m_numConqueredTriangles++;
|
|
|
|
p = 3*t;
|
|
|
|
// extract visited vertices
|
|
|
|
for(long k = 0; k < 3; ++k)
|
|
|
|
{
|
|
|
|
v = m_triangles[p+k];
|
|
|
|
if (m_vmap[v] > m_vmap[focusVertex]) // vertices are insertices by increasing traversal order
|
|
|
|
{
|
|
|
|
bool foundOrInserted = false;
|
|
|
|
for (long j = 0; j < m_numVisitedVertices; ++j)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (m_vmap[v] == m_visitedVertices[j])
|
|
|
|
{
|
|
|
|
m_visitedVerticesValence[j]++;
|
|
|
|
foundOrInserted = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (m_vmap[v] < m_visitedVertices[j])
|
|
|
|
{
|
|
|
|
++m_numVisitedVertices;
|
|
|
|
for (long h = m_numVisitedVertices-1; h > j; --h)
|
|
|
|
{
|
|
|
|
m_visitedVertices[h] = m_visitedVertices[h-1];
|
|
|
|
m_visitedVerticesValence[h] = m_visitedVerticesValence[h-1];
|
|
|
|
}
|
|
|
|
m_visitedVertices[j] = m_vmap[v];
|
|
|
|
m_visitedVerticesValence[j] = 1;
|
|
|
|
foundOrInserted = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!foundOrInserted)
|
|
|
|
{
|
|
|
|
m_visitedVertices[m_numVisitedVertices] = m_vmap[v];
|
|
|
|
m_visitedVerticesValence[m_numVisitedVertices] = 1;
|
|
|
|
m_numVisitedVertices++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// re-order visited vertices by taking into account their valence (i.e., # of conquered triangles incident to each vertex)
|
|
|
|
// in order to avoid config. 9
|
|
|
|
if (m_numVisitedVertices > 2)
|
|
|
|
{
|
|
|
|
long y;
|
|
|
|
for(long x = 1; x < m_numVisitedVertices; ++x)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (m_visitedVerticesValence[x] == 1)
|
|
|
|
{
|
|
|
|
y = x;
|
|
|
|
while( (y > 0) && (m_visitedVerticesValence[y] < m_visitedVerticesValence[y-1]) )
|
|
|
|
{
|
|
|
|
swap(m_visitedVerticesValence[y], m_visitedVerticesValence[y-1]);
|
|
|
|
swap(m_visitedVertices[y], m_visitedVertices[y-1]);
|
|
|
|
--y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_numNonConqueredTriangles > 0)
|
|
|
|
{
|
|
|
|
// compute triangle-to-triangle adjacency information
|
|
|
|
m_triangleToTriangle.AllocateNumNeighborsArray(m_numNonConqueredTriangles);
|
|
|
|
m_triangleToTriangle.ClearNumNeighborsArray();
|
|
|
|
m_triangleToTriangleInv.AllocateNumNeighborsArray(m_numNonConqueredTriangles);
|
|
|
|
m_triangleToTriangleInv.ClearNumNeighborsArray();
|
|
|
|
long * const numNeighbors = m_triangleToTriangle.GetNumNeighborsBuffer();
|
|
|
|
long * const invNumNeighbors = m_triangleToTriangleInv.GetNumNeighborsBuffer();
|
|
|
|
for(long i = 0; i < m_numNonConqueredTriangles; ++i)
|
|
|
|
{
|
|
|
|
for(long j = i+1; j < m_numNonConqueredTriangles; ++j)
|
|
|
|
{
|
|
|
|
if (m_nonConqueredEdges[2*i+1] == m_nonConqueredEdges[2*j]) // edge i is connected to edge j
|
|
|
|
{
|
|
|
|
++numNeighbors[i];
|
|
|
|
++invNumNeighbors[j];
|
|
|
|
}
|
|
|
|
if (m_nonConqueredEdges[2*i] == m_nonConqueredEdges[2*j+1]) // edge i is connected to edge j
|
|
|
|
{
|
|
|
|
++numNeighbors[j];
|
|
|
|
++invNumNeighbors[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_triangleToTriangle.AllocateNeighborsArray();
|
|
|
|
m_triangleToTriangle.ClearNeighborsArray();
|
|
|
|
m_triangleToTriangleInv.AllocateNeighborsArray();
|
|
|
|
m_triangleToTriangleInv.ClearNeighborsArray();
|
|
|
|
for(long i = 0; i < m_numNonConqueredTriangles; ++i)
|
|
|
|
{
|
|
|
|
for(long j = 1; j < m_numNonConqueredTriangles; ++j)
|
|
|
|
{
|
|
|
|
if (m_nonConqueredEdges[2*i+1] == m_nonConqueredEdges[2*j]) // edge i is connected to edge j
|
|
|
|
{
|
|
|
|
m_triangleToTriangle.AddNeighbor(i, j);
|
|
|
|
m_triangleToTriangleInv.AddNeighbor(j, i);
|
|
|
|
}
|
|
|
|
if (m_nonConqueredEdges[2*i] == m_nonConqueredEdges[2*j+1]) // edge i is connected to edge j
|
|
|
|
{
|
|
|
|
m_triangleToTriangle.AddNeighbor(j, i);
|
|
|
|
m_triangleToTriangleInv.AddNeighbor(i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::ComputeTFANDecomposition(const long focusVertex)
|
|
|
|
{
|
|
|
|
long processedTriangles = 0;
|
|
|
|
long minNumInputEdges;
|
|
|
|
long numInputEdges;
|
|
|
|
long indexSeedTriangle;
|
|
|
|
long seedTriangle;
|
|
|
|
long currentIndex;
|
|
|
|
long currentTriangle;
|
|
|
|
long i0, i1, index;
|
|
|
|
|
|
|
|
m_tfans.Clear();
|
|
|
|
while (processedTriangles != m_numNonConqueredTriangles)
|
|
|
|
{
|
|
|
|
// find non processed triangle with lowest number of inputs
|
|
|
|
minNumInputEdges = m_numTriangles;
|
|
|
|
indexSeedTriangle = -1;
|
|
|
|
for(long i = 0; i < m_numNonConqueredTriangles; ++i)
|
|
|
|
{
|
|
|
|
numInputEdges = m_triangleToTriangleInv.GetNumNeighbors(i);
|
|
|
|
if ( !m_ttags[m_nonConqueredTriangles[i]] &&
|
|
|
|
numInputEdges < minNumInputEdges )
|
|
|
|
{
|
|
|
|
minNumInputEdges = numInputEdges;
|
|
|
|
indexSeedTriangle = i;
|
|
|
|
if (minNumInputEdges == 0) // found boundary triangle
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(indexSeedTriangle >= 0);
|
|
|
|
seedTriangle = m_nonConqueredTriangles[indexSeedTriangle];
|
|
|
|
m_tfans.AddTFAN();
|
|
|
|
m_tfans.AddVertex( focusVertex );
|
|
|
|
m_tfans.AddVertex( m_nonConqueredEdges[indexSeedTriangle*2] );
|
|
|
|
m_tfans.AddVertex( m_nonConqueredEdges[indexSeedTriangle*2 + 1] );
|
|
|
|
m_ttags[ seedTriangle ] = 1; // mark triangle as processed
|
|
|
|
m_tmap[seedTriangle] = m_triangleCount++;
|
|
|
|
m_invTMap[m_tmap[seedTriangle]] = seedTriangle;
|
|
|
|
++processedTriangles;
|
|
|
|
currentIndex = indexSeedTriangle;
|
|
|
|
currentTriangle = seedTriangle;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// find next triangle
|
|
|
|
i0 = m_triangleToTriangle.Begin(currentIndex);
|
|
|
|
i1 = m_triangleToTriangle.End(currentIndex);
|
|
|
|
currentIndex = -1;
|
|
|
|
for(long i = i0; i < i1; ++i)
|
|
|
|
{
|
|
|
|
index = m_triangleToTriangle.GetNeighbor(i);
|
|
|
|
currentTriangle = m_nonConqueredTriangles[index];
|
|
|
|
if ( !m_ttags[currentTriangle] )
|
|
|
|
{
|
|
|
|
currentIndex = index;
|
|
|
|
m_tfans.AddVertex( m_nonConqueredEdges[currentIndex*2+1] );
|
|
|
|
m_ttags[currentTriangle] = 1; // mark triangle as processed
|
|
|
|
m_tmap [currentTriangle] = m_triangleCount++;
|
|
|
|
m_invTMap[m_tmap [currentTriangle]] = currentTriangle;
|
|
|
|
++processedTriangles;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (currentIndex != -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::CompressTFAN(const long focusVertex)
|
|
|
|
{
|
|
|
|
m_ctfans.PushNumTFans(m_tfans.GetNumTFANs());
|
|
|
|
|
|
|
|
const long ntfans = m_tfans.GetNumTFANs();
|
|
|
|
long degree;
|
|
|
|
long k0, k1;
|
|
|
|
long v0;
|
|
|
|
long ops[O3DGC_MAX_TFAN_SIZE];
|
|
|
|
long indices[O3DGC_MAX_TFAN_SIZE];
|
|
|
|
|
|
|
|
long numOps;
|
|
|
|
long numIndices;
|
|
|
|
long pos;
|
|
|
|
long found;
|
|
|
|
|
|
|
|
if (m_tfans.GetNumTFANs() > 0)
|
|
|
|
{
|
|
|
|
for(long f = 0; f != ntfans; f++)
|
|
|
|
{
|
|
|
|
degree = m_tfans.GetTFANSize(f) - 1;
|
|
|
|
m_ctfans.PushDegree(degree-2+ m_numConqueredTriangles);
|
|
|
|
numOps = 0;
|
|
|
|
numIndices = 0;
|
|
|
|
k0 = 1 + m_tfans.Begin(f);
|
|
|
|
k1 = m_tfans.End(f);
|
|
|
|
for(long k = k0; k < k1; k++)
|
|
|
|
{
|
|
|
|
v0 = m_tfans.GetVertex(k);
|
|
|
|
if (m_vtags[v0] == 0)
|
|
|
|
{
|
|
|
|
ops[numOps++] = 0;
|
|
|
|
m_vtags[v0] = 1;
|
|
|
|
m_vmap[v0] = m_vertexCount++;
|
|
|
|
m_invVMap[m_vmap[v0]] = v0;
|
|
|
|
m_vfifo.PushBack(v0);
|
|
|
|
m_visitedVertices[m_numVisitedVertices++] = m_vmap[v0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ops[numOps++] = 1;
|
|
|
|
pos = 0;
|
|
|
|
found = 0;
|
|
|
|
for(long u=0; u < m_numVisitedVertices; ++u)
|
|
|
|
{
|
|
|
|
pos++;
|
|
|
|
if (m_visitedVertices[u] == m_vmap[v0])
|
|
|
|
{
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found == 1)
|
|
|
|
{
|
|
|
|
indices[numIndices++] = -pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
indices[numIndices++] = m_vmap[v0] - m_vmap[focusVertex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
|
|
if (IsCase0(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 1000001 vertices: -1 -2
|
|
|
|
m_ctfans.PushConfig(0);
|
|
|
|
}
|
|
|
|
else if (IsCase1(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 1xxxxxx1 vertices: -1 x x x x x -2
|
|
|
|
long u = 1;
|
|
|
|
for(u = 1; u < degree-1; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushOperation(ops[u]);
|
|
|
|
}
|
|
|
|
for(u =1; u < numIndices-1; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushIndex(indices[u]);
|
|
|
|
}
|
|
|
|
m_ctfans.PushConfig(1);
|
|
|
|
}
|
|
|
|
else if (IsCase2(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 00000001 vertices: -1
|
|
|
|
m_ctfans.PushConfig(2);
|
|
|
|
}
|
|
|
|
else if (IsCase3(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 00000001 vertices: -2
|
|
|
|
m_ctfans.PushConfig(3);
|
|
|
|
}
|
|
|
|
else if (IsCase4(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 10000000 vertices: -1
|
|
|
|
m_ctfans.PushConfig(4);
|
|
|
|
}
|
|
|
|
else if (IsCase5(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 10000000 vertices: -2
|
|
|
|
m_ctfans.PushConfig(5);
|
|
|
|
}
|
|
|
|
else if (IsCase6(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 00000000 vertices:
|
|
|
|
m_ctfans.PushConfig(6);
|
|
|
|
}
|
|
|
|
else if (IsCase7(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 1000001 vertices: -1 -2
|
|
|
|
m_ctfans.PushConfig(7);
|
|
|
|
}
|
|
|
|
else if (IsCase8(degree, numIndices, ops, indices))
|
|
|
|
{
|
|
|
|
// ops: 1xxxxxx1 vertices: -2 x x x x x -1
|
|
|
|
long u = 1;
|
|
|
|
for(u =1; u < degree-1; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushOperation(ops[u]);
|
|
|
|
}
|
|
|
|
for(u =1; u < numIndices-1; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushIndex(indices[u]);
|
|
|
|
}
|
|
|
|
m_ctfans.PushConfig(8);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
long u = 0;
|
|
|
|
for(u =0; u < degree; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushOperation(ops[u]);
|
|
|
|
}
|
|
|
|
for(u =0; u < numIndices; u++)
|
|
|
|
{
|
|
|
|
m_ctfans.PushIndex(indices[u]);
|
|
|
|
}
|
|
|
|
m_ctfans.PushConfig(9);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
template <class T>
|
|
|
|
O3DGCErrorCode TriangleListEncoder<T>::ProcessVertex(const long focusVertex)
|
|
|
|
{
|
|
|
|
CompueLocalConnectivityInfo(focusVertex);
|
|
|
|
ComputeTFANDecomposition(focusVertex);
|
|
|
|
CompressTFAN(focusVertex);
|
|
|
|
return O3DGC_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif //O3DGC_TRIANGLE_LIST_ENCODER_INL
|