You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1437 lines
44 KiB
C++
1437 lines
44 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
#include "WaterVolumeRenderNode.h"
|
|
#include "VisAreas.h"
|
|
#include "MatMan.h"
|
|
#include "TimeOfDay.h"
|
|
#include <Cry_Geo.h>
|
|
#include "MathConversion.h"
|
|
|
|
#include <AzCore/Math/Plane.h>
|
|
#include <AzCore/Math/Vector2.h>
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// private triangulation code
|
|
namespace WaterVolumeRenderNodeUtils
|
|
{
|
|
template< typename T >
|
|
size_t PosOffset();
|
|
|
|
template<>
|
|
size_t PosOffset<Vec3>()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
template<>
|
|
size_t PosOffset<SVF_P3F_C4B_T2F>()
|
|
{
|
|
return offsetof(SVF_P3F_C4B_T2F, xyz);
|
|
}
|
|
|
|
|
|
template< typename T >
|
|
struct VertexAccess
|
|
{
|
|
VertexAccess(const T* pVertices, size_t numVertices)
|
|
: m_pVertices(pVertices)
|
|
, m_numVertices(numVertices)
|
|
{
|
|
}
|
|
|
|
const Vec3& operator[] (size_t idx) const
|
|
{
|
|
assert(idx < m_numVertices);
|
|
const T* pVertex = &m_pVertices[ idx ];
|
|
return *(Vec3*) ((size_t) pVertex + PosOffset<T>());
|
|
}
|
|
|
|
const size_t GetNumVertices() const
|
|
{
|
|
return m_numVertices;
|
|
}
|
|
|
|
private:
|
|
const T* m_pVertices;
|
|
size_t m_numVertices;
|
|
};
|
|
|
|
|
|
template< typename T >
|
|
float Area(const VertexAccess<T>& contour)
|
|
{
|
|
int n = contour.GetNumVertices();
|
|
float area = 0.0f;
|
|
|
|
for (int p = n - 1, q = 0; q < n; p = q++)
|
|
{
|
|
area += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
|
|
}
|
|
|
|
return area * 0.5f;
|
|
}
|
|
|
|
bool InsideTriangle(float Ax, float Ay, float Bx, float By, float Cx, float Cy, float Px, float Py)
|
|
{
|
|
float ax = Cx - Bx;
|
|
float ay = Cy - By;
|
|
float bx = Ax - Cx;
|
|
float by = Ay - Cy;
|
|
float cx = Bx - Ax;
|
|
float cy = By - Ay;
|
|
float apx = Px - Ax;
|
|
float apy = Py - Ay;
|
|
float bpx = Px - Bx;
|
|
float bpy = Py - By;
|
|
float cpx = Px - Cx;
|
|
float cpy = Py - Cy;
|
|
|
|
float aCROSSbp = ax * bpy - ay * bpx;
|
|
float cCROSSap = cx * apy - cy * apx;
|
|
float bCROSScp = bx * cpy - by * cpx;
|
|
|
|
const float fEpsilon = -FLT_EPSILON;
|
|
return (aCROSSbp >= fEpsilon) && (bCROSScp >= fEpsilon) && (cCROSSap >= fEpsilon);
|
|
};
|
|
|
|
template< typename T, typename S >
|
|
bool Snip(const VertexAccess<T>& contour, int u, int v, int w, int n, const S* V)
|
|
{
|
|
float Ax = contour[V[u]].x;
|
|
float Ay = contour[V[u]].y;
|
|
|
|
float Bx = contour[V[v]].x;
|
|
float By = contour[V[v]].y;
|
|
|
|
float Cx = contour[V[w]].x;
|
|
float Cy = contour[V[w]].y;
|
|
|
|
if ((((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax))) < 1e-6f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int p = 0; p < n; p++)
|
|
{
|
|
if ((p == u) || (p == v) || (p == w))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float Px = contour[V[p]].x;
|
|
float Py = contour[V[p]].y;
|
|
|
|
if (WaterVolumeRenderNodeUtils::InsideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
template< typename T, typename S >
|
|
bool Triangulate(const VertexAccess<T>& contour, std::vector<S>& result)
|
|
{
|
|
// reset result
|
|
result.resize(0);
|
|
|
|
//C6255: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead
|
|
PREFAST_SUPPRESS_WARNING(6255)
|
|
// allocate and initialize list of vertices in polygon
|
|
int n = contour.GetNumVertices();
|
|
if (n < 3)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
S* V = (S*) alloca(n * sizeof(S));
|
|
|
|
// we want a counter-clockwise polygon in V
|
|
if (0.0f < Area(contour))
|
|
{
|
|
for (int v = 0; v < n; v++)
|
|
{
|
|
V[v] = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int v = 0; v < n; v++)
|
|
{
|
|
V[v] = (n - 1) - v;
|
|
}
|
|
}
|
|
|
|
int nv = n;
|
|
|
|
// remove nv-2 vertices, creating 1 triangle every time
|
|
int count = 2 * nv; // error detection
|
|
|
|
for (int m = 0, v = nv - 1; nv > 2; )
|
|
{
|
|
// if we loop, it is probably a non-simple polygon
|
|
if (0 >= (count--))
|
|
{
|
|
return false; // ERROR - probably bad polygon!
|
|
}
|
|
// three consecutive vertices in current polygon, <u,v,w>
|
|
int u = v;
|
|
if (nv <= u)
|
|
{
|
|
u = 0; // previous
|
|
}
|
|
v = u + 1;
|
|
if (nv <= v)
|
|
{
|
|
v = 0; // new v
|
|
}
|
|
int w = v + 1;
|
|
if (nv <= w)
|
|
{
|
|
w = 0; // next
|
|
}
|
|
if (Snip(contour, u, v, w, nv, V))
|
|
{
|
|
// true names of the vertices
|
|
PREFAST_SUPPRESS_WARNING(6385)
|
|
S a = V[u];
|
|
S b = V[v];
|
|
S c = V[w];
|
|
|
|
// output triangle
|
|
result.push_back(a);
|
|
result.push_back(b);
|
|
result.push_back(c);
|
|
|
|
m++;
|
|
|
|
// remove v from remaining polygon
|
|
for (int s = v, t = v + 1; t < nv; s++, t++)
|
|
{
|
|
PREFAST_SUPPRESS_WARNING(6386)
|
|
V[s] = V[t];
|
|
}
|
|
|
|
nv--;
|
|
|
|
// reset error detection counter
|
|
count = 2 * nv;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// helpers
|
|
|
|
inline static Vec3 MapVertexToFogPlane(const Vec3& v, const Plane& p)
|
|
{
|
|
const Vec3 projDir(0, 0, 1);
|
|
float perpdist = p | v;
|
|
float cosine = p.n | projDir;
|
|
assert(fabs(cosine) > 1e-4);
|
|
float pd_c = -perpdist / cosine;
|
|
return v + projDir * pd_c;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CWaterVolumeRenderNode implementation
|
|
|
|
CWaterVolumeRenderNode::CWaterVolumeRenderNode()
|
|
: m_volumeType(IWaterVolumeRenderNode::eWVT_Unknown)
|
|
, m_volumeID(~0)
|
|
, m_volumeDepth(0)
|
|
, m_streamSpeed(0)
|
|
, m_wvParams()
|
|
, m_pMaterial(0)
|
|
, m_pWaterBodyIntoMat(0)
|
|
, m_pWaterBodyOutofMat(0)
|
|
, m_pSerParams(0)
|
|
, m_pPhysAreaInput(0)
|
|
, m_pPhysArea(0)
|
|
, m_waterSurfaceVertices()
|
|
, m_waterSurfaceIndices()
|
|
, m_parentEntityWorldTM()
|
|
, m_nLayerId(0)
|
|
, m_fogDensity(0)
|
|
, m_fogColor(0.2f, 0.5f, 0.7f)
|
|
, m_fogColorAffectedBySun(true)
|
|
, m_fogPlane(Vec3(0, 0, 1), 0)
|
|
, m_fogPlaneBase(Vec3(0, 0, 1), 0)
|
|
, m_fogShadowing(0.5f)
|
|
, m_center(0, 0, 0)
|
|
, m_WSBBox(Vec3(-1, -1, -1), Vec3(1, 1, 1))
|
|
, m_capFogAtVolumeDepth(false)
|
|
, m_caustics(true)
|
|
, m_causticIntensity(1.0f)
|
|
, m_causticTiling(1.0f)
|
|
, m_causticHeight(0.5f)
|
|
, m_attachedToEntity(false)
|
|
{
|
|
m_pWaterBodyIntoMat = GetMatMan()->LoadMaterial("EngineAssets/Materials/Fog/WaterFogVolumeInto", false);
|
|
m_pWaterBodyOutofMat = GetMatMan()->LoadMaterial("EngineAssets/Materials/Fog/WaterFogVolumeOutof", false);
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_pVolumeRE[i] = static_cast<CREWaterVolume*>(GetRenderer()->EF_CreateRE(eDATA_WaterVolume));
|
|
if (m_pVolumeRE[i])
|
|
{
|
|
m_pVolumeRE[i]->m_drawWaterSurface = false;
|
|
m_pVolumeRE[i]->m_pParams = &m_wvParams[i];
|
|
}
|
|
}
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_pSurfaceRE[i] = static_cast<CREWaterVolume*>(GetRenderer()->EF_CreateRE(eDATA_WaterVolume));
|
|
if (m_pSurfaceRE[i])
|
|
{
|
|
m_pSurfaceRE[i]->m_drawWaterSurface = true;
|
|
m_pSurfaceRE[i]->m_pParams = &m_wvParams[i];
|
|
}
|
|
}
|
|
|
|
m_parentEntityWorldTM.SetIdentity();
|
|
m_vOffset = Vec3(ZERO);
|
|
}
|
|
|
|
|
|
CWaterVolumeRenderNode::~CWaterVolumeRenderNode()
|
|
{
|
|
Dephysicalize();
|
|
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_pVolumeRE[i]->Release(true);
|
|
m_pVolumeRE[i] = 0;
|
|
m_pSurfaceRE[i]->Release(true);
|
|
m_pSurfaceRE[i] = 0;
|
|
}
|
|
SAFE_DELETE(m_pSerParams);
|
|
SAFE_DELETE(m_pPhysAreaInput);
|
|
|
|
Get3DEngine()->FreeRenderNodeState(this);
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetAreaAttachedToEntity()
|
|
{
|
|
m_attachedToEntity = true;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetFogDensity(float fogDensity)
|
|
{
|
|
m_fogDensity = fogDensity;
|
|
}
|
|
|
|
|
|
float CWaterVolumeRenderNode::GetFogDensity() const
|
|
{
|
|
return m_fogDensity;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetFogColor(const Vec3& fogColor)
|
|
{
|
|
m_fogColor = fogColor;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetFogColorAffectedBySun(bool enable)
|
|
{
|
|
m_fogColorAffectedBySun = enable;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetFogShadowing(float fogShadowing)
|
|
{
|
|
m_fogShadowing = fogShadowing;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetCapFogAtVolumeDepth(bool capFog)
|
|
{
|
|
m_capFogAtVolumeDepth = capFog;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetVolumeDepth(float volumeDepth)
|
|
{
|
|
m_volumeDepth = volumeDepth;
|
|
|
|
UpdateBoundingBox();
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetStreamSpeed(float streamSpeed)
|
|
{
|
|
m_streamSpeed = streamSpeed;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetCaustics(bool caustics)
|
|
{
|
|
m_caustics = caustics;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetCausticIntensity(float causticIntensity)
|
|
{
|
|
m_causticIntensity = causticIntensity;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetCausticTiling(float causticTiling)
|
|
{
|
|
m_causticTiling = causticTiling;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetCausticHeight(float causticHeight)
|
|
{
|
|
m_causticHeight = causticHeight;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CreateOcean([[maybe_unused]] uint64 volumeID, [[maybe_unused]] /* TBD */ bool keepSerializationParams)
|
|
{
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CreateArea(uint64 volumeID, const Vec3* pVertices, unsigned int numVertices, const Vec2& surfUVScale, const Plane& fogPlane, bool keepSerializationParams, int nSID)
|
|
{
|
|
const bool serializeWith3DEngine = keepSerializationParams && !IsAttachedToEntity();
|
|
|
|
assert(fabs(fogPlane.n.GetLengthSquared() - 1.0f) < 1e-4 && "CWaterVolumeRenderNode::CreateArea(...) -- Fog plane normal doesn't have unit length!");
|
|
assert(fogPlane.n.Dot(Vec3(0, 0, 1)) > 1e-4f && "CWaterVolumeRenderNode::CreateArea(...) -- Invalid fog plane specified!");
|
|
if (fogPlane.n.Dot(Vec3(0, 0, 1)) <= 1e-4f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(numVertices >= 3);
|
|
if (numVertices < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_volumeID = volumeID;
|
|
m_fogPlane = fogPlane;
|
|
m_fogPlaneBase = fogPlane;
|
|
m_volumeType = IWaterVolumeRenderNode::eWVT_Area;
|
|
|
|
// copy volatile creation params to be able to serialize water volume if needed (only in editor)
|
|
if (serializeWith3DEngine)
|
|
{
|
|
CopyVolatileAreaSerParams(pVertices, numVertices, surfUVScale);
|
|
}
|
|
|
|
// remove form 3d engine
|
|
Get3DEngine()->UnRegisterEntityAsJob(this);
|
|
|
|
// Edges pre-pass - break into smaller edges, in case distance threshold too big
|
|
PodArray< Vec3 > pTessVertList;
|
|
PodArray< SVF_P3F_C4B_T2F > pVertsTemp;
|
|
PodArray< uint16 > pIndicesTemp;
|
|
|
|
for (uint32 v(0); v < numVertices; ++v)
|
|
{
|
|
Vec3 in_a = pVertices[v];
|
|
Vec3 in_b = (v < numVertices - 1) ? pVertices[v + 1] : pVertices[0]; // close mesh
|
|
|
|
Vec3 vAB = in_b - in_a;
|
|
float fLenAB = vAB.len();
|
|
vAB.normalize();
|
|
|
|
pTessVertList.push_back(in_a);
|
|
|
|
const float fLenThreshold = 100.0f; // break every 100 meters
|
|
Vec3 vNewVert = Vec3(in_a + (vAB * fLenThreshold));
|
|
while (fLenAB > fLenThreshold)
|
|
{
|
|
pTessVertList.push_back(vNewVert);
|
|
|
|
vNewVert = Vec3(vNewVert + (vAB * fLenThreshold));
|
|
vAB = in_b - vNewVert;
|
|
fLenAB = vAB.len();
|
|
vAB.normalize();
|
|
}
|
|
}
|
|
|
|
m_waterSurfaceVertices.resize(pTessVertList.size());
|
|
for (uint32 i = 0; i < pTessVertList.size(); ++i)
|
|
{
|
|
// project input vertex onto fog plane
|
|
m_waterSurfaceVertices[i].xyz = MapVertexToFogPlane(pTessVertList[i], fogPlane);
|
|
|
|
// generate texture coordinates
|
|
m_waterSurfaceVertices[i].st = Vec2(surfUVScale.x * (pTessVertList[i].x - pTessVertList[0].x), surfUVScale.y * (pTessVertList[i].y - pTessVertList[0].y));
|
|
|
|
pVertsTemp.push_back(m_waterSurfaceVertices[i]);
|
|
}
|
|
|
|
// generate indices.
|
|
// Note: triangulation code not robust, relies on contour/vertices to be declared sequentially and no holes -> too many vertices will lead to stretched results
|
|
WaterVolumeRenderNodeUtils::Triangulate(WaterVolumeRenderNodeUtils::VertexAccess<SVF_P3F_C4B_T2F>(&m_waterSurfaceVertices[0], m_waterSurfaceVertices.size()), m_waterSurfaceIndices);
|
|
|
|
// update bounding info
|
|
UpdateBoundingBox();
|
|
|
|
// Safety check.
|
|
if (m_waterSurfaceIndices.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Pre-tessellate mesh further
|
|
uint32_t iterationCount = 4;
|
|
for (uint32 i = 0; i < iterationCount; ++i)
|
|
{
|
|
uint32 nIndices = m_waterSurfaceIndices.size();
|
|
for (uint32 t = 0; t < nIndices; t += 3)
|
|
{
|
|
// Get triangle, compute median edge vertex, insert to vertex list
|
|
uint16 id_a = m_waterSurfaceIndices[t + 0];
|
|
uint16 id_b = m_waterSurfaceIndices[t + 1];
|
|
uint16 id_c = m_waterSurfaceIndices[t + 2];
|
|
|
|
SVF_P3F_C4B_T2F& vtx_a = m_waterSurfaceVertices[ id_a ];
|
|
SVF_P3F_C4B_T2F& vtx_b = m_waterSurfaceVertices[ id_b ];
|
|
SVF_P3F_C4B_T2F& vtx_c = m_waterSurfaceVertices[ id_c ];
|
|
|
|
SVF_P3F_C4B_T2F vtx_m_ab;
|
|
vtx_m_ab.xyz = (vtx_a.xyz + vtx_b.xyz) * 0.5f;
|
|
vtx_m_ab.st = (vtx_a.st + vtx_b.st) * 0.5f;
|
|
vtx_m_ab.color = vtx_a.color;
|
|
|
|
pVertsTemp.push_back(vtx_m_ab);
|
|
uint16 id_d = (uint16) pVertsTemp.size() - 1;
|
|
|
|
SVF_P3F_C4B_T2F vtx_m_bc;
|
|
vtx_m_bc.xyz = (vtx_b.xyz + vtx_c.xyz) * 0.5f;
|
|
vtx_m_bc.st = (vtx_b.st + vtx_c.st) * 0.5f;
|
|
vtx_m_bc.color = vtx_a.color;
|
|
|
|
pVertsTemp.push_back(vtx_m_bc);
|
|
uint16 id_e = (uint16) pVertsTemp.size() - 1;
|
|
|
|
SVF_P3F_C4B_T2F vtx_m_ca;
|
|
vtx_m_ca.xyz = (vtx_a.xyz + vtx_c.xyz) * 0.5f;
|
|
vtx_m_ca.st = (vtx_a.st + vtx_c.st) * 0.5f;
|
|
vtx_m_ca.color = vtx_a.color;
|
|
|
|
pVertsTemp.push_back(vtx_m_ca);
|
|
uint16 id_f = (uint16) pVertsTemp.size() - 1;
|
|
|
|
// build new indices
|
|
|
|
// aed
|
|
pIndicesTemp.push_back(id_a);
|
|
pIndicesTemp.push_back(id_d);
|
|
pIndicesTemp.push_back(id_f);
|
|
|
|
// ebd
|
|
pIndicesTemp.push_back(id_d);
|
|
pIndicesTemp.push_back(id_b);
|
|
pIndicesTemp.push_back(id_e);
|
|
|
|
// bfd
|
|
pIndicesTemp.push_back(id_f);
|
|
pIndicesTemp.push_back(id_d);
|
|
pIndicesTemp.push_back(id_e);
|
|
|
|
// fcd
|
|
pIndicesTemp.push_back(id_f);
|
|
pIndicesTemp.push_back(id_e);
|
|
pIndicesTemp.push_back(id_c);
|
|
}
|
|
|
|
// update index list for new iteration
|
|
m_waterSurfaceIndices.resize(pIndicesTemp.size());
|
|
memcpy(&m_waterSurfaceIndices[0], &pIndicesTemp[0], sizeof(uint16) * pIndicesTemp.size());
|
|
m_waterSurfaceVertices.resize(pVertsTemp.size());
|
|
memcpy(&m_waterSurfaceVertices[0], &pVertsTemp[0], sizeof(SVF_P3F_C4B_T2F) * pVertsTemp.size());
|
|
pIndicesTemp.clear();
|
|
}
|
|
|
|
// update reference to vertex and index buffer
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_wvParams[i].m_pVertices = &m_waterSurfaceVertices[0];
|
|
m_wvParams[i].m_numVertices = m_waterSurfaceVertices.size();
|
|
m_wvParams[i].m_pIndices = &m_waterSurfaceIndices[0];
|
|
m_wvParams[i].m_numIndices = m_waterSurfaceIndices.size();
|
|
}
|
|
|
|
// add to 3d engine
|
|
Get3DEngine()->RegisterEntity(this, nSID, nSID);
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::CreateRiver(uint64 volumeID, const AZStd::vector<AZ::Vector3>& verticies, const AZ::Transform& transform, float uTexCoordBegin, float uTexCoordEnd, const AZ::Vector2& surfUVScale, const AZ::Plane& fogPlane, bool keepSerializationParams, int nSID)
|
|
{
|
|
PodArray<Vec3> points;
|
|
points.reserve(verticies.size());
|
|
for (auto azPoint : verticies)
|
|
{
|
|
points.Add(AZVec3ToLYVec3(transform.TransformPoint(azPoint)));
|
|
}
|
|
|
|
Plane plane = AZPlaneToLyPlane(fogPlane);
|
|
CreateRiver(volumeID, points.GetElements(), static_cast<unsigned int>(verticies.size()), uTexCoordBegin, uTexCoordEnd, Vec2(surfUVScale.GetX(), surfUVScale.GetY()), plane, keepSerializationParams, nSID);
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::CreateRiver(uint64 volumeID, const Vec3* pVertices, unsigned int numVertices, float uTexCoordBegin, float uTexCoordEnd, const Vec2& surfUVScale, const Plane& fogPlane, bool keepSerializationParams, int nSID)
|
|
{
|
|
const float precisionTolerance = 1e-2f;
|
|
|
|
assert(fabs(fogPlane.n.GetLengthSquared() - 1.0f) < precisionTolerance && "CWaterVolumeRenderNode::CreateRiver(...) -- Fog plane normal doesn't have unit length!");
|
|
assert(fogPlane.n.Dot(Vec3(0, 0, 1)) > precisionTolerance && "CWaterVolumeRenderNode::CreateRiver(...) -- Invalid fog plane specified!");
|
|
if (fogPlane.n.Dot(Vec3(0, 0, 1)) <= precisionTolerance)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(numVertices == 4);
|
|
if (numVertices != 4 || !_finite(pVertices[0].x) || !_finite(pVertices[1].x) || !_finite(pVertices[2].x) || !_finite(pVertices[3].x))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_volumeID = volumeID;
|
|
m_fogPlane = fogPlane;
|
|
m_fogPlaneBase = fogPlane;
|
|
m_volumeType = IWaterVolumeRenderNode::eWVT_River;
|
|
|
|
// copy volatile creation params to be able to serialize water volume if needed (only in editor)
|
|
if (keepSerializationParams)
|
|
{
|
|
CopyVolatileRiverSerParams(pVertices, numVertices, uTexCoordBegin, uTexCoordEnd, surfUVScale);
|
|
}
|
|
|
|
// remove form 3d engine
|
|
Get3DEngine()->UnRegisterEntityAsJob(this);
|
|
|
|
// generate vertices
|
|
m_waterSurfaceVertices.resize(5);
|
|
m_waterSurfaceVertices[0].xyz = pVertices[0];
|
|
m_waterSurfaceVertices[1].xyz = pVertices[1];
|
|
m_waterSurfaceVertices[2].xyz = pVertices[2];
|
|
m_waterSurfaceVertices[3].xyz = pVertices[3];
|
|
m_waterSurfaceVertices[4].xyz = 0.25f * (pVertices[0] + pVertices[1] + pVertices[2] + pVertices[3]);
|
|
|
|
Vec3 tv0 = Vec3(0, 0, 1.f);
|
|
Vec3 tv1 = Vec3(0, 0, -1.f);
|
|
Plane planes[4];
|
|
planes[0].SetPlane(pVertices[0], pVertices[1], pVertices[1] + tv0);
|
|
planes[1].SetPlane(pVertices[2], pVertices[3], pVertices[3] + tv1);
|
|
planes[2].SetPlane(pVertices[0], pVertices[2], pVertices[2] + tv1);
|
|
planes[3].SetPlane(pVertices[1], pVertices[3], pVertices[3] + tv0);
|
|
|
|
|
|
for (uint32 i(0); i < 5; ++i)
|
|
{
|
|
// map input vertex onto fog plane
|
|
m_waterSurfaceVertices[i].xyz = MapVertexToFogPlane(m_waterSurfaceVertices[i].xyz, fogPlane);
|
|
|
|
// generate texture coordinates
|
|
float d0(fabsf(planes[0].DistFromPlane(m_waterSurfaceVertices[i].xyz)));
|
|
float d1(fabsf(planes[1].DistFromPlane(m_waterSurfaceVertices[i].xyz)));
|
|
float d2(fabsf(planes[2].DistFromPlane(m_waterSurfaceVertices[i].xyz)));
|
|
float d3(fabsf(planes[3].DistFromPlane(m_waterSurfaceVertices[i].xyz)));
|
|
float t(fabsf(d0 + d1) < FLT_EPSILON ? 0.0f : clamp_tpl(d0 / (d0 + d1), 0.0f, 1.0f));
|
|
|
|
Vec2 st = Vec2((1 - t) * fabsf(uTexCoordBegin) + t * fabsf(uTexCoordEnd), fabsf(d2 + d3) < FLT_EPSILON ? 0.0f : clamp_tpl(d2 / (d2 + d3), 0.0f, 1.0f));
|
|
st[0] *= surfUVScale.x;
|
|
st[1] *= surfUVScale.y;
|
|
|
|
m_waterSurfaceVertices[i].st = st;
|
|
}
|
|
|
|
// generate indices
|
|
m_waterSurfaceIndices.resize(12);
|
|
m_waterSurfaceIndices[ 0] = 0;
|
|
m_waterSurfaceIndices[ 1] = 1;
|
|
m_waterSurfaceIndices[ 2] = 4;
|
|
|
|
m_waterSurfaceIndices[ 3] = 1;
|
|
m_waterSurfaceIndices[ 4] = 3;
|
|
m_waterSurfaceIndices[ 5] = 4;
|
|
|
|
m_waterSurfaceIndices[ 6] = 3;
|
|
m_waterSurfaceIndices[ 7] = 2;
|
|
m_waterSurfaceIndices[ 8] = 4;
|
|
|
|
m_waterSurfaceIndices[ 9] = 0;
|
|
m_waterSurfaceIndices[10] = 4;
|
|
m_waterSurfaceIndices[11] = 2;
|
|
|
|
// update bounding info
|
|
UpdateBoundingBox();
|
|
|
|
// update reference to vertex and index buffer
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_wvParams[i].m_pVertices = &m_waterSurfaceVertices[0];
|
|
m_wvParams[i].m_numVertices = m_waterSurfaceVertices.size();
|
|
m_wvParams[i].m_pIndices = &m_waterSurfaceIndices[0];
|
|
m_wvParams[i].m_numIndices = m_waterSurfaceIndices.size();
|
|
}
|
|
|
|
// add to 3d engine
|
|
Get3DEngine()->RegisterEntity(this, nSID, nSID);
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetAreaPhysicsArea(const Vec3* pVertices, unsigned int numVertices, bool keepSerializationParams)
|
|
{
|
|
const bool serializeWith3DEngine = keepSerializationParams && !IsAttachedToEntity();
|
|
|
|
assert(pVertices && numVertices > 3 && m_volumeType == IWaterVolumeRenderNode::eWVT_Area);
|
|
if (!pVertices || numVertices <= 3 || m_volumeType != IWaterVolumeRenderNode::eWVT_Area)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_pPhysAreaInput)
|
|
{
|
|
m_pPhysAreaInput = new SWaterVolumePhysAreaInput;
|
|
}
|
|
|
|
const Plane& fogPlane(m_fogPlane);
|
|
|
|
// generate contour vertices
|
|
m_pPhysAreaInput->m_contour.resize(numVertices);
|
|
|
|
// map input vertices onto fog plane
|
|
if (WaterVolumeRenderNodeUtils::Area(WaterVolumeRenderNodeUtils::VertexAccess<Vec3>(pVertices, numVertices)) > 0.0f)
|
|
{
|
|
for (unsigned int i(0); i < numVertices; ++i)
|
|
{
|
|
m_pPhysAreaInput->m_contour[i] = MapVertexToFogPlane(pVertices[i], fogPlane); // flip vertex order as physics expects them CCW
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (unsigned int i(0); i < numVertices; ++i)
|
|
{
|
|
m_pPhysAreaInput->m_contour[i] = MapVertexToFogPlane(pVertices[numVertices - 1 - i], fogPlane);
|
|
}
|
|
}
|
|
|
|
// triangulate contour
|
|
WaterVolumeRenderNodeUtils::Triangulate(WaterVolumeRenderNodeUtils::VertexAccess<Vec3>(&m_pPhysAreaInput->m_contour[0], m_pPhysAreaInput->m_contour.size()), m_pPhysAreaInput->m_indices);
|
|
|
|
// reset flow
|
|
m_pPhysAreaInput->m_flowContour.resize(0);
|
|
|
|
if (serializeWith3DEngine)
|
|
{
|
|
CopyVolatilePhysicsAreaContourSerParams(pVertices, numVertices);
|
|
}
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::SetRiverPhysicsArea(const AZStd::vector<AZ::Vector3>& verticies, const AZ::Transform& transform, bool keepSerializationParams)
|
|
{
|
|
PodArray<Vec3> points;
|
|
points.reserve(verticies.size());
|
|
for (auto azPoint : verticies)
|
|
{
|
|
points.Add(AZVec3ToLYVec3(transform.TransformPoint(azPoint)));
|
|
}
|
|
SetRiverPhysicsArea(points.GetElements(), static_cast<unsigned int>(verticies.size()), keepSerializationParams);
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::SetRiverPhysicsArea(const Vec3* pVertices, unsigned int numVertices, bool keepSerializationParams)
|
|
{
|
|
assert(pVertices && numVertices > 3 && !(numVertices & 1) && m_volumeType == IWaterVolumeRenderNode::eWVT_River);
|
|
if (!pVertices || numVertices <= 3 || (numVertices & 1) || m_volumeType != IWaterVolumeRenderNode::eWVT_River)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_pPhysAreaInput)
|
|
{
|
|
m_pPhysAreaInput = new SWaterVolumePhysAreaInput;
|
|
}
|
|
|
|
const Plane& fogPlane(m_fogPlane);
|
|
|
|
// generate contour vertices
|
|
m_pPhysAreaInput->m_contour.resize(numVertices);
|
|
|
|
// map input vertices onto fog plane
|
|
if (WaterVolumeRenderNodeUtils::Area(WaterVolumeRenderNodeUtils::VertexAccess<Vec3>(pVertices, numVertices)) > 0.0f)
|
|
{
|
|
for (unsigned int i(0); i < numVertices; ++i)
|
|
{
|
|
m_pPhysAreaInput->m_contour[i] = MapVertexToFogPlane(pVertices[i], fogPlane); // flip vertex order as physics expects them CCW
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (unsigned int i(0); i < numVertices; ++i)
|
|
{
|
|
m_pPhysAreaInput->m_contour[i] = MapVertexToFogPlane(pVertices[numVertices - 1 - i], fogPlane);
|
|
}
|
|
}
|
|
|
|
// generate flow along contour
|
|
unsigned int h(numVertices / 2);
|
|
unsigned int h2(numVertices);
|
|
m_pPhysAreaInput->m_flowContour.resize(numVertices);
|
|
for (unsigned int i(0); i < h; ++i)
|
|
{
|
|
if (!i)
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[i] = (m_pPhysAreaInput->m_contour[i + 1] - m_pPhysAreaInput->m_contour[i]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
else if (i == h - 1)
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[i] = (m_pPhysAreaInput->m_contour[i] - m_pPhysAreaInput->m_contour[i - 1]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
else
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[i] = (m_pPhysAreaInput->m_contour[i + 1] - m_pPhysAreaInput->m_contour[i - 1]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
}
|
|
|
|
for (unsigned int i(0); i < h; ++i)
|
|
{
|
|
if (!i)
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[h2 - 1 - i] = (m_pPhysAreaInput->m_contour[h2 - 1 - i - 1] - m_pPhysAreaInput->m_contour[h2 - 1 - i]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
else if (i == h - 1)
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[h2 - 1 - i] = (m_pPhysAreaInput->m_contour[h2 - 1 - i] - m_pPhysAreaInput->m_contour[h2 - 1 - i + 1]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
else
|
|
{
|
|
m_pPhysAreaInput->m_flowContour[h2 - 1 - i] = (m_pPhysAreaInput->m_contour[h2 - 1 - i - 1] - m_pPhysAreaInput->m_contour[h2 - 1 - i + 1]).GetNormalizedSafe() * m_streamSpeed;
|
|
}
|
|
}
|
|
|
|
// triangulate contour
|
|
m_pPhysAreaInput->m_indices.resize(3 * 2 * (numVertices / 2 - 1));
|
|
for (unsigned int i(0); i < h - 1; ++i)
|
|
{
|
|
m_pPhysAreaInput->m_indices[6 * i + 0] = i;
|
|
m_pPhysAreaInput->m_indices[6 * i + 1] = i + 1;
|
|
m_pPhysAreaInput->m_indices[6 * i + 2] = h2 - 1 - i - 1;
|
|
|
|
m_pPhysAreaInput->m_indices[6 * i + 3] = h2 - 1 - i - 1;
|
|
m_pPhysAreaInput->m_indices[6 * i + 4] = h2 - 1 - i;
|
|
m_pPhysAreaInput->m_indices[6 * i + 5] = i;
|
|
}
|
|
|
|
if (keepSerializationParams)
|
|
{
|
|
CopyVolatilePhysicsAreaContourSerParams(pVertices, numVertices);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const char* CWaterVolumeRenderNode::GetEntityClassName() const
|
|
{
|
|
return "WaterVolume";
|
|
}
|
|
|
|
|
|
const char* CWaterVolumeRenderNode::GetName() const
|
|
{
|
|
return "WaterVolume";
|
|
}
|
|
|
|
IRenderNode* CWaterVolumeRenderNode::Clone() const
|
|
{
|
|
CWaterVolumeRenderNode* pWaterVol = new CWaterVolumeRenderNode();
|
|
|
|
// CWaterVolumeRenderNode member vars
|
|
pWaterVol->m_volumeType = m_volumeType;
|
|
|
|
pWaterVol->m_volumeID = m_volumeID;
|
|
|
|
pWaterVol->m_volumeDepth = m_volumeDepth;
|
|
pWaterVol->m_streamSpeed = m_streamSpeed;
|
|
|
|
pWaterVol->m_pMaterial = m_pMaterial;
|
|
pWaterVol->m_pWaterBodyIntoMat = m_pWaterBodyIntoMat;
|
|
pWaterVol->m_pWaterBodyOutofMat = m_pWaterBodyOutofMat;
|
|
|
|
if (m_pPhysAreaInput != NULL)
|
|
{
|
|
pWaterVol->m_pPhysAreaInput = new SWaterVolumePhysAreaInput;
|
|
*pWaterVol->m_pPhysAreaInput = *m_pPhysAreaInput;
|
|
}
|
|
else
|
|
{
|
|
pWaterVol->m_pPhysAreaInput = NULL;
|
|
}
|
|
|
|
pWaterVol->m_waterSurfaceVertices = m_waterSurfaceVertices;
|
|
pWaterVol->m_waterSurfaceIndices = m_waterSurfaceIndices;
|
|
|
|
pWaterVol->m_parentEntityWorldTM = m_parentEntityWorldTM;
|
|
pWaterVol->m_nLayerId = m_nLayerId;
|
|
|
|
pWaterVol->m_fogDensity = m_fogDensity;
|
|
pWaterVol->m_fogColor = m_fogColor;
|
|
pWaterVol->m_fogColorAffectedBySun = m_fogColorAffectedBySun;
|
|
pWaterVol->m_fogShadowing = m_fogShadowing;
|
|
|
|
pWaterVol->m_fogPlane = m_fogPlane;
|
|
pWaterVol->m_fogPlaneBase = m_fogPlaneBase;
|
|
|
|
pWaterVol->m_center = m_center;
|
|
pWaterVol->m_WSBBox = m_WSBBox;
|
|
|
|
pWaterVol->m_capFogAtVolumeDepth = m_capFogAtVolumeDepth;
|
|
pWaterVol->m_attachedToEntity = m_attachedToEntity;
|
|
pWaterVol->m_caustics = m_caustics;
|
|
|
|
pWaterVol->m_causticIntensity = m_causticIntensity;
|
|
pWaterVol->m_causticTiling = m_causticTiling;
|
|
pWaterVol->m_causticShadow = m_causticShadow;
|
|
pWaterVol->m_causticHeight = m_causticHeight;
|
|
|
|
// update reference to vertex and index buffer
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
pWaterVol->m_wvParams[i].m_pVertices = &pWaterVol->m_waterSurfaceVertices[0];
|
|
pWaterVol->m_wvParams[i].m_numVertices = pWaterVol->m_waterSurfaceVertices.size();
|
|
pWaterVol->m_wvParams[i].m_pIndices = &pWaterVol->m_waterSurfaceIndices[0];
|
|
pWaterVol->m_wvParams[i].m_numIndices = pWaterVol->m_waterSurfaceIndices.size();
|
|
}
|
|
|
|
//IRenderNode member vars
|
|
// We cannot just copy over due to issues with the linked list of IRenderNode objects
|
|
CopyIRenderNodeData(pWaterVol);
|
|
|
|
return pWaterVol;
|
|
}
|
|
|
|
inline void TransformPosition(Vec3& pos, const Vec3& localOrigin, const Matrix34& l2w)
|
|
{
|
|
pos = pos - localOrigin;
|
|
pos = l2w * pos;
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::Transform(const Vec3& localOrigin, const Matrix34& l2w)
|
|
{
|
|
CRY_ASSERT_MESSAGE(!IsAttachedToEntity(), "FIXME: Don't currently support transforming attached water volumes");
|
|
|
|
if (m_pPhysAreaInput != NULL)
|
|
{
|
|
for (std::vector<Vec3>::iterator it = m_pPhysAreaInput->m_contour.begin(); it != m_pPhysAreaInput->m_contour.end(); ++it)
|
|
{
|
|
Vec3& pos = *it;
|
|
TransformPosition(pos, localOrigin, l2w);
|
|
}
|
|
|
|
for (std::vector<Vec3>::iterator it = m_pPhysAreaInput->m_flowContour.begin(); it != m_pPhysAreaInput->m_flowContour.end(); ++it)
|
|
{
|
|
Vec3& pos = *it;
|
|
TransformPosition(pos, localOrigin, l2w);
|
|
}
|
|
}
|
|
|
|
for (WaterSurfaceVertices::iterator it = m_waterSurfaceVertices.begin(); it != m_waterSurfaceVertices.end(); ++it)
|
|
{
|
|
SVF_P3F_C4B_T2F& vert = *it;
|
|
TransformPosition(vert.xyz, localOrigin, l2w);
|
|
}
|
|
|
|
Vec3 origFogPoint = m_fogPlane.n * m_fogPlane.d;
|
|
TransformPosition(origFogPoint, localOrigin, l2w);
|
|
m_fogPlane.SetPlane(l2w.TransformVector(m_fogPlaneBase.n).GetNormalized(), origFogPoint);
|
|
|
|
TransformPosition(m_center, localOrigin, l2w);
|
|
|
|
UpdateBoundingBox();
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::SetMatrix(const Matrix34& mat)
|
|
{
|
|
if (!IsAttachedToEntity())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_parentEntityWorldTM = mat;
|
|
m_fogPlane.SetPlane(m_parentEntityWorldTM.TransformVector(m_fogPlaneBase.n).GetNormalized(), m_parentEntityWorldTM.GetTranslation());
|
|
|
|
UpdateBoundingBox();
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
|
|
{
|
|
Render_JobEntry(rParam, passInfo, SRendItemSorter(rParam.rendItemSorter));
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::Render_JobEntry(const SRendParams& rParam, const SRenderingPassInfo& passInfo, SRendItemSorter rendItemSorter)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// hack: special case for when inside amphibious vehicle
|
|
if (Get3DEngine()->GetOceanRenderFlags() & OCR_NO_DRAW)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// anything to render?
|
|
if (passInfo.IsRecursivePass() || !m_pMaterial || !m_pWaterBodyIntoMat || !m_pWaterBodyOutofMat || !passInfo.RenderWaterVolumes() ||
|
|
m_waterSurfaceVertices.empty() || m_waterSurfaceIndices.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_fogDensity == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRenderer* pRenderer(GetRenderer());
|
|
|
|
const int fillThreadID = passInfo.ThreadID();
|
|
|
|
// get render objects
|
|
CRenderObject* pROVol = pRenderer->EF_GetObject_Temp(fillThreadID);
|
|
CRenderObject* pROSurf = pRenderer->EF_GetObject_Temp(fillThreadID);
|
|
if (!pROVol || !pROSurf)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_pSurfaceRE[fillThreadID])
|
|
{
|
|
return;
|
|
}
|
|
|
|
float distToWaterVolumeSurface(GetCameraDistToWaterVolumeSurface(passInfo));
|
|
bool aboveWaterVolumeSurface(distToWaterVolumeSurface > 0.0f);
|
|
bool belowWaterVolume(m_capFogAtVolumeDepth && distToWaterVolumeSurface < -m_volumeDepth);
|
|
bool insideWaterVolumeSurface2D(IsCameraInsideWaterVolumeSurface2D(passInfo));
|
|
bool insideWaterVolume(insideWaterVolumeSurface2D && !aboveWaterVolumeSurface && !belowWaterVolume);
|
|
|
|
// fill parameters to render elements
|
|
m_wvParams[fillThreadID].m_viewerInsideVolume = insideWaterVolume;
|
|
m_wvParams[fillThreadID].m_viewerCloseToWaterPlane = /*insideWaterVolumeSurface2D && */ fabsf(distToWaterVolumeSurface) < 0.5f;
|
|
m_wvParams[fillThreadID].m_viewerCloseToWaterVolume = GetCameraDistSqToWaterVolumeAABB(passInfo) < 9.0f; //Sq dist
|
|
|
|
const float hdrMultiplier = m_fogColorAffectedBySun ? 1.0f : ((CTimeOfDay*)Get3DEngine()->GetTimeOfDay())->GetHDRMultiplier();
|
|
|
|
m_wvParams[fillThreadID].m_fogDensity = m_fogDensity;
|
|
m_wvParams[fillThreadID].m_fogColor = m_fogColor * hdrMultiplier;
|
|
m_wvParams[fillThreadID].m_fogColorAffectedBySun = m_fogColorAffectedBySun;
|
|
m_wvParams[fillThreadID].m_fogPlane = m_fogPlane;
|
|
m_wvParams[fillThreadID].m_fogShadowing = m_fogShadowing;
|
|
|
|
m_wvParams[fillThreadID].m_caustics = m_caustics;
|
|
m_wvParams[fillThreadID].m_causticIntensity = m_causticIntensity;
|
|
m_wvParams[fillThreadID].m_causticTiling = m_causticTiling;
|
|
m_wvParams[fillThreadID].m_causticHeight = m_causticHeight;
|
|
|
|
m_wvParams[fillThreadID].m_center = m_center;
|
|
m_wvParams[fillThreadID].m_WSBBox = m_WSBBox;
|
|
|
|
// if above water render fog together with water surface
|
|
bool isFastpath = GetCVars()->e_WaterVolumes == 2 && (distToWaterVolumeSurface > 0.5f || insideWaterVolume);
|
|
m_pSurfaceRE[fillThreadID]->m_drawFastPath = isFastpath;
|
|
|
|
// submit volume
|
|
if (GetCVars()->e_Fog)
|
|
{
|
|
if ((insideWaterVolume || (!isFastpath && aboveWaterVolumeSurface)) && m_pVolumeRE[fillThreadID])
|
|
{
|
|
// fill in data for render object
|
|
if (!IsAttachedToEntity())
|
|
{
|
|
pROVol->m_II.m_Matrix.SetIdentity();
|
|
}
|
|
else
|
|
{
|
|
pROVol->m_II.m_Matrix = m_parentEntityWorldTM;
|
|
}
|
|
pROVol->m_fSort = 0;
|
|
|
|
// get shader item
|
|
SShaderItem& shaderItem(m_wvParams[fillThreadID].m_viewerInsideVolume ? m_pWaterBodyOutofMat->GetShaderItem(0) : m_pWaterBodyIntoMat->GetShaderItem(0));
|
|
|
|
// add to renderer
|
|
GetRenderer()->EF_AddEf(m_pVolumeRE[fillThreadID], shaderItem, pROVol, passInfo, EFSLIST_WATER_VOLUMES, aboveWaterVolumeSurface ? 0 : 1, rendItemSorter);
|
|
}
|
|
}
|
|
|
|
// submit surface
|
|
{
|
|
// fill in data for render object
|
|
if (!IsAttachedToEntity())
|
|
{
|
|
pROSurf->m_II.m_Matrix.SetIdentity();
|
|
}
|
|
else
|
|
{
|
|
pROSurf->m_II.m_Matrix = m_parentEntityWorldTM;
|
|
}
|
|
pROSurf->m_fSort = 0;
|
|
pROSurf->m_nTextureID = rParam.nTextureID;
|
|
|
|
// get shader item
|
|
SShaderItem& shaderItem(m_pMaterial->GetShaderItem(0));
|
|
|
|
// add to renderer
|
|
// Render water refractive surface between beforeWater / afterWater objects.
|
|
GetRenderer()->EF_AddEf(m_pSurfaceRE[fillThreadID], shaderItem, pROSurf, passInfo, EFSLIST_REFRACTIVE_SURFACE, 0, rendItemSorter);
|
|
}
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
|
|
{
|
|
m_pMaterial = pMat;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
SIZER_COMPONENT_NAME(pSizer, "WaterVolumeNode");
|
|
pSizer->AddObject(this, sizeof(*this));
|
|
pSizer->AddObject(m_pSerParams);
|
|
pSizer->AddObject(m_pPhysAreaInput);
|
|
pSizer->AddObject(m_waterSurfaceVertices);
|
|
pSizer->AddObject(m_waterSurfaceIndices);
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::Precache()
|
|
{
|
|
}
|
|
|
|
|
|
IPhysicalEntity* CWaterVolumeRenderNode::GetPhysics() const
|
|
{
|
|
return m_pPhysArea;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SetPhysics(IPhysicalEntity* pPhysArea)
|
|
{
|
|
m_pPhysArea = pPhysArea;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CheckPhysicalized()
|
|
{
|
|
if (!GetPhysics())
|
|
{
|
|
Physicalize();
|
|
}
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::Physicalize([[maybe_unused]] bool bInstant)
|
|
{
|
|
if (IsAttachedToEntity())
|
|
{
|
|
return;
|
|
}
|
|
|
|
Dephysicalize();
|
|
|
|
// setup physical area
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::Dephysicalize([[maybe_unused]] bool bKeepIfReferenced)
|
|
{
|
|
if (m_pPhysArea)
|
|
{
|
|
m_pPhysArea = 0;
|
|
m_attachedToEntity = false;
|
|
}
|
|
}
|
|
|
|
|
|
float CWaterVolumeRenderNode::GetCameraDistToWaterVolumeSurface(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 camPos(cam.GetPosition());
|
|
return m_fogPlane.DistFromPlane(camPos);
|
|
}
|
|
|
|
|
|
float CWaterVolumeRenderNode::GetCameraDistSqToWaterVolumeAABB(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 camPos(cam.GetPosition());
|
|
return m_WSBBox.GetDistanceSqr(camPos);
|
|
}
|
|
|
|
|
|
bool CWaterVolumeRenderNode::IsCameraInsideWaterVolumeSurface2D(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 camPos(cam.GetPosition());
|
|
|
|
pe_status_area sa;
|
|
sa.bUniformOnly = true;
|
|
MARK_UNUSED sa.ctr;
|
|
if (m_pPhysArea && m_pPhysArea->GetStatus(&sa) && sa.pSurface)
|
|
{
|
|
pe_status_contains_point scp;
|
|
scp.pt = camPos;
|
|
return m_pPhysArea->GetStatus(&scp) != 0;
|
|
}
|
|
|
|
WaterVolumeRenderNodeUtils::VertexAccess<SVF_P3F_C4B_T2F> ca(&m_waterSurfaceVertices[0], m_waterSurfaceVertices.size());
|
|
for (size_t i(0); i < m_waterSurfaceIndices.size(); i += 3)
|
|
{
|
|
const Vec3 v0 = m_parentEntityWorldTM.TransformPoint(ca[ m_waterSurfaceIndices[i] ]);
|
|
const Vec3 v1 = m_parentEntityWorldTM.TransformPoint(ca[ m_waterSurfaceIndices[i + 1] ]);
|
|
const Vec3 v2 = m_parentEntityWorldTM.TransformPoint(ca[ m_waterSurfaceIndices[i + 2] ]);
|
|
|
|
if (WaterVolumeRenderNodeUtils::InsideTriangle(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y, camPos.x, camPos.y))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::UpdateBoundingBox()
|
|
{
|
|
m_WSBBox.Reset();
|
|
for (size_t i(0); i < m_waterSurfaceVertices.size(); ++i)
|
|
{
|
|
m_WSBBox.Add(m_parentEntityWorldTM.TransformPoint(m_waterSurfaceVertices[i].xyz));
|
|
}
|
|
|
|
if (IVisArea* pArea = Get3DEngine()->GetVisAreaFromPos(m_WSBBox.GetCenter()))
|
|
{
|
|
if (m_WSBBox.min.z > pArea->GetAABBox()->min.z)
|
|
{
|
|
m_WSBBox.min.z = pArea->GetAABBox()->min.z;
|
|
}
|
|
return;
|
|
}
|
|
|
|
m_WSBBox.min.z -= m_volumeDepth;
|
|
m_center = m_WSBBox.GetCenter();
|
|
}
|
|
|
|
|
|
const SWaterVolumeSerialize* CWaterVolumeRenderNode::GetSerializationParams()
|
|
{
|
|
if (!m_pSerParams)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// before returning, copy non-volatile serialization params
|
|
m_pSerParams->m_volumeType = m_volumeType;
|
|
m_pSerParams->m_volumeID = m_volumeID;
|
|
|
|
m_pSerParams->m_pMaterial = m_pMaterial;
|
|
|
|
m_pSerParams->m_fogDensity = m_fogDensity;
|
|
m_pSerParams->m_fogColor = m_fogColor;
|
|
m_pSerParams->m_fogColorAffectedBySun = m_fogColorAffectedBySun;
|
|
m_pSerParams->m_fogPlane = m_fogPlane;
|
|
m_pSerParams->m_fogShadowing = m_fogShadowing;
|
|
|
|
m_pSerParams->m_volumeDepth = m_volumeDepth;
|
|
m_pSerParams->m_streamSpeed = m_streamSpeed;
|
|
m_pSerParams->m_capFogAtVolumeDepth = m_capFogAtVolumeDepth;
|
|
|
|
m_pSerParams->m_caustics = m_caustics;
|
|
m_pSerParams->m_causticIntensity = m_causticIntensity;
|
|
m_pSerParams->m_causticTiling = m_causticTiling;
|
|
m_pSerParams->m_causticHeight = m_causticHeight;
|
|
|
|
return m_pSerParams;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CopyVolatilePhysicsAreaContourSerParams(const Vec3* pVertices, unsigned int numVertices)
|
|
{
|
|
if (!m_pSerParams)
|
|
{
|
|
m_pSerParams = new SWaterVolumeSerialize;
|
|
}
|
|
|
|
m_pSerParams->m_physicsAreaContour.resize(numVertices);
|
|
for (unsigned int i(0); i < numVertices; ++i)
|
|
{
|
|
m_pSerParams->m_physicsAreaContour[i] = pVertices[i];
|
|
}
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CopyVolatileRiverSerParams(const Vec3* pVertices, unsigned int numVertices, float uTexCoordBegin, float uTexCoordEnd, const Vec2& surfUVScale)
|
|
{
|
|
if (!m_pSerParams)
|
|
{
|
|
m_pSerParams = new SWaterVolumeSerialize;
|
|
}
|
|
|
|
m_pSerParams->m_uTexCoordBegin = uTexCoordBegin;
|
|
m_pSerParams->m_uTexCoordEnd = uTexCoordEnd;
|
|
|
|
m_pSerParams->m_surfUScale = surfUVScale.x;
|
|
m_pSerParams->m_surfVScale = surfUVScale.y;
|
|
|
|
m_pSerParams->m_vertices.resize(numVertices);
|
|
for (uint32 i(0); i < numVertices; ++i)
|
|
{
|
|
m_pSerParams->m_vertices[i] = pVertices[i];
|
|
}
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::CopyVolatileAreaSerParams(const Vec3* pVertices, unsigned int numVertices, const Vec2& surfUVScale)
|
|
{
|
|
if (!m_pSerParams)
|
|
{
|
|
m_pSerParams = new SWaterVolumeSerialize;
|
|
}
|
|
|
|
m_pSerParams->m_uTexCoordBegin = 1.0f;
|
|
m_pSerParams->m_uTexCoordEnd = 1.0f;
|
|
|
|
m_pSerParams->m_surfUScale = surfUVScale.x;
|
|
m_pSerParams->m_surfVScale = surfUVScale.y;
|
|
|
|
m_pSerParams->m_vertices.resize(numVertices);
|
|
for (uint32 i(0); i < numVertices; ++i)
|
|
{
|
|
m_pSerParams->m_vertices[i] = pVertices[i];
|
|
}
|
|
}
|
|
|
|
|
|
int OnWaterUpdate(const EventPhysAreaChange* pEvent)
|
|
{
|
|
if (pEvent->iForeignData == PHYS_FOREIGN_ID_WATERVOLUME)
|
|
{
|
|
CWaterVolumeRenderNode* pWVRN = static_cast<CWaterVolumeRenderNode*>(pEvent->pForeignData);
|
|
pe_status_area sa;
|
|
sa.bUniformOnly = true;
|
|
MARK_UNUSED sa.ctr;
|
|
// Calling GetPhysArea() instead of GetPhysics() to avoid a crash of using bad memory. Refer to [LY-103758] for details on the crash
|
|
if (pWVRN->GetPhysArea() != pEvent->pEntity || !pEvent->pEntity->GetStatus(&sa))
|
|
{
|
|
return 1;
|
|
}
|
|
if (pEvent->pContainer)
|
|
{
|
|
pWVRN->SetAreaAttachedToEntity();
|
|
pWVRN->SetMatrix(Matrix34(Vec3(1), pEvent->qContainer, pEvent->posContainer));
|
|
}
|
|
pWVRN->SyncToPhysMesh(QuatT(pEvent->q, pEvent->pos), sa.pSurface, pEvent->depth);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
void CWaterVolumeRenderNode::SyncToPhysMesh(const QuatT& qtSurface, IGeometry* pSurface, float depth)
|
|
{
|
|
mesh_data* pmd;
|
|
if (!pSurface || pSurface->GetType() != GEOM_TRIMESH || !(pmd = (mesh_data*)pSurface->GetData()))
|
|
{
|
|
return;
|
|
}
|
|
bool bResized = false;
|
|
if (bResized = m_waterSurfaceVertices.size() != pmd->nVertices)
|
|
{
|
|
m_waterSurfaceVertices.resize(pmd->nVertices);
|
|
}
|
|
Vec2 uvScale = m_pSerParams ? Vec2(m_pSerParams->m_surfUScale, m_pSerParams->m_surfVScale) : Vec2(1.0f, 1.0f);
|
|
for (int i = 0; i < pmd->nVertices; i++)
|
|
{
|
|
m_waterSurfaceVertices[i].xyz = qtSurface * pmd->pVertices[i];
|
|
m_waterSurfaceVertices[i].st = Vec2(pmd->pVertices[i].x * uvScale.x, pmd->pVertices[i].y * uvScale.y);
|
|
}
|
|
if (m_waterSurfaceIndices.size() != pmd->nTris * 3)
|
|
{
|
|
m_waterSurfaceIndices.resize(pmd->nTris * 3), bResized = true;
|
|
}
|
|
for (int i = 0; i < pmd->nTris * 3; i++)
|
|
{
|
|
m_waterSurfaceIndices[i] = pmd->pIndices[i];
|
|
}
|
|
|
|
if (bResized)
|
|
{
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_wvParams[i].m_pVertices = &m_waterSurfaceVertices[0];
|
|
m_wvParams[i].m_numVertices = m_waterSurfaceVertices.size();
|
|
m_wvParams[i].m_pIndices = &m_waterSurfaceIndices[0];
|
|
m_wvParams[i].m_numIndices = m_waterSurfaceIndices.size();
|
|
}
|
|
}
|
|
|
|
m_fogPlane.SetPlane(qtSurface.q * Vec3(0, 0, 1), qtSurface.t);
|
|
m_volumeDepth = depth;
|
|
UpdateBoundingBox();
|
|
}
|
|
|
|
void CWaterVolumeRenderNode::OffsetPosition(const Vec3& delta)
|
|
{
|
|
if (m_pRNTmpData)
|
|
{
|
|
m_pRNTmpData->OffsetPosition(delta);
|
|
}
|
|
m_vOffset += delta;
|
|
m_center += delta;
|
|
m_WSBBox.Move(delta);
|
|
for (int i = 0; i < (int)m_waterSurfaceVertices.size(); ++i)
|
|
{
|
|
m_waterSurfaceVertices[i].xyz += delta;
|
|
}
|
|
|
|
if (m_pPhysAreaInput)
|
|
{
|
|
for (std::vector<Vec3>::iterator it = m_pPhysAreaInput->m_contour.begin(); it != m_pPhysAreaInput->m_contour.end(); ++it)
|
|
{
|
|
*it += delta;
|
|
}
|
|
for (std::vector<Vec3>::iterator it = m_pPhysAreaInput->m_flowContour.begin(); it != m_pPhysAreaInput->m_flowContour.end(); ++it)
|
|
{
|
|
*it += delta;
|
|
}
|
|
}
|
|
|
|
if (m_pPhysArea)
|
|
{
|
|
pe_params_pos par_pos;
|
|
m_pPhysArea->GetParams(&par_pos);
|
|
par_pos.bRecalcBounds |= 2;
|
|
par_pos.pos = m_vOffset;
|
|
m_pPhysArea->SetParams(&par_pos);
|
|
}
|
|
}
|
|
|
|
|