Files
FC1/CryAnimation/CryCharDecalBuilder.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

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;
}