123
This commit is contained in:
496
Cry3DEngine/StencilShadowEdgeDetector.cpp
Normal file
496
Cry3DEngine/StencilShadowEdgeDetector.cpp
Normal file
@@ -0,0 +1,496 @@
|
||||
#include "stdafx.h"
|
||||
#include "StencilShadowConnectivity.h"
|
||||
#include "StencilShadowEdgeDetector.h"
|
||||
|
||||
CStencilShadowEdgeDetector::CStencilShadowEdgeDetector():
|
||||
m_pConnectivity(NULL),
|
||||
m_pModelVertices(NULL)
|
||||
{
|
||||
m_bBitFieldIsSet=false;
|
||||
}
|
||||
|
||||
// Re-construct the whole object
|
||||
// Use this object to reuse the EdgeDetector object multiple times for different
|
||||
// connectivities and vertices. This can save on reallocating internal arrays this object uses at run time
|
||||
void CStencilShadowEdgeDetector::reinit (
|
||||
const IStencilShadowConnectivity* pConnectivity,
|
||||
const Vec3d* pDeformedVertices
|
||||
)
|
||||
{
|
||||
assert(pConnectivity);
|
||||
|
||||
m_pConnectivity = pConnectivity->GetInternalRepresentation();
|
||||
m_pModelVertices = pDeformedVertices;
|
||||
|
||||
if (!m_pModelVertices || m_pConnectivity->IsStandalone())
|
||||
m_pModelVertices = m_pConnectivity->getVertices();
|
||||
|
||||
assert (pDeformedVertices);
|
||||
|
||||
m_arrShadowEdges.clear();
|
||||
m_arrShadowFaces.clear();
|
||||
m_numShadowEdgeVertices = 0;
|
||||
}
|
||||
|
||||
CStencilShadowEdgeDetector::~CStencilShadowEdgeDetector(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// fills in the internal array of faces and vertices
|
||||
// - detected shadow faces for the given light source and model
|
||||
void CStencilShadowEdgeDetector::detectShadowFaces ()
|
||||
{
|
||||
assert(m_pConnectivity);
|
||||
if(!m_pConnectivity)
|
||||
{
|
||||
#if !defined(LINUX)
|
||||
Warning(0,0,"CStencilShadowEdgeDetector::detectShadowFaces: !m_pConnectivity");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_arrFaceOrientations.empty())
|
||||
{
|
||||
#if !defined(LINUX)
|
||||
Warning(0,0,"CStencilShadowEdgeDetector::detectShadowFaces: m_arrFaceOrientations.empty()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!m_arrFaceOrientations.empty());
|
||||
|
||||
// scan all faces and fill the backfacing ones
|
||||
unsigned nNumFaces = m_pConnectivity->numFaces();
|
||||
|
||||
unsigned* pFaceOrientBits = m_arrFaceOrientations.begin();
|
||||
for (unsigned nFace = 0; nFace < nNumFaces; ++pFaceOrientBits)
|
||||
{
|
||||
unsigned nOrientBit = 1;
|
||||
do
|
||||
{
|
||||
if ((*pFaceOrientBits & nOrientBit) == 0)
|
||||
{
|
||||
// the face is back-facing the light
|
||||
const CStencilShadowConnectivity::Face& face = m_pConnectivity->getFace(nFace);
|
||||
// revert orientation of the face 'cause the volumizer expects light-facing capping faces
|
||||
AddFace(face.getVertex(0), face.getVertex(2), face.getVertex(1));
|
||||
}
|
||||
}
|
||||
while (((++nFace) < nNumFaces) && (nOrientBit <<= 1) != 0);
|
||||
}
|
||||
|
||||
m_bBitFieldIsSet=true;
|
||||
}
|
||||
|
||||
|
||||
// returns true if the given face faces the light, and false otherwise
|
||||
bool CStencilShadowEdgeDetector::IsFaceTurnedToLight (unsigned nFace, const Vec3d& vLight)
|
||||
{
|
||||
assert(m_pModelVertices);
|
||||
assert(m_pConnectivity);
|
||||
assert(nFace>=0 && nFace<m_pConnectivity->numFaces());
|
||||
|
||||
|
||||
if (m_pConnectivity->hasPlanes())
|
||||
return m_pConnectivity->getPlane(nFace).apply (vLight) > 0;
|
||||
|
||||
|
||||
const CStencilShadowConnectivity::Face& face = m_pConnectivity->getFace(nFace);
|
||||
|
||||
DWORD dwIndex[3]={ face.getVertex(0),
|
||||
face.getVertex(1),
|
||||
face.getVertex(2) };
|
||||
|
||||
const Vec3d& vFaceV0 = m_pModelVertices[dwIndex[0]];
|
||||
|
||||
Vec3d vNormal = (m_pModelVertices[dwIndex[1]] - vFaceV0) ^ (m_pModelVertices[dwIndex[2]] - vFaceV0);
|
||||
|
||||
return vNormal * (vLight - vFaceV0) > 0;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Calculates all face orientations into the bit array
|
||||
// PARAMETERS:
|
||||
// vLight - position of the light source to detect the edges for
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CStencilShadowEdgeDetector::computeFaceOrientations (Vec3d vLight)
|
||||
{
|
||||
unsigned nModelNumFaces = m_pConnectivity->numFaces();
|
||||
|
||||
// mask that masks out the bits that address the bit in an unsigned
|
||||
//const nBitIndexMask = (sizeof(m_arrFaceOrientations[0])*8) - 1;
|
||||
|
||||
// number of unsigneds to allocate in the bit array
|
||||
unsigned nBitarraySize = (nModelNumFaces + 31u) >> 5;
|
||||
if (m_arrFaceOrientations.size() < nBitarraySize)
|
||||
m_arrFaceOrientations.reinit(nBitarraySize);
|
||||
|
||||
memset (m_arrFaceOrientations.begin(), 0, nBitarraySize * 4); // neccessary because we set only the 1 bits
|
||||
|
||||
// scan through all faces, in packets of 32 faces, and pack the orientations into
|
||||
// bit array with 32-bit portions
|
||||
unsigned nNumFaces = m_pConnectivity->numFaces();
|
||||
unsigned* pFaceOrientBits = m_arrFaceOrientations.begin();
|
||||
for (unsigned nFace = 0; nFace < nNumFaces; ++pFaceOrientBits)
|
||||
{
|
||||
unsigned nOrientBit = 1;
|
||||
do
|
||||
{
|
||||
if (IsFaceTurnedToLight (nFace, vLight))
|
||||
*pFaceOrientBits |= nOrientBit;
|
||||
}
|
||||
while (((++nFace) < nNumFaces) && (nOrientBit <<= 1) != 0);
|
||||
}
|
||||
|
||||
m_bBitFieldIsSet=true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// fills in the internal array of edges and vertices with appropriate data
|
||||
// call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
// - detected shadow edges for the given light source and model
|
||||
// ALGORITHM:
|
||||
// scans each edge and determines signs of volumes formed with this edge and light source
|
||||
// on one hand and the opposite vertex on the other hand
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void CStencilShadowEdgeDetector::detectShadowEdges( void )
|
||||
{
|
||||
assert(m_bBitFieldIsSet); // call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
|
||||
// number of vertices in the original model
|
||||
unsigned nModelNumVertices = m_pConnectivity->numVertices();
|
||||
|
||||
//
|
||||
// for each edge, insert it into m_arrShadowEdges if it's boundary;
|
||||
// insert it in reverse order if it's reverse-boundary
|
||||
// insert the vertex/-ices into the m_arrVertices array if necessary
|
||||
//
|
||||
// TODO: perhaps an optimization is possible, if we detect adjacent edges
|
||||
// and then render triangle strips without indices
|
||||
//
|
||||
|
||||
unsigned nEdge;
|
||||
unsigned nEdgeCount = m_pConnectivity->numEdges();
|
||||
for (nEdge = 0; nEdge < nEdgeCount; ++nEdge)
|
||||
{
|
||||
// this is the edge to check against being boundary
|
||||
const CStencilShadowConnectivity::Edge& rEdge = m_pConnectivity->getEdge(nEdge);
|
||||
|
||||
// check the edge
|
||||
switch (CheckEdgeType (
|
||||
rEdge.getFace(0).getFaceIndex(),
|
||||
rEdge.getFace(1).getFaceIndex()
|
||||
))
|
||||
{
|
||||
case nET_Boundary:
|
||||
AddEdge (rEdge[0], rEdge[1]);
|
||||
break;
|
||||
case nET_ReverseBoundary:
|
||||
AddEdge (rEdge[1], rEdge[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// now check the orphan edges
|
||||
nEdgeCount = m_pConnectivity->numOrphanEdges();
|
||||
for (nEdge = 0; nEdge < nEdgeCount; ++nEdge)
|
||||
{
|
||||
// this is the edge to check against being boundary
|
||||
const CStencilShadowConnectivity::OrphanEdge& rEdge = m_pConnectivity->getOrphanEdge(nEdge);
|
||||
|
||||
// check the edge
|
||||
switch (CheckOrphanEdgeType (
|
||||
rEdge.getFace().getFaceIndex()
|
||||
))
|
||||
{
|
||||
case nET_Boundary:
|
||||
AddEdge (rEdge[0], rEdge[1]);
|
||||
break;
|
||||
case nET_ReverseBoundary:
|
||||
AddEdge (rEdge[1], rEdge[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_bBitFieldIsSet=false;
|
||||
|
||||
m_numShadowEdgeVertices = m_pConnectivity->numVertices();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Adds the given shadow edge to the list of boundary edges
|
||||
// PARAMETERS:
|
||||
// nVertex[2] - indices of the edge vertices in the m_pModelVertices
|
||||
// array (and in g_arrModelVtxIdxMap).
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void CStencilShadowEdgeDetector::AddEdge( vindex nVertex0, vindex nVertex1 )
|
||||
{
|
||||
// add to the list of edges
|
||||
m_arrShadowEdges.push_back(nVertex0);
|
||||
m_arrShadowEdges.push_back(nVertex1);
|
||||
}
|
||||
|
||||
|
||||
// adds the given shadow face, in the order that is passed
|
||||
void CStencilShadowEdgeDetector::AddFace( vindex nVertex0, vindex nVertex1, vindex nVertex2 )
|
||||
{
|
||||
// add to the list of faces
|
||||
m_arrShadowFaces.push_back(nVertex0);
|
||||
m_arrShadowFaces.push_back(nVertex1);
|
||||
m_arrShadowFaces.push_back(nVertex2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// checks the edge type by the face indices: assumes that the face orientation bits have been calculated already
|
||||
CStencilShadowEdgeDetector::EdgeTypeEnum
|
||||
CStencilShadowEdgeDetector::CheckEdgeType (unsigned nFace0, unsigned nFace1)
|
||||
{
|
||||
assert(m_arrFaceOrientations.size());
|
||||
|
||||
if ((m_arrFaceOrientations[nFace0>>5]&(1<<(nFace0&0x1F))) == 0)
|
||||
{
|
||||
// the primary edge face is culled against the light
|
||||
if ((m_arrFaceOrientations[nFace1>>5]&(1<<(nFace1&0x1F))) == 0)
|
||||
{
|
||||
// the opposite face is culled against the light,
|
||||
// both are back-facing the light
|
||||
return nET_Interior;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the opposite face is facing the light
|
||||
// we've got the rev-boundary edge
|
||||
return nET_ReverseBoundary;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the primary edge face is facing the light
|
||||
if ((m_arrFaceOrientations[nFace1>>5]&(1<<(nFace1&0x1F))) == 0)
|
||||
{
|
||||
// the opposite face is culled against the light,
|
||||
// we've got the boundary edge
|
||||
return nET_Boundary;
|
||||
}
|
||||
else
|
||||
{
|
||||
// both are facing the light
|
||||
return nET_Exterior;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CStencilShadowEdgeDetector::EdgeTypeEnum
|
||||
CStencilShadowEdgeDetector::CheckOrphanEdgeType (unsigned nFace0)
|
||||
{
|
||||
assert(m_arrFaceOrientations.size());
|
||||
|
||||
if ((m_arrFaceOrientations[nFace0>>5]&(1<<(nFace0&0x1F))) == 0)
|
||||
{
|
||||
// the primary edge face is culled against the light
|
||||
|
||||
// we can either ignore the face and return nET_Interior
|
||||
// or we can pretend we have a complementary face - so that the face casts
|
||||
// the shadow with both its sides. then, return nET_ReverseBoundary
|
||||
|
||||
return nET_Interior;
|
||||
//return nET_ReverseBoundary;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the primary edge face is facing the light
|
||||
// since we don't have any neighbor to check, pretend as if the neighbor were back-facing the light
|
||||
return nET_Boundary;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN64
|
||||
#pragma warning( push ) //AMD Port
|
||||
#pragma warning( disable : 4267 )
|
||||
#endif
|
||||
|
||||
const CStencilShadowEdgeDetector::vindex *CStencilShadowEdgeDetector::getShadowEdgeArray( unsigned &outiNumEdges ) const
|
||||
{
|
||||
outiNumEdges=m_arrShadowEdges.size()/2;
|
||||
|
||||
if(!outiNumEdges)return(0);
|
||||
|
||||
return(&m_arrShadowEdges[0]);
|
||||
}
|
||||
|
||||
#ifdef WIN64
|
||||
#pragma warning( pop ) //AMD Port
|
||||
#endif
|
||||
|
||||
void CStencilShadowEdgeDetector::BuildSilhuetteFromPos( const IStencilShadowConnectivity* pConnectivity, const Vec3d &invLightPos,
|
||||
const Vec3d* inpDeformedVertices )
|
||||
{
|
||||
assert(pConnectivity);
|
||||
|
||||
m_pConnectivity = pConnectivity->GetInternalRepresentation();
|
||||
m_pModelVertices = m_pConnectivity->getVertices();
|
||||
if ( !pConnectivity->IsStandalone())
|
||||
{
|
||||
if (!m_pModelVertices)
|
||||
m_pModelVertices = inpDeformedVertices;
|
||||
}
|
||||
|
||||
assert(m_pModelVertices);
|
||||
|
||||
m_arrShadowEdges.clear();
|
||||
m_arrShadowFaces.clear();
|
||||
m_numShadowEdgeVertices = 0;
|
||||
|
||||
computeFaceOrientations(invLightPos);
|
||||
|
||||
detectShadowEdges(); // call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
detectShadowFaces();
|
||||
|
||||
// assert(m_bBitFieldIsSet);
|
||||
if(!m_bBitFieldIsSet)
|
||||
Warning(0,0,"CStencilShadowEdgeDetector::BuildSilhuetteFromPos: !m_bBitFieldIsSet");
|
||||
}
|
||||
|
||||
|
||||
void CStencilShadowEdgeDetector::BuildSilhuetteFromBitfield( const IStencilShadowConnectivity* pConnectivity, const Vec3d* inpVertices )
|
||||
{
|
||||
assert(pConnectivity);
|
||||
|
||||
assert(m_bBitFieldIsSet); // call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
|
||||
m_pConnectivity = pConnectivity->GetInternalRepresentation();
|
||||
m_pModelVertices = m_pConnectivity->getVertices();
|
||||
if (!m_pModelVertices)
|
||||
m_pModelVertices = inpVertices;
|
||||
|
||||
assert(m_pModelVertices);
|
||||
|
||||
m_arrShadowEdges.clear();
|
||||
m_arrShadowFaces.clear();
|
||||
m_numShadowEdgeVertices = 0;
|
||||
|
||||
// triangle orientation bitfield has to be provided by calling getOrientationBitfield
|
||||
detectShadowEdges(); // call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
detectShadowFaces();
|
||||
|
||||
assert(m_bBitFieldIsSet); // call computeFaceOrientations(vLight) before or create the triangle orientation bitfield yourself
|
||||
}
|
||||
|
||||
|
||||
//!
|
||||
unsigned *CStencilShadowEdgeDetector::getOrientationBitfield( int iniNumTriangles )
|
||||
{
|
||||
assert(iniNumTriangles);
|
||||
|
||||
// mask that masks out the bits that address the bit in an unsigned
|
||||
//const nBitIndexMask = (sizeof(m_arrFaceOrientations[0])*8) - 1;
|
||||
|
||||
// number of unsigneds to allocate in the bit array
|
||||
unsigned nBitarraySize = (iniNumTriangles + 31u) >> 5;
|
||||
if (m_arrFaceOrientations.size() < nBitarraySize)
|
||||
m_arrFaceOrientations.reinit(nBitarraySize);
|
||||
|
||||
m_bBitFieldIsSet=true;
|
||||
|
||||
memset (m_arrFaceOrientations.begin(), 0, nBitarraySize * 4); // neccessary because we set only the 1 bits
|
||||
|
||||
return(m_arrFaceOrientations.begin());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// make up the shadow volume
|
||||
// constructs the shadow volume mesh, and puts the mesh definition into the
|
||||
// vertex buffer (vertices that define the mesh) and index buffer (triple
|
||||
// integers defining the triangular faces, counterclockwise order)
|
||||
// The size of the vertex buffer must be at least numVertices()
|
||||
// The size of the index buffer must be at least numIndices()
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
void CStencilShadowEdgeDetector::meshShadowVolume( Vec3d vLight, float fFactor, Vec3d* outpVertexBuf, unsigned short* pIndexBuf )
|
||||
{
|
||||
assert(outpVertexBuf);
|
||||
|
||||
// The Algorithm
|
||||
// We have the edges and their vertex array in m_pShadowEdges
|
||||
// For each edge, we generate a quad (2 trianges), out of the pair of vertices defining the edge:
|
||||
// take the vertex, move it away from the light (this makes up the far vertex), this will define an extruded edge -- our actual quad
|
||||
|
||||
// 1. scan through all shadow edges, insert corresponding generated vertex indices into the index buffer;
|
||||
// 2. scan through all vertices, generate far vertices (for each near vertex, there's a far vertex), fill in the vertex buffer
|
||||
unsigned nNumVerts;
|
||||
unsigned i;
|
||||
|
||||
nNumVerts = numShadowVolumeVertices()/2;
|
||||
const Vec3d* pVertices = m_pModelVertices;
|
||||
|
||||
// go thougth all compressed vertices
|
||||
{
|
||||
Vec3d vLightFac=(1.0f-fFactor)*vLight; // optimized a little
|
||||
|
||||
for(i=0;i<nNumVerts;++i)
|
||||
{
|
||||
const Vec3d& vPos = pVertices[i];
|
||||
outpVertexBuf[i] = vPos;
|
||||
outpVertexBuf[nNumVerts+i] = vPos*fFactor + vLightFac; // == (vPos - vLight) * fFactor + vLight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned short* pIndex = pIndexBuf;
|
||||
unsigned nNumEdges;
|
||||
const unsigned short* pEdgeArray = getShadowEdgeArray(nNumEdges);
|
||||
|
||||
// fill in the shadow volume quads (extruded vertices)
|
||||
for (i = 0; i < nNumEdges; ++i)
|
||||
{
|
||||
unsigned short Edge1=*pEdgeArray++,Edge2=*pEdgeArray++;
|
||||
|
||||
assert (Edge1 < nNumVerts);
|
||||
assert (Edge2 < nNumVerts);
|
||||
|
||||
*(pIndex++) = Edge2;
|
||||
*(pIndex++) = Edge1;
|
||||
*(pIndex++) = Edge1 + nNumVerts;
|
||||
|
||||
*(pIndex++) = Edge1 + nNumVerts;
|
||||
*(pIndex++) = Edge2 + nNumVerts;
|
||||
*(pIndex++) = Edge2;
|
||||
}
|
||||
|
||||
if(m_arrShadowFaces.empty())
|
||||
return; // vlad: otherwise crash
|
||||
|
||||
// fill in the shadow volume caps
|
||||
unsigned nFaceIndices;
|
||||
const unsigned short* pFaceIndex = getShadowFaceIndices(nFaceIndices);
|
||||
const unsigned short* pFaceIndexEnd = pFaceIndex + nFaceIndices;
|
||||
// fill the near cap
|
||||
memcpy (pIndex, pFaceIndex, sizeof(*pFaceIndex)*nFaceIndices);
|
||||
|
||||
pIndex += nFaceIndices;
|
||||
|
||||
// fill the far cap
|
||||
|
||||
for (; pFaceIndex < pFaceIndexEnd; pFaceIndex += 3)
|
||||
{
|
||||
// for each face..
|
||||
assert (pFaceIndex[0] < nNumVerts);
|
||||
assert (pFaceIndex[1] < nNumVerts);
|
||||
assert (pFaceIndex[2] < nNumVerts);
|
||||
|
||||
*(pIndex++) = pFaceIndex[0] + nNumVerts;
|
||||
*(pIndex++) = pFaceIndex[2] + nNumVerts;
|
||||
*(pIndex++) = pFaceIndex[1] + nNumVerts;
|
||||
}
|
||||
|
||||
assert(numShadowVolumeVertices()==nNumVerts*2);
|
||||
assert(numShadowVolumeIndices()==(int)(pIndex-pIndexBuf));
|
||||
}
|
||||
Reference in New Issue
Block a user