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.
972 lines
27 KiB
C++
972 lines
27 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 "VolumeObjectRenderNode.h"
|
|
#include "VolumeObjectDataCreate.h"
|
|
#include "MatMan.h"
|
|
#include "Environment/OceanEnvironmentBus.h"
|
|
|
|
#include <map>
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// volume data cache
|
|
|
|
class CVolumeDataCache
|
|
{
|
|
public:
|
|
static CVolumeDataCache& Access();
|
|
|
|
public:
|
|
~CVolumeDataCache();
|
|
|
|
void AddItem(const string& name, CVolumeDataItem* pItem);
|
|
void RemoveItem(const string& name);
|
|
CVolumeDataItem* GetItem(const string& name) const;
|
|
size_t size() const;
|
|
|
|
private:
|
|
typedef std::map<string, CVolumeDataItem*> VolumeDataItemMap;
|
|
|
|
private:
|
|
CVolumeDataCache();
|
|
|
|
private:
|
|
VolumeDataItemMap m_cache;
|
|
};
|
|
|
|
|
|
CVolumeDataCache& CVolumeDataCache::Access()
|
|
{
|
|
static CVolumeDataCache s_inst;
|
|
return s_inst;
|
|
}
|
|
|
|
|
|
CVolumeDataCache::CVolumeDataCache()
|
|
: m_cache()
|
|
{
|
|
}
|
|
|
|
|
|
CVolumeDataCache::~CVolumeDataCache()
|
|
{
|
|
assert(m_cache.empty());
|
|
}
|
|
|
|
|
|
void CVolumeDataCache::AddItem(const string& name, CVolumeDataItem* pItem)
|
|
{
|
|
VolumeDataItemMap::iterator it(m_cache.find(name));
|
|
assert(it == m_cache.end());
|
|
if (it == m_cache.end())
|
|
{
|
|
m_cache.insert(VolumeDataItemMap::value_type(name, pItem));
|
|
}
|
|
}
|
|
|
|
|
|
void CVolumeDataCache::RemoveItem(const string& name)
|
|
{
|
|
VolumeDataItemMap::iterator it(m_cache.find(name));
|
|
assert(it != m_cache.end());
|
|
if (it != m_cache.end())
|
|
{
|
|
m_cache.erase(it);
|
|
}
|
|
}
|
|
|
|
|
|
CVolumeDataItem* CVolumeDataCache::GetItem(const string& name) const
|
|
{
|
|
VolumeDataItemMap::const_iterator it(m_cache.find(name));
|
|
return it != m_cache.end() ? (*it).second : 0;
|
|
}
|
|
|
|
|
|
size_t CVolumeDataCache::size() const
|
|
{
|
|
return m_cache.size();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// volume data item
|
|
|
|
class CVolumeDataItem
|
|
{
|
|
public:
|
|
static CVolumeDataItem* Create(const char* filePath, const CREVolumeObject* pVolTexFactory);
|
|
|
|
public:
|
|
void AddRef();
|
|
void Release();
|
|
|
|
bool IsValid() const;
|
|
void AddToCache();
|
|
|
|
const SVolumeDataSrcB* GetData() const;
|
|
IVolumeTexture* GetVolumeTexture() const;
|
|
|
|
const AABB& GetTightBounds() const;
|
|
float GetScale() const;
|
|
|
|
const SVolumeDataHull* GetHull() const;
|
|
_smart_ptr<IRenderMesh> GetHullMesh();
|
|
|
|
private:
|
|
CVolumeDataItem(const char* filePath, const CREVolumeObject* pVolTexFactory);
|
|
~CVolumeDataItem();
|
|
|
|
private:
|
|
int m_refCount;
|
|
bool m_isValid;
|
|
bool m_isCached;
|
|
AABB m_tightBounds;
|
|
float m_scale;
|
|
string m_volDataFilePath;
|
|
SVolumeDataSrcB* m_pData;
|
|
IVolumeTexture* m_pVolTex;
|
|
SVolumeDataHull* m_pHull;
|
|
_smart_ptr<IRenderMesh> m_pHullMesh;
|
|
};
|
|
|
|
|
|
CVolumeDataItem* CVolumeDataItem::Create(const char* filePath, const CREVolumeObject* pVolTexFactory)
|
|
{
|
|
return new CVolumeDataItem(filePath, pVolTexFactory);
|
|
}
|
|
|
|
|
|
CVolumeDataItem::CVolumeDataItem(const char* filePath, const CREVolumeObject* pVolTexFactory)
|
|
: m_refCount(1)
|
|
, m_isValid(false)
|
|
, m_isCached(false)
|
|
, m_tightBounds(Vec3(-1, -1, -1), Vec3(1, 1, 1))
|
|
, m_scale(1)
|
|
, m_volDataFilePath(filePath)
|
|
, m_pData(0)
|
|
, m_pVolTex(0)
|
|
, m_pHull(0)
|
|
, m_pHullMesh(0)
|
|
{
|
|
if (filePath && pVolTexFactory)
|
|
{
|
|
SVolumeDataSrcB* pData = new SVolumeDataSrcB(VOLUME_SIZE, VOLUME_SIZE, VOLUME_SIZE);
|
|
if (pData && pData->m_pData)
|
|
{
|
|
if (CreateVolumeObject(m_volDataFilePath, *pData, m_tightBounds, m_scale))
|
|
{
|
|
m_pVolTex = pVolTexFactory->CreateVolumeTexture();
|
|
if (m_pVolTex)
|
|
{
|
|
m_isValid = m_pVolTex->Create(pData->m_width, pData->m_height, pData->m_depth, pData->m_pData);
|
|
}
|
|
|
|
m_pHull = new SVolumeDataHull();
|
|
if (CreateVolumeDataHull(*pData, *m_pHull))
|
|
{
|
|
m_pHullMesh = gEnv->pRenderer->CreateRenderMeshInitialized(m_pHull->m_pPts, (int) m_pHull->m_numPts, eVF_P3F,
|
|
m_pHull->m_pIdx, (int) m_pHull->m_numTris * 3, prtTriangleList, "VolumeObjectHull", "VolumeObjectHull");
|
|
m_isValid &= m_pHullMesh != 0;
|
|
}
|
|
else
|
|
{
|
|
m_isValid = false;
|
|
}
|
|
|
|
// delete hull in sys mem if no debug drawing is needed
|
|
SAFE_DELETE(m_pHull);
|
|
|
|
m_pData = new SVolumeDataSrcB(VOLUME_SHADOW_SIZE, VOLUME_SHADOW_SIZE, VOLUME_SHADOW_SIZE);
|
|
if (m_pData && m_pData->m_pData)
|
|
{
|
|
m_isValid &= CreateDownscaledVolumeObject(*pData, *m_pData);
|
|
}
|
|
else
|
|
{
|
|
m_isValid = false;
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(pData);
|
|
}
|
|
}
|
|
|
|
|
|
CVolumeDataItem::~CVolumeDataItem()
|
|
{
|
|
if (m_isCached)
|
|
{
|
|
assert(CVolumeDataCache::Access().GetItem(m_volDataFilePath) == this);
|
|
CVolumeDataCache::Access().RemoveItem(m_volDataFilePath);
|
|
}
|
|
|
|
SAFE_DELETE(m_pData);
|
|
SAFE_RELEASE(m_pVolTex);
|
|
SAFE_DELETE(m_pHull);
|
|
m_pHullMesh = NULL;
|
|
}
|
|
|
|
|
|
void CVolumeDataItem::AddRef()
|
|
{
|
|
++m_refCount;
|
|
}
|
|
|
|
|
|
void CVolumeDataItem::Release()
|
|
{
|
|
--m_refCount;
|
|
if (m_refCount <= 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
|
|
bool CVolumeDataItem::IsValid() const
|
|
{
|
|
return m_isValid;
|
|
}
|
|
|
|
|
|
void CVolumeDataItem::AddToCache()
|
|
{
|
|
if (m_isValid && !m_isCached)
|
|
{
|
|
CVolumeDataCache::Access().AddItem(m_volDataFilePath, this);
|
|
m_isCached = true;
|
|
}
|
|
}
|
|
|
|
|
|
const SVolumeDataSrcB* CVolumeDataItem::GetData() const
|
|
{
|
|
return m_pData;
|
|
}
|
|
|
|
|
|
IVolumeTexture* CVolumeDataItem::GetVolumeTexture() const
|
|
{
|
|
return m_pVolTex;
|
|
}
|
|
|
|
|
|
const AABB& CVolumeDataItem::GetTightBounds() const
|
|
{
|
|
return m_tightBounds;
|
|
}
|
|
|
|
|
|
float CVolumeDataItem::GetScale() const
|
|
{
|
|
return m_scale;
|
|
}
|
|
|
|
|
|
const SVolumeDataHull* CVolumeDataItem::GetHull() const
|
|
{
|
|
return m_pHull;
|
|
}
|
|
|
|
|
|
_smart_ptr<IRenderMesh> CVolumeDataItem::GetHullMesh()
|
|
{
|
|
return m_pHullMesh;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// volume shadow creator
|
|
|
|
class CVolumeShadowCreator
|
|
{
|
|
public:
|
|
static CVolumeShadowCreator* Create();
|
|
|
|
public:
|
|
int AddRef();
|
|
int Release();
|
|
|
|
void CalculateShadows(const Vec3& newLightDir, float shadowStrength, const CVolumeDataItem* pVolSrc, IVolumeTexture* pShadDst);
|
|
|
|
private:
|
|
CVolumeShadowCreator();
|
|
~CVolumeShadowCreator();
|
|
|
|
private:
|
|
int m_refCount;
|
|
SVolumeDataSrcB* m_pShad;
|
|
};
|
|
|
|
|
|
CVolumeShadowCreator* CVolumeShadowCreator::Create()
|
|
{
|
|
return new CVolumeShadowCreator;
|
|
}
|
|
|
|
|
|
CVolumeShadowCreator::CVolumeShadowCreator()
|
|
: m_refCount(1)
|
|
, m_pShad(0)
|
|
{
|
|
}
|
|
|
|
|
|
CVolumeShadowCreator::~CVolumeShadowCreator()
|
|
{
|
|
}
|
|
|
|
|
|
int CVolumeShadowCreator::AddRef()
|
|
{
|
|
++m_refCount;
|
|
return m_refCount;
|
|
}
|
|
|
|
|
|
int CVolumeShadowCreator::Release()
|
|
{
|
|
--m_refCount;
|
|
int ref(m_refCount);
|
|
if (m_refCount <= 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
|
|
void CVolumeShadowCreator::CalculateShadows(const Vec3& newLightDir, float shadowStrength, const CVolumeDataItem* pVolSrc, IVolumeTexture* pShadDst)
|
|
{
|
|
const SVolumeDataSrcB* pSrc(pVolSrc->GetData());
|
|
if (!pSrc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_pShad || m_pShad->m_width != pSrc->m_width || m_pShad->m_height != pSrc->m_height || m_pShad->m_depth != pSrc->m_depth)
|
|
{
|
|
SAFE_DELETE(m_pShad);
|
|
m_pShad = new SVolumeDataSrcB(pSrc->m_width, pSrc->m_height, pSrc->m_depth);
|
|
}
|
|
|
|
if (m_pShad)
|
|
{
|
|
//float t = gEnv->pTimer->GetAsyncCurTime();
|
|
CreateVolumeShadow(newLightDir, shadowStrength, *pSrc, *m_pShad);
|
|
//t = gEnv->pTimer->GetAsyncCurTime() - t;
|
|
//gEnv->pLog->Log("CreateVolumeShadow(%.3f, %.3f, %.3f) took %.2fms", newLightDir.x, newLightDir.y, newLightDir.z, t * 1000.0f);
|
|
pShadDst->Update(m_pShad->m_width, m_pShad->m_height, m_pShad->m_depth, m_pShad->m_pData);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CVolumeObjectRenderNode implementation
|
|
|
|
CVolumeShadowCreator* CVolumeObjectRenderNode::ms_pVolShadowCreator(0);
|
|
StaticInstance<CVolumeObjectRenderNode::VolumeObjectSet> CVolumeObjectRenderNode::ms_volumeObjects;
|
|
|
|
ICVar* CVolumeObjectRenderNode::ms_CV_volobj_stats(0);
|
|
int CVolumeObjectRenderNode::e_volobj_stats(0);
|
|
|
|
|
|
void CVolumeObjectRenderNode::MoveVolumeObjects()
|
|
{
|
|
for (VolumeObjectSet::iterator it(ms_volumeObjects.begin()), itEnd(ms_volumeObjects.end()); it != itEnd; ++it)
|
|
{
|
|
(*it)->Move();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
if (e_volobj_stats)
|
|
{
|
|
CryLogAlways("#VolumeObjects = %" PRISIZE_T, ms_volumeObjects.size());
|
|
CryLogAlways("#VolumeDataItems = %" PRISIZE_T, CVolumeDataCache::Access().size());
|
|
e_volobj_stats = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::RegisterVolumeObject(CVolumeObjectRenderNode* p)
|
|
{
|
|
VolumeObjectSet::iterator it(ms_volumeObjects.find(p));
|
|
assert(it == ms_volumeObjects.end() && "CVolumeObjectRenderNode::RegisterVolumeObject() -- Object already registered!");
|
|
if (it == ms_volumeObjects.end())
|
|
{
|
|
ms_volumeObjects.insert(p);
|
|
}
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::UnregisterVolumeObject(CVolumeObjectRenderNode* p)
|
|
{
|
|
VolumeObjectSet::iterator it(ms_volumeObjects.find(p));
|
|
assert(it != ms_volumeObjects.end() && "CVolumeObjectRenderNode::UnregisterVolumeObject() -- Object not registered or previously removed!");
|
|
if (it != ms_volumeObjects.end())
|
|
{
|
|
ms_volumeObjects.erase(it);
|
|
}
|
|
}
|
|
|
|
|
|
CVolumeObjectRenderNode::CVolumeObjectRenderNode()
|
|
: m_WSBBox()
|
|
, m_pos(0, 0, 0)
|
|
, m_origin(0, 0, 0)
|
|
, m_matOrig()
|
|
, m_mat()
|
|
, m_matInv()
|
|
, m_lastCachedLightDir(0, 0, 0)
|
|
, m_tightBoundsOS()
|
|
, m_moveProps()
|
|
, m_alpha(1)
|
|
, m_scale(1)
|
|
, m_shadowStrength(0.4f)
|
|
, m_pMaterial(0)
|
|
, m_pVolDataItem(0)
|
|
, m_pVolShadTex(0)
|
|
{
|
|
m_matOrig.SetIdentity();
|
|
m_mat.SetIdentity();
|
|
m_matInv.SetIdentity();
|
|
|
|
m_WSBBox = AABB(Vec3(-1, -1, -1), Vec3(1, 1, 1));
|
|
m_tightBoundsOS = AABB(Vec3(-1, -1, -1), Vec3(1, 1, 1));
|
|
|
|
m_moveProps.m_autoMove = false;
|
|
m_moveProps.m_speed = Vec3(0, 0, 0);
|
|
m_moveProps.m_spaceLoopBox = Vec3(2000.0f, 2000.0f, 2000.0f);
|
|
m_moveProps.m_fadeDistance = 0;
|
|
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
m_pRE[i] = (CREVolumeObject*) GetRenderer()->EF_CreateRE(eDATA_VolumeObject);
|
|
}
|
|
|
|
m_pMaterial = GetMatMan()->LoadMaterial("Materials/VolumeData/Default", false);
|
|
|
|
if (!ms_pVolShadowCreator)
|
|
{
|
|
ms_pVolShadowCreator = CVolumeShadowCreator::Create();
|
|
}
|
|
else
|
|
{
|
|
ms_pVolShadowCreator->AddRef();
|
|
}
|
|
|
|
RegisterVolumeObject(this);
|
|
|
|
if (!ms_CV_volobj_stats)
|
|
{
|
|
ms_CV_volobj_stats = REGISTER_CVAR(e_volobj_stats, 0, VF_NULL, "");
|
|
}
|
|
}
|
|
|
|
|
|
CVolumeObjectRenderNode::~CVolumeObjectRenderNode()
|
|
{
|
|
if (ms_pVolShadowCreator->Release() <= 0)
|
|
{
|
|
ms_pVolShadowCreator = 0;
|
|
}
|
|
|
|
SAFE_RELEASE(m_pVolShadTex);
|
|
SAFE_RELEASE(m_pVolDataItem);
|
|
|
|
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
|
|
{
|
|
if (m_pRE[i])
|
|
{
|
|
m_pRE[i]->Release(false);
|
|
m_pRE[i] = 0;
|
|
}
|
|
}
|
|
|
|
Get3DEngine()->FreeRenderNodeState(this);
|
|
|
|
UnregisterVolumeObject(this);
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::LoadVolumeData(const char* filePath)
|
|
{
|
|
CREVolumeObject* pVolTextureFactory = m_pRE[0]; // ok to use first instance as function is stateless, ptr is not store in objects created below
|
|
|
|
CVolumeDataItem* pNewVolDataItem(CVolumeDataCache::Access().GetItem(filePath));
|
|
if (pNewVolDataItem)
|
|
{
|
|
pNewVolDataItem->AddRef();
|
|
if (m_pVolDataItem)
|
|
{
|
|
m_pVolDataItem->Release();
|
|
}
|
|
m_pVolDataItem = pNewVolDataItem;
|
|
InvalidateLastCachedLightDir();
|
|
}
|
|
else
|
|
{
|
|
pNewVolDataItem = CVolumeDataItem::Create(filePath, pVolTextureFactory);
|
|
if (pNewVolDataItem && pNewVolDataItem->IsValid())
|
|
{
|
|
pNewVolDataItem->AddToCache();
|
|
if (m_pVolDataItem)
|
|
{
|
|
m_pVolDataItem->Release();
|
|
}
|
|
m_pVolDataItem = pNewVolDataItem;
|
|
InvalidateLastCachedLightDir();
|
|
}
|
|
else
|
|
{
|
|
SAFE_RELEASE(pNewVolDataItem);
|
|
}
|
|
}
|
|
|
|
SetMatrix(m_matOrig);
|
|
|
|
if (m_pVolDataItem && pVolTextureFactory && !m_pVolShadTex)
|
|
{
|
|
m_pVolShadTex = pVolTextureFactory->CreateVolumeTexture();
|
|
if (!m_pVolShadTex->Create(VOLUME_SHADOW_SIZE, VOLUME_SHADOW_SIZE, VOLUME_SHADOW_SIZE, 0))
|
|
{
|
|
SAFE_RELEASE(m_pVolShadTex);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::SetProperties([[maybe_unused]] const SVolumeObjectProperties& properties)
|
|
{
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::SetMovementProperties(const SVolumeObjectMovementProperties& properties)
|
|
{
|
|
m_moveProps = properties;
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::SetMatrix(const Matrix34& mat)
|
|
{
|
|
m_matOrig = mat;
|
|
float initialScale(m_pVolDataItem ? m_pVolDataItem->GetScale() : 1);
|
|
SetMatrixInternal(mat * Matrix33::CreateScale(Vec3(initialScale)), true);
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::SetMatrixInternal(const Matrix34& mat, bool updateOrigin)
|
|
{
|
|
Get3DEngine()->UnRegisterEntityAsJob(this);
|
|
|
|
if (updateOrigin)
|
|
{
|
|
m_origin = mat.GetTranslation();
|
|
}
|
|
|
|
m_pos = mat.GetTranslation();
|
|
m_mat = mat;
|
|
m_matInv = mat.GetInverted();
|
|
|
|
m_tightBoundsOS = m_pVolDataItem ? m_pVolDataItem->GetTightBounds() : AABB(Vec3(-1, -1, -1), Vec3(1, 1, 1));
|
|
m_WSBBox.SetTransformedAABB(m_mat, m_tightBoundsOS);
|
|
|
|
Vec3 scale(m_mat.GetColumn(0).GetLength(), m_mat.GetColumn(1).GetLength(), m_mat.GetColumn(2).GetLength());
|
|
|
|
const float Epsilon = 0.001f;
|
|
AZ_Warning(
|
|
"VolumeObjectRenderNode",
|
|
fabsf(scale.x - scale.y) < Epsilon && fabsf(scale.x - scale.z) < Epsilon && fabsf(scale.y - scale.z) < Epsilon,
|
|
"VolumeObjectRenderNode is using non-uniform scale. Forcing to uniform...");
|
|
m_scale = max(max(scale.x, scale.y), scale.z);
|
|
|
|
Get3DEngine()->RegisterEntity(this);
|
|
}
|
|
|
|
|
|
const char* CVolumeObjectRenderNode::GetEntityClassName() const
|
|
{
|
|
return "VolumeObject";
|
|
}
|
|
|
|
|
|
const char* CVolumeObjectRenderNode::GetName() const
|
|
{
|
|
return "VolumeObject";
|
|
}
|
|
|
|
|
|
bool CVolumeObjectRenderNode::IsViewerInsideVolume(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 camPosOS(m_matInv.TransformPoint(cam.GetPosition()));
|
|
const Vec3& scale(m_tightBoundsOS.max);
|
|
return fabsf(camPosOS.x) < scale.x && fabsf(camPosOS.y) < scale.y && fabsf(camPosOS.z) < scale.z;
|
|
}
|
|
|
|
|
|
bool CVolumeObjectRenderNode::NearPlaneIntersectsVolume(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam = passInfo.GetCamera();
|
|
|
|
// check if bounding box intersects the near clipping plane
|
|
const Plane* pNearPlane(cam.GetFrustumPlane(FR_PLANE_NEAR));
|
|
Vec3 pntOnNearPlane(cam.GetPosition() - pNearPlane->DistFromPlane(cam.GetPosition()) * pNearPlane->n);
|
|
Vec3 pntOnNearPlaneOS(m_matInv.TransformPoint(pntOnNearPlane));
|
|
|
|
Vec3 nearPlaneOS_n(m_matInv.TransformVector(pNearPlane->n) /*.GetNormalized()*/);
|
|
f32 nearPlaneOS_d(-nearPlaneOS_n.Dot(pntOnNearPlaneOS));
|
|
|
|
// get extreme lengths
|
|
float t(fabsf(nearPlaneOS_n.x * m_tightBoundsOS.max.x) + fabsf(nearPlaneOS_n.y * m_tightBoundsOS.max.y) + fabsf(nearPlaneOS_n.z * m_tightBoundsOS.max.z));
|
|
//float t(0.0f);
|
|
//if(nearPlaneOS_n.x >= 0) t += -nearPlaneOS_n.x; else t += nearPlaneOS_n.x;
|
|
//if(nearPlaneOS_n.y >= 0) t += -nearPlaneOS_n.y; else t += nearPlaneOS_n.y;
|
|
//if(nearPlaneOS_n.z >= 0) t += -nearPlaneOS_n.z; else t += nearPlaneOS_n.z;
|
|
|
|
float t0(t + nearPlaneOS_d);
|
|
float t1(-t + nearPlaneOS_d);
|
|
|
|
return t0 * t1 < 0.0f;
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::InvalidateLastCachedLightDir()
|
|
{
|
|
m_lastCachedLightDir = Vec3(0, 0, 0);
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::UpdateShadows()
|
|
{
|
|
Vec3 newLightDir(m_p3DEngine->GetSunDirNormalized());
|
|
|
|
float shadowStrength(GetFloatCVar(e_VolObjShadowStrength));
|
|
if (ms_pVolShadowCreator)
|
|
{
|
|
Vec3 newLightDirLS(m_matInv.TransformVector(newLightDir).GetNormalized()); // 0.999f = 1.56 deg = ??? minutes of TOD update, TODO: make adjustable
|
|
if (m_shadowStrength != shadowStrength || newLightDirLS.Dot(m_lastCachedLightDir) < 0.999f)
|
|
{
|
|
ms_pVolShadowCreator->CalculateShadows(-newLightDirLS, shadowStrength, m_pVolDataItem, m_pVolShadTex);
|
|
m_lastCachedLightDir = newLightDirLS;
|
|
m_shadowStrength = shadowStrength;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Plane CVolumeObjectRenderNode::GetVolumeTraceStartPlane(bool viewerInsideVolume, const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 vdir(cam.GetViewdir());
|
|
Vec3 vpos(cam.GetPosition());
|
|
|
|
if (!viewerInsideVolume)
|
|
{
|
|
const Vec3 bbp[8] =
|
|
{
|
|
Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z),
|
|
Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z),
|
|
Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z),
|
|
Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z)
|
|
};
|
|
|
|
Plane viewPlane(vdir, -vdir.Dot(vpos));
|
|
|
|
Vec3 p(m_mat * bbp[0]);
|
|
float d(viewPlane.DistFromPlane(p));
|
|
|
|
for (int i(1); i < 8; ++i)
|
|
{
|
|
Vec3 ptmp(m_mat * bbp[i]);
|
|
float dtmp(viewPlane.DistFromPlane(ptmp));
|
|
|
|
if (dtmp < d)
|
|
{
|
|
p = ptmp;
|
|
d = dtmp;
|
|
}
|
|
}
|
|
|
|
return Plane(vdir, -vdir.Dot(p));
|
|
}
|
|
else
|
|
{
|
|
return Plane(vdir, -vdir.Dot(vpos));
|
|
}
|
|
}
|
|
|
|
|
|
float CVolumeObjectRenderNode::GetDistanceToCamera(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
const Vec3 bbp[8] =
|
|
{
|
|
m_mat* Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
m_mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
m_mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
m_mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
m_mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z),
|
|
m_mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z),
|
|
m_mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z),
|
|
m_mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z)
|
|
};
|
|
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
const Plane* pNearPlane(cam.GetFrustumPlane(FR_PLANE_NEAR));
|
|
const Vec3 camPos(cam.GetPosition());
|
|
|
|
f32 distSq(0.0f);
|
|
for (int i(0); i < 8; ++i)
|
|
{
|
|
float tmpDistSq((bbp[i] - camPos).GetLengthSquared());
|
|
if (tmpDistSq > distSq && pNearPlane->DistFromPlane(bbp[i]) < 0.0f)
|
|
{
|
|
distSq = tmpDistSq;
|
|
}
|
|
}
|
|
|
|
return sqrt_tpl(distSq);
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// anything to render?
|
|
if (passInfo.IsRecursivePass() || !m_pMaterial || !m_pVolDataItem || !m_pVolShadTex || !passInfo.RenderClouds())
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRenderer* pRenderer(GetRenderer());
|
|
|
|
const int fillThreadID = passInfo.ThreadID();
|
|
|
|
// get render objects
|
|
CRenderObject* pRO = pRenderer->EF_GetObject_Temp(fillThreadID);
|
|
if (!pRO || !m_pRE[fillThreadID])
|
|
{
|
|
return;
|
|
}
|
|
|
|
// update shadow volume
|
|
UpdateShadows();
|
|
|
|
// set basic render object properties
|
|
pRO->m_II.m_Matrix = m_mat;
|
|
pRO->m_fSort = 0;
|
|
pRO->m_fDistance = GetDistanceToCamera(passInfo);
|
|
|
|
// transform camera into object space
|
|
const CCamera& cam(passInfo.GetCamera());
|
|
Vec3 viewerPosWS(cam.GetPosition());
|
|
Vec3 viewerPosOS(m_matInv * viewerPosWS);
|
|
|
|
// set render element attributes
|
|
bool viewerInsideVolume(IsViewerInsideVolume(passInfo));
|
|
bool nearPlaneIntersectsVolume(NearPlaneIntersectsVolume(passInfo));
|
|
m_pRE[fillThreadID]->m_center = m_pos;
|
|
m_pRE[fillThreadID]->m_matInv = m_matInv;
|
|
m_pRE[fillThreadID]->m_eyePosInWS = viewerPosWS;
|
|
m_pRE[fillThreadID]->m_eyePosInOS = viewerPosOS;
|
|
m_pRE[fillThreadID]->m_volumeTraceStartPlane = GetVolumeTraceStartPlane(viewerInsideVolume, passInfo);
|
|
m_pRE[fillThreadID]->m_renderBoundsOS = m_tightBoundsOS;
|
|
m_pRE[fillThreadID]->m_pHullMesh = m_pVolDataItem->GetHullMesh();
|
|
m_pRE[fillThreadID]->m_viewerInsideVolume = viewerInsideVolume;
|
|
m_pRE[fillThreadID]->m_nearPlaneIntersectsVolume = nearPlaneIntersectsVolume;
|
|
m_pRE[fillThreadID]->m_alpha = m_alpha;
|
|
m_pRE[fillThreadID]->m_scale = m_scale;
|
|
m_pRE[fillThreadID]->m_pDensVol = m_pVolDataItem->GetVolumeTexture();
|
|
m_pRE[fillThreadID]->m_pShadVol = m_pVolShadTex;
|
|
|
|
float mvd(GetMaxViewDist());
|
|
float d((passInfo.GetCamera().GetPosition() - m_mat.GetTranslation()).GetLength());
|
|
if (d > 0.9f * mvd)
|
|
{
|
|
float s(clamp_tpl(1.0f - (d - 0.9f * mvd) / (0.1f * mvd), 0.0f, 1.0f));
|
|
m_pRE[fillThreadID]->m_alpha *= s;
|
|
}
|
|
|
|
// add to renderer
|
|
SShaderItem& shaderItem(m_pMaterial->GetShaderItem(0));
|
|
int afterWater(GetObjManager()->IsAfterWater(m_pos, passInfo));
|
|
pRenderer->EF_AddEf(m_pRE[fillThreadID], shaderItem, pRO, passInfo, EFSLIST_TRANSP, afterWater, SRendItemSorter(rParam.rendItemSorter));
|
|
|
|
#if 0
|
|
IRenderAuxGeom* p = pRenderer->GetIRenderAuxGeom();
|
|
if (p)
|
|
{
|
|
const Matrix34& mat = m_mat;
|
|
Vec3 bpp[8] =
|
|
{
|
|
mat* Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.min.z),
|
|
mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.min.z),
|
|
|
|
mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z),
|
|
mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.min.y, m_tightBoundsOS.max.z),
|
|
mat * Vec3(m_tightBoundsOS.max.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z),
|
|
mat * Vec3(m_tightBoundsOS.min.x, m_tightBoundsOS.max.y, m_tightBoundsOS.max.z)
|
|
};
|
|
|
|
p->SetRenderFlags(e_Def3DPublicRenderflags);
|
|
|
|
p->DrawLine(bpp[0], ColorB(255, 0, 0, 255), bpp[1], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[1], ColorB(255, 0, 0, 255), bpp[2], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[2], ColorB(255, 0, 0, 255), bpp[3], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[3], ColorB(255, 0, 0, 255), bpp[0], ColorB(255, 0, 0, 255));
|
|
|
|
p->DrawLine(bpp[4], ColorB(255, 0, 0, 255), bpp[5], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[5], ColorB(255, 0, 0, 255), bpp[6], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[6], ColorB(255, 0, 0, 255), bpp[7], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[7], ColorB(255, 0, 0, 255), bpp[4], ColorB(255, 0, 0, 255));
|
|
|
|
p->DrawLine(bpp[0], ColorB(255, 0, 0, 255), bpp[4], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[1], ColorB(255, 0, 0, 255), bpp[5], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[2], ColorB(255, 0, 0, 255), bpp[6], ColorB(255, 0, 0, 255));
|
|
p->DrawLine(bpp[3], ColorB(255, 0, 0, 255), bpp[7], ColorB(255, 0, 0, 255));
|
|
|
|
const SVolumeDataHull* pHull(m_pVolDataItem->GetHull());
|
|
if (pHull)
|
|
{
|
|
for (size_t i = 0; i < pHull->m_numTris; ++i)
|
|
{
|
|
p->DrawTriangle(mat * pHull->m_pPts[pHull->m_pIdx[i * 3]].xyz, ColorB(255, 0, 255, 255),
|
|
mat * pHull->m_pPts[pHull->m_pIdx[i * 3 + 1]].xyz, ColorB(255, 0, 255, 255),
|
|
mat * pHull->m_pPts[pHull->m_pIdx[i * 3 + 2]].xyz, ColorB(255, 0, 255, 255));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
|
|
{
|
|
m_pMaterial = pMat;
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::Precache()
|
|
{
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
SIZER_COMPONENT_NAME(pSizer, "VolumeObjectRenderNode");
|
|
pSizer->AddObject(this, sizeof(*this));
|
|
}
|
|
|
|
|
|
void CVolumeObjectRenderNode::Move()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
m_alpha = 1;
|
|
|
|
Vec3 pos(m_mat.GetTranslation());
|
|
|
|
ITimer* pTimer(gEnv->pTimer);
|
|
if (m_moveProps.m_autoMove)
|
|
{
|
|
// update position
|
|
float deltaTime = pTimer->GetFrameTime();
|
|
|
|
assert(deltaTime >= 0);
|
|
|
|
pos += deltaTime * m_moveProps.m_speed;
|
|
|
|
// constrain movement to specified loop box
|
|
Vec3 loopBoxMin(m_origin - m_moveProps.m_spaceLoopBox);
|
|
Vec3 loopBoxMax(m_origin + m_moveProps.m_spaceLoopBox);
|
|
|
|
if (pos.x < loopBoxMin.x)
|
|
{
|
|
pos.x = loopBoxMax.x;
|
|
}
|
|
if (pos.y < loopBoxMin.y)
|
|
{
|
|
pos.y = loopBoxMax.y;
|
|
}
|
|
if (pos.z < loopBoxMin.z)
|
|
{
|
|
pos.z = loopBoxMax.z;
|
|
}
|
|
|
|
if (pos.x > loopBoxMax.x)
|
|
{
|
|
pos.x = loopBoxMin.x;
|
|
}
|
|
if (pos.y > loopBoxMax.y)
|
|
{
|
|
pos.y = loopBoxMin.y;
|
|
}
|
|
if (pos.z > loopBoxMax.z)
|
|
{
|
|
pos.z = loopBoxMin.z;
|
|
}
|
|
|
|
// set new position
|
|
Matrix34 mat(m_mat);
|
|
mat.SetTranslation(pos);
|
|
SetMatrixInternal(mat, false);
|
|
|
|
// fade out clouds at the borders of the loop box
|
|
if (m_moveProps.m_fadeDistance > 0)
|
|
{
|
|
Vec3 fade(max(m_moveProps.m_spaceLoopBox.x, m_moveProps.m_fadeDistance),
|
|
max(m_moveProps.m_spaceLoopBox.y, m_moveProps.m_fadeDistance),
|
|
max(m_moveProps.m_spaceLoopBox.z, m_moveProps.m_fadeDistance));
|
|
|
|
fade -= Vec3(fabs(pos.x - m_origin.x), fabs(pos.y - m_origin.y), fabs(pos.z - m_origin.z));
|
|
|
|
m_alpha = clamp_tpl(min(min(fade.x, fade.y), fade.z) / m_moveProps.m_fadeDistance, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((m_origin - pos).GetLengthSquared() > 1e-4f)
|
|
{
|
|
Matrix34 mat(m_mat);
|
|
mat.SetTranslation(m_origin);
|
|
SetMatrixInternal(mat, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CVolumeObjectRenderNode::OffsetPosition(const Vec3& delta)
|
|
{
|
|
if (m_pRNTmpData)
|
|
{
|
|
m_pRNTmpData->OffsetPosition(delta);
|
|
}
|
|
m_WSBBox.Move(delta);
|
|
m_pos += delta;
|
|
m_origin += delta;
|
|
m_matOrig.SetTranslation(m_matOrig.GetTranslation() + delta);
|
|
m_mat.SetTranslation(m_mat.GetTranslation() + delta);
|
|
m_matInv = m_mat.GetInverted();
|
|
}
|