339 lines
11 KiB
C++
339 lines
11 KiB
C++
#include "stdafx.h"
|
|
#include "CryGeometryInfo.h"
|
|
#include "MathUtils.h"
|
|
#include "CryCharDecalBuilder.h"
|
|
|
|
#include "CVars.h"
|
|
|
|
CryCharDecalBuilder::CryCharDecalBuilder (CryEngineDecalInfo& rDecal, CryGeometryInfo* pGeometry, const Vec3d* pVertices):
|
|
m_rDecal (rDecal),
|
|
m_pGeometry (pGeometry),
|
|
m_pSkinVertices (pVertices)
|
|
{
|
|
// transform the bullet position to the local coordinates
|
|
m_ptSourceLCS = rDecal.vPos;
|
|
|
|
// // normalize the direction vector, if it's present
|
|
float dHitDirectionLength = rDecal.vHitDirection.Length();
|
|
if (dHitDirectionLength > 0.01)
|
|
{
|
|
// we need the hit direction and source in the LCS to project the character
|
|
m_ptHitDirectionLCS = rDecal.vHitDirection / dHitDirectionLength;
|
|
|
|
m_ptSourceLCS = m_ptSourceLCS;
|
|
|
|
// to project the character in the bullet CS, we need some fake TM (fake because we just choose any X and Y axes)
|
|
// Z axis looks in the direction of the hit
|
|
BuildMatrixFromFwdZRot (m_ptHitDirectionLCS, m_ptSourceLCS, rDecal.fAngle, m_matBullet);
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
m_matBullet(2,i) *= 2.0f;
|
|
m_matInvBullet = GetInverted44(m_matBullet);
|
|
|
|
initVerticesBCS();
|
|
//initVertexDistances();
|
|
|
|
findParticipatingFaces();
|
|
}
|
|
else
|
|
{
|
|
g_GetLog()->LogWarning ("\003CryCharDecalBuilder::CryCharDecalBuilder: hit has unknown direction");
|
|
}
|
|
}
|
|
|
|
// calculate distances to each vertex
|
|
void CryCharDecalBuilder::initVertexDistances()
|
|
{
|
|
m_arrVertexDistance.reinit (m_pGeometry->numExtToIntMapEntries());
|
|
|
|
// the index of the vertex nearest to the hit (in External indexation)
|
|
m_nNearestVertex = 0;
|
|
float fNearestVertexDistance2 = 0;
|
|
for (unsigned nVertex = 0; nVertex < m_pGeometry->numExtToIntMapEntries(); ++nVertex)
|
|
{
|
|
const Vec3d& ptVertex = m_pSkinVertices[nVertex];
|
|
float fDistance2 = m_arrVertexDistance[nVertex] = GetSquaredDistance(ptVertex,m_ptSourceLCS);
|
|
if (!nVertex || fNearestVertexDistance2 > fDistance2)
|
|
{
|
|
fNearestVertexDistance2 = fDistance2;
|
|
m_nNearestVertex = nVertex;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// find the vertices that participate in decal generation and add them to the array of vertices
|
|
// Uses m_arrVerticesBCS
|
|
void CryCharDecalBuilder::findParticipatingFaces()
|
|
{
|
|
m_arrDecalVertexMapping.reinit (m_pGeometry->numUsedVertices(), -1);
|
|
float fSize2 = g_GetCVars()->ca_EnableDecals() == 2 ? sqr(m_rDecal.fSize*3) : m_rDecal.fSize;
|
|
|
|
// find the faces to which the distance near enough
|
|
for (unsigned nFace = 0; nFace < m_pGeometry->numFaces(); ++nFace)
|
|
{
|
|
GeomFace Face = m_pGeometry->getFace(nFace);
|
|
// vertices belonging to the face, in external indexation
|
|
|
|
float fTriangleDistance = GetDistanceToTriangleBCS (Face);
|
|
if (fTriangleDistance > -fSize2 && fTriangleDistance <= fSize2)
|
|
{
|
|
// the triangle participates
|
|
addFaceBCS (Face);
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns the distance to the given 2D point in the plane xy
|
|
// from the point (0,0)
|
|
double distanceToPoint2Dxy (const Vec3d& v)
|
|
{
|
|
return sqrt(sqr((double)v.x)+sqr((double)v.y));
|
|
}
|
|
|
|
// returns the distance to the given 2D line in the plane xy
|
|
// from the point (0,0). The line is FINITE, and limited by v0 and v1
|
|
// the hints are the distances to the corresponding vertices
|
|
float distanceToLine2DxyHint (const Vec3d& v0, float d0, const Vec3d& v1, float d1)
|
|
{
|
|
// the coefficients of the line equation x -> ax + bx t, y -> ay + by t
|
|
float dx = v1.x - v0.x, dy = v1.y - v0.y;
|
|
double f2 = dx*(double)dx+dy*(double)dy;
|
|
if (f2 < 0.005)
|
|
return (d0+d1)/2;
|
|
|
|
// the point of intersection of the normal from 00 with the line, 0 means v0, 1 means v1, > 1 means beyond v1, <0 means before v0
|
|
double t = -(v0.x*dx + v0.y*dy)/f2;
|
|
|
|
if (t <= 0)
|
|
{
|
|
float fDistance = d0;
|
|
assert (d1 > fDistance);
|
|
return fDistance;
|
|
}
|
|
else
|
|
if (t >= 1)
|
|
{
|
|
float fDistance = d1;
|
|
assert (d0 > fDistance);
|
|
return fDistance;
|
|
}
|
|
else
|
|
{
|
|
float fDistance = (float)fabs((v0.x*dy-v0.y*dx) / sqrt (f2));
|
|
assert (fabs(fDistance - (float)fabs((v1.x*dy-v1.y*dx) / sqrt (f2))) < 1e-3);
|
|
return fDistance;
|
|
}
|
|
}
|
|
|
|
// returns the distance to the given 2D line in the plane xy
|
|
// from the point (0,0). The line is FINITE, and limited by v0 and v1
|
|
float distanceToLine2Dxy (const Vec3d& v0, const Vec3d& v1)
|
|
{
|
|
// the coefficients of the line equation x -> v0.x + dx t, y -> v0.y + dy t
|
|
float dx = v1.x - v0.x, dy = v1.y - v0.y;
|
|
double f2 = dx*(double)dx+dy*(double)dy;
|
|
if (f2 < 0.005)
|
|
// distance to the middle of the interval
|
|
return float(cry_sqrtf(sqr(v0.x+v1.x)+sqr(v0.y+v1.y))/2);
|
|
|
|
// the point of intersection of the normal from 00 with the line, 0 means v0, 1 means v1, > 1 means beyond v1, <0 means before v0
|
|
double t = -(v0.x*dx + v0.y*dy)/f2;
|
|
assert (fabs ((v0.x+dx*t)*dx+(v0.y+dy*t)*dy) < cry_sqrtf(sqr(v0.x+v1.x)+sqr(v0.y+v1.y))/200000);
|
|
|
|
if (t <= 0)
|
|
{
|
|
double fDistance = distanceToPoint2Dxy(v0);
|
|
assert (distanceToPoint2Dxy(v1) > fDistance);
|
|
return (float)fDistance;
|
|
}
|
|
else
|
|
if (t >= 1)
|
|
{
|
|
double fDistance = distanceToPoint2Dxy(v1);
|
|
assert (distanceToPoint2Dxy(v0) > fDistance);
|
|
return (float)fDistance;
|
|
}
|
|
else
|
|
{
|
|
float fDistance = (float)fabs((v0.x*dy-v0.y*dx) / sqrt (f2));
|
|
assert (fabs(fDistance - (float)fabs((v1.x*dy-v1.y*dx) / sqrt (f2))) < 1e-3);
|
|
return fDistance;
|
|
}
|
|
}
|
|
|
|
// returns the distance to a very thin triangle - that is,
|
|
// it's so thin that it's hard to determine whether it contains 0 or not,
|
|
// but it shouldn't really matter because it's possible to determine the
|
|
// distance to each of the sides (and if some side is degenerate, then
|
|
// to the vertices of that side)
|
|
float distanceToThinTriangle (const Vec3d v[3])
|
|
{
|
|
float d[3] = {
|
|
(float)distanceToPoint2Dxy(v[0]),
|
|
(float)distanceToPoint2Dxy(v[1]),
|
|
(float)distanceToPoint2Dxy(v[2])
|
|
};
|
|
return min3 (distanceToLine2DxyHint (v[0],d[0],v[1],d[1]), distanceToLine2DxyHint (v[1],d[1],v[2],d[2]), distanceToLine2DxyHint (v[2],d[2],v[0],d[0]));
|
|
}
|
|
|
|
// returns the distance of the given triangle from the origin (bullet)
|
|
float CryCharDecalBuilder::GetDistanceToTriangleBCS (GeomFace nVertex)
|
|
{
|
|
Vec3d v[3] = {m_arrVerticesBCS[nVertex[0]], m_arrVerticesBCS[nVertex[1]], m_arrVerticesBCS[nVertex[2]]};
|
|
|
|
if (g_GetCVars()->ca_EnableDecals() == 2)
|
|
return sqr(v[0].x)+sqr(v[0].y)+sqr(v[1].x)+sqr(v[1].y)+sqr(v[2].x)+sqr(v[2].y);
|
|
|
|
// calculate the barycentric coordinates of the triangle
|
|
float b0 = (v[1].x - v[0].x) * (v[2].y - v[0].y) - (v[2].x - v[0].x) * (v[1].y - v[0].y);
|
|
|
|
// the triangle either back-faces the bullet or has a negligible area
|
|
// - just return a negative not to use this triangle
|
|
if (g_GetCVars()->ca_PerforatingDecals())
|
|
{
|
|
if (fabs(b0) < 1e-3) // should be b0 < 1e-4 to return to the previous version
|
|
// return -100000;
|
|
return distanceToThinTriangle(v);
|
|
}
|
|
else
|
|
{
|
|
if (b0 < 1e-3)
|
|
return -100000;
|
|
}
|
|
|
|
float b1 = ( v[1].x * v[2].y - v[2].x * v[1].y ) / b0 ;
|
|
float b2 = ( v[2].x * v[0].y - v[0].x * v[2].y ) / b0 ;
|
|
float b3 = ( v[0].x * v[1].y - v[1].x * v[0].y ) / b0 ;
|
|
|
|
if (fabs(b1+b2+b3-1) > 0.03f)
|
|
// this is some almost degenerate triangle, or something else is unstable
|
|
if (g_GetCVars()->ca_PerforatingDecals())
|
|
return distanceToThinTriangle(v);
|
|
else
|
|
return -100000;
|
|
|
|
//assert (fabs(b1+b2+b3-1) < 0.01f);
|
|
#ifdef _DEBUG
|
|
// the reconstructed by the barycentric coordinates point
|
|
//Vec3d ptReconstructed = b1 * v[0] + b2 * v[1] + b3 * v[2];
|
|
//assert (ptReconstructed.x < 1e-7/b0 && ptReconstructed.x > -1e-7/b0 && ptReconstructed.y < 1e-7/b0 && ptReconstructed.y > -1e-7/b0);
|
|
#endif
|
|
// b1, b2 and b3 are the barycentric coordinates of the point 0
|
|
// if they're all > 0, the point lies inside the triangle (triangle is on the way of the bullet
|
|
|
|
if (b1 > 0)
|
|
{
|
|
if (b2 > 0)
|
|
{
|
|
if (b3 > 0)
|
|
{
|
|
// the triangle intersects Oz
|
|
// Determine the intersection point - this will be the distance
|
|
// the triangle faces the bullet - calculate the point of intersection
|
|
return 0;//b1*v[0].z+b2*v[1].z+b3*v[2].z;
|
|
}
|
|
else
|
|
{
|
|
// 0 lies outside the edge v0-v1, so we should calculate the distance to this line
|
|
return distanceToLine2Dxy (v[0],v[1]);
|
|
return max2(distanceToLine2Dxy (v[0],v[1]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b3 > 0)
|
|
{
|
|
// 0 lies outside the edge v0-v2, so we should calculate the distance to this line
|
|
return distanceToLine2Dxy (v[0],v[2]);
|
|
return max2(distanceToLine2Dxy (v[0],v[2]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
else
|
|
{
|
|
// 0 lies beside v0
|
|
return (float)distanceToPoint2Dxy (v[0]);
|
|
return max2((float)distanceToPoint2Dxy (v[0]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b2 > 0)
|
|
{
|
|
if (b3 > 0)
|
|
{
|
|
// 0 lies outside the edge v1-v2, so we should calculate the distance to this line
|
|
return distanceToLine2Dxy(v[1],v[2]);
|
|
return max2(distanceToLine2Dxy(v[1],v[2]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
else
|
|
{
|
|
// 0 lies beside v[1]
|
|
return (float)distanceToPoint2Dxy(v[1]);
|
|
return max2((float)distanceToPoint2Dxy(v[1]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert (b3 > 0);
|
|
// 0 lies beside v2
|
|
return (float)distanceToPoint2Dxy(v[2]);
|
|
return max2((float)distanceToPoint2Dxy(v[2]), min3(v[0].z,v[1].z,v[2].z));
|
|
}
|
|
}
|
|
}
|
|
|
|
// initializes the VerticesBCS array - vertices in the bullet coordinate system
|
|
void CryCharDecalBuilder::initVerticesBCS()
|
|
{
|
|
unsigned numVertices = m_pGeometry->numVertices();
|
|
m_arrVerticesBCS.reinit (numVertices);
|
|
for (unsigned i = 0; i < numVertices; ++i)
|
|
{
|
|
m_arrVerticesBCS[i] = m_matInvBullet.TransformPointOLD(m_pSkinVertices[i]);
|
|
}
|
|
}
|
|
|
|
|
|
// adds the face, with the vertices if needed
|
|
// in Character Coordinate System
|
|
void CryCharDecalBuilder::addFaceCCS (int nVertex[3], Vec3d vVertices[3])
|
|
{
|
|
m_arrDecalFaces.push_back (CryCharDecalFace(addVertexCCS(nVertex[0], vVertices[0]), addVertexCCS(nVertex[1], vVertices[1]), addVertexCCS(nVertex[2], vVertices[2])));
|
|
}
|
|
|
|
void CryCharDecalBuilder::addFaceBCS (CryCharDecalFace GeomIntFace)
|
|
{
|
|
m_arrDecalFaces.push_back (CryCharDecalFace(addVertexBCS(GeomIntFace[0]), addVertexBCS(GeomIntFace[1]), addVertexBCS(GeomIntFace[2])));
|
|
}
|
|
|
|
|
|
// maps the vertex and returns the internal index
|
|
unsigned CryCharDecalBuilder::addVertexCCS (int nVertexExtIndex, const Vec3d& vVertex)
|
|
{
|
|
int& nVertexNewIndex = m_arrDecalVertexMapping[nVertexExtIndex];
|
|
if (nVertexNewIndex == -1)
|
|
{
|
|
// the vertex in bullet coordinates will be the new UVs
|
|
Vec3d vUVW = m_matInvBullet.TransformPointOLD(vVertex);
|
|
m_arrDecalVertices.push_back (CryCharDecalVertex (nVertexExtIndex, (vUVW.x/m_rDecal.fSize+1)*0.5f, (vUVW.y/m_rDecal.fSize+1)*0.5f));
|
|
nVertexNewIndex = int(m_arrDecalVertices.size()-1);
|
|
}
|
|
|
|
return nVertexNewIndex;
|
|
}
|
|
|
|
unsigned CryCharDecalBuilder::addVertexBCS (int nSkinVertexIndex)
|
|
{
|
|
int& nVertexNewIndex = m_arrDecalVertexMapping[nSkinVertexIndex];
|
|
if (nVertexNewIndex == -1)
|
|
{
|
|
// the vertex in bullet coordinates will be the new UVs
|
|
const Vec3d& vUVW = m_arrVerticesBCS[nSkinVertexIndex];
|
|
m_arrDecalVertices.push_back (CryCharDecalVertex (nSkinVertexIndex, (vUVW.x/m_rDecal.fSize+1)*0.5f, (vUVW.y/m_rDecal.fSize+1)*0.5f));
|
|
nVertexNewIndex = int(m_arrDecalVertices.size()-1);
|
|
}
|
|
|
|
return nVertexNewIndex;
|
|
}
|