merging latest origin

This commit is contained in:
karlberg
2021-05-03 21:27:27 -07:00
4864 changed files with 37281 additions and 612775 deletions
-3
View File
@@ -9,8 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(Cry3DEngine)
add_subdirectory(CryCommon)
add_subdirectory(CryFont)
add_subdirectory(CrySystem)
add_subdirectory(RenderDll)
@@ -1,733 +0,0 @@
/*
* 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.
// Description : Light sources manager
#include "Cry3DEngine_precompiled.h"
#include "3dEngine.h"
#include "ObjMan.h"
#include "VisAreas.h"
#include "AABBSV.h"
#include "LightEntity.h"
#include "ObjectsTree.h"
#include "ClipVolumeManager.h"
ILightSource* C3DEngine::CreateLightSource()
{
// construct new object
CLightEntity* pLightEntity = new CLightEntity();
m_lstStaticLights.Add(pLightEntity);
return pLightEntity;
}
void C3DEngine::DeleteLightSource(ILightSource* pLightSource)
{
if (m_lstStaticLights.Delete((CLightEntity*)pLightSource) || pLightSource == m_pSun)
{
if (pLightSource == m_pSun)
{
m_pSun = NULL;
}
delete pLightSource;
}
else
{
assert(!"Light object not found");
}
}
void CLightEntity::Release(bool)
{
Get3DEngine()->UnRegisterEntityDirect(this);
Get3DEngine()->DeleteLightSource(this);
}
void CLightEntity::SetLightProperties(const CDLight& light)
{
C3DEngine* engine = Get3DEngine();
m_light = light;
m_bShadowCaster = (m_light.m_Flags & DLF_CASTSHADOW_MAPS) != 0;
m_light.m_fBaseRadius = m_light.m_fRadius;
m_light.m_fLightFrustumAngle = CLAMP(m_light.m_fLightFrustumAngle, 0.f, (LIGHT_PROJECTOR_MAX_FOV / 2.f));
if (!(m_light.m_Flags & (DLF_PROJECT | DLF_AREA_LIGHT)))
{
m_light.m_fLightFrustumAngle = 90.f / 2.f;
}
m_light.m_pOwner = this;
if (m_light.m_Flags & DLF_ATTACH_TO_SUN)
{
m_dwRndFlags |= ERF_RENDER_ALWAYS | ERF_HUD;
}
engine->GetLightEntities()->Delete((ILightSource*)this);
PodArray<ILightSource*>& lightEntities = *engine->GetLightEntities();
//on consoles we force all lights (except sun) to be deferred
if (GetCVars()->e_DynamicLightsForceDeferred && !(m_light.m_Flags & (DLF_SUN | DLF_POST_3D_RENDERER)))
{
m_light.m_Flags |= DLF_DEFERRED_LIGHT;
}
if (light.m_Flags & DLF_DEFERRED_LIGHT)
{
lightEntities.Add((ILightSource*)this);
}
else
{
lightEntities.InsertBefore((ILightSource*)this, 0);
}
}
void C3DEngine::ResetCasterCombinationsCache()
{
for (int nSunInUse = 0; nSunInUse < 2; nSunInUse++)
{
// clear user counters
for (ShadowFrustumListsCacheUsers::iterator it = m_FrustumsCacheUsers[nSunInUse].begin(); it != m_FrustumsCacheUsers[nSunInUse].end(); ++it)
{
it->second = 0;
}
}
}
void C3DEngine::DeleteAllStaticLightSources()
{
for (int i = 0; i < m_lstStaticLights.Count(); i++)
{
delete m_lstStaticLights[i];
}
m_lstStaticLights.Reset();
m_pSun = NULL;
}
void C3DEngine::InitShadowFrustums(const SRenderingPassInfo& passInfo)
{
assert(passInfo.IsGeneralPass());
FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
AZ_TRACE_METHOD();
if (m_pSun)
{
CDLight* pLight = &m_pSun->GetLightProperties();
CLightEntity* pLightEntity = (CLightEntity*)pLight->m_pOwner;
if (passInfo.RenderShadows() && (pLight->m_Flags & DLF_CASTSHADOW_MAPS) && pLight->m_Id >= 0)
{
pLightEntity->UpdateGSMLightSourceShadowFrustum(passInfo);
if (pLightEntity->m_pShadowMapInfo)
{
pLight->m_pShadowMapFrustums = pLightEntity->m_pShadowMapInfo->pGSM;
}
}
_smart_ptr<IMaterial> pMat = pLightEntity->GetMaterial();
if (pMat)
{
pLight->m_Shader = pMat->GetShaderItem();
}
// update copy of light ion the renderer
if (pLight->m_Id >= 0)
{
CDLight* pRndLight = NULL;
GetRenderer()->EF_Query(EFQ_LightSource, pLight->m_Id, pRndLight);
assert(pLight->m_Id == pRndLight->m_Id);
pRndLight->m_pShadowMapFrustums = pLight->m_pShadowMapFrustums;
pRndLight->m_Shader = pLight->m_Shader;
pRndLight->m_Flags = pLight->m_Flags;
}
// add per object shadow frustums
m_nCustomShadowFrustumCount = 0;
if (passInfo.RenderShadows() && GetCVars()->e_ShadowsPerObject > 0)
{
const uint nFrustumCount = m_lstPerObjectShadows.size();
if (nFrustumCount > m_lstCustomShadowFrustums.size())
{
m_lstCustomShadowFrustums.resize(nFrustumCount);
}
for (uint i = 0; i < nFrustumCount; ++i)
{
if (m_lstPerObjectShadows[i].pCaster)
{
ShadowMapFrustum* pFr = &m_lstCustomShadowFrustums[i];
pFr->m_eFrustumType = ShadowMapFrustum::e_PerObject;
CLightEntity::ProcessPerObjectFrustum(pFr, &m_lstPerObjectShadows[i], m_pSun, passInfo);
++m_nCustomShadowFrustumCount;
}
}
}
}
if (passInfo.RenderShadows())
{
ResetCasterCombinationsCache();
}
}
void C3DEngine::AddPerObjectShadow(IShadowCaster* pCaster, float fConstBias, float fSlopeBias, float fJitter, const Vec3& vBBoxScale, uint nTexSize)
{
SPerObjectShadow* pOS = GetPerObjectShadow(pCaster);
if (!pOS)
{
pOS = &m_lstPerObjectShadows.AddNew();
}
pOS->pCaster = pCaster;
pOS->fConstBias = fConstBias;
pOS->fSlopeBias = fSlopeBias;
pOS->fJitter = fJitter;
pOS->vBBoxScale = vBBoxScale;
pOS->nTexSize = nTexSize;
}
void C3DEngine::RemovePerObjectShadow(IShadowCaster* pCaster)
{
SPerObjectShadow* pOS = GetPerObjectShadow(pCaster);
if (pOS)
{
FRAME_PROFILER("C3DEngine::RemovePerObjectShadow", GetSystem(), PROFILE_3DENGINE);
size_t nIndex = (size_t)(pOS - m_lstPerObjectShadows.begin());
m_lstPerObjectShadows.Delete(nIndex);
}
}
struct SPerObjectShadow* C3DEngine::GetPerObjectShadow(IShadowCaster* pCaster)
{
for (int i = 0; i < m_lstPerObjectShadows.Count(); ++i)
{
if (m_lstPerObjectShadows[i].pCaster == pCaster)
{
return &m_lstPerObjectShadows[i];
}
}
return NULL;
}
void C3DEngine::GetCustomShadowMapFrustums(ShadowMapFrustum*& arrFrustums, int& nFrustumCount)
{
arrFrustums = m_lstCustomShadowFrustums.begin();
nFrustumCount = m_nCustomShadowFrustumCount;
}
// delete pLight->m_pProjCamera;
//pLight->m_pProjCamera=0;
//if(pLight->m_pShader)
// SAFE_RELEASE(pLight->m_pShader);
namespace
{
static inline bool CmpCastShadowFlag(const CDLight* p1, const CDLight* p2)
{
// move sun first
if ((p1->m_Flags & DLF_SUN) > (p2->m_Flags & DLF_SUN))
{
return true;
}
else if ((p1->m_Flags & DLF_SUN) < (p2->m_Flags & DLF_SUN))
{
return false;
}
// move shadow casters first
if ((p1->m_Flags & DLF_CASTSHADOW_MAPS) > (p2->m_Flags & DLF_CASTSHADOW_MAPS))
{
return true;
}
else if ((p1->m_Flags & DLF_CASTSHADOW_MAPS) < (p2->m_Flags & DLF_CASTSHADOW_MAPS))
{
return false;
}
// get some sorting consistency for shadow casters
if (p1->m_pOwner > p2->m_pOwner)
{
return true;
}
else if (p1->m_pOwner < p2->m_pOwner)
{
return false;
}
return false;
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SubmitSun(const SRenderingPassInfo& passInfo)
{
assert(passInfo.IsGeneralPass());
FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
AZ_TRACE_METHOD();
if (m_pSun)
{
CDLight* light = &m_pSun->GetLightProperties();
GetRenderer()->EF_ADDDlight(light, passInfo);
}
}
void C3DEngine::RemoveEntityLightSources(IRenderNode* pEntity)
{
for (int i = 0; i < m_lstStaticLights.Count(); i++)
{
if (m_lstStaticLights[i] == pEntity)
{
m_lstStaticLights.Delete(i);
if (pEntity == m_pSun)
{
m_pSun = NULL;
}
i--;
}
}
}
ILightSource* C3DEngine::GetSunEntity()
{
return m_pSun;
}
void C3DEngine::OnCasterDeleted(IShadowCaster* pCaster)
{
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_3DENGINE);
{ // make sure pointer to object will not be used somewhere in the renderer
if (m_pSun)
{
m_pSun->OnCasterDeleted(pCaster);
}
if (GetRenderer()->GetActiveGPUCount() > 1)
{
if (ShadowFrustumMGPUCache* pFrustumCache = GetRenderer()->GetShadowFrustumMGPUCache())
{
pFrustumCache->DeleteFromCache(pCaster);
}
}
// remove from per object shadows list
RemovePerObjectShadow(pCaster);
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::Init()
{
m_bUpdateLightVolumes = false;
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
{
m_pLightVolumes[i].reserve(LV_MAX_COUNT);
m_pLightVolsInfo[i].reserve(LV_MAX_COUNT);
}
memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
}
void CLightVolumesMgr::Reset()
{
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
{
stl::free_container(m_pLightVolumes[i]);
}
m_bUpdateLightVolumes = false;
memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
}
//////////////////////////////////////////////////////////////////////////
uint16 CLightVolumesMgr::RegisterVolume(const Vec3& vPos, f32 fRadius, uint8 nClipVolumeRef, const SRenderingPassInfo& passInfo)
{
DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
IF ((m_bUpdateLightVolumes && (lightVolsInfo.size() < LV_MAX_COUNT)) && fRadius < 256.0f, 1)
{
FUNCTION_PROFILER_3DENGINE;
int32 nPosx = (int32)(floorf(vPos.x * LV_CELL_RSIZEX));
int32 nPosy = (int32)(floorf(vPos.y * LV_CELL_RSIZEY));
int32 nPosz = (int32)(floorf(vPos.z * LV_CELL_RSIZEZ));
// Check if world cell has any light volume, else add new one
uint16 nHashIndex = GetWorldHashBucketKey(nPosx, nPosy, nPosz);
uint16* pCurrentVolumeID = &m_nWorldCells[nHashIndex];
while (*pCurrentVolumeID != 0)
{
SLightVolInfo& sVolInfo = *lightVolsInfo[*pCurrentVolumeID - 1];
int32 nVolumePosx = (int32)(floorf(sVolInfo.vVolume.x * LV_CELL_RSIZEX));
int32 nVolumePosy = (int32)(floorf(sVolInfo.vVolume.y * LV_CELL_RSIZEY));
int32 nVolumePosz = (int32)(floorf(sVolInfo.vVolume.z * LV_CELL_RSIZEZ));
if (nPosx == nVolumePosx &&
nPosy == nVolumePosy &&
nPosz == nVolumePosz &&
nClipVolumeRef == sVolInfo.nClipVolumeID)
{
return (uint16) * pCurrentVolumeID;
}
pCurrentVolumeID = &sVolInfo.nNextVolume;
}
// create new volume
SLightVolInfo* pLightVolInfo = new SLightVolInfo(vPos, fRadius, nClipVolumeRef);
lightVolsInfo.push_back(pLightVolInfo);
*pCurrentVolumeID = lightVolsInfo.size();
return *pCurrentVolumeID;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::RegisterLight(const CDLight& pDL, uint32 nLightID, [[maybe_unused]] const SRenderingPassInfo& passInfo)
{
IF ((m_bUpdateLightVolumes && !(pDL.m_Flags & LV_DLF_LIGHTVOLUMES_MASK)), 1)
{
FUNCTION_PROFILER_3DENGINE;
const f32 fColCheck = (f32) fsel(pDL.m_Color.r + pDL.m_Color.g + pDL.m_Color.b - 0.333f, 1.0f, 0.0f); //light color > threshold
const f32 fRadCheck = (f32) fsel(pDL.m_fRadius - 0.5f, 1.0f, 0.0f); //light radius > threshold
if (fColCheck * fRadCheck)
{
//if the radius is large than certain value, all the the world light cells will be lighted anyway. So we just add the light to all the cells
//the input radius restriction will be added too
if(floorf(pDL.m_fRadius*LV_LIGHT_CELL_R_SIZE) > LV_LIGHTS_WORLD_BUCKET_SIZE)
{
for (int32 idx = 0; idx < LV_LIGHTS_WORLD_BUCKET_SIZE; idx++)
{
SLightCell& lightCell = m_pWorldLightCells[idx];
CryPrefetch(&lightCell);
if (lightCell.nLightCount < LV_LIGHTS_MAX_COUNT)
{
lightCell.nLightID[lightCell.nLightCount] = nLightID;
lightCell.nLightCount += 1;
}
}
}
else
{
int32 nMiny = (int32)(floorf((pDL.m_Origin.y - pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
int32 nMaxy = (int32)(floorf((pDL.m_Origin.y + pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
int32 nMinx = (int32)(floorf((pDL.m_Origin.x - pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
int32 nMaxx = (int32)(floorf((pDL.m_Origin.x + pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
// Register light into all cells touched by light radius
for (int32 y = nMiny, ymax = nMaxy; y <= ymax; ++y)
{
for (int32 x = nMinx, xmax = nMaxx; x <= xmax; ++x)
{
SLightCell& lightCell = m_pWorldLightCells[GetWorldHashBucketKey(x, y, 1, LV_LIGHTS_WORLD_BUCKET_SIZE)];
CryPrefetch(&lightCell);
if (lightCell.nLightCount < LV_LIGHTS_MAX_COUNT)
{
//only if the las light added to the cell wasn't the same light
if (!(lightCell.nLightCount > 0 && lightCell.nLightID[lightCell.nLightCount - 1] == nLightID))
{
lightCell.nLightID[lightCell.nLightCount] = nLightID;
lightCell.nLightCount += 1;
}
}
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::AddLight(const SRenderLight& pLight, const SLightVolInfo* __restrict pVolInfo, SLightVolume& pVolume)
{
// Check for clip volume
if (pLight.m_nStencilRef[0] == pVolInfo->nClipVolumeID || pLight.m_nStencilRef[1] == pVolInfo->nClipVolumeID ||
pLight.m_nStencilRef[0] == CClipVolumeManager::AffectsEverythingStencilRef)
{
const Vec4* __restrict vLight = (Vec4*) &pLight.m_Origin.x;
const Vec4& vVolume = pVolInfo->vVolume;
const f32 fDimCheck = (f32) fsel(vLight->w - vVolume.w * 0.1f, 1.0f, 0.0f); //light radius not more than 10x smaller than volume radius
const f32 fOverlapCheck = (f32) fsel(sqr(vVolume.x - vLight->x) + sqr(vVolume.y - vLight->y) + sqr(vVolume.z - vLight->z) - sqr(vVolume.w + vLight->w), 0.0f, 1.0f);// touches volumes
if (fDimCheck * fOverlapCheck)
{
float fAttenuationBulbSize = pLight.m_fAttenuationBulbSize;
Vec3 lightColor = *((Vec3*)&pLight.m_Color);
// Adjust light intensity so that the intended brightness is reached 1 meter from the light's surface
IF (!(pLight.m_Flags & (DLF_AREA_LIGHT | DLF_AMBIENT)), 1)
{
fAttenuationBulbSize = max(fAttenuationBulbSize, 0.001f);
// Solve I * 1 / (1 + d/lightsize)^2 = 1
float intensityMul = 1.0f + 1.0f / fAttenuationBulbSize;
intensityMul *= intensityMul;
lightColor *= intensityMul;
}
pVolume.pData.push_back();
SLightVolume::SLightData& lightData = pVolume.pData[pVolume.pData.size() - 1];
lightData.vPos = *vLight;
lightData.vColor = Vec4(lightColor, fAttenuationBulbSize);
lightData.vParams = Vec4(0.f, 0.f, 0.f, 0.f);
IF (pLight.m_Flags & DLF_PROJECT, 1)
{
lightData.vParams = Vec4(pLight.m_ObjMatrix.GetColumn0(), cos_tpl(DEG2RAD(pLight.m_fLightFrustumAngle)));
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::Update(const SRenderingPassInfo& passInfo)
{
uint32 nThreadID = passInfo.ThreadID();
DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[nThreadID];
if (!m_bUpdateLightVolumes || lightVolsInfo.empty())
{
return;
}
FUNCTION_PROFILER_3DENGINE;
TArray<SRenderLight>* pLights = GetRenderer()->EF_GetDeferredLights(passInfo);
const uint32 nLightCount = pLights->size();
uint32 nLightVols = lightVolsInfo.size();
LightVolumeVector& lightVols = m_pLightVolumes[nThreadID];
uint32 existingLightVolsCount = 0; //This is 0, that just means we will be overwriting all existing light volumes
//If this is a recursive pass (not the first time that this is called this frame), we're just going to be adding on new light volumes to the existing collection
if (passInfo.IsRecursivePass())
{
existingLightVolsCount = lightVols.size();
//If no new light volumes have been added, don't bother updating
if (nLightVols == existingLightVolsCount)
{
return;
}
}
lightVols.resize(nLightVols);
if (!nLightCount)
{
//Start out existingLightVolsCount to avoid clearing out existing light volumes when we don't need to
for (uint32 v = existingLightVolsCount; v < nLightVols; ++v)
{
lightVols[v].pData.resize(0);
}
return;
}
const int MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE = 1024;
if (nLightCount > MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE)
{
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "More lights in the scene (%d) than supported by the Light Volume Update function (%d). Extra lights will be ignored.",
nLightCount, MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE);
}
//This can be a uint8 array because nLightVols should never be greater than 256(LV_MAX_COUNT)
assert(LV_MAX_COUNT <= 256);
uint8 lightProcessedStateArray[MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE];
//Start at the number of light volumes that already exist so that we don't end up re-updating light volumes unnecessarily.
for (uint32 v = existingLightVolsCount; v < nLightVols; ++v)
{
const Vec4* __restrict vBVol = &lightVolsInfo[v]->vVolume;
int32 nMiny = (int32)(floorf((vBVol->y - vBVol->w) * LV_LIGHT_CELL_R_SIZE));
int32 nMaxy = (int32)(floorf((vBVol->y + vBVol->w) * LV_LIGHT_CELL_R_SIZE));
int32 nMinx = (int32)(floorf((vBVol->x - vBVol->w) * LV_LIGHT_CELL_R_SIZE));
int32 nMaxx = (int32)(floorf((vBVol->x + vBVol->w) * LV_LIGHT_CELL_R_SIZE));
lightVols[v].pData.resize(0);
// Loop through active light cells touching bounding volume (~avg 2 cells)
for (int32 y = nMiny, ymax = nMaxy; y <= ymax; ++y)
{
for (int32 x = nMinx, xmax = nMaxx; x <= xmax; ++x)
{
const SLightCell& lightCell = m_pWorldLightCells[GetWorldHashBucketKey(x, y, 1, LV_LIGHTS_WORLD_BUCKET_SIZE)];
CryPrefetch(&lightCell);
const SRenderLight& pFirstDL = (*pLights)[lightCell.nLightID[0]];
CryPrefetch(&pFirstDL);
CryPrefetch(&pFirstDL.m_ObjMatrix);
for (uint32 l = 0; (l < lightCell.nLightCount) & (lightVols[v].pData.size() < LIGHTVOLUME_MAXLIGHTS); ++l)
{
const int32 nLightId = lightCell.nLightID[l];
//Only allow IDs < MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE to continue or else we'll overflow access to
//lightProcessedStateArray[MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE]. Skipping the extra lights shouldn't really matter
//since A) folks won't be using that many lights, and B) for the case of light emitting particles, they tend to be grouped
//so that the individual contributions tend to bleed together anyway.
if (nLightId >= MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE)
{
continue;
}
if (static_cast<uint32>(nLightId) < nLightCount)
{
const SRenderLight& pDL = (*pLights)[nLightId];
const int32 nNextLightId = lightCell.nLightID[(l + 1) & (LIGHTVOLUME_MAXLIGHTS - 1)];
const SRenderLight& pNextDL = (*pLights)[nNextLightId];
CryPrefetch(&pNextDL);
CryPrefetch(&pNextDL.m_ObjMatrix);
IF(lightProcessedStateArray[nLightId] != v + 1, 1)
{
lightProcessedStateArray[nLightId] = v + 1;
AddLight(pDL, &*lightVolsInfo[v], lightVols[v]);
}
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::Clear(const SRenderingPassInfo& passInfo)
{
DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
m_bUpdateLightVolumes = false;
if (GetCVars()->e_LightVolumes && passInfo.IsGeneralPass() && GetCVars()->e_DynamicLights)
{
memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
//Clean up volume info data
for (size_t i = 0; i < lightVolsInfo.size(); ++i)
{
delete lightVolsInfo[i];
}
m_pLightVolsInfo[passInfo.ThreadID()].clear();
m_bUpdateLightVolumes = (GetCVars()->e_LightVolumes == 1) ? true : false;
}
}
//////////////////////////////////////////////////////////////////////////
void CLightVolumesMgr::GetLightVolumes(threadID nThreadID, SLightVolume*& pLightVols, uint32& nNumVols)
{
pLightVols = 0;
nNumVols = 0;
if (GetCVars()->e_LightVolumes == 1 && GetCVars()->e_DynamicLights && !m_pLightVolumes[nThreadID].empty())
{
pLightVols = &m_pLightVolumes[nThreadID][0];
nNumVols = m_pLightVolumes[nThreadID].size();
}
}
void C3DEngine::GetLightVolumes(threadID nThreadID, SLightVolume*& pLightVols, uint32& nNumVols)
{
m_LightVolumesMgr.GetLightVolumes(nThreadID, pLightVols, nNumVols);
}
uint16 C3DEngine::RegisterVolumeForLighting(const Vec3& vPos, f32 fRadius, uint8 nClipVolumeRef, const SRenderingPassInfo& passInfo)
{
return m_LightVolumesMgr.RegisterVolume(vPos, fRadius, nClipVolumeRef, passInfo);
}
//////////////////////////////////////////////////////////////////////////
#ifndef _RELEASE
void CLightVolumesMgr::DrawDebug(const SRenderingPassInfo& passInfo)
{
DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
IRenderer* pRenderer = GetRenderer();
IRenderAuxGeom* pAuxGeom = GetRenderer()->GetIRenderAuxGeom();
if (!pAuxGeom || !passInfo.IsGeneralPass())
{
return;
}
ColorF cWhite = ColorF(1, 1, 1, 1);
ColorF cBad = ColorF(1.0f, 0.0, 0.0f, 1.0f);
ColorF cWarning = ColorF(1.0f, 1.0, 0.0f, 1.0f);
ColorF cGood = ColorF(0.0f, 0.5, 1.0f, 1.0f);
ColorF cSingleCell = ColorF(0.0f, 1.0, 0.0f, 1.0f);
const uint32 nLightVols = lightVolsInfo.size();
LightVolumeVector& lightVols = m_pLightVolumes[passInfo.ThreadID()];
const Vec3 vCamPos = passInfo.GetCamera().GetPosition();
float fYLine = 8.0f, fYStep = 20.0f;
GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, (float*)&cWhite.r, false, "Light Volumes Info (count %d)", nLightVols);
for (uint32 v = 0; v < nLightVols; ++v) // draw each light volume
{
SLightVolume& lv = lightVols[v];
SLightVolInfo& lvInfo = *lightVolsInfo[v];
ColorF& cCol = (lv.pData.size() >= 10) ? cBad : ((lv.pData.size() >= 5) ? cWarning : cGood);
const Vec3 vPos = Vec3(lvInfo.vVolume.x, lvInfo.vVolume.y, lvInfo.vVolume.z);
const float fCamDistSq = (vPos - vCamPos).len2();
cCol.a = max(0.25f, min(1.0f, 1024.0f / (fCamDistSq + 1e-6f)));
pRenderer->DrawLabelEx(vPos, 1.3f, (float*)&cCol.r, true, true, "Id: %d\nPos: %.2f %.2f %.2f\nRadius: %.2f\nLights: %d\nOutLights: %d",
v, vPos.x, vPos.y, vPos.z, lvInfo.vVolume.w, lv.pData.size(), (*(int32*)&lvInfo.vVolume.w) & (1 << 31) ? 1 : 0);
if (GetCVars()->e_LightVolumesDebug == 2)
{
const float fSideSize = 0.707f * sqrtf(lvInfo.vVolume.w * lvInfo.vVolume.w * 2);
pAuxGeom->DrawAABB(AABB(vPos - Vec3(fSideSize), vPos + Vec3(fSideSize)), false, cCol, eBBD_Faceted);
}
if (GetCVars()->e_LightVolumesDebug == 3)
{
cBad.a = 1.0f;
const Vec3 vCellPos = Vec3(floorf((lvInfo.vVolume.x) * LV_CELL_RSIZEX) * LV_CELL_SIZEX,
floorf((lvInfo.vVolume.y) * LV_CELL_RSIZEY) * LV_CELL_SIZEY,
floorf((lvInfo.vVolume.z) * LV_CELL_RSIZEZ) * LV_CELL_SIZEZ);
const Vec3 vMin = vCellPos;
const Vec3 vMax = vMin + Vec3(LV_CELL_SIZEX, LV_CELL_SIZEY, LV_CELL_SIZEZ);
pAuxGeom->DrawAABB(AABB(vMin, vMax), false, cBad, eBBD_Faceted);
}
}
}
#endif
@@ -1,30 +0,0 @@
/*
* 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 "3DEngineMemory.h"
// Static CTemporaryPool instance
CTemporaryPool* CTemporaryPool::s_Instance = NULL;
namespace util
{
void* pool_allocate(size_t nSize)
{
return CTemporaryPool::Get()->Allocate(nSize, 8);
}
void pool_free(void* ptr)
{
return CTemporaryPool::Get()->Free(ptr);
}
}
-286
View File
@@ -1,286 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_3DENGINEMEMORY_H
#define CRYINCLUDE_CRY3DENGINE_3DENGINEMEMORY_H
#pragma once
// The type of pool responsible for temporary allocations within the 3dengine
//
// Note: The header is included here for reasons of devirtualization. If
// included directly from the precompiled header in 3dEngine, the gamedll will
// fail to compile!
#include <CryPool/PoolAlloc.h>
#include <InplaceFactory.h>
using NCryPoolAlloc::CFirstFit; // speed of allocations are crucial, so simply use the first fitting free allocation
using NCryPoolAlloc::CInPlace; //
using NCryPoolAlloc::CMemoryDynamic; // the pool itself will be dynamically allocated
using NCryPoolAlloc::CListItemInPlace; // use inplace items
// Tempororary Pool Holder
class CTemporaryPool
{
private:
// Access granted for 3dEngine to create, destroy and maintain the temporary
// pool for the 3d engine
friend class C3DEngine;
// The static pool instance - one pool to rule them all (temp allocations at least)
static CTemporaryPool* s_Instance;
// The type of the backing temporary pool
typedef CFirstFit<CInPlace<CMemoryDynamic>, CListItemInPlace> TTemporaryPool;
TTemporaryPool Pool;
// A non-recursive critical section guards the pool against concurrent access
typedef CryCriticalSectionNonRecursive TTemporaryPoolLock;
TTemporaryPoolLock Lock;
// Initialize the pool manager.
//
// Allocates the backing storage and initializes the temporary pool
// itself. The backing storage is aligned to 16 bytes to reduce the amount of
// cachelines crossed by the temporary pool
static bool Initialize(size_t poolSize)
{
// Create the object instance
s_Instance = new CTemporaryPool();
if (!s_Instance)
{
CryFatalError("CTemporaryPool::Init(): could not create an instance of CTemporaryPool");
return false;
}
// Allocate the backing storage
uint8* tempPool = reinterpret_cast<uint8*>(CryModuleMemalign(poolSize, 16));
if (!tempPool)
{
CryFatalError("CTemporaryPool::Init(): could not allocate %" PRISIZE_T " bytes for temportary pool", poolSize);
return false;
}
// Initialize the actual pool
s_Instance->Pool.InitMem(poolSize, tempPool);
return true;
}
// Shutdown the temporary pool manager.
//
// Frees the temporary pool
static bool Shutdown()
{
if (s_Instance == NULL)
{
CryFatalError("CTemporaryPool::Shutdown(): no temporary pool instance present");
return false;
}
bool error = false;
CTemporaryPool& instance = *s_Instance;
if (instance.Pool.Data())
{
CryModuleMemalignFree(instance.Pool.Data());
}
else
{
error = true;
}
delete s_Instance;
s_Instance = NULL;
return !error;
}
// Templated construct helper member function using an inplace factory
//
// Called from the templated New<T, Expr> function below. Returns a typed
// pointer to the inplace constructed object.
template<typename T, typename InPlaceFactory>
T* Construct(const InPlaceFactory& factory, void* storage)
{
return reinterpret_cast<T*>(factory.template apply<T>(storage));
}
// Templated destruct helper member function.
//
// Calls the object's destructor and returns a void pointer to the storage
template<typename T>
void* Destruct(T* obj)
{
obj->~T();
return reinterpret_cast<void*>(obj);
}
// Empty private constructor/destructors to prevent clients from creating and
// destroying instances of CTemporaryPool (there should only be one instance
// in the 3DEngine).
CTemporaryPool() {};
~CTemporaryPool() {};
public:
// Allocate a block of memory with the given size and alignment
void* Allocate(size_t size, size_t align)
{
AUTO_LOCK_T(CryCriticalSectionNonRecursive, Lock);
void* pData = Pool.Allocate<void*>(size, align);
if (pData == NULL)
{
CryFatalError("**** could not allocate %" PRISIZE_T " bytes from temporary pool", size);
}
return Pool.Resolve<void*>(pData);
};
// Allocates memory and constructs object of type 'T'
//
// Note: This method is respects the alignment of 'T' via C99 alignof()
template<typename T, typename Expr>
T* New(const Expr& expr)
{
AUTO_LOCK_T(CryCriticalSectionNonRecursive, Lock);
void* pObjStorage = Pool.Allocate<void*>(sizeof(T), alignof(T));
if (pObjStorage == NULL)
{
CryFatalError("**** could not allocate %d bytes from temporary pool",
(int)sizeof (T));
}
return Construct<T>(expr, pObjStorage);
};
// Allocates memory and constructs object of type 'T'
//
// Note: This method is respects the alignment of 'T' via C99 alignof()
template<typename T>
T* New()
{
AUTO_LOCK_T(CryCriticalSectionNonRecursive, Lock);
void* pObjStorage = Pool.Allocate<void*>(sizeof(T), alignof(T));
if (pObjStorage == NULL)
{
CryFatalError("**** could not allocate %d bytes from temporary pool",
(int)sizeof (T));
}
return Construct<T>(InplaceFactory(), pObjStorage);
};
// Frees a block of memory from the temporary pool
//
void Free(void* ptr)
{
AUTO_LOCK_T(CryCriticalSectionNonRecursive, Lock);
Pool.Free(ptr);
}
// Destroys an object of type 'T' and frees the underlying block of memory
template<typename T>
void Delete(T* ptr)
{
AUTO_LOCK_T(CryCriticalSectionNonRecursive, Lock);
Pool.Free(Destruct<T>(ptr));
}
// Static function to retrieve the static instance of CTemporaryPool
static CTemporaryPool* Get() { return s_Instance; };
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(Pool.Data(), Pool.MemSize());
}
};
// A stl compliant scratch allocator that uses the given temporary pool.
template <class Type>
class scratch_allocator
{
public:
typedef Type value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template <class value_type1>
struct rebind
{
typedef scratch_allocator<value_type1> other;
};
scratch_allocator() {}
template <class value_type1>
scratch_allocator(const scratch_allocator<value_type1>&) {}
scratch_allocator(const scratch_allocator<value_type>&) {}
~scratch_allocator() {}
pointer address(reference x) const {return &x; }
const_pointer address(const_reference x) const { return &x; }
// Note: size can be zero - return value will be null in that case
value_type* allocate(size_type n, const void* = 0)
{
if (n != 0)
{
size_type buf_size = n * sizeof(value_type);
void* ret = CTemporaryPool::Get()->Allocate(
buf_size,
alignof(value_type));
return reinterpret_cast<value_type*>(ret);
}
return 0;
}
// Note: size can be zero.
void deallocate(pointer p, [[maybe_unused]] size_type n)
{
if (p != NULL)
{
CTemporaryPool::Get()->Free(p);
}
}
size_type max_size() const { return size_t(-1) / sizeof(value_type); }
void construct(pointer p, const_reference val)
{ new (reinterpret_cast<void*>(p))value_type(val); }
void destroy(pointer p) { p->~value_type(); }
void cleanup() {}
size_t get_heap_size() { return 0; }
size_t get_wasted_in_allocation() { return 0; }
size_t get_wasted_in_blocks() { return 0; }
};
// A scratch vector type to use the stl vector
template<typename Type>
class scratch_vector
: public std::vector<Type, scratch_allocator<Type> >
{
};
namespace util
{
extern void* pool_allocate(size_t nSize);
extern void pool_free(void* ptr);
}
#endif // CRYINCLUDE_CRY3DENGINE_3DENGINEMEMORY_H
File diff suppressed because it is too large Load Diff
@@ -1,574 +0,0 @@
/*
* 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.
// Description : Implementation of I3DEngine interface methods
#include "Cry3DEngine_precompiled.h"
#include <MathConversion.h>
#include "3dEngine.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include "VisAreas.h"
#include "ObjMan.h"
#include "Ocean.h"
#include "DecalManager.h"
#include "IndexedMesh.h"
#include "AABBSV.h"
#include "MatMan.h"
#include "CullBuffer.h"
#include "CGF/CGFLoader.h"
#include "CGF/ReadOnlyChunkFile.h"
#include "CloudRenderNode.h"
#include "CloudsManager.h"
#include "SkyLightManager.h"
#include "FogVolumeRenderNode.h"
#include "DecalRenderNode.h"
#include "TimeOfDay.h"
#include "LightEntity.h"
#include "FogVolumeRenderNode.h"
#include "ObjectsTree.h"
#include "WaterVolumeRenderNode.h"
#include "DistanceCloudRenderNode.h"
#include "VolumeObjectRenderNode.h"
#include "RenderMeshMerger.h"
#include "DeferredCollisionEvent.h"
#include "OpticsManager.h"
#include "ClipVolumeManager.h"
#include "Environment/OceanEnvironmentBus.h"
#if !defined(EXCLUDE_DOCUMENTATION_PURPOSE)
#include "PrismRenderNode.h"
#endif // EXCLUDE_DOCUMENTATION_PURPOSE
//Platform specific includes
#if defined(WIN32) || defined(WIN64)
#include "CryWindows.h"
#endif
///////////////////////////////////////////////////////////////////////////////
void C3DEngine::CheckAddLight(CDLight* pLight, const SRenderingPassInfo& passInfo)
{
if (pLight->m_Id < 0)
{
GetRenderer()->EF_ADDDlight(pLight, passInfo);
assert(pLight->m_Id >= 0);
}
}
///////////////////////////////////////////////////////////////////////////////
float C3DEngine::GetLightAmount(CDLight* pLight, const AABB& objBox)
{
// find amount of light
float fDist = sqrt_tpl(Distance::Point_AABBSq(pLight->m_Origin, objBox));
float fLightAttenuation = (pLight->m_Flags & DLF_DIRECTIONAL) ? 1.f : 1.f - (fDist) / (pLight->m_fRadius);
if (fLightAttenuation < 0)
{
fLightAttenuation = 0;
}
float fLightAmount =
(pLight->m_Color.r + pLight->m_Color.g + pLight->m_Color.b) * 0.233f +
(pLight->GetSpecularMult()) * 0.1f;
return fLightAmount * fLightAttenuation;
}
///////////////////////////////////////////////////////////////////////////////
float C3DEngine::GetWaterLevel()
{
if (OceanToggle::IsActive())
{
return OceanRequest::GetOceanLevel();
}
return m_pOcean ? m_pOcean->GetWaterLevel() : WATER_LEVEL_UNKNOWN;
}
///////////////////////////////////////////////////////////////////////////////
bool C3DEngine::IsTessellationAllowed(const CRenderObject* pObj, const SRenderingPassInfo& passInfo, bool bIgnoreShadowPass) const
{
#ifdef MESH_TESSELLATION_ENGINE
assert(pObj && GetCVars());
bool rendererTessellation;
GetRenderer()->EF_Query(EFQ_MeshTessellation, rendererTessellation);
if (pObj->m_fDistance < GetCVars()->e_TessellationMaxDistance
&& GetCVars()->e_Tessellation
&& rendererTessellation
&& !(pObj->m_ObjFlags & FOB_DISSOLVE)) // dissolve is not working with tessellation for now
{
bool bAllowTessellation = true;
// Check if rendering into shadow map and enable tessellation only if allowed
if (!bIgnoreShadowPass && passInfo.IsShadowPass())
{
if (IsTessellationAllowedForShadowMap(passInfo))
{
// NOTE: This might be useful for game projects
// Use tessellation only for objects visible in main view
// Shadows will switch to non-tessellated when caster gets out of view
IRenderNode* pRN = (IRenderNode*)pObj->m_pRenderNode;
if (pRN)
{
bAllowTessellation = (pRN->IsRenderNode() && (pRN->GetDrawFrame() > passInfo.GetFrameID() - 10));
}
}
else
{
bAllowTessellation = false;
}
}
return bAllowTessellation;
}
#endif //#ifdef MESH_TESSELLATION_ENGINE
return false;
}
///////////////////////////////////////////////////////////////////////////////
void C3DEngine::CreateRNTmpData(CRNTmpData** ppInfo, IRenderNode* pRNode, const SRenderingPassInfo& passInfo)
{
// m_checkCreateRNTmpData lock scope
{
AUTO_LOCK(m_checkCreateRNTmpData);
FUNCTION_PROFILER_3DENGINE;
if (*ppInfo)
{
return; // check if another thread already intialized ppInfo
}
// make sure element is allocated
if (m_LTPRootFree.pNext == &m_LTPRootFree)
{
CRNTmpData* pNew = new CRNTmpData; //m_RNTmpDataPools.GetNewElement();
pNew->Link(&m_LTPRootFree);
}
// move element from m_LTPRootFree to m_LTPRootUsed
CRNTmpData* pElem = m_LTPRootFree.pNext;
pElem->Unlink();
pElem->Link(&m_LTPRootUsed);
pElem->pOwnerRef = ppInfo;
pElem->nFrameInfoId = GetFrameInfoId(ppInfo, passInfo.GetMainFrameID());
assert(!pElem->pOwnerRef || !(*pElem->pOwnerRef));
memset(&pElem->userData, 0, sizeof(pElem->userData));
// Add a memory barrier that the write to nFrameInfoID is visible
// before *ppInfo is written, else we have a race condtition in
// CheckCreateRNTmpData as we don't use a lock there
// for performance reasons
MemoryBarrier();
*ppInfo = pElem;
}
if (pRNode)
{
pRNode->OnRenderNodeBecomeVisible(passInfo); // Internally uses the just assigned RNTmpData pointer i.e IRenderNode::m_pRNTmpData ...
if (IVisArea* pVisArea = pRNode->GetEntityVisArea())
{
pRNode->m_pRNTmpData->userData.m_pClipVolume = pVisArea;
}
else if (GetClipVolumeManager()->IsClipVolumeRequired(pRNode))
{
GetClipVolumeManager()->UpdateEntityClipVolume(pRNode->GetPos(), pRNode);
}
}
}
///////////////////////////////////////////////////////////////////////////////
void C3DEngine::RenderRenderNode_ShadowPass(IShadowCaster* pShadowCaster, const SRenderingPassInfo& passInfo, [[maybe_unused]] AZ::LegacyJobExecutor* pJobExecutor)
{
assert(passInfo.IsShadowPass());
SRendItemSorter rendItemSorter = SRendItemSorter::CreateShadowPassRendItemSorter(passInfo);
if (!pShadowCaster->IsRenderNode())
{
const Vec3 vCamPos = passInfo.GetCamera().GetPosition();
const AABB objBox = pShadowCaster->GetBBoxVirtual();
SRendParams rParams;
rParams.fDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, objBox)) * passInfo.GetZoomFactor();
rParams.lodValue = pShadowCaster->ComputeLod(0, passInfo);
rParams.rendItemSorter = rendItemSorter.GetValue();
pShadowCaster->Render(rParams, passInfo);
return;
}
IRenderNode* pRenderNode = static_cast<IRenderNode*>(pShadowCaster);
if ((pRenderNode->m_dwRndFlags & ERF_HIDDEN) != 0)
{
return;
}
int nStaticObjectLod = -1;
if (passInfo.GetShadowMapType() == SRenderingPassInfo::SHADOW_MAP_CACHED)
{
nStaticObjectLod = GetCVars()->e_ShadowsCacheObjectLod;
}
else if (passInfo.GetShadowMapType() == SRenderingPassInfo::SHADOW_MAP_CACHED_MGPU_COPY)
{
nStaticObjectLod = pRenderNode->m_cStaticShadowLod;
}
Get3DEngine()->CheckCreateRNTmpData(&pRenderNode->m_pRNTmpData, pRenderNode, passInfo);
int wantedLod = pRenderNode->m_pRNTmpData->userData.nWantedLod;
if (GetCVars()->e_LodForceUpdate && m_pObjManager)
{
const Vec3 vCamPos = passInfo.GetCamera().GetPosition();
const AABB objBox = pRenderNode->GetBBoxVirtual();
float fDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, objBox)) * passInfo.GetZoomFactor();
wantedLod = m_pObjManager->GetObjectLOD(pRenderNode, fDistance);
}
if (pRenderNode->GetShadowLodBias() != IRenderNode::SHADOW_LODBIAS_DISABLE)
{
if (passInfo.IsShadowPass() && (pRenderNode->GetDrawFrame(0) < (passInfo.GetFrameID() - 10)))
{
wantedLod += GetCVars()->e_ShadowsLodBiasInvis;
}
wantedLod += GetCVars()->e_ShadowsLodBiasFixed;
wantedLod += pRenderNode->GetShadowLodBias();
}
if (nStaticObjectLod >= 0)
{
wantedLod = nStaticObjectLod;
}
{
const Vec3 vCamPos = passInfo.GetCamera().GetPosition();
const AABB objBox = pRenderNode->GetBBoxVirtual();
SRendParams rParams;
rParams.fDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, objBox)) * passInfo.GetZoomFactor();
rParams.lodValue = pRenderNode->ComputeLod(wantedLod, passInfo);
rParams.rendItemSorter = rendItemSorter.GetValue();
rParams.pRenderNode = pRenderNode;
pRenderNode->Render(rParams, passInfo);
}
}
///////////////////////////////////////////////////////////////////////////////
ITimeOfDay* C3DEngine::GetTimeOfDay()
{
CTimeOfDay* tod = m_pTimeOfDay;
if (!tod)
{
tod = new CTimeOfDay;
m_pTimeOfDay = tod;
}
return tod;
}
///////////////////////////////////////////////////////////////////////////////
void C3DEngine::TraceFogVolumes(const Vec3& vPos, const AABB& objBBox, SFogVolumeData& fogVolData, const SRenderingPassInfo& passInfo, bool fogVolumeShadingQuality)
{
CFogVolumeRenderNode::TraceFogVolumes(vPos, objBBox, fogVolData, passInfo, fogVolumeShadingQuality);
}
///////////////////////////////////////////////////////////////////////////////
void C3DEngine::AsyncOctreeUpdate(IRenderNode* pEnt, int nSID, [[maybe_unused]] int nSIDConsideredSafe, uint32 nFrameID, bool bUnRegisterOnly)
{
FUNCTION_PROFILER_3DENGINE;
#ifdef _DEBUG // crash test basically
const char* szClass = pEnt->GetEntityClassName();
const char* szName = pEnt->GetName();
if (!szName[0] && !szClass[0])
{
Warning("I3DEngine::RegisterEntity: Entity undefined"); // do not register undefined objects
}
// if(strstr(szName,"Dude"))
// int y=0;
#endif
IF (bUnRegisterOnly, 0)
{
UnRegisterEntityImpl(pEnt);
return;
}
;
AABB aabb;
pEnt->FillBBox(aabb);
float fObjRadiusSqr = aabb.GetRadiusSqr();
EERType eERType = pEnt->GetRenderNodeType();
#ifdef SUPP_HMAP_OCCL
if (pEnt->m_pRNTmpData)
{
pEnt->m_pRNTmpData->userData.m_OcclState.vLastVisPoint.Set(0, 0, 0);
}
#endif
const unsigned int dwRndFlags = pEnt->GetRndFlags();
if (!(dwRndFlags & ERF_RENDER_ALWAYS) && !(dwRndFlags & ERF_CASTSHADOWMAPS))
{
if (GetCVars()->e_ObjFastRegister && pEnt->m_pOcNode && ((COctreeNode*)pEnt->m_pOcNode)->IsRightNode(aabb, fObjRadiusSqr, pEnt->m_fWSMaxViewDist))
{ // same octree node
Vec3 vEntCenter = GetEntityRegisterPoint(pEnt);
IVisArea* pVisArea = pEnt->GetEntityVisArea();
if (pVisArea && pVisArea->IsPointInsideVisArea(vEntCenter))
{
return; // same visarea
}
IVisArea* pVisAreaFromPos = (!m_pVisAreaManager || dwRndFlags & ERF_OUTDOORONLY) ? NULL : GetVisAreaManager()->GetVisAreaFromPos(vEntCenter);
if (pVisAreaFromPos == pVisArea)
{
// NOTE: can only get here when pVisArea==NULL due to 'same visarea' check above. So check for changed clip volume
if (GetClipVolumeManager()->IsClipVolumeRequired(pEnt))
{
GetClipVolumeManager()->UpdateEntityClipVolume(vEntCenter, pEnt);
}
return; // same visarea or same outdoor
}
}
}
if (pEnt->m_pOcNode)
{
UnRegisterEntityImpl(pEnt);
}
else if (GetCVars()->e_StreamCgf && (eERType == eERType_RenderComponent || eERType == eERType_DynamicMeshRenderComponent || eERType == eERType_GeomCache))
{ // Temporary solution: Force streaming priority update for objects that was not registered before
// and was not visible before since usual prediction system was not able to detect them
if ((uint32)pEnt->GetDrawFrame(0) < nFrameID - 16)
{
// defer the render node streaming priority update still we have a correct 3D Engine camera
int nElementID = m_deferredRenderComponentStreamingPriorityUpdates.Find(pEnt);
if (nElementID == -1) // only add elements once
{
m_deferredRenderComponentStreamingPriorityUpdates.push_back(pEnt);
}
}
}
pEnt->m_fWSMaxViewDist = pEnt->GetMaxViewDist();
bool useVisAreas = true;
if (eERType != eERType_Light)
{
if (fObjRadiusSqr > sqr(MAX_VALID_OBJECT_VOLUME) || !_finite(fObjRadiusSqr))
{
Warning("I3DEngine::RegisterEntity: Object has invalid bbox: name: %s, class name: %s, GetRadius() = %.2f",
pEnt->GetName(), pEnt->GetEntityClassName(), fObjRadiusSqr);
return; // skip invalid objects - usually only objects with invalid very big scale will reach this point
}
if (dwRndFlags & ERF_RENDER_ALWAYS)
{
if (m_lstAlwaysVisible.Find(pEnt) < 0)
{
m_lstAlwaysVisible.Add(pEnt);
}
if (dwRndFlags & ERF_HUD)
{
return;
}
}
if (pEnt->m_dwRndFlags & ERF_OUTDOORONLY)
{
useVisAreas = false;
}
}
else
{
CLightEntity* pLight = (CLightEntity*)pEnt;
uint32 lightFlag = pLight->m_light.m_Flags;
if ((lightFlag & DLF_ATTACH_TO_SUN) || //If the light is attached to the sun, we need to make sure it renders even the entity is not in view port
(lightFlag & (DLF_IGNORES_VISAREAS | DLF_DEFERRED_LIGHT | DLF_THIS_AREA_ONLY)) == (DLF_IGNORES_VISAREAS | DLF_DEFERRED_LIGHT)
)
{
if (m_lstAlwaysVisible.Find(pEnt) < 0)
{
m_lstAlwaysVisible.Add(pEnt);
}
}
if (lightFlag & DLF_IGNORES_VISAREAS)
{
useVisAreas = false;
}
}
//////////////////////////////////////////////////////////////////////////
// Check for occlusion proxy.
{
CStatObj* pStatObj = (CStatObj*)pEnt->GetEntityStatObj();
if (pStatObj)
{
if (pStatObj->m_bHaveOcclusionProxy)
{
pEnt->m_dwRndFlags |= ERF_GOOD_OCCLUDER;
pEnt->m_nInternalFlags |= IRenderNode::HAS_OCCLUSION_PROXY;
}
}
}
//////////////////////////////////////////////////////////////////////////
if (!useVisAreas || !(m_pVisAreaManager && m_pVisAreaManager->SetEntityArea(pEnt, aabb, fObjRadiusSqr)))
{
if (m_pObjectsTree == nullptr)
{
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
m_pObjectsTree = COctreeNode::Create(nSID, AABB(Vec3(0, 0, 0), Vec3(terrainAabb.GetXExtent(), terrainAabb.GetYExtent(), terrainAabb.GetZExtent())), NULL);
}
m_pObjectsTree->InsertObject(pEnt, aabb, fObjRadiusSqr, aabb.GetCenter());
}
// update clip volume: use vis area if we have one, otherwise check if we're in the same volume as before. check other volumes as last resort only
if (pEnt->m_pRNTmpData)
{
Vec3 vEntCenter = GetEntityRegisterPoint(pEnt);
CRNTmpData::SRNUserData& userData = pEnt->m_pRNTmpData->userData;
if (IVisArea* pVisArea = pEnt->GetEntityVisArea())
{
userData.m_pClipVolume = pVisArea;
}
else if (GetClipVolumeManager()->IsClipVolumeRequired(pEnt))
{
GetClipVolumeManager()->UpdateEntityClipVolume(vEntCenter, pEnt);
}
}
// register decals, to clean up longer not renderes decals and their render meshes
if (eERType == eERType_Decal)
{
m_decalRenderNodes.push_back((IDecalRenderNode*)pEnt);
}
}
///////////////////////////////////////////////////////////////////////////////
bool C3DEngine::UnRegisterEntityImpl(IRenderNode* pEnt)
{
// make sure we don't try to update the streaming priority if an object
// was added and removed in the same frame
int nElementID = m_deferredRenderComponentStreamingPriorityUpdates.Find(pEnt);
if (nElementID != -1)
{
m_deferredRenderComponentStreamingPriorityUpdates.DeleteFastUnsorted(nElementID);
}
FUNCTION_PROFILER_3DENGINE;
#ifdef _DEBUG // crash test basically
const char* szClass = pEnt->GetEntityClassName();
const char* szName = pEnt->GetName();
if (!szName[0] && !szClass[0])
{
Warning("C3DEngine::RegisterEntity: Entity undefined");
}
#endif
EERType eRenderNodeType = pEnt->GetRenderNodeType();
bool bFound = false;
if (pEnt->m_pOcNode)
{
bFound = ((COctreeNode*)pEnt->m_pOcNode)->DeleteObject(pEnt);
}
if (pEnt->m_dwRndFlags & ERF_RENDER_ALWAYS || (eRenderNodeType == eERType_Light) || (eRenderNodeType == eERType_FogVolume))
{
m_lstAlwaysVisible.Delete(pEnt);
}
if (eRenderNodeType == eERType_Decal)
{
std::vector<IDecalRenderNode*>::iterator it = std::find(m_decalRenderNodes.begin(), m_decalRenderNodes.end(), (IDecalRenderNode*)pEnt);
if (it != m_decalRenderNodes.end())
{
m_decalRenderNodes.erase(it);
}
}
if (CClipVolumeManager* pClipVolumeManager = GetClipVolumeManager())
{
pClipVolumeManager->UnregisterRenderNode(pEnt);
}
return bFound;
}
///////////////////////////////////////////////////////////////////////////////
Vec3 C3DEngine::GetEntityRegisterPoint(IRenderNode* pEnt)
{
AABB aabb;
pEnt->FillBBox(aabb);
Vec3 vPoint;
if (pEnt->m_dwRndFlags & ERF_REGISTER_BY_POSITION)
{
vPoint = pEnt->GetPos();
if (pEnt->GetRenderNodeType() != eERType_Light)
{
// check for valid position
if (aabb.GetDistanceSqr(vPoint) > sqr(128.f))
{
Warning("I3DEngine::RegisterEntity: invalid entity position: Name: %s, Class: %s, Pos=(%.1f,%.1f,%.1f), BoxMin=(%.1f,%.1f,%.1f), BoxMax=(%.1f,%.1f,%.1f)",
pEnt->GetName(), pEnt->GetEntityClassName(),
pEnt->GetPos().x, pEnt->GetPos().y, pEnt->GetPos().z,
pEnt->GetBBox().min.x, pEnt->GetBBox().min.y, pEnt->GetBBox().min.z,
pEnt->GetBBox().max.x, pEnt->GetBBox().max.y, pEnt->GetBBox().max.z
);
}
// clamp by bbox
vPoint.CheckMin(aabb.max);
vPoint.CheckMax(aabb.min + Vec3(0, 0, .5f));
}
}
else
{
vPoint = aabb.GetCenter();
}
return vPoint;
}
///////////////////////////////////////////////////////////////////////////////
Vec3 C3DEngine::GetSunDirNormalized() const
{
return m_vSunDirNormalized;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,86 +0,0 @@
/*
* 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.
*
*/
#include "Cry3DEngine_precompiled.h"
#include "3dEngine.h"
#include "Ocean.h"
#include "StatObj.h"
#include "ObjMan.h"
#include "MatMan.h"
#include "VisAreas.h"
#include "ObjectsTree.h"
#include <CryPath.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <MathConversion.h>
#include <StatObjBus.h>
#include <PakLoadDataUtils.h>
#include <AzCore/Console/IConsole.h>
#define SIGC_HIDEABILITY BIT(3)
#define SIGC_HIDEABILITYSECONDARY BIT(4)
#define SIGC_PROCEDURALLYANIMATED BIT(6)
#define SIGC_CASTSHADOW BIT(7) // Deprecated
#define SIGC_RECVSHADOW BIT(8)
#define SIGC_DYNAMICDISTANCESHADOWS BIT(9)
#define SIGC_USEALPHABLENDING BIT(10)
#define SIGC_RANDOMROTATION BIT(12)
#define SIGC_ALLOWINDOOR BIT(13)
// Bits 13-14 reserved for player hideability
#define SIGC_PLAYERHIDEABLE_LOWBIT (13)
#define SIGC_PLAYERHIDEABLE_MASK BIT(13) | BIT(14)
#define SIGC_CASTSHADOW_MINSPEC_SHIFT (15)
// Get the number of bits needed for the maximum spec level
#define SIGC_CASTSHADOW_MINSPEC_MASK_NUM_BITS_NEEDED (IntegerLog2(uint32(END_CONFIG_SPEC_ENUM - 1)) + 1)
// Create a mask based on the number of bits needed
#define SIGC_CASTSHADOW_MINSPEC_MASK_BITS ((1 << SIGC_CASTSHADOW_MINSPEC_MASK_NUM_BITS_NEEDED) - 1)
#define SIGC_CASTSHADOW_MINSPEC_MASK (SIGC_CASTSHADOW_MINSPEC_MASK_BITS << SIGC_CASTSHADOW_MINSPEC_SHIFT)
AZ_CVAR(float, bg_DefaultMaxOctreeWorldSize, 4096.0f, nullptr, AZ::ConsoleFunctorFlags::NeedsReload, "Default world size to use for the octree when terrain is not present.");
bool C3DEngine::CreateOctree(float maxRootOctreeNodeSize)
{
float rootOctreeNodeSize = (maxRootOctreeNodeSize > 0.0f) ? maxRootOctreeNodeSize : bg_DefaultMaxOctreeWorldSize;
COctreeNode* newOctreeNode = COctreeNode::Create(DEFAULT_SID, AABB(Vec3(0), Vec3(rootOctreeNodeSize)), NULL);
if (!newOctreeNode)
{
Error("Failed to create octree with initial world size=%f", rootOctreeNodeSize);
return false;
}
SetObjectTree(newOctreeNode);
Cry3DEngineBase::GetObjManager()->GetListStaticTypes().PreAllocate(1, 1);
Cry3DEngineBase::GetObjManager()->GetListStaticTypes()[DEFAULT_SID].Reset();
return true;
}
void C3DEngine::DestroyOctree()
{
COctreeNode* objectTree = GetObjectTree();
if (objectTree)
{
delete objectTree;
SetObjectTree(nullptr);
}
}
#define RAD2BYTE(x) ((x)*255.0f / float(g_PI2))
#define BYTE2RAD(x) ((x)* float(g_PI2) / 255.0f)
#include "TypeInfo_impl.h"
-23
View File
@@ -1,23 +0,0 @@
/*
* 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.
// Description : objects container, streaming, common part for indoor and outdoor sectors
#include "Cry3DEngine_precompiled.h"
CBasicArea::~CBasicArea()
{
delete m_pObjectsTree;
m_pObjectsTree = NULL;
}
-143
View File
@@ -1,143 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_BASICAREA_H
#define CRYINCLUDE_CRY3DENGINE_BASICAREA_H
#pragma once
#define COPY_MEMBER_SAVE(_dst, _src, _name) { (_dst)->_name = (_src)->_name; }
#define COPY_MEMBER_LOAD(_dst, _src, _name) { (_dst)->_name = (_src)->_name; }
enum EObjList
{
DYNAMIC_OBJECTS = 0,
STATIC_OBJECTS,
PROC_OBJECTS,
ENTITY_LISTS_NUM
};
struct SRNInfo
{
SRNInfo()
{
memset(this, 0, sizeof(*this));
}
SRNInfo(IRenderNode* _pNode)
{
fMaxViewDist = _pNode->m_fWSMaxViewDist;
AABB aabbBox = _pNode->GetBBox();
objSphere.center = aabbBox.GetCenter();
objSphere.radius = aabbBox.GetRadius();
pNode = _pNode;
nRType = _pNode->GetRenderNodeType();
/*#ifdef _DEBUG
erType = _pNode->GetRenderNodeType();
cry_strcpy(szName, _pNode->GetName());
#endif*/
}
bool operator == (const IRenderNode* _pNode) const { return (pNode == _pNode); }
bool operator == (const SRNInfo& rOther) const { return (pNode == rOther.pNode); }
float fMaxViewDist;
Sphere objSphere;
IRenderNode* pNode;
EERType nRType;
/*#ifdef _DEBUG
EERType erType;
char szName[32];
#endif*/
};
struct SCasterInfo
{
SCasterInfo()
{
memset(this, 0, sizeof(*this));
}
SCasterInfo(IRenderNode* _pNode, float fMaxDist)
{
fMaxCastingDist = fMaxDist;
objBox = _pNode->GetBBox();
objSphere.center = objBox.GetCenter();
objSphere.radius = objBox.GetRadius();
pNode = _pNode;
nRType = _pNode->GetRenderNodeType();
nRenderNodeFlags = _pNode->GetRndFlags();
bCanExecuteAsRenderJob = _pNode->CanExecuteRenderAsJob();
}
SCasterInfo(IRenderNode* _pNode, float fMaxDist, EERType renderNodeType)
{
fMaxCastingDist = fMaxDist;
_pNode->FillBBox(objBox);
objSphere.center = objBox.GetCenter();
objSphere.radius = objBox.GetRadius();
pNode = _pNode;
nRType = renderNodeType;
nRenderNodeFlags = _pNode->GetRndFlags();
bCanExecuteAsRenderJob = _pNode->CanExecuteRenderAsJob();
}
bool operator == (const IRenderNode* _pNode) const { return (pNode == _pNode); }
bool operator == (const SCasterInfo& rOther) const { return (pNode == rOther.pNode); }
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const { /*nothing*/}
float fMaxCastingDist;
Sphere objSphere;
AABB objBox;
IRenderNode* pNode;
uint32 nGSMFrameId;
EERType nRType;
bool bCanExecuteAsRenderJob;
uint32 nRenderNodeFlags;
};
#define UPDATE_PTR_AND_SIZE(_pData, _nDataSize, _SIZE_PLUS) \
{ \
_pData += (_SIZE_PLUS); \
_nDataSize -= (_SIZE_PLUS); \
assert(_nDataSize >= 0); \
} \
enum EAreaType
{
eAreaType_Undefined,
eAreaType_OcNode,
eAreaType_VisArea
};
struct CBasicArea
: public Cry3DEngineBase
{
CBasicArea()
{
m_boxArea.min = m_boxArea.max = Vec3(0, 0, 0);
m_pObjectsTree = NULL;
}
~CBasicArea();
void CompileObjects(int nListId); // optimize objects lists for rendering
class COctreeNode* m_pObjectsTree;
AABB m_boxArea; // bbox containing everything in sector including child sectors
AABB m_boxStatics; // bbox containing only objects in STATIC_OBJECTS list of this node and height-map
};
#endif // CRYINCLUDE_CRY3DENGINE_BASICAREA_H
File diff suppressed because it is too large Load Diff
-818
View File
@@ -1,818 +0,0 @@
/*
* 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 "CCullThread.h"
#include "ObjMan.h"
#include "CCullRenderer.h"
#include <AzCore/Jobs/Job.h>
#include <AzCore/Jobs/JobFunction.h>
typedef NAsyncCull::CCullRenderer<CULL_SIZEX, CULL_SIZEY> tdCullRasterizer;
volatile static NAsyncCull::tdVertexCache g_VertexCache;
uint8 g_RasterizerBuffer[sizeof(tdCullRasterizer) + 16];
tdCullRasterizer* g_Rasterizer;
#define RASTERIZER (*g_Rasterizer)
namespace NAsyncCull
{
const NVMath::vec4 MaskNot3 = NVMath::Vec4(~3u, ~0u, ~0u, ~0u);
CCullThread::CCullThread()
: m_Enabled(false)
, m_Active(false)
, m_nPrepareState(IDLE)
, m_OCMMeshCount(0)
, m_OCMInstCount(0)
, m_OCMOffsetInstances(0)
{
size_t Buffer = reinterpret_cast<size_t>(g_RasterizerBuffer);
Buffer += 127;
Buffer &= ~127;
g_Rasterizer = new(reinterpret_cast<void*>(Buffer))tdCullRasterizer();
m_NearPlane = 0;
m_FarPlane = 0;
m_NearestMax = 0;
}
bool CCullThread::LoadLevel(const char* pFolderName)
{
m_OCMBuffer.resize(0);
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen((string(pFolderName) + "/occluder.ocm").c_str(), "rbx");
if (fileHandle == AZ::IO::InvalidHandle)
{
//__debugbreak();
return false;
}
gEnv->pCryPak->FSeek(fileHandle, 0, SEEK_END);
const size_t Size = gEnv->pCryPak->FTell(fileHandle);
gEnv->pCryPak->FSeek(fileHandle, 0L, SEEK_SET);
m_OCMBuffer.reserve(Size + 144 * 3 + 16); //48tri*9byte padding for unrolled loop in rasterization without special case (not 144 algined poly count)
//16 for alignment
m_OCMBuffer.resize(Size);
size_t BufferOffset = reinterpret_cast<size_t>(&m_OCMBuffer[0]);
BufferOffset = (BufferOffset + 15) & ~15;
m_pOCMBufferAligned = reinterpret_cast<uint8*>(BufferOffset);
gEnv->pCryPak->FRead(m_pOCMBufferAligned, Size, fileHandle, false);
gEnv->pCryPak->FClose(fileHandle);
const uint32 Version = Swap(*reinterpret_cast<uint32*>(&m_pOCMBufferAligned[0]));
m_OCMMeshCount = *reinterpret_cast<uint32*>(&m_pOCMBufferAligned[4]);
m_OCMInstCount = *reinterpret_cast<uint32*>(&m_pOCMBufferAligned[8]);
m_OCMOffsetInstances = *reinterpret_cast<uint32*>(&m_pOCMBufferAligned[12]);
if (Version != ~3u && Version != ~4u)
{
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "Unsupported occlusion mesh format version. Please reexport the occluder mesh.");
stl::free_container(m_OCMBuffer);
return false;
}
if (m_OCMOffsetInstances & 3)
{
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "The occluder mesh contains invalid data. Please reexport the occluder mesh.");
stl::free_container(m_OCMBuffer);
return false;
}
if (Version == ~3u) //bump to version ~4
{
m_OCMMeshCount = Swap(m_OCMMeshCount);
m_OCMInstCount = Swap(m_OCMInstCount);
m_OCMOffsetInstances = Swap(m_OCMOffsetInstances);
PodArray<uint8> OCMBufferOut(Size * 8, Size * 8);
uint8* pOut = &OCMBufferOut[0];
*reinterpret_cast<uint32*>(&pOut[0]) = ~4u;//version
*reinterpret_cast<uint32*>(&pOut[4]) = m_OCMMeshCount;
*reinterpret_cast<uint32*>(&pOut[8]) = m_OCMInstCount;
*reinterpret_cast<uint32*>(&pOut[12]) = m_OCMOffsetInstances;//needs to be patched at the end
pOut += 16;
uint8* pMeshes = &m_pOCMBufferAligned[0];//actually starts at 16, but MeshOffset is zero based
uint8* pInstances = &m_pOCMBufferAligned[m_OCMOffsetInstances];
std::map<uint32, uint32> Offsets;//<old Offset, new Offset>
for (size_t a = 0; a < m_OCMInstCount; a++)
{
Matrix44 World(IDENTITY);
uint8* pInstance = pInstances + a * (sizeof(int) + 12 * sizeof(float));//meshoffset+worldmatrix43
uint32& MeshOffset = *reinterpret_cast<uint32*>(&pInstance[0]);
float* pWorldMat = reinterpret_cast<float*>(&pInstance[4]);
Swap(MeshOffset);
Swap(pWorldMat[0x0]);
Swap(pWorldMat[0x1]);
Swap(pWorldMat[0x2]);
Swap(pWorldMat[0x3]);
Swap(pWorldMat[0x4]);
Swap(pWorldMat[0x5]);
Swap(pWorldMat[0x6]);
Swap(pWorldMat[0x7]);
Swap(pWorldMat[0x8]);
Swap(pWorldMat[0x9]);
Swap(pWorldMat[0xA]);
Swap(pWorldMat[0xB]);
if (Offsets.find(MeshOffset) != Offsets.end())//already endian swapped?
{
continue;
}
Offsets[MeshOffset] = static_cast<uint32>(pOut - &OCMBufferOut[0]);//zero based offset
uint8* pMesh = pMeshes + MeshOffset;
uint16& QuadCount = *reinterpret_cast<uint16*>(pMesh);
uint16& TriCount = *reinterpret_cast<uint16*>(pMesh + 2);
Swap(QuadCount);
Swap(TriCount);
*reinterpret_cast<uint32*>(pOut) = TriCount + QuadCount / 4 * 6;
pOut += 16;//to keep 16byte alignment
const size_t Quads16 = (reinterpret_cast<size_t>(pMesh + 4) + 15) & ~15;
const size_t Tris16 = (Quads16 + QuadCount * 3 + 15) & ~15;
const int8* pQuads = reinterpret_cast<const int8*>(Quads16);
const int8* pTris = reinterpret_cast<const int8*>(Tris16);
for (size_t b = 0, S = QuadCount; b < S; b += 4)
{
const float x0 = *pQuads++;
const float y0 = *pQuads++;
const float z0 = *pQuads++;
const float x1 = *pQuads++;
const float y1 = *pQuads++;
const float z1 = *pQuads++;
const float x2 = *pQuads++;
const float y2 = *pQuads++;
const float z2 = *pQuads++;
const float x3 = *pQuads++;
const float y3 = *pQuads++;
const float z3 = *pQuads++;
reinterpret_cast<float*>(pOut)[0x00] = x0;
reinterpret_cast<float*>(pOut)[0x01] = y0;
reinterpret_cast<float*>(pOut)[0x02] = z0;
reinterpret_cast<float*>(pOut)[0x03] = 1.f;
reinterpret_cast<float*>(pOut)[0x04] = x2;
reinterpret_cast<float*>(pOut)[0x05] = y2;
reinterpret_cast<float*>(pOut)[0x06] = z2;
reinterpret_cast<float*>(pOut)[0x07] = 1.f;
reinterpret_cast<float*>(pOut)[0x08] = x3;
reinterpret_cast<float*>(pOut)[0x09] = y3;
reinterpret_cast<float*>(pOut)[0x0a] = z3;
reinterpret_cast<float*>(pOut)[0x0b] = 1.f;
reinterpret_cast<float*>(pOut)[0x0c] = x2;
reinterpret_cast<float*>(pOut)[0x0d] = y2;
reinterpret_cast<float*>(pOut)[0x0e] = z2;
reinterpret_cast<float*>(pOut)[0x0f] = 1.f;
reinterpret_cast<float*>(pOut)[0x10] = x0;
reinterpret_cast<float*>(pOut)[0x11] = y0;
reinterpret_cast<float*>(pOut)[0x12] = z0;
reinterpret_cast<float*>(pOut)[0x13] = 1.f;
reinterpret_cast<float*>(pOut)[0x14] = x1;
reinterpret_cast<float*>(pOut)[0x15] = y1;
reinterpret_cast<float*>(pOut)[0x16] = z1;
reinterpret_cast<float*>(pOut)[0x17] = 1.f;
pOut += 0x18 * sizeof(float);
}
for (size_t c = 0, S = TriCount; c < S; c++)
{
const float x = *pTris++;
const float y = *pTris++;
const float z = *pTris++;
reinterpret_cast<float*>(pOut)[0x00] = x;
reinterpret_cast<float*>(pOut)[0x01] = y;
reinterpret_cast<float*>(pOut)[0x02] = z;
reinterpret_cast<float*>(pOut)[0x03] = 1.f;
pOut += 4 * sizeof(float);
}
}
m_OCMOffsetInstances = static_cast<uint32>(pOut - &OCMBufferOut[0]);
const size_t InstanceSize = m_OCMInstCount * (sizeof(int) + 12 * sizeof(float));
memcpy(pOut, pInstances, InstanceSize);
for (size_t a = 0; a < m_OCMInstCount; a++)
{
uint8* pInstance = pOut + a * (sizeof(int) + 12 * sizeof(float));//meshoffset+worldmatrix43
uint32& MeshOffset = *reinterpret_cast<uint32*>(&pInstance[0]);
MeshOffset = Offsets[MeshOffset];
}
pOut += InstanceSize;
m_OCMBuffer.resize(pOut - &OCMBufferOut[0]);
size_t bufferOffset = reinterpret_cast<size_t>(&m_OCMBuffer[0]);
bufferOffset = (bufferOffset + 15) & ~15;
m_pOCMBufferAligned = reinterpret_cast<uint8*>(bufferOffset);
memcpy(m_pOCMBufferAligned, &OCMBufferOut[0], m_OCMBuffer.size());
}
// Integrity check: each mesh data must be aligned to 4 bytes
uint8* pInstances = &m_pOCMBufferAligned[m_OCMOffsetInstances];
for (size_t a = 0; a < m_OCMInstCount; a++)
{
uint8* pInstance = pInstances + a * (sizeof(int) + 12 * sizeof(float));//meshoffset+worldmatrix43
uint32 MeshOffset = *reinterpret_cast<uint32*>(&pInstance[0]);
if (MeshOffset & 3)
{
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "The occluder mesh contains invalid data. Please reexport the occluder mesh.");
stl::free_container(m_OCMBuffer);
return false;
}
}
return true;
}
void CCullThread::UnloadLevel()
{
stl::free_container(m_OCMBuffer);
m_pOCMBufferAligned = NULL;
m_OCMMeshCount = 0;
m_OCMInstCount = 0;
m_OCMOffsetInstances = 0;
}
void CCullThread::PrepareCullbufferAsync(const CCamera& rCamera)
{
Matrix44 MatProj;
Matrix44 MatView;
Matrix44 MatViewProj;
#if !defined(_RELEASE) // debug code to catch double invocations of the prepare occlusion buffer job per frame
static int _debug = -1;
if (_debug == -1)
{
_debug = gEnv->pRenderer->GetFrameID(false);
}
else if (_debug == gEnv->pRenderer->GetFrameID(false))
{
__debugbreak();
}
else
{
_debug = gEnv->pRenderer->GetFrameID(false);
}
#endif
const CCamera& rCam = rCamera;
CCamera tmp_cam = m_pRenderer->GetCamera();
m_pRenderer->SetCamera(rCam);
m_pRenderer->GetModelViewMatrix(reinterpret_cast<f32*>(&MatView));
m_pRenderer->GetProjectionMatrix(reinterpret_cast<f32*>(&MatProj));
m_pRenderer->SetCamera(tmp_cam);
uint32 nReverseDepthEnabled = 0;
m_pRenderer->EF_Query(EFQ_ReverseDepthEnabled, nReverseDepthEnabled);
if (nReverseDepthEnabled) // Convert to regular depth again. TODO: make occlusion culler work with reverse depth
{
MatProj.m22 = -MatProj.m22 + MatProj.m23;
MatProj.m32 = -MatProj.m32 + MatProj.m33;
}
m_ViewDir = rCam.GetViewdir();
MatViewProj = MatView * MatProj;
MatViewProj.Transpose();
const float SCALEX = static_cast<float>(CULL_SIZEX / 2);
const float SCALEY = static_cast<float>(CULL_SIZEY / 2);
const Matrix44A MatScreen(SCALEX, 0.f, 0.f, SCALEX,
0.f, -SCALEY, 0.f, SCALEY,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f);
m_MatScreenViewProj = MatScreen * MatViewProj;
m_MatScreenViewProjTransposed = m_MatScreenViewProj.GetTransposed();
m_NearPlane = rCam.GetNearPlane();
m_FarPlane = rCam.GetFarPlane();
m_NearestMax = m_pRenderer->GetNearestRangeMax();
m_Position = rCam.GetPosition();
HWZBuffer.ZBufferSizeX = CULL_SIZEX;
HWZBuffer.ZBufferSizeY = CULL_SIZEY;
GetObjManager()->BeginCulling();
m_nPrepareState = PREPARE_STARTED;
m_Enabled = false;
m_bCheckOcclusionRequested = 0;
RASTERIZER.Prepare();
m_PrepareBufferSync.PushCompletionFence();
m_OcclusionJobExecutor.StartJob([this]()
{
this->PrepareOcclusion();
}); // legacy: job.SetPriorityLevel(JobManager::eHighPriority); job.SetBlocking();
}
void CCullThread::CullStart(const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
// signal rasterizer that it should stop
m_bCheckOcclusionRequested = 1;
// tell the job that the PPU is ready for occlusion culling, this call will
// start the check occlusion job if the prepare step has finished, if not
// the prepare job itself will start the culling job
bool bNeedJobStart = false;
{
AUTO_LOCK(m_FollowUpLock);
if (m_nPrepareState == PREPARE_DONE)
{
m_nPrepareState = CHECK_STARTED;
bNeedJobStart = true;
}
else
{
m_nPrepareState = CHECK_REQUESTED;
*((SRenderingPassInfo*)m_passInfoForCheckOcclusion) = passInfo;
}
}
if (bNeedJobStart)
{
m_OcclusionJobExecutor.StartJob([this, passInfo]()
{
this->CheckOcclusion(passInfo);
}); // legacy: job.SetPriorityLevel(JobManager::eHighPriority);
}
}
void CCullThread::CullEnd(bool waitForOcclusionJobCompletion)
{
// If no frame was rendered, we need to remove the producer added in BeginCulling
m_PrepareBufferSync.WaitForCompletion();
bool bNeedRemoveProducer = false;
{
if (m_nPrepareState != CHECK_STARTED && m_nPrepareState != IDLE)
{
bNeedRemoveProducer = true;
}
}
if (bNeedRemoveProducer)
{
GetObjManager()->RemoveCullJobProducer();
m_nPrepareState = IDLE; // No producer so mark us as idle
}
if (waitForOcclusionJobCompletion)
{
m_OcclusionJobExecutor.WaitForCompletion();
}
}
void CCullThread::OutputMeshList()
{
}
float DistToBox(Vec3 Center, Vec3 Extends, Vec3 ViewPos)
{
Vec3 Delta = (ViewPos - Center).abs();
Delta = (Delta - Extends);
Delta.x = max(Delta.x, 0.f);
Delta.y = max(Delta.y, 0.f);
Delta.z = max(Delta.z, 0.f);
return Delta.x * Delta.x + Delta.y * Delta.y + Delta.z * Delta.z;
}
void CCullThread::RasterizeZBuffer(uint32 PolyLimit)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
if (m_OCMInstCount == 0)
{
float fRed[4] = {1, 0, 0, 1};
gEnv->pRenderer->Draw2dLabel(1.0f, 5.0f, 1.6f, fRed, false, "OCM file failed to load -> no occlusion checking possible!");
return;
}
uint Tmp[16 * sizeof(float) * 2 + 16];
const uint8* pMeshes = m_pOCMBufferAligned;//actually starts at 16, but MeshOffset is zero based
uint8* pInstances = &m_pOCMBufferAligned[m_OCMOffsetInstances];
Matrix44A& rTmp0 = *reinterpret_cast<Matrix44A*>((reinterpret_cast<size_t>(Tmp) + 15) & ~15);
Matrix44A& rTmp1 = *reinterpret_cast<Matrix44A*>((reinterpret_cast<size_t>(Tmp) + 15 + 64) & ~15);
rTmp0 = m_MatScreenViewProj.GetTransposed();
int Visible = 0;
int Invisible = 0;
uint32 Poly = 0;
float LastDist;
uint8* pLastInstance = 0;
bool Swapped = true;
for (size_t c = 0; c < 20 && Swapped; c++)//incrementally (max 20 rounds) bubblesort instances front to back
{
Swapped = false;
LastDist = -1.f;
for (size_t a = 0; a < m_OCMInstCount; a++)
{
Matrix44 World(IDENTITY);
uint8* pInstance = pInstances + a * (sizeof(int) + 12 * sizeof(float));//meshoffset+worldmatrix43
const uint32 MeshOffset = *reinterpret_cast<const uint32*>(&pInstance[0]);
const float* pWorldMat = reinterpret_cast<const float*>(&pInstance[4]);
memcpy(&World, (void*)pWorldMat, 12 * sizeof(float));
//simple incremental bubblesort
const float Dist = (World.GetTranslation() - m_Position).GetLength();
if (Dist < LastDist)
{
PREFAST_ASSUME(pLastInstance);
Swapped = true;
for (size_t b = 0; b < 13; b++)
{
std::swap(reinterpret_cast<uint32*>(pLastInstance)[b], reinterpret_cast<uint32*>(pInstance)[b]);
}
}
LastDist = Dist;
pLastInstance = pInstance;
}
}
const bool EarlyOut = GetCVars()->e_CoverageBufferEarlyOut == 1;
const int64 MaxEarlyOutDelay = (int64)(GetCVars()->e_CoverageBufferEarlyOutDelay * 1000.0f);
LastDist = -1.f;
ITimer* pTimer = gEnv->pTimer;
int64 StartTime = -1;
for (size_t a = 0; a < m_OCMInstCount && (PolyLimit == 0 || Poly < PolyLimit); a++)
{
// stop if MT need to run check occlusion
if (EarlyOut && *const_cast<volatile int*>(&m_bCheckOcclusionRequested))
{
if (StartTime < 0)
{
StartTime = pTimer->GetAsyncTime().GetMicroSecondsAsInt64();
}
int64 CurTime = pTimer->GetAsyncTime().GetMicroSecondsAsInt64();
if (CurTime - StartTime > MaxEarlyOutDelay)
{
break;
}
}
Matrix44 World(IDENTITY);
uint8* pInstance = pInstances + a * (sizeof(int) + 12 * sizeof(float));//meshoffset+worldmatrix43
const uint32 MeshOffset = *reinterpret_cast<volatile const uint32*>(&pInstance[0]);
const float* pWorldMat = reinterpret_cast<const float*>(&pInstance[4]);
memcpy(&World, (void*)pWorldMat, 12 * sizeof(float));
Vec3 Pos = World.GetTranslation(), Extend;
Extend.x = (fabsf(World.m00) + fabsf(World.m01) + fabsf(World.m02)) * (127.f);
Extend.y = (fabsf(World.m10) + fabsf(World.m11) + fabsf(World.m12)) * (127.f);
Extend.z = (fabsf(World.m20) + fabsf(World.m21) + fabsf(World.m22)) * (127.f);
const int InFrustum = RASTERIZER.AABBInFrustum(reinterpret_cast<NVMath::vec4*>(&rTmp0), Pos - Extend, Pos + Extend, m_Position);
if (!InFrustum)
{
Invisible++;
continue;
}
else
{
Visible++;
}
rTmp1 = (m_MatScreenViewProj * World).GetTransposed();
const uint8* pMesh = pMeshes + MeshOffset;
const size_t TriCount = *reinterpret_cast<const uint32*>(pMesh);
const size_t Tris16 = (reinterpret_cast<size_t>(pMesh + 4) + 15) & ~15;
const int8* pTris = reinterpret_cast<const int8*>(Tris16);
if (InFrustum & 2)
{
RASTERIZER.Rasterize<true>(reinterpret_cast<NVMath::vec4*>(&rTmp1), reinterpret_cast<const NVMath::vec4*>(pTris), TriCount);
}
else
{
RASTERIZER.Rasterize<false>(reinterpret_cast<NVMath::vec4*>(&rTmp1), reinterpret_cast<const NVMath::vec4*>(pTris), TriCount);
}
Poly += TriCount;
}
}
#if !defined(_RELEASE)
void CCullThread::CoverageBufferDebugDraw()
{
RASTERIZER.DrawDebug(m_pRenderer, 1);
}
#endif
void CCullThread::PrepareOcclusion()
{
if (!GetCVars()->e_CameraFreeze)
{
FUNCTION_PROFILER_3DENGINE;
using namespace NVMath;
int bHWZBuffer = GetCVars()->e_CoverageBufferReproj;
if (bHWZBuffer > 3 && m_OCMBuffer.empty())
{
bHWZBuffer = 2;
}
if ((bHWZBuffer & 3) > 0)
{
m_Enabled = RASTERIZER.DownLoadHWDepthBuffer(m_NearPlane, m_FarPlane, m_NearestMax, GetCVars()->e_CoverageBufferBias);
}
else
{
RASTERIZER.Clear();
}
}
m_OcclusionJobExecutor.StartJob([this]()
{
this->PrepareOcclusion_ReprojectZBuffer();
}); // legacy: job.SetPriorityLevel(JobManager::eHighPriority);
}
void CCullThread::PrepareOcclusion_ReprojectZBuffer()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
int bHWZBuffer = GetCVars()->e_CoverageBufferReproj;
if (bHWZBuffer > 3 && m_OCMBuffer.empty())
{
bHWZBuffer = 2;
}
if (!GetCVars()->e_CameraFreeze && (bHWZBuffer & 3) > 0 && m_Enabled)
{
enum
{
nLinesPerJob = 8
};
m_nRunningReprojJobs = tdCullRasterizer::RESOLUTION_Y / nLinesPerJob;
m_nRunningReprojJobsAfterMerge = tdCullRasterizer::RESOLUTION_Y / nLinesPerJob;
for (int i = 0; i < tdCullRasterizer::RESOLUTION_Y; i += nLinesPerJob)
{
m_OcclusionJobExecutor.StartJob([this, i]()
{
this->PrepareOcclusion_ReprojectZBufferLine(i, nLinesPerJob);
}); // legacy: job.SetPriorityLevel(JobManager::eHighPriority);
}
}
else
{
m_OcclusionJobExecutor.StartJob([this]()
{
this->PrepareOcclusion_RasterizeZBuffer();
}); // job.SetPriorityLevel(JobManager::eHighPriority);
}
}
void CCullThread::PrepareOcclusion_ReprojectZBufferLine(int nStartLine, int nNumLines)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
if (!GetCVars()->e_CameraFreeze)
{
uint Tmp[80];
Matrix44A& rTmp = *reinterpret_cast<Matrix44A*>((reinterpret_cast<size_t>(Tmp) + 15) & ~15);
rTmp = m_MatScreenViewProjTransposed;
RASTERIZER.ReprojectHWDepthBuffer(rTmp, m_NearPlane, m_FarPlane, m_NearestMax, GetCVars()->e_CoverageBufferBias, nStartLine, nNumLines);
}
uint32 nRemainingJobs = CryInterlockedDecrement((volatile int*)&m_nRunningReprojJobs);
if (nRemainingJobs == 0)
{
enum
{
nLinesPerJob = 8
};
for (int i = 0; i < tdCullRasterizer::RESOLUTION_Y; i += nLinesPerJob)
{
m_OcclusionJobExecutor.StartJob([this, i]()
{
this->PrepareOcclusion_ReprojectZBufferLineAfterMerge(i, nLinesPerJob);
}); // job.SetPriorityLevel(JobManager::eHighPriority);
}
}
}
void CCullThread::PrepareOcclusion_ReprojectZBufferLineAfterMerge(int nStartLine, int nNumLines)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
// merge the reprojected buffer bevore new jobs are started on it
RASTERIZER.MergeReprojectHWDepthBuffer(nStartLine, nNumLines);
if (!GetCVars()->e_CameraFreeze)
{
uint Tmp[80];
Matrix44A& rTmp = *reinterpret_cast<Matrix44A*>((reinterpret_cast<size_t>(Tmp) + 15) & ~15);
rTmp = m_MatScreenViewProjTransposed;
RASTERIZER.ReprojectHWDepthBufferAfterMerge(rTmp, m_NearPlane, m_FarPlane, m_NearestMax, GetCVars()->e_CoverageBufferBias, nStartLine, nNumLines);
}
uint32 nRemainingJobs = CryInterlockedDecrement((volatile int*)&m_nRunningReprojJobsAfterMerge);
if (nRemainingJobs == 0)
{
m_OcclusionJobExecutor.StartJob([this]()
{
this->PrepareOcclusion_RasterizeZBuffer();
}); //job.SetPriorityLevel(JobManager::eHighPriority);
}
}
void CCullThread::PrepareOcclusion_RasterizeZBuffer()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
m_Enabled = true;
if (!GetCVars()->e_CameraFreeze)
{
int bHWZBuffer = GetCVars()->e_CoverageBufferReproj;
int PolyLimit = GetCVars()->e_CoverageBufferRastPolyLimit;
if (bHWZBuffer > 3 && m_OCMBuffer.empty())
{
bHWZBuffer = 2;
}
bool rast_z_buff = (bHWZBuffer & 4) ? true : false;
if (rast_z_buff)
{
m_Enabled = true;
RasterizeZBuffer((uint32)PolyLimit);
}
}
bool bNeedJobStart = false;
{
AUTO_LOCK(m_FollowUpLock);
if (m_nPrepareState == CHECK_REQUESTED)
{
m_nPrepareState = CHECK_STARTED;
bNeedJobStart = true;
}
else
{
m_nPrepareState = PREPARE_DONE;
}
}
m_PrepareBufferSync.PopCompletionFence();
if (bNeedJobStart)
{
m_OcclusionJobExecutor.StartJob([this]()
{
this->CheckOcclusion(*reinterpret_cast<SRenderingPassInfo*>(m_passInfoForCheckOcclusion));
}); // legacy: job.SetPriorityLevel(JobManager::eHighPriority)
}
}
void CCullThread::CheckOcclusion(SRenderingPassInfo passInfo)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Renderer);
uint8 AlignBuffer[2 * sizeof(Matrix44A) + 16];
size_t pBuffer = (reinterpret_cast<size_t>(AlignBuffer) + 15) & ~15;
Matrix44A& RESTRICT_REFERENCE rMatFinalT = reinterpret_cast<Matrix44A*>(pBuffer)[1];
Vec3 localPostion;
memcpy(&localPostion, &m_Position, sizeof(Vec3));
const AABB PosAABB = AABB(m_Position, 0.5f);
const float Bias = GetCVars()->e_CoverageBufferAABBExpand;
rMatFinalT = m_MatScreenViewProj.GetTransposed();
// Debugging stats in green to screen here with how many octree nodes pass/fail and how many terrain nodes pass/fail
unsigned int octreeNodesCulled = 0;
unsigned int octreeNodesVisible = 0;
while (1)
{
SCheckOcclusionJobData jobData;
GetObjManager()->PopFromCullQueue(&jobData);
// stop processing when beeing told so
if (jobData.type == SCheckOcclusionJobData::QUIT)
{
break;
}
if (jobData.type == SCheckOcclusionJobData::OCTREE_NODE)
{
AABB rAABB;
COctreeNode* pOctTreeNode = (COctreeNode*)jobData.octTreeData.pOctTreeNode;
memcpy(&rAABB, &pOctTreeNode->GetObjectsBBox(), sizeof(AABB));
float fDistance = sqrtf(Distance::Point_AABBSq(passInfo.GetCamera().GetPosition(), rAABB));
// Test OctTree BoundingBox
if (TestAABB(rAABB, fDistance))
{
pOctTreeNode->COctreeNode::RenderContent(jobData.octTreeData.nRenderMask, passInfo, jobData.rendItemSorter, jobData.pCam);
octreeNodesVisible++;
}
else
{
octreeNodesCulled++;
}
}
else
{
__debugbreak(); // unknown culler job type
}
}
if (GetCVars()->e_CoverageBufferDebug)
{
float fGreen[4] = {0, 1, 0, 1};
gEnv->pRenderer->Draw2dLabel(16.0f, 32.0f, 1.6f, fGreen, false, AZStd::string::format("Octree Nodes Culled %i, Octree Nodes Visible %i",octreeNodesCulled, octreeNodesVisible).c_str());
}
GetObjManager()->RemoveCullJobProducer();
}
///////////////////////////////////////////////////////////////////////////////
bool CCullThread::TestAABB(const AABB& rAABB, float fEntDistance, float fVerticalExpand)
{
IF (GetCVars()->e_CheckOcclusion == 0, 0)
{
return true;
}
const AABB PosAABB = AABB(m_Position, 0.5f);
const float Bias = GetCVars()->e_CoverageBufferAABBExpand;
DEFINE_ALIGNED_DATA(Matrix44A, rMatFinalT(m_MatScreenViewProj.GetTransposed()), 16);
AABB bbox(rAABB);
if (Bias < 0.f)
{
bbox.Expand((bbox.max - bbox.min) * -Bias - Vec3(Bias, Bias, Bias));
}
else
{
bbox.Expand(Vec3(Bias * fEntDistance));
}
float fVerticalExpandScaled = fVerticalExpand * fEntDistance;
bbox.min.z -= fVerticalExpandScaled;
bbox.max.z += fVerticalExpandScaled;
if (!m_Enabled)
{
return true;
}
if (bbox.IsIntersectBox(PosAABB))
{
return true;
}
if (RASTERIZER.TestAABB(reinterpret_cast<const NVMath::vec4*>(&rMatFinalT), bbox.min, bbox.max, m_Position))
{
return true;
}
return false;
}
bool CCullThread::TestQuad(const Vec3& vCenter, const Vec3& vAxisX, const Vec3& vAxisY)
{
IF (GetCVars()->e_CheckOcclusion == 0, 0)
{
return true;
}
if (!m_Enabled)
{
return true;
}
DEFINE_ALIGNED_DATA(Matrix44A, rMatFinalT(m_MatScreenViewProj.GetTransposed()), 16);
if (RASTERIZER.TestQuad(reinterpret_cast<const NVMath::vec4*>(&rMatFinalT), vCenter, vAxisX, vAxisY))
{
return true;
}
return false;
}
} // namespace NAsyncCull
-128
View File
@@ -1,128 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CCULLTHREAD_H
#define CRYINCLUDE_CRY3DENGINE_CCULLTHREAD_H
#pragma once
#include "CryThread.h"
#include <AzCore/Jobs/LegacyJobExecutor.h>
namespace NAsyncCull
{
class CCullThread
: public Cry3DEngineBase
{
bool m_Enabled;
bool m_Active; // used to verify that the cull job is running and no new jobs are added after the job has finished
public:
enum PrepareStateT
{
IDLE, PREPARE_STARTED, PREPARE_DONE, CHECK_REQUESTED, CHECK_STARTED
};
PrepareStateT m_nPrepareState;
CryCriticalSection m_FollowUpLock;
char m_passInfoForCheckOcclusion[sizeof(SRenderingPassInfo)];
uint32 m_nRunningReprojJobs;
uint32 m_nRunningReprojJobsAfterMerge;
int m_bCheckOcclusionRequested;
private:
AZ::LegacyJobExecutor m_OcclusionJobExecutor; // All jobs pushed against this instance to gurantee a wait on all jobs before exiting ~CCullThread
AZ::LegacyJobExecutor m_PrepareBufferSync;
Matrix44A m_MatScreenViewProj _ALIGN(16);
Matrix44A m_MatScreenViewProjTransposed;
Vec3 m_ViewDir;
Vec3 m_Position;
float m_NearPlane;
float m_FarPlane;
float m_NearestMax;
PodArray<uint8> m_OCMBuffer;
uint8* m_pOCMBufferAligned;
uint32 m_OCMMeshCount;
uint32 m_OCMInstCount;
uint32 m_OCMOffsetInstances;
template<class T>
T Swap(T& rData)
{
// #if IS_LOCAL_MACHINE_BIG_ENDIAN
PREFAST_SUPPRESS_WARNING(6326)
switch (sizeof(T))
{
case 1:
break;
case 2:
SwapEndianBase(reinterpret_cast<uint16*>(&rData));
break;
case 4:
SwapEndianBase(reinterpret_cast<uint32*>(&rData));
break;
case 8:
SwapEndianBase(reinterpret_cast<uint64*>(&rData));
break;
default:
#if defined(__clang__) || defined(__GNUC__)
__builtin_unreachable();
#else
__assume(0);
#endif
}
//#endif
return rData;
}
void RasterizeZBuffer(uint32 PolyLimit);
void OutputMeshList();
public:
void CheckOcclusion(SRenderingPassInfo passInfo);
void PrepareOcclusion();
void PrepareOcclusion_RasterizeZBuffer();
void PrepareOcclusion_ReprojectZBuffer();
void PrepareOcclusion_ReprojectZBufferLine(int nStartLine, int nNumLines);
void PrepareOcclusion_ReprojectZBufferLineAfterMerge(int nStartLine, int nNumLines);
void Init();
bool LoadLevel(const char* pFolderName);
void UnloadLevel();
bool TestAABB(const AABB& rAABB, float fEntDistance, float fVerticalExpand = 0.0f);
bool TestQuad(const Vec3& vCenter, const Vec3& vAxisX, const Vec3& vAxisY);
CCullThread();
~CCullThread() = default;
#ifndef _RELEASE
void CoverageBufferDebugDraw();
#endif
void PrepareCullbufferAsync(const CCamera& rCamera);
void CullStart(const SRenderingPassInfo& passInfo);
void CullEnd(bool waitForOcclusionJobCompletion = false);
bool IsActive() const { return m_Active; }
void SetActive(bool bActive) { m_Active = bActive; }
Vec3 GetViewDir() { return m_ViewDir; };
} _ALIGN(128);
}
#endif // CRYINCLUDE_CRY3DENGINE_CCULLTHREAD_H
File diff suppressed because it is too large Load Diff
-166
View File
@@ -1,166 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CGFLOADER_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CGFLOADER_H
#pragma once
#include "../MeshCompiler/MeshCompiler.h"
#include "ChunkFile.h"
#include "CGFContent.h"
#if defined(RESOURCE_COMPILER) || defined(ENABLE_NON_COMPILED_CGF)
#include "../../../Tools/CryCommonTools/Export/MeshUtils.h"
#endif
//////////////////////////////////////////////////////////////////////////
class ILoaderCGFListener
{
public:
virtual ~ILoaderCGFListener(){}
virtual void Warning(const char* format) = 0;
virtual void Error(const char* format) = 0;
virtual bool IsValidationEnabled() { return true; }
};
class CLoaderCGF
{
public:
typedef void* (* AllocFncPtr)(size_t);
typedef void (* DestructFncPtr)(void*);
CLoaderCGF(AllocFncPtr pAlloc = operator new, DestructFncPtr pDestruct = operator delete, bool bAllowStreamSharing = true);
~CLoaderCGF();
CContentCGF* LoadCGF(const char* filename, IChunkFile& chunkFile, ILoaderCGFListener* pListener, unsigned long nLoadingFlags = 0);
bool LoadCGF(CContentCGF* pContentCGF, const char* filename, IChunkFile& chunkFile, ILoaderCGFListener* pListener, unsigned long nLoadingFlags = 0);
bool LoadCGFFromMem(CContentCGF* pContentCGF, const void* pData, size_t nDataLen, IChunkFile& chunkFile, ILoaderCGFListener* pListener, unsigned long nLoadingFlags = 0);
bool LoadCGFWork(CContentCGF* pContentCGF, const char* filename, IChunkFile& chunkFile, ILoaderCGFListener* pListener, unsigned long nLoadingFlags);
const char* GetLastError() { return m_LastError; }
CContentCGF* GetCContentCGF() { return m_pCompiledCGF; }
void SetMaxWeightsPerVertex(int maxWeightsPerVertex) { m_maxWeightsPerVertex = maxWeightsPerVertex; }
private:
bool LoadChunks(bool bJustGeometry);
bool LoadExportFlagsChunk(IChunkFile::ChunkDesc* pChunkDesc);
bool LoadNodeChunk(IChunkFile::ChunkDesc* pChunkDesc, bool bJustGeometry);
bool LoadHelperChunk(CNodeCGF* pNode, IChunkFile::ChunkDesc* pChunkDesc);
bool LoadGeomChunk(CNodeCGF* pNode, IChunkFile::ChunkDesc* pChunkDesc);
bool LoadCompiledMeshChunk(CNodeCGF* pNode, IChunkFile::ChunkDesc* pChunkDesc);
template<class MESH_CHUNK_DESC>
bool LoadCompiledMeshChunk(CNodeCGF* pNode, IChunkFile::ChunkDesc* pChunkDesc, MESH_CHUNK_DESC chunk);
bool LoadMeshSubsetsChunk(CMesh& mesh, IChunkFile::ChunkDesc* pChunkDesc, std::vector<std::vector<uint16> >& globalBonesPerSubset);
bool LoadStreamDataChunk(int nChunkId, void*& pStreamData, int& nStreamType, int& nStreamIndex, int& nCount, int& nElemSize, bool& bSwapEndianness);
template<class T, class MESH_CHUNK_DESC>
bool LoadStreamChunk(CMesh& mesh, const MESH_CHUNK_DESC& chunk, ECgfStreamType Type, int streamIndex, CMesh::EStream MStream);
//! Used to load data into one of two potential destination streams in CMesh determined by the element size of the data stored in the .cgf. e.g. load into either CMesh::POSITIONS or CMesh::POSITIONS16
template<class TA, class TB, class MESH_CHUNK_DESC>
bool LoadStreamChunk(CMesh& mesh, const MESH_CHUNK_DESC& chunk, ECgfStreamType Type, int streamIndex, CMesh::EStream MStreamA, CMesh::EStream MStreamB);
template<class MESH_CHUNK_DESC>
bool LoadBoneMappingStreamChunk(CMesh& mesh, const MESH_CHUNK_DESC& chunk, const std::vector<std::vector<uint16> >& globalBonesPerSubset);
template<class MESH_CHUNK_DESC>
bool LoadIndexStreamChunk(CMesh& mesh, const MESH_CHUNK_DESC& chunk);
bool LoadPhysicsDataChunk(CNodeCGF* pNode, int nPhysGeomType, int nChunkId);
bool LoadFoliageInfoChunk(IChunkFile::ChunkDesc* pChunkDesc);
CMaterialCGF* LoadMaterialFromChunk(int nChunkId);
CMaterialCGF* LoadMaterialNameChunk(IChunkFile::ChunkDesc* pChunkDesc);
void ProcessNodes();
void SetupMeshSubsets(CMesh& mesh, CMaterialCGF* pMaterialCGF);
//////////////////////////////////////////////////////////////////////////
// loading of skinned meshes
//////////////////////////////////////////////////////////////////////////
bool ProcessSkinning();
CContentCGF* MakeCompiledSkinCGF(CContentCGF* pCGF, std::vector<int>* pVertexRemapping, std::vector<int>* pIndexRemapping);
//old chunks
bool ReadBoneNameList(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadMorphTargets(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadBoneInitialPos(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadBoneHierarchy(IChunkFile::ChunkDesc* pChunkDesc);
uint32 RecursiveBoneLoader(int nBoneParentIndex, int nBoneIndex);
bool ReadBoneMesh(IChunkFile::ChunkDesc* pChunkDesc);
//new chunks
bool ReadCompiledBones(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledPhysicalBones(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledPhysicalProxies(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledMorphTargets(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledIntFaces(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledIntSkinVertice(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledExt2IntMap(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledBonesBoxes(IChunkFile::ChunkDesc* pChunkDesc);
bool ReadCompiledBreakablePhysics(IChunkFile::ChunkDesc* pChunkDesc);
void Warning(const char* szFormat, ...) PRINTF_PARAMS(2, 3);
private:
uint32 m_IsCHR;
uint32 m_CompiledBones;
uint32 m_CompiledBonesBoxes;
uint32 m_CompiledMesh;
uint32 m_numBonenameList;
uint32 m_numBoneInitialPos;
uint32 m_numMorphTargets;
uint32 m_numBoneHierarchy;
std::vector<uint32> m_arrIndexToId; // the mapping BoneIndex -> BoneID
std::vector<uint32> m_arrIdToIndex; // the mapping BoneID -> BineIndex
std::vector<string> m_arrBoneNameTable; // names of bones
std::vector<Matrix34> m_arrInitPose34;
#if defined(RESOURCE_COMPILER) || defined(ENABLE_NON_COMPILED_CGF)
std::vector<MeshUtils::VertexLinks> m_arrLinksTmp;
std::vector<int> m_vertexOldToNew; // used to re-map uncompiled Morph Target vertices right after reading them
#endif
CContentCGF* m_pCompiledCGF;
const void* m_pBoneAnimRawData, * m_pBoneAnimRawDataEnd;
uint32 m_numBones;
int m_nNextBone;
//////////////////////////////////////////////////////////////////////////
string m_LastError;
char m_filename[260];
IChunkFile* m_pChunkFile;
CContentCGF* m_pCGF;
// To find really used materials
uint16 MatIdToSubset[MAX_SUB_MATERIALS];
int nLastChunkId;
ILoaderCGFListener* m_pListener;
bool m_bUseReadOnlyMesh;
bool m_bAllowStreamSharing;
int m_maxWeightsPerVertex;
AllocFncPtr m_pAllocFnc;
DestructFncPtr m_pDestructFnc;
};
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CGFLOADER_H
File diff suppressed because it is too large Load Diff
-110
View File
@@ -1,110 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CGFSAVER_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CGFSAVER_H
#pragma once
#if defined(RESOURCE_COMPILER) || defined(INCLUDE_SAVECGF)
#include "CGFContent.h"
class CChunkFile;
//////////////////////////////////////////////////////////////////////////
class CSaverCGF
{
public:
CSaverCGF(CChunkFile& chunkFile);
void SaveContent(CContentCGF* pCGF, bool bSwapEndian, bool bStorePositionsAsF16, bool bUseQtangents, bool bStoreIndicesAsU16);
void SetContent(CContentCGF* pCGF);
const CContentCGF* GetContent() const;
// Enable/Disable saving of the node mesh.
void SetMeshDataSaving(bool bEnable);
// Enable/Disable saving of non-mesh related data.
void SetNonMeshDataSaving(bool bEnable);
// Enable/disable saving of physics meshes.
void SetSavePhysicsMeshes(bool bEnable);
// Enable compaction of vertex streams (for optimised streaming)
void SetVertexStreamCompacting(bool bEnable);
// Enable computation of subset texel density
void SetSubsetTexelDensityComputing(bool bEnable);
void SaveNodes(bool bSwapEndian, bool bStorePositionsAsF16, bool bUseQtangents, bool bStoreIndicesAsU16);
int SaveNode(CNodeCGF* pNode, bool bSwapEndian, bool bStorePositionsAsF16, bool bUseQtangents, bool bStoreIndicesAsU16);
void SaveMaterials(bool bSwapEndian);
int SaveMaterial(CMaterialCGF* pMtl, bool bNeedSwap);
int SaveExportFlags(bool bSwapEndian);
// Compiled chunks for characters
int SaveCompiledBones(bool bSwapEndian, void* pData, int nSize, int version);
int SaveCompiledPhysicalBones(bool bSwapEndian, void* pData, int nSize, int version);
int SaveCompiledPhysicalProxis(bool bSwapEndian, void* pData, int nSize, uint32 numIntMorphTargets, int version);
int SaveCompiledMorphTargets(bool bSwapEndian, void* pData, int nSize, uint32 numIntMorphTargets, int version);
int SaveCompiledIntFaces(bool bSwapEndian, void* pData, int nSize, int version);
int SaveCompiledIntSkinVertices(bool bSwapEndian, void* pData, int nSize, int version);
int SaveCompiledExt2IntMap(bool bSwapEndian, void* pData, int nSize, int version);
int SaveCompiledBoneBox(bool bSwapEndian, void* pData, int nSize, int version);
// Chunks for characters (for Collada->cgf export)
int SaveBones(bool bSwapEndian, void* pData, int numBones, int nSize);
int SaveBoneNames(bool bSwapEndian, char* boneList, int numBones, int listSize);
int SaveBoneInitialMatrices(bool bSwapEndian, SBoneInitPosMatrix* matrices, int numBones, int nSize);
int SaveBoneMesh(bool bSwapEndian, PhysicalProxy& proxy);
#if defined(RESOURCE_COMPILER)
void SaveUncompiledNodes();
int SaveUncompiledNode(CNodeCGF* pNode);
void SaveUncompiledMorphTargets();
int SaveUncompiledNodeMesh(CNodeCGF* pNode);
int SaveUncompiledHelperChunk(CNodeCGF* pNode);
#endif
int SaveBreakablePhysics(bool bNeedEndianSwap);
int SaveController831(bool bSwapEndian, const CONTROLLER_CHUNK_DESC_0831& ctrlChunk, void* pData, int nSize);
int SaveControllerDB905(bool bSwapEndian, const CONTROLLER_CHUNK_DESC_0905& ctrlChunk, void* pData, int nSize);
int SaveFoliage();
private:
// Return mesh chunk id
int SaveNodeMesh(CNodeCGF* pNode, bool bSwapEndian, bool bStorePositionsAsF16, bool bUseQTangents, bool bStoreIndicesAsU16);
int SaveHelperChunk(CNodeCGF* pNode, bool bSwapEndian);
int SaveMeshSubsetsChunk(CMesh& mesh, bool bSwapEndian);
int SaveStreamDataChunk(const void* pStreamData, int nStreamType, int nStreamIndex, int nCount, int nElemSize, bool bSwapEndian);
int SavePhysicalDataChunk(const void* pData, int nSize, bool bSwapEndian);
private:
CChunkFile* m_pChunkFile;
CContentCGF* m_pCGF;
std::set<CNodeCGF*> m_savedNodes;
std::set<CMaterialCGF*> m_savedMaterials;
std::map<CMesh*, int> m_mapMeshToChunk;
bool m_bDoNotSaveMeshData;
bool m_bDoNotSaveNonMeshData;
bool m_bSavePhysicsMeshes;
bool m_bCompactVertexStreams;
bool m_bComputeSubsetTexelDensity;
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CGFSAVER_H
@@ -1,39 +0,0 @@
#
# 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.
#
ly_add_target(
NAME Cry3DEngine.CGF.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
cry3dengine_cgf_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
)
ly_add_target(
NAME Cry3DEngine.CGF.RC.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
cry3dengine_cgf_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
COMPILE_DEFINITIONS
PRIVATE
RESOURCE_COMPILER
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
)
@@ -1,40 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CHUNKDATA_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CHUNKDATA_H
#pragma once
struct CChunkData
{
char* data;
int size;
CChunkData() { data = 0; size = 0; }
~CChunkData() { free(data); }
template <class T>
void Add(const T& object)
{
AddData(&object, sizeof(object));
}
void AddData(const void* pSrcData, int nSrcDataSize)
{
data = (char*)realloc(data, size + nSrcDataSize);
memcpy(data + size, pSrcData, nSrcDataSize);
size += nSrcDataSize;
}
};
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CHUNKDATA_H
@@ -1,428 +0,0 @@
/*
* 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 <platform.h>
#include "ChunkFile.h"
#include "ChunkFileReaders.h"
#include "ChunkFileWriters.h"
#if !defined(FUNCTION_PROFILER_3DENGINE)
#define FUNCTION_PROFILER_3DENGINE
#endif
#if !defined(LOADING_TIME_PROFILE_SECTION)
#define LOADING_TIME_PROFILE_SECTION
#endif
namespace
{
inline bool ChunkLessOffset(const IChunkFile::ChunkDesc* const p0, const IChunkFile::ChunkDesc* const p1)
{
return IChunkFile::ChunkDesc::LessOffset(*p0, *p1);
}
}
CChunkFile::CChunkFile()
: m_pInternalData(NULL)
{
Clear();
}
CChunkFile::~CChunkFile()
{
Clear();
}
void CChunkFile::Clear()
{
ReleaseMemoryBuffer();
ReleaseChunks();
m_nLastChunkId = 0;
m_pInternalData = NULL;
m_bLoaded = false;
}
//////////////////////////////////////////////////////////////////////////
CChunkFile::ChunkDesc* CChunkFile::GetChunk(int nChunkIdx)
{
assert(nChunkIdx >= 0 && nChunkIdx < (int)m_chunks.size());
return m_chunks[nChunkIdx];
}
const CChunkFile::ChunkDesc* CChunkFile::GetChunk(int nChunkIdx) const
{
assert(nChunkIdx >= 0 && nChunkIdx < (int)m_chunks.size());
return m_chunks[nChunkIdx];
}
// number of chunks
int CChunkFile::NumChunks() const
{
return (int)m_chunks.size();
}
//////////////////////////////////////////////////////////////////////////
int CChunkFile::AddChunk(ChunkTypes chunkType, int chunkVersion, EEndianness eEndianness, const void* chunkData, int chunkSize)
{
ChunkDesc* const pChunk = new ChunkDesc;
pChunk->bSwapEndian = (eEndianness == eEndianness_NonNative);
// This block of code is used for debugging only
if (false)
{
for (size_t i = 0, n = m_chunks.size(); i < n; ++i)
{
if (m_chunks[i]->bSwapEndian != pChunk->bSwapEndian)
{
break;
}
}
}
pChunk->chunkType = chunkType;
pChunk->chunkVersion = chunkVersion;
const int chunkId = ++m_nLastChunkId;
pChunk->chunkId = chunkId;
pChunk->data = new char[chunkSize];
pChunk->size = chunkSize;
memcpy(pChunk->data, chunkData, chunkSize);
m_chunks.push_back(pChunk);
m_chunkIdMap[chunkId] = pChunk;
return chunkId;
}
//////////////////////////////////////////////////////////////////////////
void CChunkFile::DeleteChunkById(int nChunkId)
{
for (size_t i = 0, n = m_chunks.size(); i < n; ++i)
{
if (m_chunks[i]->chunkId == nChunkId)
{
m_chunkIdMap.erase(nChunkId);
if (m_chunks[i]->data)
{
delete [] (char*)m_chunks[i]->data;
m_chunks[i]->data = 0;
}
delete m_chunks[i];
m_chunks.erase(m_chunks.begin() + i);
return;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CChunkFile::DeleteChunksByType(ChunkTypes nChunkType)
{
size_t j = 0;
for (size_t i = 0, n = m_chunks.size(); i < n; ++i)
{
if (m_chunks[i]->chunkType == nChunkType)
{
m_chunkIdMap.erase(m_chunks[i]->chunkId);
if (m_chunks[i]->data)
{
delete [] (char*)m_chunks[i]->data;
m_chunks[i]->data = 0;
}
delete m_chunks[i];
}
else
{
m_chunks[j] = m_chunks[i];
++j;
}
}
m_chunks.resize(j);
}
//////////////////////////////////////////////////////////////////////////
void CChunkFile::ReleaseChunks()
{
m_bLoaded = false;
for (size_t i = 0; i < m_chunks.size(); ++i)
{
if (m_chunks[i]->data)
{
delete [] (char*) m_chunks[i]->data;
}
delete m_chunks[i];
}
m_chunks.clear();
m_chunkIdMap.clear();
}
//////////////////////////////////////////////////////////////////////////
CChunkFile::ChunkDesc* CChunkFile::FindChunkByType(ChunkTypes nChunkType)
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
if (m_chunks[i]->chunkType == nChunkType)
{
return m_chunks[i];
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
CChunkFile::ChunkDesc* CChunkFile::FindChunkById(int nChunkId)
{
ChunkIdMap::iterator it = m_chunkIdMap.find(nChunkId);
if (it != m_chunkIdMap.end())
{
return it->second;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
bool CChunkFile::Write(const char* filename)
{
if (m_chunks.empty())
{
m_LastError.Format("Writing *empty* chunk files is not supported (file '%s')", filename);
return false;
}
// Validate requested endianness. Kept here for debug.
if (false)
{
bool bHasSwapEndianTrue = false;
bool bHasSwapEndianFalse = false;
for (size_t i = 0; i < m_chunks.size(); ++i)
{
const ChunkDesc& cd = *m_chunks[i];
if (cd.bSwapEndian)
{
bHasSwapEndianTrue = true;
}
else
{
bHasSwapEndianFalse = true;
}
}
if (bHasSwapEndianTrue && bHasSwapEndianFalse)
{
//Warning("Writing chunk files with *mixed* endianness is not supported (file '%s')", filename);
}
}
ChunkFile::OsFileWriter writer;
if (!writer.Create(filename))
{
m_LastError.Format("Failed to open '%s' for writing", filename);
return false;
}
ChunkFile::MemorylessChunkFileWriter wr(ChunkFile::MemorylessChunkFileWriter::eChunkFileFormat_0x746, &writer);
wr.SetAlignment(4);
while (wr.StartPass())
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
const ChunkDesc& cd = *m_chunks[i];
wr.StartChunk((cd.bSwapEndian ? eEndianness_NonNative : eEndianness_Native), cd.chunkType, cd.chunkVersion, cd.chunkId);
wr.AddChunkData(cd.data, cd.size);
}
}
if (!wr.HasWrittenSuccessfully())
{
m_LastError.Format("Failed to write '%s'", filename);
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CChunkFile::WriteToMemoryBuffer(void** pData, int* nSize)
{
ReleaseMemoryBuffer();
*pData = 0;
if (m_chunks.empty())
{
m_LastError.Format("Writing *empty* chunk files is not supported");
return false;
}
// Do writing in *two* stages:
// 1) computing required size (see sizeWriter below)
// 2) allocating and writing data (see memoryWriter below)
ChunkFile::SizeWriter sizeWriter;
ChunkFile::MemoryWriter memoryWriter;
for (int stage = 0; stage < 2; ++stage)
{
ChunkFile::IWriter* pWriter;
if (stage == 0)
{
sizeWriter.Start();
pWriter = &sizeWriter;
}
else
{
assert(m_pInternalData == 0);
*nSize = sizeWriter.GetPos();
assert(*nSize > 0);
m_pInternalData = (char*)malloc(*nSize);
if (m_pInternalData == 0)
{
m_LastError.Format("Failed to allocate %u bytes", uint(*nSize));
return false;
}
if (!memoryWriter.Start(m_pInternalData, *nSize))
{
assert(0);
m_LastError.Format("Internal error");
ReleaseMemoryBuffer();
return false;
}
pWriter = &memoryWriter;
}
ChunkFile::MemorylessChunkFileWriter wr(ChunkFile::MemorylessChunkFileWriter::eChunkFileFormat_0x746, pWriter);
wr.SetAlignment(4);
while (wr.StartPass())
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
const ChunkDesc& cd = *m_chunks[i];
wr.StartChunk((cd.bSwapEndian ? eEndianness_NonNative : eEndianness_Native), cd.chunkType, cd.chunkVersion, cd.chunkId);
wr.AddChunkData(cd.data, cd.size);
}
}
if (!wr.HasWrittenSuccessfully())
{
m_LastError.Format("Failed to write");
ReleaseMemoryBuffer();
return false;
}
assert(stage != 2 || memoryWriter.GetPos() == *nSize);
}
*pData = m_pInternalData;
return true;
}
//////////////////////////////////////////////////////////////////////////
void CChunkFile::ReleaseMemoryBuffer()
{
if (m_pInternalData)
{
free(m_pInternalData);
m_pInternalData = 0;
}
}
//////////////////////////////////////////////////////////////////////////
bool CChunkFile::Read(const char* filename)
{
LOADING_TIME_PROFILE_SECTION;
ReleaseChunks();
// Loading chunks
{
ChunkFile::CryFileReader f;
if (!f.Open(filename))
{
m_LastError.Format("File %s failed to open for reading", filename);
return false;
}
{
const char* err = 0;
err = ChunkFile::GetChunkTableEntries_0x746(&f, m_chunks);
if (err)
{
err = ChunkFile::GetChunkTableEntries_0x744_0x745(&f, m_chunks);
if (!err)
{
err = ChunkFile::StripChunkHeaders_0x744_0x745(&f, m_chunks);
}
}
if (err)
{
m_LastError = err;
return false;
}
}
for (size_t i = 0; i < m_chunks.size(); ++i)
{
ChunkDesc& cd = *m_chunks[i];
assert(cd.data == 0);
cd.data = new char[cd.size];
if (!f.SetPos(cd.fileOffset) ||
!f.Read(cd.data, cd.size))
{
m_LastError.Format(
"Failed to read chunk data (offset:%u, size:%u) from file %s",
cd.fileOffset, cd.size, filename);
return false;
}
}
}
m_nLastChunkId = 0;
for (size_t i = 0; i < m_chunks.size(); ++i)
{
// Add chunk to chunk map.
m_chunkIdMap[m_chunks[i]->chunkId] = m_chunks[i];
// Update last chunk ID.
if (m_chunks[i]->chunkId > m_nLastChunkId)
{
m_nLastChunkId = m_chunks[i]->chunkId;
}
}
if (m_chunks.size() != m_chunkIdMap.size())
{
const int duplicateCount = (int)(m_chunks.size() - m_chunkIdMap.size());
m_LastError.Format(
"%d duplicate chunk ID%s found in file %s",
duplicateCount, ((duplicateCount > 1) ? "s" : ""), filename);
return false;
}
m_bLoaded = true;
return true;
}
@@ -1,97 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILE_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILE_H
#pragma once
#include <CrySizer.h>
#include <CryHeaders.h>
#include <smartptr.h>
#include <IChunkFile.h>
////////////////////////////////////////////////////////////////////////
// Chunk file reader.
// Accesses a chunked file structure through file mapping object.
// Opens a chunk file and checks for its validity.
// If it's invalid, closes it as if there was no open operation.
// Error handling is performed through the return value of Read():
// it must be true for successfully open files
////////////////////////////////////////////////////////////////////////
class CChunkFile
: public IChunkFile
{
public:
//////////////////////////////////////////////////////////////////////////
CChunkFile();
virtual ~CChunkFile();
// interface IChunkFile --------------------------------------------------
virtual void Release() { delete this; }
void Clear();
virtual bool IsReadOnly() const { return false; }
virtual bool IsLoaded() const { return m_bLoaded; }
virtual bool Read(const char* filename);
virtual bool ReadFromMemory([[maybe_unused]] const void* pData, [[maybe_unused]] int nDataSize) { return false; }
virtual bool Write(const char* filename);
virtual bool WriteToMemoryBuffer(void** pData, int* nSize);
virtual void ReleaseMemoryBuffer();
virtual int AddChunk(ChunkTypes chunkType, int chunkVersion, EEndianness eEndianness, const void* chunkData, int chunkSize);
virtual void DeleteChunkById(int nChunkId);
virtual void DeleteChunksByType(ChunkTypes nChunkType);
virtual ChunkDesc* FindChunkByType(ChunkTypes nChunkType);
virtual ChunkDesc* FindChunkById(int nChunkId);
virtual int NumChunks() const;
virtual ChunkDesc* GetChunk(int nIndex);
virtual const ChunkDesc* GetChunk(int nIndex) const;
virtual const char* GetLastError() const { return m_LastError; }
// -----------------------------------------------------------------------
virtual void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_LastError);
pSizer->AddObject(m_chunks);
pSizer->AddObject(m_chunkIdMap);
}
private:
void ReleaseChunks();
private:
// this variable contains the last error occurred in this class
string m_LastError;
int m_nLastChunkId;
std::vector<ChunkDesc*> m_chunks;
typedef std::map<int, ChunkDesc*> ChunkIdMap;
ChunkIdMap m_chunkIdMap;
char* m_pInternalData;
bool m_bLoaded;
};
TYPEDEF_AUTOPTR(CChunkFile);
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILE_H
@@ -1,265 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILECOMPONENTS_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILECOMPONENTS_H
#pragma once
#include "CryHeaders.h"
namespace ChunkFile
{
// All chunk files use *little-endian* format to store file header and chunk.
// Chunk data are stored in either little-endian or big-endian format,
// see kBigEndianVersionFlag.
struct FileHeader_0x744_0x745
{
char signature[7];
char _pad_[1];
uint32 fileType;
uint32 version;
uint32 chunkTableOffset;
enum EFileType
{
eFileType_Geom = 0xFFFF0000U,
eFileType_Anim,
};
static const char* GetExpectedSignature()
{
return "CryTek";
}
bool HasValidSignature() const
{
return memcmp(GetExpectedSignature(), signature, sizeof(signature)) == 0;
}
void Set(uint32 a_chunkTableOffset)
{
memcpy(signature, GetExpectedSignature(), sizeof(signature));
_pad_[0] = 0;
// We need to set eFileType_Geom or eFileType_Anim, but asking
// the caller to provide us the type will complicate the code, so we
// set eFileType_Geom only. It's ok because all our readers
// don't differentiate between eFileType_Geom and eFileType_Anim.
fileType = eFileType_Geom;
version = 0x745;
chunkTableOffset = a_chunkTableOffset;
}
void SwapEndianness()
{
SwapEndianBase(&fileType, 1);
SwapEndianBase(&version, 1);
SwapEndianBase(&chunkTableOffset, 1);
}
};
struct FileHeader_0x746
{
char signature[4];
uint32 version;
uint32 chunkCount;
uint32 chunkTableOffset;
static const char* GetExpectedSignature()
{
return "CrCh";
}
static const char* GetExpectedSpeedTreeSignature()
{
return "STCh";
}
bool HasValidSignature() const
{
return (memcmp(GetExpectedSignature(), signature, sizeof(signature)) == 0) ||
(memcmp(GetExpectedSpeedTreeSignature(), signature, sizeof(signature)) == 0);
}
void Set(int32 a_chunkCount, uint32 a_chunkTableOffset)
{
memcpy(signature, GetExpectedSignature(), sizeof(signature));
version = 0x746;
chunkCount = a_chunkCount;
chunkTableOffset = a_chunkTableOffset;
}
void SwapEndianness()
{
SwapEndianBase(&version, 1);
SwapEndianBase(&chunkCount, 1);
SwapEndianBase(&chunkTableOffset, 1);
}
};
struct ChunkHeader_0x744_0x745
{
uint32 type;
uint32 version;
uint32 offsetInFile;
uint32 id;
enum
{
kBigEndianVersionFlag = 0x80000000U
};
void SwapEndianness()
{
SwapEndianBase(&type, 1);
SwapEndianBase(&version, 1);
SwapEndianBase(&offsetInFile, 1);
SwapEndianBase(&id, 1);
}
};
struct ChunkTableEntry_0x744
: public ChunkHeader_0x744_0x745
{
void SwapEndianness()
{
ChunkHeader_0x744_0x745::SwapEndianness();
}
};
struct ChunkTableEntry_0x745
: public ChunkHeader_0x744_0x745
{
uint32 size;
void SwapEndianness()
{
ChunkHeader_0x744_0x745::SwapEndianness();
SwapEndianBase(&size, 1);
}
};
struct ChunkTableEntry_0x746
{
uint16 type;
uint16 version;
uint32 id;
uint32 size;
uint32 offsetInFile;
enum
{
kBigEndianVersionFlag = 0x8000U
};
void SwapEndianness()
{
SwapEndianBase(&type, 1);
SwapEndianBase(&version, 1);
SwapEndianBase(&id, 1);
SwapEndianBase(&size, 1);
SwapEndianBase(&offsetInFile, 1);
}
};
// We need this function to strip 0x744 & 0x745 chunk headers
// from chunk data properly: some chunks in 0x744 and 0x745 formats
// don't have chunk headers in their data.
// 'chunkType' is expected to be provided in the 0x746 format.
inline bool ChunkContainsHeader_0x744_0x745(const uint16 chunkType, const uint16 chunkVersion)
{
switch (chunkType)
{
case ChunkType_SourceInfo:
return false;
case ChunkType_Controller:
return (chunkVersion != CONTROLLER_CHUNK_DESC_0827::VERSION && chunkVersion != CONTROLLER_CHUNK_DESC_0830::VERSION);
case ChunkType_BoneNameList:
return (chunkVersion != BONENAMELIST_CHUNK_DESC_0745::VERSION);
case ChunkType_MeshMorphTarget:
return (chunkVersion != MESHMORPHTARGET_CHUNK_DESC_0001::VERSION);
case ChunkType_BoneInitialPos:
return (chunkVersion != BONEINITIALPOS_CHUNK_DESC_0001::VERSION);
default:
return true;
}
}
static inline uint16 ConvertChunkTypeTo0x746(uint32 type)
{
if (type <= 0xFFFF)
{
// Input type seems to be already in 0x746 format (or it's 0)
return type;
}
// Input type seems to be in 0x745 format
if ((type & 0xFFFF) >= 0xF000)
{
// Cannot fit into resulting 0x746 type (uint16)
return 0;
}
if ((type & 0xFFFF0000U) == 0xCCCC0000U)
{
return 0x1000 + (type & 0x0FFF);
}
if ((type & 0xFFFF0000U) == 0xACDC0000U)
{
return 0x2000 + (type & 0x0FFF);
}
if ((type & 0xFFFF0000U) == 0xAAFC0000U)
{
return 0x3000 + (type & 0x0FFF);
}
// Unknown 0x745 chunk type
return 0;
}
static inline uint32 ConvertChunkTypeTo0x745(uint32 type)
{
if (type > 0xFFFF)
{
// Input type seems to be already in 0x745 format
return type;
}
// Input type seems to be in 0x746 format
if ((type & 0xF000) == 0x1000)
{
return 0xCCCC0000U + (type & 0x0FFF);
}
if ((type & 0xF000) == 0x2000)
{
return 0xACDC0000U + (type & 0x0FFF);
}
if ((type & 0xF000) == 0x3000)
{
return 0xAAFC0000U + (type & 0x0FFF);
}
// Unknown 0x746 chunk type (or it's 0)
return 0;
}
} // namespace ChunkFile
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILECOMPONENTS_H
@@ -1,585 +0,0 @@
/*
* 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 <platform.h>
#include <CryFile.h>
#include <CryHeaders.h>
#include "ChunkFileReaders.h"
#include "CryPath.h"
namespace ChunkFile
{
//////////////////////////////////////////////////////////////////////////
CryFileReader::CryFileReader()
{
}
CryFileReader::~CryFileReader()
{
Close();
}
bool CryFileReader::Open(const char* filename)
{
Close();
if (filename == 0 || filename[0] == 0)
{
return false;
}
if (!m_f.Open(filename, "rb"))
{
return false;
}
m_offset = 0;
return true;
}
void CryFileReader::Close()
{
m_f.Close();
}
int32 CryFileReader::GetSize()
{
return m_f.GetLength();
}
bool CryFileReader::SetPos(int32 pos)
{
if (pos < 0)
{
return false;
}
m_offset = pos;
return m_f.Seek(m_offset, SEEK_SET) == 0;
}
bool CryFileReader::Read(void* buffer, size_t size)
{
return m_f.ReadRaw(buffer, size) == size;
}
//////////////////////////////////////////////////////////////////////////
MemoryReader::MemoryReader()
: m_ptr(0)
, m_size(0)
{
}
MemoryReader::~MemoryReader()
{
}
bool MemoryReader::Start(void* ptr, int32 size)
{
if (ptr == 0 || size <= 0)
{
return false;
}
m_ptr = (char*)ptr;
m_size = size;
m_offset = 0;
return true;
}
void MemoryReader::Close()
{
}
int32 MemoryReader::GetSize()
{
return m_size;
}
bool MemoryReader::SetPos(int32 pos)
{
if (pos < 0 || pos > m_size)
{
return false;
}
m_offset = pos;
return true;
}
bool MemoryReader::Read(void* buffer, size_t size)
{
if (!m_ptr)
{
return false;
}
if (size <= 0)
{
return true;
}
if ((size_t)m_offset + size > (size_t)m_size)
{
return false;
}
memcpy(buffer, &m_ptr[m_offset], size);
m_offset += size;
return true;
}
//////////////////////////////////////////////////////////////////////////
namespace
{
class ChunkListRef
{
std::vector<IChunkFile::ChunkDesc>& m_chunks;
public:
ChunkListRef(std::vector<IChunkFile::ChunkDesc>& chunks)
: m_chunks(chunks)
{
}
void Clear()
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
if (m_chunks[i].data)
{
delete [] (char*)m_chunks[i].data;
m_chunks[i].data = 0;
}
}
m_chunks.clear();
}
void Create(size_t count)
{
Clear();
m_chunks.resize(count);
for (size_t i = 0; i < count; ++i)
{
m_chunks[i].data = 0;
m_chunks[i].size = 0;
}
}
void Sort()
{
std::sort(m_chunks.begin(), m_chunks.end(), IChunkFile::ChunkDesc::LessOffset);
}
size_t GetCount() const
{
return m_chunks.size();
}
IChunkFile::ChunkDesc& Get(size_t index)
{
return m_chunks[index];
}
};
class ChunkPtrListRef
{
std::vector<IChunkFile::ChunkDesc*>& m_chunks;
public:
ChunkPtrListRef(std::vector<IChunkFile::ChunkDesc*>& chunks)
: m_chunks(chunks)
{
}
void Clear()
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
if (m_chunks[i])
{
if (m_chunks[i]->data)
{
delete [] (char*)(m_chunks[i]->data);
m_chunks[i]->data = 0;
}
delete m_chunks[i];
}
}
m_chunks.clear();
}
void Create(size_t count)
{
Clear();
m_chunks.resize(count, 0);
for (size_t i = 0; i < count; ++i)
{
m_chunks[i] = new IChunkFile::ChunkDesc;
m_chunks[i]->data = 0;
m_chunks[i]->size = 0;
}
}
void Sort()
{
std::sort(m_chunks.begin(), m_chunks.end(), IChunkFile::ChunkDesc::LessOffsetByPtr);
}
size_t GetCount() const
{
return m_chunks.size();
}
IChunkFile::ChunkDesc& Get(size_t index)
{
return *m_chunks[index];
}
};
} // namespace
//////////////////////////////////////////////////////////////////////////
template <class TListRef>
static const char* GetChunkTableEntries_0x744_0x745_Tpl(IReader* pReader, TListRef& chunks)
{
chunks.Clear();
ChunkFile::FileHeader_0x744_0x745 header;
if (!pReader->SetPos(0) ||
!pReader->Read(&header, sizeof(header)))
{
return "Cannot read header of chunk file";
}
if (!header.HasValidSignature())
{
return "Unknown signature in chunk file";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
header.SwapEndianness();
}
if (header.version != 0x744 && header.version != 0x745)
{
return "Version of chunk file is neither 0x744 nor 0x745";
}
if (header.fileType != header.eFileType_Geom && header.fileType != header.eFileType_Anim)
{
return "Type of chunk file is neither FileType_Geom nor FileType_Anim";
}
uint32 chunkCount = 0;
{
if (!pReader->SetPos(header.chunkTableOffset) ||
!pReader->Read(&chunkCount, sizeof(chunkCount)))
{
return "Failed to read # of chunks";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
SwapEndianBase(&chunkCount, 1);
}
if (chunkCount < 0 || chunkCount > 1000000)
{
return "Invalid # of chunks in file";
}
}
if (chunkCount <= 0)
{
return 0;
}
chunks.Create(chunkCount);
if (header.version == 0x744)
{
std::vector<ChunkFile::ChunkTableEntry_0x744> srcChunks;
srcChunks.resize(chunkCount);
if (!pReader->Read(&srcChunks[0], sizeof(srcChunks[0]) * srcChunks.size()))
{
return "Failed to read chunk entries from file";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
for (uint32 i = 0; i < chunkCount; ++i)
{
srcChunks[i].SwapEndianness();
}
}
for (uint32 i = 0; i < chunkCount; ++i)
{
IChunkFile::ChunkDesc& cd = chunks.Get(i);
cd.chunkType = (ChunkTypes)ConvertChunkTypeTo0x746(srcChunks[i].type);
cd.chunkVersion = srcChunks[i].version & ~ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag;
cd.chunkId = srcChunks[i].id;
cd.fileOffset = srcChunks[i].offsetInFile;
cd.bSwapEndian = (srcChunks[i].version & ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag) ? SYSTEM_IS_LITTLE_ENDIAN : SYSTEM_IS_BIG_ENDIAN;
}
chunks.Sort();
const uint32 endOfChunkData = (header.chunkTableOffset < chunks.Get(0).fileOffset)
? (uint32)pReader->GetSize()
: header.chunkTableOffset;
for (uint32 i = 0; i < chunkCount; ++i)
{
// calculate chunk size based on the next (by offset in file) chunk or
// on the end of the chunk data portion of the file
const size_t nextOffsetInFile = (i + 1 < chunkCount)
? chunks.Get(i + 1).fileOffset
: endOfChunkData;
chunks.Get(i).size = nextOffsetInFile - chunks.Get(i).fileOffset;
}
}
else // header.version == 0x745
{
std::vector<ChunkFile::ChunkTableEntry_0x745> srcChunks;
srcChunks.resize(chunkCount);
if (!pReader->Read(&srcChunks[0], sizeof(srcChunks[0]) * srcChunks.size()))
{
return "Failed to read chunk entries from file.";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
for (uint32 i = 0; i < chunkCount; ++i)
{
srcChunks[i].SwapEndianness();
}
}
for (uint32 i = 0; i < chunkCount; ++i)
{
IChunkFile::ChunkDesc& cd = chunks.Get(i);
cd.chunkType = (ChunkTypes)ConvertChunkTypeTo0x746(srcChunks[i].type);
cd.chunkVersion = srcChunks[i].version & ~ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag;
cd.chunkId = srcChunks[i].id;
cd.fileOffset = srcChunks[i].offsetInFile;
cd.size = srcChunks[i].size;
cd.bSwapEndian = (srcChunks[i].version & ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag) ? SYSTEM_IS_LITTLE_ENDIAN : SYSTEM_IS_BIG_ENDIAN;
}
}
const uint32 fileSize = (uint32)pReader->GetSize();
for (uint32 i = 0; i < chunkCount; ++i)
{
const IChunkFile::ChunkDesc& cd = chunks.Get(i);
if (cd.size + cd.fileOffset > fileSize)
{
return "Data in chunk file are corrupted";
}
}
return 0;
}
template <class TListRef>
static const char* GetChunkTableEntries_0x746_Tpl(IReader* pReader, TListRef& chunks)
{
chunks.Clear();
ChunkFile::FileHeader_0x746 header;
if (!pReader->SetPos(0) ||
!pReader->Read(&header, sizeof(header)))
{
return "Cannot read header from file.";
}
if (!header.HasValidSignature())
{
return "Unknown signature in chunk file";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
header.SwapEndianness();
}
if (header.version != 0x746)
{
return "Version of chunk file is not 0x746";
}
if (header.chunkCount < 0 || header.chunkCount > 10000000)
{
return "Invalid # of chunks in file.";
}
if (header.chunkCount <= 0)
{
return 0;
}
chunks.Create(header.chunkCount);
std::vector<ChunkFile::ChunkTableEntry_0x746> srcChunks;
srcChunks.resize(header.chunkCount);
if (!pReader->SetPos(header.chunkTableOffset) ||
!pReader->Read(&srcChunks[0], sizeof(srcChunks[0]) * srcChunks.size()))
{
return "Failed to read chunk entries from file";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
for (uint32 i = 0; i < header.chunkCount; ++i)
{
srcChunks[i].SwapEndianness();
}
}
for (size_t i = 0, n = chunks.GetCount(); i < n; ++i)
{
IChunkFile::ChunkDesc& cd = chunks.Get(i);
cd.chunkType = (ChunkTypes)srcChunks[i].type;
cd.chunkVersion = srcChunks[i].version & ~ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag;
cd.chunkId = srcChunks[i].id;
cd.size = srcChunks[i].size;
cd.fileOffset = srcChunks[i].offsetInFile;
cd.bSwapEndian = (srcChunks[i].version & ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag) ? SYSTEM_IS_LITTLE_ENDIAN : SYSTEM_IS_BIG_ENDIAN;
}
return 0;
}
template <class TListRef>
static const char* StripChunkHeaders_0x744_0x745_Tpl(IReader* pReader, TListRef& chunks)
{
for (size_t i = 0, n = chunks.GetCount(); i < n; ++i)
{
IChunkFile::ChunkDesc& ct = chunks.Get(i);
if (ChunkFile::ChunkContainsHeader_0x744_0x745(ct.chunkType, ct.chunkVersion))
{
ChunkFile::ChunkHeader_0x744_0x745 ch;
if (ct.size < sizeof(ch))
{
return "Damaged data: reported size of chunk data is less that size of the chunk header";
}
// Validation
{
if (!pReader->SetPos(ct.fileOffset) ||
!pReader->Read(&ch, sizeof(ch)))
{
return "Failed to read chunk header from file";
}
if (SYSTEM_IS_BIG_ENDIAN)
{
ch.SwapEndianness();
}
ch.version &= ~ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag;
if (ConvertChunkTypeTo0x746(ch.type) != ct.chunkType ||
ch.version != ct.chunkVersion ||
ch.id != ct.chunkId)
{
return "Data in a chunk header don't match data in the chunk table";
}
// The following check is commented out because we have (on 2013/11/25)
// big number of .cgf files in Crysis 3 that fail to pass the check.
//if (ch.offsetInFile != ct.fileOffset)
//{
// return "File offset data in a chunk header don't match data in the chunk table";
//}
}
ct.fileOffset += sizeof(ch);
ct.size -= sizeof(ch);
if (ct.data)
{
ct.data = ((char*)ct.data) + sizeof(ch);
}
}
if (ct.size < 0)
{
return "A negative-length chunk found in file";
}
}
return 0;
}
const char* GetChunkTableEntries_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks)
{
ChunkListRef c(chunks);
return GetChunkTableEntries_0x744_0x745_Tpl(pReader, c);
}
const char* GetChunkTableEntries_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks)
{
ChunkPtrListRef c(chunks);
return GetChunkTableEntries_0x744_0x745_Tpl(pReader, c);
}
const char* GetChunkTableEntries_0x746(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks)
{
ChunkListRef c(chunks);
return GetChunkTableEntries_0x746_Tpl(pReader, c);
}
const char* GetChunkTableEntries_0x746(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks)
{
ChunkPtrListRef c(chunks);
return GetChunkTableEntries_0x746_Tpl(pReader, c);
}
const char* StripChunkHeaders_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks)
{
ChunkListRef c(chunks);
return StripChunkHeaders_0x744_0x745_Tpl(pReader, c);
}
const char* StripChunkHeaders_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks)
{
ChunkPtrListRef c(chunks);
return StripChunkHeaders_0x744_0x745_Tpl(pReader, c);
}
} // namespace ChunkFile
@@ -1,95 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEREADERS_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEREADERS_H
#pragma once
#include <CryFile.h> // for CCryFile
#include "ChunkFileComponents.h"
#include <CryFile.h>
#include "IChunkFile.h"
namespace ChunkFile
{
struct IReader
{
virtual ~IReader()
{
}
virtual void Close() = 0;
virtual int32 GetSize() = 0;
virtual bool SetPos(int32 pos) = 0;
virtual bool Read(void* buffer, size_t size) = 0;
};
class CryFileReader
: public IReader
{
public:
CryFileReader();
virtual ~CryFileReader();
bool Open(const char* filename);
//-------------------------------------------------------
// IReader interface
virtual void Close();
virtual int32 GetSize();
virtual bool SetPos(int32 pos);
virtual bool Read(void* buffer, size_t size);
//-------------------------------------------------------
private:
CCryFile m_f;
int32 m_offset;
};
class MemoryReader
: public IReader
{
public:
MemoryReader();
virtual ~MemoryReader();
bool Start(void* ptr, int32 size);
//-------------------------------------------------------
// IReader interface
virtual void Close();
virtual int32 GetSize();
virtual bool SetPos(int32 pos);
virtual bool Read(void* buffer, size_t size);
//-------------------------------------------------------
private:
char* m_ptr;
int32 m_size;
int32 m_offset;
};
const char* GetChunkTableEntries_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks);
const char* GetChunkTableEntries_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks);
const char* GetChunkTableEntries_0x746(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks);
const char* GetChunkTableEntries_0x746(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks);
const char* StripChunkHeaders_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc>& chunks);
const char* StripChunkHeaders_0x744_0x745(IReader* pReader, std::vector<IChunkFile::ChunkDesc*>& chunks);
} // namespace ChunkFile
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEREADERS_H
@@ -1,643 +0,0 @@
/*
* 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 <platform.h>
#include "ChunkFileWriters.h"
#include <AzFramework/Archive/IArchive.h>
static inline size_t ComputeSizeOfAlignment(size_t pos, size_t alignment)
{
if (alignment <= 1 || (alignment & (alignment - 1)))
{
return 0;
}
const size_t mask = alignment - 1;
return (alignment - (pos & mask)) & mask;
}
namespace ChunkFile
{
//////////////////////////////////////////////////////////////////////////
bool IWriter::WriteZeros(size_t size)
{
if (size <= 0)
{
return true;
}
char bf[1024];
memset(bf, 0, (sizeof(bf) < size ? sizeof(bf) : size));
while (size > 0)
{
const uint32 sz = (sizeof(bf) < size ? sizeof(bf) : size);
size -= sz;
if (!Write(bf, sz))
{
return false;
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
OsFileWriter::OsFileWriter()
: m_f(0)
{
}
OsFileWriter::~OsFileWriter()
{
Erase();
}
bool OsFileWriter::Create(const char* filename)
{
Erase();
if (filename == 0 || filename[0] == 0)
{
// RCLogError("Filename is empty");
return false;
}
m_filename = filename;
m_f = nullptr;
azfopen(&m_f, filename, "wb");
if (!m_f)
{
// RCLogError("Failed to create file %s.", m_filename.c_str());
return false;
}
m_offset = 0;
return true;
}
void OsFileWriter::Erase()
{
if (m_f)
{
Close();
::remove(m_filename.c_str());
}
}
void OsFileWriter::Close()
{
if (m_f)
{
fclose(m_f);
m_f = 0;
}
}
int32 OsFileWriter::GetPos() const
{
return m_offset;
}
bool OsFileWriter::Write(const void* buffer, size_t size)
{
if (!m_f)
{
return false;
}
if (size <= 0)
{
return true;
}
if (fwrite(buffer, size, 1, m_f) != 1)
{
// RCLogError("Failed to write %u byte(s) to file %s.", (uint)size, m_filename.c_str());
Erase();
return false;
}
m_offset += size;
return true;
}
//////////////////////////////////////////////////////////////////////////
#if !defined(RESOURCE_COMPILER)
CryPakFileWriter::CryPakFileWriter()
: m_pPak(0)
, m_fileHandle(AZ::IO::InvalidHandle)
{
}
CryPakFileWriter::~CryPakFileWriter()
{
Erase();
}
bool CryPakFileWriter::Create(AZ::IO::IArchive* pPak, const char* filename)
{
Erase();
if (pPak == 0 || filename == 0 || filename[0] == 0)
{
return false;
}
m_pPak = pPak;
m_filename = filename;
m_fileHandle = m_pPak->FOpen(m_filename.c_str(), "w+b");
if (m_fileHandle == AZ::IO::InvalidHandle)
{
return false;
}
m_offset = 0;
return true;
}
void CryPakFileWriter::Erase()
{
if (m_fileHandle != AZ::IO::InvalidHandle)
{
Close();
m_pPak->RemoveFile(m_filename.c_str());
}
}
void CryPakFileWriter::Close()
{
if (m_fileHandle != AZ::IO::InvalidHandle)
{
m_pPak->FClose(m_fileHandle);
m_fileHandle = AZ::IO::InvalidHandle;
}
}
int32 CryPakFileWriter::GetPos() const
{
return m_offset;
}
bool CryPakFileWriter::Write(const void* buffer, size_t size)
{
if (m_fileHandle == AZ::IO::InvalidHandle)
{
return false;
}
if (size <= 0)
{
return true;
}
if (m_pPak->FWrite(buffer, 1, size, m_fileHandle) != size)
{
Erase();
return false;
}
m_offset += size;
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////
MemoryWriter::MemoryWriter()
: m_ptr(0)
, m_size(0)
{
}
MemoryWriter::~MemoryWriter()
{
}
bool MemoryWriter::Start(void* ptr, int32 size)
{
Erase();
if (ptr == 0 || size <= 0)
{
return false;
}
m_ptr = (char*)ptr;
m_size = size;
m_offset = 0;
return true;
}
void MemoryWriter::Erase()
{
m_ptr = 0;
m_size = 0;
}
void MemoryWriter::Close()
{
m_ptr = 0;
m_size = 0;
}
int32 MemoryWriter::GetPos() const
{
return m_offset;
}
bool MemoryWriter::Write(const void* buffer, size_t size)
{
if (!m_ptr)
{
return false;
}
if (size <= 0)
{
return true;
}
if ((size_t)m_offset + size > (size_t)m_size)
{
Erase();
return false;
}
memcpy(&m_ptr[m_offset], buffer, size);
m_offset += size;
return true;
}
//////////////////////////////////////////////////////////////////////////
MemorylessChunkFileWriter::MemorylessChunkFileWriter(
EChunkFileFormat eFormat,
IWriter* pWriter)
: m_eChunkFileFormat(eFormat)
, m_pWriter(pWriter)
, m_alignment(4)
, m_chunkCount(0)
, m_eState(eState_Init)
{
if (!m_pWriter)
{
m_eState = eState_Fail;
}
}
MemorylessChunkFileWriter::~MemorylessChunkFileWriter()
{
if (m_eState != eState_Success)
{
Fail();
}
}
void MemorylessChunkFileWriter::SetAlignment(size_t alignment)
{
m_alignment = (alignment < 1) ? 1 : alignment;
}
bool MemorylessChunkFileWriter::StartPass()
{
switch (m_eState)
{
case eState_Init:
m_eState = eState_CountingChunks;
m_chunkIndex = -1;
break;
case eState_CountingChunks:
WriteFileHeader(m_chunkIndex + 1, GetSizeOfHeader());
m_eState = eState_WritingChunkTable;
WriteChunkTableHeader(m_chunkIndex + 1);
m_dataOffsetInFile = GetSizeOfHeader() + GetSizeOfChunkTable(m_chunkIndex + 1);
m_chunkIndex = -1;
break;
case eState_WritingChunkTable:
if (m_chunkIndex >= 0)
{
WriteChunkEntry();
}
m_eState = eState_WritingData;
m_dataOffsetInFile = GetSizeOfHeader() + GetSizeOfChunkTable(m_chunkIndex + 1);
m_chunkIndex = -1;
break;
case eState_WritingData:
m_eState = eState_Success;
m_pWriter->Close();
return false;
case eState_Fail:
return false;
default:
assert(0);
Fail();
return false;
}
return true;
}
void MemorylessChunkFileWriter::StartChunk(EEndianness eEndianness, uint32 type, uint32 version, uint32 id)
{
if (type != 0)
{
type = ConvertChunkTypeTo0x746(type);
if (type == 0)
{
Fail();
return;
}
}
if (version >= ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag)
{
Fail();
return;
}
switch (m_eState)
{
case eState_CountingChunks:
++m_chunkIndex;
break;
case eState_WritingChunkTable:
if (m_chunkIndex >= 0)
{
WriteChunkEntry();
}
/* fall through */
case eState_WritingData:
{
size_t size = ComputeSizeOfAlignment(m_dataOffsetInFile, m_alignment);
// Make sure that zero-length chunks have distinct positions in file
if (size == 0 && m_chunkIndex > 0 && m_chunkSize == 0)
{
size = m_alignment;
}
m_dataOffsetInFile += size;
m_chunkOffsetInFile = m_dataOffsetInFile;
if (m_eState == eState_WritingData && !m_pWriter->WriteZeros(size))
{
Fail();
return;
}
}
++m_chunkIndex;
m_chunkEndianness = eEndianness;
m_chunkType = type;
m_chunkVersion = version;
m_chunkId = id;
m_chunkSize = 0;
if (m_eChunkFileFormat == eChunkFileFormat_0x745 &&
ChunkContainsHeader_0x744_0x745(m_chunkType, m_chunkVersion))
{
ChunkHeader_0x744_0x745 c;
c.type = ConvertChunkTypeTo0x745(m_chunkType);
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag : 0);
c.id = m_chunkId;
c.offsetInFile = m_chunkOffsetInFile;
if (SYSTEM_IS_BIG_ENDIAN)
{
c.SwapEndianness();
}
AddChunkData(&c, sizeof(c));
}
break;
default:
Fail();
break;
}
}
void MemorylessChunkFileWriter::AddChunkData(void* ptr, size_t size)
{
if (m_chunkIndex < 0)
{
Fail();
return;
}
switch (m_eState)
{
case eState_CountingChunks:
break;
case eState_WritingChunkTable:
case eState_WritingData:
m_chunkSize += size;
m_dataOffsetInFile += size;
if (m_eState == eState_WritingData && !m_pWriter->Write(ptr, size))
{
Fail();
}
break;
default:
Fail();
break;
}
}
void MemorylessChunkFileWriter::AddChunkDataZeros(size_t size)
{
if (m_chunkIndex < 0)
{
Fail();
return;
}
switch (m_eState)
{
case eState_CountingChunks:
break;
case eState_WritingChunkTable:
case eState_WritingData:
m_chunkSize += size;
m_dataOffsetInFile += size;
if (m_eState == eState_WritingData && !m_pWriter->WriteZeros(size))
{
Fail();
}
break;
default:
Fail();
break;
}
}
void MemorylessChunkFileWriter::AddChunkDataAlignment(size_t alignment)
{
const size_t size = ComputeSizeOfAlignment(m_chunkSize, alignment);
return AddChunkDataZeros(size);
}
bool MemorylessChunkFileWriter::HasWrittenSuccessfully() const
{
return m_eState == eState_Success;
}
IWriter* MemorylessChunkFileWriter::GetWriter() const
{
return m_pWriter;
}
//////////////////////////////////////////////////////////////////////////
void MemorylessChunkFileWriter::Fail()
{
m_eState = eState_Fail;
if (m_pWriter)
{
m_pWriter->Erase();
}
}
size_t MemorylessChunkFileWriter::GetSizeOfHeader() const
{
return (m_eChunkFileFormat == eChunkFileFormat_0x745)
? sizeof(FileHeader_0x744_0x745)
: sizeof(FileHeader_0x746);
}
void MemorylessChunkFileWriter::WriteFileHeader(int32 chunkCount, uint32 chunkTableOffsetInFile)
{
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
{
FileHeader_0x744_0x745 h;
h.Set(chunkTableOffsetInFile);
if (SYSTEM_IS_BIG_ENDIAN)
{
h.SwapEndianness();
}
if (!m_pWriter->Write(&h, sizeof(h)))
{
Fail();
}
}
else
{
FileHeader_0x746 h;
h.Set(chunkCount, chunkTableOffsetInFile);
if (SYSTEM_IS_BIG_ENDIAN)
{
h.SwapEndianness();
}
if (!m_pWriter->Write(&h, sizeof(h)))
{
Fail();
}
}
}
size_t MemorylessChunkFileWriter::GetSizeOfChunkTable(int32 chunkCount) const
{
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
{
return sizeof(uint32) + chunkCount * sizeof(ChunkTableEntry_0x745);
}
else
{
return chunkCount * sizeof(ChunkTableEntry_0x746);
}
}
void MemorylessChunkFileWriter::WriteChunkTableHeader(int32 chunkCount)
{
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
{
if (SYSTEM_IS_BIG_ENDIAN)
{
SwapEndianBase(&chunkCount, 1);
}
if (!m_pWriter->Write(&chunkCount, sizeof(chunkCount)))
{
Fail();
}
}
}
void MemorylessChunkFileWriter::WriteChunkEntry()
{
if (m_chunkIndex < 0)
{
assert(0);
Fail();
return;
}
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
{
ChunkTableEntry_0x745 c;
c.type = ConvertChunkTypeTo0x745(m_chunkType);
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag : 0);
c.id = m_chunkId;
c.size = m_chunkSize;
c.offsetInFile = m_chunkOffsetInFile;
if (SYSTEM_IS_BIG_ENDIAN)
{
c.SwapEndianness();
}
if (!m_pWriter->Write(&c, sizeof(c)))
{
Fail();
}
}
else
{
ChunkTableEntry_0x746 c;
c.type = m_chunkType;
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag : 0);
c.id = m_chunkId;
c.size = m_chunkSize;
c.offsetInFile = m_chunkOffsetInFile;
if (SYSTEM_IS_BIG_ENDIAN)
{
c.SwapEndianness();
}
if (!m_pWriter->Write(&c, sizeof(c)))
{
Fail();
}
}
}
//////////////////////////////////////////////////////////////////////////
} // namespace ChunkFile
@@ -1,341 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEWRITERS_H
#define CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEWRITERS_H
#pragma once
#include "ChunkFileComponents.h"
// RESOURCE_COMPIELR is defined by max plugin, too, and the max plugin MAY NOT include any of azcore
// or anything else which uses modern C++
#if !defined(RESOURCE_COMPILER)
#include <AzCore/IO/FileIO.h>
#endif
namespace AZ::IO
{
struct IArchive;
}
namespace ChunkFile
{
struct IWriter
{
virtual ~IWriter()
{
}
virtual void Erase() = 0;
virtual void Close() = 0; // if Close() is not called then the file should be deleted in destructor
virtual int32 GetPos() const = 0;
virtual bool Write(const void* buffer, size_t size) = 0;
// non-virtual helper function
bool WriteZeros(size_t size);
};
class OsFileWriter
: public IWriter
{
public:
OsFileWriter();
virtual ~OsFileWriter();
bool Create(const char* filename);
//-------------------------------------------------------
// IWriter interface
virtual void Erase();
virtual void Close();
virtual int32 GetPos() const;
virtual bool Write(const void* buffer, size_t size);
//-------------------------------------------------------
private:
string m_filename;
FILE* m_f;
int32 m_offset;
};
#if !defined(RESOURCE_COMPILER)
class CryPakFileWriter
: public IWriter
{
public:
CryPakFileWriter();
virtual ~CryPakFileWriter();
bool Create(AZ::IO::IArchive* pPak, const char* filename);
//-------------------------------------------------------
// IWriter interface
virtual void Erase();
virtual void Close();
virtual int32 GetPos() const;
virtual bool Write(const void* buffer, size_t size);
//-------------------------------------------------------
private:
string m_filename;
AZ::IO::IArchive* m_pPak;
AZ::IO::HandleType m_fileHandle;
int32 m_offset;
};
#endif
// Doesn't write any data, just computes the size
class SizeWriter
: public IWriter
{
public:
SizeWriter()
{
}
virtual ~SizeWriter()
{
}
void Start()
{
m_offset = 0;
}
//-------------------------------------------------------
// IWriter interface
virtual void Erase()
{
}
virtual void Close()
{
}
virtual int32 GetPos() const
{
return m_offset;
}
virtual bool Write([[maybe_unused]] const void* buffer, size_t size)
{
m_offset += size;
return true;
}
//-------------------------------------------------------
private:
int32 m_offset;
};
class MemoryWriter
: public IWriter
{
public:
MemoryWriter();
virtual ~MemoryWriter();
bool Start(void* ptr, int32 size);
//-------------------------------------------------------
// IWriter interface
virtual void Erase();
virtual void Close();
virtual int32 GetPos() const;
virtual bool Write(const void* buffer, size_t size);
//-------------------------------------------------------
private:
char* m_ptr;
int32 m_size;
int32 m_offset;
};
// Memoryless chunk file writer
//
// Usage example:
//
// OsFileWriter writer;
// if (!writer.Create(filename))
// {
// showAnErrorMessage();
// }
// else
// {
// MemorylessChunkFileWriter wr(eChunFileFormat, writer);
// while (wr.StartPass())
// {
// // default alignment of chunk data in file is 4, but you may change it by calling wr.SetAlignment(xxx)
//
// wr.StartChunk(eEndianness_Native, chunkA_type, chunkA_version, chunkA_id);
// wr.AddChunkData(data_ptr0, data_size0); // make sure that data have endianness specified by bLittleEdndian
// wr.AddChunkData(data_ptr1, data_size1);
// ...
// wr.StartChunk(eEndianness_Native, chunkB_type, chunkB_version, chunkB_id);
// wr.AddChunkData(data_ptrN, data_sizeN);
// ...
// }
// if (!wr.HasWrittenSuccessfully())
// {
// showAnErrorMessage();
// }
// }
//
// Usage example (interface way): see
//
// OsFileWriter writer;
// if (!writer.Create(filename))
// {
// showAnErrorMessage();
// }
// else
// {
// MemorylessChunkFileWriter wr(eChunFileFormat, writer);
// while (wr.StartPass())
// {
// // default alignment of chunk data in file is 4, but you may change it by calling wr.SetAlignment(xxx)
//
// wr.StartChunk(eEndianness_Native, chunkA_type, chunkA_version, chunkA_id);
// wr.AddChunkData(data_ptr0, data_size0); // make sure that data have endianness specified by bLittleEdndian
// wr.AddChunkData(data_ptr1, data_size1);
// ...
// wr.StartChunk(eEndianness_Native, chunkB_type, chunkB_version, chunkB_id);
// wr.AddChunkData(data_ptrN, data_sizeN);
// ...
// }
// if (!wr.HasWrittenSuccessfully())
// {
// showAnErrorMessage();
// }
// }
//
struct IChunkFileWriter
{
virtual ~IChunkFileWriter()
{
}
// Sets alignment for *beginning* of chunk data.
// Allowed to be called at any time, influences all future
// StartChunk() calls (until a new SetAlignment() call).
virtual void SetAlignment(size_t alignment) = 0;
// Returns false when there is no more passes left.
virtual bool StartPass() = 0;
// eEndianness specifies endianness of the data user is
// going to provide via AddChunkData*(). The data will be
// sent to the low-level writer as is, without any re-coding.
virtual void StartChunk(EEndianness eEndianness, uint32 type, uint32 version, uint32 id) = 0;
virtual void AddChunkData(void* ptr, size_t size) = 0;
virtual void AddChunkDataZeros(size_t size) = 0;
virtual void AddChunkDataAlignment(size_t alignment) = 0;
virtual bool HasWrittenSuccessfully() const = 0;
virtual IWriter* GetWriter() const = 0;
};
class MemorylessChunkFileWriter
: public IChunkFileWriter
{
public:
enum EChunkFileFormat
{
eChunkFileFormat_0x745,
eChunkFileFormat_0x746,
};
MemorylessChunkFileWriter(EChunkFileFormat eFormat, IWriter* pWriter);
//-------------------------------------------------------
// IChunkFileWriter interface
virtual ~MemorylessChunkFileWriter();
virtual void SetAlignment(size_t alignment);
virtual bool StartPass();
virtual void StartChunk(EEndianness eEndianness, uint32 type, uint32 version, uint32 id);
virtual void AddChunkData(void* ptr, size_t size);
virtual void AddChunkDataZeros(size_t size);
virtual void AddChunkDataAlignment(size_t alignment);
virtual bool HasWrittenSuccessfully() const;
virtual IWriter* GetWriter() const;
//-------------------------------------------------------
private:
void Fail();
static size_t ComputeSizeOfAlignmentArea(size_t pos, size_t alignment);
size_t GetSizeOfHeader() const;
void WriteFileHeader(int32 chunkCount, uint32 chunkTableOffsetInFile);
size_t GetSizeOfChunkTable(int32 chunkCount) const;
void WriteChunkTableHeader(int32 chunkCount);
void WriteChunkEntry();
private:
IWriter* const m_pWriter;
EChunkFileFormat m_eChunkFileFormat;
size_t m_alignment;
int32 m_chunkCount;
int32 m_chunkIndex;
uint16 m_chunkType;
uint16 m_chunkVersion;
uint32 m_chunkId;
uint32 m_chunkSize;
uint32 m_chunkOffsetInFile;
EEndianness m_chunkEndianness;
uint32 m_dataOffsetInFile;
enum EState
{
eState_Init,
eState_CountingChunks,
eState_WritingChunkTable,
eState_WritingData,
eState_Success,
eState_Fail,
};
EState m_eState;
};
} // namespace ChunkFile
#endif // CRYINCLUDE_CRY3DENGINE_CGF_CHUNKFILEWRITERS_H
@@ -1,230 +0,0 @@
/*
* 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 <platform.h>
#include "ReadOnlyChunkFile.h"
#include "ChunkFileComponents.h"
#include "ChunkFileReaders.h"
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/FileIO.h>
#define MAX_CHUNKS_NUM 10000000
#if !defined(FUNCTION_PROFILER_3DENGINE)
# define FUNCTION_PROFILER_3DENGINE
#endif
#if !defined(LOADING_TIME_PROFILE_SECTION)
# define LOADING_TIME_PROFILE_SECTION
#endif
//////////////////////////////////////////////////////////////////////////
CReadOnlyChunkFile::CReadOnlyChunkFile(bool bCopyFileData, bool bNoWarningMode)
{
m_pFileBuffer = 0;
m_nBufferSize = 0;
m_bNoWarningMode = bNoWarningMode;
m_bOwnFileBuffer = false;
m_bLoaded = false;
m_bCopyFileData = bCopyFileData;
}
CReadOnlyChunkFile::~CReadOnlyChunkFile()
{
FreeBuffer();
}
//////////////////////////////////////////////////////////////////////////
void CReadOnlyChunkFile::FreeBuffer()
{
if (m_pFileBuffer && m_bOwnFileBuffer)
{
delete [] m_pFileBuffer;
}
m_pFileBuffer = 0;
m_nBufferSize = 0;
m_bOwnFileBuffer = false;
m_bLoaded = false;
}
//////////////////////////////////////////////////////////////////////////
CReadOnlyChunkFile::ChunkDesc* CReadOnlyChunkFile::GetChunk(int nIndex)
{
assert(size_t(nIndex) < m_chunks.size());
return &m_chunks[nIndex];
}
//////////////////////////////////////////////////////////////////////////
const CReadOnlyChunkFile::ChunkDesc* CReadOnlyChunkFile::GetChunk(int nIndex) const
{
assert(size_t(nIndex) < m_chunks.size());
return &m_chunks[nIndex];
}
// number of chunks
int CReadOnlyChunkFile::NumChunks() const
{
return (int)m_chunks.size();
}
//////////////////////////////////////////////////////////////////////////
CReadOnlyChunkFile::ChunkDesc* CReadOnlyChunkFile::FindChunkByType(ChunkTypes nChunkType)
{
for (size_t i = 0, count = m_chunks.size(); i < count; ++i)
{
if (m_chunks[i].chunkType == nChunkType)
{
return &m_chunks[i];
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
CReadOnlyChunkFile::ChunkDesc* CReadOnlyChunkFile::FindChunkById(int id)
{
ChunkDesc chunkToFind;
chunkToFind.chunkId = id;
std::vector<ChunkDesc>::iterator it = std::lower_bound(m_chunks.begin(), m_chunks.end(), chunkToFind, IChunkFile::ChunkDesc::LessId);
if (it != m_chunks.end() && id == (*it).chunkId)
{
return &(*it);
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
bool CReadOnlyChunkFile::ReadChunkTableFromBuffer()
{
LOADING_TIME_PROFILE_SECTION;
if (m_pFileBuffer == 0)
{
m_LastError.Format("Unexpected empty buffer");
return false;
}
{
ChunkFile::MemoryReader f;
if (!f.Start(m_pFileBuffer, m_nBufferSize))
{
m_LastError.Format("Empty memory chunk file");
return false;
}
bool bStripHeaders = false;
const char* err = 0;
err = ChunkFile::GetChunkTableEntries_0x746(&f, m_chunks);
if (err)
{
err = ChunkFile::GetChunkTableEntries_0x744_0x745(&f, m_chunks);
bStripHeaders = true;
}
if (!err)
{
for (size_t i = 0; i < m_chunks.size(); ++i)
{
ChunkDesc& cd = m_chunks[i];
cd.data = m_pFileBuffer + cd.fileOffset;
}
if (bStripHeaders)
{
err = ChunkFile::StripChunkHeaders_0x744_0x745(&f, m_chunks);
}
}
if (err)
{
m_LastError = err;
return false;
}
}
// Sort chunks by Id, for faster queries later (see FindChunkById()).
std::sort(m_chunks.begin(), m_chunks.end(), IChunkFile::ChunkDesc::LessId);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CReadOnlyChunkFile::Read(const char* filename)
{
LOADING_TIME_PROFILE_SECTION;
FreeBuffer();
if (!AZ::IO::FileIOBase::GetInstance())
{
m_LastError = "File system not ready yet.";
return false;
}
if (!AZ::IO::FileIOBase::GetInstance()->Exists(filename))
{
m_LastError.Format("File '%s' not found", filename);
return false;
}
AZ::u64 fileSize;
if (!AZ::IO::FileIOBase::GetInstance()->Size(filename, fileSize))
{
m_LastError.Format("Failed to retrieve file size for '%s'", filename);
return false;
}
m_pFileBuffer = new char[fileSize];
m_bOwnFileBuffer = true;
AZ::IO::FileIOStream stream(filename, AZ::IO::OpenMode::ModeRead);
if (stream.Read(fileSize, m_pFileBuffer) != fileSize)
{
m_LastError.Format("Failed to read %u bytes from file '%s'", fileSize, filename);
return false;
}
m_nBufferSize = aznumeric_caster(fileSize);
if (!ReadChunkTableFromBuffer())
{
return false;
}
m_bLoaded = true;
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CReadOnlyChunkFile::ReadFromMemory(const void* pData, int nDataSize)
{
LOADING_TIME_PROFILE_SECTION;
FreeBuffer();
m_pFileBuffer = (char*)pData;
m_bOwnFileBuffer = false;
m_nBufferSize = nDataSize;
if (!ReadChunkTableFromBuffer())
{
return false;
}
m_bLoaded = true;
return true;
}
@@ -1,96 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CGF_READONLYCHUNKFILE_H
#define CRYINCLUDE_CRY3DENGINE_CGF_READONLYCHUNKFILE_H
#pragma once
#include <CrySizer.h>
#include <CryHeaders.h>
#include <smartptr.h>
#include <IChunkFile.h>
////////////////////////////////////////////////////////////////////////
// Chunk file reader.
// Accesses a chunked file structure through file mapping object.
// Opens a chunk file and checks for its validity.
// If it's invalid, closes it as if there was no open operation.
// Error handling is performed through the return value of Read: it must
// be true for successfully open files
////////////////////////////////////////////////////////////////////////
class CReadOnlyChunkFile
: public IChunkFile
{
public:
//////////////////////////////////////////////////////////////////////////
CReadOnlyChunkFile(bool bCopyFileData, bool bNoWarningMode = false);
virtual ~CReadOnlyChunkFile();
// interface IChunkFile --------------------------------------------------
virtual void Release() { delete this; }
virtual bool IsReadOnly() const { return true; }
virtual bool IsLoaded() const { return m_bLoaded; }
virtual bool Read(const char* filename);
virtual bool ReadFromMemory(const void* pData, int nDataSize);
virtual bool Write([[maybe_unused]] const char* filename) { return false; }
virtual bool WriteToMemoryBuffer([[maybe_unused]] void** pData, [[maybe_unused]] int* nSize) { return false; }
virtual void ReleaseMemoryBuffer() {}
virtual int AddChunk([[maybe_unused]] ChunkTypes chunkType, [[maybe_unused]] int chunkVersion, [[maybe_unused]] EEndianness eEndianness, [[maybe_unused]] const void* chunkData, [[maybe_unused]] int chunkSize) { return -1; }
virtual void DeleteChunkById([[maybe_unused]] int nChunkId) {}
virtual void DeleteChunksByType([[maybe_unused]] ChunkTypes nChunkType) {}
virtual ChunkDesc* FindChunkByType(ChunkTypes nChunkType);
virtual ChunkDesc* FindChunkById(int nChunkId);
virtual int NumChunks() const;
virtual ChunkDesc* GetChunk(int nIndex);
virtual const ChunkDesc* GetChunk(int nIndex) const;
virtual const char* GetLastError() const { return m_LastError; }
// -----------------------------------------------------------------------
virtual void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_LastError);
pSizer->AddObject(m_chunks);
}
private:
bool ReadChunkTableFromBuffer();
void FreeBuffer();
private:
// this variable contains the last error occurred in this class
string m_LastError;
std::vector<ChunkDesc> m_chunks;
char* m_pFileBuffer;
int m_nBufferSize;
bool m_bOwnFileBuffer;
bool m_bNoWarningMode;
bool m_bLoaded;
bool m_bCopyFileData;
};
TYPEDEF_AUTOPTR(CReadOnlyChunkFile);
#endif // CRYINCLUDE_CRY3DENGINE_CGF_READONLYCHUNKFILE_H
@@ -1,75 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_TOOLS_RC_RESOURCECOMPILER_SWAPENDIANNESS_H
#define CRYINCLUDE_TOOLS_RC_RESOURCECOMPILER_SWAPENDIANNESS_H
#pragma once
inline void SwapEndians_(void* pData, size_t nCount, size_t nSizeCheck)
{
// Primitive type.
switch (nSizeCheck)
{
case 1:
break;
case 2:
{
while (nCount--)
{
uint16& i = *((uint16*&)pData)++;
i = ((i >> 8) + (i << 8)) & 0xFFFF;
}
break;
}
case 4:
{
while (nCount--)
{
uint32& i = *((uint32*&)pData)++;
i = (i >> 24) + ((i >> 8) & 0xFF00) + ((i & 0xFF00) << 8) + (i << 24);
}
break;
}
case 8:
{
while (nCount--)
{
uint64& i = *((uint64*&)pData)++;
i = (i >> 56) + ((i >> 40) & 0xFF00) + ((i >> 24) & 0xFF0000) + ((i >> 8) & 0xFF000000)
+ ((i & 0xFF000000) << 8) + ((i & 0xFF0000) << 24) + ((i & 0xFF00) << 40) + (i << 56);
}
break;
}
default:
assert(0);
}
}
template<class T>
void SwapEndianness(T* t, std::size_t count)
{
SwapEndians_(t, count, sizeof(T));
}
template<class T>
void SwapEndianness(T& t)
{
SwapEndianness(&t, 1);
}
#endif // CRYINCLUDE_TOOLS_RC_RESOURCECOMPILER_SWAPENDIANNESS_H
@@ -1,27 +0,0 @@
#
# 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.
#
set(FILES
CGFLoader.cpp
CGFSaver.cpp
ChunkFile.cpp
ReadOnlyChunkFile.cpp
CGFLoader.h
CGFSaver.h
ChunkData.h
ChunkFile.h
ReadOnlyChunkFile.h
ChunkFileReaders.cpp
ChunkFileReaders.h
ChunkFileWriters.cpp
ChunkFileWriters.h
SwapEndianness.h
)
-80
View File
@@ -1,80 +0,0 @@
#
# 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.
#
add_subdirectory(CGF)
add_subdirectory(MeshCompiler)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_pal_tool_dirs(pal_tool_dirs ${CMAKE_CURRENT_LIST_DIR}/Platform)
ly_add_target(
NAME Cry3DEngine.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
cry3dengine_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
PLATFORM_INCLUDE_FILES
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
${pal_dir}
PRIVATE
${pal_tool_dirs}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::mikkelsen
Legacy::CryCommon
Legacy::CryRender.Headers
)
ly_add_target(
NAME Cry3DEngine ${PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE}
NAMESPACE Legacy
FILES_CMAKE
cry3dengine_shared_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
..
BUILD_DEPENDENCIES
PRIVATE
Legacy::Cry3DEngine.Static
Legacy::Cry3DEngine.MeshCompiler.Static
Legacy::Cry3DEngine.CGF.Static
Legacy::CryCommon
)
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME Cry3DEngine.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Legacy
FILES_CMAKE
cry3dengine_test_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Legacy::CryCommon
Legacy::Cry3DEngine.Static
Legacy::Cry3DEngine.MeshCompiler.Static
Legacy::Cry3DEngine.CGF.Static
)
ly_add_googletest(
NAME Legacy::Cry3DEngine.Tests
)
endif()
@@ -1,196 +0,0 @@
/*
* 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.
// Description : Occlusion buffer
#include "Cry3DEngine_precompiled.h"
#include "CZBufferCuller.h"
SHWOccZBuffer HWZBuffer;
void CZBufferCuller::BeginFrame(const SRenderingPassInfo& passInfo)
{
if (!GetCVars()->e_CoverageBuffer)
{
return;
}
const CCamera& rCam = passInfo.GetCamera();
m_AccurateTest = GetCVars()->e_CoverageBufferAccurateOBBTest;
m_Treshold = GetCVars()->e_CoverageBufferTolerance;
FUNCTION_PROFILER_3DENGINE;
if (GetCVars()->e_CoverageBufferDebugFreeze || GetCVars()->e_CameraFreeze)
{
return;
}
m_ObjectsTested =
m_ObjectsTestedAndRejected = 0;
//to enable statistics
m_Camera = rCam;
m_Position = rCam.GetPosition();
uint32 oldSizeX = m_SizeX;
const uint32 sizeX = min(max(1, GetCVars()->e_CoverageBufferResolution), 1024);
const uint32 sizeY = sizeX;
m_SizeX = sizeX;
m_SizeY = sizeY;
m_fSizeX = static_cast<f32>(sizeX);
m_fSizeY = static_cast<f32>(sizeY);
m_fSizeZ = static_cast<f32>(TZB_MAXDEPTH);
if (oldSizeX != sizeX)
{
CryModuleMemalignFree(m_ZBuffer);
//64-byte buffer to avoid memory page issues when vector loading
m_ZBuffer = (TZBZexel*)CryModuleMemalign((sizeof(TZBZexel) * sizeX * sizeY) + 64, 128);
}
m_MatViewProj.Transpose();
m_RotationSafe = GetCVars()->e_CoverageBufferRotationSafeCheck;
m_DebugFreez = GetCVars()->e_CoverageBufferDebugFreeze != 0;
}
void CZBufferCuller::ReloadBuffer(const uint32 BufferID)
{
if (m_DebugFreez)
{
return;
}
m_Bias = BufferID == 0 ? static_cast<int32>(GetCVars()->e_CoverageBufferBias) : 0;
}
CZBufferCuller::CZBufferCuller()
: m_OutdoorVisible(1)
, m_MatViewProj(IDENTITY)
{
m_SizeX =
m_SizeY = min(max(1, GetCVars()->e_CoverageBufferResolution), 1024);
m_ZBuffer = (TZBZexel*)CryModuleMemalign((sizeof(TZBZexel) * m_SizeX * m_SizeY) + 64, 128);
m_ObjectsTested =
m_ObjectsTestedAndRejected = 0;
}
bool CZBufferCuller::IsBoxVisible(const AABB& objBox, [[maybe_unused]] uint32* const __restrict pResDest)
{
FUNCTION_PROFILER_3DENGINE;
m_ObjectsTested++;
Vec4 Verts[8] =
{
m_MatViewProj* Vec4(objBox.min.x, objBox.min.y, objBox.min.z, 1.f),//0
m_MatViewProj * Vec4(objBox.min.x, objBox.max.y, objBox.min.z, 1.f),//1
m_MatViewProj * Vec4(objBox.max.x, objBox.min.y, objBox.min.z, 1.f),//2
m_MatViewProj * Vec4(objBox.max.x, objBox.max.y, objBox.min.z, 1.f),//3
m_MatViewProj * Vec4(objBox.min.x, objBox.min.y, objBox.max.z, 1.f),//4
m_MatViewProj * Vec4(objBox.min.x, objBox.max.y, objBox.max.z, 1.f),//5
m_MatViewProj * Vec4(objBox.max.x, objBox.min.y, objBox.max.z, 1.f),//6
m_MatViewProj * Vec4(objBox.max.x, objBox.max.y, objBox.max.z, 1.f)//7
};
bool CutNearPlane = Verts[0].w <= 0.f;
CutNearPlane |= Verts[1].w <= 0.f;
CutNearPlane |= Verts[2].w <= 0.f;
CutNearPlane |= Verts[3].w <= 0.f;
CutNearPlane |= Verts[4].w <= 0.f;
CutNearPlane |= Verts[5].w <= 0.f;
CutNearPlane |= Verts[6].w <= 0.f;
CutNearPlane |= Verts[7].w <= 0.f;
if (CutNearPlane)
{
return true;
}
IF (m_RotationSafe == 1, 0)
{
return Rasterize<1>(Verts, 8);
}
IF (m_RotationSafe == 2, 1)
{
return Rasterize<2>(Verts, 8);
}
return Rasterize<0>(Verts, 8);
}
static int sh = 8;
void CZBufferCuller::DrawDebug(int32 nStep)
{ // project buffer to the screen
nStep %= 32;
if (!nStep)
{
return;
}
const CCamera& rCam = GetCamera();
float farPlane = rCam.GetFarPlane();
float nearPlane = rCam.GetNearPlane();
float a = farPlane / (farPlane - nearPlane);
float b = farPlane * nearPlane / (nearPlane - farPlane);
const float scale = 10.0f;
TransformationMatrices backupSceneMatrices;
m_pRenderer->Set2DMode(m_SizeX, m_SizeY, backupSceneMatrices);
SAuxGeomRenderFlags Flags = e_Def3DPublicRenderflags;
Flags.SetDepthWriteFlag(e_DepthWriteOff);
Flags.SetAlphaBlendMode(e_AlphaBlended);
m_pRenderer->GetIRenderAuxGeom()->SetRenderFlags(Flags);
Vec3 vSize(.4f, .4f, .4f);
if (nStep == 1)
{
vSize = Vec3(.5f, .5f, .5f);
}
for (uint32 y = 0; y < m_SizeY; y += nStep)
{
for (uint32 x = 0; x < m_SizeX; x += nStep)
{
Vec3 vPos((float)x, (float)(m_SizeY - y - 1), 0);
vPos += Vec3(0.5f, -0.5f, 0);
const uint32 Value = m_ZBuffer[x + y * m_SizeX];
//Value>>=sh;
float w = Value / (65535.0f);
float z = b / (w - a);
uint32 ValueC = 255u - min(255u, (uint32)(z * scale));
ColorB col;
col = ColorB(ValueC, ValueC, ValueC, 200);
if (Value != 0xffff)
{
//ColorB col((Value&31)<<3,((Value>>5)&31)<<3,((Value>>10)&63)<<2,200);
GetRenderer()->GetIRenderAuxGeom()->DrawAABB(AABB(vPos - vSize, vPos + vSize), nStep <= 2, col, eBBD_Faceted);
}
}
}
//m_pRenderer->GetIRenderAuxGeom()->Flush();
m_pRenderer->Unset2DMode(backupSceneMatrices);
}
void CZBufferCuller::GetMemoryUsage(ICrySizer* pSizer) const
{
SIZER_COMPONENT_NAME(pSizer, "CoverageBuffer");
pSizer->AddObject(m_ZBuffer, sizeof(TZBZexel) * m_SizeX * m_SizeY);
}
-273
View File
@@ -1,273 +0,0 @@
/*
* 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.
// Description : Occlusion Culler using hardware generated ZBuffer
#ifndef CRYINCLUDE_CRY3DENGINE_CZBUFFERCULLER_H
#define CRYINCLUDE_CRY3DENGINE_CZBUFFERCULLER_H
#pragma once
#include <Cry_Math.h>
struct IRenderMesh;
typedef uint16 TZBZexel;
const uint64 TZB_MAXDEPTH = (1 << (sizeof(TZBZexel) * 8)) - 1;
class CZBufferCuller
: public Cry3DEngineBase
{
protected:
bool m_DebugFreez;
uint32 m_SizeX;
uint32 m_SizeY;
f32 m_fSizeX;
f32 m_fSizeY;
f32 m_fSizeZ;
TZBZexel* m_ZBuffer;
Matrix44 m_MatProj;
Matrix44 m_MatView;
Matrix44 m_MatViewProj;
Matrix44A m_MatViewProjT;
Vec3 m_Position;
int32 m_Bias;
uint32 m_RotationSafe;
uint32 m_AccurateTest;
uint32 m_Treshold;
f32 m_FixedZFar;
uint32 m_ObjectsTested;
uint32 m_ObjectsTestedAndRejected;
CCamera m_Camera;
int m_OutdoorVisible;
template<uint32 ROTATE, class T>
bool Rasterize(const T rVertices, const uint32 VCount)
{
int64 MinX = m_SizeX;
int64 MaxX = 0;
int64 MinY = m_SizeY;
int64 MaxY = 0;
int64 MinZ = TZB_MAXDEPTH;
for (uint32 a = 0; a < VCount; a++)
{
Vec4 V = rVertices[a];
const f32 InvW = 1.f / V.w;
int64 X = static_cast<int64>((V.x * InvW * 0.5f + 0.5f) * m_fSizeX + 0.5f);
int64 Y = static_cast<int64>((V.y * InvW * 0.5f + 0.5f) * m_fSizeY + 0.5f);
int64 Z = static_cast<int64>(V.z * InvW * m_fSizeZ);
if (X < MinX)
{
MinX = X;
}
else
if (X > MaxX)
{
MaxX = X;
}
if (Y < MinY)
{
MinY = Y;
}
else
if (Y > MaxY)
{
MaxY = Y;
}
if (Z < MinZ)
{
MinZ = Z;
}
}
if (MinX < 0)
{
if constexpr (ROTATE == 1)
{
return true;
}
else
{
MinX = 0;
}
}
if (MaxX > m_SizeX)
{
if constexpr (ROTATE == 1)
{
return true;
}
else
{
MaxX = m_SizeX;
}
}
if (MinY < 0)
{
if constexpr (ROTATE == 1)
{
return true;
}
else
{
MinY = 0;
}
}
if (MaxY > m_SizeY)
{
if constexpr (ROTATE == 1)
{
return true;
}
else
{
MaxY = m_SizeY;
}
}
if constexpr (ROTATE == 2)
{
if (MinX >= m_SizeX || MinY >= m_SizeY || MaxX < 0 || MaxX < 0)
{
return true;
}
}
for (int64 y = MinY; y < MaxY; y++)
{
for (int64 x = MinX; x < MaxX; x++)
{
if (static_cast<int64>(m_ZBuffer[static_cast<int32>(x) + static_cast<int32>(y) * m_SizeX]) > MinZ)
{
return true;
}
}
}
return false;
}
bool IsBoxVisible_OCCLUDER(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_OCEAN(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_OCCELL(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_OCCELL_OCCLUDER(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_OBJECT(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_OBJECT_TO_LIGHT(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_TERRAIN_NODE(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible_PORTAL(const AABB& objBox, uint32* const __restrict pResDest = NULL);
bool IsBoxVisible(const AABB& objBox, uint32* const __restrict pResDest = NULL);
public:
CZBufferCuller();
~CZBufferCuller(){CryModuleMemalignFree(m_ZBuffer); }
// start new frame
void BeginFrame(const SRenderingPassInfo& passInfo);
void ReloadBuffer(const uint32 BufferID);
// render into buffer
ILINE void AddRenderMesh([[maybe_unused]] IRenderMesh* pRM, [[maybe_unused]] Matrix34A* pTranRotMatrix, _smart_ptr<IMaterial> pMaterial, [[maybe_unused]] bool bOutdoorOnly, [[maybe_unused]] bool bCompletelyInFrustum, [[maybe_unused]] bool bNoCull){}
ILINE bool IsObjectVisible(const AABB& objBox, EOcclusionObjectType eOcclusionObjectType, [[maybe_unused]] float fDistance, uint32* pRetVal = NULL)
{
switch (eOcclusionObjectType)
{
case eoot_OCCLUDER:
return IsBoxVisible_OCCLUDER(objBox, pRetVal);
case eoot_OCEAN:
return IsBoxVisible_OCEAN(objBox, pRetVal);
case eoot_OCCELL:
return IsBoxVisible_OCCELL(objBox, pRetVal);
case eoot_OCCELL_OCCLUDER:
return IsBoxVisible_OCCELL_OCCLUDER(objBox, pRetVal);
case eoot_OBJECT:
return IsBoxVisible_OBJECT(objBox, pRetVal);
case eoot_OBJECT_TO_LIGHT:
return IsBoxVisible_OBJECT_TO_LIGHT(objBox, pRetVal);
case eoot_TERRAIN_NODE:
return IsBoxVisible_TERRAIN_NODE(objBox, pRetVal);
case eoot_PORTAL:
return IsBoxVisible_PORTAL(objBox, pRetVal);
}
assert(!"Undefined occluder type");
return true;
}
// draw content to the screen for debug
void DrawDebug(int32 nStep);
// return current camera
const CCamera& GetCamera() const {return m_Camera; }
void GetMemoryUsage(ICrySizer* pSizer) const;
bool IsOutdooVisible(){return m_OutdoorVisible == 1; }
int32 TrisWritten() const{return 0; }
int32 ObjectsWritten() const{return 0; }
int32 TrisTested() const{return 0; }
int32 ObjectsTested() const{return m_ObjectsTested; }
int32 ObjectsTestedAndRejected() const{return m_ObjectsTestedAndRejected; }
int32 SelRes() const{return m_SizeX; }
float FixedZFar() const{return m_FixedZFar; }
float GetZNearInMeters() const{return 0.f; }
float GetZFarInMeters() const{return 1024; }
} _ALIGN(128);
ILINE bool CZBufferCuller::IsBoxVisible_TERRAIN_NODE(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OCCELL_OCCLUDER(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OCCLUDER(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OCEAN(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OCCELL(const AABB& objBox, uint32* const __restrict pResDest)
{
if (GetCVars()->e_CoverageBufferDebugFreeze)
{
return true;
}
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OBJECT(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_OBJECT_TO_LIGHT(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
ILINE bool CZBufferCuller::IsBoxVisible_PORTAL(const AABB& objBox, uint32* const __restrict pResDest)
{
return IsBoxVisible(objBox, pResDest);
}
#endif // CRYINCLUDE_CRY3DENGINE_CZBUFFERCULLER_H
-113
View File
@@ -1,113 +0,0 @@
/*
* 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 "ClipVolume.h"
CClipVolume::CClipVolume()
: m_nStencilRef(0)
, m_WorldTM(IDENTITY)
, m_InverseWorldTM(IDENTITY)
, m_BBoxWS(AABB::RESET)
, m_BBoxLS(AABB::RESET)
, m_pBspTree(NULL)
{
memset(m_sName, 0x0, sizeof(m_sName));
}
CClipVolume::~CClipVolume()
{
m_pRenderMesh = NULL;
for (size_t i = 0; i < m_lstRenderNodes.size(); ++i)
{
if (m_lstRenderNodes[i]->m_pRNTmpData)
{
m_lstRenderNodes[i]->m_pRNTmpData->userData.m_pClipVolume = NULL;
}
}
}
void CClipVolume::SetName(const char* szName)
{
cry_strcpy(m_sName, szName);
}
void CClipVolume::GetClipVolumeMesh(_smart_ptr<IRenderMesh>& renderMesh, Matrix34& worldTM) const
{
renderMesh = m_pRenderMesh;
worldTM = m_WorldTM;
}
AABB CClipVolume::GetClipVolumeBBox() const
{
return m_BBoxWS;
}
void CClipVolume::Update(_smart_ptr<IRenderMesh> pRenderMesh, IBSPTree3D* pBspTree, const Matrix34& worldTM, uint32 flags)
{
const bool bMeshUpdated = m_pRenderMesh != pRenderMesh;
m_pRenderMesh = pRenderMesh;
m_pBspTree = pBspTree;
m_WorldTM = worldTM;
m_InverseWorldTM = worldTM.GetInverted();
m_BBoxWS.Reset();
m_BBoxLS.Reset();
m_nFlags = flags;
if (m_pRenderMesh)
{
pRenderMesh->GetBBox(m_BBoxLS.min, m_BBoxLS.max);
m_BBoxWS.SetTransformedAABB(worldTM, m_BBoxLS);
}
}
bool CClipVolume::IsPointInsideClipVolume(const Vec3& point) const
{
FUNCTION_PROFILER_3DENGINE;
if (!m_pRenderMesh || !m_pBspTree || !m_BBoxWS.IsContainPoint(point))
{
return false;
}
Vec3 pt = m_InverseWorldTM.TransformPoint(point);
return m_BBoxLS.IsContainPoint(pt) && m_pBspTree->IsInside(pt);
}
void CClipVolume::RegisterRenderNode(IRenderNode* pRenderNode)
{
if (m_lstRenderNodes.Find(pRenderNode) < 0)
{
m_lstRenderNodes.Add(pRenderNode);
if (pRenderNode->m_pRNTmpData)
{
pRenderNode->m_pRNTmpData->userData.m_pClipVolume = this;
}
}
}
void CClipVolume::UnregisterRenderNode(IRenderNode* pRenderNode)
{
if (m_lstRenderNodes.Delete(pRenderNode) && pRenderNode->m_pRNTmpData)
{
pRenderNode->m_pRNTmpData->userData.m_pClipVolume = NULL;
}
}
void CClipVolume::GetMemoryUsage(class ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
}
-60
View File
@@ -1,60 +0,0 @@
/*
* 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.
#ifndef __INCLUDE_CRY3DENGINE_CLIPVOLUME_H
#define __INCLUDE_CRY3DENGINE_CLIPVOLUME_H
struct IBSPTree3D;
class CClipVolume
: public IClipVolume
{
public:
CClipVolume();
virtual ~CClipVolume();
////////////// IClipVolume implementation //////////////
virtual void GetClipVolumeMesh(_smart_ptr<IRenderMesh>& renderMesh, Matrix34& worldTM) const;
virtual AABB GetClipVolumeBBox() const;
virtual uint8 GetStencilRef() const { return m_nStencilRef; }
virtual uint GetClipVolumeFlags() const { return m_nFlags; }
virtual bool IsPointInsideClipVolume(const Vec3& vPos) const;
////////////////////////////
void SetName(const char* szName);
void SetStencilRef(int nStencilRef) { m_nStencilRef = nStencilRef; }
void Update(_smart_ptr<IRenderMesh> pRenderMesh, IBSPTree3D* pBspTree, const Matrix34& worldTM, uint32 flags);
void RegisterRenderNode(IRenderNode* pRenderNode);
void UnregisterRenderNode(IRenderNode* pRenderNode);
void GetMemoryUsage(class ICrySizer* pSizer) const;
private:
uint8 m_nStencilRef;
uint32 m_nFlags;
Matrix34 m_WorldTM;
Matrix34 m_InverseWorldTM;
AABB m_BBoxWS;
AABB m_BBoxLS;
_smart_ptr<IRenderMesh> m_pRenderMesh;
IBSPTree3D* m_pBspTree;
PodArray<IRenderNode*> m_lstRenderNodes;
char m_sName[64];
};
#endif //__INCLUDE_CRY3DENGINE_CLIPVOLUME_H
@@ -1,176 +0,0 @@
/*
* 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 "ClipVolume.h"
#include "ClipVolumeManager.h"
#include "LightEntity.h"
#include "FogVolumeRenderNode.h"
#include "ObjMan.h"
CClipVolumeManager::~CClipVolumeManager()
{
assert(m_ClipVolumes.empty());
}
IClipVolume* CClipVolumeManager::CreateClipVolume()
{
SClipVolumeInfo volumeInfo(new CClipVolume());
m_ClipVolumes.push_back(volumeInfo);
return m_ClipVolumes.back().m_pVolume;
}
bool CClipVolumeManager::DeleteClipVolume(IClipVolume* pClipVolume)
{
if (m_ClipVolumes.Delete(static_cast<CClipVolume*>(pClipVolume)))
{
delete pClipVolume;
return true;
}
return false;
}
bool CClipVolumeManager::UpdateClipVolume(IClipVolume* pClipVolume, _smart_ptr<IRenderMesh> pRenderMesh, IBSPTree3D* pBspTree, const Matrix34& worldTM, bool bActive, uint32 flags, const char* szName)
{
int nVolumeIndex = m_ClipVolumes.Find((CClipVolume*)pClipVolume);
if (nVolumeIndex >= 0)
{
SClipVolumeInfo& volumeInfo = m_ClipVolumes[nVolumeIndex];
volumeInfo.m_pVolume->Update(pRenderMesh, pBspTree, worldTM, flags);
volumeInfo.m_pVolume->SetName(szName);
volumeInfo.m_bActive = bActive;
AABB volumeBBox = pClipVolume->GetClipVolumeBBox();
Get3DEngine()->GetObjManager()->ReregisterEntitiesInArea(volumeBBox.min, volumeBBox.max);
return true;
}
return false;
}
void CClipVolumeManager::PrepareVolumesForRendering(const SRenderingPassInfo& passInfo)
{
for (size_t i = 0; i < m_ClipVolumes.size(); ++i)
{
SClipVolumeInfo& volInfo = m_ClipVolumes[i];
volInfo.m_pVolume->SetStencilRef(InactiveVolumeStencilRef);
if (volInfo.m_bActive && passInfo.GetCamera().IsAABBVisible_F(volInfo.m_pVolume->GetClipVolumeBBox()))
{
uint8 nStencilRef = GetRenderer()->EF_AddDeferredClipVolume(volInfo.m_pVolume);
volInfo.m_pVolume->SetStencilRef(nStencilRef);
}
}
}
void CClipVolumeManager::UpdateEntityClipVolume(const Vec3& pos, IRenderNode* pRenderNode)
{
FRAME_PROFILER("CClipVolumeManager::UpdateEntityClipVolume", GetSystem(), PROFILE_3DENGINE);
if (!pRenderNode || !pRenderNode->m_pRNTmpData)
{
return;
}
IClipVolume* pPreviousVolume = pRenderNode->m_pRNTmpData->userData.m_pClipVolume;
UnregisterRenderNode(pRenderNode);
// user assigned clip volume
CLightEntity* pLight = static_cast<CLightEntity*>(pRenderNode);
if (pRenderNode->GetRenderNodeType() == eERType_Light && (pLight->m_light.m_Flags & DLF_HAS_CLIP_VOLUME) != 0)
{
for (int i = 1; i >= 0; --i)
{
if (CClipVolume* pVolume = static_cast<CClipVolume*>(pLight->m_light.m_pClipVolumes[i]))
{
pVolume->RegisterRenderNode(pRenderNode);
}
}
}
else // assign by position
{
// Check if entity is in same clip volume as before
if (pPreviousVolume && (pPreviousVolume->GetClipVolumeFlags() & IClipVolume::eClipVolumeIsVisArea) == 0)
{
CClipVolume* pVolume = static_cast<CClipVolume*>(pPreviousVolume);
if (pVolume->IsPointInsideClipVolume(pos))
{
pVolume->RegisterRenderNode(pRenderNode);
return;
}
}
if (CClipVolume* pVolume = GetClipVolumeByPos(pos, pPreviousVolume))
{
pVolume->RegisterRenderNode(pRenderNode);
}
}
}
void CClipVolumeManager::UnregisterRenderNode(IRenderNode* pRenderNode)
{
if (!pRenderNode)
{
return;
}
for (size_t i = 0; i < m_ClipVolumes.size(); ++i)
{
m_ClipVolumes[i].m_pVolume->UnregisterRenderNode(pRenderNode);
}
if (pRenderNode->m_pRNTmpData)
{
pRenderNode->m_pRNTmpData->userData.m_pClipVolume = NULL;
}
}
bool CClipVolumeManager::IsClipVolumeRequired(IRenderNode* pRenderNode) const
{
const uint32 NoClipVolumeLights = DLF_SUN | DLF_ATTACH_TO_SUN;
const bool bForwardObject = (pRenderNode->m_nInternalFlags & IRenderNode::REQUIRES_FORWARD_RENDERING) != 0;
const EERType ertype = pRenderNode->GetRenderNodeType();
const bool bIsValidLight = ertype == eERType_Light &&
(static_cast<CLightEntity*>(pRenderNode)->m_light.m_Flags & NoClipVolumeLights) == 0;
const bool bIsValidFogVolume = (ertype == eERType_FogVolume) &&
static_cast<CFogVolumeRenderNode*>(pRenderNode)->IsAffectsThisAreaOnly();
return bIsValidLight || bForwardObject || bIsValidFogVolume;
}
CClipVolume* CClipVolumeManager::GetClipVolumeByPos(const Vec3& pos, const IClipVolume* pIgnoreVolume) const
{
for (size_t i = 0; i < m_ClipVolumes.size(); ++i)
{
const SClipVolumeInfo& volInfo = m_ClipVolumes[i];
if (volInfo.m_bActive && volInfo.m_pVolume != pIgnoreVolume && volInfo.m_pVolume->IsPointInsideClipVolume(pos))
{
return m_ClipVolumes[i].m_pVolume;
}
}
return NULL;
}
void CClipVolumeManager::GetMemoryUsage(class ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(this));
for (size_t i = 0; i < m_ClipVolumes.size(); ++i)
{
pSizer->AddObject(m_ClipVolumes[i].m_pVolume);
}
}
@@ -1,63 +0,0 @@
/*
* 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.
#ifndef __INCLUDE_CRY3DENGINE_CLIPVOLUMEMANAGER_H
#define __INCLUDE_CRY3DENGINE_CLIPVOLUMEMANAGER_H
class CClipVolume;
class CClipVolumeManager
: public Cry3DEngineBase
{
struct SClipVolumeInfo
{
CClipVolume* m_pVolume;
bool m_bActive;
SClipVolumeInfo()
: m_bActive(false) {}
SClipVolumeInfo(CClipVolume* pVolume)
: m_pVolume(pVolume)
, m_bActive(false)
{}
bool operator==(const SClipVolumeInfo& other) const { return m_pVolume == other.m_pVolume; }
};
public:
static const uint8 InactiveVolumeStencilRef = 0xFD;
static const uint8 AffectsEverythingStencilRef = 0xFE;
virtual ~CClipVolumeManager();
virtual IClipVolume* CreateClipVolume();
virtual bool DeleteClipVolume(IClipVolume* pClipVolume);
virtual bool UpdateClipVolume(IClipVolume* pClipVolume, _smart_ptr<IRenderMesh> pRenderMesh, IBSPTree3D* pBspTree, const Matrix34& worldTM, bool bActive, uint32 flags, const char* szName);
void PrepareVolumesForRendering(const SRenderingPassInfo& passInfo);
void UpdateEntityClipVolume(const Vec3& pos, IRenderNode* pRenderNode);
void UnregisterRenderNode(IRenderNode* pRenderNode);
bool IsClipVolumeRequired(IRenderNode* pRenderNode) const;
CClipVolume* GetClipVolumeByPos(const Vec3& pos, const IClipVolume* pIgnoreVolume = NULL) const;
void GetMemoryUsage(class ICrySizer* pSizer) const;
size_t GetClipVolumeCount() const { return m_ClipVolumes.size(); }
private:
PodArray<SClipVolumeInfo> m_ClipVolumes;
};
#endif //__INCLUDE_CRY3DENGINE_CLIPVOLUMEMANAGER_H
@@ -1,280 +0,0 @@
/*
* 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 "CloudRenderNode.h"
#include "CloudsManager.h"
#include "VisAreas.h"
#include "ObjMan.h"
#include "Environment/OceanEnvironmentBus.h"
//////////////////////////////////////////////////////////////////////////
CCloudRenderNode::CCloudRenderNode()
{
m_bounds.min = Vec3(-1, -1, -1);
m_bounds.max = Vec3(1, 1, 1);
m_fScale = 1.0f;
m_offsetedMatrix.SetIdentity();
m_matrix.SetIdentity();
m_vOffset.Set(0, 0, 0);
m_alpha = 1.f;
m_pCloudRenderElement = (CREBaseCloud*)GetRenderer()->EF_CreateRE(eDATA_Cloud);
m_pREImposter = (CREImposter*) GetRenderer()->EF_CreateRE(eDATA_Imposter);
GetCloudsManager()->AddCloudRenderNode(this);
m_origin = Vec3(0, 0, 0);
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;
}
//////////////////////////////////////////////////////////////////////////
CCloudRenderNode::~CCloudRenderNode()
{
GetCloudsManager()->RemoveCloudRenderNode(this);
m_pCloudRenderElement->Release(false);
m_pREImposter->Release(false);
Get3DEngine()->FreeRenderNodeState(this);
}
//////////////////////////////////////////////////////////////////////////
bool CCloudRenderNode::LoadCloudFromXml(XmlNodeRef root)
{
m_pCloudDesc = new SCloudDescription;
GetCloudsManager()->ParseCloudFromXml(root, m_pCloudDesc);
SetCloudDesc(m_pCloudDesc);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CCloudRenderNode::LoadCloud(const char* sCloudFilename)
{
m_bounds.min = Vec3(-1, -1, -1);
m_bounds.max = Vec3(1, 1, 1);
SetCloudDesc(GetCloudsManager()->LoadCloud(sCloudFilename));
return m_pCloudDesc != 0;
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::SetMovementProperties(const SCloudMovementProperties& properties)
{
m_moveProps = properties;
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::SetCloudDesc(SCloudDescription* pCloud)
{
m_pCloudDesc = pCloud;
if (m_pCloudDesc != NULL && m_pCloudDesc->m_particles.size() > 0)
{
m_vOffset = m_pCloudDesc->m_offset;
m_bounds.min = m_pCloudDesc->m_bounds.min - m_pCloudDesc->m_offset;
m_bounds.max = m_pCloudDesc->m_bounds.max - m_pCloudDesc->m_offset;
if (m_pCloudDesc->m_pMaterial)
{
m_pMaterial = m_pCloudDesc->m_pMaterial;
}
m_pCloudRenderElement->SetParticles(&m_pCloudDesc->m_particles[0], m_pCloudDesc->m_particles.size());
m_WSBBox.SetTransformedAABB(m_matrix, m_bounds);
m_fScale = m_matrix.GetColumn(0).GetLength();
// Offset matrix by the cloud bounds offset.
m_offsetedMatrix = m_matrix * Matrix34::CreateTranslationMat(-m_vOffset);
}
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::SetMatrix(const Matrix34& mat)
{
SetMatrixInternal(mat, true);
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::SetMatrixInternal(const Matrix34& mat, bool updateOrigin)
{
m_dwRndFlags |= ERF_OUTDOORONLY;
if (updateOrigin)
{
m_origin = mat.GetTranslation();
}
m_matrix = mat;
m_pos = mat.GetTranslation();
// m_WSBBox.SetTransformedAABB( m_matrix,m_bounds );
m_fScale = mat.GetColumn(0).GetLength();
m_WSBBox.SetTransformedAABB(Matrix34::CreateTranslationMat(m_pos), AABB(m_bounds.min * m_fScale, m_bounds.max * m_fScale));
// Offset matrix by the cloud bounds offset.
// m_offsetedMatrix = m_matrix * Matrix34::CreateTranslationMat(-m_vOffset);
m_offsetedMatrix = Matrix34::CreateTranslationMat(m_pos - m_vOffset * m_fScale);
m_offsetedMatrix.ScaleColumn(Vec3(m_fScale, m_fScale, m_fScale));
Get3DEngine()->RegisterEntity(this);
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::MoveCloud()
{
FUNCTION_PROFILER_3DENGINE;
Vec3 pos(m_matrix.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_matrix);
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_matrix);
mat.SetTranslation(m_origin);
SetMatrixInternal(mat, false);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CCloudRenderNode::Render(const SRendParams& rParams, const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
if (!m_pMaterial || !passInfo.RenderClouds())
{
return;
}
IRenderer* pRenderer(GetRenderer());
// get render objects
CRenderObject* pRO = pRenderer->EF_GetObject_Temp(passInfo.ThreadID());
if (!pRO)
{
return;
}
SShaderItem& shaderItem = (rParams.pMaterial) ? rParams.pMaterial->GetShaderItem(0) : m_pMaterial->GetShaderItem(0);
pRO->m_II.m_Matrix = m_offsetedMatrix;
SRenderObjData* pOD = pRenderer->EF_GetObjData(pRO, true, passInfo.ThreadID());
pOD->m_fTempVars[0] = m_fScale;
pRO->m_fSort = 0;
pRO->m_fDistance = rParams.fDistance;
int nAfterWater = GetObjManager()->IsAfterWater(m_offsetedMatrix.GetTranslation(), passInfo) ? 1 : 0;
pRO->m_II.m_AmbColor = rParams.AmbientColor;
pRO->m_fAlpha = rParams.fAlpha * m_alpha;
float mvd(GetMaxViewDist());
float d((passInfo.GetCamera().GetPosition() - m_offsetedMatrix.GetTranslation()).GetLength());
if (d > 0.9f * mvd)
{
float s(clamp_tpl(1.0f - (d - 0.9f * mvd) / (0.1f * mvd), 0.0f, 1.0f));
pRO->m_fAlpha *= s;
}
pRenderer->EF_AddEf(m_pCloudRenderElement, shaderItem, pRO, passInfo, EFSLIST_TRANSP, nAfterWater, SRendItemSorter(rParams.rendItemSorter));
}
//////////////////////////////////////////////////////////////////////////
bool CCloudRenderNode::CheckIntersection(const Vec3& p1, const Vec3& p2)
{
if (p1 == p2)
{
return false;
}
if (m_pCloudDesc && m_pCloudDesc->m_pCloudTree)
{
Vec3 outp;
if (Intersect::Lineseg_AABB(Lineseg(p1, p2), m_WSBBox, outp))
{
Matrix34 pInv = m_offsetedMatrix.GetInverted();
return m_pCloudDesc->m_pCloudTree->CheckIntersection(pInv * p1, pInv * p2);
}
}
return false;
}
void CCloudRenderNode::OffsetPosition(const Vec3& delta)
{
if (m_pRNTmpData)
{
m_pRNTmpData->OffsetPosition(delta);
}
m_pos += delta;
m_origin += delta;
m_matrix.SetTranslation(m_matrix.GetTranslation() + delta);
m_offsetedMatrix.SetTranslation(m_offsetedMatrix.GetTranslation() + delta);
m_WSBBox.Move(delta);
}
@@ -1,91 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CLOUDRENDERNODE_H
#define CRYINCLUDE_CRY3DENGINE_CLOUDRENDERNODE_H
#pragma once
struct SCloudDescription;
//////////////////////////////////////////////////////////////////////////
// RenderNode for rendering single cloud object.
//////////////////////////////////////////////////////////////////////////
class CCloudRenderNode
: public ICloudRenderNode
, public Cry3DEngineBase
{
public:
CCloudRenderNode();
//////////////////////////////////////////////////////////////////////////
// Implements ICloudRenderNode
//////////////////////////////////////////////////////////////////////////
virtual bool LoadCloud(const char* sCloudFilename);
virtual bool LoadCloudFromXml(XmlNodeRef cloudNode);
virtual void SetMovementProperties(const SCloudMovementProperties& properties);
//////////////////////////////////////////////////////////////////////////
// Implements IRenderNode
//////////////////////////////////////////////////////////////////////////
virtual void GetLocalBounds(AABB& bbox) { bbox = m_bounds; };
virtual void SetMatrix(const Matrix34& mat);
virtual EERType GetRenderNodeType();
virtual const char* GetEntityClassName() const { return "Cloud"; }
virtual const char* GetName() const { return "Cloud"; }
virtual Vec3 GetPos(bool bWorldOnly = true) const;
virtual void Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo);
void SetMaterial(_smart_ptr<IMaterial> pMat) override { m_pMaterial = pMat; }
virtual _smart_ptr<IMaterial> GetMaterial(Vec3* pHitPos = NULL);
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return m_pMaterial; }
virtual float GetMaxViewDist();
virtual const AABB GetBBox() const { return m_WSBBox; }
virtual void SetBBox(const AABB& WSBBox) { m_WSBBox = WSBBox; }
virtual void FillBBox(AABB& aabb);
virtual void OffsetPosition(const Vec3& delta);
//////////////////////////////////////////////////////////////////////////
bool CheckIntersection(const Vec3& p1, const Vec3& p2);
void MoveCloud();
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
}
private:
void SetCloudDesc(SCloudDescription* pCloud);
~CCloudRenderNode();
virtual void SetMatrixInternal(const Matrix34& mat, bool updateOrigin);
private:
Vec3 m_pos;
float m_fScale;
_smart_ptr<IMaterial> m_pMaterial;
_smart_ptr<SCloudDescription> m_pCloudDesc;
Matrix34 m_matrix;
Matrix34 m_offsetedMatrix;
Vec3 m_vOffset;
AABB m_bounds;
CREBaseCloud* m_pCloudRenderElement;
CREImposter* m_pREImposter;
float m_alpha;
Vec3 m_origin;
SCloudMovementProperties m_moveProps;
AABB m_WSBBox;
};
#endif // CRYINCLUDE_CRY3DENGINE_CLOUDRENDERNODE_H
@@ -1,335 +0,0 @@
/*
* 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 "CloudRenderNode.h"
#include "CloudsManager.h"
#include <CryPath.h>
#include "MatMan.h"
//////////////////////////////////////////////////////////////////////////
SCloudDescription::~SCloudDescription()
{
delete m_pCloudTree;
m_pCloudTree = 0;
// Unregister itself from clouds manager.
if (!filename.empty())
{
Get3DEngine()->GetCloudsManager()->Unregister(this);
}
}
//////////////////////////////////////////////////////////////////////////
SCloudDescription* CCloudsManager::LoadCloud(const char* sFilename)
{
string filename = PathUtil::ToUnixPath(sFilename);
SCloudDescription* pCloud = stl::find_in_map(m_cloudsMap, filename, NULL);
if (!pCloud)
{
XmlNodeRef root = GetISystem()->LoadXmlFromFile(filename);
if (root)
{
pCloud = new SCloudDescription;
pCloud->filename = filename;
ParseCloudFromXml(root, pCloud);
CloudParticles particles;
particles.resize(pCloud->m_particles.size());
for (uint32 i = 0; i < particles.size(); i++)
{
particles[i] = &pCloud->m_particles[i];
}
pCloud->m_pCloudTree = new SCloudQuadTree();
pCloud->m_pCloudTree->Init(pCloud->m_bounds, particles);
Register(pCloud);
}
}
return pCloud;
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::ParseCloudFromXml(XmlNodeRef root, SCloudDescription* pCloud)
{
assert(pCloud);
pCloud->m_bounds.min = Vec3(0, 0, 0);
pCloud->m_bounds.max = Vec3(0, 0, 0);
pCloud->m_pMaterial = 0;
const char* sMtlName = root->getAttr("Material");
if (sMtlName && sMtlName[0] != '\0')
{
pCloud->m_pMaterial = GetMatMan()->LoadMaterial(sMtlName);
if (!pCloud->m_pMaterial)
{
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "Error: Failed to load cloud material" /*,sMtlName*/);
}
}
int numRows = 1;
int numCols = 1;
root->getAttr("TextureNumRows", numRows);
root->getAttr("TextureNumCols", numCols);
if (numRows < 1)
{
numRows = 1;
}
if (numCols < 1)
{
numCols = 1;
}
pCloud->m_textureRows = numRows;
pCloud->m_textureCols = numCols;
pCloud->m_numSprites = root->getChildCount();
pCloud->m_particles.reserve(pCloud->m_numSprites);
pCloud->m_particles.clear();
float xTextureStep = 1.0f / numCols;
float yTextureStep = 1.0f / numRows;
Vec3 pos(0, 0, 0);
int texID = 0;
float angle = 0;
float radius = 0;
Vec2 uv[2];
if (pCloud->m_numSprites > 0)
{
pCloud->m_bounds.Reset();
}
for (int i = 0; i < root->getChildCount(); i++)
{
XmlNodeRef child = root->getChild(i);
child->getAttr("Pos", pos);
child->getAttr("texID", texID);
child->getAttr("Radius", radius);
if (!child->getAttr("Angle", angle))
{
angle = 0;
}
int x = texID % numCols;
int y = texID / numCols;
uv[0].x = x * xTextureStep;
uv[0].y = y * yTextureStep;
uv[1].x = (x + 1) * xTextureStep;
uv[1].y = (y + 1) * yTextureStep;
SCloudParticle sprite(pos, radius, radius, 0, 0, uv);
pCloud->m_particles.push_back(sprite);
pCloud->m_bounds.Add(pos - Vec3(radius, radius, radius));
pCloud->m_bounds.Add(pos + Vec3(radius, radius, radius));
}
// Offset particles so that bounding box is centered at origin.
pCloud->m_offset = -pCloud->m_bounds.GetCenter();
pCloud->m_bounds.min += pCloud->m_offset;
pCloud->m_bounds.max += pCloud->m_offset;
for (uint32 i = 0; i < pCloud->m_particles.size(); i++)
{
pCloud->m_particles[i].SetPosition(pCloud->m_particles[i].GetPosition() + pCloud->m_offset);
}
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::Register(SCloudDescription* desc)
{
assert(desc);
m_cloudsMap[desc->filename] = desc;
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::Unregister(SCloudDescription* desc)
{
assert(desc);
m_cloudsMap.erase(desc->filename);
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::AddCloudRenderNode(CCloudRenderNode* pNode)
{
m_cloudNodes.push_back(pNode);
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::RemoveCloudRenderNode(CCloudRenderNode* pNode)
{
int size = m_cloudNodes.size();
for (int i = 0; i < size; i++)
{
if (m_cloudNodes[i] == pNode)
{
if (i < size - 1)
{
m_cloudNodes[i] = m_cloudNodes[size - 1];
}
m_cloudNodes.resize(size - 1);
break;
}
}
}
//!!! WARNING
//bool Sp_IsDraw = false;
//Matrix34 Sp_Mat;
//Vec3 Sp_Pos;
//float Sp_Rad;
//////////////////////////////////////////////////////////////////////////
bool CCloudsManager::CheckIntersectClouds(const Vec3& p1, const Vec3& p2)
{
for (std::vector<CCloudRenderNode*>::iterator it = m_cloudNodes.begin(); it != m_cloudNodes.end(); ++it)
{
//Sp_IsDraw = false;
if ((*it)->CheckIntersection(p1, p2))
{
//Sp_Mat = (*it)->m_offsetedMatrix;
//Sp_IsDraw = true;
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CCloudsManager::MoveClouds()
{
FUNCTION_PROFILER_3DENGINE;
std::vector<CCloudRenderNode*>::iterator it(m_cloudNodes.begin());
std::vector<CCloudRenderNode*>::iterator itEnd(m_cloudNodes.end());
for (; it != itEnd; ++it)
{
(*it)->MoveCloud();
}
}
//////////////////////////////////////////////////////////////////////////
void SCloudQuadTree::Init(const AABB& bounds, const CloudParticles& particles, int maxlevel)
{
m_bounds = bounds;
if (m_level >= maxlevel)
{
m_particles.resize(particles.size());
memcpy(&m_particles[0], &particles[0], particles.size() * sizeof(SCloudParticle*));
return;
}
CloudParticles parts;
for (int k = 0; k < 4; k++)
{
AABB bnds;
parts.resize(0);
Vec3 centr = (bounds.min + bounds.max) / 2;
if (k == 0)
{
bnds = AABB(bounds.min, Vec3(centr.x, centr.y, bounds.max.z));
}
else if (k == 1)
{
bnds = AABB(Vec3(bounds.min.x, centr.y, bounds.min.z), Vec3(centr.x, bounds.max.y, bounds.max.z));
}
else if (k == 2)
{
bnds = AABB(Vec3(centr.x, bounds.min.y, bounds.min.z), Vec3(bounds.max.x, centr.y, bounds.max.z));
}
else if (k == 3)
{
bnds = AABB(Vec3(centr.x, centr.y, bounds.min.z), bounds.max);
}
for (uint32 i = 0; i < particles.size(); i++)
{
SCloudParticle* pPtcl = particles[i];
if (bnds.IsOverlapSphereBounds(pPtcl->GetPosition(), pPtcl->GetRadiusX()) ||
bnds.IsContainSphere(pPtcl->GetPosition(), pPtcl->GetRadiusX()))
{
parts.push_back(pPtcl);
}
}
if (!parts.empty())
{
m_pQuads[k] = new SCloudQuadTree(m_level + 1);
m_pQuads[k]->Init(bnds, parts, maxlevel);
}
}
}
//////////////////////////////////////////////////////////////////////////
bool SCloudQuadTree::CheckIntersection(const Vec3& p1, const Vec3& p2)
{
Vec3 outp, outp2;
if (Intersect::Lineseg_AABB(Lineseg(p1, p2), m_bounds, outp))
{
if (m_pQuads[0] && m_pQuads[0]->CheckIntersection(p1, p2))
{
return true;
}
if (m_pQuads[1] && m_pQuads[1]->CheckIntersection(p1, p2))
{
return true;
}
if (m_pQuads[2] && m_pQuads[2]->CheckIntersection(p1, p2))
{
return true;
}
if (m_pQuads[3] && m_pQuads[3]->CheckIntersection(p1, p2))
{
return true;
}
if (!m_particles.empty())
{
for (uint32 i = 0; i < m_particles.size(); i++)
{
//if(Intersect::Lineseg_Sphere(Lineseg(p1, p2), Sphere (m_particles[i]->GetPosition(), m_particles[i]->GetRadiusX()*(2.0f/3)), outp, outp2))
if (Intersect::Lineseg_Sphere(Lineseg(p1, p2), Sphere (m_particles[i]->GetPosition(), m_particles[i]->GetRadiusX()), outp, outp2))
{
//Sp_Pos = m_particles[i]->GetPosition();
//Sp_Rad = m_particles[i]->GetRadiusX()/2;
return true;
}
}
}
}
return false;
}
/*
//!!! WARNING
extern bool Sp_IsDraw;
extern Matrix34 Sp_Mat;
extern Vec3 Sp_Pos;
extern float Sp_Rad;
void C3DEngine::Hack_GetSprite(bool & IsDraw, Matrix34 & Mat, Vec3 & Pos, float & Rad)
{
IsDraw = Sp_IsDraw;
Mat = Sp_Mat;
Pos = Sp_Pos;
Rad = Sp_Rad;
}
*/
-116
View File
@@ -1,116 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_CLOUDSMANAGER_H
#define CRYINCLUDE_CRY3DENGINE_CLOUDSMANAGER_H
#pragma once
class CCloudRenderNode;
typedef std::vector<SCloudParticle*> CloudParticles;
struct SCloudQuadTree
{
CloudParticles m_particles;
AABB m_bounds;
SCloudQuadTree* m_pQuads[4];
int m_level;
SCloudQuadTree(int level = 0)
{
m_pQuads[0] = 0;
m_pQuads[1] = 0;
m_pQuads[2] = 0;
m_pQuads[3] = 0;
m_level = level;
}
~SCloudQuadTree()
{
delete m_pQuads[0];
delete m_pQuads[1];
delete m_pQuads[2];
delete m_pQuads[3];
m_pQuads[0] = m_pQuads[1] = m_pQuads[2] = m_pQuads[3] = 0;
}
void Init(const AABB& bounds, const CloudParticles& particles, int maxlevel = 2);
bool CheckIntersection(const Vec3& p1, const Vec3& p2);
};
//////////////////////////////////////////////////////////////////////////
// SCloudDescription contains cached representation of the cloud description file.
//////////////////////////////////////////////////////////////////////////
struct SCloudDescription
: public _reference_target_t
, public Cry3DEngineBase
{
string filename;
int m_textureRows;
int m_textureCols;
int m_numSprites;
AABB m_bounds;
Vec3 m_offset;
_smart_ptr<IMaterial> m_pMaterial;
std::vector<SCloudParticle> m_particles;
SCloudQuadTree* m_pCloudTree;
SCloudDescription()
{
m_textureRows = 0;
m_textureCols = 0;
m_numSprites = 0;
m_pCloudTree = 0;
};
~SCloudDescription();
};
//////////////////////////////////////////////////////////////////////////
// CloudsManager is used to manage cloud descriptions loaded from the files.
// When cloud file is once loaded it caches its content and next time the same
// cloud file is request, Clients will get the cached content.
//////////////////////////////////////////////////////////////////////////
class CCloudsManager
: public Cry3DEngineBase
{
public:
CCloudsManager() {};
~CCloudsManager() {};
// Loads cloud file and returns cloud description.
// If cloud was already loaded cached instance is returned.
// Reference count of the cloud description is incremented,Client must call Release on returned pointer to free cloud description.
SCloudDescription* LoadCloud(const char* sFilename);
// Used to parse xml node and create a cloud description from it.
void ParseCloudFromXml(XmlNodeRef node, SCloudDescription* pCloud);
void AddCloudRenderNode(CCloudRenderNode* pNode);
void RemoveCloudRenderNode(CCloudRenderNode* pNode);
bool CheckIntersectClouds(const Vec3& p1, const Vec3& p2);
void MoveClouds();
private:
friend struct SCloudDescription;
void Register(SCloudDescription* desc);
void Unregister(SCloudDescription* desc);
private:
typedef std::map<string, SCloudDescription*, stl::less_stricmp<string> > CloudsMaps;
CloudsMaps m_cloudsMap;
std::vector<CCloudRenderNode*> m_cloudNodes;
};
#endif // CRYINCLUDE_CRY3DENGINE_CLOUDSMANAGER_H
-176
View File
@@ -1,176 +0,0 @@
/*
* 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.
// Description : Defines the DLL entry point, implements access to other modules
#include "Cry3DEngine_precompiled.h"
#include "MatMan.h"
#include <IEngineModule.h>
#include <CryExtension/Impl/ClassWeaver.h>
#include "I3DEngine_info.h"
//////////////////////////////////////////////////////////////////////
struct CSystemEventListner_3DEngine
: public ISystemEventListener
{
public:
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_LEVEL_PRECACHE_START:
if (Cry3DEngineBase::Get3DEngine())
{
Cry3DEngineBase::Get3DEngine()->ClearPrecacheInfo();
}
break;
case ESYSTEM_EVENT_RANDOM_SEED:
cry_random_seed(gEnv->bNoRandomSeed ? 0 : (uint32)wparam);
break;
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
{
STLALLOCATOR_CLEANUP;
if (Cry3DEngineBase::Get3DEngine())
{
Cry3DEngineBase::Get3DEngine()->ClearDebugFPSInfo(true);
}
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
if (Cry3DEngineBase::Get3DEngine())
{
Cry3DEngineBase::Get3DEngine()->ClearDebugFPSInfo();
}
if (Cry3DEngineBase::GetObjManager())
{
Cry3DEngineBase::GetObjManager()->FreeNotUsedCGFs();
}
Cry3DEngineBase::m_bLevelLoadingInProgress = false;
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_START:
{
Cry3DEngineBase::m_bLevelLoadingInProgress = true;
break;
}
case ESYSTEM_EVENT_LEVEL_UNLOAD:
{
Cry3DEngineBase::m_bLevelLoadingInProgress = true;
break;
}
case ESYSTEM_EVENT_3D_POST_RENDERING_START:
{
Cry3DEngineBase::GetMatMan()->DoLoadSurfaceTypesInInit(false);
break;
}
case ESYSTEM_EVENT_3D_POST_RENDERING_END:
{
if (Cry3DEngineBase::Get3DEngine()->GetObjectTree())
{
delete Cry3DEngineBase::Get3DEngine()->GetObjectTree();
Cry3DEngineBase::Get3DEngine()->SetObjectTree(nullptr);
}
if (IObjManager* pObjManager = Cry3DEngineBase::GetObjManager())
{
pObjManager->UnloadObjects(true);
}
if (Cry3DEngineBase::GetMatMan())
{
Cry3DEngineBase::GetMatMan()->ShutDown();
Cry3DEngineBase::GetMatMan()->DoLoadSurfaceTypesInInit(true);
}
break;
}
}
}
};
static CSystemEventListner_3DEngine g_system_event_listener_engine;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class CEngineModule_Cry3DEngine
: public IEngineModule
{
CRYINTERFACE_SIMPLE(IEngineModule)
CRYGENERATE_SINGLETONCLASS(CEngineModule_Cry3DEngine, "EngineModule_Cry3DEngine", 0x2d38f12a521d43cf, 0xba18fd1fa7ea5020)
//////////////////////////////////////////////////////////////////////////
virtual const char* GetName() const {
return "Cry3DEngine";
};
virtual const char* GetCategory() const { return "CryEngine"; };
//////////////////////////////////////////////////////////////////////////
virtual bool Initialize(SSystemGlobalEnvironment& env, [[maybe_unused]] const SSystemInitParams& initParams)
{
ISystem* pSystem = env.pSystem;
ModuleInitISystem(pSystem, "Cry3DEngine");
pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_engine);
C3DEngine* p3DEngine = CryAlignedNew<C3DEngine>(pSystem);
env.p3DEngine = p3DEngine;
return true;
}
};
CRYREGISTER_SINGLETON_CLASS(CEngineModule_Cry3DEngine)
CEngineModule_Cry3DEngine::CEngineModule_Cry3DEngine()
{
};
CEngineModule_Cry3DEngine::~CEngineModule_Cry3DEngine()
{
};
#if !defined(AZ_MONOLITHIC_BUILD)
#include <CrtDebugStats.h>
#endif
#include "TypeInfo_impl.h"
// 3DEngine types
#include "SkyLightNishita_info.h"
STRUCT_INFO_BEGIN(SImageSubInfo)
VAR_INFO(nDummy)
VAR_INFO(nDim)
VAR_INFO(fTilingIn)
VAR_INFO(fTiling)
VAR_INFO(fSpecularAmount)
VAR_INFO(nSortOrder)
STRUCT_INFO_END(SImageSubInfo)
STRUCT_INFO_BEGIN(SImageInfo)
VAR_INFO(baseInfo)
VAR_INFO(detailInfo)
VAR_INFO(szDetMatName)
VAR_INFO(arrTextureId)
VAR_INFO(nPhysSurfaceType)
VAR_INFO(szBaseTexName)
VAR_INFO(fUseRemeshing)
VAR_INFO(layerFilterColor)
VAR_INFO(nLayerId)
VAR_INFO(fBr)
STRUCT_INFO_END(SImageInfo)
-111
View File
@@ -1,111 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Russian resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Russian resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// German (Germany) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
#ifdef _WIN32
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000904b0"
BEGIN
VALUE "CompanyName", "Amazon.com, Inc."
VALUE "FileVersion", "1, 0, 0, 1"
VALUE "LegalCopyright", "Portions of this file Copyright (c) Amazon.com, Inc. or its affiliates. All Rights Reserved. Original file Copyright (c) Crytek GMBH. Used under license by Amazon.com, Inc. and its affiliates."
VALUE "ProductName", "Lumberyard"
VALUE "ProductVersion", "1, 0, 0, 1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x9, 1200
END
END
#endif // German (Germany) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -1,237 +0,0 @@
/*
* 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.
*
*/
#include "Cry3DEngine_precompiled.h"
#include <Cry3DEngineBase.h>
#define MAX_ERROR_STRING MAX_WARNING_LENGTH
//////////////////////////////////////////////////////////////////////////
void Cry3DEngineBase::PrintComment(const char* szText, ...)
{
if (!szText)
{
return;
}
va_list args;
va_start(args, szText);
GetLog()->LogV(IMiniLog::eComment, szText, args);
va_end(args);
}
void Cry3DEngineBase::PrintMessage(const char* szText, ...)
{
if (!szText)
{
return;
}
va_list args;
va_start(args, szText);
GetLog()->LogV(GetCVars()->e_3dEngineLogAlways ? IMiniLog::eAlways : IMiniLog::eMessage, szText, args);
va_end(args);
GetLog()->UpdateLoadingScreen(0);
}
void Cry3DEngineBase::PrintMessagePlus(const char* szText, ...)
{
if (!szText)
{
return;
}
va_list arglist;
char buf[MAX_ERROR_STRING];
va_start(arglist, szText);
int count = azvsnprintf(buf, sizeof(buf), szText, arglist);
if (count == -1 || count >= sizeof(buf))
{
buf[sizeof(buf) - 1] = '\0';
}
va_end(arglist);
GetLog()->LogPlus(buf);
GetLog()->UpdateLoadingScreen(0);
}
float Cry3DEngineBase::GetCurTimeSec()
{
return (gEnv->pTimer->GetCurrTime());
}
float Cry3DEngineBase::GetCurAsyncTimeSec()
{
return (gEnv->pTimer->GetAsyncTime().GetSeconds());
}
//////////////////////////////////////////////////////////////////////////
void Cry3DEngineBase::Warning(const char* format, ...)
{
if (!format)
{
return;
}
va_list args;
va_start(args, format);
// Call to validating warning of system.
m_pSystem->WarningV(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, 0, 0, format, args);
va_end(args);
GetLog()->UpdateLoadingScreen(0);
}
//////////////////////////////////////////////////////////////////////////
void Cry3DEngineBase::Error(const char* format, ...)
{
// assert(!"Cry3DEngineBase::Error");
if (format)
{
va_list args;
va_start(args, format);
// Call to validating warning of system.
m_pSystem->WarningV(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, 0, 0, format, args);
va_end(args);
}
GetLog()->UpdateLoadingScreen(0);
}
//////////////////////////////////////////////////////////////////////////
void Cry3DEngineBase::FileWarning(int flags, const char* file, const char* format, ...)
{
if (format)
{
va_list args;
va_start(args, format);
// Call to validating warning of system.
m_pSystem->WarningV(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, flags | VALIDATOR_FLAG_FILE, file, format, args);
va_end(args);
}
GetLog()->UpdateLoadingScreen(0);
}
_smart_ptr<IMaterial> Cry3DEngineBase::MakeSystemMaterialFromShader(const char* sShaderName, SInputShaderResources* Res)
{
_smart_ptr<IMaterial> pMat = Get3DEngine()->GetMaterialManager()->CreateMaterial(sShaderName);
//pMat->AddRef();
SShaderItem si;
si = GetRenderer()->EF_LoadShaderItem(sShaderName, true, 0, Res);
pMat->AssignShaderItem(si);
return pMat;
}
//////////////////////////////////////////////////////////////////////////
bool Cry3DEngineBase::IsValidFile(const char* sFilename)
{
LOADING_TIME_PROFILE_SECTION;
return gEnv->pCryPak->IsFileExist(sFilename);
}
//////////////////////////////////////////////////////////////////////////
bool Cry3DEngineBase::IsResourceLocked(const char* sFilename)
{
auto pResList = GetPak()->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel);
if (pResList)
{
return pResList->IsExist(sFilename);
}
return false;
}
void Cry3DEngineBase::DrawBBoxLabeled(const AABB& aabb, const Matrix34& m34, const ColorB& col, const char* format, ...)
{
va_list args;
va_start(args, format);
char szText[256];
vsnprintf_s(szText, sizeof(szText), sizeof(szText) - 1, format, args);
float fColor[4] = { col[0] / 255.f, col[1] / 255.f, col[2] / 255.f, col[3] / 255.f };
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->DrawLabelEx(m34.TransformPoint(aabb.GetCenter()), 1.3f, fColor, true, true, szText);
GetRenderer()->GetIRenderAuxGeom()->DrawAABB(aabb, m34, false, col, eBBD_Faceted);
va_end(args);
}
//////////////////////////////////////////////////////////////////////////
void Cry3DEngineBase::DrawBBox(const Vec3& vMin, const Vec3& vMax, ColorB col)
{
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->GetIRenderAuxGeom()->DrawAABB(AABB(vMin, vMax), false, col, eBBD_Faceted);
}
void Cry3DEngineBase::DrawBBox(const AABB& box, ColorB col)
{
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->GetIRenderAuxGeom()->DrawAABB(box, false, col, eBBD_Faceted);
}
void Cry3DEngineBase::DrawLine(const Vec3& vMin, const Vec3& vMax, ColorB col)
{
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->GetIRenderAuxGeom()->DrawLine(vMin, col, vMax, col);
}
void Cry3DEngineBase::DrawSphere(const Vec3& vPos, float fRadius, ColorB color)
{
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->GetIRenderAuxGeom()->DrawSphere(vPos, fRadius, color);
}
void Cry3DEngineBase::DrawQuad(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3, ColorB color)
{
GetRenderer()->GetIRenderAuxGeom()->SetRenderFlags(SAuxGeomRenderFlags());
GetRenderer()->GetIRenderAuxGeom()->DrawTriangle(v0, color, v2, color, v3, color);
GetRenderer()->GetIRenderAuxGeom()->DrawTriangle(v0, color, v1, color, v2, color);
}
// Check if preloading is enabled.
bool Cry3DEngineBase::IsPreloadEnabled()
{
bool bPreload = false;
ICVar* pSysPreload = GetConsole()->GetCVar("sys_preload");
if (pSysPreload && pSysPreload->GetIVal() != 0)
{
bPreload = true;
}
return bPreload;
}
//////////////////////////////////////////////////////////////////////////
bool Cry3DEngineBase::CheckMinSpec(uint32 nMinSpec)
{
if ((int)nMinSpec != 0 && GetCVars()->e_ObjQuality != 0 && (int)nMinSpec > GetCVars()->e_ObjQuality)
{
return false;
}
return true;
}
bool Cry3DEngineBase::IsEscapePressed()
{
#ifdef WIN32
if (Cry3DEngineBase::m_bEditor && (CryGetAsyncKeyState(0x03) & 1)) // Ctrl+Break
{
Get3DEngine()->PrintMessage("*** Ctrl-Break was pressed - operation aborted ***");
return true;
}
#endif
return false;
}
@@ -1,202 +0,0 @@
/*
* 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.
// Description : Access to external stuff used by 3d engine. Most 3d engine classes
// are derived from this base class to access other interfaces
#ifndef CRYINCLUDE_CRY3DENGINE_CRY3DENGINEBASE_H
#define CRYINCLUDE_CRY3DENGINE_CRY3DENGINEBASE_H
#pragma once
#include "3DEngineMemory.h"
struct ISystem;
struct IRenderer;
struct ILog;
struct ITimer;
struct IConsole;
struct I3DEngine;
struct IObjManager;
struct CVars;
struct CVisAreaManager;
class COcean;
class C3DEngine;
class CParticleManager;
class CDecalManager;
class CRainManager;
class CCloudsManager;
class CSkyLightManager;
class CRenderMeshMerger;
class CMergedMeshesManager;
class CGeomCacheManager;
class CBreezeGenerator;
class CMatMan;
class CClipVolumeManager;
namespace AZ::IO
{
struct IArchive;
}
#define DISTANCE_TO_THE_SUN 1000000
#if !defined(_RELEASE)
#define OBJMAN_STREAM_STATS
#endif
struct Cry3DEngineBase
{
static ISystem* m_pSystem;
static IRenderer* m_pRenderer;
static ITimer* m_pTimer;
static ILog* m_pLog;
static ::IConsole* m_pConsole;
static C3DEngine* m_p3DEngine;
static CVars* m_pCVars;
static AZ::IO::IArchive* m_pCryPak;
static CObjManager* m_pObjManager;
static COcean* m_pOcean;
static IOpticsManager* m_pOpticsManager;
static CDecalManager* m_pDecalManager;
static CCloudsManager* m_pCloudsManager;
static CVisAreaManager* m_pVisAreaManager;
static CClipVolumeManager* m_pClipVolumeManager;
static CMatMan* m_pMatMan;
static CSkyLightManager* m_pSkyLightManager;
static CRenderMeshMerger* m_pRenderMeshMerger;
static IStreamedObjectListener* m_pStreamListener;
#if defined(USE_GEOM_CACHES)
static CGeomCacheManager* m_pGeomCacheManager;
#endif
static float m_fInvDissolveDistBand;
static threadID m_nMainThreadId;
static bool m_bLevelLoadingInProgress;
static bool m_bIsInRenderScene;
static bool m_bAsyncOctreeUpdates;
static bool m_bRenderTypeEnabled[eERType_TypesNum];
static int m_CpuFlags;
static ESystemConfigSpec m_LightConfigSpec;
#if defined(CONSOLE)
static const bool m_bEditor = false;
#else
static bool m_bEditor;
#endif
static int m_arrInstancesCounter[eERType_TypesNum];
// components access
ILINE static ISystem* GetSystem() { return m_pSystem; }
ILINE static IRenderer* GetRenderer() { return m_pRenderer; }
ILINE static ITimer* GetTimer() { return m_pTimer; }
ILINE static ILog* GetLog() { return m_pLog; }
inline static ::IConsole* GetConsole() { return m_pConsole; }
inline static C3DEngine* Get3DEngine() { return m_p3DEngine; }
inline static CObjManager* GetObjManager() { return m_pObjManager; };
inline static COcean* GetOcean() { return m_pOcean; };
inline static CVars* GetCVars() { return m_pCVars; }
inline static CVisAreaManager* GetVisAreaManager() { return m_pVisAreaManager; }
inline static AZ::IO::IArchive* GetPak() { return m_pCryPak; }
inline static CMatMan* GetMatMan() { return m_pMatMan; }
inline static CCloudsManager* GetCloudsManager() { return m_pCloudsManager; }
inline static CRenderMeshMerger* GetSharedRenderMeshMerger() { return m_pRenderMeshMerger; };
inline static CTemporaryPool* GetTemporaryPool() { return CTemporaryPool::Get(); };
#if defined(USE_GEOM_CACHES)
inline static CGeomCacheManager* GetGeomCacheManager() { return m_pGeomCacheManager; };
#endif
ILINE static bool IsRenderNodeTypeEnabled(EERType rnType) { return m_bRenderTypeEnabled[(int)rnType]; }
ILINE static void SetRenderNodeTypeEnabled(EERType rnType, bool bEnabled) {m_bRenderTypeEnabled[(int)rnType] = bEnabled; }
inline static int GetDefSID() { return DEFAULT_SID; };
float GetCurTimeSec();
float GetCurAsyncTimeSec();
static void PrintMessage(const char* szText, ...) PRINTF_PARAMS(1, 2);
static void PrintMessagePlus(const char* szText, ...) PRINTF_PARAMS(1, 2);
static void PrintComment(const char* szText, ...) PRINTF_PARAMS(1, 2);
// Validator warning.
static void Warning(const char* format, ...) PRINTF_PARAMS(1, 2);
static void Error(const char* format, ...) PRINTF_PARAMS(1, 2);
static void FileWarning(int flags, const char* file, const char* format, ...)
PRINTF_PARAMS(3, 4);
CRenderObject* GetIdentityCRenderObject(int nThreadID)
{
CRenderObject* pCRenderObject = GetRenderer()->EF_GetObject_Temp(nThreadID);
if (!pCRenderObject)
{
return NULL;
}
pCRenderObject->m_II.m_Matrix.SetIdentity();
return pCRenderObject;
}
static bool IsValidFile(const char* sFilename);
static bool IsResourceLocked(const char* sFilename);
static bool IsPreloadEnabled();
_smart_ptr<IMaterial> MakeSystemMaterialFromShader(const char* sShaderName, SInputShaderResources* Res = NULL);
void DrawBBoxLabeled(const AABB& aabb, const Matrix34& m34, const ColorB& col, const char* format, ...) PRINTF_PARAMS(5, 6);
void DrawBBox(const Vec3& vMin, const Vec3& vMax, ColorB col = Col_White);
void DrawBBox(const AABB& box, ColorB col = Col_White);
void DrawLine(const Vec3& vMin, const Vec3& vMax, ColorB col = Col_White);
void DrawSphere(const Vec3& vPos, float fRadius, ColorB color = ColorB(255, 255, 255, 255));
void DrawQuad(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3, ColorB color);
int& GetInstCount(EERType eType) { return m_arrInstancesCounter[eType]; }
uint32 GetMinSpecFromRenderNodeFlags(uint32 dwRndFlags) const { return (dwRndFlags & ERF_SPEC_BITS_MASK) >> ERF_SPEC_BITS_SHIFT; }
static bool CheckMinSpec(uint32 nMinSpec);
static bool IsEscapePressed();
size_t fread(
void* buffer,
size_t elementSize,
size_t count,
FILE* stream)
{
size_t res = ::fread(buffer, elementSize, count, stream);
if (res != count)
{
Error("fread() failed");
}
return res;
}
int fseek (
FILE* stream,
long offset,
int whence
)
{
int res = ::fseek(stream, offset, whence);
if (res != 0)
{
Error("fseek() failed");
}
return res;
}
};
#endif // CRYINCLUDE_CRY3DENGINE_CRY3DENGINEBASE_H
@@ -1,35 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include "ProjectDefines.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(Cry3DEngineTraits_h)
#else
#if defined(WIN32) || defined(WIN64)
#define AZ_LEGACY_3DENGINE_TRAIT_DEFINE_MM_MULLO_EPI32_EMU 1
#endif
#if defined(WIN32) || defined(WIN64)
#define AZ_LEGACY_3DENGINE_TRAIT_DO_EXTRA_GEOMCACHE_PROCESSING 1 // probably needs a better name
#endif
#define AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_CVTEPI16_EPI32 0
#define AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_MULLO_EPI32 0
#define AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_PACKUS_EPI32 0
#if defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(MAC)
#define AZ_LEGACY_3DENGINE_TRAIT_HAS_SSE 1
#endif
#define AZ_LEGACY_3DENGINE_TRAIT_UNROLL_GEOMETRY_BACKING_LOOPS 1
#if defined(APPLE) || defined(LINUX)
#define AZ_LEGACY_3DENGINE_TRAIT_DISABLE_MMRM_SSE_INSTRUCTIONS 1
#endif
#endif
@@ -1,14 +0,0 @@
/*
* 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"
@@ -1,186 +0,0 @@
/*
* 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.
#pragma once
const int nThreadsNum = 3;
#include <platform.h>
#include <vector>
//#define DEFINE_MODULE_NAME "Cry3DEngine"
//#define FORCE_STANDARD_ASSERT // fix edit and continue
#if defined(WIN64)
#define CRY_INTEGRATE_DX12
#endif
//////////////////////////////////////////////////////////////////////////////////////////////
// Highlevel defines
// deferred cull queue handling - currently disabled
// #define USE_CULL_QUEUE
// Compilation (Export to Engine) not needed on consoles
#if defined(CONSOLE)
# define ENGINE_ENABLE_COMPILATION 0
#else
# define ENGINE_ENABLE_COMPILATION 1
#endif
#include <stdio.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Casting/lossy_cast.h>
#define MAX_PATH_LENGTH 512
#include <ITimer.h>
#include <IProcess.h>
#include <Cry_Math.h>
#include <Cry_Camera.h>
#include <Cry_XOptimise.h>
#include <Cry_Geo.h>
#include <ILog.h>
#include <ISystem.h>
#include <IConsole.h>
#include <IPhysics.h>
#include <IRenderer.h>
#include <IRenderAuxGeom.h>
#include <IEntityRenderState.h>
#include <StackContainer.h>
#include <I3DEngine.h>
#include <CryFile.h>
#include <smartptr.h>
#include <CryArray.h>
#include <CryHeaders.h>
#include "Cry3DEngineBase.h"
#include <float.h>
#include "CryArray.h"
#include "cvars.h"
#include <CrySizer.h>
#include <StlUtils.h>
#include <CryArray2d.h>
#include "Material.h"
#include "3dEngine.h"
#include "ObjMan.h"
#include <ISerialize.h>
#include "BasicArea.h"
#include "Environment/OceanEnvironmentBus.h"
#include "ObjectsTree.h"
inline int snprintf(char* buf, int size, const char* format, ...)
{
va_list arglist;
va_start(arglist, format);
int res = azvsnprintf(buf, size, format, arglist);
va_end(arglist);
return res;
}
template <class T>
void AddToPtr(byte*& pPtr, T& rObj, EEndian eEndian)
{
PREFAST_SUPPRESS_WARNING(6326) COMPILE_TIME_ASSERT(((sizeof(T) % 4) == 0));
assert(!((INT_PTR)pPtr & 3));
memcpy(pPtr, &rObj, sizeof(rObj));
SwapEndian(*(T*)pPtr, eEndian);
pPtr += sizeof(rObj);
assert(!((INT_PTR)pPtr & 3));
}
template <class T>
void AddToPtr(byte*& pPtr, int& nDataSize, T& rObj, EEndian eEndian)
{
PREFAST_SUPPRESS_WARNING(6326) COMPILE_TIME_ASSERT(((sizeof(T) % 4) == 0));
assert(!((INT_PTR)pPtr & 3));
memcpy(pPtr, &rObj, sizeof(rObj));
SwapEndian(*(T*)pPtr, eEndian);
pPtr += sizeof(rObj);
nDataSize -= sizeof(rObj);
assert(nDataSize >= 0);
assert(!((INT_PTR)pPtr & 3));
}
inline void FixAlignment(byte*& pPtr, int& nDataSize)
{
while ((UINT_PTR)pPtr & 3)
{
*pPtr = 222;
pPtr++;
nDataSize--;
}
}
inline void FixAlignment(byte*& pPtr)
{
while ((UINT_PTR)pPtr & 3)
{
*pPtr = 222;
pPtr++;
}
}
template <class T>
void AddToPtr(byte*& pPtr, int& nDataSize, const T* pArray, int nElemNum, EEndian eEndian, bool bFixAlignment = false)
{
assert(!((INT_PTR)pPtr & 3));
memcpy(pPtr, pArray, nElemNum * sizeof(T));
SwapEndian((T*)pPtr, nElemNum, eEndian);
pPtr += nElemNum * sizeof(T);
nDataSize -= nElemNum * sizeof(T);
assert(nDataSize >= 0);
if (bFixAlignment)
{
FixAlignment(pPtr, nDataSize);
}
else
{
assert(!((INT_PTR)pPtr & 3));
}
}
template <class T>
void AddToPtr(byte*& pPtr, const T* pArray, int nElemNum, EEndian eEndian, bool bFixAlignment = false)
{
assert(!((INT_PTR)pPtr & 3));
memcpy(pPtr, pArray, nElemNum * sizeof(T));
SwapEndian((T*)pPtr, nElemNum, eEndian);
pPtr += nElemNum * sizeof(T);
if (bFixAlignment)
{
FixAlignment(pPtr);
}
else
{
assert(!((INT_PTR)pPtr & 3));
}
}
struct TriangleIndex
{
TriangleIndex() { ZeroStruct(*this); }
uint16& operator [] (const int& n) { assert(n >= 0 && n < 3); return idx[n]; }
const uint16& operator [] (const int& n) const { assert(n >= 0 && n < 3); return idx[n]; }
uint16 idx[3];
uint16 nCull;
};
#define FUNCTION_PROFILER_3DENGINE FUNCTION_PROFILER(gEnv->pSystem, PROFILE_3DENGINE)
#define FUNCTION_PROFILER_3DENGINE_LEGACYONLY FUNCTION_PROFILER_LEGACYONLY(gEnv->pSystem, PROFILE_3DENGINE)
@@ -1,990 +0,0 @@
/*
* 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.
*
*/
#include <Cry3DEngine/Cry_LegacyPhysUtils.h>
namespace LegacyCryPhysicsUtils
{
namespace qhull_IMPL
{
static int __qhullcalled = 0;
int qhull(strided_pointer<Vec3> _pts, int npts, index_t*& pTris, qhullmalloc qmalloc)
{
#if defined(PLATFORM_64BIT)
static ptitem ptbuf[4096];
static qhtritem trbuf[4096];
static qhtritem* tmparr_ptr_buf[2048];
static int tmparr_idx_buf[2048];
#else
static ptitem ptbuf[1024];
static qhtritem trbuf[1024];
static qhtritem* tmparr_ptr_buf[512];
static int tmparr_idx_buf[512];
#endif
static volatile int g_lockQhull;
int iter = 0, maxiter = 0;
__qhullcalled++;
strided_pointer<Vec3mem> pts = strided_pointer<Vec3mem>((Vec3mem*)_pts.data, _pts.iStride);
ptitem* pt, * ptmax, * ptdeleted, * ptlist = npts > sizeof(ptbuf) / sizeof(ptbuf[0]) ? new ptitem[npts] : ptbuf;
qhtritem* tr, * trnext, * trend, * trnew, * trdata = trbuf, * trstart = 0, * trlast, * trbest = nullptr;
int i, j, k, ti, trdatasz = sizeof(trbuf) / sizeof(trbuf[0]), bidx[6], n, next_iter, delbuds;
qhtritem** tmparr_ptr = tmparr_ptr_buf;
int* tmparr_idx = tmparr_idx_buf, tmparr_sz = 512;
float dist, maxdist /*,e*/;
Vec3 pmin(VMAX), pmax(VMIN);
WriteLock lock(g_lockQhull);
// select points for initial tetrahedron
// first, find 6 points corresponding to min and max coordinates
for (i = 1; i < npts; i++)
{
if (pts[i].x > pmax.x)
{
pmax.x = pts[i].x;
bidx[0] = i;
}
if (pts[i].x < pmin.x)
{
pmin.x = pts[i].x;
bidx[1] = i;
}
if (pts[i].y > pmax.y)
{
pmax.y = pts[i].y;
bidx[2] = i;
}
if (pts[i].y < pmin.y)
{
pmin.y = pts[i].y;
bidx[3] = i;
}
if (pts[i].z > pmax.z)
{
pmax.z = pts[i].z;
bidx[4] = i;
}
if (pts[i].z < pmin.z)
{
pmin.z = pts[i].z;
bidx[5] = i;
}
}
// e = max(max(pmax.x-pmin.x,pmax.y-pmin.y),pmax.z-pmin.z)*0.01f;
for (bidx[0] = 0, i = 1; i < npts; i++)
{
bidx[0] += i - bidx[0] & -isneg(pts[i].x - pts[bidx[0]].x);
}
for (bidx[1] = 0, i = 1; i < npts; i++)
{
bidx[1] += i - bidx[1] & -isneg((pts[bidx[1]] - pts[bidx[0]]).len2() - (pts[i] - pts[bidx[0]]).len2());
}
Vec3 norm = pts[bidx[1]] - pts[bidx[0]];
for (bidx[2] = 0, i = 1; i < npts; i++)
{
bidx[2] += i - bidx[2] & -isneg((norm ^ pts[bidx[2]] - pts[bidx[0]]).len2() - (norm ^ pts[i] - pts[bidx[0]]).len2());
}
norm = pts[bidx[1]] - pts[bidx[0]] ^ pts[bidx[2]] - pts[bidx[0]];
for (bidx[3] = 0, i = 1; i < npts; i++)
{
bidx[3] += i - bidx[3] & -isneg(fabs_tpl((pts[bidx[3]] - pts[bidx[0]]) * norm) - fabs_tpl((pts[i] - pts[bidx[0]]) * norm));
}
if ((pts[bidx[3]] - pts[bidx[0]]) * norm > 0)
{
i = bidx[1];
bidx[1] = bidx[2];
bidx[2] = i;
}
// build a double linked list from all points
for (i = 0; i < npts; i++)
{
ptlist[i].prev = ptlist + i - 1;
ptlist[i].next = ptlist + i + 1;
}
ptlist[0].prev = ptlist + npts - 1;
ptlist[npts - 1].next = ptlist;
// remove selected points from the list
for (i = 0; i < 4; i++)
{
delete_item_from_list(ptlist + bidx[i]);
}
// assign 3 points to each of 4 initial triangles
for (i = 0; i < 4; i++)
{
for (j = k = 0; j < 4; j++)
{
if (j != i)
{
trbuf[i].idx[k++] = bidx[j]; // skip i.th point in i.th triangle
}
}
trbuf[i].n = pts[trbuf[i].idx[1]] - pts[trbuf[i].idx[0]] ^ pts[trbuf[i].idx[2]] - pts[trbuf[i].idx[0]];
trbuf[i].pt0 = pts[trbuf[i].idx[0]];
if (e_cansee(pts[bidx[i]] - trbuf[i].pt0, trbuf[i].n, 0)) // flip the orientation so that ccw normal points outwards
{
ti = trbuf[i].idx[0];
trbuf[i].idx[0] = trbuf[i].idx[2];
trbuf[i].idx[2] = ti;
trbuf[i].n = -trbuf[i].n;
}
trbuf[i].ptassoc = 0;
trbuf[i].deleted = 0;
add_item_to_list(trstart, trbuf + i);
}
// fill buddy links for each triangle
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (j != i)
{
for (k = 0; k < 3; k++)
{
for (ti = 0; ti < 3; ti++)
{
if (trbuf[i].idx[k] == trbuf[j].idx[ti] && trbuf[i].idx[k == 2 ? 0 : k + 1] == trbuf[j].idx[ti == 0 ? 2 : ti - 1])
{
trbuf[i].buddy[k] = trbuf + j;
break;
}
}
}
}
}
}
trend = trstart + 4;
for (i = 0; i < 4; i++)
{
if (trbuf[i].n.len2() < 1E-6f)
{
#ifdef _DEBUG
//OutputDebugString("WARNING: convex hull not computed because of degenerate initial triangles\n");
#endif
n = 0;
goto endqhull; // some degenerate case, don't party with it
}
}
// associate points with one of the initial triangles
for (i = 0; i < npts; i++)
{
if (ptlist[i].next)
{
break;
}
}
associate_ptlist_with_trilist(ptlist + i, trstart, ptlist, pts);
#define DELETE_TRI(ptri) { \
merge_lists(ptdeleted, (ptri)->ptassoc); \
if ((ptri) == trstart) {trstart = (ptri)->next; } \
if ((ptri) == trnext) {trnext = (ptri)->next; } \
delete_item_from_list(ptri); (ptri)->deleted = 1; }
// main loop
iter = 0;
maxiter = npts * npts * 2;
ptmax = trstart->ptassoc;
tr = trstart;
do
{
trnext = tr->next;
pt = tr->ptassoc;
if (pt)
{
// find the fartherst of the associated with the triangle points
maxdist = -1E37f;
do
{
if ((dist = pts[(int)(pt - ptlist)] * tr->n) > maxdist)
{
maxdist = dist;
ptmax = pt;
}
pt = pt->next;
} while (pt != tr->ptassoc);
ptdeleted = 0;
if (tr->ptassoc == ptmax)
{
tr->ptassoc = ptmax->next;
}
delete_item_from_list(ptmax);
if (tr->ptassoc == ptmax)
{
tr->ptassoc = 0;
}
// find the triangle that the point can see "most confidently"
tr = trstart;
trlast = tr->prev;
ti = static_cast<int>(ptmax - ptlist);
maxdist = -1E37f;
do
{
trnext = tr->next;
if ((pts[ti] - tr->pt0) * tr->n > maxdist)
{
maxdist = (pts[ti] - tr->pt0) * tr->n;
trbest = tr;
}
if (tr == trlast)
{
break;
}
tr = trnext;
} while (true);
// "flood fill" triangles that the point can see around that one
DELETE_TRI(trbest)
tr = trbest->next = trbest->prev = trbest;
do
{
if (tr->buddy[0] && !tr->buddy[0]->deleted && e_cansee(pts[ti] - tr->buddy[0]->pt0, tr->buddy[0]->n, 0))
{
DELETE_TRI(tr->buddy[0])
add_item_to_list(tr, tr->buddy[0]);
}
if (tr->buddy[1] && !tr->buddy[1]->deleted && e_cansee(pts[ti] - tr->buddy[1]->pt0, tr->buddy[1]->n, 0))
{
DELETE_TRI(tr->buddy[1])
add_item_to_list(tr, tr->buddy[1]);
}
if (tr->buddy[2] && !tr->buddy[2]->deleted && e_cansee(pts[ti] - tr->buddy[2]->pt0, tr->buddy[2]->n, 0))
{
DELETE_TRI(tr->buddy[2])
add_item_to_list(tr, tr->buddy[2]);
}
tr = tr->next;
} while (tr != trbest);
// delete near-visible triangles around deleted area edges to preserve hole convexity
// do as many iterations as needed
do
{
tr = trstart;
trlast = tr->prev;
next_iter = 0;
do
{
trnext = tr->next;
if (e_cansee(pts[ti] - tr->pt0, tr->n, -0.001f))
{
delbuds = tr->buddy[0]->deleted + tr->buddy[1]->deleted + tr->buddy[2]->deleted;
if (delbuds >= 2) // delete triangles that have 2+ buddies deleted
{
if (tr == trlast)
{
trlast = tr->next;
}
DELETE_TRI(tr);
next_iter = 1;
}
else if (delbuds == 1) // follow triangle fan around both shared edge ends
{
int bi, bi0, bi1, nfantris, fandir;
qhtritem* fantris[64], * tr1;
for (bi0 = 0; bi0 < 3 && !tr->buddy[bi0]->deleted; bi0++)
{
; // bi0 - deleted buddy index
}
for (fandir = -1; fandir <= 1; fandir += 2) // follow fans in 2 possible directions
{
tr1 = tr;
bi1 = bi0;
nfantris = 0;
do
{
if (nfantris == 64)
{
break;
}
bi = bi1 + fandir;
if (bi > 2)
{
bi -= 3;
}
if (bi < 0)
{
bi += 3;
}
for (bi1 = 0; bi1 < 3 && tr1->buddy[bi]->buddy[bi1] != tr1; bi1++)
{
;
}
fantris[nfantris++] = tr1; // store this triangle in a temporary fan list
tr1 = tr1->buddy[bi];
bi = bi1; // go to the next fan triangle
if (!e_cansee(pts[ti] - tr1->pt0, tr1->n, -0.002f))
{
break; // discard this fan
}
if (tr1->deleted)
{
if (tr1 != tr->buddy[bi0])
{
// delete fan only if it ended on _another_ deleted triangle
for (--nfantris; nfantris >= 0; nfantris--)
{
if (fantris[nfantris] == trlast)
{
trlast = fantris[nfantris]->next;
}
DELETE_TRI(fantris[nfantris])
}
next_iter = 1;
}
break; // fan end
}
} while (true);
}
}
}
if (tr == trlast)
{
break;
}
tr = trnext;
} while (tr);
} while (next_iter && trstart);
if (!trstart || trstart->deleted)
{
n = 0;
goto endqhull;
}
// find triangles that shared an edge with deleted triangles
trnew = 0;
tr = trstart;
do
{
for (i = 0; i < 3; i++)
{
if (tr->buddy[i]->deleted)
{
// create a new triangle
if (trend >= trdata + trdatasz)
{
qhtritem* trdata_new = new qhtritem[trdatasz += 256];
memcpy(trdata_new, trdata, (trend - trdata) * sizeof(qhtritem));
intptr_t diff = (intptr_t)trdata_new - (intptr_t)trdata;
for (n = 0; n < trdatasz - 256; n++)
{
relocate_tritem(trdata_new + n, diff);
}
relocate_ptritem(trend, diff);
relocate_ptritem(trstart, diff);
relocate_ptritem(trnext, diff);
relocate_ptritem(tr, diff);
relocate_ptritem(trbest, diff);
relocate_ptritem(trnew, diff);
if (trdata != trbuf)
{
delete[] trdata;
}
trdata = trdata_new;
}
trend->idx[0] = static_cast<int>(ptmax - ptlist);
trend->idx[1] = tr->idx[i == 2 ? 0 : i + 1];
trend->idx[2] = tr->idx[i];
trend->ptassoc = 0;
trend->deleted = 0;
trend->n = pts[trend->idx[1]] - pts[trend->idx[0]] ^ pts[trend->idx[2]] - pts[trend->idx[0]];
trend->pt0 = pts[trend->idx[0]];
trend->buddy[1] = tr;
tr->buddy[i] = trend;
trend->buddy[0] = trend->buddy[2] = 0;
add_item_to_list(trnew, trend++);
}
}
tr = tr->next;
} while (tr != trstart);
// sort pointers to the new triangles by their 2nd vertex index
n = static_cast<int>(trend - trnew);
if (tmparr_sz < n)
{
if (tmparr_idx != tmparr_idx_buf)
{
delete[] tmparr_idx;
}
if (tmparr_ptr != tmparr_ptr_buf)
{
delete[] tmparr_ptr;
}
tmparr_idx = new int[n];
tmparr_ptr = new qhtritem * [n];
}
for (tr = trnew, i = 0; tr < trend; tr++, i++)
{
tmparr_idx[i] = tr->idx[2];
tmparr_ptr[i] = tr;
}
qsort(tmparr_idx, (void**)tmparr_ptr, 0, static_cast<int>(trend - trnew - 1));
// find 0th buddy for each new triangle (i.e. the triangle, which has its idx[2]==tr->idx[1]
for (tr = trnew; tr < trend; tr++)
{
i = bin_search(tmparr_idx, n, tr->idx[1]);
tr->buddy[0] = tmparr_ptr[i];
tmparr_ptr[i]->buddy[2] = tr;
}
for (tr = trnew; tr < trend; tr++)
{
if (!tr->buddy[0] || !tr->buddy[2])
{
goto endqh;
}
}
// assign all points from the deleted triangles to the new triangles
associate_ptlist_with_trilist(ptdeleted, trnew, ptlist, pts);
// add new triangles to the list
merge_lists(trnext, trnew);
}
else if (trnext == trstart)
{
break; // all triangles in queue have no associated vertices
}
tr = trnext;
} while (++iter < maxiter);
endqh:
// build the final triangle list
for (tr = trstart, n = 1; tr->next != trstart; tr = tr->next, n++)
{
;
}
if (!pTris)
{
pTris = !qmalloc ? new index_t[n * 3] : (index_t*)qmalloc(sizeof(index_t) * n * 3);
}
i = 0;
tr = trstart;
do
{
pTris[i] = tr->idx[0];
pTris[i + 1] = tr->idx[1];
pTris[i + 2] = tr->idx[2];
tr = tr->next;
i += 3;
} while (tr != trstart);
endqhull:
if (ptlist != ptbuf)
{
delete[] ptlist;
}
if (tmparr_idx != tmparr_idx_buf)
{
delete[] tmparr_idx;
}
if (tmparr_ptr != tmparr_ptr_buf)
{
delete[] tmparr_ptr;
}
if (trdata != trbuf)
{
delete[] trdata;
}
return n;
}
#undef DELETE_TRI
void associate_ptlist_with_trilist(ptitem* ptlist, qhtritem* trilist, ptitem* pt0, strided_pointer<Vec3mem> pvtx)
{
if (!ptlist)
{
return;
}
ptitem* pt = ptlist, * ptnext, * ptlast = ptlist->prev;
qhtritem* tr;
int i;
do
{
ptnext = pt->next;
delete_item_from_list(pt);
tr = trilist;
i = static_cast<int>(pt - pt0);
do
{
if (e_cansee(pvtx[i] - tr->pt0, tr->n))
{
add_item_to_list(tr->ptassoc, pt);
break;
}
tr = tr->next;
} while (tr != trilist);
if (pt == ptlast)
{
break;
}
pt = ptnext;
} while (true);
}
void qsort(int* v, void** p, int left, int right)
{
if (left >= right)
{
return;
}
int i, last;
swap(v, p, left, (left + right) >> 1);
for (last = left, i = left + 1; i <= right; i++)
{
if (v[i] < v[left])
{
swap(v, p, ++last, i);
}
}
swap(v, p, left, last);
qsort(v, p, left, last - 1);
qsort(v, p, last + 1, right);
}
int bin_search(int* v, int n, int idx)
{
int left = 0, right = n, m;
do
{
m = (left + right) >> 1;
if (v[m] == idx)
{
return m;
}
if (v[m] < idx)
{
left = m;
}
else
{
right = m;
}
} while (left < right - 1);
return left;
}
}
int qhull(strided_pointer<Vec3> _pts, int npts, index_t*& pTris, qhullmalloc qmalloc)
{
return qhull_IMPL::qhull(_pts, npts, pTris, qmalloc);
}
int TriangulatePoly(vector2df* pVtx, int nVtx, int* pTris, int szTriBuf)
{
return TriangulatePoly_IMPL::TriangulatePoly(pVtx, nVtx, pTris, szTriBuf);
}
namespace TriangulatePoly_IMPL
{
int TriangulatePolyBruteforce(vector2df* pVtx, int nVtx, int* pTris, int szTriBuf)
{
int i, nThunks, nNonEars, nTris = 0;
vtxthunk* ptr, * ptr0, bufThunks[32], * pThunks = nVtx <= 31 ? bufThunks : new vtxthunk[nVtx + 1];
ptr = ptr0 = pThunks;
for (i = nThunks = 0; i < nVtx; i++)
{
if (!is_unused(pVtx[i].x))
{
pThunks[nThunks].next[0] = pThunks + nThunks - 1;
pThunks[nThunks].next[1] = pThunks + nThunks + 1;
pThunks[nThunks].pt = pVtx + i;
ptr = pThunks + nThunks++;
}
}
if (nThunks < 3)
{
return 0;
}
ptr->next[1] = ptr0;
ptr0->next[0] = ptr;
for (i = 0; i < nThunks; i++)
{
pThunks[i].bProcessed = (*pThunks[i].next[1]->pt - *pThunks[i].pt ^ *pThunks[i].next[0]->pt - *pThunks[i].pt) > 0;
}
for (nNonEars = 0; nNonEars < nThunks && nTris < szTriBuf; ptr0 = ptr0->next[1])
{
if (nThunks == 3)
{
pTris[nTris * 3] = static_cast<int>(ptr0->pt - pVtx);
pTris[nTris * 3 + 1] = static_cast<int>(ptr0->next[1]->pt - pVtx);
pTris[nTris * 3 + 2] = static_cast<int>(ptr0->next[0]->pt - pVtx);
nTris++;
break;
}
for (i = 0; (*ptr0->next[1]->pt - *ptr0->pt ^ *ptr0->next[0]->pt - *ptr0->pt) < 0 && i < nThunks; ptr0 = ptr0->next[1], i++)
{
;
}
if (i == nThunks)
{
break;
}
for (ptr = ptr0->next[1]->next[1]; ptr != ptr0->next[0] && ptr->bProcessed; ptr = ptr->next[1])
{
; // find the 1st non-convex vertex after ptr0
}
for (; ptr != ptr0->next[0] && min(min(*ptr0->pt - *ptr0->next[0]->pt ^ *ptr->pt - *ptr0->next[0]->pt,
*ptr0->next[1]->pt - *ptr0->pt ^ *ptr->pt - *ptr0->pt),
*ptr0->next[0]->pt - *ptr0->next[1]->pt ^ *ptr->pt - *ptr0->next[1]->pt) < 0; ptr = ptr->next[1])
{
;
}
if (ptr == ptr0->next[0]) // vertex is an ear, output the corresponding triangle
{
pTris[nTris * 3] = static_cast<int>(ptr0->pt - pVtx);
pTris[nTris * 3 + 1] = static_cast<int>(ptr0->next[1]->pt - pVtx);
pTris[nTris * 3 + 2] = static_cast<int>(ptr0->next[0]->pt - pVtx);
nTris++;
ptr0->next[1]->next[0] = ptr0->next[0];
ptr0->next[0]->next[1] = ptr0->next[1];
nThunks--;
nNonEars = 0;
}
else
{
nNonEars++;
}
}
if (pThunks != bufThunks)
{
delete[] pThunks;
}
return nTris;
}
int TriangulatePoly(vector2df* pVtx, int nVtx, int* pTris, int szTriBuf)
{
if (nVtx < 3)
{
return 0;
}
vtxthunk* pThunks, * pPrevThunk, * pContStart, ** pSags, ** pBottoms, * pPinnacle = nullptr, * pBounds[2], * pPrevBounds[2], * ptr, * ptr_next;
vtxthunk bufThunks[32], * bufSags[16], * bufBottoms[16];
int i, nThunks, nBottoms = 0, nSags = 0, iBottom = 0, nConts = 0, j, isag, nThunks0, nTris = 0, nPrevSags, nTrisCnt, iter, nDegenTris = 0;
float ymax, ymin, e, area0 = 0, area1 = 0, cntarea, minCntArea;
isag = is_unused(pVtx[0].x);
ymin = ymax = pVtx[isag].y;
for (i = isag; i < nVtx; i++)
{
if (!is_unused(pVtx[i].x))
{
ymin = min(ymin, pVtx[i].y);
ymax = max(ymax, pVtx[i].y);
}
}
e = (ymax - ymin) * 0.0005f;
for (i = 1 + isag; i < nVtx; i++)
{
if (!is_unused(pVtx[i].x))
{
j = i < nVtx - 1 && !is_unused(pVtx[i + 1].x) ? i + 1 : isag;
if ((ymin = min(pVtx[j].y, pVtx[i - 1].y)) > pVtx[i].y - e)
{
if ((pVtx[j] - pVtx[i] ^ pVtx[i - 1] - pVtx[i]) > 0)
{
nBottoms++; // we have a bottom
}
else if (ymin > pVtx[i].y + 1E-8f)
{
nSags++; // we have a sag
}
}
}
else
{
nConts++;
isag = ++i;
}
}
nSags += nConts;
if ((nConts - 2) >> 31 & g_bBruteforceTriangulation)
{
return TriangulatePolyBruteforce(pVtx, nVtx, pTris, szTriBuf);
}
pThunks = nVtx + nSags * 2 <= sizeof(bufThunks) / sizeof(bufThunks[0]) ? bufThunks : new vtxthunk[nVtx + nSags * 2];
for (i = nThunks = 0, pContStart = pPrevThunk = pThunks; i < nVtx; i++)
{
if (!is_unused(pVtx[i].x))
{
pThunks[nThunks].next[1] = pThunks + nThunks;
pThunks[nThunks].next[1] = pPrevThunk->next[1];
pPrevThunk->next[1] = pThunks + nThunks;
pThunks[nThunks].next[0] = pPrevThunk;
pThunks[nThunks].jump = 0;
pPrevThunk = pThunks + nThunks;
pThunks[nThunks].bProcessed = 0;
pThunks[nThunks++].pt = &pVtx[i];
}
else
{
pPrevThunk->next[1] = pContStart;
pContStart->next[0] = pThunks + nThunks - 1;
pContStart = pPrevThunk = pThunks + nThunks;
}
}
for (i = j = 0, cntarea = 0, minCntArea = 1; i < nThunks; i++)
{
cntarea += *pThunks[i].pt ^ *pThunks[i].next[1]->pt;
j++;
if (pThunks[i].next[1] != pThunks + i + 1)
{
if (j >= 3)
{
area0 += cntarea;
minCntArea = min(cntarea, minCntArea);
}
cntarea = 0;
j = 0;
}
}
if (minCntArea > 0 && nConts > 1)
{
// if all contours are positive, triangulate them as separate (it's more safe)
for (i = 0; i < nThunks; i++)
{
if (pThunks[i].next[0] != pThunks + i - 1)
{
nTrisCnt = TriangulatePoly(pThunks[i].pt, static_cast<int>((pThunks[i].next[0]->pt - pThunks[i].pt) + 2), pTris + nTris * 3, szTriBuf - nTris * 3);
for (j = 0, isag = static_cast<int>(pThunks[i].pt - pVtx); j < nTrisCnt * 3; j++)
{
pTris[nTris * 3 + j] += isag;
}
i = static_cast<int>(pThunks[i].next[0] - pThunks);
nTris += nTrisCnt;
}
}
if (pThunks != bufThunks)
{
delete[] pThunks;
}
return nTris;
}
pSags = nSags <= sizeof(bufSags) / sizeof(bufSags[0]) ? bufSags : new vtxthunk * [nSags];
pBottoms = nSags + nBottoms <= sizeof(bufBottoms) / sizeof(bufBottoms[0]) ? bufBottoms : new vtxthunk * [nSags + nBottoms];
for (i = nSags = nBottoms = 0; i < nThunks; i++)
{
if ((ymin = min(pThunks[i].next[1]->pt->y, pThunks[i].next[0]->pt->y)) > pThunks[i].pt->y - e)
{
if ((*pThunks[i].next[1]->pt - *pThunks[i].pt ^ *pThunks[i].next[0]->pt - *pThunks[i].pt) >= 0)
{
pBottoms[nBottoms++] = pThunks + i; // we have a bottom
}
else if (ymin > pThunks[i].pt->y + e)
{
pSags[nSags++] = pThunks + i; // we have a sag
}
}
}
iBottom = -1;
pBounds[0] = pBounds[1] = pPrevBounds[0] = pPrevBounds[1] = 0;
nThunks0 = nThunks;
nPrevSags = nSags;
iter = nThunks * 4;
do
{
nextiter:
if (!pBounds[0]) // if bounds are empty, get the next available bottom
{
for (++iBottom; iBottom < nBottoms && !pBottoms[iBottom]->next[0]; iBottom++)
{
;
}
if (iBottom >= nBottoms)
{
break;
}
pBounds[0] = pBounds[1] = pPinnacle = pBottoms[iBottom];
}
pBounds[0]->bProcessed = pBounds[1]->bProcessed = 1;
if (pBounds[0] == pPrevBounds[0] && pBounds[1] == pPrevBounds[1] && nSags == nPrevSags || !pBounds[0]->next[0] || !pBounds[1]->next[0])
{
pBounds[0] = pBounds[1] = 0;
continue;
}
pPrevBounds[0] = pBounds[0];
pPrevBounds[1] = pBounds[1];
nPrevSags = nSags;
// check if left or right is a top
for (i = 0; i < 2; i++)
{
if (pBounds[i]->next[0]->pt->y < pBounds[i]->pt->y && pBounds[i]->next[1]->pt->y <= pBounds[i]->pt->y &&
(*pBounds[i]->next[0]->pt - *pBounds[i]->pt ^ *pBounds[i]->next[1]->pt - *pBounds[i]->pt) > 0)
{
if (pBounds[i]->jump)
{
do
{
ptr = pBounds[i]->jump;
pBounds[i]->jump = 0;
pBounds[i] = ptr;
} while (pBounds[i]->jump);
}
else
{
pBounds[i]->jump = pBounds[i ^ 1];
pBounds[0] = pBounds[1] = 0;
goto nextiter;
}
if (!pBounds[0]->next[0] || !pBounds[1]->next[0])
{
pBounds[0] = pBounds[1] = 0;
goto nextiter;
}
}
}
i = isneg(pBounds[1]->next[1]->pt->y - pBounds[0]->next[0]->pt->y);
ymax = pBounds[i ^ 1]->next[i ^ 1]->pt->y;
ymin = min(pBounds[0]->pt->y, pBounds[1]->pt->y);
for (j = 0, isag = -1; j < nSags; j++)
{
if (inrange(pSags[j]->pt->y, ymin, ymax) && // find a sag in next left-left-right-next right quad
pSags[j] != pBounds[0]->next[0] && pSags[j] != pBounds[1]->next[1] &&
(*pBounds[0]->pt - *pBounds[0]->next[0]->pt ^ *pSags[j]->pt - *pBounds[0]->next[0]->pt) >= 0 &&
(*pBounds[1]->pt - *pBounds[0]->pt ^ *pSags[j]->pt - *pBounds[0]->pt) >= 0 &&
(*pBounds[1]->next[1]->pt - *pBounds[1]->pt ^ *pSags[j]->pt - *pBounds[1]->pt) >= 0 &&
(*pBounds[0]->next[0]->pt - *pBounds[1]->next[1]->pt ^ *pSags[j]->pt - *pBounds[1]->next[1]->pt) >= 0)
{
ymax = pSags[j]->pt->y;
isag = j;
}
}
if (isag >= 0) // build a bridge between the sag and the highest active point
{
if (pSags[isag]->next[0])
{
pPinnacle->next[1]->next[0] = pThunks + nThunks;
pSags[isag]->next[0]->next[1] = pThunks + nThunks + 1;
pThunks[nThunks].next[0] = pThunks + nThunks + 1;
pThunks[nThunks].next[1] = pPinnacle->next[1];
pThunks[nThunks + 1].next[1] = pThunks + nThunks;
pThunks[nThunks + 1].next[0] = pSags[isag]->next[0];
pPinnacle->next[1] = pSags[isag];
pSags[isag]->next[0] = pPinnacle;
pThunks[nThunks].pt = pPinnacle->pt;
pThunks[nThunks + 1].pt = pSags[isag]->pt;
pThunks[nThunks].jump = pThunks[nThunks + 1].jump = 0;
pThunks[nThunks].bProcessed = pThunks[nThunks + 1].bProcessed = 0;
if (pBounds[1] == pPinnacle)
{
pBounds[1] = pThunks + nThunks;
}
for (ptr = pThunks + nThunks, j = 0; ptr != pBounds[1]->next[1] && j < nThunks; ptr = ptr->next[1], j++)
{
if (min(ptr->next[0]->pt->y, ptr->next[1]->pt->y) > ptr->pt->y) // ptr is a bottom
{
pBottoms[nBottoms++] = ptr;
break;
}
}
pBounds[1] = pPinnacle;
pPinnacle = pSags[isag];
nThunks += 2;
}
for (j = isag; j < nSags - 1; j++)
{
pSags[j] = pSags[j + 1];
}
--nSags;
continue;
}
// create triangles featuring the new vertex
for (ptr = pBounds[i]; ptr != pBounds[i ^ 1] && nTris < szTriBuf; ptr = ptr_next)
{
if ((*ptr->next[i ^ 1]->pt - *ptr->pt ^ *ptr->next[i]->pt - *ptr->pt) * (1 - i * 2) > 0 || pBounds[0]->next[0] == pBounds[1]->next[1])
{
// output the triangle
pTris[nTris * 3] = static_cast<int>(pBounds[i]->next[i]->pt - pVtx);
pTris[nTris * 3 + 1 + i] = static_cast<int>(ptr->pt - pVtx);
pTris[nTris * 3 + 2 - i] = static_cast<int>(ptr->next[i ^ 1]->pt - pVtx);
vector2df edge0 = pVtx[pTris[nTris * 3 + 1]] - pVtx[pTris[nTris * 3]], edge1 = pVtx[pTris[nTris * 3 + 2]] - pVtx[pTris[nTris * 3]];
float darea = edge0 ^ edge1;
area1 += darea;
nDegenTris += isneg(sqr(darea) - sqr(0.02f) * (edge0 * edge0) * (edge1 * edge1));
nTris++;
ptr->next[i ^ 1]->next[i] = ptr->next[i];
ptr->next[i]->next[i ^ 1] = ptr->next[i ^ 1];
pBounds[i] = ptr_next = ptr->next[i ^ 1];
if (pPinnacle == ptr)
{
pPinnacle = ptr->next[i];
}
ptr->next[0] = ptr->next[1] = 0;
ptr->bProcessed = 1;
}
else
{
break;
}
}
if ((pBounds[i] = pBounds[i]->next[i]) == pBounds[i ^ 1]->next[i ^ 1])
{
pBounds[0] = pBounds[1] = 0;
}
else if (pBounds[i]->pt->y > pPinnacle->pt->y)
{
pPinnacle = pBounds[i];
}
} while (nTris < szTriBuf && --iter);
if (pThunks != bufThunks)
{
delete[] pThunks;
}
if (pBottoms != bufBottoms)
{
delete[] pBottoms;
}
if (pSags != bufSags)
{
delete[] pSags;
}
int bProblem = nTris<nThunks0 - nConts * 2 || fabs_tpl(area0 - area1)>area0 * 0.003f || nTris >= szTriBuf;
if (bProblem || nDegenTris)
{
if (nConts == 1)
{
return TriangulatePolyBruteforce(pVtx, nVtx, pTris, szTriBuf);
}
else
{
g_nTriangulationErrors += bProblem;
}
}
return nTris;
}
}
}
-15
View File
@@ -1,15 +0,0 @@
/*
* 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"
-31
View File
@@ -1,31 +0,0 @@
/*
* 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.
// Description : Occlusion buffer main include
#ifndef CRYINCLUDE_CRY3DENGINE_CULLBUFFER_H
#define CRYINCLUDE_CRY3DENGINE_CULLBUFFER_H
#pragma once
#include "ProjectDefines.h"
#include "ObjMan.h" // EOcclusionObjectType
#include "CZBufferCuller.h"
class CCullBuffer
: public CZBufferCuller
{
};
#endif // CRYINCLUDE_CRY3DENGINE_CULLBUFFER_H
-438
View File
@@ -1,438 +0,0 @@
/*
* 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.
// Description : draw, create decals on the world
#include "Cry3DEngine_precompiled.h"
#include "DecalManager.h"
#include "3dEngine.h"
#include "ObjMan.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
IGeometry* CDecal::s_pSphere = 0;
void CDecal::ResetStaticData()
{
SAFE_RELEASE(s_pSphere);
}
int CDecal::Update(bool& active, const float fFrameTime)
{
// process life time and disable decal when needed
m_fLifeTime -= fFrameTime;
if (m_fLifeTime < 0)
{
active = 0;
FreeRenderData();
}
else if (m_ownerInfo.pRenderNode && m_ownerInfo.pRenderNode->m_nInternalFlags & IRenderNode::UPDATE_DECALS)
{
active = false;
return 1;
}
return 0;
}
Vec3 CDecal::GetWorldPosition()
{
Vec3 vPos = m_vPos;
if (m_ownerInfo.pRenderNode)
{
if (m_eDecalType == eDecalType_OS_SimpleQuad || m_eDecalType == eDecalType_OS_OwnersVerticesUsed)
{
assert(m_ownerInfo.pRenderNode);
if (m_ownerInfo.pRenderNode)
{
Matrix34A objMat;
if (IStatObj* pEntObject = m_ownerInfo.GetOwner(objMat))
{
vPos = objMat.TransformPoint(vPos);
}
}
}
}
return vPos;
}
void CDecal::Render(const float fCurTime, int nAfterWater, float fDistanceFading, float fDistance, const SRenderingPassInfo& passInfo, const SRendItemSorter& rendItemSorter)
{
FUNCTION_PROFILER_3DENGINE;
if (!m_pMaterial || !m_pMaterial->GetShaderItem().m_pShader || m_pMaterial->GetShaderItem().m_pShader->GetShaderType() != eST_General)
{
return; // shader not supported for decals
}
// Get decal alpha from life time
float fAlpha = m_fLifeTime * 2;
if (fAlpha > 1.f)
{
fAlpha = 1.f;
}
else if (fAlpha < 0)
{
return;
}
fAlpha *= fDistanceFading;
float fSizeK;
if (m_fGrowTime)
{
fSizeK = min(1.f, sqrt_tpl((fCurTime - m_fLifeBeginTime) / m_fGrowTime));
}
else
{
fSizeK = 1.f;
}
float fSizeAlphaK;
if (m_fGrowTimeAlpha)
{
fSizeAlphaK = min(1.f, sqrt_tpl((fCurTime - m_fLifeBeginTime) / m_fGrowTimeAlpha));
}
else
{
fSizeAlphaK = 1.f;
}
if (m_bDeferred)
{
SDeferredDecal newItem;
newItem.fAlpha = fAlpha;
newItem.pMaterial = m_pMaterial;
newItem.nSortOrder = m_sortPrio;
newItem.nFlags = 0;
Vec3 vRight, vUp, vNorm;
Matrix34A objMat;
if (IStatObj* pEntObject = m_ownerInfo.GetOwner(objMat))
{
vRight = objMat.TransformVector(m_vRight * m_fSize);
vUp = objMat.TransformVector(m_vUp * m_fSize);
vNorm = objMat.TransformVector((Vec3(m_vRight).Cross(m_vUp)) * m_fSize);
}
else
{
vRight = (m_vRight * m_fSize);
vUp = (m_vUp * m_fSize);
vNorm = ((Vec3(m_vRight).Cross(m_vUp)) * m_fSize);
}
Matrix33 matRotation;
matRotation.SetColumn(0, vRight);
matRotation.SetColumn(1, vUp);
matRotation.SetColumn(2, vNorm * GetFloatCVar(e_DecalsDefferedDynamicDepthScale));
newItem.projMatrix.SetRotation33(matRotation);
newItem.projMatrix.SetTranslation(m_vWSPos + vNorm * .1f * m_fWSSize);
if (m_fGrowTimeAlpha)
{
newItem.fGrowAlphaRef = max(.02f, 1.f - fSizeAlphaK);
}
else
{
newItem.fGrowAlphaRef = 0;
}
GetRenderer()->EF_AddDeferredDecal(newItem);
return;
}
switch (m_eDecalType)
{
case eDecalType_WS_Merged:
case eDecalType_OS_OwnersVerticesUsed:
{
// check if owner mesh was deleted
if (m_pRenderMesh && (m_pRenderMesh->GetVertexContainer() == m_pRenderMesh) && m_pRenderMesh->GetVerticesCount() < 3)
{
FreeRenderData();
}
if (!m_pRenderMesh)
{
break;
}
// setup transformation
CRenderObject* pObj = GetRenderer()->EF_GetObject_Temp(passInfo.ThreadID());
if (!pObj)
{
return;
}
pObj->m_fSort = 0;
pObj->m_RState = 0;
Matrix34A objMat;
if (m_ownerInfo.pRenderNode && !m_ownerInfo.GetOwner(objMat))
{
assert(0);
return;
}
else
if (!m_ownerInfo.pRenderNode)
{
objMat.SetIdentity();
if (m_eDecalType == eDecalType_WS_Merged)
{
objMat.SetTranslation(m_vPos);
}
}
pObj->m_II.m_Matrix = objMat;
pObj->m_nSort = m_sortPrio;
// somehow it's need's to be twice bigger to be same as simple decals
float fSize2 = m_fSize * fSizeK * 2.f; ///m_ownerInfo.pRenderNode->GetScale();
if (fSize2 < 0.0001f)
{
return;
}
// setup texgen
// S component
float correctScale(-1);
m_arrBigDecalRMCustomData[0] = correctScale * m_vUp.x / fSize2;
m_arrBigDecalRMCustomData[1] = correctScale * m_vUp.y / fSize2;
m_arrBigDecalRMCustomData[2] = correctScale * m_vUp.z / fSize2;
Vec3 vPosDecS = m_vPos;
if (m_eDecalType == eDecalType_WS_Merged)
{
vPosDecS.zero();
}
float D0 =
m_arrBigDecalRMCustomData[0] * vPosDecS.x +
m_arrBigDecalRMCustomData[1] * vPosDecS.y +
m_arrBigDecalRMCustomData[2] * vPosDecS.z;
m_arrBigDecalRMCustomData[3] = -D0 + 0.5f;
// T component
m_arrBigDecalRMCustomData[4] = m_vRight.x / fSize2;
m_arrBigDecalRMCustomData[5] = m_vRight.y / fSize2;
m_arrBigDecalRMCustomData[6] = m_vRight.z / fSize2;
float D1 =
m_arrBigDecalRMCustomData[4] * vPosDecS.x +
m_arrBigDecalRMCustomData[5] * vPosDecS.y +
m_arrBigDecalRMCustomData[6] * vPosDecS.z;
m_arrBigDecalRMCustomData[7] = -D1 + 0.5f;
// pass attenuation info
m_arrBigDecalRMCustomData[8] = vPosDecS.x;
m_arrBigDecalRMCustomData[9] = vPosDecS.y;
m_arrBigDecalRMCustomData[10] = vPosDecS.z;
m_arrBigDecalRMCustomData[11] = m_fSize;
// N component
Vec3 vNormal(Vec3(correctScale* m_vUp).Cross(m_vRight).GetNormalized());
m_arrBigDecalRMCustomData[12] = vNormal.x * (m_fSize / m_fWSSize);
m_arrBigDecalRMCustomData[13] = vNormal.y * (m_fSize / m_fWSSize);
m_arrBigDecalRMCustomData[14] = vNormal.z * (m_fSize / m_fWSSize);
m_arrBigDecalRMCustomData[15] = 0;
// draw complex decal using new indices and original object vertices
pObj->m_fAlpha = fAlpha;
pObj->m_ObjFlags |= FOB_DECAL | FOB_DECAL_TEXGEN_2D;
pObj->m_nTextureID = -1;
//pObj->m_nTextureID1 = -1;
pObj->m_II.m_AmbColor = m_vAmbient;
m_pRenderMesh->SetREUserData(m_arrBigDecalRMCustomData, 0, fAlpha);
m_pRenderMesh->AddRenderElements(m_pMaterial, pObj, passInfo, EFSLIST_GENERAL, nAfterWater);
}
break;
case eDecalType_OS_SimpleQuad:
{
assert(m_ownerInfo.pRenderNode);
if (!m_ownerInfo.pRenderNode)
{
break;
}
// transform decal in software from owner space into world space and render as quad
Matrix34A objMat;
IStatObj* pEntObject = m_ownerInfo.GetOwner(objMat);
if (!pEntObject)
{
break;
}
Vec3 vPos = objMat.TransformPoint(m_vPos);
Vec3 vRight = objMat.TransformVector(m_vRight * m_fSize);
Vec3 vUp = objMat.TransformVector(m_vUp * m_fSize);
UCol uCol;
uCol.dcolor = 0xffffffff;
uCol.bcolor[3] = fastround_positive(fAlpha * 255);
GetObjManager()->AddDecalToRenderer(fDistance, m_pMaterial, m_sortPrio, vRight * fSizeK, vUp * fSizeK, uCol,
OS_ALPHA_BLEND, m_vAmbient, vPos, nAfterWater, passInfo, rendItemSorter);
}
break;
case eDecalType_WS_SimpleQuad:
{ // draw small world space decal untransformed
UCol uCol;
uCol.dcolor = 0;
uCol.bcolor[3] = fastround_positive(fAlpha * 255);
GetObjManager()->AddDecalToRenderer(fDistance, m_pMaterial, m_sortPrio, m_vRight * m_fSize * fSizeK,
m_vUp * m_fSize * fSizeK, uCol, OS_ALPHA_BLEND, m_vAmbient, m_vPos, nAfterWater, passInfo,
rendItemSorter);
}
break;
case eDecalType_WS_OnTheGround:
{
RenderBigDecalOnTerrain(fAlpha, fSizeK, passInfo);
}
break;
}
}
void CDecal::FreeRenderData()
{
// delete render mesh
m_pRenderMesh = NULL;
m_ownerInfo.pRenderNode = 0;
}
void CDecal::RenderBigDecalOnTerrain(float fAlpha, float fScale, const SRenderingPassInfo& passInfo)
{
float fRadius = m_fSize * fScale;
// check terrain bounds
if (m_vPos.x < -fRadius || m_vPos.y < -fRadius)
{
return;
}
auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
if (!terrain)
{
return;
}
const AZ::Aabb terrainAabb = terrain->GetTerrainAabb();
const float terrainSizeX = terrainAabb.GetXExtent();
const float terrainSizeY = terrainAabb.GetYExtent();
if (m_vPos.x >= terrainSizeX + fRadius || m_vPos.y >= terrainSizeY + fRadius)
{
return;
}
const AZ::Vector2 terrainGridResolution = terrain->GetTerrainGridResolution();
const int nUsintSize = static_cast<int>(AZ::GetMax(terrainGridResolution.GetX(), terrainGridResolution.GetY()));
fRadius += nUsintSize;
const float terrainHeight = terrain->GetHeightFromFloats(m_vPos.x, m_vPos.y, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP);
if (fabs(m_vPos.z - terrainHeight) > fRadius)
{
return; // too far from ground surface
}
// setup texgen
float fSize = m_fSize * fScale;
if (fSize < 0.05f)
{
return;
}
// m_vUp and m_vRight are the scaled binormal and tangent
// The shader projects the vertex pass position onto these to calculate UVs
// However binormal and tangent are only half the height and width of the decal
// So we need to double them
Vec3 uvUp = m_vUp * 2.0f;
Vec3 uvRight = m_vRight * 2.0f;
// Let T denote the tangent, B the binormal and P the vertex position in decal space
// The shader calculates UVs by projecting vertex position onto the tangent and binormal:
// U = dot( T, P )
// V = dot( B, P )
// UVs should range 0...1, so normalize by the length of the tangent and binormal:
// U = dot( normalize(T), P / length(T) )
// V = dot( normalize(B), P / length(B) )
// This is equivalent to:
// U = dot( T, P ) / ( length(T) * length(T) )
// V = dot( B, P ) / ( length(B) * length(B) )
// The length squared can be folded into the tangent and binormal:
// U = dot( T / lengthSq(T), P )
// V = dot( B / lengthSq(B), P )
// Hence:
uvUp /= uvUp.GetLengthSquared();
uvRight /= uvRight.GetLengthSquared();
// S component
float correctScale(-1);
m_arrBigDecalRMCustomData[0] = correctScale * uvUp.x / fSize;
m_arrBigDecalRMCustomData[1] = correctScale * uvUp.y / fSize;
m_arrBigDecalRMCustomData[2] = correctScale * uvUp.z / fSize;
// T component
m_arrBigDecalRMCustomData[4] = uvRight.x / fSize;
m_arrBigDecalRMCustomData[5] = uvRight.y / fSize;
m_arrBigDecalRMCustomData[6] = uvRight.z / fSize;
// UV centering happens in the shader
// See shader function _TCModifyDecal
m_arrBigDecalRMCustomData[3] = 0.0f;
m_arrBigDecalRMCustomData[7] = 0.0f;
// pass attenuation info
m_arrBigDecalRMCustomData[8] = 0;
m_arrBigDecalRMCustomData[9] = 0;
m_arrBigDecalRMCustomData[10] = 0;
m_arrBigDecalRMCustomData[11] = fSize * 2.0f;
Vec3 vNormal(Vec3(correctScale* m_vUp).Cross(m_vRight).GetNormalized());
m_arrBigDecalRMCustomData[12] = vNormal.x;
m_arrBigDecalRMCustomData[13] = vNormal.y;
m_arrBigDecalRMCustomData[14] = vNormal.z;
m_arrBigDecalRMCustomData[15] = 0;
CRenderObject* pObj = GetIdentityCRenderObject(passInfo.ThreadID());
if (!pObj)
{
return;
}
pObj->m_II.m_Matrix.SetTranslation(m_vPos);
pObj->m_fAlpha = fAlpha;
pObj->m_ObjFlags |= FOB_DECAL | FOB_DECAL_TEXGEN_2D;
pObj->m_nTextureID = -1;
//pObj->m_nTextureID1 = -1;
pObj->m_II.m_AmbColor = m_vAmbient;
pObj->m_nSort = m_sortPrio;
Plane planes[4];
planes[0].SetPlane(m_vRight, m_vRight * m_fSize + m_vPos);
planes[1].SetPlane(-m_vRight, -m_vRight * m_fSize + m_vPos);
planes[2].SetPlane(m_vUp, m_vUp * m_fSize + m_vPos);
planes[3].SetPlane(-m_vUp, -m_vUp * m_fSize + m_vPos);
}
File diff suppressed because it is too large Load Diff
-159
View File
@@ -1,159 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_DECALMANAGER_H
#define CRYINCLUDE_CRY3DENGINE_DECALMANAGER_H
#pragma once
#define DECAL_COUNT (512) // must be pow2
#define ENTITY_DECAL_DIST_FACTOR (200)
#define DIST_FADING_FACTOR (6.f)
class C3DEngine;
enum EDecal_Type
{
eDecalType_Undefined,
eDecalType_OS_OwnersVerticesUsed,
eDecalType_WS_Merged,
eDecalType_WS_OnTheGround,
eDecalType_WS_SimpleQuad,
eDecalType_OS_SimpleQuad
};
class CDecal
: public Cry3DEngineBase
{
public:
// cur state
Vec3 m_vPos;
Vec3 m_vRight, m_vUp, m_vFront;
float m_fSize;
Vec3 m_vWSPos; // Decal position (world coordinates) from DecalInfo.vPos
float m_fWSSize; // Decal size (world coordinates) from DecalInfo.fSize
// life style
float m_fLifeTime; // relative time left till decal should die
Vec3 m_vAmbient; // ambient color
SDecalOwnerInfo m_ownerInfo;
EDecal_Type m_eDecalType;
float m_fGrowTime, m_fGrowTimeAlpha; // e.g. growing blood pools
float m_fLifeBeginTime; //
uint8 m_iAssembleSize; // of how many decals has this decal be assembled, 0 if not to assemble
uint8 m_sortPrio;
uint8 m_bDeferred;
// render data
_smart_ptr<IRenderMesh> m_pRenderMesh; // only needed for terrain decals, 4 of them because they might cross borders
float m_arrBigDecalRMCustomData[16]; // only needed if one of m_arrBigDecalRMs[]!=0, most likely we can reduce to [12]
_smart_ptr< IMaterial > m_pMaterial;
uint32 m_nGroupId; // used for multi-component decals
#ifdef _DEBUG
char m_decalOwnerEntityClassName[256];
char m_decalOwnerName[256];
EERType m_decalOwnerType;
#endif
CDecal()
: m_vPos(0, 0, 0)
, m_vRight(0, 0, 0)
, m_vUp(0, 0, 0)
, m_vFront(0, 0, 0)
, m_fSize(0)
, m_vWSPos(0, 0, 0)
, m_fWSSize(0)
, m_fLifeTime(0)
, m_vAmbient(0, 0, 0)
, m_fGrowTime(0)
, m_fGrowTimeAlpha(0)
, m_fLifeBeginTime(0)
, m_sortPrio(0)
, m_pMaterial(0)
, m_nGroupId(0)
, m_iAssembleSize(0)
, m_bDeferred(0)
{
m_eDecalType = eDecalType_Undefined;
m_pRenderMesh = NULL;
memset(&m_arrBigDecalRMCustomData[0], 0, sizeof(m_arrBigDecalRMCustomData));
#ifdef _DEBUG
m_decalOwnerEntityClassName[0] = '\0';
m_decalOwnerName[0] = '\0';
m_decalOwnerType = eERType_NotRenderNode;
#endif
}
~CDecal()
{
FreeRenderData();
}
void Render(const float fFrameTime, int nAfterWater, float fDistanceFading, float fDiatance, const SRenderingPassInfo& passInfo, const SRendItemSorter& rendItemSorter);
int Update(bool& active, const float fFrameTime);
void RenderBigDecalOnTerrain(float fAlpha, float fScale, const SRenderingPassInfo& passInfo);
void FreeRenderData();
static void ResetStaticData();
bool IsBigDecalUsed() const { return m_pRenderMesh != 0; }
Vec3 GetWorldPosition();
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
}
private:
static IGeometry* s_pSphere;
};
class CDecalManager
: public Cry3DEngineBase
{
CDecal m_arrDecals[DECAL_COUNT];
bool m_arrbActiveDecals[DECAL_COUNT];
int m_nCurDecal;
PodArray<IRenderNode*> m_arrTempUpdatedOwners;
public: // ---------------------------------------------------------------
CDecalManager();
~CDecalManager();
bool Spawn(CryEngineDecalInfo Decal, CDecal* pCallerManagedDecal = 0);
// once per frame
void Update(const float fFrameTime);
// maybe multiple times per frame
void Render(const SRenderingPassInfo& passInfo);
void OnEntityDeleted(IRenderNode* pEnt);
void OnRenderMeshDeleted(IRenderMesh* pRenderMesh);
// complex decals
void FillBigDecalIndices(IRenderMesh* pRenderMesh, Vec3 vPos, float fRadius, Vec3 vProjDir, PodArray<vtx_idx>* plstIndices, _smart_ptr<IMaterial> pMat, AABB& meshBBox, float& texelAreaDensity);
_smart_ptr<IRenderMesh> MakeBigDecalRenderMesh(IRenderMesh* pSourceRenderMesh, Vec3 vPos, float fRadius, Vec3 vProjDir, _smart_ptr<IMaterial> pDecalMat, _smart_ptr<IMaterial> pSrcMat);
void MoveToEdge(IRenderMesh* pRM, const float fRadius, Vec3& vPos, Vec3& vOutNorm, const Vec3& vTri0, const Vec3& vTri1, const Vec3& vTri2);
void GetMemoryUsage(ICrySizer* pSizer) const;
void Reset() { memset(m_arrbActiveDecals, 0, sizeof(m_arrbActiveDecals)); m_nCurDecal = 0; }
void DeleteDecalsInRange(AABB* pAreaBox, IRenderNode* pEntity);
bool AdjustDecalPosition(CryEngineDecalInfo& DecalInfo, bool bMakeFatTest);
static bool RayRenderMeshIntersection(IRenderMesh* pRenderMesh, const Vec3& vInPos, const Vec3& vInDir, Vec3& vOutPos, Vec3& vOutNormal, bool bFastTest, float fMaxHitDistance, _smart_ptr<IMaterial> pMat);
void Serialize(TSerialize ser);
bool SpawnHierarchical(const CryEngineDecalInfo& rootDecalInfo, CDecal* pCallerManagedDecal);
private:
_smart_ptr<IMaterial> GetMaterialForDecalTexture(const char* pTextureName);
};
#endif // CRYINCLUDE_CRY3DENGINE_DECALMANAGER_H
@@ -1,469 +0,0 @@
/*
* 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 "DecalRenderNode.h"
#include "VisAreas.h"
#include "ObjMan.h"
#include "MatMan.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include "Environment/OceanEnvironmentBus.h"
int CDecalRenderNode::m_nFillBigDecalIndicesCounter = 0;
CDecalRenderNode::CDecalRenderNode()
: m_pos(0, 0, 0)
, m_localBounds(Vec3(-1, -1, -1), Vec3(1, 1, 1))
, m_pMaterial(NULL)
, m_updateRequested(false)
, m_decalProperties()
, m_decal(nullptr)
, m_nLastRenderedFrameId(0)
, m_nLayerId(0)
{
m_Matrix.SetIdentity();
}
CDecalRenderNode::~CDecalRenderNode()
{
DeleteDecal();
GetISystem()->GetI3DEngine()->FreeRenderNodeState(this);
}
const SDecalProperties* CDecalRenderNode::GetDecalProperties() const
{
return &m_decalProperties;
}
void CDecalRenderNode::DeleteDecal()
{
if (m_decal)
{
delete m_decal;
m_decal = nullptr;
}
}
void CDecalRenderNode::SetCommonProperties(CryEngineDecalInfo& decalInfo)
{
decalInfo.fSize = m_decalProperties.m_radius;
decalInfo.pExplicitRightUpFront = &m_decalProperties.m_explicitRightUpFront;
decalInfo.sortPrio = m_decalProperties.m_sortPrio;
decalInfo.pIStatObj = nullptr;
decalInfo.ownerInfo.pRenderNode = nullptr;
decalInfo.fLifeTime = 1.0f; // default life time for rendering, decal won't grow older as we don't update it
decalInfo.fGrowTime = 0.0f;
decalInfo.fAngle = 0.0f;
// We don't set decalInfo.szMaterialName here because that is handled in CDecalRenderNode::CreateDecal()
}
void CDecalRenderNode::CreatePlanarDecal()
{
CryEngineDecalInfo decalInfo;
SetCommonProperties(decalInfo);
// necessary params
decalInfo.vPos = m_decalProperties.m_pos;
decalInfo.vNormal = m_decalProperties.m_normal;
// default for all other
decalInfo.vHitDirection = Vec3(0, 0, 0);
decalInfo.preventDecalOnGround = true;
CreateDecal(decalInfo);
}
void CDecalRenderNode::CreateDecalOnTerrain()
{
bool terrainExists = false;
float terrainHeight = AZ::Constants::FloatMax;
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainHeight
, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats
, m_decalProperties.m_pos.x, m_decalProperties.m_pos.y, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &terrainExists);
if (!terrainExists)
{
//No terrain system available, or there's a hole at the given location.
return;
}
float terrainDelta(m_decalProperties.m_pos.z - terrainHeight);
if (terrainDelta < m_decalProperties.m_radius && terrainDelta > -0.5f)
{
CryEngineDecalInfo decalInfo;
SetCommonProperties(decalInfo);
// necessary params
decalInfo.vPos = Vec3(m_decalProperties.m_pos.x, m_decalProperties.m_pos.y, terrainHeight);
decalInfo.vNormal = Vec3(0, 0, 1);
decalInfo.vHitDirection = Vec3(0, 0, -1);
decalInfo.preventDecalOnGround = false;
CreateDecal(decalInfo);
}
}
void CDecalRenderNode::CreateDecal(const CryEngineDecalInfo& decalInfo)
{
m_decal = new CDecal();
if (m_p3DEngine->CreateDecalInstance(decalInfo, m_decal))
{
// Rather than setting decalInfo.szMaterialName in SetCommonProperties(), it's better to set IMaterial directly since we already have the desired material.
// This is more reliable than using the material name. For example, if the material was cloned from another one it would have the same name
// as the original, and CreateDecalInstance() would load the original from disk rather than the clone.
m_decal->m_pMaterial = m_pMaterial;
}
else
{
DeleteDecal();
}
}
void CDecalRenderNode::CreateDecals()
{
DeleteDecal();
if (m_decalProperties.m_deferred)
{
return;
}
_smart_ptr<IMaterial> pMaterial(GetMaterial());
assert(0 != pMaterial && "CDecalRenderNode::CreateDecals() -- Invalid Material!");
if (!pMaterial)
{
return;
}
switch (m_decalProperties.m_projectionType)
{
case SDecalProperties::ePlanar:
{
CreatePlanarDecal();
break;
}
case SDecalProperties::eProjectOnTerrain:
{
CreateDecalOnTerrain();
break;
}
default:
{
assert(!"CDecalRenderNode::CreateDecals() : Unsupported decal projection type!");
break;
}
}
}
void CDecalRenderNode::ProcessUpdateRequest()
{
if (!m_updateRequested || m_nFillBigDecalIndicesCounter >= GetCVars()->e_DecalsMaxUpdatesPerFrame)
{
return;
}
CreateDecals();
m_updateRequested = false;
}
void CDecalRenderNode::UpdateAABBFromRenderMeshes()
{
if (m_decalProperties.m_projectionType == SDecalProperties::eProjectOnTerrain)
{
AABB WSBBox;
WSBBox.Reset();
if (m_decal && m_decal->m_pRenderMesh && m_decal->m_eDecalType != eDecalType_OS_OwnersVerticesUsed)
{
AABB aabb;
m_decal->m_pRenderMesh->GetBBox(aabb.min, aabb.max);
if (m_decal->m_eDecalType == eDecalType_WS_Merged || m_decal->m_eDecalType == eDecalType_WS_OnTheGround)
{
aabb.min += m_decal->m_vPos;
aabb.max += m_decal->m_vPos;
}
WSBBox.Add(aabb);
}
if (!WSBBox.IsReset())
{
m_WSBBox = WSBBox;
}
}
}
//special check for def decals forcing
bool CDecalRenderNode::CheckForceDeferred()
{
if (m_pMaterial != NULL)
{
SShaderItem& sItem = m_pMaterial->GetShaderItem(0);
if (sItem.m_pShaderResources != NULL)
{
float fCosA = m_decalProperties.m_normal.GetNormalized().Dot(Vec3(0, 0, 1));
if (fCosA > 0.5f)
{
return false;
}
if (SEfResTexture* pEnvRes0 = sItem.m_pShaderResources->GetTextureResource(EFTT_ENV))
{
if (pEnvRes0->m_Sampler.m_pITex == NULL)
{
m_decalProperties.m_projectionType = SDecalProperties::ePlanar;
m_decalProperties.m_deferred = true;
return true;
}
}
else
{
m_decalProperties.m_projectionType = SDecalProperties::ePlanar;
m_decalProperties.m_deferred = true;
return true;
}
}
}
return false;
}
void CDecalRenderNode::SetDecalProperties(const SDecalProperties& properties)
{
// update bounds
m_localBounds = AABB(-properties.m_radius * Vec3(1, 1, 1), properties.m_radius * Vec3(1, 1, 1));
// register material
m_pMaterial = GetMatMan()->LoadMaterial(properties.m_pMaterialName, false);
// copy decal properties
m_decalProperties = properties;
m_decalProperties.m_pMaterialName = 0; // reset this as it's assumed to be a temporary pointer only, refer to m_materialID to get material
// request update
m_updateRequested = true;
bool bForced = 0;
if (properties.m_deferred || (GetCVars()->e_DecalsDefferedStatic && (m_decalProperties.m_projectionType != SDecalProperties::ePlanar && m_decalProperties.m_projectionType != SDecalProperties::eProjectOnTerrain)))
{
m_decalProperties.m_deferred = true;
}
if (GetCVars()->e_DecalsForceDeferred)
{
if (CheckForceDeferred())
{
bForced = true;
}
}
// set normal just in case; normal direction will be determined by m_explicitRightUpFront
m_decalProperties.m_normal = properties.m_normal;
m_fWSMaxViewDist = properties.m_maxViewDist;
// set matrix
m_Matrix.SetRotation33(m_decalProperties.m_explicitRightUpFront);
Matrix33 matScale;
if (bForced && !properties.m_deferred)
{
matScale.SetScale(Vec3(properties.m_radius, properties.m_radius, properties.m_radius * 0.05f));
}
else
{
matScale.SetScale(Vec3(properties.m_radius, properties.m_radius, properties.m_radius * properties.m_depth));
}
m_Matrix = m_Matrix * matScale;
m_Matrix.SetTranslation(properties.m_pos);
}
IRenderNode* CDecalRenderNode::Clone() const
{
CDecalRenderNode* pDestDecal = new CDecalRenderNode();
// CDecalRenderNode member vars
pDestDecal->m_pos = m_pos;
pDestDecal->m_localBounds = m_localBounds;
pDestDecal->m_pMaterial = m_pMaterial;
pDestDecal->m_updateRequested = true;
pDestDecal->m_decalProperties = m_decalProperties;
pDestDecal->m_WSBBox = m_WSBBox;
pDestDecal->m_Matrix = m_Matrix;
pDestDecal->m_nLayerId = m_nLayerId;
//IRenderNode member vars
// We cannot just copy over due to issues with the linked list of IRenderNode objects
CopyIRenderNodeData(pDestDecal);
return pDestDecal;
}
void CDecalRenderNode::SetMatrix(const Matrix34& mat)
{
m_pos = mat.GetTranslation();
if (m_decalProperties.m_projectionType == SDecalProperties::ePlanar)
{
m_WSBBox.SetTransformedAABB(m_Matrix, AABB(-Vec3(1, 1, 0.5f), Vec3(1, 1, 0.5f)));
}
else
{
m_WSBBox.SetTransformedAABB(m_Matrix, AABB(-Vec3(1, 1, 1), Vec3(1, 1, 1)));
}
Get3DEngine()->RegisterEntity(this);
}
void CDecalRenderNode::SetMatrixFull(const Matrix34& mat)
{
m_Matrix = mat;
m_pos = mat.GetTranslation();
if (m_decalProperties.m_projectionType == SDecalProperties::ePlanar)
{
m_WSBBox.SetTransformedAABB(m_Matrix, AABB(-Vec3(1, 1, 0.5f), Vec3(1, 1, 0.5f)));
}
else
{
m_WSBBox.SetTransformedAABB(m_Matrix, AABB(-Vec3(1, 1, 1), Vec3(1, 1, 1)));
}
}
const char* CDecalRenderNode::GetEntityClassName() const
{
return "Decal";
}
const char* CDecalRenderNode::GetName() const
{
return "Decal";
}
void CDecalRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
if (!passInfo.RenderDecals())
{
return; // false;
}
float distFading = SATURATE((1.f - rParam.fDistance / m_fWSMaxViewDist) * DIST_FADING_FACTOR);
if (m_decalProperties.m_deferred)
{
if (passInfo.IsShadowPass())
{
return; // otherwise causing flickering with GI
}
SDeferredDecal newItem;
newItem.fAlpha = m_decalProperties.m_opacity;
newItem.angleAttenuation = m_decalProperties.m_angleAttenuation;
newItem.pMaterial = m_pMaterial;
newItem.projMatrix = m_Matrix;
newItem.nSortOrder = m_decalProperties.m_sortPrio;
newItem.nFlags = DECAL_STATIC;
GetRenderer()->EF_AddDeferredDecal(newItem);
return;
}
// update last rendered frame id
m_nLastRenderedFrameId = passInfo.GetMainFrameID();
bool bUpdateAABB = m_updateRequested;
if (passInfo.IsGeneralPass())
{
ProcessUpdateRequest();
}
if (m_decal && 0 != m_decal->m_pMaterial)
{
m_decal->m_vAmbient.x = rParam.AmbientColor.r;
m_decal->m_vAmbient.y = rParam.AmbientColor.g;
m_decal->m_vAmbient.z = rParam.AmbientColor.b;
bool bAfterWater = GetObjManager()->IsAfterWater(m_decal->m_vWSPos, passInfo);
m_decal->Render(0, bAfterWater, distFading, rParam.fDistance, passInfo, SRendItemSorter(rParam.rendItemSorter));
}
// terrain decal meshes are created only during rendering so only after that bbox can be computed
if (bUpdateAABB)
{
UpdateAABBFromRenderMeshes();
}
}
void CDecalRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
{
if (m_decal)
{
m_decal->m_pMaterial = pMat;
}
m_pMaterial = pMat;
//special check for def decals forcing
if (GetCVars()->e_DecalsForceDeferred)
{
CheckForceDeferred();
}
}
void CDecalRenderNode::Precache()
{
ProcessUpdateRequest();
}
void CDecalRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
{
SIZER_COMPONENT_NAME(pSizer, "DecalNode");
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_decal);
}
void CDecalRenderNode::CleanUpOldDecals()
{
if (m_nLastRenderedFrameId != 0 && // was rendered at least once
(int)GetRenderer()->GetFrameID(false) > (int)m_nLastRenderedFrameId + GetCVars()->e_DecalsMaxValidFrames)
{
DeleteDecal();
m_nLastRenderedFrameId = 0;
m_updateRequested = true; // make sure if rendered again, that the decal is recreated
}
}
void CDecalRenderNode::OffsetPosition(const Vec3& delta)
{
if (m_pRNTmpData)
{
m_pRNTmpData->OffsetPosition(delta);
}
m_pos += delta;
m_WSBBox.Move(delta);
m_Matrix.SetTranslation(m_Matrix.GetTranslation() + delta);
}
@@ -1,95 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_DECALRENDERNODE_H
#define CRYINCLUDE_CRY3DENGINE_DECALRENDERNODE_H
#pragma once
#include "DecalManager.h"
class CDecalRenderNode
: public IDecalRenderNode
, public Cry3DEngineBase
{
public:
// implements IDecalRenderNode
virtual void SetDecalProperties(const SDecalProperties& properties);
virtual const SDecalProperties* GetDecalProperties() const;
virtual void CleanUpOldDecals();
// implements IRenderNode
virtual IRenderNode* Clone() const;
virtual void SetMatrix(const Matrix34& mat);
virtual const Matrix34& GetMatrix() { return m_Matrix; }
virtual EERType GetRenderNodeType();
virtual const char* GetEntityClassName() const;
virtual const char* GetName() const;
virtual Vec3 GetPos(bool bWorldOnly = true) const;
virtual void Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo);
void SetMaterial(_smart_ptr<IMaterial> pMat) override;
virtual _smart_ptr<IMaterial> GetMaterial(Vec3* pHitPos = 0);
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return m_pMaterial; }
virtual float GetMaxViewDist();
virtual void Precache();
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
virtual const AABB GetBBox() const { return m_WSBBox; }
virtual void SetBBox(const AABB& WSBBox) { m_WSBBox = WSBBox; }
virtual void FillBBox(AABB& aabb);
virtual void OffsetPosition(const Vec3& delta);
virtual uint8 GetSortPriority() { return m_decalProperties.m_sortPrio; }
virtual void SetLayerId(uint16 nLayerId) { m_nLayerId = nLayerId; }
virtual uint16 GetLayerId() { return m_nLayerId; }
static void ResetDecalUpdatesCounter() { CDecalRenderNode::m_nFillBigDecalIndicesCounter = 0; }
// SetMatrix only supports changing position, this will do the full transform
void SetMatrixFull(const Matrix34& mat);
public:
CDecalRenderNode();
void RequestUpdate() { m_updateRequested = true; DeleteDecal(); }
void DeleteDecal();
private:
~CDecalRenderNode();
void CreateDecals();
void ProcessUpdateRequest();
void UpdateAABBFromRenderMeshes();
bool CheckForceDeferred();
void SetCommonProperties(CryEngineDecalInfo& decalInfo);
void CreatePlanarDecal();
void CreateDecalOnTerrain();
void CreateDecal(const CryEngineDecalInfo& decalInfo);
private:
Vec3 m_pos;
AABB m_localBounds;
_smart_ptr<IMaterial> m_pMaterial;
bool m_updateRequested;
SDecalProperties m_decalProperties;
CDecal* m_decal;
AABB m_WSBBox;
Matrix34 m_Matrix;
uint32 m_nLastRenderedFrameId;
uint16 m_nLayerId;
public:
static int m_nFillBigDecalIndicesCounter;
};
#endif // CRYINCLUDE_CRY3DENGINE_DECALRENDERNODE_H
@@ -1,226 +0,0 @@
/*
* 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 "DeferredCollisionEvent.h"
CDeferredPhysicsEventManager::CDeferredPhysicsEventManager()
: m_hThreadPool(-1)
, m_bEntitySystemReset(false)
{
if (gEnv->IsDedicated())
{
return;
}
// Disable MT if Editor.
if (gEnv->IsEditor())
{
return;
}
ThreadPoolDesc threadPoolDesc;
threadPoolDesc.sPoolName = "DeferredPhysicsEvents";
threadPoolDesc.nThreadStackSizeKB = 24;
// let the DeferredPhysicsEvents run on Core3
if (!threadPoolDesc.CreateThread(BIT(3)))
{
return;
}
IThreadTaskManager* pThreadTaskManager = gEnv->pSystem->GetIThreadTaskManager();
assert(pThreadTaskManager);
m_hThreadPool = pThreadTaskManager->CreateThreadsPool(threadPoolDesc);
}
CDeferredPhysicsEventManager::~CDeferredPhysicsEventManager()
{
}
void CDeferredPhysicsEventManager::DispatchDeferredEvent(IDeferredPhysicsEvent* pEvent)
{
assert(pEvent);
// execute immediately if we don't use deferred physcis events
if (GetCVars()->e_DeferredPhysicsEvents == 0 || m_hThreadPool < 0)
{
pEvent->OnUpdate();
return;
}
// Register the task with the ThreadTask manager
IThreadTaskManager* pThreadTaskManager = gEnv->pSystem->GetIThreadTaskManager();
assert(pThreadTaskManager);
pEvent->GetTaskInfo()->m_params.name = "DeferredPhysicsEvents";
pEvent->GetTaskInfo()->m_params.nFlags = THREAD_TASK_ASSIGN_TO_POOL;
pEvent->GetTaskInfo()->m_params.nThreadsGroupId = m_hThreadPool;
pEvent->GetTaskInfo()->m_pThread = NULL;
pThreadTaskManager->RegisterTask(pEvent, pEvent->GetTaskInfo()->m_params);
}
void ApplyCollisionImpulse(EventPhysCollision* pCollision)
{
if (pCollision->normImpulse && pCollision->pEntity[1] &&
pCollision->pEntity[0] && pCollision->pEntity[0]->GetType() == PE_PARTICLE &&
pCollision->pEntity[0]->GetForeignData(pCollision->pEntity[0]->GetiForeignData())) // no foreign data mean it's likely scheduled for deletion
{
pe_action_impulse ai;
ai.point = pCollision->pt;
ai.partid = pCollision->partid[1];
ai.impulse = (pCollision->vloc[0] - pCollision->vloc[1]) * pCollision->normImpulse;
pCollision->pEntity[1]->Action(&ai);
}
}
int CDeferredPhysicsEventManager::HandleEvent(const EventPhys* pEvent, IDeferredPhysicsEventManager::CreateEventFunc pCreateFunc, [[maybe_unused]] IDeferredPhysicsEvent::DeferredEventType type)
{
EventPhysCollision* pCollision = (EventPhysCollision*)pEvent;
assert(pCollision);
if (pCollision->deferredState == EPC_DEFERRED_FINISHED)
{
ApplyCollisionImpulse(pCollision);
return pCollision->deferredResult;
}
// == create new deferred event object, and do some housekeeping(ensuring entities not deleted, remebering event for cleanup) == //
IDeferredPhysicsEvent* pDeferredEvent = pCreateFunc(pEvent);
// == start executing == //
pDeferredEvent->Start();
// == check if we really needed to deferred this event(early outs, not deferred code paths) == //
if (GetCVars()->e_DeferredPhysicsEvents == 0 || pDeferredEvent->HasFinished())
{
int nResult = pDeferredEvent->Result((EventPhys*)pEvent);
SAFE_DELETE(pDeferredEvent);
ApplyCollisionImpulse(pCollision);
return nResult;
}
if (pCollision->pEntity[0])
{
pCollision->pEntity[0]->AddRef();
}
if (pCollision->pEntity[1])
{
pCollision->pEntity[1]->AddRef();
}
// == re-queue event for the next frame, to keep the physical entity alive == //
RegisterDeferredEvent(pDeferredEvent);
return 0;
}
void CDeferredPhysicsEventManager::RegisterDeferredEvent(IDeferredPhysicsEvent* pDeferredEvent)
{
assert(pDeferredEvent);
m_activeDeferredEvents.push_back(pDeferredEvent);
}
void CDeferredPhysicsEventManager::UnRegisterDeferredEvent(IDeferredPhysicsEvent* pDeferredEvent)
{
std::vector<IDeferredPhysicsEvent*>::iterator it = std::find(m_activeDeferredEvents.begin(), m_activeDeferredEvents.end(), pDeferredEvent);
if (it == m_activeDeferredEvents.end())
{
return;
}
// == remove from active list == //
m_activeDeferredEvents.erase(it);
if (m_bEntitySystemReset)
{
return;
}
// == decrement keep alive counter on entity == //
EventPhysCollision* pCollision = (EventPhysCollision*)pDeferredEvent->PhysicsEvent();
if (pCollision->pEntity[0])
{
pCollision->pEntity[0]->Release();
}
if (pCollision->pEntity[1])
{
pCollision->pEntity[1]->Release();
}
}
void CDeferredPhysicsEventManager::ClearDeferredEvents()
{
// move content of the active deferred events array to a tmp one to prevent provlems with UnRegisterDeferredEvent called by destructors
std::vector<IDeferredPhysicsEvent*> tmp = m_activeDeferredEvents;
m_bEntitySystemReset = true;
for (std::vector<IDeferredPhysicsEvent*>::iterator it = tmp.begin(); it != tmp.end(); ++it)
{
(*it)->Sync();
delete *it;
}
stl::free_container(m_activeDeferredEvents);
m_bEntitySystemReset = false;
}
void CDeferredPhysicsEventManager::Update()
{
std::vector<IDeferredPhysicsEvent*> tmp = m_activeDeferredEvents;
for (std::vector<IDeferredPhysicsEvent*>::iterator it = tmp.begin(), end = tmp.end(); it != end; ++it)
{
IDeferredPhysicsEvent* collisionEvent = *it;
assert(collisionEvent);
PREFAST_ASSUME(collisionEvent);
EventPhysCollision* epc = (EventPhysCollision*) collisionEvent->PhysicsEvent();
if (collisionEvent->HasFinished() == false)
{
continue;
}
epc->deferredResult = collisionEvent->Result();
if (epc->deferredState != EPC_DEFERRED_FINISHED)
{
epc->deferredState = EPC_DEFERRED_FINISHED;
}
else
{
SAFE_DELETE(collisionEvent);
}
}
}
IDeferredPhysicsEvent* CDeferredPhysicsEventManager::GetLastCollisionEventForEntity(IPhysicalEntity* pPhysEnt)
{
EventPhysCollision* pLastPhysEvent;
for (int i = m_activeDeferredEvents.size() - 1; i >= 0; i--)
{
pLastPhysEvent = (EventPhysCollision*)m_activeDeferredEvents[i]->PhysicsEvent();
if (pLastPhysEvent && pLastPhysEvent->idval == EventPhysCollision::id && pLastPhysEvent->pEntity[0] == pPhysEnt)
{
return m_activeDeferredEvents[i];
}
}
return 0;
}
@@ -1,47 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_DEFERREDCOLLISIONEVENT_H
#define CRYINCLUDE_CRY3DENGINE_DEFERREDCOLLISIONEVENT_H
#pragma once
#include <IDeferredCollisionEvent.h>
// Implementation class for the DeferredPhysicsEvent Manager
class CDeferredPhysicsEventManager
: public IDeferredPhysicsEventManager
, public Cry3DEngineBase
{
public:
CDeferredPhysicsEventManager();
virtual ~CDeferredPhysicsEventManager();
virtual void DispatchDeferredEvent(IDeferredPhysicsEvent* pEvent);
virtual int HandleEvent(const EventPhys* pEvent, IDeferredPhysicsEventManager::CreateEventFunc, IDeferredPhysicsEvent::DeferredEventType);
virtual void RegisterDeferredEvent(IDeferredPhysicsEvent* pDeferredEvent);
virtual void UnRegisterDeferredEvent(IDeferredPhysicsEvent* pDeferredEvent);
virtual void ClearDeferredEvents();
virtual void Update();
virtual IDeferredPhysicsEvent* GetLastCollisionEventForEntity(IPhysicalEntity* pPhysEnt);
private:
ThreadPoolHandle m_hThreadPool; // thread pool to use for deferred event tasks
std::vector<IDeferredPhysicsEvent*> m_activeDeferredEvents; // list of all active deferred events, used for cleanup and statistics
bool m_bEntitySystemReset; // means all entity ptrs in events are stale
};
#endif // CRYINCLUDE_CRY3DENGINE_DEFERREDCOLLISIONEVENT_H
@@ -1,194 +0,0 @@
/*
* 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 "DistanceCloudRenderNode.h"
#include "VisAreas.h"
#include "ObjMan.h"
#include "MatMan.h"
#include "Environment/OceanEnvironmentBus.h"
CDistanceCloudRenderNode::CDistanceCloudRenderNode()
: m_pos(0, 0, 0)
, m_sizeX(1)
, m_sizeY(1)
, m_rotationZ(0)
, m_pMaterial(NULL)
{
}
CDistanceCloudRenderNode::~CDistanceCloudRenderNode()
{
Get3DEngine()->FreeRenderNodeState(this);
}
SDistanceCloudProperties CDistanceCloudRenderNode::GetProperties() const
{
SDistanceCloudProperties properties;
properties.m_sizeX = m_sizeX;
properties.m_sizeY = m_sizeY;
properties.m_rotationZ = m_rotationZ;
properties.m_pos = m_pos;
properties.m_pMaterialName = 0; // query materialID instead!
return properties;
}
void CDistanceCloudRenderNode::SetProperties(const SDistanceCloudProperties& properties)
{
// register material
m_pMaterial = GetMatMan()->LoadMaterial(properties.m_pMaterialName, false);
// copy distance cloud properties
m_sizeX = properties.m_sizeX;
m_sizeY = properties.m_sizeY;
m_rotationZ = properties.m_rotationZ;
m_pos = properties.m_pos;
}
void CDistanceCloudRenderNode::SetMatrix(const Matrix34& mat)
{
Get3DEngine()->UnRegisterEntityAsJob(this);
m_pos = mat.GetTranslation();
m_WSBBox.SetTransformedAABB(mat, AABB(-Vec3(1, 1, 1e-4f), Vec3(1, 1, 1e-4f)));
Get3DEngine()->RegisterEntity(this);
}
const char* CDistanceCloudRenderNode::GetEntityClassName() const
{
return "DistanceCloud";
}
const char* CDistanceCloudRenderNode::GetName() const
{
return "DistanceCloud";
}
static inline uint16 HalfFlip(uint16 h)
{
uint16 mask = -int16(h >> 15) | 0x8000;
return h ^ mask;
}
void CDistanceCloudRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
_smart_ptr<IMaterial> pMaterial(GetMaterial());
if (!passInfo.RenderClouds() || !pMaterial)
{
return; // false;
}
CRenderObject* pOb(gEnv->pRenderer->EF_GetObject_Temp(passInfo.ThreadID()));
if (!pOb)
{
return; // false;
}
const CCamera& cam(passInfo.GetCamera());
float zDist = cam.GetPosition().z - m_pos.z;
if (cam.GetViewdir().z < 0)
{
zDist = -zDist;
}
pOb->m_nSort = HalfFlip(CryConvertFloatToHalf(zDist));
//pOb->m_II.m_Matrix.SetIdentity();
// fill general vertex data
f32 sinZ(0), cosZ(1);
sincos_tpl(DEG2RAD(m_rotationZ), &sinZ, &cosZ);
Vec3 right(m_sizeX * cosZ, m_sizeY * sinZ, 0);
Vec3 up(-m_sizeX * sinZ, m_sizeY * cosZ, 0);
SVF_P3F_C4B_T2F pVerts[4];
pVerts[0].xyz = (-right - up) + m_pos;
pVerts[0].st = Vec2(0, 1);
pVerts[0].color.dcolor = ~0;
pVerts[1].xyz = (right - up) + m_pos;
pVerts[1].st = Vec2(1, 1);
pVerts[1].color.dcolor = ~0;
pVerts[2].xyz = (right + up) + m_pos;
pVerts[2].st = Vec2(1, 0);
pVerts[2].color.dcolor = ~0;
pVerts[3].xyz = (-right + up) + m_pos;
pVerts[3].st = Vec2(0, 0);
pVerts[3].color.dcolor = ~0;
// prepare tangent space (tangent, bitangent) and fill it in
Vec3 rightUnit(cosZ, sinZ, 0);
Vec3 upUnit(-sinZ, cosZ, 0);
SPipTangents pTangents[4];
pTangents[0] = SPipTangents(rightUnit, -upUnit, 1);
pTangents[1] = pTangents[0];
pTangents[2] = pTangents[0];
pTangents[3] = pTangents[0];
// prepare indices
uint16 pIndices[6];
pIndices[0] = 0;
pIndices[1] = 1;
pIndices[2] = 2;
pIndices[3] = 0;
pIndices[4] = 2;
pIndices[5] = 3;
int afterWater(GetObjManager()->IsAfterWater(m_pos, passInfo));
GetRenderer()->EF_AddPolygonToScene(pMaterial->GetShaderItem(), 4, pVerts, pTangents, pOb, passInfo, pIndices, 6, afterWater, SRendItemSorter(rParam.rendItemSorter));
// return true;
}
void CDistanceCloudRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
{
m_pMaterial = pMat;
}
void CDistanceCloudRenderNode::Precache()
{
}
void CDistanceCloudRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
{
SIZER_COMPONENT_NAME(pSizer, "DistanceCloudNode");
pSizer->AddObject(this, sizeof(*this));
}
void CDistanceCloudRenderNode::OffsetPosition(const Vec3& delta)
{
if (m_pRNTmpData)
{
m_pRNTmpData->OffsetPosition(delta);
}
m_pos += delta;
m_WSBBox.Move(delta);
}
@@ -1,66 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_DISTANCECLOUDRENDERNODE_H
#define CRYINCLUDE_CRY3DENGINE_DISTANCECLOUDRENDERNODE_H
#pragma once
class CDistanceCloudRenderNode
: public IDistanceCloudRenderNode
, public Cry3DEngineBase
{
public:
// implements IDistanceCloudRenderNode
virtual void SetProperties(const SDistanceCloudProperties& properties);
// implements IRenderNode
virtual void SetMatrix(const Matrix34& mat);
virtual EERType GetRenderNodeType();
virtual const char* GetEntityClassName() const;
virtual const char* GetName() const;
virtual Vec3 GetPos(bool bWorldOnly = true) const;
virtual void Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo);
void SetMaterial(_smart_ptr<IMaterial> pMat) override;
virtual _smart_ptr<IMaterial> GetMaterial(Vec3* pHitPos = 0);
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return m_pMaterial; }
virtual float GetMaxViewDist();
virtual void Precache();
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
virtual const AABB GetBBox() const { return m_WSBBox; }
virtual void SetBBox(const AABB& WSBBox) { m_WSBBox = WSBBox; }
virtual void FillBBox(AABB& aabb);
virtual void OffsetPosition(const Vec3& delta);
virtual void SetLayerId(uint16 nLayerId) { m_nLayerId = nLayerId; }
virtual uint16 GetLayerId() { return m_nLayerId; }
public:
CDistanceCloudRenderNode();
SDistanceCloudProperties GetProperties() const;
private:
~CDistanceCloudRenderNode();
private:
Vec3 m_pos;
float m_sizeX;
float m_sizeY;
float m_rotationZ;
_smart_ptr< IMaterial > m_pMaterial;
AABB m_WSBBox;
uint16 m_nLayerId;
};
#endif // CRYINCLUDE_CRY3DENGINE_DISTANCECLOUDRENDERNODE_H
@@ -1,329 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector3.h>
#include <I3DEngine.h>
#include <ISystem.h>
#include <OceanConstants.h>
namespace AZ
{
/**
* Feature toggle for the ocean feature(s)
*/
class OceanFeatureToggle
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
virtual ~OceanFeatureToggle() = default;
virtual bool OceanComponentEnabled() const { return false; }
};
using OceanFeatureToggleBus = AZ::EBus<OceanFeatureToggle>;
/*!
* Messages services for environment data points
* Note: The Gem for Water is meant to override this when enabled in a project
*/
class OceanEnvironmentRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// flags for toggling ocean reflections
enum class ReflectionFlags
{
Entities = SRenderingPassInfo::ENTITIES,
StaticObjects = SRenderingPassInfo::STATIC_OBJECTS
};
// Ocean requests
virtual bool OceanIsEnabled() const = 0;
// Fast option - use if just ocean height required
virtual float GetOceanLevel() const = 0;
virtual void SetOceanLevel(float oceanLevel) = 0;
virtual float GetOceanLevelOrDefault(const float defaultValue) const = 0;
// This will return ocean height or water volume height, optional for accurate water height query
virtual float GetWaterLevel(const Vec3& position) const = 0;
// Only use for Accurate query - this will return exact ocean height
virtual float GetAccurateOceanHeight(const Vec3& position) const = 0;
// gets the amount of water tessellation
virtual int GetWaterTessellationAmount() const = 0;
virtual void SetWaterTessellationAmount(int amount) = 0;
// the ocean material asset
virtual const AZStd::string& GetOceanMaterialName() const = 0;
virtual void SetOceanMaterialName(const AZStd::string& matName) = 0;
// Animation data
virtual float GetAnimationWindDirection() const = 0;
virtual float GetAnimationWindSpeed() const = 0;
virtual float GetAnimationWavesSpeed() const = 0;
virtual float GetAnimationWavesSize() const = 0;
virtual float GetAnimationWavesAmount() const = 0;
virtual void SetAnimationWindDirection(float dir) = 0;
virtual void SetAnimationWindSpeed(float speed) = 0;
virtual void SetAnimationWavesSpeed(float speed) = 0;
virtual void SetAnimationWavesSize(float size) = 0;
virtual void SetAnimationWavesAmount(float amount) = 0;
// Ocean reflection
virtual void ApplyReflectRenderFlags(int& flags) const = 0;
virtual bool GetReflectRenderFlag(ReflectionFlags flag) const = 0;
virtual float GetReflectResolutionScale() const = 0;
virtual bool GetReflectionAnisotropic() const = 0;
virtual void SetReflectRenderFlag(ReflectionFlags flag, bool value) = 0;
virtual void SetReflectResolutionScale(float scale) = 0;
virtual void SetReflectionAnisotropic(bool enabled) = 0;
// Ocean bottom
virtual bool GetUseOceanBottom() const = 0;
virtual void SetUseOceanBottom(bool use) = 0;
// Underwater Effects
virtual bool GetGodRaysEnabled() const = 0;
virtual void SetGodRaysEnabled(bool enabled) = 0;
virtual float GetUnderwaterDistortion() const = 0;
virtual void SetUnderwaterDistortion(float) = 0;
// Caustics
virtual bool GetCausticsEnabled() const = 0;
virtual float GetCausticsDepth() const = 0;
virtual float GetCausticsIntensity() const = 0;
virtual float GetCausticsTiling() const = 0;
virtual float GetCausticsDistanceAttenuation() const = 0;
virtual void SetCausticsEnabled(bool enable) = 0;
virtual void SetCausticsDepth(float depth) = 0;
virtual void SetCausticsIntensity(float intensity) = 0;
virtual void SetCausticsTiling(float tiling) = 0;
virtual void SetCausticsDistanceAttenuation(float dist) = 0;
// Ocean fog data
virtual AZ::Color GetFogColorPremultiplied() const = 0;
virtual AZ::Color GetFogColor() const = 0;
virtual void SetFogColor(const AZ::Color& fogColor) = 0;
virtual float GetFogColorMultiplier() const = 0;
virtual void SetFogColorMultiplier(float fogMultiplier) = 0;
virtual AZ::Color GetNearFogColor() const = 0;
virtual void SetNearFogColor(const AZ::Color& nearColor) = 0;
virtual float GetFogDensity() const = 0;
virtual void SetFogDensity(float density) = 0;
};
using OceanEnvironmentBus = AZ::EBus<OceanEnvironmentRequests>;
} // namespace AZ
namespace OceanToggle
{
/**
* As long as the Water gem is in a preview state, the legacy code and data will be protected by this feature toggle check.
*/
AZ_INLINE bool IsActive()
{
bool bHasOceanFeature = false;
AZ::OceanFeatureToggleBus::BroadcastResult(bHasOceanFeature, &AZ::OceanFeatureToggleBus::Events::OceanComponentEnabled);
return bHasOceanFeature;
}
} // namespace OceanToggle
namespace OceanRequest
{
AZ_INLINE bool OceanIsEnabled()
{
bool enabled = false;
AZ::OceanEnvironmentBus::BroadcastResult(enabled, &AZ::OceanEnvironmentBus::Events::OceanIsEnabled);
return enabled;
}
// Ocean level
AZ_INLINE float GetOceanLevel()
{
float fWaterLevel = AZ::OceanConstants::s_HeightUnknown;
AZ::OceanEnvironmentBus::BroadcastResult(fWaterLevel, &AZ::OceanEnvironmentBus::Events::GetOceanLevel);
return fWaterLevel;
}
AZ_INLINE float GetOceanLevelOrDefault(const float defaultValue)
{
return OceanIsEnabled() ? GetOceanLevel() : defaultValue;
}
AZ_INLINE float GetWaterLevel(const Vec3& position)
{
float fWaterLevel = AZ::OceanConstants::s_HeightUnknown;
AZ::OceanEnvironmentBus::BroadcastResult(fWaterLevel, &AZ::OceanEnvironmentBus::Events::GetWaterLevel, position);
return fWaterLevel;
}
AZ_INLINE float GetAccurateOceanHeight(const Vec3& position)
{
float fWaterLevel = AZ::OceanConstants::s_HeightUnknown;
AZ::OceanEnvironmentBus::BroadcastResult(fWaterLevel, &AZ::OceanEnvironmentBus::Events::GetAccurateOceanHeight, position);
return fWaterLevel;
}
// the ocean material
AZ_INLINE AZStd::string GetOceanMaterialName()
{
AZStd::string value = "EngineAssets/Materials/Water/Ocean_default.mtl";
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetOceanMaterialName);
return value;
}
// Wave animation data
AZ_INLINE float GetWavesAmount()
{
float value = AZ::OceanConstants::s_animationWavesAmountDefault;
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetAnimationWavesAmount);
return value;
}
AZ_INLINE float GetWavesSpeed()
{
float value = AZ::OceanConstants::s_animationWavesSpeedDefault;
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetAnimationWavesSpeed);
return value;
}
AZ_INLINE float GetWavesSize()
{
float value = AZ::OceanConstants::s_animationWavesSizeDefault;
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetAnimationWavesSize);
return value;
}
AZ_INLINE float GetWindDirection()
{
float value = AZ::OceanConstants::s_animationWindDirectionDefault;
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetAnimationWindDirection);
return value;
}
AZ_INLINE float GetWindSpeed()
{
float value = AZ::OceanConstants::s_animationWindSpeedDefault;
AZ::OceanEnvironmentBus::BroadcastResult(value, &AZ::OceanEnvironmentBus::Events::GetAnimationWindSpeed);
return value;
}
// Ocean bottom
AZ_INLINE bool GetUseOceanBottom()
{
bool useOceanBottom = AZ::OceanConstants::s_UseOceanBottom;
AZ::OceanEnvironmentBus::BroadcastResult(useOceanBottom, &AZ::OceanEnvironmentBus::Events::GetUseOceanBottom);
return useOceanBottom;
}
AZ_INLINE bool GetGodRaysEnabled()
{
bool godRaysEnabled = AZ::OceanConstants::s_GodRaysEnabled;
AZ::OceanEnvironmentBus::BroadcastResult(godRaysEnabled, &AZ::OceanEnvironmentBus::Events::GetGodRaysEnabled);
return godRaysEnabled;
}
AZ_INLINE float GetUnderwaterDistortion()
{
float underwaterDistortion = AZ::OceanConstants::s_UnderwaterDistortion;
AZ::OceanEnvironmentBus::BroadcastResult(underwaterDistortion, &AZ::OceanEnvironmentBus::Events::GetUnderwaterDistortion);
return underwaterDistortion;
}
// Cuastics
AZ_INLINE bool GetCausticsEnabled()
{
bool causticsEnabled = false;
AZ::OceanEnvironmentBus::BroadcastResult(causticsEnabled, &AZ::OceanEnvironmentBus::Events::GetCausticsEnabled);
return causticsEnabled;
}
AZ_INLINE float GetCausticsDepth()
{
float causticsDepth = AZ::OceanConstants::s_CausticsDepthDefault;
AZ::OceanEnvironmentBus::BroadcastResult(causticsDepth, &AZ::OceanEnvironmentBus::Events::GetCausticsDepth);
return causticsDepth;
}
AZ_INLINE float GetCausticsIntensity()
{
float causticsIntensity = AZ::OceanConstants::s_CausticsIntensityDefault;
AZ::OceanEnvironmentBus::BroadcastResult(causticsIntensity, &AZ::OceanEnvironmentBus::Events::GetCausticsIntensity);
return causticsIntensity;
}
AZ_INLINE float GetCausticsTiling()
{
float causticsTiling = AZ::OceanConstants::s_CausticsTilingDefault;
AZ::OceanEnvironmentBus::BroadcastResult(causticsTiling, &AZ::OceanEnvironmentBus::Events::GetCausticsTiling);
return causticsTiling;
}
AZ_INLINE float GetCausticsDistanceAttenuation()
{
float causticsDistanceAtten = AZ::OceanConstants::s_CausticsDistanceAttenDefault;
AZ::OceanEnvironmentBus::BroadcastResult(causticsDistanceAtten, &AZ::OceanEnvironmentBus::Events::GetCausticsDistanceAttenuation);
return causticsDistanceAtten;
}
// Ocean fog
AZ_INLINE AZ::Vector3 GetFogColorPremultiplied()
{
AZ::Color fogColor = AZ::OceanConstants::s_oceanFogColorDefault;
AZ::OceanEnvironmentBus::BroadcastResult(fogColor, &AZ::OceanEnvironmentBus::Events::GetFogColorPremultiplied);
return fogColor.GetAsVector3();
}
AZ_INLINE AZ::Vector3 GetNearFogColor()
{
AZ::Color nearFogColor = AZ::OceanConstants::s_oceanNearFogColorDefault;
AZ::OceanEnvironmentBus::BroadcastResult(nearFogColor, &AZ::OceanEnvironmentBus::Events::GetNearFogColor);
return nearFogColor.GetAsVector3();
}
AZ_INLINE float GetFogDensity()
{
float fogDensity = AZ::OceanConstants::s_oceanFogDensityDefault;
AZ::OceanEnvironmentBus::BroadcastResult(fogDensity, &AZ::OceanEnvironmentBus::Events::GetFogDensity);
return fogDensity;
}
} // namespace OceanRequest
@@ -1,546 +0,0 @@
/*
* 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 "ITimeOfDay.h"
#include "EnvironmentPreset.h"
#include <Serialization/IArchive.h>
#include <Serialization/IArchiveHost.h>
#include <Serialization/ClassFactory.h>
#include <Serialization/Enum.h>
#include <Bezier.h>
#include <Serialization/Enum.h>
SERIALIZATION_ENUM_BEGIN_NESTED(SBezierControlPoint, ETangentType, "TangentType")
SERIALIZATION_ENUM_VALUE_NESTED(SBezierControlPoint, eTangentType_Custom, "Custom")
SERIALIZATION_ENUM_VALUE_NESTED(SBezierControlPoint, eTangentType_Auto, "Smooth")
SERIALIZATION_ENUM_VALUE_NESTED(SBezierControlPoint, eTangentType_Zero, "Zero")
SERIALIZATION_ENUM_VALUE_NESTED(SBezierControlPoint, eTangentType_Step, "Step")
SERIALIZATION_ENUM_VALUE_NESTED(SBezierControlPoint, eTangentType_Linear, "Linear")
SERIALIZATION_ENUM_END()
namespace EnvironmentPresetDetails
{
static const float sBezierSplineKeyValueEpsilon = 0.001f;
//////////////////////////////////////////////////////////////////////////
SBezierKey ApplyInTangent(const SBezierKey& key, const SBezierKey& leftKey, const SBezierKey* pRightKey)
{
SBezierKey newKey = key;
if (leftKey.m_controlPoint.m_outTangentType == SBezierControlPoint::eTangentType_Step)
{
newKey.m_controlPoint.m_inTangent = Vec2(0.0f, 0.0f);
return newKey;
}
else if (key.m_controlPoint.m_inTangentType != SBezierControlPoint::eTangentType_Step)
{
const SAnimTime leftTime = leftKey.m_time;
const SAnimTime rightTime = pRightKey ? pRightKey->m_time : key.m_time;
// Rebase to [0, rightTime - leftTime] to increase float precision
const float floatTime = (key.m_time - leftTime).ToFloat();
const float floatLeftTime = 0.0f;
const float floatRightTime = (rightTime - leftTime).ToFloat();
newKey.m_controlPoint = Bezier::CalculateInTangent(floatTime, key.m_controlPoint,
floatLeftTime, &leftKey.m_controlPoint,
floatRightTime, pRightKey ? &pRightKey->m_controlPoint : NULL);
}
else
{
newKey.m_controlPoint.m_inTangent = Vec2(0.0f, 0.0f);
newKey.m_controlPoint.m_value = leftKey.m_controlPoint.m_value;
}
return newKey;
}
SBezierKey ApplyOutTangent(const SBezierKey& key, const SBezierKey* pLeftKey, const SBezierKey& rightKey)
{
SBezierKey newKey = key;
if (rightKey.m_controlPoint.m_inTangentType == SBezierControlPoint::eTangentType_Step
&& key.m_controlPoint.m_outTangentType != SBezierControlPoint::eTangentType_Step)
{
newKey.m_controlPoint.m_outTangent = Vec2(0.0f, 0.0f);
}
else if (key.m_controlPoint.m_outTangentType != SBezierControlPoint::eTangentType_Step)
{
const SAnimTime leftTime = pLeftKey ? pLeftKey->m_time : key.m_time;
const SAnimTime rightTime = rightKey.m_time;
// Rebase to [0, rightTime - leftTime] to increase float precision
const float floatTime = (key.m_time - leftTime).ToFloat();
const float floatLeftTime = 0.0f;
const float floatRightTime = (rightTime - leftTime).ToFloat();
newKey.m_controlPoint = Bezier::CalculateOutTangent(floatTime, key.m_controlPoint,
floatLeftTime, pLeftKey ? &pLeftKey->m_controlPoint : NULL,
floatRightTime, &rightKey.m_controlPoint);
}
else
{
newKey.m_controlPoint.m_outTangent = Vec2(0.0f, 0.0f);
newKey.m_controlPoint.m_value = rightKey.m_controlPoint.m_value;
}
return newKey;
}
}
CBezierSpline::CBezierSpline()
{
m_keys.reserve(2);
}
CBezierSpline::~CBezierSpline()
{
}
void CBezierSpline::Init(float fDefaultValue)
{
m_keys.clear();
InsertKey(SAnimTime(0.0f), fDefaultValue);
InsertKey(SAnimTime(1.0f), fDefaultValue);
}
float CBezierSpline::Evaluate(float t) const
{
if (m_keys.size() == 0)
{
return 0.0f;
}
if (m_keys.size() == 1)
{
return m_keys.front().m_controlPoint.m_value;
}
const SAnimTime time(t);
if (time <= m_keys.front().m_time)
{
return m_keys.front().m_controlPoint.m_value;
}
else if (time >= m_keys.back().m_time)
{
return m_keys.back().m_controlPoint.m_value;
}
const TKeyContainer::const_iterator it = std::upper_bound(m_keys.begin(), m_keys.end(), time, SCompKeyTime());
const TKeyContainer::const_iterator startIt = it - 1;
if (startIt->m_controlPoint.m_outTangentType == SBezierControlPoint::eTangentType_Step)
{
return it->m_controlPoint.m_value;
}
if (it->m_controlPoint.m_inTangentType == SBezierControlPoint::eTangentType_Step)
{
return startIt->m_controlPoint.m_value;
}
const SAnimTime deltaTime = it->m_time - startIt->m_time;
if (deltaTime == SAnimTime(0))
{
return startIt->m_controlPoint.m_value;
}
const float timeInSegment = (time - startIt->m_time).ToFloat();
const SBezierKey* pKeyLeftOfSegment = (startIt != m_keys.begin()) ? &*(startIt - 1) : NULL;
const SBezierKey* pKeyRightOfSegment = (startIt != (m_keys.end() - 2)) ? &*(startIt + 2) : NULL;
const SBezierKey segmentStart = EnvironmentPresetDetails::ApplyOutTangent(*startIt, pKeyLeftOfSegment, *(startIt + 1));
const SBezierKey segmentEnd = EnvironmentPresetDetails::ApplyInTangent(*(startIt + 1), *startIt, pKeyRightOfSegment);
const float factor = Bezier::InterpolationFactorFromX(timeInSegment, deltaTime.ToFloat(), segmentStart.m_controlPoint, segmentEnd.m_controlPoint);
const float fResult = Bezier::EvaluateY(factor, segmentStart.m_controlPoint, segmentEnd.m_controlPoint);
return fResult;
}
void CBezierSpline::InsertKey(SAnimTime time, float value)
{
SBezierKey key;
key.m_time = time;
key.m_controlPoint.m_value = value;
const size_t nKeyNum = m_keys.size();
for (size_t i = 0; i < nKeyNum; ++i)
{
if (m_keys[i].m_time > time)
{
m_keys.insert(m_keys.begin() + i, key);
return;
}
}
m_keys.push_back(key);
}
void CBezierSpline::UpdateKeyForTime(float fTime, float value)
{
const SAnimTime time(fTime);
const size_t nKeyNum = m_keys.size();
for (size_t i = 0; i < nKeyNum; ++i)
{
if (fabs(m_keys[i].m_time.ToFloat() - fTime) < EnvironmentPresetDetails::sBezierSplineKeyValueEpsilon)
{
m_keys[i].m_controlPoint.m_value = value;
return;
}
}
InsertKey(time, value);
}
void CBezierSpline::Serialize(Serialization::IArchive& ar)
{
ar(m_keys, "keys");
}
//////////////////////////////////////////////////////////////////////////
CTimeOfDayVariable::CTimeOfDayVariable()
: m_id(ITimeOfDay::PARAM_TOTAL)
, m_type(ITimeOfDay::TYPE_FLOAT)
, m_name(NULL)
, m_displayName(NULL)
, m_group(NULL)
, m_minValue(0.0f)
, m_maxValue(0.0f)
, m_value(ZERO)
{
}
CTimeOfDayVariable::~CTimeOfDayVariable()
{
}
void CTimeOfDayVariable::Init(const char* group, const char* displayName, const char* name, ITimeOfDay::ETimeOfDayParamID nParamId, ITimeOfDay::EVariableType type, float defVal0, float defVal1, float defVal2)
{
m_id = nParamId;
m_type = type;
m_name = name;
m_displayName = (displayName && *displayName) ? displayName : name;
m_group = (group && *group) ? group : "Default";
if (ITimeOfDay::TYPE_FLOAT == type)
{
m_value.x = defVal0;
m_minValue = defVal1;
m_maxValue = defVal2;
m_spline[0].Init(defVal0);
}
else if (ITimeOfDay::TYPE_COLOR == type)
{
m_value.x = defVal0;
m_value.y = defVal1;
m_value.z = defVal2;
m_minValue = 0.0f;
m_maxValue = 1.0f;
m_spline[0].Init(defVal0);
m_spline[1].Init(defVal1);
m_spline[2].Init(defVal2);
}
}
void CTimeOfDayVariable::Update(float time)
{
m_value = GetInterpolatedAt(time);
}
Vec3 CTimeOfDayVariable::GetInterpolatedAt(float t) const
{
Vec3 result;
result.x = clamp_tpl(m_spline[0].Evaluate(t), m_minValue, m_maxValue);
result.y = clamp_tpl(m_spline[1].Evaluate(t), m_minValue, m_maxValue);
result.z = clamp_tpl(m_spline[2].Evaluate(t), m_minValue, m_maxValue);
return result;
}
size_t CTimeOfDayVariable::GetSplineKeyCount(int nSpline) const
{
if (const CBezierSpline* pSpline = GetSpline(nSpline))
{
return pSpline->GetKeyCount();
}
return 0;
}
bool CTimeOfDayVariable::GetSplineKeys(int nSpline, SBezierKey* keysArray, unsigned int keysArraySize) const
{
if (const CBezierSpline* pSpline = GetSpline(nSpline))
{
if (keysArraySize < pSpline->GetKeyCount())
{
return false;
}
pSpline->GetKeys(keysArray);
return true;
}
return false;
}
bool CTimeOfDayVariable::SetSplineKeys(int nSpline, const SBezierKey* keysArray, unsigned int keysArraySize)
{
if (CBezierSpline* pSpline = GetSpline(nSpline))
{
pSpline->SetKeys(keysArray, keysArraySize);
return true;
}
return false;
}
bool CTimeOfDayVariable::UpdateSplineKeyForTime(int nSpline, float fTime, float newKey)
{
if (CBezierSpline* pSpline = GetSpline(nSpline))
{
pSpline->UpdateKeyForTime(fTime, newKey);
return true;
}
return false;
}
void CTimeOfDayVariable::Serialize(Serialization::IArchive& ar)
{
ITimeOfDay::ETimeOfDayParamID defID = m_id;
ITimeOfDay::EVariableType defType = m_type;
ar(defID, "id");
ar(defType, "type");
if (!ar.IsInput() || (defID == m_id && defType == m_type))
{ // Write always, Read only when ID and Type are correct/Schema hasn't changed
ar(m_minValue, "minValue");
ar(m_maxValue, "maxValue");
ar(m_spline[0], "spline0");
ar(m_spline[1], "spline1");
ar(m_spline[2], "spline2");
}
}
//////////////////////////////////////////////////////////////////////////
CEnvironmentPreset::CEnvironmentPreset()
{
ResetVariables();
}
CEnvironmentPreset::~CEnvironmentPreset()
{
}
void CEnvironmentPreset::ResetVariables()
{
const float fRecip255 = 1.0f / 255.0f;
AddVar("Sun", "", "Sun color", ITimeOfDay::PARAM_SUN_COLOR, ITimeOfDay::TYPE_COLOR, 255.0f * fRecip255, 248.0f * fRecip255, 248.0f * fRecip255);
AddVar("Sun", "Sun intensity (lux)", "Sun intensity", ITimeOfDay::PARAM_SUN_INTENSITY, ITimeOfDay::TYPE_FLOAT, 119000.0f, 0.0f, 550000.0f);
AddVar("Sun", "", "Sun specular multiplier", ITimeOfDay::PARAM_SUN_SPECULAR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 4.0f);
AddVar("Fog", "Color (bottom)", "Fog color", ITimeOfDay::PARAM_FOG_COLOR, ITimeOfDay::TYPE_COLOR, 0.0f, 0.0f, 0.0f);
AddVar("Fog", "Color (bottom) multiplier", "Fog color multiplier", ITimeOfDay::PARAM_FOG_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 16.0f);
AddVar("Fog", "Height (bottom)", "Fog height (bottom)", ITimeOfDay::PARAM_VOLFOG_HEIGHT, ITimeOfDay::TYPE_FLOAT, 0.0f, -5000.0f, 30000.0f);
AddVar("Fog", "Density (bottom)", "Fog layer density (bottom)", ITimeOfDay::PARAM_VOLFOG_DENSITY, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Fog", "Color (top)", "Fog color (top)", ITimeOfDay::PARAM_FOG_COLOR2, ITimeOfDay::TYPE_COLOR, 0.0f, 0.0f, 0.0f);
AddVar("Fog", "Color (top) multiplier", "Fog color (top) multiplier", ITimeOfDay::PARAM_FOG_COLOR2_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 16.0f);
AddVar("Fog", "Height (top)", "Fog height (top)", ITimeOfDay::PARAM_VOLFOG_HEIGHT2, ITimeOfDay::TYPE_FLOAT, 4000.0f, -5000.0f, 30000.0f);
AddVar("Fog", "Density (top)", "Fog layer density (top)", ITimeOfDay::PARAM_VOLFOG_DENSITY2, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f);
AddVar("Fog", "Color height offset", "Fog color height offset", ITimeOfDay::PARAM_VOLFOG_HEIGHT_OFFSET, ITimeOfDay::TYPE_FLOAT, 0.0f, -1.0f, 1.0f);
AddVar("Fog", "Color (radial)", "Fog color (radial)", ITimeOfDay::PARAM_FOG_RADIAL_COLOR, ITimeOfDay::TYPE_COLOR, 0.0f, 0.0f, 0.0f);
AddVar("Fog", "Color (radial) multiplier", "Fog color (radial) multiplier", ITimeOfDay::PARAM_FOG_RADIAL_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 16.0f);
AddVar("Fog", "Radial size", "Fog radial size", ITimeOfDay::PARAM_VOLFOG_RADIAL_SIZE, ITimeOfDay::TYPE_FLOAT, 0.75f, 0.0f, 1.0f);
AddVar("Fog", "Radial lobe", "Fog radial lobe", ITimeOfDay::PARAM_VOLFOG_RADIAL_LOBE, ITimeOfDay::TYPE_FLOAT, 0.5f, 0.0f, 1.0f);
AddVar("Fog", "Global density", "Volumetric fog: Global density", ITimeOfDay::PARAM_VOLFOG_GLOBAL_DENSITY, ITimeOfDay::TYPE_FLOAT, 0.02f, 0.0f, 100.0f);
AddVar("Fog", "Final density clamp", "Volumetric fog: Final density clamp", ITimeOfDay::PARAM_VOLFOG_FINAL_DENSITY_CLAMP, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Fog", "Ramp start", "Volumetric fog: Ramp start", ITimeOfDay::PARAM_VOLFOG_RAMP_START, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 30000.0f);
AddVar("Fog", "Ramp end", "Volumetric fog: Ramp end", ITimeOfDay::PARAM_VOLFOG_RAMP_END, ITimeOfDay::TYPE_FLOAT, 100.0f, 0.0f, 30000.0f);
AddVar("Fog", "Ramp influence", "Volumetric fog: Ramp influence", ITimeOfDay::PARAM_VOLFOG_RAMP_INFLUENCE, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f);
AddVar("Fog", "Shadow darkening", "Volumetric fog: Shadow darkening", ITimeOfDay::PARAM_VOLFOG_SHADOW_DARKENING, ITimeOfDay::TYPE_FLOAT, 0.25f, 0.0f, 1.0f);
AddVar("Fog", "Shadow darkening sun", "Volumetric fog: Shadow darkening sun", ITimeOfDay::PARAM_VOLFOG_SHADOW_DARKENING_SUN, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Fog", "Shadow darkening ambient", "Volumetric fog: Shadow darkening ambient", ITimeOfDay::PARAM_VOLFOG_SHADOW_DARKENING_AMBIENT, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Fog", "Shadow range", "Volumetric fog: Shadow range", ITimeOfDay::PARAM_VOLFOG_SHADOW_RANGE, ITimeOfDay::TYPE_FLOAT, 0.1f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Height (bottom)", "Volumetric fog 2: Fog height (bottom)", ITimeOfDay::PARAM_VOLFOG2_HEIGHT, ITimeOfDay::TYPE_FLOAT, 0.0f, -5000.0f, 30000.0f);
AddVar("Volumetric fog", "Density (bottom)", "Volumetric fog 2: Fog layer density (bottom)", ITimeOfDay::PARAM_VOLFOG2_DENSITY, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Height (top)", "Volumetric fog 2: Fog height (top)", ITimeOfDay::PARAM_VOLFOG2_HEIGHT2, ITimeOfDay::TYPE_FLOAT, 4000.0f, -5000.0f, 30000.0f);
AddVar("Volumetric fog", "Density (top)", "Volumetric fog 2: Fog layer density (top)", ITimeOfDay::PARAM_VOLFOG2_DENSITY2, ITimeOfDay::TYPE_FLOAT, 0.0001f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Global density", "Volumetric fog 2: Global fog density", ITimeOfDay::PARAM_VOLFOG2_GLOBAL_DENSITY, ITimeOfDay::TYPE_FLOAT, 0.1f, 0.0f, 100.0f);
AddVar("Volumetric fog", "Ramp start", "Volumetric fog 2: Ramp start", ITimeOfDay::PARAM_VOLFOG2_RAMP_START, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 30000.0f);
AddVar("Volumetric fog", "Ramp end", "Volumetric fog 2: Ramp end", ITimeOfDay::PARAM_VOLFOG2_RAMP_END, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 30000.0f);
AddVar("Volumetric fog", "Color (atmosphere)", "Volumetric fog 2: Fog albedo color (atmosphere)", ITimeOfDay::PARAM_VOLFOG2_COLOR1, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("Volumetric fog", "Anisotropy (atmosphere)", "Volumetric fog 2: Anisotropy factor (atmosphere)", ITimeOfDay::PARAM_VOLFOG2_ANISOTROPIC1, ITimeOfDay::TYPE_FLOAT, 0.2f, -1.0f, 1.0f);
AddVar("Volumetric fog", "Color (sun radial)", "Volumetric fog 2: Fog albedo color (sun radial)", ITimeOfDay::PARAM_VOLFOG2_COLOR2, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("Volumetric fog", "Anisotropy (sun radial)", "Volumetric fog 2: Anisotropy factor (sun radial)", ITimeOfDay::PARAM_VOLFOG2_ANISOTROPIC2, ITimeOfDay::TYPE_FLOAT, 0.95f, -1.0f, 1.0f);
AddVar("Volumetric fog", "Radial blend factor", "Volumetric fog 2: Blend factor for sun scattering", ITimeOfDay::PARAM_VOLFOG2_BLEND_FACTOR, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Radial blend mode", "Volumetric fog 2: Blend mode for sun scattering", ITimeOfDay::PARAM_VOLFOG2_BLEND_MODE, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Range", "Volumetric fog 2: Maximum range of ray-marching", ITimeOfDay::PARAM_VOLFOG2_RANGE, ITimeOfDay::TYPE_FLOAT, 64.0f, 0.0f, 8192.0f);
AddVar("Volumetric fog", "In-scattering", "Volumetric fog 2: In-scattering factor", ITimeOfDay::PARAM_VOLFOG2_INSCATTER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 100.0f);
AddVar("Volumetric fog", "Extinction", "Volumetric fog 2: Extinction factor", ITimeOfDay::PARAM_VOLFOG2_EXTINCTION, ITimeOfDay::TYPE_FLOAT, 0.3f, 0.0f, 100.0f);
AddVar("Volumetric fog", "Color (entities)", "Volumetric fog 2: Fog albedo color (entities)", ITimeOfDay::PARAM_VOLFOG2_COLOR, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("Volumetric fog", "Anisotropy (entities)", "Volumetric fog 2: Anisotropy factor (entities)", ITimeOfDay::PARAM_VOLFOG2_ANISOTROPIC, ITimeOfDay::TYPE_FLOAT, 0.6f, -1.0f, 1.0f);
AddVar("Volumetric fog", "Analytical fog visibility", "Volumetric fog 2: Analytical volumetric fog visibility", ITimeOfDay::PARAM_VOLFOG2_GLOBAL_FOG_VISIBILITY, ITimeOfDay::TYPE_FLOAT, 0.5f, 0.0f, 1.0f);
AddVar("Volumetric fog", "Final density clamp", "Volumetric fog 2: Final density clamp", ITimeOfDay::PARAM_VOLFOG2_FINAL_DENSITY_CLAMP, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Sky Light", "Sun intensity", "Sky light: Sun intensity", ITimeOfDay::PARAM_SKYLIGHT_SUN_INTENSITY, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("Sky Light", "Sun intensity multiplier", "Sky light: Sun intensity multiplier", ITimeOfDay::PARAM_SKYLIGHT_SUN_INTENSITY_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 50.0f, 0.0f, 1000.0f);
AddVar("Sky Light", "Mie scattering", "Sky light: Mie scattering", ITimeOfDay::PARAM_SKYLIGHT_KM, ITimeOfDay::TYPE_FLOAT, 4.8f, 0.0f, 1000.0f);
AddVar("Sky Light", "Rayleigh scattering", "Sky light: Rayleigh scattering", ITimeOfDay::PARAM_SKYLIGHT_KR, ITimeOfDay::TYPE_FLOAT, 2.0f, 0.0f, 1000.0f);
AddVar("Sky Light", "Sun anisotropy factor", "Sky light: Sun anisotropy factor", ITimeOfDay::PARAM_SKYLIGHT_G, ITimeOfDay::TYPE_FLOAT, -0.997f, -0.9999f, 0.9999f);
AddVar("Sky Light", "Wavelength (R)", "Sky light: Wavelength (R)", ITimeOfDay::PARAM_SKYLIGHT_WAVELENGTH_R, ITimeOfDay::TYPE_FLOAT, 694.0f, 380.0f, 780.0f);
AddVar("Sky Light", "Wavelength (G)", "Sky light: Wavelength (G)", ITimeOfDay::PARAM_SKYLIGHT_WAVELENGTH_G, ITimeOfDay::TYPE_FLOAT, 597.0f, 380.0f, 780.0f);
AddVar("Sky Light", "Wavelength (B)", "Sky light: Wavelength (B)", ITimeOfDay::PARAM_SKYLIGHT_WAVELENGTH_B, ITimeOfDay::TYPE_FLOAT, 488.0f, 380.0f, 780.0f);
AddVar("Night Sky", "Horizon color", "Night sky: Horizon color", ITimeOfDay::PARAM_NIGHSKY_HORIZON_COLOR, ITimeOfDay::TYPE_COLOR, 222.0f * fRecip255, 148.0f * fRecip255, 47.0f * fRecip255);
AddVar("Night Sky", "Zenith color", "Night sky: Zenith color", ITimeOfDay::PARAM_NIGHSKY_ZENITH_COLOR, ITimeOfDay::TYPE_COLOR, 17.0f * fRecip255, 38.0f * fRecip255, 78.0f * fRecip255);
AddVar("Night Sky", "Zenith shift", "Night sky: Zenith shift", ITimeOfDay::PARAM_NIGHSKY_ZENITH_SHIFT, ITimeOfDay::TYPE_FLOAT, 0.25f, 0.0f, 16.0f);
AddVar("Night Sky", "Star intensity", "Night sky: Star intensity", ITimeOfDay::PARAM_NIGHSKY_START_INTENSITY, ITimeOfDay::TYPE_FLOAT, 0.01f, 0.0f, 16.0f);
AddVar("Night Sky", "Moon color", "Night sky: Moon color", ITimeOfDay::PARAM_NIGHSKY_MOON_COLOR, ITimeOfDay::TYPE_COLOR, 255.0f * fRecip255, 255.0f * fRecip255, 255.0f * fRecip255);
AddVar("Night Sky", "Moon inner corona color", "Night sky: Moon inner corona color", ITimeOfDay::PARAM_NIGHSKY_MOON_INNERCORONA_COLOR, ITimeOfDay::TYPE_COLOR, 230.0f * fRecip255, 255.0f * fRecip255, 255.0f * fRecip255);
AddVar("Night Sky", "Moon inner corona scale", "Night sky: Moon inner corona scale", ITimeOfDay::PARAM_NIGHSKY_MOON_INNERCORONA_SCALE, ITimeOfDay::TYPE_FLOAT, 0.499f, 0.0f, 2.0f);
AddVar("Night Sky", "Moon outer corona color", "Night sky: Moon outer corona color", ITimeOfDay::PARAM_NIGHSKY_MOON_OUTERCORONA_COLOR, ITimeOfDay::TYPE_COLOR, 128.0f * fRecip255, 200.0f * fRecip255, 255.0f * fRecip255);
AddVar("Night Sky", "Moon outer corona scale", "Night sky: Moon outer corona scale", ITimeOfDay::PARAM_NIGHSKY_MOON_OUTERCORONA_SCALE, ITimeOfDay::TYPE_FLOAT, 0.006f, 0.0f, 2.0f);
AddVar("Night Sky Multiplier", "Horizon color", "Night sky: Horizon color multiplier", ITimeOfDay::PARAM_NIGHSKY_HORIZON_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.0001f, 0.0f, 1.0f);
AddVar("Night Sky Multiplier", "Zenith color", "Night sky: Zenith color multiplier", ITimeOfDay::PARAM_NIGHSKY_ZENITH_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.00002f, 0.0f, 1.0f);
AddVar("Night Sky Multiplier", "Moon color", "Night sky: Moon color multiplier", ITimeOfDay::PARAM_NIGHSKY_MOON_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.01f, 0.0f, 1.0f);
AddVar("Night Sky Multiplier", "Moon inner corona color", "Night sky: Moon inner corona color multiplier", ITimeOfDay::PARAM_NIGHSKY_MOON_INNERCORONA_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.0001f, 0.0f, 1.0f);
AddVar("Night Sky Multiplier", "Moon outer corona color", "Night sky: Moon outer corona color multiplier", ITimeOfDay::PARAM_NIGHSKY_MOON_OUTERCORONA_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.00005f, 0.0f, 1.0f);
AddVar("Cloud Shading", "Sun contribution", "Cloud shading: Sun light multiplier", ITimeOfDay::PARAM_CLOUDSHADING_SUNLIGHT_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.96f, 0.0f, 16.0f);
AddVar("Cloud Shading", "Sun custom color", "Cloud shading: Sun custom color", ITimeOfDay::PARAM_CLOUDSHADING_SUNLIGHT_CUSTOM_COLOR, ITimeOfDay::TYPE_COLOR, 215.0f * fRecip255, 200.0f * fRecip255, 170.0f * fRecip255);
AddVar("Cloud Shading", "Sun custom color multiplier", "Cloud shading: Sun custom color multiplier", ITimeOfDay::PARAM_CLOUDSHADING_SUNLIGHT_CUSTOM_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 16.0f);
AddVar("Cloud Shading", "Sun custom color influence", "Cloud shading: Sun custom color influence", ITimeOfDay::PARAM_CLOUDSHADING_SUNLIGHT_CUSTOM_COLOR_INFLUENCE, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f);
AddVar("Sun Rays Effect", "", "Sun shafts visibility", ITimeOfDay::PARAM_SUN_SHAFTS_VISIBILITY, ITimeOfDay::TYPE_FLOAT, 0.25f, 0.0f, 1.0f);
AddVar("Sun Rays Effect", "", "Sun rays visibility", ITimeOfDay::PARAM_SUN_RAYS_VISIBILITY, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 10.0f);
AddVar("Sun Rays Effect", "", "Sun rays attenuation", ITimeOfDay::PARAM_SUN_RAYS_ATTENUATION, ITimeOfDay::TYPE_FLOAT, 5.0f, 0.0f, 10.0f);
AddVar("Sun Rays Effect", "", "Sun rays suncolor influence", ITimeOfDay::PARAM_SUN_RAYS_SUNCOLORINFLUENCE, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Sun Rays Effect", "", "Sun rays custom color", ITimeOfDay::PARAM_SUN_RAYS_CUSTOMCOLOR, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("HDR", "", "Film curve shoulder scale", ITimeOfDay::PARAM_HDR_FILMCURVE_SHOULDER_SCALE, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 10.0f);
AddVar("HDR", "", "Film curve midtones scale", ITimeOfDay::PARAM_HDR_FILMCURVE_LINEAR_SCALE, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 10.0f);
AddVar("HDR", "", "Film curve toe scale", ITimeOfDay::PARAM_HDR_FILMCURVE_TOE_SCALE, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 10.0f);
AddVar("HDR", "", "Film curve whitepoint", ITimeOfDay::PARAM_HDR_FILMCURVE_WHITEPOINT, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 10.0f);
AddVar("HDR", "", "Saturation", ITimeOfDay::PARAM_HDR_COLORGRADING_COLOR_SATURATION, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 2.0f);
AddVar("HDR", "", "Color balance", ITimeOfDay::PARAM_HDR_COLORGRADING_COLOR_BALANCE, ITimeOfDay::TYPE_COLOR, 1.0f, 1.0f, 1.0f);
AddVar("HDR", "(Dep) Scene key", "Scene key", ITimeOfDay::PARAM_HDR_EYEADAPTATION_SCENEKEY, ITimeOfDay::TYPE_FLOAT, 0.18f, 0.0f, 1.0f);
AddVar("HDR", "(Dep) Min exposure", "Min exposure", ITimeOfDay::PARAM_HDR_EYEADAPTATION_MIN_EXPOSURE, ITimeOfDay::TYPE_FLOAT, 0.36f, 0.0f, 10.0f);
AddVar("HDR", "(Dep) Max exposure", "Max exposure", ITimeOfDay::PARAM_HDR_EYEADAPTATION_MAX_EXPOSURE, ITimeOfDay::TYPE_FLOAT, 2.8f, 0.0f, 10.0f);
AddVar("HDR", "", "EV Min", ITimeOfDay::PARAM_HDR_EYEADAPTATION_EV_MIN, ITimeOfDay::TYPE_FLOAT, 4.5f, -10.0f, 20.0f);
AddVar("HDR", "", "EV Max", ITimeOfDay::PARAM_HDR_EYEADAPTATION_EV_MAX, ITimeOfDay::TYPE_FLOAT, 17.0f, -10.0f, 20.0f);
AddVar("HDR", "", "EV Auto compensation", ITimeOfDay::PARAM_HDR_EYEADAPTATION_EV_AUTO_COMPENSATION, ITimeOfDay::TYPE_FLOAT, 1.5f, -5.0f, 5.0f);
AddVar("HDR", "", "Bloom amount", ITimeOfDay::PARAM_HDR_BLOOM_AMOUNT, ITimeOfDay::TYPE_FLOAT, 0.1f, 0.0f, 10.0f);
AddVar("Filters", "Grain", "Filters: grain", ITimeOfDay::PARAM_COLORGRADING_FILTERS_GRAIN, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 8.0f); // deprecated
AddVar("Filters", "Photofilter color", "Filters: photofilter color", ITimeOfDay::PARAM_COLORGRADING_FILTERS_PHOTOFILTER_COLOR, ITimeOfDay::TYPE_COLOR, 0.952f, 0.517f, 0.09f); // deprecated
AddVar("Filters", "Photofilter density", "Filters: photofilter density", ITimeOfDay::PARAM_COLORGRADING_FILTERS_PHOTOFILTER_DENSITY, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f); // deprecated
AddVar("Depth Of Field", "Focus range", "Dof: focus range", ITimeOfDay::PARAM_COLORGRADING_DOF_FOCUSRANGE, ITimeOfDay::TYPE_FLOAT, 1000.0f, 0.0f, 10000.0f);
AddVar("Depth Of Field", "Blur amount", "Dof: blur amount", ITimeOfDay::PARAM_COLORGRADING_DOF_BLURAMOUNT, ITimeOfDay::TYPE_FLOAT, 0.0f, 0.0f, 1.0f);
AddVar("Advanced", "", "Ocean fog color", ITimeOfDay::PARAM_OCEANFOG_COLOR, ITimeOfDay::TYPE_COLOR, 29.0f * fRecip255, 102.0f * fRecip255, 141.0f * fRecip255);
AddVar("Advanced", "", "Ocean fog color multiplier", ITimeOfDay::PARAM_OCEANFOG_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
AddVar("Advanced", "", "Ocean fog density", ITimeOfDay::PARAM_OCEANFOG_DENSITY, ITimeOfDay::TYPE_FLOAT, 0.2f, 0.0f, 1.0f);
AddVar("Advanced", "", "Static skybox multiplier", ITimeOfDay::PARAM_SKYBOX_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 1.0f);
const float arrDepthConstBias[] = {1.0f, 1.0f, 1.9f, 3.0f, 2.0f, 2.0f, 2.0f, 2.0f};
const float arrDepthSlopeBias[] = {4.0f, 2.0f, 0.24f, 0.24f, 0.5f, 0.5f, 0.5f, 0.5f};
AddVar("Shadows", "", "Cascade 0: Bias", ITimeOfDay::PARAM_SHADOWSC0_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[0], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 0: Slope Bias", ITimeOfDay::PARAM_SHADOWSC0_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[0], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 1: Bias", ITimeOfDay::PARAM_SHADOWSC1_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[1], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 1: Slope Bias", ITimeOfDay::PARAM_SHADOWSC1_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[1], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 2: Bias", ITimeOfDay::PARAM_SHADOWSC2_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[2], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 2: Slope Bias", ITimeOfDay::PARAM_SHADOWSC2_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[2], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 3: Bias", ITimeOfDay::PARAM_SHADOWSC3_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[3], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 3: Slope Bias", ITimeOfDay::PARAM_SHADOWSC3_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[3], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 4: Bias", ITimeOfDay::PARAM_SHADOWSC4_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[4], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 4: Slope Bias", ITimeOfDay::PARAM_SHADOWSC4_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[4], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 5: Bias", ITimeOfDay::PARAM_SHADOWSC5_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[5], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 5: Slope Bias", ITimeOfDay::PARAM_SHADOWSC5_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[5], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 6: Bias", ITimeOfDay::PARAM_SHADOWSC6_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[6], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 6: Slope Bias", ITimeOfDay::PARAM_SHADOWSC6_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[6], 0.0f, 500.0f);
AddVar("Shadows", "", "Cascade 7: Bias", ITimeOfDay::PARAM_SHADOWSC7_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthConstBias[7], 0.0f, 10.0f);
AddVar("Shadows", "", "Cascade 7: Slope Bias", ITimeOfDay::PARAM_SHADOWSC7_SLOPE_BIAS, ITimeOfDay::TYPE_FLOAT, arrDepthSlopeBias[7], 0.0f, 500.0f);
AddVar("Shadows", "", "Shadow jittering", ITimeOfDay::PARAM_SHADOW_JITTERING, ITimeOfDay::TYPE_FLOAT, 2.5f, 0.f, 10.f);
AddVar("Obsolete", "", "HDR dynamic power factor", ITimeOfDay::PARAM_HDR_DYNAMIC_POWER_FACTOR, ITimeOfDay::TYPE_FLOAT, 0.0f, -4.0f, 4.0f);
AddVar("Obsolete", "", "Sky brightening (terrain occlusion)", ITimeOfDay::PARAM_TERRAIN_OCCL_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 0.3f, 0.f, 1.f);
AddVar("Obsolete", "", "Sun color multiplier", ITimeOfDay::PARAM_SUN_COLOR_MULTIPLIER, ITimeOfDay::TYPE_FLOAT, 1.0f, 0.0f, 16.0f);
}
void CEnvironmentPreset::Serialize(Serialization::IArchive& ar)
{
for (size_t i = 0; i < ITimeOfDay::PARAM_TOTAL; ++i)
{
ar(m_vars[i], "var");
}
}
void CEnvironmentPreset::Update(float t)
{
for (size_t i = 0; i < ITimeOfDay::PARAM_TOTAL; ++i)
{
m_vars[i].Update(t);
}
}
CTimeOfDayVariable* CEnvironmentPreset::GetVar(const char* varName)
{
for (size_t i = 0; i < ITimeOfDay::PARAM_TOTAL; ++i)
{
if (strcmp(m_vars[i].GetName(), varName) == 0)
{
return &m_vars[i];
}
}
return NULL;
}
bool CEnvironmentPreset::InterpolateVarInRange(ITimeOfDay::ETimeOfDayParamID id, float fMin, float fMax, unsigned int nCount, Vec3* resultArray) const
{
const float fdx = 1.0f / float(nCount);
float normX = 0.0f;
for (unsigned int i = 0; i < nCount; ++i)
{
const float time = Lerp(fMin, fMax, normX);
resultArray[i] = m_vars[id].GetInterpolatedAt(time);
normX += fdx;
}
return true;
}
void CEnvironmentPreset::AddVar(const char* group, const char* displayName, const char* name, ITimeOfDay::ETimeOfDayParamID nParamId, ITimeOfDay::EVariableType type, float defVal0, float defVal1, float defVal2)
{
CTimeOfDayVariable& var = m_vars[nParamId];
var.Init(group, displayName, name, nParamId, type, defVal0, defVal1, defVal2);
}
@@ -1,143 +0,0 @@
/*
* 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.
#ifndef _environment_preset_h_
#define _environment_preset_h_
#pragma once
#include <Bezier.h>
class CBezierSpline
{
public:
CBezierSpline();
~CBezierSpline();
void Init(float fDefaultValue);
float Evaluate(float t) const;
void SetKeys(const SBezierKey* keysArray, unsigned int keysArraySize) { m_keys.resize(keysArraySize); memcpy(&m_keys[0], keysArray, keysArraySize * sizeof(SBezierKey)); }
void GetKeys(SBezierKey* keys) const { memcpy(keys, &m_keys[0], m_keys.size() * sizeof(SBezierKey)); }
void InsertKey(SAnimTime time, float value);
void UpdateKeyForTime(float fTime, float value);
void Resize(size_t nSize) { m_keys.resize(nSize); }
size_t GetKeyCount() const { return m_keys.size(); }
const SBezierKey& GetKey(size_t nIndex) const { return m_keys[nIndex]; }
SBezierKey& GetKey(size_t nIndex) { return m_keys[nIndex]; }
void Serialize(Serialization::IArchive& ar);
private:
typedef std::vector<SBezierKey> TKeyContainer;
TKeyContainer m_keys;
struct SCompKeyTime
{
bool operator()(const TKeyContainer::value_type& l, const TKeyContainer::value_type& r) const { return l.m_time < r.m_time; }
bool operator()(SAnimTime l, const TKeyContainer::value_type& r) const { return l < r.m_time; }
bool operator()(const TKeyContainer::value_type& l, SAnimTime r) const { return l.m_time < r; }
};
};
//////////////////////////////////////////////////////////////////////////
class CTimeOfDayVariable
{
public:
CTimeOfDayVariable();
~CTimeOfDayVariable();
void Init(const char* group, const char* displayName, const char* name, ITimeOfDay::ETimeOfDayParamID nParamId, ITimeOfDay::EVariableType type, float defVal0, float defVal1, float defVal2);
void Update(float time);
Vec3 GetInterpolatedAt(float t) const;
ITimeOfDay::EVariableType GetType() const {return m_type; }
const char* GetName() const { return m_name; }
const char* GetDisplayName() const { return m_displayName; }
const char* GetGroupName() const { return m_group; }
const Vec3 GetValue() const { return m_value; }
float GetMinValue() const { return m_minValue; }
float GetMaxValue() const { return m_maxValue; }
const CBezierSpline* GetSpline(int nIndex) const
{
if (nIndex >= 0 && nIndex < Vec3::component_count)
{
return &m_spline[nIndex];
}
else
{
return NULL;
}
}
CBezierSpline* GetSpline(int nIndex)
{
if (nIndex >= 0 && nIndex < Vec3::component_count)
{
return &m_spline[nIndex];
}
else
{
return NULL;
}
}
size_t GetSplineKeyCount(int nSpline) const;
bool GetSplineKeys(int nSpline, SBezierKey* keysArray, unsigned int keysArraySize) const;
bool SetSplineKeys(int nSpline, const SBezierKey* keysArray, unsigned int keysArraySize);
bool UpdateSplineKeyForTime(int nSpline, float fTime, float newKey);
void Serialize(Serialization::IArchive& ar);
private:
ITimeOfDay::ETimeOfDayParamID m_id;
ITimeOfDay::EVariableType m_type;
const char* m_name; // Variable name.
const char* m_displayName; // Variable user readable name.
const char* m_group; // Group name.
float m_minValue;
float m_maxValue;
Vec3 m_value;
CBezierSpline m_spline[Vec3::component_count]; //spline for each component in m_value
};
//////////////////////////////////////////////////////////////////////////
class CEnvironmentPreset
{
public:
CEnvironmentPreset();
~CEnvironmentPreset();
void ResetVariables();
void Update(float t);
const CTimeOfDayVariable* GetVar(ITimeOfDay::ETimeOfDayParamID id) const { return &m_vars[id]; }
CTimeOfDayVariable* GetVar(ITimeOfDay::ETimeOfDayParamID id) { return &m_vars[id]; }
CTimeOfDayVariable* GetVar(const char* varName);
bool InterpolateVarInRange(ITimeOfDay::ETimeOfDayParamID id, float fMin, float fMax, unsigned int nCount, Vec3* resultArray) const;
void Serialize(Serialization::IArchive& ar);
private:
void AddVar(const char* group, const char* displayName, const char* name, ITimeOfDay::ETimeOfDayParamID nParamId, ITimeOfDay::EVariableType type, float defVal0, float defVal1, float defVal2);
CTimeOfDayVariable m_vars[ITimeOfDay::PARAM_TOTAL];
};
#endif //_environment_preset_h_
@@ -1,559 +0,0 @@
/*
* 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 "FogVolumeRenderNode.h"
#include "VisAreas.h"
#include "CREFogVolume.h"
#include "Cry_Geo.h"
#include "ObjMan.h"
#include "ClipVolumeManager.h"
#include "Environment/OceanEnvironmentBus.h"
#include <limits>
AABB CFogVolumeRenderNode::s_tracableFogVolumeArea(Vec3(0, 0, 0), Vec3(0, 0, 0));
StaticInstance<CFogVolumeRenderNode::CachedFogVolumes> CFogVolumeRenderNode::s_cachedFogVolumes;
StaticInstance<CFogVolumeRenderNode::GlobalFogVolumeMap> CFogVolumeRenderNode::s_globalFogVolumeMap;
bool CFogVolumeRenderNode::s_forceTraceableAreaUpdate(false);
void CFogVolumeRenderNode::StaticReset()
{
stl::free_container(s_cachedFogVolumes);
}
void CFogVolumeRenderNode::ForceTraceableAreaUpdate()
{
s_forceTraceableAreaUpdate = true;
}
void CFogVolumeRenderNode::SetTraceableArea(const AABB& traceableArea, [[maybe_unused]] const SRenderingPassInfo& passInfo)
{
// do we bother?
if (!GetCVars()->e_Fog || !GetCVars()->e_FogVolumes)
{
return;
}
if (GetCVars()->e_VolumetricFog != 0)
{
return;
}
// is update of traceable areas necessary
if (!s_forceTraceableAreaUpdate)
{
if ((s_tracableFogVolumeArea.GetCenter() - traceableArea.GetCenter()).GetLengthSquared() < 1e-4f && (s_tracableFogVolumeArea.GetSize() - traceableArea.GetSize()).GetLengthSquared() < 1e-4f)
{
return;
}
}
// set new area and reset list of traceable fog volumes
s_tracableFogVolumeArea = traceableArea;
s_cachedFogVolumes.resize(0);
// collect all candidates
Vec3 traceableAreaCenter(s_tracableFogVolumeArea.GetCenter());
IVisArea* pVisAreaOfCenter(GetVisAreaManager() ? GetVisAreaManager()->GetVisAreaFromPos(traceableAreaCenter) : NULL);
GlobalFogVolumeMap::const_iterator itEnd(s_globalFogVolumeMap.end());
for (GlobalFogVolumeMap::const_iterator it(s_globalFogVolumeMap.begin()); it != itEnd; ++it)
{
const CFogVolumeRenderNode* pFogVolume(*it);
if (pVisAreaOfCenter || (!pVisAreaOfCenter && !pFogVolume->GetEntityVisArea())) // if outside only add fog volumes which are outside as well
{
if (Overlap::AABB_AABB(s_tracableFogVolumeArea, pFogVolume->m_WSBBox)) // bb of fog volume overlaps with traceable area
{
s_cachedFogVolumes.push_back(SCachedFogVolume(pFogVolume, Vec3(pFogVolume->m_pos - traceableAreaCenter).GetLengthSquared()));
}
}
}
// sort by distance
std::sort(s_cachedFogVolumes.begin(), s_cachedFogVolumes.end());
// reset force-update flags
s_forceTraceableAreaUpdate = false;
}
void CFogVolumeRenderNode::RegisterFogVolume(const CFogVolumeRenderNode* pFogVolume)
{
GlobalFogVolumeMap::const_iterator it(s_globalFogVolumeMap.find(pFogVolume));
assert(it == s_globalFogVolumeMap.end() &&
"CFogVolumeRenderNode::RegisterFogVolume() -- Fog volume already registered!");
if (it == s_globalFogVolumeMap.end())
{
s_globalFogVolumeMap.insert(pFogVolume);
ForceTraceableAreaUpdate();
}
}
void CFogVolumeRenderNode::UnregisterFogVolume(const CFogVolumeRenderNode* pFogVolume)
{
GlobalFogVolumeMap::iterator it(s_globalFogVolumeMap.find(pFogVolume));
assert(it != s_globalFogVolumeMap.end() &&
"CFogVolumeRenderNode::UnRegisterFogVolume() -- Fog volume previously not registered!");
if (it != s_globalFogVolumeMap.end())
{
s_globalFogVolumeMap.erase(it);
ForceTraceableAreaUpdate();
}
}
CFogVolumeRenderNode::CFogVolumeRenderNode()
: m_matNodeWS()
, m_matWS()
, m_matWSInv()
, m_volumeType(0)
, m_pos(0, 0, 0)
, m_x(1, 0, 0)
, m_y(0, 1, 0)
, m_z(0, 0, 1)
, m_size(1, 1, 1)
, m_scale(1, 1, 1)
, m_globalDensity(1)
, m_densityOffset(0)
, m_nearCutoff(0)
, m_fHDRDynamic(0)
, m_softEdges(1)
, m_color(1, 1, 1, 1)
, m_useGlobalFogColor(false)
, m_affectsThisAreaOnly(false)
, m_rampParams(0, 1, 0)
, m_updateFrameID(0)
, m_windInfluence(1)
, m_noiseElapsedTime(-5000.0f)
, m_densityNoiseScale(0)
, m_densityNoiseOffset(0)
, m_densityNoiseTimeFrequency(0)
, m_densityNoiseFrequency(1, 1, 1)
, m_heightFallOffDir(0, 0, 1)
, m_heightFallOffDirScaled(0, 0, 1)
, m_heightFallOffShift(0, 0, 0)
, m_heightFallOffBasePoint(0, 0, 0)
, m_localBounds(Vec3(-0.5f, -0.5f, -0.5f), Vec3(0.5f, 0.5f, 0.5f))
, m_globalDensityFader()
, m_pMatFogVolEllipsoid(0)
, m_pMatFogVolBox(0)
, m_WSBBox()
, m_cachedSoftEdgesLerp(1, 0)
, m_cachedFogColor(1, 1, 1, 1)
{
m_matNodeWS.SetIdentity();
m_matWS.SetIdentity();
m_matWSInv.SetIdentity();
m_windOffset.x = cry_random(0.0f, 1000.0f);
m_windOffset.y = cry_random(0.0f, 1000.0f);
m_windOffset.z = cry_random(0.0f, 1000.0f);
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
{
m_pFogVolumeRenderElement[i] = (CREFogVolume*) GetRenderer()->EF_CreateRE(eDATA_FogVolume);
}
m_pMatFogVolEllipsoid = Get3DEngine()->m_pMatFogVolEllipsoid;
m_pMatFogVolBox = Get3DEngine()->m_pMatFogVolBox;
//Get3DEngine()->RegisterEntity( this );
RegisterFogVolume(this);
}
CFogVolumeRenderNode::~CFogVolumeRenderNode()
{
for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
{
if (m_pFogVolumeRenderElement[i])
{
m_pFogVolumeRenderElement[i]->Release(false);
m_pFogVolumeRenderElement[i] = 0;
}
}
UnregisterFogVolume(this);
Get3DEngine()->FreeRenderNodeState(this);
}
void CFogVolumeRenderNode::UpdateFogVolumeMatrices()
{
// update matrices used for ray tracing, distance sorting, etc.
Matrix34 mtx = Matrix34::CreateFromVectors(m_size.x * m_x * 0.5f, m_size.y * m_y * 0.5f, m_size.z * m_z * 0.5f, m_pos);
m_matWS = mtx;
m_matWSInv = mtx.GetInverted();
}
void CFogVolumeRenderNode::UpdateWorldSpaceBBox()
{
// update bounding box in world space used for culling
m_WSBBox.SetTransformedAABB(m_matNodeWS, m_localBounds);
}
void CFogVolumeRenderNode::UpdateHeightFallOffBasePoint()
{
m_heightFallOffBasePoint = m_pos + m_heightFallOffShift;
}
void CFogVolumeRenderNode::SetFogVolumeProperties(const SFogVolumeProperties& properties)
{
m_globalDensityFader.SetInvalid();
assert(properties.m_size.x > 0 && properties.m_size.y > 0 && properties.m_size.z > 0);
if ((m_size - properties.m_size).GetLengthSquared() > 1e-4)
{
m_size = properties.m_size;
m_localBounds.min = Vec3(-0.5f, -0.5f, -0.5f).CompMul(m_size);
m_localBounds.max = -m_localBounds.min;
UpdateWorldSpaceBBox();
}
m_volumeType = properties.m_volumeType;
assert(m_volumeType >= 0 && m_volumeType <= 1);
m_color = properties.m_color;
assert(properties.m_globalDensity >= 0);
m_useGlobalFogColor = properties.m_useGlobalFogColor;
m_globalDensity = properties.m_globalDensity;
m_densityOffset = properties.m_densityOffset;
m_nearCutoff = properties.m_nearCutoff;
m_fHDRDynamic = properties.m_fHDRDynamic;
assert(properties.m_softEdges >= 0 && properties.m_softEdges <= 1);
m_softEdges = properties.m_softEdges;
// IgnoreVisArea and AffectsThisAreaOnly don't work concurrently.
SetRndFlags(ERF_RENDER_ALWAYS, properties.m_ignoresVisAreas && !properties.m_affectsThisAreaOnly);
m_affectsThisAreaOnly = properties.m_affectsThisAreaOnly;
float latiArc(DEG2RAD(90.0f - properties.m_heightFallOffDirLati));
float longArc(DEG2RAD(properties.m_heightFallOffDirLong));
float sinLati(sinf(latiArc));
float cosLati(cosf(latiArc));
float sinLong(sinf(longArc));
float cosLong(cosf(longArc));
m_heightFallOffDir = Vec3(sinLati * cosLong, sinLati * sinLong, cosLati);
m_heightFallOffShift = m_heightFallOffDir * properties.m_heightFallOffShift;
m_heightFallOffDirScaled = m_heightFallOffDir * properties.m_heightFallOffScale;
UpdateHeightFallOffBasePoint();
m_rampParams = Vec3(properties.m_rampStart, properties.m_rampEnd, properties.m_rampInfluence);
m_windInfluence = properties.m_windInfluence;
m_densityNoiseScale = properties.m_densityNoiseScale;
m_densityNoiseOffset = properties.m_densityNoiseOffset + 1.0f;
m_densityNoiseTimeFrequency = properties.m_densityNoiseTimeFrequency;
m_densityNoiseFrequency = properties.m_densityNoiseFrequency * 0.01f;// scale the value to useful range
}
const Matrix34& CFogVolumeRenderNode::GetMatrix() const
{
return m_matNodeWS;
}
void CFogVolumeRenderNode::GetLocalBounds(AABB& bbox)
{
bbox = m_localBounds;
};
void CFogVolumeRenderNode::SetMatrix(const Matrix34& mat)
{
m_matNodeWS = mat;
// get translation and rotational part of fog volume from entity matrix
// scale is specified explicitly as fog volumes can be non-uniformly scaled
m_pos = m_matNodeWS.GetTranslation();
m_x = m_matNodeWS.GetColumn(0);
m_y = m_matNodeWS.GetColumn(1);
m_z = m_matNodeWS.GetColumn(2);
UpdateFogVolumeMatrices();
UpdateWorldSpaceBBox();
UpdateHeightFallOffBasePoint();
Get3DEngine()->RegisterEntity(this);
ForceTraceableAreaUpdate();
}
void CFogVolumeRenderNode::SetScale(const Vec3& scale)
{
m_scale = scale;
}
void CFogVolumeRenderNode::FadeGlobalDensity(float fadeTime, float newGlobalDensity)
{
if (newGlobalDensity >= 0)
{
if (fadeTime == 0)
{
m_globalDensity = newGlobalDensity;
m_globalDensityFader.SetInvalid();
}
else if (fadeTime > 0)
{
float curFrameTime(gEnv->pTimer->GetCurrTime());
m_globalDensityFader.Set(curFrameTime, curFrameTime + fadeTime, m_globalDensity, newGlobalDensity);
}
}
}
const char* CFogVolumeRenderNode::GetEntityClassName() const
{
return "FogVolume";
}
const char* CFogVolumeRenderNode::GetName() const
{
return "FogVolume";
}
ColorF CFogVolumeRenderNode::GetFogColor() const
{
//FUNCTION_PROFILER_3DENGINE
Vec3 fogColor(m_color.r, m_color.g, m_color.b);
bool bVolFogEnabled = (GetCVars()->e_VolumetricFog != 0);
if (bVolFogEnabled)
{
if (m_useGlobalFogColor)
{
Get3DEngine()->GetGlobalParameter(E3DPARAM_VOLFOG2_COLOR, fogColor);
}
}
else
{
if (m_useGlobalFogColor)
{
fogColor = Get3DEngine()->GetFogColor();
}
bool bHDRModeEnabled = false;
GetRenderer()->EF_Query(EFQ_HDRModeEnabled, bHDRModeEnabled);
if (bHDRModeEnabled)
{
const float HDRDynamicMultiplier = 2.0f;
fogColor *= powf(HDRDynamicMultiplier, m_fHDRDynamic);
}
}
return fogColor;
}
Vec2 CFogVolumeRenderNode::GetSoftEdgeLerp(const Vec3& viewerPosOS) const
{
// Volumetric fog doesn't need special treatment when camera is in the ellipsoid.
if (GetCVars()->e_VolumetricFog != 0)
{
return Vec2(m_softEdges, 1.0f - m_softEdges);
}
//FUNCTION_PROFILER_3DENGINE
// ramp down soft edge factor as soon as camera enters the ellipsoid
float softEdge(m_softEdges * clamp_tpl((viewerPosOS.GetLength() - 0.95f) * 20.0f, 0.0f, 1.0f));
return Vec2(softEdge, 1.0f - softEdge);
}
bool CFogVolumeRenderNode::IsViewerInsideVolume(const SRenderingPassInfo& passInfo) const
{
const CCamera& cam(passInfo.GetCamera());
// check if fog volumes 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_matWSInv.TransformPoint(pntOnNearPlane));
Vec3 nearPlaneOS_n(m_matWSInv.TransformVector(pNearPlane->n) /*.GetNormalized()*/);
f32 nearPlaneOS_d(-nearPlaneOS_n.Dot(pntOnNearPlaneOS));
// get extreme lengths
float t(fabsf(nearPlaneOS_n.x) + fabsf(nearPlaneOS_n.y) + fabsf(nearPlaneOS_n.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 CFogVolumeRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
// anything to render?
if (passInfo.IsRecursivePass())
{
return;
}
if (!m_pMatFogVolBox || !m_pMatFogVolEllipsoid || GetCVars()->e_Fog == 0 || GetCVars()->e_FogVolumes == 0)
{
return;
}
const int32 fillThreadID = passInfo.ThreadID();
if (!m_pFogVolumeRenderElement[fillThreadID])
{
return;
}
if (m_globalDensityFader.IsValid())
{
float curFrameTime(gEnv->pTimer->GetCurrTime());
m_globalDensity = m_globalDensityFader.GetValue(curFrameTime);
if (!m_globalDensityFader.IsTimeInRange(curFrameTime))
{
m_globalDensityFader.SetInvalid();
}
}
// transform camera into fog volumes object space (where fog volume is a unit-sphere at (0,0,0))
const CCamera& cam(passInfo.GetCamera());
Vec3 viewerPosWS(cam.GetPosition());
Vec3 viewerPosOS(m_matWSInv * viewerPosWS);
m_cachedFogColor = GetFogColor();
m_cachedSoftEdgesLerp = GetSoftEdgeLerp(viewerPosOS);
bool bVolFog = (GetCVars()->e_VolumetricFog != 0);
// reset elapsed time for noise when FogVolume stayed out of viewport for 30 frames.
// this prevents the time from being too large number.
if ((m_updateFrameID + 30) < passInfo.GetMainFrameID() && m_noiseElapsedTime > 5000.0f)
{
m_noiseElapsedTime = -5000.0f;
}
if (bVolFog && m_densityNoiseScale > 0.0f && m_updateFrameID != passInfo.GetMainFrameID())
{
Vec3 wind = Get3DEngine()->GetGlobalWind(false);
const float elapsedTime = gEnv->pTimer->GetFrameTime();
m_windOffset = ((-m_windInfluence * elapsedTime) * wind) + m_windOffset;
const float windOffsetSpan = 1000.0f;// it should match the constant value in FogVolume.cfx
m_windOffset.x = m_windOffset.x - floor(m_windOffset.x / windOffsetSpan) * windOffsetSpan;
m_windOffset.y = m_windOffset.y - floor(m_windOffset.y / windOffsetSpan) * windOffsetSpan;
m_windOffset.z = m_windOffset.z - floor(m_windOffset.z / windOffsetSpan) * windOffsetSpan;
m_noiseElapsedTime += m_densityNoiseTimeFrequency * elapsedTime;
m_updateFrameID = passInfo.GetMainFrameID();
}
float densityOffset = bVolFog ? (m_densityOffset * 0.001f) : m_densityOffset;// scale the value to useful range
// set render element attributes
m_pFogVolumeRenderElement[fillThreadID]->m_center = m_pos;
m_pFogVolumeRenderElement[fillThreadID]->m_viewerInsideVolume = IsViewerInsideVolume(passInfo) ? 1 : 0;
m_pFogVolumeRenderElement[fillThreadID]->m_affectsThisAreaOnly = m_affectsThisAreaOnly ? 1 : 0;
m_pFogVolumeRenderElement[fillThreadID]->m_stencilRef = rParam.nClipVolumeStencilRef;
m_pFogVolumeRenderElement[fillThreadID]->m_volumeType = (m_volumeType != 0) ? 1 : 0;
m_pFogVolumeRenderElement[fillThreadID]->m_localAABB = m_localBounds;
m_pFogVolumeRenderElement[fillThreadID]->m_matWSInv = m_matWSInv;
m_pFogVolumeRenderElement[fillThreadID]->m_fogColor = m_cachedFogColor;
m_pFogVolumeRenderElement[fillThreadID]->m_globalDensity = m_globalDensity;
m_pFogVolumeRenderElement[fillThreadID]->m_densityOffset = densityOffset;
m_pFogVolumeRenderElement[fillThreadID]->m_nearCutoff = m_nearCutoff;
m_pFogVolumeRenderElement[fillThreadID]->m_softEdgesLerp = m_cachedSoftEdgesLerp;
m_pFogVolumeRenderElement[fillThreadID]->m_heightFallOffDirScaled = m_heightFallOffDirScaled;
m_pFogVolumeRenderElement[fillThreadID]->m_heightFallOffBasePoint = m_heightFallOffBasePoint;
m_pFogVolumeRenderElement[fillThreadID]->m_eyePosInWS = viewerPosWS;
m_pFogVolumeRenderElement[fillThreadID]->m_eyePosInOS = viewerPosOS;
m_pFogVolumeRenderElement[fillThreadID]->m_rampParams = m_rampParams;
m_pFogVolumeRenderElement[fillThreadID]->m_windOffset = m_windOffset;
m_pFogVolumeRenderElement[fillThreadID]->m_noiseScale = m_densityNoiseScale;
m_pFogVolumeRenderElement[fillThreadID]->m_noiseFreq = m_densityNoiseFrequency;
m_pFogVolumeRenderElement[fillThreadID]->m_noiseOffset = m_densityNoiseOffset;
m_pFogVolumeRenderElement[fillThreadID]->m_noiseElapsedTime = m_noiseElapsedTime;
m_pFogVolumeRenderElement[fillThreadID]->m_scale = m_scale;
if (bVolFog && GetCVars()->e_FogVolumesTiledInjection)
{
// add FogVolume to volumetric fog renderer
GetRenderer()->PushFogVolume(m_pFogVolumeRenderElement[fillThreadID], passInfo);
}
else
{
IRenderer* pRenderer = GetRenderer();
CRenderObject* pRenderObject = pRenderer->EF_GetObject_Temp(fillThreadID);
if (!pRenderObject)
{
return;
}
// set basic render object properties
pRenderObject->m_II.m_Matrix = m_matNodeWS;
pRenderObject->m_fSort = 0;
int nAfterWater = GetObjManager()->IsAfterWater(m_pos, passInfo) ? 1 : 0;
// TODO: add constant factor to sortID to make fog volumes render before all other alpha transparent geometry (or have separate render list?)
pRenderObject->m_fSort = WATER_LEVEL_SORTID_OFFSET * 0.5f;
// get shader item
SShaderItem& shaderItem(0 != rParam.pMaterial ? rParam.pMaterial->GetShaderItem(0) :
1 == m_volumeType ? m_pMatFogVolBox->GetShaderItem(0) : m_pMatFogVolEllipsoid->GetShaderItem(0));
// get target render list
int nList = bVolFog ? EFSLIST_FOG_VOLUME : EFSLIST_TRANSP;
// add to renderer
GetRenderer()->EF_AddEf(m_pFogVolumeRenderElement[fillThreadID], shaderItem, pRenderObject, passInfo, nList, nAfterWater, SRendItemSorter(rParam.rendItemSorter));
}
}
void CFogVolumeRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
{
}
void CFogVolumeRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
{
SIZER_COMPONENT_NAME(pSizer, "FogVolumeNode");
pSizer->AddObject(this, sizeof(*this));
}
void CFogVolumeRenderNode::OffsetPosition(const Vec3& delta)
{
if (m_pRNTmpData)
{
m_pRNTmpData->OffsetPosition(delta);
}
m_pos += delta;
m_matNodeWS.SetTranslation(m_matNodeWS.GetTranslation() + delta);
m_matWS.SetTranslation(m_matWS.GetTranslation() + delta);
m_matWSInv = m_matWS.GetInverted();
m_heightFallOffBasePoint += delta;
m_WSBBox.Move(delta);
}
@@ -1,217 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_FOGVOLUMERENDERNODE_H
#define CRYINCLUDE_CRY3DENGINE_FOGVOLUMERENDERNODE_H
#pragma once
class CREFogVolume;
class CFogVolumeRenderNode
: public IFogVolumeRenderNode
, public Cry3DEngineBase
{
public:
static void StaticReset();
static void SetTraceableArea(const AABB& traceableArea, const SRenderingPassInfo& passInfo);
static void TraceFogVolumes(const Vec3& vPos, const AABB& objBBox, SFogVolumeData& fogVolData, const SRenderingPassInfo& passInfo, bool fogVolumeShadingQuality);
static bool OverlapProjectedAABB(const AABB& objBBox0, const AABB& objBBox1, const CCamera& camera);
public:
CFogVolumeRenderNode();
// implements IFogVolumeRenderNode
virtual void SetFogVolumeProperties(const SFogVolumeProperties& properties);
virtual const Matrix34& GetMatrix() const;
virtual void FadeGlobalDensity(float fadeTime, float newGlobalDensity);
// implements IRenderNode
virtual void GetLocalBounds(AABB& bbox);
virtual void SetMatrix(const Matrix34& mat);
virtual void SetScale(const Vec3& scale);
virtual EERType GetRenderNodeType();
virtual const char* GetEntityClassName() const;
virtual const char* GetName() const;
virtual Vec3 GetPos(bool bWorldOnly = true) const;
virtual void Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo);
void SetMaterial(_smart_ptr<IMaterial> pMat) override;
virtual _smart_ptr<IMaterial> GetMaterial(Vec3* pHitPos);
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return NULL; }
virtual float GetMaxViewDist();
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
virtual const AABB GetBBox() const { return m_WSBBox; }
virtual void SetBBox(const AABB& WSBBox) { m_WSBBox = WSBBox; }
virtual void FillBBox(AABB& aabb);
virtual void OffsetPosition(const Vec3& delta);
float GetGlobalDensity() const { return m_globalDensity; }
float GetDensityoffset() const { return m_densityOffset; }
Vec3 GetHeightFallOffBasePoint() const { return m_heightFallOffBasePoint; }
Vec3 GetHeightFallOffDirScaled() const { return m_heightFallOffDirScaled; }
ILINE bool IsAffectsThisAreaOnly() const { return m_affectsThisAreaOnly; }
int GetVolumeType() const { return m_volumeType; }
private:
static void RegisterFogVolume(const CFogVolumeRenderNode* pFogVolume);
static void UnregisterFogVolume(const CFogVolumeRenderNode* pFogVolume);
private:
~CFogVolumeRenderNode();
void UpdateFogVolumeMatrices();
void UpdateWorldSpaceBBox();
void UpdateHeightFallOffBasePoint();
ColorF GetFogColor() const;
Vec2 GetSoftEdgeLerp(const Vec3& viewerPosOS) const;
bool IsViewerInsideVolume(const SRenderingPassInfo& passInfo) const;
void GetVolumetricFogColorEllipsoid(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const;
void GetVolumetricFogColorBox(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const;
static void ForceTraceableAreaUpdate();
private:
struct SCachedFogVolume
{
SCachedFogVolume()
: m_pFogVol(0)
, m_distToCenterSq(0) {}
SCachedFogVolume(const CFogVolumeRenderNode* pFogVol, float distToCenterSq)
: m_pFogVol(pFogVol)
, m_distToCenterSq(distToCenterSq)
{
}
bool operator < (const SCachedFogVolume& rhs) const
{
return m_distToCenterSq > rhs.m_distToCenterSq;
}
const CFogVolumeRenderNode* m_pFogVol;
float m_distToCenterSq;
};
typedef std::vector< SCachedFogVolume > CachedFogVolumes;
typedef std::set< const CFogVolumeRenderNode* > GlobalFogVolumeMap;
static AABB s_tracableFogVolumeArea;
static StaticInstance<CachedFogVolumes> s_cachedFogVolumes;
static StaticInstance<GlobalFogVolumeMap> s_globalFogVolumeMap;
static bool s_forceTraceableAreaUpdate;
struct SFader
{
SFader()
: m_startTime(0)
, m_endTime(0)
, m_startValue(0)
, m_endValue(0)
{
}
void Set(float startTime, float endTime, float startValue, float endValue)
{
m_startTime = startTime;
m_endTime = endTime;
m_startValue = startValue;
m_endValue = endValue;
}
void SetInvalid()
{
Set(0, 0, 0, 0);
}
bool IsValid()
{
return m_startTime >= 0 && m_endTime > m_startTime && m_startValue != m_endValue;
}
bool IsTimeInRange(float time)
{
return time >= m_startTime && time <= m_endTime;
}
float GetValue(float time)
{
float t = clamp_tpl((time - m_startTime) / (m_endTime - m_startTime), 0.0f, 1.0f);
return m_startValue + t * (m_endValue - m_startValue);
}
private:
float m_startTime;
float m_endTime;
float m_startValue;
float m_endValue;
};
private:
Matrix34 m_matNodeWS;
Matrix34 m_matWS;
Matrix34 m_matWSInv;
int m_volumeType;
Vec3 m_pos;
Vec3 m_x;
Vec3 m_y;
Vec3 m_z;
// size of fog set by SFogVolumeProperties
Vec3 m_size;
// scale on entity
Vec3 m_scale;
float m_globalDensity;
float m_densityOffset;
float m_nearCutoff;
float m_fHDRDynamic;
float m_softEdges;
ColorF m_color;
bool m_useGlobalFogColor;
bool m_affectsThisAreaOnly;
Vec3 m_rampParams;
uint32 m_updateFrameID;
float m_windInfluence;
Vec3 m_windOffset;
float m_noiseElapsedTime;
float m_densityNoiseScale;
float m_densityNoiseOffset;
float m_densityNoiseTimeFrequency;
Vec3 m_densityNoiseFrequency;
Vec3 m_heightFallOffDir;
Vec3 m_heightFallOffDirScaled;
Vec3 m_heightFallOffShift;
Vec3 m_heightFallOffBasePoint;
AABB m_localBounds;
SFader m_globalDensityFader;
_smart_ptr< IMaterial > m_pMatFogVolEllipsoid;
_smart_ptr< IMaterial > m_pMatFogVolBox;
CREFogVolume* m_pFogVolumeRenderElement[RT_COMMAND_BUF_COUNT];
AABB m_WSBBox;
Vec2 m_cachedSoftEdgesLerp;
ColorF m_cachedFogColor;
};
#endif // CRYINCLUDE_CRY3DENGINE_FOGVOLUMERENDERNODE_H
@@ -1,399 +0,0 @@
/*
* 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 "FogVolumeRenderNode.h"
#include "VisAreas.h"
#include "CREFogVolume.h"
#include "Cry_Geo.h"
#include "ObjMan.h"
///////////////////////////////////////////////////////////////////////////////
inline static float expf_s(float arg)
{
return expf(clamp_tpl(arg, -80.0f, 80.0f));
}
AABB UnprojectAABB2D(const AABB& aabb, const CCamera& camera)
{
Vec3 results[4];
camera.Unproject(Vec3(aabb.min.x, aabb.min.y, 1), results[0]);
camera.Unproject(Vec3(aabb.max.x, aabb.min.y, 1), results[1]);
camera.Unproject(Vec3(aabb.max.x, aabb.max.y, 1), results[2]);
camera.Unproject(Vec3(aabb.min.x, aabb.max.y, 1), results[3]);
AABB newAABB(AABB::RESET);
newAABB.Add(results[0]);
newAABB.Add(results[1]);
newAABB.Add(results[2]);
newAABB.Add(results[3]);
return newAABB;
}
AABB GetProjectedQuadFromAABB(const AABB& aabb, const CCamera& camera)
{
// Get all 8 points of the AABB
Vec3 aabbPoints[] =
{
Vec3(aabb.max.x, aabb.max.y, aabb.max.z),
Vec3(aabb.max.x, aabb.min.y, aabb.max.z),
Vec3(aabb.min.x, aabb.min.y, aabb.max.z),
Vec3(aabb.min.x, aabb.max.y, aabb.max.z),
Vec3(aabb.max.x, aabb.max.y, aabb.min.z),
Vec3(aabb.max.x, aabb.min.y, aabb.min.z),
Vec3(aabb.min.x, aabb.min.y, aabb.min.z),
Vec3(aabb.min.x, aabb.max.y, aabb.min.z),
};
const int numAABBPoints = AZ_ARRAY_SIZE(aabbPoints);
AZ_Assert(numAABBPoints==8,"you should have 8 points in the aabb");
// Project each AABB point and construct another AABB.
AABB quadResult(AABB::RESET);
for (int i = 0; i < numAABBPoints; ++i)
{
Vec3 projectedPoint;
camera.Project(aabbPoints[i], projectedPoint);
quadResult.Add(projectedPoint);
}
return quadResult;
}
// To know if aabb are aligned with camera, we test if projected quads overlap.
bool CFogVolumeRenderNode::OverlapProjectedAABB(const AABB& aabb0, const AABB& aabb1, const CCamera& camera)
{
// quads are AABB2D.
AABB quad0 = GetProjectedQuadFromAABB(aabb0, camera);
AABB quad1 = GetProjectedQuadFromAABB(aabb1, camera);
bool bOverlap = Overlap::AABB_AABB2D(quad0, quad1);
return bOverlap;
}
void AverageFogVolume(SFogVolumeData& fogVolData, const CFogVolumeRenderNode* pFogVol)
{
AABB& avgAABBoxOut = fogVolData.avgAABBox;
avgAABBoxOut.Add(pFogVol->GetBBox());
// ratio is used to approximate fog volumes contribution depending of the importance of their size.
float ratio = pFogVol->GetBBox().GetRadius() / avgAABBoxOut.GetRadius();
fogVolData.m_heightFallOffBasePoint = Lerp(fogVolData.m_heightFallOffBasePoint,pFogVol->GetHeightFallOffBasePoint(), ratio);
fogVolData.m_heightFallOffDirScaled = Lerp(fogVolData.m_heightFallOffDirScaled,pFogVol->GetHeightFallOffDirScaled(), ratio);
fogVolData.m_densityOffset = Lerp(fogVolData.m_densityOffset, pFogVol->GetDensityoffset(), ratio);
fogVolData.m_globalDensity = Lerp(fogVolData.m_globalDensity, pFogVol->GetGlobalDensity(), ratio);
fogVolData.m_volumeType |= pFogVol->GetVolumeType();
}
///////////////////////////////////////////////////////////////////////////////
// TraceFogVolumes :
// if object intersects/is aligned with fog volumes then register these fog volumes,
// check if object is in front of the fog volume and compute average color.
// if highVertexShadingQuality average fog volume box.
///////////////////////////////////////////////////////////////////////////////
void CFogVolumeRenderNode::TraceFogVolumes(const Vec3& objPosition, const AABB& objAABB, SFogVolumeData& fogVolData, const SRenderingPassInfo& passInfo, bool fogVolumeShadingQuality)
{
FUNCTION_PROFILER_3DENGINE;
PrefetchLine(&s_tracableFogVolumeArea, 0);
// init default result
ColorF localFogColor = ColorF(0.0f, 0.0f, 0.0f, 0.0f);
// We will "accumulate" fog volumes contribution the same way that fog color.
// trace is needed when volumetric fog is off.
if (GetCVars()->e_Fog && GetCVars()->e_FogVolumes && (GetCVars()->e_VolumetricFog == 0))
{
const Vec3 worldPos = objPosition;
// init view ray
Vec3 camPos(s_tracableFogVolumeArea.GetCenter());
Lineseg lineseg(camPos, objPosition);
#ifdef _DEBUG
const SCachedFogVolume* prev(0);
#endif
// loop over all traceable fog volumes
CachedFogVolumes::const_iterator itEnd(s_cachedFogVolumes.end());
for (CachedFogVolumes::const_iterator it(s_cachedFogVolumes.begin()); it != itEnd; ++it)
{
// get current fog volume
const CFogVolumeRenderNode* pFogVol((*it).m_pFogVol);
bool isAligned = false;
bool isFrontOfBoxCenter = false;
// only trace visible fog volumes
if (!(pFogVol->GetRndFlags() & ERF_HIDDEN))
{
bool projectedAABBIntersect = false;
bool isInside = Overlap::Point_AABB(objPosition, pFogVol->m_WSBBox);
if (fogVolumeShadingQuality)
{
projectedAABBIntersect = OverlapProjectedAABB(pFogVol->m_WSBBox, objAABB, passInfo.GetCamera());
isInside = isInside || Overlap::AABB_AABB(objAABB, pFogVol->m_WSBBox);
}
// check if view ray intersects with bounding box of current fog volume
isAligned = Overlap::Lineseg_AABB(lineseg, pFogVol->m_WSBBox);
if (projectedAABBIntersect || isAligned)
{
// compute contribution of current fog volume
ColorF color(0,0,0);
// Get distance camera to fog volume.
const CCamera& cam(passInfo.GetCamera());
Vec3 cameraToObject(objPosition - cam.GetPosition());
Vec3 cameraToFogVolume(pFogVol->m_WSBBox.GetCenter() - cam.GetPosition());
isFrontOfBoxCenter = (cameraToFogVolume.GetLengthSquared() > cameraToObject.GetLengthSquared());
// if particle is in front of the box center and not inside the box, then do not accumulate fog color.
if (isFrontOfBoxCenter && !isInside)
{
continue;
}
if (fogVolumeShadingQuality)
{
// Accumulate this fogVolData
AverageFogVolume(fogVolData, pFogVol);
color = pFogVol->GetFogColor();
}
else
{
if (0 == pFogVol->m_volumeType)
{
pFogVol->GetVolumetricFogColorEllipsoid(worldPos, passInfo, color);
}
else
{
pFogVol->GetVolumetricFogColorBox(worldPos, passInfo, color);
}
color.a = 1.0f - color.a; // 0 = transparent, 1 = opaque
}
// blend fog colors
localFogColor.r = Lerp(localFogColor.r, color.r, color.a);
localFogColor.g = Lerp(localFogColor.g, color.g, color.a);
localFogColor.b = Lerp(localFogColor.b, color.b, color.a);
localFogColor.a = Lerp(localFogColor.a, 1.0f, color.a);
}
}
#ifdef _DEBUG
if (prev)
{
assert(prev->m_distToCenterSq >= (*it).m_distToCenterSq);
prev = &(*it);
}
#endif
}
const float fDivisor = (float)fsel(-localFogColor.a, 1.0f, localFogColor.a);
const float fMultiplier = (float)fsel(-localFogColor.a, 0.0f, 1.0f / fDivisor);
localFogColor.r *= fMultiplier;
localFogColor.g *= fMultiplier;
localFogColor.b *= fMultiplier;
}
localFogColor.a = 1.0f - localFogColor.a;
fogVolData.fogColor = localFogColor;
}
///////////////////////////////////////////////////////////////////////////////
void CFogVolumeRenderNode::GetVolumetricFogColorEllipsoid(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const
{
const CCamera& cam(passInfo.GetCamera());
Vec3 camPos(cam.GetPosition());
Vec3 camDir(cam.GetViewdir());
Vec3 cameraLookDir(worldPos - camPos);
resultColor = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
if (cameraLookDir.GetLengthSquared() > 1e-4f)
{
// setup ray tracing in OS
Vec3 cameraPosInOSx2(m_matWSInv.TransformPoint(camPos) * 2.0f);
Vec3 cameraLookDirInOS(m_matWSInv.TransformVector(cameraLookDir));
float tI(sqrtf(cameraLookDirInOS.Dot(cameraLookDirInOS)));
float invOfScaledCamDirLength(1.0f / tI);
cameraLookDirInOS *= invOfScaledCamDirLength;
// calc coefficients for ellipsoid parametrization (just a simple unit-sphere in its own space)
float B(cameraPosInOSx2.Dot(cameraLookDirInOS));
float Bsq(B * B);
float C(cameraPosInOSx2.Dot(cameraPosInOSx2) - 4.0f);
// solve quadratic equation
float discr(Bsq - C);
if (discr >= 0.0)
{
float discrSqrt = sqrtf(discr);
// ray hit
Vec3 cameraPosInWS(camPos);
Vec3 cameraLookDirInWS((worldPos - camPos) * invOfScaledCamDirLength);
//////////////////////////////////////////////////////////////////////////
float tS(max(0.5f * (-B - discrSqrt), 0.0f)); // clamp to zero so front ray-ellipsoid intersection is NOT behind camera
float tE(max(0.5f * (-B + discrSqrt), 0.0f)); // clamp to zero so back ray-ellipsoid intersection is NOT behind camera
//float tI( ( worldPos - camPos ).Dot( camDir ) / cameraLookDirInWS.Dot( camDir ) );
tI = max(tS, min(tI, tE)); // clamp to range [tS, tE]
Vec3 front(tS * cameraLookDirInWS + cameraPosInWS);
Vec3 dist((tI - tS) * cameraLookDirInWS);
float distLength(dist.GetLength());
float fogInt(distLength * expf_s(-(front - m_heightFallOffBasePoint).Dot(m_heightFallOffDirScaled)));
//////////////////////////////////////////////////////////////////////////
float heightDiff(dist.Dot(m_heightFallOffDirScaled));
if (fabsf(heightDiff) > 0.001f)
{
fogInt *= (1.0f - expf_s(-heightDiff)) / heightDiff;
}
float softArg(clamp_tpl(discr * m_cachedSoftEdgesLerp.x + m_cachedSoftEdgesLerp.y, 0.0f, 1.0f));
fogInt *= softArg * (2.0f - softArg);
float fog(expf_s(-m_globalDensity * fogInt));
resultColor = ColorF(m_cachedFogColor.r, m_cachedFogColor.g, m_cachedFogColor.b, min(fog, 1.0f));
}
}
}
///////////////////////////////////////////////////////////////////////////////
void CFogVolumeRenderNode::GetVolumetricFogColorBox(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const
{
const CCamera& cam(passInfo.GetCamera());
Vec3 camPos(cam.GetPosition());
Vec3 cameraLookDir(worldPos - camPos);
resultColor = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
if (cameraLookDir.GetLengthSquared() > 1e-4f)
{
// setup ray tracing in OS
Vec3 cameraPosInOS(m_matWSInv.TransformPoint(camPos));
Vec3 cameraLookDirInOS(m_matWSInv.TransformVector(cameraLookDir));
float tI(sqrtf(cameraLookDirInOS.Dot(cameraLookDirInOS)));
float invOfScaledCamDirLength(1.0f / tI);
cameraLookDirInOS *= invOfScaledCamDirLength;
const float fMax = std::numeric_limits<float>::max();
float tS(0), tE(fMax);
//TODO:
// May be worth profiling use of a loop here, iterating over elements of vector;
// might save on i-cache, but suspect we'll lose instruction interleaving and hit
// more register dependency issues.
//These fsels mean that the result is ignored if cameraLookDirInOS.x is 0.0f,
// avoiding a floating point compare. Avoiding the fcmp is ~15% faster
const float fXSelect = -fabsf(cameraLookDirInOS.x);
const float fXDivisor = (float)fsel(fXSelect, 1.0f, cameraLookDirInOS.x);
const float fXMultiplier = (float)fsel(fXSelect, 0.0f, 1.0f);
const float fXInvMultiplier = 1.0f - fXMultiplier;
//Accurate to 255/256ths on console
float invCameraDirInOSx = fres(fXDivisor); //(1.0f / fXDivisor);
float tPosPlane((1 - cameraPosInOS.x) * invCameraDirInOSx);
float tNegPlane((-1 - cameraPosInOS.x) * invCameraDirInOSx);
float tFrontFace = (float)fsel(-cameraLookDirInOS.x, tPosPlane, tNegPlane);
float tBackFace = (float)fsel(-cameraLookDirInOS.x, tNegPlane, tPosPlane);
tS = max(tS, tFrontFace * fXMultiplier);
tE = min(tE, (tBackFace * fXMultiplier) + (fXInvMultiplier * fMax));
const float fYSelect = -fabsf(cameraLookDirInOS.y);
const float fYDivisor = (float)fsel(fYSelect, 1.0f, cameraLookDirInOS.y);
const float fYMultiplier = (float)fsel(fYSelect, 0.0f, 1.0f);
const float fYInvMultiplier = 1.0f - fYMultiplier;
//Accurate to 255/256ths on console
float invCameraDirInOSy = fres(fYDivisor); //(1.0f / fYDivisor);
tPosPlane = ((1 - cameraPosInOS.y) * invCameraDirInOSy);
tNegPlane = ((-1 - cameraPosInOS.y) * invCameraDirInOSy);
tFrontFace = (float)fsel(-cameraLookDirInOS.y, tPosPlane, tNegPlane);
tBackFace = (float)fsel(-cameraLookDirInOS.y, tNegPlane, tPosPlane);
tS = max(tS, tFrontFace * fYMultiplier);
tE = min(tE, (tBackFace * fYMultiplier) + (fYInvMultiplier * fMax));
const float fZSelect = -fabsf(cameraLookDirInOS.z);
const float fZDivisor = (float)fsel(fZSelect, 1.0f, cameraLookDirInOS.z);
const float fZMultiplier = (float)fsel(fZSelect, 0.0f, 1.0f);
const float fZInvMultiplier = 1.0f - fZMultiplier;
//Accurate to 255/256ths on console
float invCameraDirInOSz = fres(fZDivisor); //(1.0f / fZDivisor);
tPosPlane = ((1 - cameraPosInOS.z) * invCameraDirInOSz);
tNegPlane = ((-1 - cameraPosInOS.z) * invCameraDirInOSz);
tFrontFace = (float)fsel(-cameraLookDirInOS.z, tPosPlane, tNegPlane);
tBackFace = (float)fsel(-cameraLookDirInOS.z, tNegPlane, tPosPlane);
tS = max(tS, tFrontFace * fZMultiplier);
tE = min(tE, (tBackFace * fZMultiplier) + (fZInvMultiplier * fMax));
tE = max(tE, 0.0f);
if (tS <= tE)
{
Vec3 cameraPosInWS(camPos);
Vec3 cameraLookDirInWS((worldPos - camPos) * invOfScaledCamDirLength);
//////////////////////////////////////////////////////////////////////////
tI = max(tS, min(tI, tE)); // clamp to range [tS, tE]
Vec3 front(tS * cameraLookDirInWS + cameraPosInWS);
Vec3 dist((tI - tS) * cameraLookDirInWS);
float distLength(dist.GetLength());
float fogInt(distLength * expf_s(-(front - m_heightFallOffBasePoint).Dot(m_heightFallOffDirScaled)));
//////////////////////////////////////////////////////////////////////////
float heightDiff(dist.Dot(m_heightFallOffDirScaled));
//heightDiff = fabsf( heightDiff ) > 0.001f ? heightDiff : 0.001f
heightDiff = (float)fsel((-fabsf(heightDiff) + 0.001f), 0.001f, heightDiff);
fogInt *= (1.0f - expf_s(-heightDiff)) * fres(heightDiff);
float fog(expf_s(-m_globalDensity * fogInt));
resultColor = ColorF(m_cachedFogColor.r, m_cachedFogColor.g, m_cachedFogColor.b, min(fog, 1.0f));
}
}
}
File diff suppressed because it is too large Load Diff
-309
View File
@@ -1,309 +0,0 @@
/*
* 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.
// Description : Manages geometry cache data
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHE_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHE_H
#pragma once
#if defined(USE_GEOM_CACHES)
#include <IGeomCache.h>
#include "GeomCacheFileFormat.h"
struct SGeomCacheStaticMeshData
{
bool m_bUsePredictor;
uint8 m_positionPrecision[3];
float m_uvMax;
uint32 m_numVertices;
GeomCacheFile::EStreams m_constantStreams;
GeomCacheFile::EStreams m_animatedStreams;
uint64 m_hash;
AABB m_aabb;
string m_name;
std::vector<vtx_idx> m_indices;
std::vector<uint32> m_numIndices;
stl::aligned_vector<Vec3, 16> m_positions;
stl::aligned_vector<UCol, 16> m_colors;
stl::aligned_vector<Vec2, 16> m_texcoords;
stl::aligned_vector<SPipTangents, 16> m_tangents;
std::vector<uint16> m_materialIds;
std::vector<uint16> m_predictorData;
};
struct SGeomCacheStaticNodeData
{
uint32 m_meshOrGeometryIndex;
uint32 m_numChildren;
GeomCacheFile::ENodeType m_type;
GeomCacheFile::ETransformType m_transformType;
QuatTNS m_localTransform;
uint32 m_nameHash;
string m_name;
};
class CGeomCacheStreamReader
{
public:
CGeomCacheStreamReader(const char* pData, const size_t length)
: m_pData(pData)
, m_length(length)
, m_position(0) {}
template<class T>
bool Read(T* pDest, size_t numElements)
{
const size_t numBytes = sizeof(T) * numElements;
if (m_position + numBytes > m_length)
{
return false;
}
memcpy(pDest, &m_pData[m_position], numBytes);
m_position += numBytes;
return true;
}
template<class T>
bool Read(T* pDest)
{
const size_t numBytes = sizeof(T);
if (m_position + numBytes > m_length)
{
return false;
}
memcpy(pDest, &m_pData[m_position], numBytes);
m_position += numBytes;
return true;
}
private:
const char* m_pData;
const size_t m_length;
size_t m_position;
};
struct IGeomCacheListener
{
public:
virtual ~IGeomCacheListener() {}
virtual void OnGeomCacheStaticDataLoaded() = 0;
virtual void OnGeomCacheStaticDataUnloaded() = 0;
};
class CGeomCache
: public IGeomCache
, public IStreamCallback
, public Cry3DEngineBase
{
friend class CGeomCacheManager;
public:
CGeomCache(const char* pFileName);
~CGeomCache();
// Gets number of frames
uint GetNumFrames() const;
// Returns true if cache plays back from memory
bool PlaybackFromMemory() const;
const char* GetFrameData(const uint frameIndex) const;
uint64 GetCompressedAnimationDataSize() const;
// Gets the max extend of the geom cache through the entire animation
const AABB& GetAABB() const override;
void SetProcessedByRenderNode(bool processedByRenderNode) override { m_processedByRenderNode = processedByRenderNode; }
// Returns frame for specific time. Rounds to ceil or floor
uint GetFloorFrameIndex(const float time) const;
uint GetCeilFrameIndex(const float time) const;
// Frame infos
GeomCacheFile::EFrameType GetFrameType(const uint frameIndex) const;
uint64 GetFrameOffset(const uint frameIndex) const;
uint32 GetFrameSize(const uint frameIndex) const;
float GetFrameTime(const uint frameIndex) const;
uint GetPrevIFrame(const uint frameIndex) const;
uint GetNextIFrame(const uint frameIndex) const;
// Returns true if this frame uses motion prediction and needs the last two frames
bool NeedsPrevFrames(const uint frameIndex) const;
// Validates a frame range for reading from disk
void ValidateReadRange(const uint start, uint& end) const;
// Get block compression format
GeomCacheFile::EBlockCompressionFormat GetBlockCompressionFormat() const;
// Access to the mesh and node lists
const std::vector<SGeomCacheStaticMeshData>& GetStaticMeshData() const { return m_staticMeshData; }
const std::vector<SGeomCacheStaticNodeData>& GetStaticNodeData() const { return m_staticNodeData; }
const std::vector<phys_geometry*>& GetPhysicsGeometries() const { return m_physicsGeometries; }
// Listener interface for async loading
void AddListener(IGeomCacheListener* pListener);
void RemoveListener(IGeomCacheListener* pListener);
bool IsLoaded() const { return m_bLoaded; }
void UnloadData();
// Ref count for streams
uint GetNumStreams() const { return m_numStreams; }
void IncreaseNumStreams() { ++m_numStreams; }
void DecreaseNumStreams() { --m_numStreams; }
// IGeomCache
virtual int AddRef();
virtual int Release();
virtual bool IsValid() const { return m_bValid; }
virtual void SetMaterial(_smart_ptr<IMaterial> pMaterial);
virtual _smart_ptr<IMaterial> GetMaterial();
virtual const _smart_ptr<IMaterial> GetMaterial() const;
virtual const char* GetFilePath() const;
virtual float GetDuration() const;
virtual IGeomCache::SStatistics GetStatistics() const;
virtual void Reload();
// Static data streaming
void UpdateStreamableComponents(float importance, const Matrix34A& objMatrix, IRenderNode* pRenderNode, bool bFullUpdate);
void SetLastDrawMainFrameId(const uint32 id) { m_lastDrawMainFrameId = id; }
// IStreamable
virtual void StartStreaming(bool bFinishNow, IReadStream_AutoPtr* ppStream);
virtual int GetStreamableContentMemoryUsage(bool bJustForDebug);
virtual void ReleaseStreamableContent();
virtual void GetStreamableName(string& sName);
virtual uint32 GetLastDrawMainFrameId();
virtual bool IsUnloadable() const;
// IStreamCallback
virtual void StreamOnComplete(IReadStream* pStream, unsigned nError);
virtual void StreamAsyncOnComplete(IReadStream* pStream, unsigned nError);
private:
struct SFrameInfo
{
float m_frameTime;
uint32 m_frameType;
uint32 m_frameSize;
uint32 m_prevIFrame;
uint32 m_nextIFrame;
uint64 m_frameOffset;
};
void Shutdown();
bool LoadGeomCache();
bool ReadFrameInfos(AZ::IO::HandleType fileHandle, const uint32 numFrames);
bool ReadStaticBlock(AZ::IO::HandleType fileHandle, GeomCacheFile::EBlockCompressionFormat compressionFormat, std::vector<char>& compressedData);
bool DecompressStaticBlock(GeomCacheFile::EBlockCompressionFormat compressionFormat, const char* pCompressedData, std::vector<char>& decompressedData);
bool ReadMeshesStaticData(CGeomCacheStreamReader& reader, const char* pFileName);
bool ReadMeshStaticData(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo,
SGeomCacheStaticMeshData& mesh, const char* pFileName);
bool LoadAnimatedData(const char* pData, const size_t bufferOffset);
bool ReadNodesStaticDataRec(CGeomCacheStreamReader& reader);
static bool CompareFrameTimes(const SFrameInfo& a, const SFrameInfo& b)
{
return a.m_frameTime < b.m_frameTime;
}
char* GetFrameData(const uint frameIndex);
bool m_bValid;
bool m_bLoaded;
int m_refCount;
_smart_ptr<IMaterial> m_pMaterial;
string m_fileName;
string m_lastError;
// Static data streaming state
bool m_bUseStreaming;
uint32 m_lastDrawMainFrameId;
IReadStreamPtr m_pStaticDataReadStream;
// Cache block compression format
GeomCacheFile::EBlockCompressionFormat m_blockCompressionFormat;
// Playback from memory flag
bool m_bPlaybackFromMemory;
// Number of frames
uint m_numFrames;
// Number of streams reading from this cache
uint m_numStreams;
// Offset of static mesh data
uint64 m_staticMeshDataOffset;
// Total size of animated data
uint64 m_compressedAnimationDataSize;
// Total size of uncompressed animation data
uint64 m_totalUncompressedAnimationSize;
// AABB of entire animation
AABB m_aabb;
// Static data size;
GeomCacheFile::SCompressedBlockHeader m_staticDataHeader;
// Frame infos
std::vector<SFrameInfo> m_frameInfos;
std::vector<SGeomCacheStaticMeshData> m_staticMeshData;
std::vector<SGeomCacheStaticNodeData> m_staticNodeData;
// Physics
std::vector<phys_geometry*> m_physicsGeometries;
// Holds references of static render meshes until cache object dies
std::vector<_smart_ptr<IRenderMesh> > m_staticRenderMeshes;
// Listeners
std::vector<IGeomCacheListener*> m_listeners;
// Animation data (memory playback)
std::vector<char> m_animationData;
// Only matters when e_streamCGF is 0
bool m_processedByRenderNode = true;
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHE_H
@@ -1,973 +0,0 @@
/*
* 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.
// Description : Decodes geom cache data
#include "Cry3DEngine_precompiled.h"
#if defined(USE_GEOM_CACHES)
#include "GeomCacheDecoder.h"
#include "GeomCache.h"
#include "GeomCacheRenderNode.h"
#include "GeomCachePredictors.h"
#include "IZlibDecompressor.h"
#include "ILZ4Decompressor.h"
#include "IZStdDecompressor.h"
#include "Cry3DEngineTraits.h"
namespace GeomCacheDecoder
{
// This namespace will provide the different vertex decode function permutations to avoid dynamic branching
const uint kNumPermutations = 2 * 2 * 2 * 3;
GeomCacheFile::Color FixedPointColorLerp(int32 a, int32 b, const int32 lerpFactor)
{
return a + (((b - a) * lerpFactor) >> 16);
}
inline uint GetDecodeVerticesPerm(const bool bMotionBlur, const GeomCacheFile::EStreams constantStreamMask, const GeomCacheFile::EStreams animatedStreamMask)
{
uint permutation = 0;
permutation += bMotionBlur ? (2 * 2 * 3) : 0;
permutation += (constantStreamMask& GeomCacheFile::eStream_Positions) ? (2 * 3) : 0;
permutation += (constantStreamMask& GeomCacheFile::eStream_Texcoords) ? 3 : 0;
permutation += (constantStreamMask& GeomCacheFile::eStream_Colors) ? 1 : 0;
permutation += (animatedStreamMask& GeomCacheFile::eStream_Colors) ? 2 : 0;
return permutation;
}
#if AZ_LEGACY_3DENGINE_TRAIT_DO_EXTRA_GEOMCACHE_PROCESSING
#define vec4f_swizzle(v, p, q, r, s) (_mm_shuffle_ps((v), (v), ((s) << 6 | (r) << 4 | (q) << 2 | (p))))
void ConvertToTangentAndBitangentVec4f(const __m128 interpolated, const __m128 floor, __m128& tangent, __m128& bitangent)
{
const __m128 comparedAgainstW = _mm_setr_ps(FLT_MIN, FLT_MIN, FLT_MIN, 0.0f);
const __m128 flipSignMask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
const __m128 twos = _mm_setr_ps(2.0f, 2.0f, 2.0f, 0.0f);
// (interpolated.w < 0.0f) != (floor.w < 0.0f) => flip sign of quaternions in registers
const __m128 cmp = _mm_xor_ps(_mm_cmplt_ps(interpolated, comparedAgainstW), _mm_cmplt_ps(floor, comparedAgainstW));
const __m128 signCmp = vec4f_swizzle(cmp, 3, 3, 3, 3);
const __m128 xyzw = _mm_xor_ps(interpolated, _mm_and_ps(signCmp, flipSignMask));
const __m128 wSignBit = _mm_and_ps(_mm_castsi128_ps(_mm_setr_epi32(0, 0, 0, 0x80000000)), xyzw);
// Calculate tangent & bitangent
const __m128 xxxx = vec4f_swizzle(xyzw, 0, 0, 0, 0);
const __m128 yyyy = vec4f_swizzle(xyzw, 1, 1, 1, 1);
const __m128 wwww = vec4f_swizzle(xyzw, 3, 3, 3, 3);
const __m128 wzyx = vec4f_swizzle(xyzw, 3, 2, 1, 0);
const __m128 zwxy = vec4f_swizzle(xyzw, 2, 3, 0, 1);
// tangent = (2 * (x * x + w * w) - 1, 2 * (y * x + z * w), 2 * (z * x - y * w), sign(w))
__m128 wwnw = _mm_xor_ps(wwww, _mm_castsi128_ps(_mm_setr_epi32(0, 0, 0x80000000, 0))); // -> (w, w, -w, w)
tangent = _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(xyzw, xxxx), _mm_mul_ps(wzyx, wwnw)), twos), _mm_setr_ps(-1.0f, 0.0f, 0.0f, 1.0f));
tangent = _mm_or_ps(wSignBit, tangent);
// bitangent = (2 * (x * y - z * w), 2 * (y * y + w * w) - 1, 2 * (z * y + x * w), sign(w))
__m128 nwww = _mm_xor_ps(wwww, _mm_castsi128_ps(_mm_setr_epi32(0x80000000, 0, 0, 0))); // -> (-w, w, w, w)
bitangent = _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(xyzw, yyyy), _mm_mul_ps(zwxy, nwww)), twos), _mm_setr_ps(0.0f, -1.0f, 0.0f, 1.0f));
bitangent = _mm_or_ps(wSignBit, bitangent);
}
// Don't use _mm_dp_ps because it's slower than the _mm_hadd_ps way (_mm_dp_ps is a microcoded instruction).
ILINE __m128 _mm_dp_ps_emu(const __m128& a, const __m128& b)
{
__m128 tmp1 = _mm_mul_ps(a, b);
__m128 tmp2 = _mm_hadd_ps(tmp1, tmp1);
return _mm_hadd_ps(tmp2, tmp2);
}
__m128i _mm_cvtepi16_epi32_emu(const __m128i& a)
{
#if AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_CVTEPI16_EPI32
return _mm_cvtepi16_epi32(a);
#else
// 5 instructions (unpack, and, cmp, and, or). Idea is to fill 0xFFFF in the hi-word if the sign bit of the lo-word 1.
const __m128i signBitsMask = _mm_set1_epi32(0x00008000);
const __m128i hiWordBitMask = _mm_set1_epi32(0xFFFF0000);
// Unsigned conversion, upper word will be 0x0000 even if sign bit is set
const __m128i unpacked = _mm_unpacklo_epi16(a, _mm_set1_epi16(0));
// Mask out sign bits
const __m128i signBitsMasked = _mm_castps_si128(_mm_and_ps(_mm_castsi128_ps(unpacked), _mm_castsi128_ps(signBitsMask)));
// Sets dwords to 0xFFFFFFFF if sign bit is 1
const __m128i cmpBits = _mm_cmpeq_epi32(signBitsMasked, signBitsMask);
// Mask dwords to 0xFFFF0000 if sign bit was set
const __m128i signExtendBits = _mm_and_si128(hiWordBitMask, cmpBits);
// Finally sign extend with 0xFFFF0000 if sign bit was set
return _mm_or_si128(unpacked, signExtendBits);
#endif
}
#endif
void DecodeAndInterpolateTangents(const uint numVertices, const float lerpFactor, const GeomCacheFile::QTangent* __restrict pFloorQTangents,
const GeomCacheFile::QTangent* __restrict pCeilQTangents, strided_pointer<SPipTangents> pTangents)
{
#if AZ_LEGACY_3DENGINE_TRAIT_DO_EXTRA_GEOMCACHE_PROCESSING
const uint numVerticesPerIteration = 2;
const uint numSIMDIterations = numVertices / numVerticesPerIteration;
const float kMultiplier = float((2 << (GeomCacheFile::kTangentQuatPrecision - 1)) - 1);
const __m128 convertFromUint16FactorPacked = _mm_set1_ps(1.0f / kMultiplier);
const __m128 lerpFactorPacked = _mm_set1_ps(lerpFactor);
const __m128i zero = _mm_setzero_si128();
const __m128 flipSignMask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
const __m128 scaleToInt16Factor = _mm_setr_ps(32767.0f, 32767.0f, 32767.0f, 32767.0f);
__m128i* pFloorQTangents128 = (__m128i*)&pFloorQTangents[0];
__m128i* pCeilQTangents128 = (__m128i*)&pCeilQTangents[0];
__m128i* pTangents128 = (__m128i*)pTangents.data;
for (unsigned int i = 0, j = 0; i < numSIMDIterations; ++i, j += 2)
{
const __m128i floorQTangents = _mm_load_si128(pFloorQTangents128 + i);
const __m128i ceilQTangents = _mm_load_si128(pCeilQTangents128 + i);
// Unpack to lo/hi qTangents and convert to float [-1, 1]
__m128 floorLo = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepi16_epi32_emu(floorQTangents)), convertFromUint16FactorPacked);
__m128 floorHi = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepi16_epi32_emu(_mm_shuffle_epi32(floorQTangents, _MM_SHUFFLE(1, 0, 3, 2)))), convertFromUint16FactorPacked);
__m128 ceilLo = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepi16_epi32_emu(ceilQTangents)), convertFromUint16FactorPacked);
__m128 ceilHi = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepi16_epi32_emu(_mm_shuffle_epi32(ceilQTangents, _MM_SHUFFLE(1, 0, 3, 2)))), convertFromUint16FactorPacked);
// Need to flip sign of the ceil quaternion if the dot product of floor and ceil < 0
__m128 dotLo = _mm_dp_ps_emu(floorLo, ceilLo);
__m128 dotCmpLo = _mm_cmplt_ps(dotLo, _mm_castsi128_ps(zero));
__m128 flipSignLo = _mm_and_ps(dotCmpLo, flipSignMask);
ceilLo = _mm_xor_ps(ceilLo, flipSignLo);
__m128 dotHi = _mm_dp_ps_emu(floorHi, ceilHi);
__m128 dotCmpHi = _mm_cmplt_ps(dotHi, _mm_castsi128_ps(zero));
__m128 flipSignHi = _mm_and_ps(dotCmpHi, flipSignMask);
ceilHi = _mm_xor_ps(ceilHi, flipSignHi);
// Interpolate the quaternions
__m128 interpolatedLo = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(ceilLo, floorLo), lerpFactorPacked), floorLo);
__m128 interpolatedHi = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(ceilHi, floorHi), lerpFactorPacked), floorHi);
// Normalize
interpolatedLo = _mm_mul_ps(_mm_rsqrt_ps(_mm_dp_ps_emu(interpolatedLo, interpolatedLo)), interpolatedLo);
interpolatedHi = _mm_mul_ps(_mm_rsqrt_ps(_mm_dp_ps_emu(interpolatedHi, interpolatedHi)), interpolatedHi);
// Convert to tangent/bitangent pairs
__m128 tangentLo, bitangentLo, tangentHi, bitangentHi;
ConvertToTangentAndBitangentVec4f(interpolatedLo, floorLo, tangentLo, bitangentLo);
ConvertToTangentAndBitangentVec4f(interpolatedHi, floorHi, tangentHi, bitangentHi);
// Scale and convert to int
__m128i tangentIntLo = _mm_cvtps_epi32(_mm_mul_ps(tangentLo, scaleToInt16Factor));
__m128i bitangentIntLo = _mm_cvtps_epi32(_mm_mul_ps(bitangentLo, scaleToInt16Factor));
__m128i tangentIntHi = _mm_cvtps_epi32(_mm_mul_ps(tangentHi, scaleToInt16Factor));
__m128i bitangentIntHi = _mm_cvtps_epi32(_mm_mul_ps(bitangentHi, scaleToInt16Factor));
// Pack
__m128i tangentBitangentLo = _mm_packs_epi32(tangentIntLo, bitangentIntLo);
__m128i tangentBitangentHi = _mm_packs_epi32(tangentIntHi, bitangentIntHi);
// And finally store
_mm_store_si128(pTangents128 + j, tangentBitangentLo);
_mm_store_si128(pTangents128 + j + 1, tangentBitangentHi);
}
const uint scalarStart = numSIMDIterations * numVerticesPerIteration;
#else
const uint scalarStart = 0;
#endif
for (unsigned int i = scalarStart; i < numVertices; ++i)
{
const Quat decodedFloorQTangent = DecodeQTangent(pFloorQTangents[i]);
const Quat decodedCeilQTangent = DecodeQTangent(pCeilQTangents[i]);
Quat interpolatedQTangent = Quat::CreateNlerp(decodedFloorQTangent, decodedCeilQTangent, lerpFactor);
if ((interpolatedQTangent.w < 0.0f) != (decodedFloorQTangent.w < 0.0f))
{
interpolatedQTangent = -interpolatedQTangent;
}
ConvertToTangentAndBitangent(interpolatedQTangent, pTangents[i]);
}
}
template<bool bConstantColors, bool bAnimatedColors, bool bConstantTexcoords>
void DecodeAndInterpolateColorAndTexcoords([[maybe_unused]] SGeomCacheRenderMeshUpdateContext& updateContext, [[maybe_unused]] const uint index,
const SGeomCacheStaticMeshData& staticMeshData, [[maybe_unused]] uint32 fpLerpFactor, const float lerpFactor,
[[maybe_unused]] const GeomCacheFile::Color* __restrict pFloorReds, [[maybe_unused]] const GeomCacheFile::Color* __restrict pCeilReds,
[[maybe_unused]] const GeomCacheFile::Color* __restrict pFloorGreens, [[maybe_unused]] const GeomCacheFile::Color* __restrict pCeilGreens,
[[maybe_unused]] const GeomCacheFile::Color* __restrict pFloorBlues, [[maybe_unused]] const GeomCacheFile::Color* __restrict pCeilBlues,
[[maybe_unused]] const GeomCacheFile::Color* __restrict pFloorAlphas, [[maybe_unused]] const GeomCacheFile::Color* __restrict pCeilAlphas,
[[maybe_unused]] const GeomCacheFile::Texcoords* __restrict pFloorTexcoords, [[maybe_unused]] const GeomCacheFile::Texcoords* __restrict pCeilTexcoords)
{
if constexpr (!bConstantColors && !bAnimatedColors)
{
updateContext.m_pColors[index].dcolor = 0xFFFFFFFF;
}
else if (bConstantColors)
{
updateContext.m_pColors[index].bcolor[0] = staticMeshData.m_colors[index].bcolor[0];
updateContext.m_pColors[index].bcolor[1] = staticMeshData.m_colors[index].bcolor[1];
updateContext.m_pColors[index].bcolor[2] = staticMeshData.m_colors[index].bcolor[2];
updateContext.m_pColors[index].bcolor[3] = staticMeshData.m_colors[index].bcolor[3];
}
else if (bAnimatedColors)
{
updateContext.m_pColors[index].bcolor[0] = FixedPointColorLerp(pFloorBlues[index], pCeilBlues[index], fpLerpFactor);
updateContext.m_pColors[index].bcolor[1] = FixedPointColorLerp(pFloorGreens[index], pCeilGreens[index], fpLerpFactor);
updateContext.m_pColors[index].bcolor[2] = FixedPointColorLerp(pFloorReds[index], pCeilReds[index], fpLerpFactor);
updateContext.m_pColors[index].bcolor[3] = FixedPointColorLerp(pFloorAlphas[index], pCeilAlphas[index], fpLerpFactor);
}
if (bConstantTexcoords)
{
updateContext.m_pTexcoords[index] = staticMeshData.m_texcoords[index];
}
else
{
updateContext.m_pTexcoords[index] = Vec2::CreateLerp(DecodeTexcoord(pFloorTexcoords[index], staticMeshData.m_uvMax), DecodeTexcoord(pCeilTexcoords[index], staticMeshData.m_uvMax), lerpFactor);
}
}
template<uint Permutation>
void DecodeMeshVerticesBranchless(SGeomCacheRenderMeshUpdateContext& updateContext,
const SGeomCacheStaticMeshData& staticMeshData, const char* pFloorFrameDataPtr,
const char* pCeilFrameDataPtr, const float lerpFactor)
{
const unsigned int numVertices = staticMeshData.m_numVertices;
const int32 fpLerpFactor = int32(lerpFactor * 65535.0f);
const bool bMotionBlur = Permutation % (2 * 2 * 2 * 3) >= (2 * 2 * 3);
const bool bConstantPositions = Permutation % (2 * 2 * 3) >= (2 * 3);
const bool bConstantTexcoords = (Permutation % (2 * 3)) >= 3;
const bool bConstantColors = (Permutation % 3) == 1;
const bool bAnimatedColors = (Permutation % 3) == 2;
const Vec3& aabbMin = staticMeshData.m_aabb.min;
const Vec3 aabbSize = staticMeshData.m_aabb.GetSize();
const GeomCacheFile::Position* __restrict pFloorPositions = bConstantPositions ? NULL :
reinterpret_cast<const GeomCacheFile::Position*>(pFloorFrameDataPtr);
const GeomCacheFile::Position* __restrict pCeilPositions = bConstantPositions ? NULL :
reinterpret_cast<const GeomCacheFile::Position*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += bConstantPositions ? 0 : ((numVertices * sizeof(GeomCacheFile::Position) + 15) & ~15);
pCeilFrameDataPtr += bConstantPositions ? 0 : ((numVertices * sizeof(GeomCacheFile::Position) + 15) & ~15);
const GeomCacheFile::Texcoords* __restrict pFloorTexcoords = bConstantTexcoords ? NULL :
reinterpret_cast<const GeomCacheFile::Texcoords*>(pFloorFrameDataPtr);
const GeomCacheFile::Texcoords* __restrict pCeilTexcoords = bConstantTexcoords ? NULL :
reinterpret_cast<const GeomCacheFile::Texcoords*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += bConstantTexcoords ? 0 : ((numVertices * sizeof(GeomCacheFile::Texcoords) + 15) & ~15);
pCeilFrameDataPtr += bConstantTexcoords ? 0 : ((numVertices * sizeof(GeomCacheFile::Texcoords) + 15) & ~15);
const GeomCacheFile::QTangent* __restrict pFloorQTangents = (bConstantPositions && bConstantTexcoords) ? NULL :
reinterpret_cast<const GeomCacheFile::QTangent*>(pFloorFrameDataPtr);
const GeomCacheFile::QTangent* __restrict pCeilQTangents = (bConstantPositions && bConstantTexcoords) ? NULL :
reinterpret_cast<const GeomCacheFile::QTangent*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += (bConstantPositions && bConstantTexcoords) ? 0 : ((numVertices * sizeof(GeomCacheFile::QTangent) + 15) & ~15);
pCeilFrameDataPtr += (bConstantPositions && bConstantTexcoords) ? 0 : ((numVertices * sizeof(GeomCacheFile::QTangent) + 15) & ~15);
const GeomCacheFile::Color* __restrict pFloorReds = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pFloorFrameDataPtr);
const GeomCacheFile::Color* __restrict pCeilReds = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
pCeilFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
const GeomCacheFile::Color* __restrict pFloorGreens = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pFloorFrameDataPtr);
const GeomCacheFile::Color* __restrict pCeilGreens = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
pCeilFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
const GeomCacheFile::Color* __restrict pFloorBlues = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pFloorFrameDataPtr);
const GeomCacheFile::Color* __restrict pCeilBlues = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
pCeilFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
const GeomCacheFile::Color* __restrict pFloorAlphas = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pFloorFrameDataPtr);
const GeomCacheFile::Color* __restrict pCeilAlphas = !bAnimatedColors ? NULL :
reinterpret_cast<const GeomCacheFile::Color*>(pCeilFrameDataPtr);
pFloorFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
pCeilFrameDataPtr += !bAnimatedColors ? 0 : ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15);
const Vec3 posConvertFactor = Vec3(1.0f / float((2 << (staticMeshData.m_positionPrecision[0] - 1)) - 1),
1.0f / float((2 << (staticMeshData.m_positionPrecision[1] - 1)) - 1),
1.0f / float((2 << (staticMeshData.m_positionPrecision[2] - 1)) - 1));
#if AZ_LEGACY_3DENGINE_TRAIT_DO_EXTRA_GEOMCACHE_PROCESSING
const uint numVerticesPerIteration = 8;
const uint numPackedFloatsPerIteration = (numVerticesPerIteration * sizeof(Vec3)) / 16;
const uint numPackedUInt16PerIteration = (numVerticesPerIteration * sizeof(Vec3_tpl<uint16>)) / 16;
const uint numFloatsPerIteration = numVerticesPerIteration * (sizeof(Vec3) / sizeof(float));
const uint numFloatsPerPack = 4;
const uint numSIMDIterations = numVertices / numVerticesPerIteration;
float* pPrevPositionsF = updateContext.m_prevPositions.size() > 0 ? (float*)&updateContext.m_prevPositions[0] : NULL;
float* pVelocitiesF = (float*)&updateContext.m_pVelocities[0];
__m128i* pFloorPositions128 = (__m128i*)&pFloorPositions[0];
__m128i* pCeilPositions128 = (__m128i*)&pCeilPositions[0];
const __m128 lerpFactorPacked = _mm_set1_ps(lerpFactor);
__m128 convertFromUint16FactorPacked[numPackedFloatsPerIteration];
convertFromUint16FactorPacked[0] = _mm_setr_ps(posConvertFactor.x, posConvertFactor.y, posConvertFactor.z, posConvertFactor.x);
convertFromUint16FactorPacked[1] = _mm_setr_ps(posConvertFactor.y, posConvertFactor.z, posConvertFactor.x, posConvertFactor.y);
convertFromUint16FactorPacked[2] = _mm_setr_ps(posConvertFactor.z, posConvertFactor.x, posConvertFactor.y, posConvertFactor.z);
convertFromUint16FactorPacked[3] = _mm_setr_ps(posConvertFactor.x, posConvertFactor.y, posConvertFactor.z, posConvertFactor.x);
convertFromUint16FactorPacked[4] = _mm_setr_ps(posConvertFactor.y, posConvertFactor.z, posConvertFactor.x, posConvertFactor.y);
convertFromUint16FactorPacked[5] = _mm_setr_ps(posConvertFactor.z, posConvertFactor.x, posConvertFactor.y, posConvertFactor.z);
__m128 aabbMinPacked[numPackedFloatsPerIteration];
aabbMinPacked[0] = _mm_setr_ps(aabbMin.x, aabbMin.y, aabbMin.z, aabbMin.x);
aabbMinPacked[1] = _mm_setr_ps(aabbMin.y, aabbMin.z, aabbMin.x, aabbMin.y);
aabbMinPacked[2] = _mm_setr_ps(aabbMin.z, aabbMin.x, aabbMin.y, aabbMin.z);
aabbMinPacked[3] = _mm_setr_ps(aabbMin.x, aabbMin.y, aabbMin.z, aabbMin.x);
aabbMinPacked[4] = _mm_setr_ps(aabbMin.y, aabbMin.z, aabbMin.x, aabbMin.y);
aabbMinPacked[5] = _mm_setr_ps(aabbMin.z, aabbMin.x, aabbMin.y, aabbMin.z);
__m128 aabbSizePacked[numPackedFloatsPerIteration];
aabbSizePacked[0] = _mm_setr_ps(aabbSize.x, aabbSize.y, aabbSize.z, aabbSize.x);
aabbSizePacked[1] = _mm_setr_ps(aabbSize.y, aabbSize.z, aabbSize.x, aabbSize.y);
aabbSizePacked[2] = _mm_setr_ps(aabbSize.z, aabbSize.x, aabbSize.y, aabbSize.z);
aabbSizePacked[3] = _mm_setr_ps(aabbSize.x, aabbSize.y, aabbSize.z, aabbSize.x);
aabbSizePacked[4] = _mm_setr_ps(aabbSize.y, aabbSize.z, aabbSize.x, aabbSize.y);
aabbSizePacked[5] = _mm_setr_ps(aabbSize.z, aabbSize.x, aabbSize.y, aabbSize.z);
__m128 newPositions[numPackedFloatsPerIteration];
__m128 oldPositions[numPackedFloatsPerIteration];
for (unsigned int i = 0; i < numSIMDIterations; ++i)
{
const uint floatOffset = i * numFloatsPerIteration;
if constexpr (bMotionBlur && !bConstantPositions)
{
for (uint j = 0; j < numPackedFloatsPerIteration; ++j)
{
oldPositions[j] = _mm_load_ps(&pPrevPositionsF[floatOffset + j * numFloatsPerPack]);
}
}
if (bConstantPositions)
{
for (uint j = 0; j < numVerticesPerIteration; ++j)
{
const uint index = (i * numVerticesPerIteration) + j;
updateContext.m_pPositions[index] = staticMeshData.m_positions[index];
}
}
else if (!bConstantPositions)
{
const __m128i zero = _mm_setzero_si128();
for (uint j = 0, k = 0; j < numPackedUInt16PerIteration; ++j, k += 2)
{
const uint indexLo = k;
const uint indexHi = k + 1;
__m128i floorPositions = _mm_load_si128(pFloorPositions128 + (i * numPackedUInt16PerIteration) + j);
__m128i ceilPositions = _mm_load_si128(pCeilPositions128 + (i * numPackedUInt16PerIteration) + j);
// Unpack and convert to float [0, 1]
__m128 floorLo = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(floorPositions, zero)), convertFromUint16FactorPacked[indexLo]);
__m128 floorHi = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(floorPositions, zero)), convertFromUint16FactorPacked[indexHi]);
__m128 ceilLo = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(ceilPositions, zero)), convertFromUint16FactorPacked[indexLo]);
__m128 ceilHi = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(ceilPositions, zero)), convertFromUint16FactorPacked[indexHi]);
// Convert to [aabbMin, aabbMax] range
floorLo = _mm_add_ps(_mm_mul_ps(floorLo, aabbSizePacked[indexLo]), aabbMinPacked[indexLo]);
floorHi = _mm_add_ps(_mm_mul_ps(floorHi, aabbSizePacked[indexHi]), aabbMinPacked[indexHi]);
ceilLo = _mm_add_ps(_mm_mul_ps(ceilLo, aabbSizePacked[indexLo]), aabbMinPacked[indexLo]);
ceilHi = _mm_add_ps(_mm_mul_ps(ceilHi, aabbSizePacked[indexHi]), aabbMinPacked[indexHi]);
// Interpolate
newPositions[indexLo] = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(ceilLo, floorLo), lerpFactorPacked), floorLo);
newPositions[indexHi] = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(ceilHi, floorHi), lerpFactorPacked), floorHi);
}
// Store to scratch & prev position array
_MS_ALIGN(16) Vec3 positionScratch[numVerticesPerIteration];
float* __restrict pPositionScratch128 = (float*)&positionScratch[0];
for (uint j = 0; j < numPackedFloatsPerIteration; ++j)
{
_mm_store_ps(pPositionScratch128 + j * numFloatsPerPack, newPositions[j]);
_mm_store_ps(&pPrevPositionsF[floatOffset + j * numFloatsPerPack], newPositions[j]);
}
// Scatter to position vertex stream
for (uint j = 0; j < numVerticesPerIteration; ++j)
{
const uint index = (i * numVerticesPerIteration) + j;
updateContext.m_pPositions[index] = positionScratch[j];
}
}
for (uint j = 0; j < numVerticesPerIteration; ++j)
{
const uint index = (i * numVerticesPerIteration) + j;
DecodeAndInterpolateColorAndTexcoords<bConstantColors, bAnimatedColors, bConstantTexcoords>(updateContext, index, staticMeshData, fpLerpFactor, lerpFactor,
pFloorReds, pCeilReds, pFloorGreens, pCeilGreens, pFloorBlues, pCeilBlues, pFloorAlphas, pCeilAlphas, pFloorTexcoords, pCeilTexcoords);
}
if (!bMotionBlur)
{
__m128 zero = _mm_setzero_ps();
for (uint j = 0; j < numPackedFloatsPerIteration; ++j)
{
_mm_store_ps(&pVelocitiesF[floatOffset + j * numFloatsPerPack], zero);
}
}
else if (!bConstantPositions)
{
for (uint j = 0; j < numPackedFloatsPerIteration; ++j)
{
__m128 motionVectors = _mm_sub_ps(oldPositions[j], newPositions[j]);
_mm_store_ps(&pVelocitiesF[floatOffset + j * numFloatsPerPack], motionVectors);
}
}
}
const uint scalarStart = numSIMDIterations * numVerticesPerIteration;
#else
const uint scalarStart = 0;
#endif
for (unsigned int i = scalarStart; i < numVertices; ++i)
{
Vec3 newPosition;
Vec3 oldPosition;
if constexpr (bMotionBlur && !bConstantPositions)
{
oldPosition = updateContext.m_prevPositions[i];
}
if (bConstantPositions)
{
newPosition = staticMeshData.m_positions[i];
}
else if (!bConstantPositions)
{
newPosition = Vec3::CreateLerp(DecodePosition(aabbMin, aabbSize, pFloorPositions[i], posConvertFactor),
DecodePosition(aabbMin, aabbSize, pCeilPositions[i], posConvertFactor), lerpFactor);
}
Vec3 oldPos = updateContext.m_pPositions[i];
updateContext.m_pPositions[i] = newPosition;
updateContext.m_prevPositions[i] = newPosition;
DecodeAndInterpolateColorAndTexcoords<bConstantColors, bAnimatedColors, bConstantTexcoords>(updateContext, i, staticMeshData, fpLerpFactor, lerpFactor,
pFloorReds, pCeilReds, pFloorGreens, pCeilGreens, pFloorBlues, pCeilBlues, pFloorAlphas, pCeilAlphas, pFloorTexcoords, pCeilTexcoords);
if (!bMotionBlur)
{
updateContext.m_pVelocities[i] = Vec3(0.0f, 0.0f, 0.0f);
}
else if (!bConstantPositions)
{
updateContext.m_pVelocities[i] = oldPosition - newPosition;
}
}
if constexpr (bConstantPositions && bConstantTexcoords)
{
for (unsigned int i = 0; i < numVertices; ++i)
{
updateContext.m_pTangents[i] = staticMeshData.m_tangents[i];
}
}
else
{
DecodeAndInterpolateTangents(numVertices, lerpFactor, pFloorQTangents, pCeilQTangents, updateContext.m_pTangents);
}
}
uint32 GetMeshDataSize(const SGeomCacheStaticMeshData& staticMeshData)
{
const unsigned int numVertices = staticMeshData.m_numVertices;
const GeomCacheFile::EStreams constantStreamMask = staticMeshData.m_constantStreams;
const GeomCacheFile::EStreams animatedStreamMask = staticMeshData.m_animatedStreams;
const bool bConstantPositions = (constantStreamMask& GeomCacheFile::eStream_Positions) != 0;
const bool bConstantTexcoords = (constantStreamMask& GeomCacheFile::eStream_Texcoords) != 0;
const bool bAnimatedColors = (animatedStreamMask& GeomCacheFile::eStream_Colors) != 0;
uint32 offset = 0;
offset += bConstantPositions ? 0 : ((numVertices * sizeof(GeomCacheFile::Position) + 15) & ~15);
offset += bConstantTexcoords ? 0 : ((numVertices * sizeof(GeomCacheFile::Texcoords) + 15) & ~15);
offset += (bConstantPositions && bConstantTexcoords) ? 0 : ((numVertices * sizeof(GeomCacheFile::QTangent) + 15) & ~15);
offset += !bAnimatedColors ? 0 : (4 * ((numVertices * sizeof(GeomCacheFile::Color) + 15) & ~15));
return offset;
}
typedef void (* TDecodeVerticesBranchlessPtr)(SGeomCacheRenderMeshUpdateContext& updateContext,
const SGeomCacheStaticMeshData& staticMeshData, const char* pFloorFrameDataPtr,
const char* pCeilFrameDataPtr, const float lerpFactor);
TDecodeVerticesBranchlessPtr pDecodeFunctions[kNumPermutations];
template <unsigned int Permutation>
struct PermutationInit
{
// Need two variables, because GCC otherwise complains about too many recursions
static TDecodeVerticesBranchlessPtr m_pFunction1;
static TDecodeVerticesBranchlessPtr m_pFunction2;
};
template <unsigned int Permutation>
TDecodeVerticesBranchlessPtr PermutationInit<Permutation>::m_pFunction1 = pDecodeFunctions[Permutation - 1]
= PermutationInit<Permutation - 1>::m_pFunction1 = &DecodeMeshVerticesBranchless<Permutation - 1>;
template<>
TDecodeVerticesBranchlessPtr PermutationInit<0>::m_pFunction1 = &DecodeMeshVerticesBranchless<0>;
template <unsigned int Permutation>
TDecodeVerticesBranchlessPtr PermutationInit<Permutation>::m_pFunction2 = pDecodeFunctions[Permutation - 1 + (kNumPermutations / 2)]
= PermutationInit<Permutation - 1>::m_pFunction2 = &DecodeMeshVerticesBranchless<Permutation - 1 + (kNumPermutations / 2)>;
template<>
TDecodeVerticesBranchlessPtr PermutationInit<0>::m_pFunction2 = &DecodeMeshVerticesBranchless<(kNumPermutations / 2)>;
// This forces the instantiation of PermutationInit<kNumPermutations / 2>::m_pFunction
// and therefore recursively initializes pDecodeFunctions with the DecodeVertices<N> permutations
template struct PermutationInit<kNumPermutations / 2>;
void DecodeIFrame(const CGeomCache* pGeomCache, char* pData)
{
// Skip header
pData += sizeof(GeomCacheFile::SFrameHeader);
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = pGeomCache->GetStaticMeshData();
const uint numMeshes = staticMeshData.size();
for (uint i = 0; i < numMeshes; ++i)
{
const SGeomCacheStaticMeshData& currentStaticMeshData = staticMeshData[i];
if (currentStaticMeshData.m_animatedStreams == 0)
{
continue;
}
pData += sizeof(GeomCacheFile::SMeshFrameHeader);
const GeomCacheFile::EStreams streamMask = currentStaticMeshData.m_animatedStreams;
const bool bUsePrediction = currentStaticMeshData.m_bUsePredictor;
const uint numVertices = currentStaticMeshData.m_numVertices;
if (streamMask & GeomCacheFile::eStream_Positions)
{
if (bUsePrediction)
{
GeomCacheFile::Position* pPositions = reinterpret_cast<GeomCacheFile::Position*>(pData);
GeomCachePredictors::ParallelogramPredictor<GeomCacheFile::Position, false>(numVertices, pPositions, pPositions, currentStaticMeshData.m_predictorData);
}
pData += ((sizeof(GeomCacheFile::Position) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_Texcoords)
{
if (bUsePrediction)
{
GeomCacheFile::Texcoords* pTexcoords = reinterpret_cast<GeomCacheFile::Texcoords*>(pData);
GeomCachePredictors::ParallelogramPredictor<GeomCacheFile::Texcoords, false>(numVertices, pTexcoords, pTexcoords, currentStaticMeshData.m_predictorData);
}
pData += ((sizeof(GeomCacheFile::Texcoords) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_QTangents)
{
if (bUsePrediction)
{
GeomCacheFile::QTangent* pQTangents = reinterpret_cast<GeomCacheFile::QTangent*>(pData);
GeomCachePredictors::QTangentPredictor<false>(numVertices, pQTangents, pQTangents, currentStaticMeshData.m_predictorData);
}
pData += ((sizeof(GeomCacheFile::QTangent) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_Colors)
{
if (bUsePrediction)
{
GeomCacheFile::Color* pReds = reinterpret_cast<GeomCacheFile::Color*>(pData);
pData += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pGreens = reinterpret_cast<GeomCacheFile::Color*>(pData);
pData += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pBlues = reinterpret_cast<GeomCacheFile::Color*>(pData);
pData += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pAlphas = reinterpret_cast<GeomCacheFile::Color*>(pData);
pData += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCachePredictors::ColorPredictor<false>(numVertices, pReds, pReds, currentStaticMeshData.m_predictorData);
GeomCachePredictors::ColorPredictor<false>(numVertices, pGreens, pGreens, currentStaticMeshData.m_predictorData);
GeomCachePredictors::ColorPredictor<false>(numVertices, pBlues, pBlues, currentStaticMeshData.m_predictorData);
GeomCachePredictors::ColorPredictor<false>(numVertices, pAlphas, pAlphas, currentStaticMeshData.m_predictorData);
}
else
{
pData += 4 * ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
}
}
}
}
void DecodeBFrame(const CGeomCache* pGeomCache, char* pData, char* pPrevFramesData[2], char* pFloorIndexFrameData, char* pCeilIndexFrameData)
{
// Skip header
size_t offset = sizeof(GeomCacheFile::SFrameHeader);
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = pGeomCache->GetStaticMeshData();
const uint numMeshes = staticMeshData.size();
for (uint i = 0; i < numMeshes; ++i)
{
const SGeomCacheStaticMeshData& currentStaticMeshData = staticMeshData[i];
if (currentStaticMeshData.m_animatedStreams == 0)
{
continue;
}
const GeomCacheFile::SMeshFrameHeader* pFrameHeader = reinterpret_cast<GeomCacheFile::SMeshFrameHeader*>(pData + offset);
offset += sizeof(GeomCacheFile::SMeshFrameHeader);
if ((pFrameHeader->m_flags & GeomCacheFile::eFrameFlags_Hidden) != 0)
{
offset += GetMeshDataSize(currentStaticMeshData);
continue;
}
const GeomCacheFile::EStreams streamMask = currentStaticMeshData.m_animatedStreams;
const uint numVertices = currentStaticMeshData.m_numVertices;
if (streamMask & GeomCacheFile::eStream_Positions)
{
GeomCacheFile::Position* pPositions = reinterpret_cast<GeomCacheFile::Position*>(pData + offset);
GeomCachePredictors::STemporalPredictorData<GeomCacheFile::Position> predictorData;
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Position*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Position*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Position*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Position*>(pCeilIndexFrameData + offset);
typedef Vec3_tpl<uint32> I;
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Position, false>
(pFrameHeader->m_positionStreamPredictorControl, predictorData, pPositions, pPositions);
offset += ((sizeof(GeomCacheFile::Position) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_Texcoords)
{
GeomCacheFile::Texcoords* pTexcoords = reinterpret_cast<GeomCacheFile::Texcoords*>(pData + offset);
GeomCachePredictors::STemporalPredictorData<GeomCacheFile::Texcoords> predictorData;
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Texcoords*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Texcoords*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Texcoords*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Texcoords*>(pCeilIndexFrameData + offset);
typedef Vec2_tpl<uint32> I;
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Texcoords, false>
(pFrameHeader->m_texcoordStreamPredictorControl, predictorData, pTexcoords, pTexcoords);
offset += ((sizeof(GeomCacheFile::Texcoords) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_QTangents)
{
GeomCacheFile::QTangent* pQTangents = reinterpret_cast<GeomCacheFile::QTangent*>(pData + offset);
GeomCachePredictors::STemporalPredictorData<GeomCacheFile::QTangent> predictorData;
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::QTangent*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::QTangent*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::QTangent*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::QTangent*>(pCeilIndexFrameData + offset);
typedef Vec4_tpl<uint32> I;
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::QTangent, false>
(pFrameHeader->m_qTangentStreamPredictorControl, predictorData, pQTangents, pQTangents);
offset += ((sizeof(GeomCacheFile::QTangent) * numVertices) + 15) & ~15;
}
if (streamMask & GeomCacheFile::eStream_Colors)
{
GeomCachePredictors::STemporalPredictorData<GeomCacheFile::Color> predictorData;
typedef uint16 I;
GeomCacheFile::Color* pReds = reinterpret_cast<GeomCacheFile::Color*>(pData + offset);
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Color*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Color*>(pCeilIndexFrameData + offset);
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Color, false>
(pFrameHeader->m_colorStreamPredictorControl[0], predictorData, pReds, pReds);
offset += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pGreens = reinterpret_cast<GeomCacheFile::Color*>(pData + offset);
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Color*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Color*>(pCeilIndexFrameData + offset);
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Color, false>
(pFrameHeader->m_colorStreamPredictorControl[1], predictorData, pGreens, pGreens);
offset += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pBlues = reinterpret_cast<GeomCacheFile::Color*>(pData + offset);
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Color*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Color*>(pCeilIndexFrameData + offset);
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Color, false>
(pFrameHeader->m_colorStreamPredictorControl[2], predictorData, pBlues, pBlues);
offset += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
GeomCacheFile::Color* pAlphas = reinterpret_cast<GeomCacheFile::Color*>(pData + offset);
predictorData.m_numElements = numVertices;
predictorData.m_pPrevFrames[0] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[0] + offset);
predictorData.m_pPrevFrames[1] = reinterpret_cast<GeomCacheFile::Color*>(pPrevFramesData[1] + offset);
predictorData.m_pFloorFrame = reinterpret_cast<GeomCacheFile::Color*>(pFloorIndexFrameData + offset);
predictorData.m_pCeilFrame = reinterpret_cast<GeomCacheFile::Color*>(pCeilIndexFrameData + offset);
GeomCachePredictors::InterpolateMotionDeltaPredictor<I, GeomCacheFile::Color, false>
(pFrameHeader->m_colorStreamPredictorControl[3], predictorData, pAlphas, pAlphas);
offset += ((sizeof(GeomCacheFile::Color) * numVertices) + 15) & ~15;
}
}
}
bool PrepareFillMeshData([[maybe_unused]] SGeomCacheRenderMeshUpdateContext& updateContext, const SGeomCacheStaticMeshData& staticMeshData,
const char*& pFloorFrameMeshData, const char*& pCeilFrameMeshData, size_t& offsetToNextMesh, float& lerpFactor)
{
const GeomCacheFile::SMeshFrameHeader* pFloorHeader = reinterpret_cast<const GeomCacheFile::SMeshFrameHeader* const>(pFloorFrameMeshData);
const GeomCacheFile::SMeshFrameHeader* pCeilHeader = reinterpret_cast<const GeomCacheFile::SMeshFrameHeader* const>(pCeilFrameMeshData);
pFloorFrameMeshData += sizeof(GeomCacheFile::SMeshFrameHeader);
pCeilFrameMeshData += sizeof(GeomCacheFile::SMeshFrameHeader);
const bool bFloorFrameHidden = (pFloorHeader->m_flags & GeomCacheFile::eFrameFlags_Hidden) != 0;
const bool bCeilFrameHidden = (pCeilHeader->m_flags & GeomCacheFile::eFrameFlags_Hidden) != 0;
offsetToNextMesh = GetMeshDataSize(staticMeshData);
if (bFloorFrameHidden && bCeilFrameHidden)
{
return false;
}
else if (bFloorFrameHidden)
{
lerpFactor = 1.0f;
}
else if (bCeilFrameHidden)
{
lerpFactor = 0.0f;
}
#if defined(CONSOLE_CONST_CVAR_MODE)
if constexpr (CVars::e_GeomCacheLerpBetweenFrames == 0)
#else
if (Cry3DEngineBase::m_pCVars->e_GeomCacheLerpBetweenFrames == 0)
#endif
{
pCeilFrameMeshData = pFloorFrameMeshData;
lerpFactor = 0.0f;
}
return true;
}
void FillMeshDataFromDecodedFrame(const bool bMotionBlur, SGeomCacheRenderMeshUpdateContext& updateContext,
const SGeomCacheStaticMeshData& staticMeshData, const char* pFloorFrameMeshData, const char* pCeilFrameMeshData, float lerpFactor)
{
// Fetch indices from static data
const uint numIndices = staticMeshData.m_indices.size();
for (uint i = 0; i < numIndices; ++i)
{
updateContext.m_pIndices[i] = staticMeshData.m_indices[i];
}
const uint permutation = GetDecodeVerticesPerm(bMotionBlur, staticMeshData.m_constantStreams, staticMeshData.m_animatedStreams);
TDecodeVerticesBranchlessPtr pDecodeFunction = pDecodeFunctions[permutation];
(*pDecodeFunction)(updateContext, staticMeshData, pFloorFrameMeshData, pCeilFrameMeshData, lerpFactor);
}
Vec3 DecodePosition(const Vec3& aabbMin, const Vec3& aabbSize, const GeomCacheFile::Position& inPosition, const Vec3& convertFactor)
{
return Vec3(aabbMin.x + ((float)inPosition.x * convertFactor.x) * aabbSize.x,
aabbMin.y + ((float)inPosition.y * convertFactor.y) * aabbSize.y,
aabbMin.z + ((float)inPosition.z * convertFactor.z) * aabbSize.z);
}
Vec2 DecodeTexcoord(const GeomCacheFile::Texcoords& inTexcoords, float uvMax)
{
const float convertFromInt16Factor = 1.0f / 32767.0f;
return Vec2((float)inTexcoords.x * convertFromInt16Factor * uvMax,
(float)inTexcoords.y * convertFromInt16Factor * uvMax);
}
Quat DecodeQTangent(const GeomCacheFile::QTangent& inQTangent)
{
const float kMultiplier = float((2 << (GeomCacheFile::kTangentQuatPrecision - 1)) - 1);
const float convertFromInt16Factor = 1.0f / kMultiplier;
return Quat((float)inQTangent.w * convertFromInt16Factor, (float)inQTangent.x * convertFromInt16Factor,
(float)inQTangent.y * convertFromInt16Factor, (float)inQTangent.z * convertFromInt16Factor);
}
void TransformAndConvertToTangentAndBitangent(const Quat& rotation, const Quat& inQTangent, SPipTangents& outTangents)
{
int16 reflection = alias_cast<uint32>(inQTangent.w) & 0x80000000 ? -1 : +1;
Quat transformedQTangent = rotation * inQTangent;
outTangents = SPipTangents(transformedQTangent, reflection);
}
void ConvertToTangentAndBitangent(const Quat& inQTangent, SPipTangents& outTangents)
{
int16 reflection = alias_cast<uint32>(inQTangent.w) & 0x80000000 ? -1 : +1;
outTangents = SPipTangents(inQTangent, reflection);
}
uint32 GetDecompressBufferSize(const char* const pStartBlock, const unsigned int numFrames)
{
FUNCTION_PROFILER_3DENGINE;
uint32 totalUncompressedSize = 0;
const char* pCurrentBlock = pStartBlock;
const uint32 headersSize = ((sizeof(SGeomCacheFrameHeader) * numFrames) + 15) & ~15;
for (unsigned int i = 0; i < numFrames; ++i)
{
const GeomCacheFile::SCompressedBlockHeader* pBlockHeader = reinterpret_cast<const GeomCacheFile::SCompressedBlockHeader*>(pCurrentBlock);
pCurrentBlock += sizeof(GeomCacheFile::SCompressedBlockHeader) + pBlockHeader->m_compressedSize;
totalUncompressedSize += pBlockHeader->m_uncompressedSize;
}
uint32 totalSize = headersSize + totalUncompressedSize;
if (totalSize % 16 != 0)
{
CryFatalError("GetDecompressBufferSize mod 16 != 0");
}
return totalSize;
}
bool DecompressBlock(const GeomCacheFile::EBlockCompressionFormat compressionFormat, char* const pDest, const char* const pSource)
{
FUNCTION_PROFILER_3DENGINE;
const GeomCacheFile::SCompressedBlockHeader* const pBlockHeader = reinterpret_cast<const GeomCacheFile::SCompressedBlockHeader* const>(pSource);
const char* const pBlockData = reinterpret_cast<const char* const>(pSource + sizeof(GeomCacheFile::SCompressedBlockHeader));
if (compressionFormat == GeomCacheFile::eBlockCompressionFormat_None)
{
assert(pBlockHeader->m_compressedSize == pBlockHeader->m_uncompressedSize);
memcpy(const_cast<char*>(pDest), pSource + sizeof(GeomCacheFile::SCompressedBlockHeader), pBlockHeader->m_uncompressedSize);
}
else if (compressionFormat == GeomCacheFile::eBlockCompressionFormat_Deflate)
{
IZLibInflateStream* pInflateStream = GetISystem()->GetIZLibDecompressor()->CreateInflateStream();
pInflateStream->SetOutputBuffer(const_cast<char*>(pDest), pBlockHeader->m_uncompressedSize);
pInflateStream->Input(pBlockData, pBlockHeader->m_compressedSize);
pInflateStream->EndInput();
EZInflateState state = pInflateStream->GetState();
assert(state == eZInfState_Finished);
pInflateStream->Release();
if (state == eZInfState_Error)
{
return false;
}
}
else if (compressionFormat == GeomCacheFile::eBlockCompressionFormat_LZ4HC)
{
ILZ4Decompressor* pDecompressor = GetISystem()->GetLZ4Decompressor();
return pDecompressor->DecompressData(pBlockData, const_cast<char*>(pDest), pBlockHeader->m_uncompressedSize);
}
else if (compressionFormat == GeomCacheFile::eBlockCompressionFormat_ZSTD)
{
IZStdDecompressor* pDecompressor = GetISystem()->GetZStdDecompressor();
return pDecompressor->DecompressData(pBlockData, pBlockHeader->m_compressedSize, const_cast<char*>(pDest), pBlockHeader->m_uncompressedSize);
}
else
{
return false;
}
return true;
}
bool DecompressBlocks(const GeomCacheFile::EBlockCompressionFormat compressionFormat, char* const pDest,
const char* const pSource, const uint blockOffset, const uint numBlocks, const uint numHandleFrames)
{
FUNCTION_PROFILER_3DENGINE;
const char* pCurrentSource = pSource;
const uint32 headersSize = ((sizeof(SGeomCacheFrameHeader) * numHandleFrames) + 15) & ~15;
char* pCurrentDest = pDest + headersSize;
for (uint i = 0; i < blockOffset; ++i)
{
const GeomCacheFile::SCompressedBlockHeader* const pBlockHeader = reinterpret_cast<const GeomCacheFile::SCompressedBlockHeader* const>(pCurrentSource);
pCurrentSource += sizeof(GeomCacheFile::SCompressedBlockHeader) + pBlockHeader->m_compressedSize;
pCurrentDest += pBlockHeader->m_uncompressedSize;
}
for (uint i = blockOffset; i < blockOffset + numBlocks; ++i)
{
const GeomCacheFile::SCompressedBlockHeader* const pBlockHeader = reinterpret_cast<const GeomCacheFile::SCompressedBlockHeader* const>(pCurrentSource);
if (!DecompressBlock(compressionFormat, pCurrentDest, pCurrentSource))
{
return false;
}
SGeomCacheFrameHeader* pHeader = reinterpret_cast<SGeomCacheFrameHeader*>(pDest + i * sizeof(SGeomCacheFrameHeader));
pHeader->m_offset = static_cast<uint32>(pCurrentDest - pDest);
pHeader->m_state = SGeomCacheFrameHeader::eFHS_Undecoded;
pCurrentSource += sizeof(GeomCacheFile::SCompressedBlockHeader) + pBlockHeader->m_compressedSize;
pCurrentDest += pBlockHeader->m_uncompressedSize;
}
return true;
}
}
#endif
@@ -1,77 +0,0 @@
/*
* 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.
// Description : Decodes geom cache data
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHEDECODER_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHEDECODER_H
#pragma once
#if defined(USE_GEOM_CACHES)
#include "GeomCacheFileFormat.h"
class CGeomCache;
struct SGeomCacheRenderMeshUpdateContext;
struct SGeomCacheStaticMeshData;
struct SGeomCacheFrameHeader
{
enum EFrameHeaderState
{
eFHS_Uninitialized = 0,
eFHS_Undecoded = 1,
eFHS_Decoded = 2
};
EFrameHeaderState m_state;
uint32 m_offset;
};
namespace GeomCacheDecoder
{
// Decodes an index frame
void DecodeIFrame(const CGeomCache* pGeomCache, char* pData);
// Decodes a bi-directional predicted frame
void DecodeBFrame(const CGeomCache * pGeomCache, char* pData, char* pPrevFramesData[2],
char* pFloorIndexFrameData, char* pCeilIndexFrameData);
bool PrepareFillMeshData(SGeomCacheRenderMeshUpdateContext& updateContext, const SGeomCacheStaticMeshData& staticMeshData,
const char*& pFloorFrameMeshData, const char*& pCeilFrameMeshData, size_t& offsetToNextMesh, float& lerpFactor);
void FillMeshDataFromDecodedFrame(const bool bMotionBlur, SGeomCacheRenderMeshUpdateContext& updateContext,
const SGeomCacheStaticMeshData& staticMeshData, const char* pFloorFrameMeshData,
const char* pCeilFrameMeshData, float lerpFactor);
// Gets total needed space for uncompressing successive blocks
uint32 GetDecompressBufferSize(const char* const pStartBlock, const uint numFrames);
// Decompresses one block of compressed data with header for input
bool DecompressBlock(const GeomCacheFile::EBlockCompressionFormat compressionFormat, char* const pDest, const char* const pSource);
// Decompresses blocks of compressed data with headers for input and output
bool DecompressBlocks(const GeomCacheFile::EBlockCompressionFormat compressionFormat, char* const pDest,
const char* const pSource, const uint blockOffset, const uint numBlocks, const uint numHandleFrames);
Vec3 DecodePosition(const Vec3& aabbMin, const Vec3& aabbSize, const GeomCacheFile::Position& inPosition, const Vec3& convertFactor);
Vec2 DecodeTexcoord(const GeomCacheFile::Texcoords& inTexcoords, float uvMax);
Quat DecodeQTangent(const GeomCacheFile::QTangent& inQTangent);
void TransformAndConvertToTangentAndBitangent(const Quat& rotation, const Quat& inQTangent, SPipTangents& outTangents);
void ConvertToTangentAndBitangent(const Quat& inQTangent, SPipTangents& outTangents);
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHEDECODER_H
File diff suppressed because it is too large Load Diff
@@ -1,283 +0,0 @@
/*
* 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.
// Description : Manages geometry cache instances and streaming
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H
#pragma once
#if defined(USE_GEOM_CACHES)
#include "GeomCacheDecoder.h"
#include "GeomCacheMeshManager.h"
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzCore/Jobs/LegacyJobExecutor.h>
class CGeomCache;
class CGeomCacheRenderNode;
struct SGeomCacheStreamInfo;
struct SGeomCacheBufferHandle
{
SGeomCacheBufferHandle()
{
m_pNext = NULL;
m_startFrame = 0;
m_endFrame = 0;
m_bufferSize = 0;
m_pBuffer = NULL;
m_pStream = NULL;
m_numJobReferences = 0;
}
volatile int m_numJobReferences;
uint32 m_bufferSize;
uint32 m_startFrame;
uint32 m_endFrame;
char* m_pBuffer;
SGeomCacheStreamInfo* m_pStream;
CTimeValue m_frameTime;
// Next buffer handle for this stream or in the free list
SGeomCacheBufferHandle* m_pNext;
CryConditionVariable m_jobReferencesCV;
};
// This handle represents a block in the read buffer
struct SGeomCacheReadRequestHandle
: public SGeomCacheBufferHandle
, public IStreamCallback
{
// IStreamCallback
virtual void StreamOnComplete([[maybe_unused]] IReadStream* pStream, [[maybe_unused]] unsigned nError) {}
virtual void StreamAsyncOnComplete(IReadStream* pStream, unsigned nError)
{
if (nError != 0 && nError != ERROR_USER_ABORT)
{
string error = "Geom cache read request failed with error: " + string(pStream->GetErrorName());
gEnv->pLog->LogError("%s", error.c_str());
}
m_state = eRRHS_FinishedRead;
m_error = nError;
if (CryInterlockedDecrement(&m_numJobReferences) == 0)
{
m_jobReferencesCV.Notify();
}
}
enum EReadRequestHandleState
{
eRRHS_Reading = 0,
eRRHS_FinishedRead = 1,
eRRHS_Decompressing = 2,
eRRHS_Done = 3
};
volatile EReadRequestHandleState m_state;
volatile long m_error;
IReadStreamPtr m_pReadStream;
};
struct SGeomCacheStreamInfo
{
SGeomCacheStreamInfo(CGeomCacheRenderNode* pRenderNode, CGeomCache* pGeomCache, const uint numFrames)
: m_pRenderNode(pRenderNode)
, m_pGeomCache(pGeomCache)
, m_numFrames(numFrames)
, m_displayedFrameTime(-1.0f)
, m_wantedPlaybackTime(0.0f)
, m_wantedFloorFrame(0)
, m_wantedCeilFrame(0)
, m_sameFrameFillCount(0)
, m_numFramesMissed(0)
, m_pOldestReadRequestHandle(NULL)
, m_pNewestReadRequestHandle(NULL)
, m_pReadAbortListHead(NULL)
, m_pOldestDecompressHandle(NULL)
, m_pNewestDecompressHandle(NULL)
, m_pDecompressAbortListHead(NULL)
, m_bAbort(0L)
, m_bLooping(false)
{}
CGeomCacheRenderNode* m_pRenderNode;
CGeomCache* m_pGeomCache;
uint m_numFrames;
volatile float m_displayedFrameTime;
volatile float m_wantedPlaybackTime;
volatile uint m_wantedFloorFrame;
volatile uint m_wantedCeilFrame;
volatile int m_sameFrameFillCount;
uint m_numFramesMissed;
SGeomCacheReadRequestHandle* m_pOldestReadRequestHandle;
SGeomCacheReadRequestHandle* m_pNewestReadRequestHandle;
SGeomCacheReadRequestHandle* m_pReadAbortListHead;
SGeomCacheBufferHandle* m_pOldestDecompressHandle;
SGeomCacheBufferHandle* m_pNewestDecompressHandle;
SGeomCacheBufferHandle* m_pDecompressAbortListHead;
volatile bool m_bAbort;
CryCriticalSection m_abortCS;
bool m_bLooping;
AZ::LegacyJobExecutor m_fillRenderNodeJobExecutor;
struct SFrameData
{
bool m_bDecompressJobLaunched;
// For each frame we initialize a counter to the number of jobs
// that need to complete before the frame can be decoded.
//
// Each index frame has exactly one dependent job (inflate)
// The first B frame after an index frame has three dependencies (inflate + previous and last index frame)
// All other B frames have only two dependencies (inflate + previous B frame)
int m_decodeDependencyCounter;
// Pointer to decompress handle for this frame.
SGeomCacheBufferHandle* m_pDecompressHandle;
};
// Array with data for each frame
std::vector<SFrameData> m_frameData;
};
struct SDecodeFrameJobData
{
uint m_frameIndex;
const CGeomCache* m_pGeomCache;
SGeomCacheStreamInfo* m_pStreamInfo;
};
class CGeomCacheManager
: public Cry3DEngineBase
, public AzFramework::LegacyAssetEventBus::Handler
{
public:
CGeomCacheManager();
~CGeomCacheManager();
// Called during level unload to free all resource references
void Reset();
CGeomCache* LoadGeomCache(const char* szFileName);
void DeleteGeomCache(CGeomCache* pGeomCache);
void StreamingUpdate();
void RegisterForStreaming(CGeomCacheRenderNode* pRenderNode);
void UnRegisterForStreaming(CGeomCacheRenderNode* pRenderNode, bool bWaitForJobs);
float GetPrecachedTime(const IGeomCacheRenderNode* pRenderNode);
#ifndef _RELEASE
void DrawDebugInfo();
void ResetDebugInfo() { m_numMissedFrames = 0; m_numStreamAborts = 0; m_numFailedAllocs = 0; }
#endif
void DecompressFrame_JobEntry(SGeomCacheStreamInfo* pStreamInfo, const uint blockIndex,
SGeomCacheBufferHandle* pDecompressHandle, SGeomCacheReadRequestHandle* pReadRequestHandle);
void FillRenderNodeAsync_JobEntry(SGeomCacheStreamInfo* pStreamInfo);
void DecodeIFrame_JobEntry(SDecodeFrameJobData jobState);
void DecodeBFrame_JobEntry(SDecodeFrameJobData jobState);
CGeomCacheMeshManager& GetMeshManager() { return m_meshManager; }
void StopCacheStreamsAndWait(CGeomCache* pGeomCache);
CGeomCache* FindGeomCacheByFilename(const char* filename);
// For changing the buffer size on runtime. This will do a blocking wait on all active streams,
// so it should only be called when we are sure that no caches are playing (e.g. on level load)
void ChangeBufferSize(const uint newSizeInMiB);
private:
// override from LegacyAssetEventBus::Handler
void OnFileChanged(AZStd::string assetPath) override;
static void OnChangeBufferSize(ICVar* pCVar);
void ReinitializeStreamFrameData(SGeomCacheStreamInfo& streamInfo, uint startFrame, uint endFrame);
void UnloadGeomCaches();
bool IssueDiskReadRequest(SGeomCacheStreamInfo& pStreamInfo);
void LaunchStreamingJobs(const uint numStreams, const CTimeValue currentFrameTime);
void LaunchDecompressJobs(SGeomCacheStreamInfo* pStreamInfo, const CTimeValue currentFrameTime);
void LaunchDecodeJob(SDecodeFrameJobData jobState);
template<class TBufferHandleType>
TBufferHandleType* NewBufferHandle(const uint32 size, SGeomCacheStreamInfo& streamInfo);
SGeomCacheReadRequestHandle* NewReadRequestHandle(const uint32 size, SGeomCacheStreamInfo& streamInfo);
void RetireHandles(SGeomCacheStreamInfo& streamInfo);
void RetireOldestReadRequestHandle(SGeomCacheStreamInfo& streamInfo);
void RetireOldestDecompressHandle(SGeomCacheStreamInfo& streamInfo);
void RetireDecompressHandle(SGeomCacheStreamInfo& streamInfo, SGeomCacheBufferHandle* pHandle);
template<class TBufferHandleType>
void RetireBufferHandle(TBufferHandleType* pHandle);
void RetireRemovedStreams();
void ValidateStream(SGeomCacheStreamInfo& streamInfo);
void AbortStream(SGeomCacheStreamInfo& streamInfo);
void AbortStreamAndWait(SGeomCacheStreamInfo& streamInfo);
void RetireAbortedHandles(SGeomCacheStreamInfo& streamInfo);
SGeomCacheBufferHandle* GetFrameDecompressHandle(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex);
SGeomCacheFrameHeader* GetFrameDecompressHeader(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex);
char* GetFrameDecompressData(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex);
int* GetDependencyCounter(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex);
void* m_pPoolBaseAddress;
IGeneralMemoryHeap* m_pPool;
size_t m_poolSize;
uint m_lastRequestStream;
uint m_numMissedFrames;
uint m_numStreamAborts;
uint m_numErrorAborts;
uint m_numDecompressStreamAborts;
uint m_numReadStreamAborts;
uint m_numFailedAllocs;
typedef std::vector<SGeomCacheStreamInfo*>::iterator TStreamInfosIter;
std::vector<SGeomCacheStreamInfo*> m_streamInfos;
std::vector<SGeomCacheStreamInfo*> m_streamInfosAbortList;
typedef std::map<string, CGeomCache*, stl::less_stricmp<string> > TGeomCacheMap;
TGeomCacheMap m_nameToGeomCacheMap;
CGeomCacheMeshManager m_meshManager;
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H
@@ -1,328 +0,0 @@
/*
* 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.
// Description : Manages static meshes for geometry caches
#include "Cry3DEngine_precompiled.h"
#if defined(USE_GEOM_CACHES)
#include "GeomCacheMeshManager.h"
#include "GeomCacheDecoder.h"
void CGeomCacheMeshManager::Reset()
{
stl::free_container(m_meshMap);
}
bool CGeomCacheMeshManager::ReadMeshStaticData(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, SGeomCacheStaticMeshData& staticMeshData) const
{
LOADING_TIME_PROFILE_SECTION;
if (staticMeshData.m_constantStreams & GeomCacheFile::eStream_Indices)
{
if (!ReadMeshIndices(reader, meshInfo, staticMeshData, staticMeshData.m_indices))
{
return false;
}
}
if ((staticMeshData.m_constantStreams & GeomCacheFile::eStream_Positions) != 0)
{
staticMeshData.m_positions.resize(staticMeshData.m_numVertices);
strided_pointer<Vec3> positions(&staticMeshData.m_positions[0], sizeof(Vec3));
if (!ReadMeshPositions(reader, meshInfo, positions))
{
return false;
}
}
if ((staticMeshData.m_constantStreams & GeomCacheFile::eStream_Texcoords) != 0)
{
staticMeshData.m_texcoords.resize(staticMeshData.m_numVertices);
strided_pointer<Vec2> texcoords(&staticMeshData.m_texcoords[0], sizeof(Vec2));
if (!ReadMeshTexcoords(reader, meshInfo, texcoords))
{
return false;
}
}
if ((staticMeshData.m_constantStreams & GeomCacheFile::eStream_QTangents) != 0)
{
staticMeshData.m_tangents.resize(staticMeshData.m_numVertices);
strided_pointer<SPipTangents> tangents(&staticMeshData.m_tangents[0], sizeof(SPipTangents));
if (!ReadMeshQTangents(reader, meshInfo, tangents))
{
return false;
}
}
if ((staticMeshData.m_constantStreams & GeomCacheFile::eStream_Colors) != 0)
{
UCol defaultColor;
defaultColor.dcolor = 0xFFFFFFFF;
staticMeshData.m_colors.resize(staticMeshData.m_numVertices, defaultColor);
strided_pointer<UCol> colors(&staticMeshData.m_colors[0], sizeof(UCol));
if (!ReadMeshColors(reader, meshInfo, colors))
{
return false;
}
}
if (staticMeshData.m_bUsePredictor)
{
uint32 predictorDataSize;
if (!reader.Read(&predictorDataSize))
{
return false;
}
staticMeshData.m_predictorData.resize(predictorDataSize);
if (!reader.Read(&staticMeshData.m_predictorData[0], predictorDataSize))
{
return false;
}
}
return true;
}
_smart_ptr<IRenderMesh> CGeomCacheMeshManager::ConstructStaticRenderMesh(CGeomCacheStreamReader& reader,
const GeomCacheFile::SMeshInfo& meshInfo, SGeomCacheStaticMeshData& staticMeshData, const char* pFileName)
{
LOADING_TIME_PROFILE_SECTION;
std::vector<char> vertexData(staticMeshData.m_numVertices * sizeof(SVF_P3F_C4B_T2F), 0);
std::vector<SPipTangents> tangentData(staticMeshData.m_numVertices);
std::vector<vtx_idx> indices;
strided_pointer<Vec3> positions((Vec3*)(&vertexData[0] + offsetof(SVF_P3F_C4B_T2F, xyz)), sizeof(SVF_P3F_C4B_T2F));
strided_pointer<UCol> colors((UCol*)(&vertexData[0] + offsetof(SVF_P3F_C4B_T2F, color)), sizeof(SVF_P3F_C4B_T2F));
strided_pointer<Vec2> texcoords((Vec2*)(&vertexData[0] + offsetof(SVF_P3F_C4B_T2F, st)), sizeof(SVF_P3F_C4B_T2F));
strided_pointer<SPipTangents> tangents((SPipTangents*)((char*)(&tangentData[0])), sizeof(SPipTangents));
if (!ReadMeshIndices(reader, meshInfo, staticMeshData, indices)
|| !ReadMeshPositions(reader, meshInfo, positions)
|| !ReadMeshTexcoords(reader, meshInfo, texcoords)
|| !ReadMeshQTangents(reader, meshInfo, tangents))
{
return NULL;
}
if (meshInfo.m_constantStreams & GeomCacheFile::eStream_Colors)
{
if (!ReadMeshColors(reader, meshInfo, colors))
{
return NULL;
}
}
else
{
UCol defaultColor;
defaultColor.dcolor = 0xFFFFFFFF;
const uint numVertices = meshInfo.m_numVertices;
for (uint i = 0; i < numVertices; ++i)
{
colors[i] = defaultColor;
}
}
TMeshMap::iterator findIter = m_meshMap.find(staticMeshData.m_hash);
if (findIter != m_meshMap.end())
{
++findIter->second.m_refCount;
return findIter->second.m_pRenderMesh;
}
_smart_ptr<IRenderMesh> pRenderMesh = gEnv->pRenderer->CreateRenderMeshInitialized(&vertexData[0], meshInfo.m_numVertices,
eVF_P3F_C4B_T2F, &indices[0], indices.size(), prtTriangleList, "GeomCacheConstantMesh", pFileName, eRMT_Static, 1, 0,
NULL, NULL, false, false, &tangentData[0]);
CRenderChunk chunk;
chunk.nNumVerts = meshInfo.m_numVertices;
uint32 currentIndexOffset = 0;
for (unsigned int i = 0; i < meshInfo.m_numMaterials; ++i)
{
chunk.nFirstIndexId = currentIndexOffset;
chunk.nNumIndices = staticMeshData.m_numIndices[i];
chunk.m_nMatID = staticMeshData.m_materialIds[i];
chunk.m_vertexFormat = eVF_P3F_C4B_T2F;
pRenderMesh->SetChunk(i, chunk);
currentIndexOffset += chunk.nNumIndices;
}
SMeshMapInfo meshMapInfo;
meshMapInfo.m_refCount = 1;
meshMapInfo.m_pRenderMesh = pRenderMesh;
m_meshMap[staticMeshData.m_hash] = meshMapInfo;
return pRenderMesh;
}
_smart_ptr<IRenderMesh> CGeomCacheMeshManager::GetStaticRenderMesh(const uint64 hash) const
{
TMeshMap::const_iterator findIter = m_meshMap.find(hash);
if (findIter != m_meshMap.end())
{
return findIter->second.m_pRenderMesh;
}
return NULL;
}
void CGeomCacheMeshManager::RemoveReference(SGeomCacheStaticMeshData& staticMeshData)
{
TMeshMap::iterator findIter = m_meshMap.find(staticMeshData.m_hash);
if (findIter != m_meshMap.end())
{
uint& refCount = findIter->second.m_refCount;
--refCount;
if (refCount == 0)
{
m_meshMap.erase(findIter);
}
}
}
bool CGeomCacheMeshManager::ReadMeshIndices(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo,
SGeomCacheStaticMeshData& staticMeshData, std::vector<vtx_idx>& indices) const
{
const uint16 numMaterials = meshInfo.m_numMaterials;
staticMeshData.m_numIndices.reserve(numMaterials);
for (unsigned int i = 0; i < numMaterials; ++i)
{
uint32 numIndices;
if (!reader.Read(&numIndices))
{
return false;
}
staticMeshData.m_numIndices.push_back(numIndices);
const uint indicesStart = indices.size();
indices.resize(indicesStart + numIndices);
if (!reader.Read(&indices[indicesStart], numIndices))
{
return false;
}
}
return true;
}
bool CGeomCacheMeshManager::ReadMeshPositions(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<Vec3> positions) const
{
const Vec3 aabbMin = Vec3(meshInfo.m_aabbMin[0], meshInfo.m_aabbMin[1], meshInfo.m_aabbMin[2]);
const Vec3 aabbMax = Vec3(meshInfo.m_aabbMax[0], meshInfo.m_aabbMax[1], meshInfo.m_aabbMax[2]);
const AABB meshAABB(aabbMin, aabbMax);
const Vec3 aabbSize = meshAABB.GetSize();
const Vec3 posConvertFactor = Vec3(1.0f / float((2 << (meshInfo.m_positionPrecision[0] - 1)) - 1),
1.0f / float((2 << (meshInfo.m_positionPrecision[1] - 1)) - 1),
1.0f / float((2 << (meshInfo.m_positionPrecision[2] - 1)) - 1));
const uint numVertices = meshInfo.m_numVertices;
for (uint i = 0; i < numVertices; ++i)
{
GeomCacheFile::Position position;
if (!reader.Read(&position))
{
return false;
}
positions[i] = GeomCacheDecoder::DecodePosition(aabbMin, aabbSize, position, posConvertFactor);
}
return true;
}
bool CGeomCacheMeshManager::ReadMeshTexcoords(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<Vec2> texcoords) const
{
const uint numVertices = meshInfo.m_numVertices;
for (uint i = 0; i < numVertices; ++i)
{
GeomCacheFile::Texcoords texcoord;
if (!reader.Read(&texcoord))
{
return false;
}
texcoords[i] = GeomCacheDecoder::DecodeTexcoord(texcoord, meshInfo.m_uvMax);
}
return true;
}
bool CGeomCacheMeshManager::ReadMeshQTangents(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<SPipTangents> tangents) const
{
const uint numVertices = meshInfo.m_numVertices;
for (uint i = 0; i < numVertices; ++i)
{
GeomCacheFile::QTangent qTangent;
if (!reader.Read(&qTangent))
{
return false;
}
Quat qDecodedTangent = GeomCacheDecoder::DecodeQTangent(qTangent);
GeomCacheDecoder::ConvertToTangentAndBitangent(qDecodedTangent, tangents[i]);
}
return true;
}
bool CGeomCacheMeshManager::ReadMeshColors(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<UCol> colors) const
{
const uint numVertices = meshInfo.m_numVertices;
for (int colorIndex = 2; colorIndex >= 0; --colorIndex)
{
for (uint i = 0; i < numVertices; ++i)
{
GeomCacheFile::Color color;
if (!reader.Read(&color))
{
return false;
}
colors[i].bcolor[colorIndex] = color;
}
}
for (uint i = 0; i < numVertices; ++i)
{
GeomCacheFile::Color color;
if (!reader.Read(&color))
{
return false;
}
colors[i].bcolor[3] = color;
}
return true;
}
#endif
@@ -1,58 +0,0 @@
/*
* 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.
// Description : Manages static meshes for geometry caches
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHEMESHMANAGER_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHEMESHMANAGER_H
#pragma once
#if defined(USE_GEOM_CACHES)
#include "IRenderMesh.h"
#include "GeomCacheFileFormat.h"
#include "GeomCache.h"
#include <StlUtils.h>
class CGeomCacheMeshManager
{
public:
void Reset();
bool ReadMeshStaticData(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, SGeomCacheStaticMeshData& staticMeshData) const;
_smart_ptr<IRenderMesh> ConstructStaticRenderMesh(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo,
SGeomCacheStaticMeshData& staticMeshData, const char* pFileName);
_smart_ptr<IRenderMesh> GetStaticRenderMesh(const uint64 hash) const;
void RemoveReference(SGeomCacheStaticMeshData& staticMeshData);
private:
bool ReadMeshIndices(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo,
SGeomCacheStaticMeshData& staticMeshData, std::vector<vtx_idx>& indices) const;
bool ReadMeshPositions(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<Vec3> positions) const;
bool ReadMeshTexcoords(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<Vec2> texcoords) const;
bool ReadMeshQTangents(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<SPipTangents> tangents) const;
bool ReadMeshColors(CGeomCacheStreamReader& reader, const GeomCacheFile::SMeshInfo& meshInfo, strided_pointer<UCol> colors) const;
struct SMeshMapInfo
{
_smart_ptr<IRenderMesh> m_pRenderMesh;
uint m_refCount;
};
// Map from mesh hash to render mesh
typedef AZStd::unordered_map<uint64, SMeshMapInfo> TMeshMap;
TMeshMap m_meshMap;
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHEMESHMANAGER_H
@@ -1,449 +0,0 @@
/*
* 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.
// Description : Predictors for index frame compression
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHEPREDICTORS_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHEPREDICTORS_H
#pragma once
#include "GeomCacheFileFormat.h"
#include "Cry3DEngineTraits.h"
namespace GeomCachePredictors
{
//////////////////////////////////////////////////////////////////////////
// Index frame prediction
//////////////////////////////////////////////////////////////////////////
template<class T, bool kbEncode>
void ParallelogramPredictor(const uint numValues, T* pIn, T* pOut, const std::vector<uint16>& predictorData)
{
T* pAbsoluteValues = kbEncode ? pIn : pOut;
uint outPosition = 0;
for (uint i = 0, predictorDataPos = 0; i < numValues; ++i)
{
const uint16 uDist = predictorData[predictorDataPos++];
T predictedValue;
if (uDist == 0xFFFF)
{
if (i == 0)
{
// There is no previous value, so we just pass through
pOut[outPosition++] = pIn[i];
continue;
}
// No neighbour triangle, just use previous value for prediction
predictedValue = pAbsoluteValues[i - 1];
}
else
{
// Parallelogram prediction
const uint16 vDist = predictorData[predictorDataPos++];
const uint16 wDist = predictorData[predictorDataPos++];
const T& u = pAbsoluteValues[i - uDist];
const T& v = pAbsoluteValues[i - vDist];
const T& w = pAbsoluteValues[i - wDist];
predictedValue = u + v - w;
}
if (kbEncode)
{
const T realValue = pIn[i];
const T delta = realValue - predictedValue;
pOut[outPosition++] = delta;
}
else
{
const T delta = pIn[i];
const T realValue = delta + predictedValue;
pOut[outPosition++] = realValue;
}
}
}
template<bool kbEncode>
void QTangentPredictor(const uint numValues, const GeomCacheFile::QTangent* pIn, GeomCacheFile::QTangent* pOut, const std::vector<uint16>& predictorData)
{
const GeomCacheFile::QTangent* pAbsoluteValues = kbEncode ? pIn : pOut;
uint outPosition = 0;
for (uint i = 0, predictorDataPos = 0; i < numValues; ++i)
{
const uint16 uDist = predictorData[predictorDataPos++];
Vec4_tpl<int32> predictedValue;
if (uDist == 0xFFFF)
{
if (i == 0)
{
// There is no previous value, so we just pass through
pOut[outPosition++] = pIn[i];
continue;
}
// No neighbour triangle, just use previous value for prediction
predictedValue = pAbsoluteValues[i - 1];
}
else
{
// Average value of two nearest vertices of adjancent triangle
const uint16 vDist = predictorData[predictorDataPos++];
++predictorDataPos;
const GeomCacheFile::QTangent& u = pAbsoluteValues[i - uDist];
const GeomCacheFile::QTangent& v = pAbsoluteValues[i - vDist];
predictedValue = Vec4_tpl<int32>(u) + Vec4_tpl<int32>(v);
// Vec4_tpl defines division in a way that only works for floats
predictedValue.x /= 2;
predictedValue.y /= 2;
predictedValue.z /= 2;
predictedValue.w /= 2;
}
if (kbEncode)
{
const GeomCacheFile::QTangent& realValue = pIn[i];
const GeomCacheFile::QTangent delta = realValue - predictedValue;
pOut[outPosition++] = delta;
}
else
{
const GeomCacheFile::QTangent delta = pIn[i];
const GeomCacheFile::QTangent realValue = delta + predictedValue;
pOut[outPosition++] = realValue;
}
}
}
template<bool kbEncode>
inline void ColorPredictor(const uint numValues, const GeomCacheFile::Color* pIn, GeomCacheFile::Color* pOut, const std::vector<uint16>& predictorData)
{
const GeomCacheFile::Color* pAbsoluteValues = kbEncode ? pIn : pOut;
uint outPosition = 0;
for (uint i = 0, predictorDataPos = 0; i < numValues; ++i)
{
const uint16 uDist = predictorData[predictorDataPos++];
int32 predictedValue;
if (uDist == 0xFFFF)
{
if (i == 0)
{
// There is no previous value, so we just pass through
pOut[outPosition++] = pIn[i];
continue;
}
// No neighbour triangle, just use previous value for prediction
predictedValue = pAbsoluteValues[i - 1];
}
else
{
// Average value of two nearest vertices of adjancent triangle
const uint16 vDist = predictorData[predictorDataPos++];
++predictorDataPos;
const GeomCacheFile::Color& u = pAbsoluteValues[i - uDist];
const GeomCacheFile::Color& v = pAbsoluteValues[i - vDist];
predictedValue = (int32(u) + int32(v)) / 2;
}
if (kbEncode)
{
const GeomCacheFile::Color& realValue = pIn[i];
const GeomCacheFile::Color delta = realValue - predictedValue;
pOut[outPosition++] = delta;
}
else
{
const GeomCacheFile::Color delta = pIn[i];
const GeomCacheFile::Color realValue = delta + predictedValue;
pOut[outPosition++] = realValue;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Temporal prediction
//////////////////////////////////////////////////////////////////////////
// Motion predictor input data
template<class T>
struct STemporalPredictorData
{
uint m_numElements;
const T* m_pPrevFrames[2];
const T* m_pFloorFrame;
const T* m_pCeilFrame;
};
template<class T>
Vec2_tpl<T> operator>>(const Vec2_tpl<T>& v, uint shift)
{
Vec2_tpl<T> result = v;
result.x >>= shift;
result.y >>= shift;
return result;
}
template<class T>
Vec3_tpl<T> operator>>(const Vec3_tpl<T>& v, uint shift)
{
Vec3_tpl<T> result = v;
result.x >>= shift;
result.y >>= shift;
result.z >>= shift;
return result;
}
template<class T>
Vec4_tpl<T> operator>>(const Vec4_tpl<T>& v, uint shift)
{
Vec4_tpl<T> result = v;
result.x >>= shift;
result.y >>= shift;
result.z >>= shift;
result.w >>= shift;
return result;
}
template<class I, class T>
void InterpolateDeltaEncode(const uint numValues, const uint8 lerpFactor, const T* pFloorFrame, const T* pCeilFrame, const T* pIn, T* pOut)
{
for (uint i = 0; i < numValues; ++i)
{
const I floorValue = I(pFloorFrame[i]);
const I ceilValue = I(pCeilFrame[i]);
const T predictedValue = T(floorValue + (((ceilValue - floorValue) * lerpFactor) >> 8));
const T& realValue = pIn[i];
const T delta = realValue - predictedValue;
pOut[i] = delta;
}
}
template<class I, class T>
void MotionDeltaEncode(const uint numValues, const uint8 acceleration, const T* const pPrevFrames[2], const T* pIn, T* pOut)
{
for (uint i = 0; i < numValues; ++i)
{
const I prevPrevFrameValue = I(pPrevFrames[0][i]);
const I prevFrameValue = I(pPrevFrames[1][i]);
const T predictedValue = T(prevFrameValue + (((prevFrameValue - prevPrevFrameValue) * acceleration) >> 7));
const T& realValue = pIn[i];
const T delta = realValue - predictedValue;
pOut[i] = delta;
}
}
template<class I, class T, bool kbEncode>
void InterpolateMotionDeltaPredictor(const GeomCacheFile::STemporalPredictorControl& controlIn, const STemporalPredictorData<T>& data, const T* pIn, T* pOut)
{
const T* pFloorFrame = data.m_pFloorFrame;
const T* const pCeilFrame = data.m_pCeilFrame;
const T* const* pPrevFrames = data.m_pPrevFrames;
const uint8& lerpFactor = controlIn.m_indexFrameLerpFactor;
const uint8& acceleration = controlIn.m_acceleration;
const uint8 combineFactor = controlIn.m_combineFactor;
const uint numElements = data.m_numElements;
for (uint i = 0; i < numElements; ++i)
{
const I prevPrevFrameValue = I(pPrevFrames[0][i]);
const I prevFrameValue = I(pPrevFrames[1][i]);
const I floorValue = I(pFloorFrame[i]);
const I ceilValue = I(pCeilFrame[i]);
const I interpolatePredictedValue = T(floorValue + (((ceilValue - floorValue) * lerpFactor) >> 8));
const I motionPredictedValue = T(prevFrameValue + (((prevFrameValue - prevPrevFrameValue) * acceleration) >> 7));
const T predictedValue = (interpolatePredictedValue + (((motionPredictedValue - interpolatePredictedValue) * combineFactor) >> 7));
if (kbEncode)
{
const T realValue = pIn[i];
const T delta = realValue - predictedValue;
pOut[i] = delta;
}
else
{
const T delta = pIn[i];
const T realValue = delta + predictedValue;
pOut[i] = realValue;
}
}
}
#if AZ_LEGACY_3DENGINE_TRAIT_DEFINE_MM_MULLO_EPI32_EMU
ILINE __m128i _mm_mullo_epi32_emu(const __m128i& a, const __m128i& b)
{
#if AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_MULLO_EPI32
return _mm_mullo_epi32(a, b);
#else
__m128i tmp1 = _mm_mul_epu32(a, b);
__m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(a, 4), _mm_srli_si128(b, 4));
return _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0)));
#endif
}
ILINE __m128i _mm_packus_epi32_emu(__m128i& a, __m128i& b)
{
#if AZ_LEGACY_3DENGINE_TRAIT_HAS_MM_PACKUS_EPI32
return _mm_packus_epi32(a, b);
#else
a = _mm_slli_epi32(a, 16);
b = _mm_slli_epi32(b, 16);
a = _mm_srai_epi32(a, 16);
b = _mm_srai_epi32(b, 16);
return _mm_packs_epi32(a, b);
#endif
}
ILINE __m128i Interpolate(__m128i a, __m128i b, __m128i c, const uint32 factor, const int shiftFactor)
{
const __m128i zero = _mm_setzero_si128();
const __m128i truncate = _mm_set_epi16(0, -1, 0, -1, 0, -1, 0, -1);
// Unpack to 2x4 32 bit integers
__m128i factors = _mm_set1_epi32(factor);
__m128i aLo = _mm_unpacklo_epi16(a, zero);
__m128i aHi = _mm_unpackhi_epi16(a, zero);
__m128i bLo = _mm_unpacklo_epi16(b, zero);
__m128i bHi = _mm_unpackhi_epi16(b, zero);
// Interpolate and pack again
__m128i lerpLo = _mm_sub_epi32(bLo, aLo);
lerpLo = _mm_mullo_epi32_emu(lerpLo, factors);
lerpLo = _mm_srli_epi32(lerpLo, shiftFactor);
lerpLo = _mm_and_si128(lerpLo, truncate);
__m128i lerpHi = _mm_sub_epi32(bHi, aHi);
lerpHi = _mm_mullo_epi32_emu(lerpHi, factors);
lerpHi = _mm_srli_epi32(lerpHi, shiftFactor);
lerpHi = _mm_and_si128(lerpHi, truncate);
__m128i lerp = _mm_packus_epi32_emu(lerpLo, lerpHi);
__m128i result = _mm_add_epi16(lerp, c);
return result;
}
template<>
void InterpolateMotionDeltaPredictor<uint32, uint16, false>
(const GeomCacheFile::STemporalPredictorControl& controlIn, const STemporalPredictorData<uint16>& data, const uint16* pIn, uint16* pOut)
{
__m128i* pRawIn = (__m128i*)pIn;
__m128i* pRawOut = (__m128i*)pOut;
__m128i* pFloorFrame = (__m128i*)data.m_pFloorFrame;
__m128i* pCeilFrame = (__m128i*)data.m_pCeilFrame;
__m128i* pPrevFrames[2] = { (__m128i*)data.m_pPrevFrames[0], (__m128i*)data.m_pPrevFrames[1] };
const uint8 lerpFactor = controlIn.m_indexFrameLerpFactor;
const uint8 acceleration = controlIn.m_acceleration;
const uint8 combineFactor = controlIn.m_combineFactor;
// vector store as much as possible, but account for cases where the output buffer
// size doesn't divide evenly by 8
const uint remainingElements = data.m_numElements % 8;
const uint numElementsPadded = data.m_numElements / 8 + (remainingElements != 0);
const uint lastElement = numElementsPadded - 1;
for (uint i = 0; i < numElementsPadded; ++i)
{
// Load 8 floor & ceil values
__m128i floorValues = _mm_load_si128(pFloorFrame + i);
__m128i ceilValues = _mm_load_si128(pCeilFrame + i);
// Load 8 prep prev & prev frame values
__m128i prevPrevFrameValues = _mm_load_si128(pPrevFrames[0] + i);
__m128i prevFrameValues = _mm_load_si128(pPrevFrames[1] + i);
// Calculate prediction
__m128i lerp = Interpolate(floorValues, ceilValues, floorValues, lerpFactor, 8);
__m128i motion = Interpolate(prevPrevFrameValues, prevFrameValues, prevFrameValues, acceleration, 7);
__m128i predictedValues = Interpolate(lerp, motion, lerp, combineFactor, 7);
__m128i delta = _mm_load_si128(pRawIn + i);
__m128i realValues = _mm_add_epi16(delta, predictedValues);
if (i == lastElement)
{
memcpy(pOut + (i * 8), &realValues, remainingElements * sizeof(uint16));
}
else
{
_mm_store_si128(pRawOut + i, realValues);
}
}
}
template<>
void InterpolateMotionDeltaPredictor<Vec2_tpl<uint32>, Vec2_tpl<uint16>, false>
(const GeomCacheFile::STemporalPredictorControl& controlIn, const STemporalPredictorData<Vec2_tpl<uint16> >& data,
const Vec2_tpl<uint16>* pIn, Vec2_tpl<uint16>* pOut)
{
STemporalPredictorData<uint16> uInt16Data;
uInt16Data.m_pFloorFrame = (uint16*)data.m_pFloorFrame;
uInt16Data.m_pCeilFrame = (uint16*)data.m_pCeilFrame;
uInt16Data.m_pPrevFrames[0] = (uint16*)data.m_pPrevFrames[0];
uInt16Data.m_pPrevFrames[1] = (uint16*)data.m_pPrevFrames[1];
uInt16Data.m_numElements = data.m_numElements * 2;
InterpolateMotionDeltaPredictor<uint32, uint16, false>(controlIn, uInt16Data, (uint16*)pIn, (uint16*)pOut);
}
template<>
void InterpolateMotionDeltaPredictor<Vec3_tpl<uint32>, Vec3_tpl<uint16>, false>
(const GeomCacheFile::STemporalPredictorControl& controlIn, const STemporalPredictorData<Vec3_tpl<uint16> >& data,
const Vec3_tpl<uint16>* pIn, Vec3_tpl<uint16>* pOut)
{
STemporalPredictorData<uint16> uInt16Data;
uInt16Data.m_pFloorFrame = (uint16*)data.m_pFloorFrame;
uInt16Data.m_pCeilFrame = (uint16*)data.m_pCeilFrame;
uInt16Data.m_pPrevFrames[0] = (uint16*)data.m_pPrevFrames[0];
uInt16Data.m_pPrevFrames[1] = (uint16*)data.m_pPrevFrames[1];
uInt16Data.m_numElements = data.m_numElements * 3;
InterpolateMotionDeltaPredictor<uint32, uint16, false>(controlIn, uInt16Data, (uint16*)pIn, (uint16*)pOut);
}
template<>
void InterpolateMotionDeltaPredictor<Vec4_tpl<uint32>, Vec4_tpl<uint16>, false>
(const GeomCacheFile::STemporalPredictorControl& controlIn, const STemporalPredictorData<Vec4_tpl<uint16> >& data,
const Vec4_tpl<uint16>* pIn, Vec4_tpl<uint16>* pOut)
{
STemporalPredictorData<uint16> uInt16Data;
uInt16Data.m_pFloorFrame = (uint16*)data.m_pFloorFrame;
uInt16Data.m_pCeilFrame = (uint16*)data.m_pCeilFrame;
uInt16Data.m_pPrevFrames[0] = (uint16*)data.m_pPrevFrames[0];
uInt16Data.m_pPrevFrames[1] = (uint16*)data.m_pPrevFrames[1];
uInt16Data.m_numElements = data.m_numElements * 4;
InterpolateMotionDeltaPredictor<uint32, uint16, false>(controlIn, uInt16Data, (uint16*)pIn, (uint16*)pOut);
}
#endif
}
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHEPREDICTORS_H
File diff suppressed because it is too large Load Diff
@@ -1,287 +0,0 @@
/*
* 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.
// Description : Draws geometry caches
#ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHERENDERNODE_H
#define CRYINCLUDE_CRY3DENGINE_GEOMCACHERENDERNODE_H
#pragma once
#if defined(USE_GEOM_CACHES)
#include "GeomCache.h"
#include "GeomCacheDecoder.h"
struct SGeomCacheRenderMeshUpdateContext
{
SGeomCacheRenderMeshUpdateContext()
: m_meshId(0)
, m_pRenderMesh(NULL)
, m_pUpdateState(NULL)
, m_pIndices(NULL) {}
// Information needed to create the render mesh each frame
uint m_meshId;
// The render mesh
_smart_ptr<IRenderMesh> m_pRenderMesh;
// Locks the render mesh from rendering until it was filled
volatile int* m_pUpdateState;
// Previous positions for motion blur
stl::aligned_vector<Vec3, 16> m_prevPositions;
// Data pointers for updating
vtx_idx* m_pIndices;
strided_pointer<Vec3> m_pPositions;
strided_pointer<UCol> m_pColors;
strided_pointer<Vec2> m_pTexcoords;
strided_pointer<SPipTangents> m_pTangents;
strided_pointer<Vec3> m_pVelocities;
};
struct SGeomCacheRenderElementData
{
CREGeomCache* m_pRenderElement;
volatile int* m_pUpdateState;
int m_threadId;
DynArray<CREGeomCache::SMeshRenderData>* m_pCurrentFillData;
};
class CGeomCacheRenderNode
: public IGeomCacheRenderNode
, public IGeomCacheListener
, public Cry3DEngineBase
{
public:
CGeomCacheRenderNode();
virtual ~CGeomCacheRenderNode();
virtual const char* GetName() const;
virtual const char* GetEntityClassName() const;
virtual EERType GetRenderNodeType() { return eERType_GeomCache; }
virtual Vec3 GetPos(bool bWorldOnly) const;
virtual void SetBBox(const AABB& WSBBox);
virtual const AABB GetBBox() const;
virtual void GetLocalBounds(AABB& bbox);
// Called before rendering to update to current frame bbox
void UpdateBBox();
virtual void Render(const struct SRendParams& entDrawParams, const SRenderingPassInfo& passInfo);
void SetMatrix(const Matrix34& matrix);
const Matrix34& GetMatrix() const { return m_matrix; }
virtual void SetMaterial(_smart_ptr<IMaterial> pMat);
virtual _smart_ptr<IMaterial> GetMaterial(Vec3* pHitPos);
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return m_pMaterial; }
void SetBaseMaxViewDistance(float maxViewDistance) override { m_maxViewDist = maxViewDistance; }
virtual float GetMaxViewDist();
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
// Streaming
float GetStreamingTime() const { return std::max(m_streamingTime, m_playbackTime); }
// Called for starting the update job in CGeomCacheManager
void StartAsyncUpdate();
// Called by fill job if it didn't call FillFrameAsync because data wasn't available
void SkipFrameFill();
// Called from the update job in CGeomCacheManager
bool FillFrameAsync(const char* const pFloorFrameData, const char* const pCeilFrameData, const float lerpFactor);
// Called from FillFrameAsync
void UpdateMesh_JobEntry(SGeomCacheRenderMeshUpdateContext *pUpdateContext, const SGeomCacheStaticMeshData *pStaticMeshData,
const char* pFloorMeshData, const char* pCeilMeshData, float lerpFactor);
// Called from CGeomCacheManager when playback stops
void ClearFillData();
// Called from CObjManager to update streaming
void UpdateStreamableComponents(float fImportance, float fDistance, bool bFullUpdate, int nLod, const float fInvScale, bool bDrawNear);
// IGeomCacheRenderNode
virtual bool LoadGeomCache(const char* sGeomCacheFileName);
void SetGeomCache(_smart_ptr<IGeomCache> geomCache) override;
virtual void SetPlaybackTime(const float time);
virtual float GetPlaybackTime() const { return m_playbackTime; }
virtual bool IsStreaming() const;
virtual void StartStreaming(const float time);
virtual void StopStreaming();
virtual bool IsLooping() const;
virtual void SetLooping(const bool bEnable);
virtual float GetPrecachedTime() const;
virtual IGeomCache* GetGeomCache() const { return m_pGeomCache; }
virtual bool DidBoundsChange();
virtual void SetDrawing(bool bDrawing) { m_bDrawing = bDrawing; }
// Set stand in CGFs and distance
virtual void SetStandIn(const char* pFilePath, const char* pMaterial);
IStatObj* GetStandIn() override { return m_pStandIn; }
virtual void SetFirstFrameStandIn(const char* pFilePath, const char* pMaterial);
IStatObj* GetFirstFrameStandIn() override { return m_pFirstFrameStandIn; }
virtual void SetLastFrameStandIn(const char* pFilePath, const char* pMaterial);
IStatObj* GetLastFrameStandIn() override { return m_pLastFrameStandIn; }
virtual void SetStandInDistance(const float distance);
float GetStandInDistance() override { return m_standInDistance; }
// Set distance at which cache will start streaming automatically (0 means no auto streaming)
virtual void SetStreamInDistance(const float distance);
float GetStreamInDistance() override { return m_streamInDistance; }
virtual void DebugDraw(const SGeometryDebugDrawInfo& info, float fExtrudeScale, uint nodeIndex) const;
virtual bool RayIntersection(SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pCustomMtl, uint* pHitNodeIndex) const;
// Get node information
virtual uint GetNodeCount() const;
virtual Matrix34 GetNodeTransform(const uint nodeIndex) const;
virtual const char* GetNodeName(const uint nodeIndex) const;
virtual uint32 GetNodeNameHash(const uint nodeIndex) const;
virtual bool IsNodeDataValid(const uint nodeIndex) const;
// Physics
virtual void InitPhysicalEntity(IPhysicalEntity* pPhysicalEntity, const pe_articgeomparams& params);
void OffsetPosition([[maybe_unused]] const Vec3& delta) {}
#ifndef _RELEASE
void DebugRender();
#endif
private:
void CalcBBox();
void FillRenderObject(const SRendParams& rendParams, const SRenderingPassInfo& passInfo, _smart_ptr<IMaterial> pMaterial, CRenderObject* pRenderObject);
bool Initialize();
bool InitializeRenderMeshes();
_smart_ptr<IRenderMesh> SetupDynamicRenderMesh(SGeomCacheRenderMeshUpdateContext& updateContext);
void Clear(bool bWaitForStreamingJobs);
void InitTransformsRec(uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData, const QuatTNS& currentTransform);
void UpdateTransformsRec(uint& currentNodeIndex, uint& currentMeshIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData,
const std::vector<SGeomCacheStaticMeshData>& staticMeshData, uint& currentNodeDataOffset, const char* const pFloorNodeData,
const char* const pCeilNodeData, const QuatTNS& currentTransform, const float lerpFactor);
// IGeomCacheListener
virtual void OnGeomCacheStaticDataLoaded();
virtual void OnGeomCacheStaticDataUnloaded();
void DebugDrawRec(const SGeometryDebugDrawInfo& info, float fExtrudeScale,
uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData) const;
bool RayIntersectionRec(SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pCustomMtl, uint* pHitNodeIndex,
uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData,
SRayHitInfo& hitOut, float& fMinDistance) const;
#ifndef _RELEASE
void InstancingDebugDrawRec(uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData);
#endif
enum EStandInType
{
eStandInType_None,
eStandInType_Default,
eStandInType_FirstFrame,
eStandInType_LastFrame
};
EStandInType SelectStandIn() const;
IStatObj* GetStandIn(const EStandInType type) const;
void PrecacheStandIn(IStatObj* pStandIn, float fImportance, float fDistance, bool bFullUpdate, int nLod, const float fInvScale, bool bDrawNear);
void UpdatePhysicalEntity(const pe_articgeomparams* pParams);
void UpdatePhysicalMaterials();
// Material ID -> render element data + update state pointer
typedef AZStd::unordered_map<uint32, SGeomCacheRenderElementData> TRenderElementMap;
TRenderElementMap m_pRenderElements;
// Saved node transforms for motion blur and attachments
std::vector<Matrix34> m_nodeMatrices;
// All render meshes
std::vector<_smart_ptr<IRenderMesh> > m_renderMeshes;
// Update contexts for render meshes
std::vector<SGeomCacheRenderMeshUpdateContext> m_renderMeshUpdateContexts;
// Override material
_smart_ptr<IMaterial> m_pMaterial;
// The rendered cache
_smart_ptr<CGeomCache> m_pGeomCache;
// World space matrix
Matrix34 m_matrix;
// Playback
volatile float m_playbackTime;
// Streaming flag
volatile float m_streamingTime;
// Misc
IPhysicalEntity* m_pPhysicalEntity;
float m_maxViewDist;
// World space bounding box
AABB m_bBox;
// AABB of current displayed frame and render buffer
AABB m_currentAABB;
AABB m_currentDisplayAABB;
// Used for editor debug rendering & ray intersection
mutable CryCriticalSection m_fillCS;
// Transform ready sync
mutable CryMutex m_bTransformsReadyCS;
mutable CryConditionVariable m_bTransformReadyCV;
// Stand in stat objects
EStandInType m_standInVisible;
_smart_ptr<IStatObj> m_pStandIn;
_smart_ptr<IStatObj> m_pFirstFrameStandIn;
_smart_ptr<IStatObj> m_pLastFrameStandIn;
float m_standInDistance;
// Distance at which render node will automatically start streaming
float m_streamInDistance;
// Flags
volatile bool m_bInitialized;
bool m_bLooping;
volatile bool m_bIsStreaming;
bool m_bFilledFrameOnce;
bool m_bBoundsChanged;
bool m_bDrawing;
bool m_bTransformReady;
};
#endif
#endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHERENDERNODE_H
-113
View File
@@ -1,113 +0,0 @@
/*
* 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 "IndexedMesh.h"
#include "MeshCompiler/MeshCompiler.h"
DEFINE_INTRUSIVE_LINKED_LIST(CIndexedMesh)
CIndexedMesh::CIndexedMesh()
{
}
CIndexedMesh::~CIndexedMesh()
{
}
void CIndexedMesh::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
CMesh::GetMemoryUsage(pSizer);
}
void CIndexedMesh::RestoreFacesFromIndices()
{
const int indexCount = GetIndexCount();
SetFaceCount(indexCount / 3);
memset(m_pFaces, 0, GetFaceCount() * sizeof(m_pFaces[0]));
int nFaceId = 0;
for (int i = 0; i < indexCount; i += 3)
{
if (m_pIndices[i] != (vtx_idx) - 1) // deleted faces have -1 here
{
for (int v = 0; v < 3; ++v)
{
assert((int)m_pIndices[i + v] < GetVertexCount());
m_pFaces[nFaceId].v[v] = m_pIndices[i + v];
}
++nFaceId;
}
}
SetFaceCount(nFaceId);
}
void CIndexedMesh::Optimize(const char* szComment)
{
mesh_compiler::CMeshCompiler meshCompiler;
if (szComment)
{
// mesh_compiler::MESH_COMPILE_OPTIMIZE is a bit expensive so we show a warning if it's used at run time
Warning("CIndexedMesh::Optimize is called at run time by %s", szComment);
}
if (!meshCompiler.Compile(*this, (mesh_compiler::MESH_COMPILE_TANGENTS | mesh_compiler::MESH_COMPILE_OPTIMIZE)))
{
Warning("CIndexedMesh::Optimize failed: %s", meshCompiler.GetLastError());
}
}
void CIndexedMesh::CalcBBox()
{
const int vertexCount = GetVertexCount();
if (vertexCount == 0 || !m_pPositions)
{
m_bbox = AABB(Vec3(0, 0, 0), Vec3(0, 0, 0));
return;
}
assert(m_pPositionsF16 == 0);
m_bbox.Reset();
const int faceCount = GetFaceCount();
if (faceCount > 0)
{
for (int i = 0; i < faceCount; ++i)
{
for (int v = 0; v < 3; ++v)
{
const int nIndex = m_pFaces[i].v[v];
assert(nIndex >= 0 && nIndex < vertexCount);
m_bbox.Add(m_pPositions[nIndex]);
}
}
}
else
{
const int indexCount = GetIndexCount();
for (int i = 0; i < indexCount; ++i)
{
m_bbox.Add(m_pPositions[m_pIndices[i]]);
}
}
}
-202
View File
@@ -1,202 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_INDEXEDMESH_H
#define CRYINCLUDE_CRY3DENGINE_INDEXEDMESH_H
#pragma once
#include "CryArray.h"
#include "CryHeaders.h"
#include "IIndexedMesh.h"
class CIndexedMesh
: public IIndexedMesh
, public CMesh
, public stl::intrusive_linked_list_node<CIndexedMesh>
, public Cry3DEngineBase
{
public:
CIndexedMesh();
virtual ~CIndexedMesh();
//////////////////////////////////////////////////////////////////////////
// IIndexedMesh
//////////////////////////////////////////////////////////////////////////
virtual void Release()
{
delete this;
}
// gives read-only access to mesh data
virtual void GetMeshDescription(SMeshDescription& meshDesc) const
{
meshDesc.m_pFaces = m_pFaces;
meshDesc.m_pVerts = m_pPositions;
meshDesc.m_pVertsF16 = m_pPositionsF16;
meshDesc.m_pNorms = m_pNorms;
meshDesc.m_pColor = m_pColor0;
meshDesc.m_pTexCoord = m_pTexCoord;
meshDesc.m_pIndices = m_pIndices;
meshDesc.m_nFaceCount = GetFaceCount();
meshDesc.m_nVertCount = GetVertexCount();
meshDesc.m_nCoorCount = GetTexCoordCount();
meshDesc.m_nIndexCount = GetIndexCount();
}
virtual CMesh* GetMesh()
{
return this;
}
virtual void SetMesh(CMesh& mesh)
{
Copy(mesh);
}
virtual void FreeStreams()
{
return CMesh::FreeStreams();
}
virtual int GetFaceCount() const
{
return CMesh::GetFaceCount();
}
virtual void SetFaceCount(int nNewCount)
{
CMesh::SetFaceCount(nNewCount);
}
virtual int GetVertexCount() const
{
return CMesh::GetVertexCount();
}
virtual void SetVertexCount(int nNewCount)
{
CMesh::SetVertexCount(nNewCount);
}
virtual void SetColorCount(int nNewCount)
{
CMesh::ReallocStream(COLORS, 0, nNewCount);
}
virtual int GetTexCoordCount() const
{
return CMesh::GetTexCoordCount();
}
virtual void SetTexCoordCount(int nNewCount, int numStreams = 1)
{
for (int i = 0; i < numStreams; ++i)
{
CMesh::ReallocStream(TEXCOORDS, i, nNewCount);
}
}
virtual int GetTangentCount() const
{
return CMesh::GetTangentCount();
}
virtual void SetTangentCount(int nNewCount)
{
CMesh::ReallocStream(TANGENTS, 0, nNewCount);
}
virtual void SetTexCoordsAndTangentsCount(int nNewCount)
{
CMesh::SetTexCoordsAndTangentsCount(nNewCount);
}
virtual int GetIndexCount() const
{
return CMesh::GetIndexCount();
}
virtual void SetIndexCount(int nNewCount)
{
CMesh::SetIndexCount(nNewCount);
}
virtual void AllocateBoneMapping()
{
ReallocStream(BONEMAPPING, 0, GetVertexCount());
}
virtual int GetSubSetCount() const
{
return m_subsets.size();
}
virtual void SetSubSetCount(int nSubsets)
{
m_subsets.resize(nSubsets);
}
virtual const SMeshSubset& GetSubSet(int nIndex) const
{
return m_subsets[nIndex];
}
virtual void SetSubsetBounds(int nIndex, const Vec3& vCenter, float fRadius)
{
m_subsets[nIndex].vCenter = vCenter;
m_subsets[nIndex].fRadius = fRadius;
}
virtual void SetSubsetIndexVertexRanges(int nIndex, int nFirstIndexId, int nNumIndices, int nFirstVertId, int nNumVerts)
{
m_subsets[nIndex].nFirstIndexId = nFirstIndexId;
m_subsets[nIndex].nNumIndices = nNumIndices;
m_subsets[nIndex].nFirstVertId = nFirstVertId;
m_subsets[nIndex].nNumVerts = nNumVerts;
}
virtual void SetSubsetMaterialId(int nIndex, int nMatID)
{
m_subsets[nIndex].nMatID = nMatID;
}
virtual void SetSubsetMaterialProperties(int nIndex, int nMatFlags, int nPhysicalizeType, const AZ::Vertex::Format& vertexFormat)
{
m_subsets[nIndex].nMatFlags = nMatFlags;
m_subsets[nIndex].nPhysicalizeType = nPhysicalizeType;
m_subsets[nIndex].vertexFormat = vertexFormat;
}
virtual AABB GetBBox() const
{
return m_bbox;
}
virtual void SetBBox(const AABB& box)
{
m_bbox = box;
}
virtual void CalcBBox();
virtual void Optimize(const char* szComment = NULL);
virtual void RestoreFacesFromIndices();
//////////////////////////////////////////////////////////////////////////
void GetMemoryUsage(class ICrySizer* pSizer) const;
};
#endif // CRYINCLUDE_CRY3DENGINE_INDEXEDMESH_H
File diff suppressed because it is too large Load Diff
-116
View File
@@ -1,116 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_LIGHTENTITY_H
#define CRYINCLUDE_CRY3DENGINE_LIGHTENTITY_H
#pragma once
const float LIGHT_PROJECTOR_MAX_FOV = 180.f;
struct CLightEntity
: public ILightSource
, public Cry3DEngineBase
{
static void StaticReset();
public:
virtual EERType GetRenderNodeType();
virtual const char* GetEntityClassName(void) const { return "LightEntityClass"; }
virtual const char* GetName(void) const;
virtual Vec3 GetPos(bool) const;
virtual void Render(const SRendParams&, const SRenderingPassInfo& passInfo);
virtual void SetMaterial(_smart_ptr<IMaterial> pMat) { m_pMaterial = pMat; }
virtual _smart_ptr<IMaterial> GetMaterial([[maybe_unused]] Vec3* pHitPos = NULL) { return m_pMaterial; }
virtual _smart_ptr<IMaterial> GetMaterialOverride() { return m_pMaterial; }
virtual float GetMaxViewDist();
virtual void SetLightProperties(const CDLight& light);
virtual CDLight& GetLightProperties() { return m_light; };
virtual void Release(bool);
virtual void SetMatrix(const Matrix34& mat);
virtual const Matrix34& GetMatrix() { return m_Matrix; }
virtual struct ShadowMapFrustum* GetShadowFrustum(int nId = 0);
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
virtual const AABB GetBBox() const { return m_WSBBox; }
virtual void SetBBox(const AABB& WSBBox) { m_WSBBox = WSBBox; }
virtual void FillBBox(AABB& aabb);
virtual void OffsetPosition(const Vec3& delta);
virtual void SetCastingException(IRenderNode* pNotCaster) { m_pNotCaster = pNotCaster; }
virtual bool IsLightAreasVisible();
virtual struct IStatObj* GetEntityStatObj([[maybe_unused]] unsigned int nPartId = 0, [[maybe_unused]] unsigned int nSubPartId = 0, [[maybe_unused]] Matrix34A* pMatrix = NULL, [[maybe_unused]] bool bReturnOnlyVisible = false) { return NULL; }
virtual int GetSlotCount() const;
virtual EVoxelGIMode GetVoxelGIMode() override;
virtual void SetDesiredVoxelGIMode(EVoxelGIMode mode) override;
virtual void SetName(const char* name);
void InitEntityShadowMapInfoStructure();
void UpdateGSMLightSourceShadowFrustum(const SRenderingPassInfo& passInfo);
int UpdateGSMLightSourceDynamicShadowFrustum(int nDynamicLodCount, int nDistanceLodCount, float& fDistanceFromViewLastDynamicLod, float& fGSMBoxSizeLastDynamicLod, bool bFadeLastCascade, const SRenderingPassInfo& passInfo);
int UpdateGSMLightSourceCachedShadowFrustum(int nFirstLod, int nLodCount, float& fDistFromViewDynamicLod, float fRadiusDynamicLod, const SRenderingPassInfo& passInfo);
bool ProcessFrustum(int nLod, float fCamBoxSize, float fDistanceFromView, PodArray<struct SPlaneObject>& lstCastersHull, const SRenderingPassInfo& passInfo);
static void ProcessPerObjectFrustum(ShadowMapFrustum* pFr, struct SPerObjectShadow* pPerObjectShadow, ILightSource* pLightSource, const SRenderingPassInfo& passInfo);
void InitShadowFrustum_SUN_Conserv(ShadowMapFrustum* pFr, int dwAllowedTypes, float fGSMBoxSize, float fDistance, int nLod, const SRenderingPassInfo& passInfo);
void InitShadowFrustum_PROJECTOR(ShadowMapFrustum* pFr, int dwAllowedTypes, const SRenderingPassInfo& passInfo);
void InitShadowFrustum_OMNI(ShadowMapFrustum* pFr, int dwAllowedTypes, const SRenderingPassInfo& passInfo);
void FillFrustumCastersList_SUN(ShadowMapFrustum* pFr, int dwAllowedTypes, int nRenderNodeFlags, PodArray<struct SPlaneObject>& lstCastersHull, int nLod, const SRenderingPassInfo& passInfo);
void FillFrustumCastersList_PROJECTOR(ShadowMapFrustum* pFr, int dwAllowedTypes, const SRenderingPassInfo& passInfo);
void FillFrustumCastersList_OMNI(ShadowMapFrustum* pFr, int dwAllowedTypes, const SRenderingPassInfo& passInfo);
void CheckValidFrustums_OMNI(ShadowMapFrustum* pFr, const SRenderingPassInfo& passInfo);
bool CheckFrustumsIntersect(CLightEntity* lightEnt);
bool GetGsmFrustumBounds(const CCamera& viewFrustum, ShadowMapFrustum* pShadowFrustum);
void DetectCastersListChanges(ShadowMapFrustum* pFr, const SRenderingPassInfo& passInfo);
void OnCasterDeleted(IShadowCaster* pCaster);
int MakeShadowCastersHullSun(PodArray<SPlaneObject>& lstCastersHull, const SRenderingPassInfo& passInfo);
static Vec3 GSM_GetNextScreenEdge(float fPrevRadius, float fPrevDistanceFromView, const SRenderingPassInfo& passInfo);
static float GSM_GetLODProjectionCenter(const Vec3& vEdgeScreen, float fRadius);
static bool FrustumIntersection(const CCamera& viewFrustum, const CCamera& shadowFrustum);
void UpdateCastShadowFlag(float fDistance, const SRenderingPassInfo& passInfo);
void CalculateShadowBias(ShadowMapFrustum* pFr, int nLod, float fGSMBoxSize) const;
CLightEntity();
~CLightEntity();
CDLight m_light;
bool m_bShadowCaster : 1;
_smart_ptr<IMaterial> m_pMaterial;
Matrix34 m_Matrix;
IRenderNode* m_pNotCaster;
// used for shadow maps
struct ShadowMapInfo
{
ShadowMapInfo() { memset(this, 0, sizeof(ShadowMapInfo)); }
void Release(struct IRenderer* pRenderer);
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
}
struct ShadowMapFrustum* pGSM[MAX_GSM_LODS_NUM];
}* m_pShadowMapInfo;
AABB m_WSBBox;
void SetLayerId(uint16 nLayerId) { m_layerId = nLayerId; }
uint16 GetLayerId() { return m_layerId; }
private:
static PodArray<SPlaneObject> s_lstTmpCastersHull;
private:
IStatObj* m_pStatObj;
uint16 m_layerId;
EVoxelGIMode m_VoxelGIMode;
AZStd::string m_Name;
};
#endif
File diff suppressed because it is too large Load Diff
-256
View File
@@ -1,256 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_MATMAN_H
#define CRYINCLUDE_CRY3DENGINE_MATMAN_H
#pragma once
#include "Cry3DEngineBase.h"
#include "SurfaceTypeManager.h"
#include <CryThreadSafeRendererContainer.h>
#include "MaterialHelpers.h"
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/parallel/condition_variable.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
// forward declarations.
struct IMaterial;
struct ISurfaceType;
struct ISurfaceTypeManager;
class CMatInfo;
class ManualResetEvent
{
public:
ManualResetEvent()
: m_flag(false),
m_mutex(),
m_conditionVariable()
{
}
void Wait()
{
AZStd::unique_lock<AZStd::mutex> lock(m_mutex);
m_conditionVariable.wait(lock, [this]
{
return m_flag == true;
});
}
void Set()
{
{
AZStd::unique_lock<AZStd::mutex> lock(m_mutex);
m_flag = true;
}
m_conditionVariable.notify_all();
}
void Unset()
{
AZStd::unique_lock<AZStd::mutex> lock(m_mutex);
m_flag = false;
}
bool IsSet() const
{
AZStd::unique_lock<AZStd::mutex> lock(m_mutex);
return m_flag;
}
private:
bool m_flag;
mutable AZStd::mutex m_mutex;
AZStd::condition_variable m_conditionVariable;
};
class UniqueManualEvent
{
public:
UniqueManualEvent(ManualResetEvent* manualResetEvent, bool hasControl)
: m_manualResetEvent(manualResetEvent),
m_hasControl(hasControl)
{
}
//! Indicates if the current thread has control of the event and is blocking other threads from proceeding
bool HasControl() const
{
return m_hasControl;
}
void Set()
{
if (m_hasControl)
{
m_manualResetEvent->Set();
m_hasControl = false;
}
}
~UniqueManualEvent()
{
Set();
}
private:
bool m_hasControl;
ManualResetEvent* m_manualResetEvent;
};
//////////////////////////////////////////////////////////////////////////
//
// CMatMan is a material manager class.
//
//////////////////////////////////////////////////////////////////////////
class CMatMan
: public IMaterialManager
, public Cry3DEngineBase
, public AzFramework::LegacyAssetEventBus::Handler
{
public:
CMatMan();
virtual ~CMatMan();
void ShutDown();
// interface IMaterialManager --------------------------------------------------------
virtual _smart_ptr<IMaterial> CreateMaterial(const char* sMtlName, int nMtlFlags = 0);
virtual _smart_ptr<IMaterial> FindMaterial(const char* sMtlName) const;
virtual _smart_ptr<IMaterial> LoadMaterial(const char* sMtlName, bool bMakeIfNotFound = true, bool bNonremovable = false, unsigned long nLoadingFlags = 0);
virtual _smart_ptr<IMaterial> LoadMaterialFromXml(const char* sMtlName, XmlNodeRef mtlNode);
virtual void ReloadMaterial(_smart_ptr<IMaterial> pMtl);
virtual void SetListener(IMaterialManagerListener* pListener) { m_pListener = pListener; };
virtual _smart_ptr<IMaterial> GetDefaultMaterial();
virtual _smart_ptr<IMaterial> GetDefaultTerrainLayerMaterial()
{
if (!m_bInitialized)
{
InitDefaults();
}
return m_pDefaultTerrainLayersMtl;
}
virtual _smart_ptr<IMaterial> GetDefaultLayersMaterial();
virtual _smart_ptr<IMaterial> GetDefaultHelperMaterial();
virtual ISurfaceType* GetSurfaceTypeByName(const char* sSurfaceTypeName, const char* sWhy = NULL);
virtual int GetSurfaceTypeIdByName(const char* sSurfaceTypeName, const char* sWhy = NULL);
virtual ISurfaceType* GetSurfaceType(int nSurfaceTypeId, const char* sWhy = NULL)
{
return m_pSurfaceTypeManager->GetSurfaceTypeFast(nSurfaceTypeId, sWhy);
}
virtual ISurfaceTypeManager* GetSurfaceTypeManager() { return m_pSurfaceTypeManager; }
_smart_ptr<IMaterial> LoadCGFMaterial(CMaterialCGF* pMaterialCGF, const char* sCgfFilename, unsigned long nLoadingFlags = 0) override;
virtual _smart_ptr<IMaterial> CloneMaterial(_smart_ptr<IMaterial> pMtl, int nSubMtl = -1);
virtual _smart_ptr<IMaterial> CloneMultiMaterial(_smart_ptr<IMaterial> pMtl, const char* sSubMtlName = 0);
virtual void GetLoadedMaterials(AZStd::vector<_smart_ptr<IMaterial>>* pData, uint32& nObjCount) const;
virtual bool SaveMaterial(XmlNodeRef mtlNode, _smart_ptr<IMaterial> pMtl);
virtual void CopyMaterial(_smart_ptr<IMaterial> pMtlSrc, _smart_ptr<IMaterial> pMtlDest, EMaterialCopyFlags flags);
virtual void RenameMaterial(_smart_ptr<IMaterial> pMtl, const char* sNewName);
virtual void RefreshMaterialRuntime();
// ------------------------------------------------------------------------------------
void InitDefaults();
void PreloadLevelMaterials();
void DoLoadSurfaceTypesInInit(bool doLoadSurfaceTypesInInit);
void UpdateShaderItems();
void RefreshShaderResourceConstants();
// Load all known game decal materials.
void PreloadDecalMaterials();
void SetSketchMode(int mode);
int GetSketchMode() { return e_sketch_mode; }
void SetTexelDensityDebug(int mode);
int GetTexelDensityDebug() { return e_texeldensity; }
//////////////////////////////////////////////////////////////////////////
ISurfaceType* GetSurfaceTypeFast(int nSurfaceTypeId, const char* sWhy = NULL) { return m_pSurfaceTypeManager->GetSurfaceTypeFast(nSurfaceTypeId, sWhy); }
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
private: // -----------------------------------------------------------------------------
friend class CMatInfo;
bool Unregister(_smart_ptr<IMaterial> pMat, bool deleteEditorMaterial = true);
_smart_ptr<IMaterial> CreateMaterialPlaceholder(const char* materialName, int nMtlFlags, const char* textureName, _smart_ptr<IMaterial> existingMtl = nullptr);
bool LoadMaterialShader(_smart_ptr<IMaterial> pMtl, _smart_ptr<IMaterial> pParentMtl, const char* sShader, uint64 nShaderGenMask, SInputShaderResources& sr, XmlNodeRef& publicsNode);
bool LoadMaterialLayerSlot(uint32 nSlot, _smart_ptr<IMaterial> pMtl, const char* szShaderName, SInputShaderResources& pBaseResources, XmlNodeRef& pPublicsNode, uint8 nLayerFlags);
void ParsePublicParams(SInputShaderResources& sr, XmlNodeRef paramsNode);
AZStd::string UnifyName(const char* sMtlName) const;
// Can be called after material creation and initialization, to inform editor that new material in engine exist.
// Only used internally.
void NotifyCreateMaterial(_smart_ptr<IMaterial> pMtl);
// Make a valid material from the XML node.
_smart_ptr<IMaterial> MakeMaterialFromXml(const AZStd::string& sMtlName, XmlNodeRef node, bool bForcePureChild, uint16 sortPrio = 0, _smart_ptr<IMaterial> pExistingMtl = 0, unsigned long nLoadingFlags = 0, _smart_ptr<IMaterial> pParentMtl = 0);
template<typename T>
UniqueManualEvent CheckMaterialCache(const AZStd::string& name, T& cachedMaterial);
_smart_ptr<IMaterial> LoadMaterialInternal(const char* sMtlName, bool bMakeIfNotFound, bool bNonremovable, unsigned long nLoadingFlags);
// override from LegacyAssetEventBus::Handler
// Notifies listeners that a file changed
void OnFileChanged(AZStd::string assetPath) override;
void OnFileRemoved(AZStd::string assetPath) override;
private:
typedef AZStd::unordered_map<AZStd::string, _smart_ptr<IMaterial> > MtlNameMap;
MtlNameMap m_mtlNameMap; //
IMaterialManagerListener* m_pListener; //
_smart_ptr<IMaterial> m_pDefaultMtl; //
_smart_ptr<IMaterial> m_pDefaultLayersMtl; //
_smart_ptr<IMaterial> m_pDefaultTerrainLayersMtl; //
_smart_ptr<IMaterial> m_pNoDrawMtl; //
_smart_ptr<IMaterial> m_pDefaultHelperMtl;
std::vector<_smart_ptr<CMatInfo> > m_nonRemovables; //
CSurfaceTypeManager* m_pSurfaceTypeManager; //
//////////////////////////////////////////////////////////////////////////
// Cached XML parser.
_smart_ptr<IXmlParser> m_pXmlParser;
bool m_bInitialized;
bool m_bLoadSurfaceTypesInInit;
mutable AZStd::mutex m_nonRemovablesMutex;
mutable AZStd::recursive_mutex m_materialMapMutex;
AZStd::unordered_map<AZStd::string, AZStd::unique_ptr<ManualResetEvent>> m_pendingMaterialLoads;
public:
// Global namespace "instance", not a class "instance", no member-variables, only const functions;
// Used to encapsulate the material-definition/io into Cry3DEngine (and make it plugable that way).
static MaterialHelpers s_materialHelpers;
static int e_sketch_mode;
static int e_lowspec_mode;
static int e_pre_sketch_spec;
static int e_texeldensity;
};
#endif // CRYINCLUDE_CRY3DENGINE_MATMAN_H
File diff suppressed because it is too large Load Diff
-342
View File
@@ -1,342 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_MATERIAL_H
#define CRYINCLUDE_CRY3DENGINE_MATERIAL_H
#pragma once
#include <IMaterial.h>
#if !defined(CONSOLE)
# define SUPPORT_MATERIAL_EDITING
#endif
#ifndef _RELEASE
#define SUPPORT_MATERIAL_SKETCH
#endif
class CMaterialLayer
: public IMaterialLayer
{
public:
CMaterialLayer()
: m_nRefCount(0)
, m_nFlags(0)
{
}
virtual ~CMaterialLayer()
{
SAFE_RELEASE(m_pShaderItem.m_pShader);
SAFE_RELEASE(m_pShaderItem.m_pShaderResources);
}
virtual void AddRef()
{
m_nRefCount++;
};
virtual void Release()
{
if (--m_nRefCount <= 0)
{
delete this;
}
}
virtual void Enable(bool bEnable = true)
{
m_nFlags |= (bEnable == false) ? MTL_LAYER_USAGE_NODRAW : 0;
}
virtual bool IsEnabled() const
{
return (m_nFlags & MTL_LAYER_USAGE_NODRAW) ? false : true;
}
virtual void FadeOut(bool bFadeOut = true)
{
m_nFlags |= (bFadeOut == false) ? MTL_LAYER_USAGE_FADEOUT : 0;
}
virtual bool DoesFadeOut() const
{
return (m_nFlags & MTL_LAYER_USAGE_FADEOUT) ? true : false;
}
virtual void SetShaderItem(const _smart_ptr<IMaterial> pParentMtl, const SShaderItem& pShaderItem);
virtual const SShaderItem& GetShaderItem() const
{
return m_pShaderItem;
}
virtual SShaderItem& GetShaderItem()
{
return m_pShaderItem;
}
virtual void SetFlags(uint8 nFlags)
{
m_nFlags = nFlags;
}
virtual uint8 GetFlags() const
{
return m_nFlags;
}
void GetMemoryUsage(ICrySizer* pSizer);
size_t GetResourceMemoryUsage(ICrySizer* pSizer);
private:
uint8 m_nFlags;
int m_nRefCount;
SShaderItem m_pShaderItem;
};
//////////////////////////////////////////////////////////////////////
class CMatInfo
: public IMaterial
, public Cry3DEngineBase
{
public:
CMatInfo();
~CMatInfo();
void ShutDown();
virtual void AddRef();
virtual void Release();
virtual int GetNumRefs() { return m_nRefCount; };
//////////////////////////////////////////////////////////////////////////
// IMaterial implementation
//////////////////////////////////////////////////////////////////////////
int Size();
virtual IMaterialHelpers& GetMaterialHelpers();
virtual IMaterialManager* GetMaterialManager();
virtual void SetName(const char* pName);
virtual const char* GetName() const { return m_sMaterialName; };
virtual void SetFlags(int flags) { m_Flags = flags; };
virtual int GetFlags() const { return m_Flags; };
virtual void UpdateFlags();
bool IsMaterialGroup() const override;
bool IsSubMaterial() const override;
// Returns true if this is the default material.
virtual bool IsDefault();
virtual int GetSurfaceTypeId() { return m_nSurfaceTypeId; };
virtual void SetSurfaceType(const char* sSurfaceTypeName);
virtual ISurfaceType* GetSurfaceType();
void SetShaderName(const char* pName) override
{
m_shaderName = pName;
};
const char* GetShaderName() const override
{
return m_shaderName.c_str();
};
// shader item
virtual void ReleaseCurrentShaderItem();
virtual void SetShaderItem(const SShaderItem& _ShaderItem);
// [Alexey] EF_LoadShaderItem return value with RefCount = 1, so if you'll use SetShaderItem after EF_LoadShaderItem use Assign function
virtual void AssignShaderItem(const SShaderItem& _ShaderItem);
virtual SShaderItem& GetShaderItem();
virtual const SShaderItem& GetShaderItem() const;
/**
* Retrieves the shader item of the sub-material of the given index
*
* If the material has no sub-materials or is not flagged as having any sub-materials
* this will return its own shader item. If no shader item is found for the given
* sub-material the default material's shader item will be returned instead.
*
* @param nSubMtlSlot The index to the requested sub-material
*
* @return A reference to the sub-material's shader item
*/
virtual SShaderItem& GetShaderItem(int nSubMtlSlot);
/**
* Retrieves the shader item of the sub-material of the given index
*
* If the material has no sub-materials or is not flagged as having any sub-materials
* this will return its own shader item. If no shader item is found for the given
* sub-material the default material's shader item will be returned instead.
*
* @param nSubMtlSlot The index to the requested sub-material
*
* @return A reference to the sub-material's shader item
*/
virtual const SShaderItem& GetShaderItem(int nSubMtlSlot) const;
virtual bool IsStreamedIn(const int nMinPrecacheRoundIds[MAX_STREAM_PREDICTION_ZONES], IRenderMesh* pRenderMesh) const;
bool AreChunkTexturesStreamedIn(CRenderChunk* pChunk, const int nMinPrecacheRoundIds[MAX_STREAM_PREDICTION_ZONES]) const;
bool AreTexturesStreamedIn(const int nMinPrecacheRoundIds[MAX_STREAM_PREDICTION_ZONES]) const;
//////////////////////////////////////////////////////////////////////////
// Functions to set param into the material/material group
// If this is a material group, materialIndex specifies the index of the sub-material to be modified
//////////////////////////////////////////////////////////////////////////
bool SetGetMaterialParamFloat(const char* sParamName, float& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
bool SetGetMaterialParamVec3(const char* sParamName, Vec3& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
bool SetGetMaterialParamVec4(const char* sParamName, Vec4& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
void SetDirty(bool dirty = true) override;
bool IsDirty() const override;
//////////////////////////////////////////////////////////////////////////
// Sub materials.
//////////////////////////////////////////////////////////////////////////
virtual void SetSubMtlCount(int numSubMtl);
virtual int GetSubMtlCount(){return m_subMtls.size(); }
virtual _smart_ptr<IMaterial> GetSubMtl(int nSlot)
{
if (m_subMtls.empty() || !(m_Flags & MTL_FLAG_MULTI_SUBMTL))
{
return 0; // Not Multi material.
}
if (nSlot >= 0 && nSlot < (int)m_subMtls.size())
{
return m_subMtls[nSlot];
}
else
{
return 0;
}
}
virtual void SetSubMtl(int nSlot, _smart_ptr<IMaterial> pMtl);
virtual void SetUserData(void* pUserData);
virtual void* GetUserData() const;
virtual _smart_ptr<IMaterial> GetSafeSubMtl(int nSlot);
virtual _smart_ptr<CMatInfo> Clone();
virtual void Copy(_smart_ptr<IMaterial> pMtlDest, EMaterialCopyFlags flags);
//////////////////////////////////////////////////////////////////////////
// Layers
//////////////////////////////////////////////////////////////////////////
virtual void SetLayerCount(uint32 nCount);
virtual uint32 GetLayerCount() const;
virtual void SetLayer(uint32 nSlot, IMaterialLayer* pLayer);
virtual const IMaterialLayer* GetLayer(uint8 nLayersMask, uint8 nLayersUsageMask) const;
virtual const IMaterialLayer* GetLayer(uint32 nSlot) const;
virtual IMaterialLayer* CreateLayer();
// Fill int table with surface ids of sub materials.
// Return number of filled items.
int FillSurfaceTypeIds(int pSurfaceIdsTable[]);
virtual void GetMemoryUsage(ICrySizer* pSizer) const;
virtual size_t GetResourceMemoryUsage(ICrySizer* pSizer);
void UpdateShaderItems() override;
void RefreshShaderResourceConstants();
//////////////////////////////////////////////////////////////////////////
void SetSketchMode(int mode);
void SetTexelDensityDebug(int mode);
// Check for specific rendering conditions (forward rendering/nearest cubemap requirement)
bool IsForwardRenderingRequired();
bool IsNearestCubemapRequired();
void DisableTextureStreaming() override;
virtual void RequestTexturesLoading(const float fMipFactor);
virtual void PrecacheMaterial(const float fEntDistance, struct IRenderMesh* pRenderMesh, bool bFullUpdate, bool bDrawNear = false);
void PrecacheTextures(const float fMipFactor, const int nFlags, bool bFullUpdate);
void PrecacheChunkTextures(const float fInstanceDistance, const int nFlags, CRenderChunk* pRenderChunk, bool bFullUpdate);
virtual int GetTextureMemoryUsage(ICrySizer* pSizer, int nSubMtlSlot = -1);
virtual void SetKeepLowResSysCopyForDiffTex();
virtual void SetMaterialLinkName(const char* name);
virtual const char* GetMaterialLinkName() const;
uint32 GetDccMaterialHash() const override { return m_dccMaterialHash; }
void SetDccMaterialHash(uint32 hash) override { m_dccMaterialHash = hash; }
virtual CryCriticalSection& GetSubMaterialResizeLock();
private:
friend class CMatMan;
friend class CMaterialLayer;
//////////////////////////////////////////////////////////////////////////
string m_sMaterialName;
string m_sUniqueMaterialName;
// Id of surface type assigned to this material.
int m_nSurfaceTypeId;
//! Number of references to this material.
int m_nRefCount;
//! Material flags.
//! @see EMatInfoFlags
int m_Flags;
uint32 m_dccMaterialHash;
SShaderItem m_shaderItem;
//! shader full name
AZStd::string m_shaderName;
#ifdef SUPPORT_MATERIAL_SKETCH
_smart_ptr<IShader> m_pPreSketchShader;
int m_nPreSketchTechnique;
#endif
//! Array of Sub materials.
typedef DynArray<_smart_ptr<CMatInfo> > SubMtls;
SubMtls m_subMtls;
#ifdef SUPPORT_MATERIAL_EDITING
// User data used by Editor.
void* m_pUserData;
string m_sMaterialLinkName;
#endif
//! Material layers
typedef std::vector< _smart_ptr< CMaterialLayer > > MatLayers;
MatLayers* m_pMaterialLayers;
//! Used for material layers
mutable CMaterialLayer* m_pActiveLayer;
struct SStreamingPredictionZone
{
int nRoundId : 31;
int bHighPriority : 1;
float fMinMipFactor;
} m_streamZoneInfo[2];
bool m_isDirty;
};
#endif // CRYINCLUDE_CRY3DENGINE_MATERIAL_H
@@ -1,861 +0,0 @@
/*
* 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 "IShader.h"
#include "MaterialHelpers.h"
/* -----------------------------------------------------------------------
* These functions are used in Cry3DEngine, CrySystem, CryRenderD3D11,
* Editor, ResourceCompilerMaterial and more
*/
//////////////////////////////////////////////////////////////////////////
namespace
{
static struct
{
EEfResTextures slot;
const char* ename;
bool adjustable;
const char* name;
const char* description;
const char* suffix;
}
s_TexSlotSemantics[] =
{
// NOTE: must be in order with filled holes to allow direct lookup
{ EFTT_DIFFUSE, "EFTT_DIFFUSE", true, "Diffuse" , "Base surface color. Alpha mask is contained in alpha channel." , "_diff" },
{ EFTT_NORMALS, "EFTT_NORMALS", true, "Bumpmap" , "Normal direction for each pixel simulating bumps on the surface. Smoothness map contained in alpha channel." , "_ddn" }, // Ideally "Normal" but need to keep backwards-compatibility
{ EFTT_SPECULAR, "EFTT_SPECULAR", true, "Specular" , "Reflective and shininess intensity and color of reflective highlights" , "_spec" },
{ EFTT_ENV, "EFTT_ENV", true, "Environment" , "Deprecated" , "_cm" },
{ EFTT_DETAIL_OVERLAY, "EFTT_DETAIL_OVERLAY", true, "Detail" , "Increases micro and macro surface bump, diffuse and gloss detail. To use, enable the 'Detail Mapping' shader gen param. " , "_detail" },
{ EFTT_SECOND_SMOOTHNESS, "EFTT_SECOND_SMOOTHNESS", false, "SecondSmoothness" , "" , "" },
{ EFTT_HEIGHT, "EFTT_HEIGHT", true, "Heightmap" , "Height for offset bump, POM, silhouette POM, and displacement mapping defined by a Grayscale texture" , "_displ" },
{ EFTT_DECAL_OVERLAY, "EFTT_DECAL_OVERLAY", true, "Decal" , "" , "" }, // called "DecalOverlay" in the shaders
{ EFTT_SUBSURFACE, "EFTT_SUBSURFACE", true, "SubSurface" , "" , "_sss" }, // called "Subsurface" in the shaders
{ EFTT_CUSTOM, "EFTT_CUSTOM", true, "Custom" , "" , "" }, // called "CustomMap" in the shaders
{ EFTT_CUSTOM_SECONDARY, "EFTT_CUSTOM_SECONDARY", true, "[1] Custom" , "" , "" },
{ EFTT_OPACITY, "EFTT_OPACITY", true, "Opacity" , "SubSurfaceScattering map to simulate thin areas for light to penetrate" , "" },
{ EFTT_SMOOTHNESS, "EFTT_SMOOTHNESS", false, "Smoothness" , "" , "_ddna" },
{ EFTT_EMITTANCE, "EFTT_EMITTANCE", true, "Emittance" , "Multiplies the emissive color with RGB texture. Emissive alpha mask is contained in alpha channel." , "_em" },
{ EFTT_OCCLUSION, "EFTT_OCCLUSION", true, "Occlusion" , "Grayscale texture to mask diffuse lighting response and simulate darker areas" , "" },
{ EFTT_SPECULAR_2, "EFTT_SPECULAR_2", true, "Specular2" , "" , "_spec" },
// Backwards compatible names are found here and mapped to the updated enum
{ EFTT_NORMALS, "EFTT_BUMP", false, "Normal" , "" , "" }, // called "Bump" in the shaders
{ EFTT_SMOOTHNESS, "EFTT_GLOSS_NORMAL_A", false, "GlossNormalA" , "" , "" },
{ EFTT_HEIGHT, "EFTT_BUMPHEIGHT", false, "Height" , "" , "" }, // called "BumpHeight" in the shaders
// This is the terminator for the name-search
{ EFTT_UNKNOWN, "EFTT_UNKNOWN", false, NULL , "" },
};
#if 0
static class Verify
{
public:
Verify()
{
for (int i = 0; s_TexSlotSemantics[i].name; i++)
{
if (s_TexSlotSemantics[i].slot != i)
{
throw std::runtime_error("Invalid texture slot lookup array.");
}
}
}
}
s_VerifyTexSlotSemantics;
#endif
}
// This should be done per shader (hence, semantics lookup map should be constructed per shader type)
EEfResTextures MaterialHelpers::FindTexSlot(const char* texName) const
{
for (int i = 0; s_TexSlotSemantics[i].name; i++)
{
if (azstricmp(s_TexSlotSemantics[i].name, texName) == 0)
{
return s_TexSlotSemantics[i].slot;
}
}
return EFTT_UNKNOWN;
}
const char* MaterialHelpers::FindTexName(EEfResTextures texSlot) const
{
for (int i = 0; s_TexSlotSemantics[i].name; i++)
{
if (s_TexSlotSemantics[i].slot == texSlot)
{
return s_TexSlotSemantics[i].name;
}
}
return NULL;
}
const char* MaterialHelpers::LookupTexName(EEfResTextures texSlot) const
{
assert((texSlot >= 0) && (texSlot < EFTT_MAX));
return s_TexSlotSemantics[texSlot].name;
}
const char* MaterialHelpers::LookupTexDesc(EEfResTextures texSlot) const
{
assert((texSlot >= 0) && (texSlot < EFTT_MAX));
return s_TexSlotSemantics[texSlot].description;
}
const char* MaterialHelpers::LookupTexEnum(EEfResTextures texSlot) const
{
assert((texSlot >= 0) && (texSlot < EFTT_MAX));
return s_TexSlotSemantics[texSlot].ename;
}
const char* MaterialHelpers::LookupTexSuffix(EEfResTextures texSlot) const
{
assert((texSlot >= 0) && (texSlot < EFTT_MAX));
return s_TexSlotSemantics[texSlot].suffix;
}
bool MaterialHelpers::IsAdjustableTexSlot(EEfResTextures texSlot) const
{
assert((texSlot >= 0) && (texSlot < EFTT_MAX));
return s_TexSlotSemantics[texSlot].adjustable;
}
//////////////////////////////////////////////////////////////////////////
// [Shader System TO DO] - automate these lookups to be data driven!
bool MaterialHelpers::SetGetMaterialParamFloat(IRenderShaderResources& pShaderResources, const char* sParamName, float& v, bool bGet) const
{
EEfResTextures texSlot = EFTT_UNKNOWN;
if (!azstricmp("emissive_intensity", sParamName))
{
texSlot = EFTT_EMITTANCE;
}
else if (!azstricmp("shininess", sParamName))
{
texSlot = EFTT_SMOOTHNESS;
}
else if (!azstricmp("opacity", sParamName))
{
texSlot = EFTT_OPACITY;
}
if (!azstricmp("alpha", sParamName))
{
if (bGet)
{
v = pShaderResources.GetAlphaRef();
}
else
{
pShaderResources.SetAlphaRef(v);
}
return true;
}
else if (texSlot != EFTT_UNKNOWN)
{
if (bGet)
{
v = pShaderResources.GetStrengthValue(texSlot);
}
else
{
pShaderResources.SetStrengthValue(texSlot, v);
}
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool MaterialHelpers::SetGetMaterialParamVec3(IRenderShaderResources& pShaderResources, const char* sParamName, Vec3& v, bool bGet) const
{
EEfResTextures texSlot = EFTT_UNKNOWN;
if (!azstricmp("diffuse", sParamName))
{
texSlot = EFTT_DIFFUSE;
}
else if (!azstricmp("specular", sParamName))
{
texSlot = EFTT_SPECULAR;
}
else if (!azstricmp("emissive_color", sParamName))
{
texSlot = EFTT_EMITTANCE;
}
if (texSlot != EFTT_UNKNOWN)
{
if (bGet)
{
v = pShaderResources.GetColorValue(texSlot).toVec3();
}
else
{
pShaderResources.SetColorValue(texSlot, ColorF(v, 1.0f));
}
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetTexModFromXml(SEfTexModificator& pTextureModifier, const XmlNodeRef& modNode) const
{
// Modificators
float f;
uint8 c;
modNode->getAttr("TexMod_RotateType", pTextureModifier.m_eRotType);
modNode->getAttr("TexMod_TexGenType", pTextureModifier.m_eTGType);
modNode->getAttr("TexMod_bTexGenProjected", pTextureModifier.m_bTexGenProjected);
for (int baseu = 'U', u = baseu; u <= 'W'; u++)
{
char RT[] = "Rotate?";
RT[6] = u;
if (modNode->getAttr(RT, f))
{
pTextureModifier.m_Rot [u - baseu] = Degr2Word(f);
}
char RR[] = "TexMod_?RotateRate";
RR[7] = u;
char RP[] = "TexMod_?RotatePhase";
RP[7] = u;
char RA[] = "TexMod_?RotateAmplitude";
RA[7] = u;
char RC[] = "TexMod_?RotateCenter";
RC[7] = u;
if (modNode->getAttr(RR, f))
{
pTextureModifier.m_RotOscRate [u - baseu] = Degr2Word(f);
}
if (modNode->getAttr(RP, f))
{
pTextureModifier.m_RotOscPhase [u - baseu] = Degr2Word(f);
}
if (modNode->getAttr(RA, f))
{
pTextureModifier.m_RotOscAmplitude[u - baseu] = Degr2Word(f);
}
if (modNode->getAttr(RC, f))
{
pTextureModifier.m_RotOscCenter [u - baseu] = f;
}
if (u > 'V')
{
continue;
}
char TL[] = "Tile?";
TL[4] = u;
char OF[] = "Offset?";
OF[6] = u;
if (modNode->getAttr(TL, f))
{
pTextureModifier.m_Tiling [u - baseu] = f;
}
if (modNode->getAttr(OF, f))
{
pTextureModifier.m_Offs [u - baseu] = f;
}
char OT[] = "TexMod_?OscillatorType";
OT[7] = u;
char OR[] = "TexMod_?OscillatorRate";
OR[7] = u;
char OP[] = "TexMod_?OscillatorPhase";
OP[7] = u;
char OA[] = "TexMod_?OscillatorAmplitude";
OA[7] = u;
if (modNode->getAttr(OT, c))
{
pTextureModifier.m_eMoveType [u - baseu] = c;
}
if (modNode->getAttr(OR, f))
{
pTextureModifier.m_OscRate [u - baseu] = f;
}
if (modNode->getAttr(OP, f))
{
pTextureModifier.m_OscPhase [u - baseu] = f;
}
if (modNode->getAttr(OA, f))
{
pTextureModifier.m_OscAmplitude [u - baseu] = f;
}
}
}
//////////////////////////////////////////////////////////////////////////
static SEfTexModificator defaultTexMod;
static bool defaultTexMod_Initialized = false;
void MaterialHelpers::SetXmlFromTexMod(const SEfTexModificator& pTextureModifier, XmlNodeRef& node) const
{
if (!defaultTexMod_Initialized)
{
ZeroStruct(defaultTexMod);
defaultTexMod.m_Tiling[0] = 1;
defaultTexMod.m_Tiling[1] = 1;
defaultTexMod_Initialized = true;
}
if (memcmp(&pTextureModifier, &defaultTexMod, sizeof(pTextureModifier)) == 0)
{
return;
}
XmlNodeRef modNode = node->newChild("TexMod");
if (modNode)
{
// Modificators
float f;
uint16 s;
uint8 c;
modNode->setAttr("TexMod_RotateType", pTextureModifier.m_eRotType);
modNode->setAttr("TexMod_TexGenType", pTextureModifier.m_eTGType);
modNode->setAttr("TexMod_bTexGenProjected", pTextureModifier.m_bTexGenProjected);
for (int baseu = 'U', u = baseu; u <= 'W'; u++)
{
char RT[] = "Rotate?";
RT[6] = u;
if ((s = pTextureModifier.m_Rot [u - baseu]) != defaultTexMod.m_Rot [u - baseu])
{
modNode->setAttr(RT, Word2Degr(s));
}
char RR[] = "TexMod_?RotateRate";
RR[7] = u;
char RP[] = "TexMod_?RotatePhase";
RP[7] = u;
char RA[] = "TexMod_?RotateAmplitude";
RA[7] = u;
char RC[] = "TexMod_?RotateCenter";
RC[7] = u;
if ((s = pTextureModifier.m_RotOscRate [u - baseu]) != defaultTexMod.m_RotOscRate [u - baseu])
{
modNode->setAttr(RR, Word2Degr(s));
}
if ((s = pTextureModifier.m_RotOscPhase [u - baseu]) != defaultTexMod.m_RotOscPhase [u - baseu])
{
modNode->setAttr(RP, Word2Degr(s));
}
if ((s = pTextureModifier.m_RotOscAmplitude[u - baseu]) != defaultTexMod.m_RotOscAmplitude[u - baseu])
{
modNode->setAttr(RA, Word2Degr(s));
}
if ((f = pTextureModifier.m_RotOscCenter [u - baseu]) != defaultTexMod.m_RotOscCenter [u - baseu])
{
modNode->setAttr(RC, f);
}
if (u > 'V')
{
continue;
}
char TL[] = "Tile?";
TL[4] = u;
char OF[] = "Offset?";
OF[6] = u;
if ((f = pTextureModifier.m_Tiling [u - baseu]) != defaultTexMod.m_Tiling [u - baseu])
{
modNode->setAttr(TL, f);
}
if ((f = pTextureModifier.m_Offs [u - baseu]) != defaultTexMod.m_Offs [u - baseu])
{
modNode->setAttr(OF, f);
}
char OT[] = "TexMod_?OscillatorType";
OT[7] = u;
char OR[] = "TexMod_?OscillatorRate";
OR[7] = u;
char OP[] = "TexMod_?OscillatorPhase";
OP[7] = u;
char OA[] = "TexMod_?OscillatorAmplitude";
OA[7] = u;
if ((c = pTextureModifier.m_eMoveType [u - baseu]) != defaultTexMod.m_eMoveType [u - baseu])
{
modNode->setAttr(OT, c);
}
if ((f = pTextureModifier.m_OscRate [u - baseu]) != defaultTexMod.m_OscRate [u - baseu])
{
modNode->setAttr(OR, f);
}
if ((f = pTextureModifier.m_OscPhase [u - baseu]) != defaultTexMod.m_OscPhase [u - baseu])
{
modNode->setAttr(OP, f);
}
if ((f = pTextureModifier.m_OscAmplitude [u - baseu]) != defaultTexMod.m_OscAmplitude [u - baseu])
{
modNode->setAttr(OA, f);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetTexturesFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const
{
const char* texmap = "";
const char* fileName = "";
XmlNodeRef texturesNode = node->findChild("Textures");
if (texturesNode)
{
for (int c = 0; c < texturesNode->getChildCount(); c++)
{
XmlNodeRef texNode = texturesNode->getChild(c);
texmap = texNode->getAttr("Map");
// [Shader System TO DO] - this must become per shader (and not global) according to the parser
uint8 texSlot = MaterialHelpers::FindTexSlot(texmap);
// [Shader System TO DO] - in the new system simply gather texture slot names, then identify name usage
// and accordingly match the slot (dynamically associated per shader by the parser).
if (texSlot == EFTT_UNKNOWN)
{
continue;
}
fileName = texNode->getAttr("File");
// legacy. Some textures used to be referenced using "engine\\" or "engine/" - this is no longer valid
if (
(strlen(fileName) > 7) &&
(azstrnicmp(fileName, "engine", 6) == 0) &&
((fileName[6] == '\\') || (fileName[6] == '/'))
)
{
fileName = fileName + 7;
}
// legacy: Files were saved into a mtl with many leading forward or back slashes, we eat them all here. We want it to start with a rel path.
const char* actualFileName = fileName;
while ((actualFileName[0]) && ((actualFileName[0] == '\\') || (actualFileName[0] == '/')))
{
++actualFileName;
}
fileName = actualFileName;
// Next insert the texture resource if did not exist
TexturesResourcesMap* pTextureReourcesMap = pShaderResources.GetTexturesResourceMap();
SEfResTexture* pTextureRes = &(*pTextureReourcesMap)[texSlot];
pTextureRes->m_Name = fileName;
texNode->getAttr("IsTileU", pTextureRes->m_bUTile);
texNode->getAttr("IsTileV", pTextureRes->m_bVTile);
texNode->getAttr("TexType", pTextureRes->m_Sampler.m_eTexType);
int filter = pTextureRes->m_Filter;
if (texNode->getAttr("Filter", filter))
{
pTextureRes->m_Filter = filter;
}
// Next look for modulation node - add it only if exist
XmlNodeRef modNode = texNode->findChild("TexMod");
if (modNode)
SetTexModFromXml( *(pTextureRes->AddModificator()), modNode);
}
}
}
//////////////////////////////////////////////////////////////////////////
static SInputShaderResources defaultShaderResource; // for comparison with the default values
static SEfResTexture defaultTextureResource; // for comparison with the default values
void MaterialHelpers::SetXmlFromTextures( SInputShaderResources& pShaderResources, XmlNodeRef& node) const
{
// Save texturing data.
XmlNodeRef texturesNode = node->newChild("Textures");
for (auto& iter : *(pShaderResources.GetTexturesResourceMap()) )
{
EEfResTextures texId = static_cast<EEfResTextures>(iter.first);
const SEfResTexture* pTextureRes = &(iter.second);
if (pTextureRes && !pTextureRes->m_Name.empty() && IsAdjustableTexSlot(texId))
{
XmlNodeRef texNode = texturesNode->newChild("Texture");
texNode->setAttr("Map", MaterialHelpers::LookupTexName(texId));
texNode->setAttr("File", pTextureRes->m_Name.c_str());
if (pTextureRes->m_Filter != defaultTextureResource.m_Filter)
{
texNode->setAttr("Filter", pTextureRes->m_Filter);
}
if (pTextureRes->m_bUTile != defaultTextureResource.m_bUTile)
{
texNode->setAttr("IsTileU", pTextureRes->m_bUTile);
}
if (pTextureRes->m_bVTile != defaultTextureResource.m_bVTile)
{
texNode->setAttr("IsTileV", pTextureRes->m_bVTile);
}
if (pTextureRes->m_Sampler.m_eTexType != defaultTextureResource.m_Sampler.m_eTexType)
{
texNode->setAttr("TexType", pTextureRes->m_Sampler.m_eTexType);
}
//////////////////////////////////////////////////////////////////////////
// Save texture modificators Modificators
//////////////////////////////////////////////////////////////////////////
SetXmlFromTexMod( *pTextureRes->GetModificator(), texNode);
}
/* [Shader System] - TO DO: test to see if slots can be removed
else
{
AZ_Assert(!pTextureRes->m_Name.empty(), "Shader resource texture error - Texture exists without a name");
}
*/
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetVertexDeformFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const
{
if (defaultShaderResource.m_DeformInfo.m_eType != pShaderResources.m_DeformInfo.m_eType)
{
node->setAttr("vertModifType", pShaderResources.m_DeformInfo.m_eType);
}
XmlNodeRef deformNode = node->findChild("VertexDeform");
if (deformNode)
{
int deform_type = eDT_Unknown;
deformNode->getAttr("Type", deform_type);
pShaderResources.m_DeformInfo.m_eType = (EDeformType)deform_type;
deformNode->getAttr("DividerX", pShaderResources.m_DeformInfo.m_fDividerX);
deformNode->getAttr("NoiseScale", pShaderResources.m_DeformInfo.m_vNoiseScale);
XmlNodeRef waveX = deformNode->findChild("WaveX");
if (waveX)
{
int type = eWF_None;
waveX->getAttr("Type", type);
pShaderResources.m_DeformInfo.m_WaveX.m_eWFType = (EWaveForm)type;
waveX->getAttr("Amp", pShaderResources.m_DeformInfo.m_WaveX.m_Amp);
waveX->getAttr("Level", pShaderResources.m_DeformInfo.m_WaveX.m_Level);
waveX->getAttr("Phase", pShaderResources.m_DeformInfo.m_WaveX.m_Phase);
waveX->getAttr("Freq", pShaderResources.m_DeformInfo.m_WaveX.m_Freq);
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetXmlFromVertexDeform(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const
{
int vertModif = pShaderResources.m_DeformInfo.m_eType;
node->setAttr("vertModifType", vertModif);
if (pShaderResources.m_DeformInfo.m_eType != eDT_Unknown)
{
XmlNodeRef deformNode = node->newChild("VertexDeform");
deformNode->setAttr("Type", pShaderResources.m_DeformInfo.m_eType);
deformNode->setAttr("DividerX", pShaderResources.m_DeformInfo.m_fDividerX);
deformNode->setAttr("NoiseScale", pShaderResources.m_DeformInfo.m_vNoiseScale);
XmlNodeRef waveX = deformNode->newChild("WaveX");
waveX->setAttr("Type", pShaderResources.m_DeformInfo.m_WaveX.m_eWFType);
waveX->setAttr("Amp", pShaderResources.m_DeformInfo.m_WaveX.m_Amp);
waveX->setAttr("Level", pShaderResources.m_DeformInfo.m_WaveX.m_Level);
waveX->setAttr("Phase", pShaderResources.m_DeformInfo.m_WaveX.m_Phase);
waveX->setAttr("Freq", pShaderResources.m_DeformInfo.m_WaveX.m_Freq);
}
}
//////////////////////////////////////////////////////////////////////////
static inline ColorF ToCFColor(const Vec3& col)
{
return ColorF(col);
}
void MaterialHelpers::SetLightingFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const
{
// Load lighting data.
Vec3 vColor;
Vec4 vColor4;
if (node->getAttr("Diffuse", vColor4))
{
pShaderResources.m_LMaterial.m_Diffuse = ColorF(vColor4.x, vColor4.y, vColor4.z, vColor4.w);
}
else if (node->getAttr("Diffuse", vColor))
{
pShaderResources.m_LMaterial.m_Diffuse = ToCFColor(vColor);
}
if (node->getAttr("Specular", vColor4))
{
pShaderResources.m_LMaterial.m_Specular = ColorF(vColor4.x, vColor4.y, vColor4.z, vColor4.w);
}
else if (node->getAttr("Specular", vColor))
{
pShaderResources.m_LMaterial.m_Specular = ToCFColor(vColor);
}
if (node->getAttr("Emittance", vColor4))
{
pShaderResources.m_LMaterial.m_Emittance = ColorF(vColor4.x, vColor4.y, vColor4.z, vColor4.w);
}
node->getAttr("Shininess", pShaderResources.m_LMaterial.m_Smoothness);
node->getAttr("Opacity", pShaderResources.m_LMaterial.m_Opacity);
node->getAttr("AlphaTest", pShaderResources.m_AlphaRef);
node->getAttr("VoxelCoverage", pShaderResources.m_VoxelCoverage);
}
//////////////////////////////////////////////////////////////////////////
static inline Vec3 ToVec3(const ColorF& col)
{
return Vec3(col.r, col.g, col.b);
}
static inline Vec4 ToVec4(const ColorF& col)
{
return Vec4(col.r, col.g, col.b, col.a);
}
void MaterialHelpers::SetXmlFromLighting(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const
{
// Save ligthing data.
if (defaultShaderResource.m_LMaterial.m_Diffuse != pShaderResources.m_LMaterial.m_Diffuse)
{
node->setAttr("Diffuse", ToVec4(pShaderResources.m_LMaterial.m_Diffuse));
}
if (defaultShaderResource.m_LMaterial.m_Specular != pShaderResources.m_LMaterial.m_Specular)
{
node->setAttr("Specular", ToVec4(pShaderResources.m_LMaterial.m_Specular));
}
if (defaultShaderResource.m_LMaterial.m_Emittance != pShaderResources.m_LMaterial.m_Emittance)
{
node->setAttr("Emittance", ToVec4(pShaderResources.m_LMaterial.m_Emittance));
}
if (defaultShaderResource.m_LMaterial.m_Opacity != pShaderResources.m_LMaterial.m_Opacity)
{
node->setAttr("Opacity", pShaderResources.m_LMaterial.m_Opacity);
}
if (defaultShaderResource.m_LMaterial.m_Smoothness != pShaderResources.m_LMaterial.m_Smoothness)
{
node->setAttr("Shininess", pShaderResources.m_LMaterial.m_Smoothness);
}
if (defaultShaderResource.m_AlphaRef != pShaderResources.m_AlphaRef)
{
node->setAttr("AlphaTest", pShaderResources.m_AlphaRef);
}
if (defaultShaderResource.m_VoxelCoverage != pShaderResources.m_VoxelCoverage)
{
node->setAttr("VoxelCoverage", pShaderResources.m_VoxelCoverage);
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetShaderParamsFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const
{
int nA = node->getNumAttributes();
if (!nA)
{
return;
}
for (int i = 0; i < nA; i++)
{
const char* key = NULL, * val = NULL;
node->getAttributeByIndex(i, &key, &val);
// try to set existing param first
bool bFound = false;
for (int j = 0; j < pShaderResources.m_ShaderParams.size(); j++)
{
SShaderParam* pParam = &pShaderResources.m_ShaderParams[j];
if (pParam->m_Name == key)
{
bFound = true;
switch (pParam->m_Type)
{
case eType_BYTE:
node->getAttr(key, pParam->m_Value.m_Byte);
break;
case eType_SHORT:
node->getAttr(key, pParam->m_Value.m_Short);
break;
case eType_INT:
node->getAttr(key, pParam->m_Value.m_Int);
break;
case eType_FLOAT:
node->getAttr(key, pParam->m_Value.m_Float);
break;
case eType_FCOLOR:
case eType_FCOLORA:
{
Vec3 vValue;
node->getAttr(key, vValue);
pParam->m_Value.m_Color[0] = vValue.x;
pParam->m_Value.m_Color[1] = vValue.y;
pParam->m_Value.m_Color[2] = vValue.z;
}
break;
case eType_VECTOR:
{
Vec4 vValue;
if (node->getAttr(key, vValue))
{
pParam->m_Value.m_Color[0] = vValue.x;
pParam->m_Value.m_Color[1] = vValue.y;
pParam->m_Value.m_Color[2] = vValue.z;
pParam->m_Value.m_Color[3] = vValue.w;
}
else
{
Vec3 vValue3;
if (node->getAttr(key, vValue3))
{
pParam->m_Value.m_Color[0] = vValue3.x;
pParam->m_Value.m_Color[1] = vValue3.y;
pParam->m_Value.m_Color[2] = vValue3.z;
pParam->m_Value.m_Color[3] = 1.0f;
}
}
}
break;
default:
break;
}
}
}
if (!bFound)
{
assert(val && key);
SShaderParam Param;
Param.m_Name = key;
Param.m_Value.m_Color[0] = Param.m_Value.m_Color[1] = Param.m_Value.m_Color[2] = Param.m_Value.m_Color[3] = 0;
#if !defined(NDEBUG)
int res =
#endif
azsscanf(val, "%f,%f,%f,%f", &Param.m_Value.m_Color[0], &Param.m_Value.m_Color[1], &Param.m_Value.m_Color[2], &Param.m_Value.m_Color[3]);
assert(res);
pShaderResources.m_ShaderParams.push_back(Param);
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialHelpers::SetXmlFromShaderParams(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const
{
for (int i = 0; i < pShaderResources.m_ShaderParams.size(); i++)
{
const SShaderParam* pParam = &pShaderResources.m_ShaderParams[i];
switch (pParam->m_Type)
{
case eType_BYTE:
node->setAttr(pParam->m_Name.c_str(), (int)pParam->m_Value.m_Byte);
break;
case eType_SHORT:
node->setAttr(pParam->m_Name.c_str(), (int)pParam->m_Value.m_Short);
break;
case eType_INT:
node->setAttr(pParam->m_Name.c_str(), (int)pParam->m_Value.m_Int);
break;
case eType_FLOAT:
node->setAttr(pParam->m_Name.c_str(), (float)pParam->m_Value.m_Float);
break;
case eType_FCOLOR:
node->setAttr(pParam->m_Name.c_str(), Vec3(pParam->m_Value.m_Color[0], pParam->m_Value.m_Color[1], pParam->m_Value.m_Color[2]));
break;
case eType_VECTOR:
node->setAttr(pParam->m_Name.c_str(), Vec3(pParam->m_Value.m_Vector[0], pParam->m_Value.m_Vector[1], pParam->m_Value.m_Vector[2]));
break;
default:
break;
}
}
}
//------------------------------------------------------------------------------
// [Shader System TO DO] - the following function supports older version of data
// and converts them.
// This needs to go away soon!
//------------------------------------------------------------------------------
void MaterialHelpers::MigrateXmlLegacyData(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const
{
float glowAmount;
// Migrate glow from 3.8.3 to emittance
if (node->getAttr("GlowAmount", glowAmount) && glowAmount > 0)
{
SEfResTexture* pTextureRes = pShaderResources.GetTextureResource(EFTT_DIFFUSE);
if (pTextureRes && (pTextureRes->m_Sampler.m_eTexType == eTT_2D))
{
// The following line will create and insert a new texture data slot if did not exist.
pShaderResources.m_TexturesResourcesMap[EFTT_EMITTANCE].m_Name = pTextureRes->m_Name;
}
const float legacyHDRDynMult = 2.0f;
const float legacyIntensityScale = 10.0f; // Legacy scale factor 10000 divided by 1000 for kilonits
// Clamp this at EMISSIVE_INTENSITY_SOFT_MAX because some previous glow parameters become extremely bright.
pShaderResources.m_LMaterial.m_Emittance.a = min(powf(glowAmount * legacyHDRDynMult, legacyHDRDynMult) * legacyIntensityScale, EMISSIVE_INTENSITY_SOFT_MAX);
std::string materialName = node->getAttr("Name");
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Material %s has had legacy GlowAmount automatically converted to Emissive Intensity. The material parameters related to Emittance should be manually adjusted for this material.", materialName.c_str());
}
XmlNodeRef publicParamsNode = node->findChild("PublicParams");
if (publicParamsNode && publicParamsNode->haveAttr("BlendLayer2Specular"))
{
// Check to see if the BlendLayer2Specular is a float
AZStd::string blendLayer2SpecularString(publicParamsNode->getAttr("BlendLayer2Specular"));
// If there are no commas in the string representation, it must be a single float instead of a color
if (blendLayer2SpecularString.find(',') == AZStd::string::npos)
{
float blendLayer2SpecularFloat = 0.0f;
publicParamsNode->getAttr("BlendLayer2Specular", blendLayer2SpecularFloat);
publicParamsNode->setAttr("BlendLayer2Specular", Vec4(blendLayer2SpecularFloat, blendLayer2SpecularFloat, blendLayer2SpecularFloat, 0.0));
}
}
}
@@ -1,64 +0,0 @@
/*
* 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.
#ifndef __MaterialHelpers_h__
#define __MaterialHelpers_h__
#pragma once
#include <IMaterial.h>
// Description:
// Namespace "implementation", not a class "implementation", no member-variables, only const functions;
// Used to encapsulate the material-definition/io into Cry3DEngine (and make it plugable that way).
struct MaterialHelpers
: public IMaterialHelpers
{
//////////////////////////////////////////////////////////////////////////
virtual EEfResTextures FindTexSlot(const char* texName) const final;
virtual const char* FindTexName(EEfResTextures texSlot) const final;
virtual const char* LookupTexName(EEfResTextures texSlot) const final;
virtual const char* LookupTexDesc(EEfResTextures texSlot) const final;
virtual const char* LookupTexEnum(EEfResTextures texSlot) const final;
virtual const char* LookupTexSuffix(EEfResTextures texSlot) const final;
virtual bool IsAdjustableTexSlot(EEfResTextures texSlot) const final;
//////////////////////////////////////////////////////////////////////////
virtual bool SetGetMaterialParamFloat(IRenderShaderResources& pShaderResources, const char* sParamName, float& v, bool bGet) const final;
virtual bool SetGetMaterialParamVec3(IRenderShaderResources& pShaderResources, const char* sParamName, Vec3& v, bool bGet) const final;
//////////////////////////////////////////////////////////////////////////
virtual void SetTexModFromXml(SEfTexModificator& pShaderResources, const XmlNodeRef& modNode) const final;
virtual void SetXmlFromTexMod(const SEfTexModificator& pShaderResources, XmlNodeRef& node) const final;
//////////////////////////////////////////////////////////////////////////
virtual void SetTexturesFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const final;
virtual void SetXmlFromTextures( SInputShaderResources& pShaderResources, XmlNodeRef& node) const final;
//////////////////////////////////////////////////////////////////////////
virtual void SetVertexDeformFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const final;
virtual void SetXmlFromVertexDeform(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const final;
//////////////////////////////////////////////////////////////////////////
virtual void SetLightingFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const final;
virtual void SetXmlFromLighting(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const final;
//////////////////////////////////////////////////////////////////////////
virtual void SetShaderParamsFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const final;
virtual void SetXmlFromShaderParams(const SInputShaderResources& pShaderResources, XmlNodeRef& node) const final;
//////////////////////////////////////////////////////////////////////////
virtual void MigrateXmlLegacyData(SInputShaderResources& pShaderResources, const XmlNodeRef& node) const final;
};
#endif
@@ -1,147 +0,0 @@
/*
* 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.
// Description : Material Manager Implementation
#include "Cry3DEngine_precompiled.h"
#include "MatMan.h"
#include "3dEngine.h"
#include "ObjMan.h"
#include "IRenderer.h"
#include "SurfaceTypeManager.h"
#include "CGFContent.h"
#include <IResourceManager.h>
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
_smart_ptr<IMaterial> CMatMan::GetDefaultMaterial()
{
return m_pDefaultMtl;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
_smart_ptr<IMaterial> CMatInfo::GetSafeSubMtl(int nSubMtlSlot)
{
if (m_subMtls.empty() || !(m_Flags & MTL_FLAG_MULTI_SUBMTL))
{
return this; // Not Multi material.
}
if (nSubMtlSlot >= 0 && nSubMtlSlot < (int)m_subMtls.size() && m_subMtls[nSubMtlSlot] != NULL)
{
return m_subMtls[nSubMtlSlot];
}
else
{
return GetMatMan()->GetDefaultMaterial();
}
}
///////////////////////////////////////////////////////////////////////////////
SShaderItem& CMatInfo::GetShaderItem()
{
return m_shaderItem;
}
///////////////////////////////////////////////////////////////////////////////
const SShaderItem& CMatInfo::GetShaderItem() const
{
return m_shaderItem;
}
//////////////////////////////////////////////////////////////////////////
SShaderItem& CMatInfo::GetShaderItem(int nSubMtlSlot)
{
SShaderItem* pShaderItem = NULL;
if (m_subMtls.empty() || !(m_Flags & MTL_FLAG_MULTI_SUBMTL))
{
pShaderItem = &m_shaderItem; // Not Multi material.
}
else if (nSubMtlSlot >= 0 && nSubMtlSlot < (int)m_subMtls.size() && m_subMtls[nSubMtlSlot] != NULL)
{
pShaderItem = &(m_subMtls[nSubMtlSlot]->m_shaderItem);
}
else
{
_smart_ptr<IMaterial> pDefaultMaterial = GetMatMan()->GetDefaultMaterial();
pShaderItem = &(static_cast<CMatInfo*>(pDefaultMaterial.get())->m_shaderItem);
}
return *pShaderItem;
}
///////////////////////////////////////////////////////////////////////////////
const SShaderItem& CMatInfo::GetShaderItem(int nSubMtlSlot) const
{
const SShaderItem* pShaderItem = NULL;
if (m_subMtls.empty() || !(m_Flags & MTL_FLAG_MULTI_SUBMTL))
{
pShaderItem = &m_shaderItem; // Not Multi material.
}
else if (nSubMtlSlot >= 0 && nSubMtlSlot < (int)m_subMtls.size() && m_subMtls[nSubMtlSlot] != NULL)
{
pShaderItem = &(m_subMtls[nSubMtlSlot]->m_shaderItem);
}
else
{
_smart_ptr<IMaterial> pDefaultMaterial = GetMatMan()->GetDefaultMaterial();
pShaderItem = &(static_cast<CMatInfo*>(pDefaultMaterial.get())->m_shaderItem);
}
return *pShaderItem;
}
///////////////////////////////////////////////////////////////////////////////
bool CMatInfo::IsForwardRenderingRequired()
{
bool bRequireForwardRendering = (m_Flags & MTL_FLAG_REQUIRE_FORWARD_RENDERING) != 0;
if (!bRequireForwardRendering)
{
for (int i = 0; i < (int)m_subMtls.size(); ++i)
{
if (m_subMtls[i] != 0 && m_subMtls[i]->m_Flags & MTL_FLAG_REQUIRE_FORWARD_RENDERING)
{
bRequireForwardRendering = true;
break;
}
}
}
return bRequireForwardRendering;
}
///////////////////////////////////////////////////////////////////////////////
bool CMatInfo::IsNearestCubemapRequired()
{
bool bRequireNearestCubemap = (m_Flags & MTL_FLAG_REQUIRE_NEAREST_CUBEMAP) != 0;
if (!bRequireNearestCubemap)
{
for (int i = 0; i < (int)m_subMtls.size(); ++i)
{
if (m_subMtls[i] != 0 && m_subMtls[i]->m_Flags & MTL_FLAG_REQUIRE_NEAREST_CUBEMAP)
{
bRequireNearestCubemap = true;
break;
}
}
}
return bRequireNearestCubemap;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@@ -1,28 +0,0 @@
#
# 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.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_add_target(
NAME Cry3DEngine.MeshCompiler.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
meshcompiler_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::mikkelsen
Legacy::CryCommon
)
@@ -1,351 +0,0 @@
/*
* 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 <platform.h> // for assert
#include "ForsythFaceReorderer.h"
#include <cmath> // powf()
ForsythFaceReorderer::ForsythFaceReorderer()
{
computeValencyScoreTable();
}
bool ForsythFaceReorderer::reorderFaces(
const size_t cacheSize,
const uint verticesPerFace,
const size_t indexCount,
const uint32* const inVertexIndices,
uint32* const outVertexIndices,
uint32* const outFaceToOldFace)
{
clear();
if (verticesPerFace < sk_minVerticesPerFace || verticesPerFace > sk_maxVerticesPerFace)
{
return false;
}
if (indexCount <= 0)
{
return true;
}
if (indexCount % verticesPerFace != 0)
{
return false;
}
if (inVertexIndices == 0)
{
return false;
}
if (outVertexIndices == 0)
{
return false;
}
if ((cacheSize < verticesPerFace) || (cacheSize > sk_maxCacheSize))
{
return false;
}
m_cacheSize = (int)cacheSize;
m_cacheUsedSize = 0;
computeCacheScoreTable(verticesPerFace);
const size_t faceCount = indexCount / verticesPerFace;
if (indexCount / verticesPerFace >= (uint32) - 1)
{
// Face count is too high
return false;
}
size_t writtenFaceCount = 0;
uint32 vertexCount;
{
// TODO: use minVertexIndex also. It will allow to use less memory for ranged indices.
// For example indices in ranges [800;899] will use memory size 100, not 900.
uint32 maxVertexIndex = 0;
for (size_t i = 0; i < indexCount; ++i)
{
if (inVertexIndices[i] > maxVertexIndex)
{
maxVertexIndex = inVertexIndices[i];
}
}
if (((size_t)maxVertexIndex) + 1 >= (uint32) - 1)
{
// Vertex count is too high
return false;
}
vertexCount = maxVertexIndex + 1;
}
// Allocate and initialize arrays
{
{
Vertex initVertex;
initVertex.m_pFaceList = 0;
initVertex.m_aliveFaceCount = 0;
initVertex.m_posInCache = -1;
initVertex.m_score = 0;
m_vertices.resize(vertexCount, initVertex);
}
m_deadFacesBitArray.resize((faceCount + 7) / 8, 0);
m_faceScores.resize(faceCount, 0);
m_vertexFaceLists.resize(faceCount * verticesPerFace);
}
// Fill per-vertex face lists
{
for (size_t i = 0; i < indexCount; ++i)
{
const uint32 vertexIndex = inVertexIndices[i];
if (m_vertices[vertexIndex].m_aliveFaceCount >= sk_maxValency)
{
// Vertex valency is too high
return false;
}
++m_vertices[vertexIndex].m_aliveFaceCount;
}
uint32 pos = 0;
for (uint32 vi = 0; vi < vertexCount; ++vi)
{
Vertex& v = m_vertices[vi];
v.m_pFaceList = &m_vertexFaceLists[pos];
pos += v.m_aliveFaceCount;
v.m_aliveFaceCount = 0;
}
assert(pos == faceCount * verticesPerFace);
const uint32* pVertexIndex = &inVertexIndices[0];
for (uint32 fi = 0; fi < faceCount; ++fi, pVertexIndex += verticesPerFace)
{
for (uint j = 0; j < verticesPerFace; ++j)
{
Vertex& v = m_vertices[pVertexIndex[j]];
v.m_pFaceList[v.m_aliveFaceCount++] = fi;
}
}
}
// Compute vertex and face scores
{
for (uint32 vi = 0; vi < vertexCount; ++vi)
{
computeVertexScore(m_vertices[vi]);
}
const uint32* pVertexIndex = &inVertexIndices[0];
for (uint32 fi = 0; fi < faceCount; ++fi, pVertexIndex += verticesPerFace)
{
m_faceScores[fi] = 0;
for (uint j = 0; j < verticesPerFace; ++j)
{
const Vertex& v = m_vertices[pVertexIndex[j]];
m_faceScores[fi] += v.m_score;
}
}
}
// Add faces with highest scores to the output buffer, one by one.
uint32 faceSearchCursor = 0;
uint32 bestFaceToAdd;
for (;; )
{
// Find face with highest score
{
bestFaceToAdd = (uint32) - 1;
float highestScore = -1;
for (int i = 0; i < m_cacheUsedSize; ++i)
{
const Vertex& v = m_vertices[m_cache[i]];
const uint32* const pFaces = v.m_pFaceList;
for (valency_type j = 0; j < v.m_aliveFaceCount; ++j)
{
const uint32 faceIndex = pFaces[j];
if (highestScore < m_faceScores[faceIndex])
{
highestScore = m_faceScores[faceIndex];
bestFaceToAdd = faceIndex;
}
}
}
if (bestFaceToAdd == (uint32) - 1)
{
bestFaceToAdd = findBestFaceToAdd(faceSearchCursor);
assert(bestFaceToAdd != (uint32) - 1);
}
}
// Add the best face to the output buffer
{
size_t writtenIndexCount = writtenFaceCount * verticesPerFace;
for (uint j = 0; j < verticesPerFace; ++j)
{
outVertexIndices[writtenIndexCount + j] = inVertexIndices[(size_t)bestFaceToAdd * verticesPerFace + j];
}
if (outFaceToOldFace)
{
outFaceToOldFace[writtenFaceCount] = bestFaceToAdd;
}
if (++writtenFaceCount == faceCount)
{
// We're done.
return true;
}
}
// Make changes to the cache, vertex & cache scores, vertex face lists
{
m_deadFacesBitArray[bestFaceToAdd >> 3] |= 1 << (bestFaceToAdd & 7);
for (int j = verticesPerFace - 1; j >= 0; --j)
{
const uint32 vertexIndex = inVertexIndices[(size_t)bestFaceToAdd * verticesPerFace + j];
moveVertexToCacheTop(vertexIndex);
removeFaceFromVertex(vertexIndex, bestFaceToAdd);
}
for (int i = 0; i < m_cacheUsedSize; ++i)
{
Vertex& v = m_vertices[m_cache[i]];
if (i >= m_cacheSize)
{
v.m_posInCache = -1;
}
const float oldScore = v.m_score;
computeVertexScore(v);
const float differenceScore = v.m_score - oldScore;
const uint32* const pFaces = v.m_pFaceList;
for (valency_type j = 0; j < v.m_aliveFaceCount; ++j)
{
m_faceScores[pFaces[j]] += differenceScore;
}
}
if (m_cacheUsedSize > m_cacheSize)
{
m_cacheUsedSize = m_cacheSize;
}
}
}
}
void ForsythFaceReorderer::clear()
{
m_vertices.clear();
m_deadFacesBitArray.clear();
m_faceScores.clear();
m_vertexFaceLists.clear();
}
void ForsythFaceReorderer::computeCacheScoreTable(const int verticesPerFace)
{
static const float lastFaceScore = 0.75f;
static const float cacheDecayPower = 1.5f;
// Vertices of last added face should have *same* fixed score,
// because otherwise results will depend on the order of vertices
// in face (5,6,7 and 7,5,6 will produce different results).
for (int j = 0; j < verticesPerFace; ++j)
{
m_scoreTable_cachePosition[j] = lastFaceScore;
}
for (int i = verticesPerFace; i < m_cacheSize; ++i)
{
const float x = 1.0f - ((i - verticesPerFace) / (m_cacheSize - verticesPerFace));
m_scoreTable_cachePosition[i] = powf(x, cacheDecayPower);
}
}
void ForsythFaceReorderer::computeValencyScoreTable()
{
// Lower number of alive faces in the vertex produces higher score.
// It allows to get rid of lone vertices quickly.
static const float valencyPower = -0.5f;
static const float valencyScale = 2.0f;
m_scoreTable_valency[0] = 0;
for (valency_type i = 1; i < sk_valencyTableSize; ++i)
{
m_scoreTable_valency[i] = valencyScale * powf(i, valencyPower);
}
}
void ForsythFaceReorderer::computeVertexScore(ForsythFaceReorderer::Vertex& v)
{
if (v.m_aliveFaceCount > 0)
{
assert(v.m_posInCache < m_cacheSize);
const float valencyScore = (v.m_aliveFaceCount < sk_valencyTableSize) ? m_scoreTable_valency[v.m_aliveFaceCount] : 0;
// Preventing "SCA: warning C6385: Invalid data: accessing 'm_scoreTable_cachePosition', the readable size is '200' bytes, but '484' bytes might be read"
PREFAST_SUPPRESS_WARNING(6385) const float cacheScore = (v.m_posInCache >= 0) ? m_scoreTable_cachePosition[v.m_posInCache] : 0;
v.m_score = valencyScore + cacheScore;
}
}
void ForsythFaceReorderer::moveVertexToCacheTop(const uint32 vertexIndex)
{
const int oldPosInCache = m_vertices[vertexIndex].m_posInCache;
for (int dst = (oldPosInCache >= 0) ? oldPosInCache : m_cacheUsedSize; dst > 0; --dst)
{
const uint32 v = m_cache[dst - 1];
m_cache[dst] = v;
++m_vertices[v].m_posInCache;
}
m_cache[0] = vertexIndex;
m_vertices[vertexIndex].m_posInCache = 0;
if (oldPosInCache < 0)
{
++m_cacheUsedSize;
}
}
void ForsythFaceReorderer::removeFaceFromVertex(const uint32 vertexIndex, const uint32 faceIndex)
{
Vertex& v = m_vertices[vertexIndex];
assert(v.m_aliveFaceCount > 0);
uint32* const pFaces = v.m_pFaceList;
for (int j = 0;; ++j)
{
if (pFaces[j] == faceIndex)
{
pFaces[j] = pFaces[--v.m_aliveFaceCount];
return;
}
}
}
uint32 ForsythFaceReorderer::findBestFaceToAdd(uint32& faceSearchCursor) const
{
assert(!m_faceScores.empty());
assert(faceSearchCursor < m_faceScores.size());
while (m_deadFacesBitArray[faceSearchCursor >> 3] & (1 << (faceSearchCursor & 7)))
{
++faceSearchCursor;
}
return faceSearchCursor++;
}
@@ -1,95 +0,0 @@
/*
* 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.
#pragma once
#include <vector>
#include <BaseTypes.h>
#include "CompileTimeAssert.h"
//
// Note: This implementation, in contrast to many other implementations
// of the Forsyth's algorithm, does not crash when the input faces contain
// duplicate indices, for example (8,3,8) or (1,1,9).
//
class ForsythFaceReorderer
{
public:
static const size_t sk_maxCacheSize = 50; // you can change it. note: making it higher will increase sizeof(*this)
static const size_t sk_minVerticesPerFace = 3;
static const size_t sk_maxVerticesPerFace = 4;
COMPILE_TIME_ASSERT(sk_minVerticesPerFace >= 3); // Bad min # of vertices per face
COMPILE_TIME_ASSERT(sk_minVerticesPerFace <= sk_maxVerticesPerFace); // Bad # of vertices per face
COMPILE_TIME_ASSERT(sk_maxVerticesPerFace <= sk_maxCacheSize); // Bad max cache size
private:
typedef uint16 valency_type;
static const valency_type sk_maxValency = 0xFFFF;
typedef int8 cachepos_type;
static const cachepos_type sk_maxCachePos = 127;
struct Vertex
{
uint32* m_pFaceList;
valency_type m_aliveFaceCount;
cachepos_type m_posInCache;
float m_score;
};
static const size_t sk_valencyTableSize = 32; // note: size_t is used instead of valency_type because valency_type overflows if sk_valencyTableSize == 1 + sk_maxValency
COMPILE_TIME_ASSERT(sk_valencyTableSize - 1 <= sk_maxValency); // Bad valency table size
COMPILE_TIME_ASSERT(sk_maxCacheSize <= 1 + (size_t)sk_maxCachePos); // Max cache size is too big
std::vector<Vertex> m_vertices;
std::vector<uint8> m_deadFacesBitArray;
std::vector<float> m_faceScores; // score of every face
std::vector<uint32> m_vertexFaceLists; // lists with indices of faces (each vertex has own list)
int m_cacheSize;
int m_cacheUsedSize;
uint32 m_cache[sk_maxCacheSize + sk_maxVerticesPerFace]; // +sk_maxVerticesPerFace is temporary storage for vertices of the incoming face
float m_scoreTable_valency[sk_valencyTableSize];
float m_scoreTable_cachePosition[sk_maxCacheSize];
public:
ForsythFaceReorderer();
// notes:
// 1) it's not allowed to pass same array for inVertexIndices and outVertexIndices
// 2) outFaceToOldFace is optional (pass 0 if you don't need this array filled)
bool reorderFaces(
const size_t cacheSize,
const uint verticesPerFace,
const size_t indexCount,
const uint32* const inVertexIndices,
uint32* const outVertexIndices,
uint32* const outFaceToOldFace);
private:
void clear();
void computeCacheScoreTable(const int verticesPerFace);
void computeValencyScoreTable();
void computeVertexScore(Vertex& v);
void moveVertexToCacheTop(const uint32 vertexIndex);
void removeFaceFromVertex(const uint32 vertexIndex, const uint32 faceIndex);
uint32 findBestFaceToAdd(uint32& faceSearchCursor) const;
};
File diff suppressed because it is too large Load Diff
@@ -1,235 +0,0 @@
/*
* 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.
#ifndef CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_MESHCOMPILER_H
#define CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_MESHCOMPILER_H
#pragma once
#include "IIndexedMesh.h"
namespace mesh_compiler
{
enum EMeshCompileFlags
{
MESH_COMPILE_OPTIMIZE = BIT(0),
MESH_COMPILE_TANGENTS = BIT(1),
MESH_COMPILE_USECUSTOMNORMALS = BIT(3),
MESH_COMPILE_VALIDATE = BIT(4),
// Optimizes a mesh using PowerVR SDK's optimizer.
// This should only be set with "OptimizedPrimitiveType=1" when compiling mobile assets outside the editor.
MESH_COMPILE_PVR_STRIPIFY = BIT(5),
MESH_COMPILE_VALIDATE_FAIL_ON_DEGENERATE_FACES = BIT(6),
};
//////////////////////////////////////////////////////////////////////////
class CMeshCompiler
{
public:
CMeshCompiler();
~CMeshCompiler();
// for flags see EMeshCompilerFlags
bool Compile(CMesh& mesh, int flags);
void SetVertexRemapping(std::vector<int>* pVertexMap)
{
m_pVertexMap = pVertexMap;
}
void SetIndexRemapping(std::vector<int>* pIndexMap)
{
m_pIndexMap = pIndexMap;
}
inline bool IsEquivalentVec3dCheckYFirst(const Vec3& v0, const Vec3& v1, float fEpsilon)
{
if (fabsf(v0.y - v1.y) < fEpsilon)
{
if (fabsf(v0.x - v1.x) < fEpsilon)
{
if (fabsf(v0.z - v1.z) < fEpsilon)
{
return true;
}
}
}
return false;
}
_inline const Vec3& ToVec3(const Vec3& vec) { return vec; }
_inline const Vec3 ToVec3(const Vec3f16& vec) { return vec.ToVec3(); }
template<class T>
inline int FindInPosBuffer_VF_P3X(const Vec3& vPosToFind, const T* pVertBuff, std::vector<int>* pHash, float fEpsilon)
{
for (uint32 i = 0; i < pHash->size(); i++)
{
if (IsEquivalentVec3dCheckYFirst(ToVec3(pVertBuff[(*pHash)[i]].xyz), vPosToFind, fEpsilon))
{
return (*pHash)[i];
}
}
return -1;
}
template<class V, class I>
void WeldPos_VF_P3X(
PodArray<V>& vertices,
PodArray<SPipTangents>& tangents,
PodArray<SPipNormal>& normals,
PodArray<I>& indices,
float fEpsilon,
const AABB& boxBoundary)
{
const int numVertices = vertices.Count();
V* pTmpVerts = new V[numVertices];
SPipTangents* pTmpTangents = new SPipTangents[tangents.Count()];
#if ENABLE_NORMALSTREAM_SUPPORT
SPipNormal* pTmpNormals = NULL;
if (normals.Count() > 0)
{
pTmpNormals = new SPipNormal[normals.Count()];
}
SPipNormal emptyNormal;
#endif
int nCurVertex = 0;
PodArray<I> newIndices;
std::vector<int> arrHashTable[256];
newIndices.reserve(indices.size());
std::vector<int>* pHash = 0;
float fHashElemSize = 256.0f / max(boxBoundary.max.x - boxBoundary.min.x, 0.01f);
for (uint32 i = 0; i < indices.size(); i++)
{
int v = indices[i];
assert(v < vertices.Count());
V& vPos = vertices[v];
SPipTangents& vTang = tangents[v];
#if ENABLE_NORMALSTREAM_SUPPORT
SPipNormal& vNorm = pTmpNormals ? normals[v] : emptyNormal;
#endif
bool bInRange(
vPos.xyz.x > boxBoundary.min.x && vPos.xyz.y > boxBoundary.min.y && vPos.xyz.z > boxBoundary.min.z &&
vPos.xyz.x < boxBoundary.max.x && vPos.xyz.y < boxBoundary.max.y && vPos.xyz.z < boxBoundary.max.z);
int nHashValue = int((vPos.xyz.x - boxBoundary.min.x) * fHashElemSize);
pHash = &arrHashTable[(unsigned char)(nHashValue)];
int nFind = FindInPosBuffer_VF_P3X(ToVec3(vPos.xyz), pTmpVerts, pHash, bInRange ? fEpsilon : 0.01f);
if (nFind < 0)
{
pHash->push_back(nCurVertex);
// make sure neighbor hashes also have this vertex
if (bInRange && fEpsilon > 0.01f)
{
pHash = &arrHashTable[(unsigned char)(nHashValue + 1)];
if (FindInPosBuffer_VF_P3X(ToVec3(vPos.xyz), pTmpVerts, pHash, fEpsilon) < 0)
{
pHash->push_back(nCurVertex);
}
pHash = &arrHashTable[(unsigned char)(nHashValue - 1)];
if (FindInPosBuffer_VF_P3X(ToVec3(vPos.xyz), pTmpVerts, pHash, fEpsilon) < 0)
{
pHash->push_back(nCurVertex);
}
}
PREFAST_ASSUME(nCurVertex < numVertices);
// add new vertex
pTmpVerts[nCurVertex] = vPos;
pTmpTangents[nCurVertex] = vTang;
#if ENABLE_NORMALSTREAM_SUPPORT
if (pTmpNormals)
{
pTmpNormals[nCurVertex] = vNorm;
}
#endif
newIndices.push_back(nCurVertex);
nCurVertex++;
}
else
{
newIndices.push_back(nFind);
}
}
indices.Clear();
indices.AddList(newIndices);
vertices.Clear();
vertices.AddList(pTmpVerts, nCurVertex);
tangents.Clear();
tangents.AddList(pTmpTangents, nCurVertex);
#if ENABLE_NORMALSTREAM_SUPPORT
if (pTmpNormals)
{
normals.Clear();
normals.AddList(pTmpNormals, nCurVertex);
delete[] pTmpNormals;
}
#endif
delete [] pTmpVerts;
delete [] pTmpTangents;
}
static bool CompareMeshes(const CMesh& mesh1, const CMesh& mesh2);
const char* GetLastError() const
{
return m_LastError;
}
private:
bool CreateIndicesAndDeleteDuplicateVertices(CMesh& mesh);
bool StripifyMesh_Forsyth(CMesh& mesh);
bool StripifyMesh_PVRTriStripList(CMesh& mesh);
public:
static bool CheckForDegenerateFaces(const CMesh& mesh);
private:
static void FindVertexRanges(CMesh& mesh);
private:
struct SBasisFace
{
int v[3];
};
std::vector<const SMeshFace*> m_vhash_table[MAX_SUB_MATERIALS];
std::vector<SBasisFace> m_thash_table[MAX_SUB_MATERIALS];
std::vector<int>* m_pVertexMap;
std::vector<int>* m_pIndexMap;
string m_LastError;
};
} // namespace mesh_compiler
#endif // CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_MESHCOMPILER_H
@@ -1,947 +0,0 @@
// Modifications copyright Amazon.com, Inc. or its affiliates.
/******************************************************************************
@File PVRTTriStrip.cpp
@Title PVRTTriStrip
@Version @Version
@Copyright Copyright (c) Imagination Technologies Limited.
@Platform Independent
@Description Strips a triangle list.
******************************************************************************/
/****************************************************************************
** Includes
****************************************************************************/
#include <platform.h>
#include <stdlib.h>
#include "PVRTTriStrip.h"
/****************************************************************************
** Defines
****************************************************************************/
#define RND_TRIS_ORDER
#define FREE(X) { if(X) { free(X); (X) = 0; } }
/****************************************************************************
** Structures
****************************************************************************/
/****************************************************************************
** Class: CTri
****************************************************************************/
class CTri;
/*!***************************************************************************
@Class CTriState
@Description Stores a pointer to the triangles either side of itself,
as well as it's winding.
*****************************************************************************/
class CTriState
{
public:
CTri *pRev, *pFwd;
bool bWindFwd;
CTriState()
{
bWindFwd = true; // Initial value irrelevent
pRev = NULL;
pFwd = NULL;
}
};
/*!***************************************************************************
@Class CTri
@Description Object used to store information about the triangle, such as
the vertex indices it is made from, which triangles are
adjacent to it, etc.
*****************************************************************************/
class CTri
{
public:
CTriState sNew, sOld;
CTri *pAdj[3];
bool bInStrip;
const unsigned int *pIdx; // three indices for the tri
bool bOutput;
public:
CTri();
int FindEdge(const unsigned int pw0, const unsigned int pw1) const;
void Cement();
void Undo();
int EdgeFromAdjTri(const CTri &tri) const; // Find the index of the adjacent tri
};
/*!***************************************************************************
@Class CStrip
@Description Object used to store the triangles that a given strip is
composed from.
*****************************************************************************/
class CStrip
{
protected:
unsigned int m_nTriCnt;
CTri *m_pTri;
unsigned int m_nStrips;
CTri **m_psStrip; // Working space for finding strips
public:
CStrip(
const unsigned int * const pui32TriList,
const unsigned int nTriCnt);
~CStrip();
protected:
bool StripGrow(
CTri &triFrom,
const unsigned int nEdgeFrom,
const int nMaxChange);
public:
void StripFromEdges();
void StripImprove();
void Output(
unsigned int **ppui32Strips,
unsigned int **ppnStripLen,
unsigned int *pnStripCnt);
};
/****************************************************************************
** Constants
****************************************************************************/
/****************************************************************************
** Code: Class: CTri
****************************************************************************/
CTri::CTri()
{
pAdj[0] = NULL;
pAdj[1] = NULL;
pAdj[2] = NULL;
bInStrip = false;
bOutput = false;
}
/*!***************************************************************************
@Function FindEdge
@Input pw0 The first index
@Input pw1 The second index
@Return The index of the edge
@Description Finds the index of the edge that the current object shares
with the two vertex index values that have been passed in
(or returns -1 if they dont share an edge).
*****************************************************************************/
int CTri::FindEdge(const unsigned int pw0, const unsigned int pw1) const
{
if((pIdx[0] == pw0 && pIdx[1] == pw1))
return 0;
if((pIdx[1] == pw0 && pIdx[2] == pw1))
return 1;
if((pIdx[2] == pw0 && pIdx[0] == pw1))
return 2;
return -1;
}
/*!***************************************************************************
@Function Cement
@Description Assigns the new state as the old state.
*****************************************************************************/
void CTri::Cement()
{
sOld = sNew;
}
/*!***************************************************************************
@Function Undo
@Description Reverts the new state to the old state.
*****************************************************************************/
void CTri::Undo()
{
sNew = sOld;
}
/*!***************************************************************************
@Function EdgeFromAdjTri
@Input tri The triangle to compare
@Return int Index of adjacent triangle (-1 if not adjacent)
@Description If the input triangle is adjacent to the current triangle,
it's index is returned.
*****************************************************************************/
int CTri::EdgeFromAdjTri(const CTri &tri) const
{
for(int i = 0; i < 3; ++i)
{
if(pAdj[i] == &tri)
{
return i;
}
}
assert(false);
return -1;
}
/****************************************************************************
** Local code
****************************************************************************/
/*!***************************************************************************
@Function OrphanTri
@Input tri The triangle test
@Return int Returns 1 if change was made
@Description If the input triangle is not wound forward and is not the last
triangle in the strip, the connection with the next triangle
in the strip is removed.
*****************************************************************************/
static int OrphanTri(
CTri * const pTri)
{
assert(!pTri->bInStrip);
if(pTri->sNew.bWindFwd || !pTri->sNew.pFwd)
return 0;
pTri->sNew.pFwd->sNew.pRev = NULL;
pTri->sNew.pFwd = NULL;
return 1;
}
/*!***************************************************************************
@Function TakeTri
@Input pTri The triangle to take
@Input pRevNew The triangle that is before pTri in the new strip
@Return int Returns 1 if a new strip has been created
@Description Removes the triangle from it's current strip
and places it in a new one (following pRevNew in the new strip).
*****************************************************************************/
static int TakeTri(
CTri * const pTri,
CTri * const pRevNew,
const bool bFwd)
{
int nRet;
assert(!pTri->bInStrip);
if(pTri->sNew.pFwd && pTri->sNew.pRev)
{
assert(pTri->sNew.pFwd->sNew.pRev == pTri);
pTri->sNew.pFwd->sNew.pRev = NULL;
assert(pTri->sNew.pRev->sNew.pFwd == pTri);
pTri->sNew.pRev->sNew.pFwd = NULL;
// If in the middle of a Strip, this will generate a new Strip
nRet = 1;
// The second tri in the strip may need to be orphaned, or it will have wrong winding order
nRet += OrphanTri(pTri->sNew.pFwd);
}
else if(pTri->sNew.pFwd)
{
assert(pTri->sNew.pFwd->sNew.pRev == pTri);
pTri->sNew.pFwd->sNew.pRev = NULL;
// If at the beginning of a Strip, no change
nRet = 0;
// The second tri in the strip may need to be orphaned, or it will have wrong winding order
nRet += OrphanTri(pTri->sNew.pFwd);
}
else if(pTri->sNew.pRev)
{
assert(pTri->sNew.pRev->sNew.pFwd == pTri);
pTri->sNew.pRev->sNew.pFwd = NULL;
// If at the end of a Strip, no change
nRet = 0;
}
else
{
// Otherwise it's a lonesome triangle; one Strip removed!
nRet = -1;
}
pTri->sNew.pFwd = NULL;
pTri->sNew.pRev = pRevNew;
pTri->bInStrip = true;
pTri->sNew.bWindFwd = bFwd;
if(pRevNew)
{
assert(!pRevNew->sNew.pFwd);
pRevNew->sNew.pFwd = pTri;
}
return nRet;
}
/*!***************************************************************************
@Function TryLinkEdge
@Input src The source triangle
@Input cmp The triangle to compare with
@Input nSrcEdge The edge of souce triangle to compare
@Input idx0 Vertex index 0 of the compare triangle
@Input idx1 Vertex index 1 of the compare triangle
@Description If the triangle to compare currently has no adjacent
triangle along the specified edge, link the source triangle
(along it's specified edge) with the compare triangle.
*****************************************************************************/
static bool TryLinkEdge(
CTri &src,
CTri &cmp,
const int nSrcEdge,
const unsigned int idx0,
const unsigned int idx1)
{
int nCmpEdge;
nCmpEdge = cmp.FindEdge(idx0, idx1);
if(nCmpEdge != -1 && !cmp.pAdj[nCmpEdge])
{
cmp.pAdj[nCmpEdge] = &src;
src.pAdj[nSrcEdge] = &cmp;
return true;
}
return false;
}
/****************************************************************************
** Code: Class: CStrip
****************************************************************************/
CStrip::CStrip(
const unsigned int * const pui32TriList,
const unsigned int nTriCnt)
{
unsigned int i, j;
bool b0, b1, b2;
m_nTriCnt = nTriCnt;
/*
Generate adjacency info
*/
m_pTri = new CTri[nTriCnt];
for(i = 0; i < nTriCnt; ++i)
{
// Set pointer to indices
m_pTri[i].pIdx = &pui32TriList[3 * i];
b0 = false;
b1 = false;
b2 = false;
for(j = 0; j < i && !(b0 & b1 & b2); ++j)
{
if(!b0)
b0 = TryLinkEdge(m_pTri[i], m_pTri[j], 0, m_pTri[i].pIdx[1], m_pTri[i].pIdx[0]);
if(!b1)
b1 = TryLinkEdge(m_pTri[i], m_pTri[j], 1, m_pTri[i].pIdx[2], m_pTri[i].pIdx[1]);
if(!b2)
b2 = TryLinkEdge(m_pTri[i], m_pTri[j], 2, m_pTri[i].pIdx[0], m_pTri[i].pIdx[2]);
}
}
// Initially, every triangle is a strip.
m_nStrips = m_nTriCnt;
// Allocate working space for the strippers
m_psStrip = new CTri*[m_nTriCnt];
}
CStrip::~CStrip()
{
delete [] m_pTri;
delete [] m_psStrip;
}
/*!***************************************************************************
@Function StripGrow
@Input triFrom The triangle to begin from
@Input nEdgeFrom The edge of the triangle to begin from
@Input maxChange The maximum number of changes to be made
@Description Takes triFrom as a starting point of triangles to add to
the list and adds triangles sequentially by finding the next
triangle that is adjacent to the current triangle.
This is repeated until the maximum number of changes
have been made.
*****************************************************************************/
bool CStrip::StripGrow(
CTri &triFrom,
const unsigned int nEdgeFrom,
const int nMaxChange)
{
unsigned int i;
bool bFwd;
int nDiff, nDiffTot, nEdge;
CTri *pTri, *pTriPrev, *pTmp;
unsigned int nStripLen;
// Start strip from this tri
pTri = &triFrom;
pTriPrev = NULL;
nDiffTot = 0;
nStripLen = 0;
// Start strip from this edge
nEdge = nEdgeFrom;
bFwd = true;
// Extend the strip until we run out, or we find an improvement
nDiff = 1;
while(nDiff > nMaxChange)
{
// Add pTri to the strip
assert(pTri);
nDiff += TakeTri(pTri, pTriPrev, bFwd);
assert(nStripLen < m_nTriCnt);
m_psStrip[nStripLen++] = pTri;
// Jump to next tri
pTriPrev = pTri;
pTri = pTri->pAdj[nEdge];
if(!pTri)
break; // No more tris, gotta stop
if(pTri->bInStrip)
break; // No more tris, gotta stop
// Find which edge we came over
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
// Find the edge to leave over
if(bFwd)
{
if(--nEdge < 0)
nEdge = 2;
}
else
{
if(++nEdge > 2)
nEdge = 0;
}
// Swap the winding order for the next tri
bFwd = !bFwd;
}
assert(!pTriPrev->sNew.pFwd);
/*
Accept or reject this strip.
Accepting changes which don't change the number of strips
adds variety, which can help better strips to develop.
*/
if(nDiff <= nMaxChange)
{
nDiffTot += nDiff;
// Great, take the Strip
for(i = 0; i < nStripLen; ++i)
{
pTri = m_psStrip[i];
assert(pTri->bInStrip);
// Cement affected tris
pTmp = pTri->sOld.pFwd;
if(pTmp && !pTmp->bInStrip)
{
if(pTmp->sOld.pFwd && !pTmp->sOld.pFwd->bInStrip)
pTmp->sOld.pFwd->Cement();
pTmp->Cement();
}
pTmp = pTri->sOld.pRev;
if(pTmp && !pTmp->bInStrip)
{
pTmp->Cement();
}
// Cement this tris
pTri->bInStrip = false;
pTri->Cement();
}
}
else
{
// Shame, undo the strip
for(i = 0; i < nStripLen; ++i)
{
pTri = m_psStrip[i];
assert(pTri->bInStrip);
// Undo affected tris
pTmp = pTri->sOld.pFwd;
if(pTmp && !pTmp->bInStrip)
{
if(pTmp->sOld.pFwd && !pTmp->sOld.pFwd->bInStrip)
pTmp->sOld.pFwd->Undo();
pTmp->Undo();
}
pTmp = pTri->sOld.pRev;
if(pTmp && !pTmp->bInStrip)
{
pTmp->Undo();
}
// Undo this tris
pTri->bInStrip = false;
pTri->Undo();
}
}
#ifdef _DEBUG
for(int nDbg = 0; nDbg < (int)m_nTriCnt; ++nDbg)
{
assert(m_pTri[nDbg].bInStrip == false);
assert(m_pTri[nDbg].bOutput == false);
assert(m_pTri[nDbg].sOld.pRev == m_pTri[nDbg].sNew.pRev);
assert(m_pTri[nDbg].sOld.pFwd == m_pTri[nDbg].sNew.pFwd);
if(m_pTri[nDbg].sNew.pRev)
{
assert(m_pTri[nDbg].sNew.pRev->sNew.pFwd == &m_pTri[nDbg]);
}
if(m_pTri[nDbg].sNew.pFwd)
{
assert(m_pTri[nDbg].sNew.pFwd->sNew.pRev == &m_pTri[nDbg]);
}
}
#endif
if(nDiffTot)
{
m_nStrips += nDiffTot;
return true;
}
return false;
}
/*!***************************************************************************
@Function StripFromEdges
@Description Creates a strip from the object's edge information.
*****************************************************************************/
void CStrip::StripFromEdges()
{
unsigned int i, j, nTest;
CTri *pTri, *pTriPrev;
int nEdge = 0;
/*
Attempt to create grid-oriented strips.
*/
for(i = 0; i < m_nTriCnt; ++i)
{
pTri = &m_pTri[i];
// Count the number of empty edges
nTest = 0;
for(j = 0; j < 3; ++j)
{
if(!pTri->pAdj[j])
{
++nTest;
}
else
{
nEdge = j;
}
}
if(nTest != 2)
continue;
for(;;)
{
// A tri with two empty edges is a corner (there are other corners too, but this works so...)
while(StripGrow(*pTri, nEdge, -1)) {};
pTriPrev = pTri;
pTri = pTri->pAdj[nEdge];
if(!pTri)
break;
// Find the edge we came over
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
// Step around to the next edge
if(++nEdge > 2)
nEdge = 0;
pTriPrev = pTri;
pTri = pTri->pAdj[nEdge];
if(!pTri)
break;
// Find the edge we came over
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
// Step around to the next edge
if(--nEdge < 0)
nEdge = 2;
#if 0
// If we're not tracking the edge, give up
nTest = nEdge - 1;
if(nTest < 0)
nTest = 2;
if(pTri->pAdj[nTest])
break;
else
continue;
#endif
}
}
}
#ifdef RND_TRIS_ORDER
struct pair
{
unsigned int i, o;
};
static int compare(const void *arg1, const void *arg2)
{
return ((pair*)arg1)->i - ((pair*)arg2)->i;
}
#endif
/*!***************************************************************************
@Function StripImprove
@Description Optimises the strip
*****************************************************************************/
void CStrip::StripImprove()
{
unsigned int i, j;
bool bChanged;
int nRepCnt, nChecks;
int nMaxChange;
#ifdef RND_TRIS_ORDER
pair *pnOrder;
/*
Create a random order to process the tris
*/
pnOrder = new pair[m_nTriCnt];
#endif
nRepCnt = 0;
nChecks = 2;
nMaxChange = 0;
/*
Reduce strip count by growing each of the three strips each tri can start.
*/
while(nChecks)
{
--nChecks;
bChanged = false;
#ifdef RND_TRIS_ORDER
/*
Create a random order to process the tris
*/
for(i = 0; i < m_nTriCnt; ++i)
{
pnOrder[i].i = rand() * rand();
pnOrder[i].o = i;
}
qsort(pnOrder, m_nTriCnt, sizeof(*pnOrder), compare);
#endif
/*
Process the tris
*/
for(i = 0; i < m_nTriCnt; ++i)
{
for(j = 0; j < 3; ++j)
{
#ifdef RND_TRIS_ORDER
bChanged |= StripGrow(m_pTri[pnOrder[i].o], j, nMaxChange);
#else
bChanged |= StripGrow(m_pTri[i], j, nMaxChange);
#endif
}
}
++nRepCnt;
// Check the results once or twice
if(bChanged)
nChecks = 2;
nMaxChange = (nMaxChange == 0 ? -1 : 0);
}
#ifdef RND_TRIS_ORDER
delete [] pnOrder;
#endif
//_RPT1(_CRT_WARN, "Reps: %d\n", nRepCnt);
}
/*!***************************************************************************
@Function Output
@Output ppui32Strips
@Output ppnStripLen The length of the strip
@Output pnStripCnt
@Description Outputs key information about the strip to the output
parameters.
*****************************************************************************/
void CStrip::Output(
unsigned int **ppui32Strips,
unsigned int **ppnStripLen,
unsigned int *pnStripCnt)
{
unsigned int *pui32Strips;
unsigned int *pnStripLen;
unsigned int i, j, nIdx, nStrip;
CTri *pTri;
/*
Output Strips
*/
pnStripLen = (unsigned int*)malloc(m_nStrips * sizeof(*pnStripLen));
pui32Strips = (unsigned int*)malloc((m_nTriCnt + m_nStrips * 2) * sizeof(*pui32Strips));
nStrip = 0;
nIdx = 0;
for(i = 0; i < m_nTriCnt; ++i)
{
pTri = &m_pTri[i];
if(pTri->sNew.pRev)
continue;
assert(!pTri->sNew.pFwd || pTri->sNew.bWindFwd);
assert(pTri->bOutput == false);
if(!pTri->sNew.pFwd)
{
pui32Strips[nIdx++] = pTri->pIdx[0];
pui32Strips[nIdx++] = pTri->pIdx[1];
pui32Strips[nIdx++] = pTri->pIdx[2];
pnStripLen[nStrip] = 1;
pTri->bOutput = true;
}
else
{
if(pTri->sNew.pFwd == pTri->pAdj[0])
{
pui32Strips[nIdx++] = pTri->pIdx[2];
pui32Strips[nIdx++] = pTri->pIdx[0];
}
else if(pTri->sNew.pFwd == pTri->pAdj[1])
{
pui32Strips[nIdx++] = pTri->pIdx[0];
pui32Strips[nIdx++] = pTri->pIdx[1];
}
else
{
assert(pTri->sNew.pFwd == pTri->pAdj[2]);
pui32Strips[nIdx++] = pTri->pIdx[1];
pui32Strips[nIdx++] = pTri->pIdx[2];
}
pnStripLen[nStrip] = 0;
do
{
assert(pTri->bOutput == false);
// Increment tris-in-this-strip counter
++pnStripLen[nStrip];
// Output the new vertex index
for(j = 0; j < 3; ++j)
{
if(
(pui32Strips[nIdx-2] != pTri->pIdx[j]) &&
(pui32Strips[nIdx-1] != pTri->pIdx[j]))
{
break;
}
}
assert(j != 3);
pui32Strips[nIdx++] = pTri->pIdx[j];
// Double-check that the previous three indices are the indices of this tris (in some order)
assert(
((pui32Strips[nIdx-3] == pTri->pIdx[0]) && (pui32Strips[nIdx-2] == pTri->pIdx[1]) && (pui32Strips[nIdx-1] == pTri->pIdx[2])) ||
((pui32Strips[nIdx-3] == pTri->pIdx[1]) && (pui32Strips[nIdx-2] == pTri->pIdx[2]) && (pui32Strips[nIdx-1] == pTri->pIdx[0])) ||
((pui32Strips[nIdx-3] == pTri->pIdx[2]) && (pui32Strips[nIdx-2] == pTri->pIdx[0]) && (pui32Strips[nIdx-1] == pTri->pIdx[1])) ||
((pui32Strips[nIdx-3] == pTri->pIdx[2]) && (pui32Strips[nIdx-2] == pTri->pIdx[1]) && (pui32Strips[nIdx-1] == pTri->pIdx[0])) ||
((pui32Strips[nIdx-3] == pTri->pIdx[1]) && (pui32Strips[nIdx-2] == pTri->pIdx[0]) && (pui32Strips[nIdx-1] == pTri->pIdx[2])) ||
((pui32Strips[nIdx-3] == pTri->pIdx[0]) && (pui32Strips[nIdx-2] == pTri->pIdx[2]) && (pui32Strips[nIdx-1] == pTri->pIdx[1])));
// Check that the latest three indices are not degenerate
assert(pui32Strips[nIdx-1] != pui32Strips[nIdx-2]);
assert(pui32Strips[nIdx-1] != pui32Strips[nIdx-3]);
assert(pui32Strips[nIdx-2] != pui32Strips[nIdx-3]);
pTri->bOutput = true;
// Check that the next triangle is adjacent to this triangle
assert(
(pTri->sNew.pFwd == pTri->pAdj[0]) ||
(pTri->sNew.pFwd == pTri->pAdj[1]) ||
(pTri->sNew.pFwd == pTri->pAdj[2]) ||
(!pTri->sNew.pFwd));
// Check that this triangle is adjacent to the next triangle
assert(
(!pTri->sNew.pFwd) ||
(pTri == pTri->sNew.pFwd->pAdj[0]) ||
(pTri == pTri->sNew.pFwd->pAdj[1]) ||
(pTri == pTri->sNew.pFwd->pAdj[2]));
pTri = pTri->sNew.pFwd;
} while(pTri);
}
++nStrip;
}
assert(nIdx == m_nTriCnt + m_nStrips * 2);
assert(nStrip == m_nStrips);
// Check all triangles have been output
for(i = 0; i < m_nTriCnt; ++i)
{
assert(m_pTri[i].bOutput == true);
}
// Check all triangles are present
j = 0;
for(i = 0; i < m_nStrips; ++i)
{
j += pnStripLen[i];
}
assert(j == m_nTriCnt);
// Output data
*pnStripCnt = m_nStrips;
*ppui32Strips = pui32Strips;
*ppnStripLen = pnStripLen;
}
/****************************************************************************
** Code
****************************************************************************/
/*!***************************************************************************
@Function PVRTTriStrip
@Output ppui32Strips
@Output ppnStripLen
@Output pnStripCnt
@Input pui32TriList
@Input nTriCnt
@Description Reads a triangle list and generates an optimised triangle strip.
*****************************************************************************/
void PVRTTriStrip(
unsigned int **ppui32Strips,
unsigned int **ppnStripLen,
unsigned int *pnStripCnt,
const unsigned int * const pui32TriList,
const unsigned int nTriCnt)
{
unsigned int *pui32Strips;
unsigned int *pnStripLen;
unsigned int nStripCnt;
/*
If the order in which triangles are tested as strip roots is
randomised, then several attempts can be made. Use the best result.
*/
for(int i = 0; i <
#ifdef RND_TRIS_ORDER
5
#else
1
#endif
; ++i)
{
CStrip stripper(pui32TriList, nTriCnt);
#ifdef RND_TRIS_ORDER
srand(i);
#endif
stripper.StripFromEdges();
stripper.StripImprove();
stripper.Output(&pui32Strips, &pnStripLen, &nStripCnt);
if(!i || nStripCnt < *pnStripCnt)
{
if(i)
{
FREE(*ppui32Strips);
FREE(*ppnStripLen);
}
*ppui32Strips = pui32Strips;
*ppnStripLen = pnStripLen;
*pnStripCnt = nStripCnt;
}
else
{
FREE(pui32Strips);
FREE(pnStripLen);
}
}
}
/*!***************************************************************************
@Function PVRTTriStripList
@Modified pui32TriList
@Input nTriCnt
@Description Reads a triangle list and generates an optimised triangle strip.
Result is converted back to a triangle list.
*****************************************************************************/
void PVRTTriStripList(unsigned int * const pui32TriList, const unsigned int nTriCnt)
{
unsigned int *pui32Strips;
unsigned int *pnStripLength;
unsigned int nNumStrips;
unsigned int *pui32TriPtr, *pui32StripPtr;
/*
Strip the geometry
*/
PVRTTriStrip(&pui32Strips, &pnStripLength, &nNumStrips, pui32TriList, nTriCnt);
/*
Convert back to a triangle list
*/
pui32StripPtr = pui32Strips;
pui32TriPtr = pui32TriList;
for(unsigned int i = 0; i < nNumStrips; ++i)
{
*pui32TriPtr++ = *pui32StripPtr++;
*pui32TriPtr++ = *pui32StripPtr++;
*pui32TriPtr++ = *pui32StripPtr++;
for(unsigned int j = 1; j < pnStripLength[i]; ++j)
{
// Use two indices from previous triangle, flipping tri order alternately.
if(j & 0x01)
{
*pui32TriPtr++ = pui32StripPtr[-1];
*pui32TriPtr++ = pui32StripPtr[-2];
}
else
{
*pui32TriPtr++ = pui32StripPtr[-2];
*pui32TriPtr++ = pui32StripPtr[-1];
}
*pui32TriPtr++ = *pui32StripPtr++;
}
}
free(pui32Strips);
free(pnStripLength);
}
/*****************************************************************************
End of file (PVRTTriStrip.cpp)
*****************************************************************************/
@@ -1,57 +0,0 @@
/*
* 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.
*
*/
/*!****************************************************************************
@file PVRTTriStrip.h
@copyright Copyright (c) Imagination Technologies Limited.
@brief Strips a triangle list.
******************************************************************************/
#ifndef _PVRTTRISTRIP_H_
#define _PVRTTRISTRIP_H_
/****************************************************************************
** Declarations
****************************************************************************/
/*!***************************************************************************
@brief Reads a triangle list and generates an optimised triangle strip.
@param[out] ppui32Strips
@param[out] ppnStripLen
@param[out] pnStripCnt
@param[in] pui32TriList
@param[in] nTriCnt
*****************************************************************************/
void PVRTTriStrip(
unsigned int** ppui32Strips,
unsigned int** ppnStripLen,
unsigned int* pnStripCnt,
const unsigned int* const pui32TriList,
const unsigned int nTriCnt);
/*!***************************************************************************
@brief Reads a triangle list and generates an optimised triangle strip. Result is
converted back to a triangle list.
@param[in,out] pui32TriList
@param[in] nTriCnt
*****************************************************************************/
void PVRTTriStripList(unsigned int* const pui32TriList, const unsigned int nTriCnt);
#endif /* _PVRTTRISTRIP_H_ */
/*****************************************************************************
End of file (PVRTTriStrip.h)
*****************************************************************************/
@@ -1,13 +0,0 @@
#
# 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.
#
set(FILES
)
@@ -1,823 +0,0 @@
/*
* 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 <platform.h>
#include <Cry_Vector2.h>
#include <Cry_Vector3.h>
#include "TangentSpaceCalculation.h"
#include <mikkelsen/mikktspace.h>
struct SMikkVertex
{
Vec3 pos, norm, tang, bitang;
Vec2 magST;
Vec2 texc;
};
struct SMikkFace
{
int vertexOffset;
int nrOriginalFace;
};
struct SMikkMesh
{
std::vector<SMikkVertex> mikkVerts;
std::vector<SMikkFace> mikkFaces;
int mikkNumFaces;
};
CTangentSpaceCalculation::CBase33::CBase33()
{
}
CTangentSpaceCalculation::CBase33::CBase33(const Vec3& Uval, const Vec3& Vval, const Vec3& Nval)
{
u = Uval;
v = Vval;
n = Nval;
}
bool CTangentSpaceCalculation::CVec3PredicateLess::operator() (const Vec3& first, const Vec3& second) const
{
if (first.x < second.x)
{
return true;
}
if (first.x > second.x)
{
return false;
}
if (first.y < second.y)
{
return true;
}
if (first.y > second.y)
{
return false;
}
return (first.z < second.z);
}
bool CTangentSpaceCalculation::CBase33PredicateLess::operator() (const CBase33& first, const CBase33& second) const
{
if (first.n.x < second.n.x)
{
return true;
}
if (first.n.x > second.n.x)
{
return false;
}
if (first.n.y < second.n.y)
{
return true;
}
if (first.n.y > second.n.y)
{
return false;
}
if (first.n.z < second.n.z)
{
return true;
}
if (first.n.z > second.n.z)
{
return false;
}
if (first.u.x < second.u.x)
{
return true;
}
if (first.u.x > second.u.x)
{
return false;
}
if (first.u.y < second.u.y)
{
return true;
}
if (first.u.y > second.u.y)
{
return false;
}
if (first.u.z < second.u.z)
{
return true;
}
if (first.u.z > second.u.z)
{
return false;
}
if (first.v.x < second.v.x)
{
return true;
}
if (first.v.x > second.v.x)
{
return false;
}
if (first.v.y < second.v.y)
{
return true;
}
if (first.v.y > second.v.y)
{
return false;
}
return first.v.z < second.v.z;
}
bool CTangentSpaceCalculation::CBaseIndexOrder::operator() (const CBaseIndex& a, const CBaseIndex& b) const
{
// first sort by position
if (a.m_posIndex < b.m_posIndex)
{
return true;
}
if (a.m_posIndex > b.m_posIndex)
{
return false;
}
// then by normal
if (a.m_normIndex < b.m_normIndex)
{
return true;
}
if (a.m_normIndex > b.m_normIndex)
{
return false;
}
return false;
}
float CTangentSpaceCalculation::CalcAngleBetween(const Vec3& invA, const Vec3& invB)
{
double LengthQ = sqrt(invA.len2() * invB.len2());
// to prevent division by zero
if (LengthQ < 0.00000001)
{
LengthQ = 0.00000001;
}
double f = invA.Dot(invB) / LengthQ;
// acos_tpl need input in the range [-1..1]
if (f > 1.0f)
{
f = 1.0f;
}
else if (f < -1.0f)
{
f = -1.0f;
}
// cosf is not available on every platform
float fRet = (float)acos_tpl(f);
return fRet;
}
void CTangentSpaceCalculation::DebugMesh(const ITriangleInputProxy& proxy) const
{
uint32 dwTriCount = proxy.GetTriangleCount();
// search for polygons that use the same indices (input data problems)
for (uint32 a = 0; a < dwTriCount; a++)
{
uint32 dwAPos[3], dwANorm[3], dwAUV[3];
proxy.GetTriangleIndices(a, dwAPos, dwANorm, dwAUV);
for (uint32 b = a + 1; b < dwTriCount; b++)
{
uint32 dwBPos[3], dwBNorm[3], dwBUV[3];
proxy.GetTriangleIndices(b, dwBPos, dwBNorm, dwBUV);
assert(!(dwAPos[0] == dwBPos[0] && dwAPos[1] == dwBPos[1] && dwAPos[2] == dwBPos[2]));
assert(!(dwAPos[1] == dwBPos[0] && dwAPos[2] == dwBPos[1] && dwAPos[0] == dwBPos[2]));
assert(!(dwAPos[2] == dwBPos[0] && dwAPos[0] == dwBPos[1] && dwAPos[1] == dwBPos[2]));
assert(!(dwAPos[1] == dwBPos[0] && dwAPos[0] == dwBPos[1] && dwAPos[2] == dwBPos[2]));
assert(!(dwAPos[2] == dwBPos[0] && dwAPos[1] == dwBPos[1] && dwAPos[0] == dwBPos[2]));
assert(!(dwAPos[0] == dwBPos[0] && dwAPos[2] == dwBPos[1] && dwAPos[1] == dwBPos[2]));
}
}
}
Vec3 CTangentSpaceCalculation::Rotate(const Vec3& vFrom, const Vec3& vTo, const Vec3& vInput)
{
// no mesh is perfect
// assert(IsNormalized(vFrom));
// no mesh is perfect
// assert(IsNormalized(vTo));
// rotation axis
Vec3 vRotAxis = vFrom.cross(vTo);
float fSin = vRotAxis.len();
float fCos = vFrom.Dot(vTo);
// no rotation
if (fSin < 0.00001f)
{
return vInput;
}
// normalize
vRotAxis = vRotAxis * (1.0f / fSin);
// perpendicular to vRotAxis and vFrom90deg
Vec3 vFrom90deg = (vRotAxis.cross(vFrom)).normalize();
// Base is vFrom,vFrom90deg,vRotAxis
float fXInPlane = vFrom.Dot(vInput);
float fYInPlane = vFrom90deg.Dot(vInput);
Vec3 a = vFrom * (fXInPlane * fCos - fYInPlane * fSin);
Vec3 b = vFrom90deg * (fXInPlane * fSin + fYInPlane * fCos);
Vec3 c = vRotAxis * (vRotAxis.Dot(vInput));
return a + b + c;
}
eCalculateTangentSpaceErrorCode CTangentSpaceCalculation::CalculateTangentSpace(const ITriangleInputProxy& inInput, const bool bUseCustomNormals, string& errorMessage)
{
if (bUseCustomNormals)
{
return CalculateTangentSpaceMikk(inInput, errorMessage);
}
uint32 dwTriCount = inInput.GetTriangleCount();
// not a number in texture coordinates
bool bTextureCoordinatesBroken = false;
// clear result
m_baseVectors.clear();
m_trianglesBaseAssigment.clear();
m_trianglesBaseAssigment.reserve(dwTriCount);
assert(m_baseVectors.empty());
assert(m_trianglesBaseAssigment.empty());
// second=index into m_BaseVectors, generated output data
std::multimap<CBaseIndex, uint32, CBaseIndexOrder> mBaseMap;
// base vectors per triangle
std::vector<CBase33> vTriangleBase;
// calculate the base vectors per triangle -------------------------------------------
{
eCalculateTangentSpaceErrorCode errorCode = CALCULATE_TANGENT_SPACE_NO_ERRORS;
for (uint32 i = 0; i < dwTriCount; i++)
{
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
Vec3 vPos[3];
Vec2 vUV[3];
for (int e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
inInput.GetUV(dwUV[e], vUV[e]);
}
// calculate tangent vectors ---------------------------
Vec3 vA = vPos[1] - vPos[0];
Vec3 vB = vPos[2] - vPos[0];
Vec3 vC = vPos[2] - vPos[1];
if (vA.IsZero())
{
//vert 0 and 1 have the same coordinates
errorMessage.Format("Vertices 0 and 1 have the same coordinate: (%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
if (vB.IsZero())
{
//vert 2 and 0 have the same coordinates
errorMessage.Format("Vertices 2 and 0 have the same coordinate: (%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
if (vC.IsZero())
{
//vert 2 and 1 have the same coordinates
errorMessage.Format("Vertices 2 and 1 have the same coordinate: (%f : %f : %f)\n", vPos[1].x, vPos[1].y, vPos[1].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
float fDeltaU1 = vUV[1].x - vUV[0].x;
float fDeltaU2 = vUV[2].x - vUV[0].x;
float fDeltaV1 = vUV[1].y - vUV[0].y;
float fDeltaV2 = vUV[2].y - vUV[0].y;
float div = (fDeltaU1 * fDeltaV2 - fDeltaU2 * fDeltaV1);
if (_isnan(div))
{
errorMessage.Format("Vertices 0,1,2 have broken texture coordinates v0:(%f : %f : %f) v1:(%f : %f : %f) v2:(%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z, vPos[1].x, vPos[1].y, vPos[1].z, vPos[2].x, vPos[2].y, vPos[2].z);
bTextureCoordinatesBroken = true;
div = 0.0f;
}
Vec3 vU, vV, vN = (vA.cross(vB)).normalize();
if (div != 0.0)
{
// 2D triangle area = (u1*v2-u2*v1)/2
float a = fDeltaV2; // /div was removed - no required because of normalize()
float b = -fDeltaV1;
float c = -fDeltaU2;
float d = fDeltaU1;
// /fAreaMul2*fAreaMul2 was optimized away -> small triangles in UV should contribute less and
// less artifacts (no divide and multiply)
vU = (vA * a + vB * b) * fsgnf(div);
vV = (vA * c + vB * d) * fsgnf(div);
}
else
{
vU = Vec3(1, 0, 0);
vV = Vec3(0, 1, 0);
}
vTriangleBase.push_back(CBase33(vU, vV, vN));
}
if (errorCode != CALCULATE_TANGENT_SPACE_NO_ERRORS)
{
return errorCode;
}
}
// distribute the normals to the vertices
{
// we create a new tangent base for every vertex index that has a different normal (later we split further for mirrored use)
// and sum the base vectors (weighted by angle and mirrored if necessary)
for (uint32 i = 0; i < dwTriCount; i++)
{
uint32 e;
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
CBase33 TriBase = vTriangleBase[i];
Vec3 vPos[3];
for (e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
}
// for each triangle vertex
for (e = 0; e < 3; e++)
{
// weight by angle to fix the L-Shape problem
float fWeight = CalcAngleBetween(vPos[(e + 2) % 3] - vPos[e], vPos[(e + 1) % 3] - vPos[e]);
if (fWeight <= 0.0f)
{
fWeight = 0.0001f;
}
AddNormal2Base(mBaseMap, dwPos[e], dwNorm[e], TriBase.n * fWeight);
}
}
}
// distribute the uv vectors to the vertices
{
// we create a new tangent base for every vertex index that has a different normal
// if the base vectors does'nt fit we split as well
for (uint32 i = 0; i < dwTriCount; i++)
{
uint32 e;
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
CTriBaseIndex Indx;
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
CBase33 TriBase = vTriangleBase[i];
Vec3 vPos[3];
for (e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
}
// for each triangle vertex
for (e = 0; e < 3; e++)
{
// weight by angle to fix the L-Shape problem
float fWeight = CalcAngleBetween(vPos[(e + 2) % 3] - vPos[e], vPos[(e + 1) % 3] - vPos[e]);
Indx.p[e] = AddUV2Base(mBaseMap, dwPos[e], dwNorm[e], TriBase.u * fWeight, TriBase.v * fWeight, TriBase.n.normalize());
}
m_trianglesBaseAssigment.push_back(Indx);
}
}
// adjust the base vectors per vertex -------------------------------------------
{
std::vector<CBase33>::iterator it;
for (it = m_baseVectors.begin(); it != m_baseVectors.end(); ++it)
{
CBase33& ref = (*it);
// rotate u and v in n plane
{
Vec3 vUout, vVout, vNout;
vNout = ref.n;
vNout.normalize();
// project u in n plane
// project v in n plane
vUout = ref.u - vNout * (vNout.Dot(ref.u));
vVout = ref.v - vNout * (vNout.Dot(ref.v));
ref.u = vUout;
ref.u.normalize();
ref.v = vVout;
ref.v.normalize();
ref.n = vNout;
//assert(ref.u.x>=-1 && ref.u.x<=1);
//assert(ref.u.y>=-1 && ref.u.y<=1);
//assert(ref.u.z>=-1 && ref.u.z<=1);
//assert(ref.v.x>=-1 && ref.v.x<=1);
//assert(ref.v.y>=-1 && ref.v.y<=1);
//assert(ref.v.z>=-1 && ref.v.z<=1);
//assert(ref.n.x>=-1 && ref.n.x<=1);
//assert(ref.n.y>=-1 && ref.n.y<=1);
//assert(ref.n.z>=-1 && ref.n.z<=1);
}
}
}
return bTextureCoordinatesBroken ? BROKEN_TEXTURE_COORDINATES : CALCULATE_TANGENT_SPACE_NO_ERRORS;
}
uint32 CTangentSpaceCalculation::AddUV2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap,
const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inU, const Vec3& inV, const Vec3& inNormN)
{
CBaseIndex Indx;
Indx.m_posIndex = indwPosNo;
Indx.m_normIndex = indwNormNo;
std::multimap<CBaseIndex, uint32, CBaseIndexOrder>::iterator iFind, iFindEnd;
iFind = inMap.lower_bound(Indx);
assert(iFind != inMap.end());
Vec3 vNormal = m_baseVectors[(*iFind).second].n;
iFindEnd = inMap.upper_bound(Indx);
uint32 dwBaseUVIndex = 0xffffffff; // init with not found
bool bParity = inU.cross(inV).Dot(inNormN) > 0.0f;
for (; iFind != iFindEnd; ++iFind)
{
CBase33& refFound = m_baseVectors[(*iFind).second];
if (!refFound.u.IsZero())
{
bool bParityRef = refFound.u.cross(refFound.v).Dot(refFound.n) > 0.0f;
bool bParityCheck = (bParityRef == bParity);
if (!bParityCheck)
{
continue;
}
// bool bHalfAngleCheck=normalize(inU+inV) * normalize(refFound.u+refFound.v) > 0.0f;
Vec3 normRefFound = refFound.n;
normRefFound.normalize();
Vec3 uvRefSum = refFound.u + refFound.v;
uvRefSum.normalize();
Vec3 vRotHalf = Rotate(normRefFound, inNormN, uvRefSum);
Vec3 uvInSum = inU + inV;
uvInSum.normalize();
bool bHalfAngleCheck = uvInSum.Dot(vRotHalf) > 0.0f;
// bool bHalfAngleCheck=normalize(normalize(inU)+normalize(inV)) * normalize(normalize(refFound.u)+normalize(refFound.v)) > 0.0f;
if (!bHalfAngleCheck)
{
continue;
}
}
dwBaseUVIndex = (*iFind).second;
break;
}
// not found
if (dwBaseUVIndex == 0xffffffff)
{
// otherwise create a new base
CBase33 Base(Vec3(0, 0, 0), Vec3(0, 0, 0), vNormal);
dwBaseUVIndex = m_baseVectors.size();
inMap.insert(std::pair<CBaseIndex, uint32>(Indx, dwBaseUVIndex));
m_baseVectors.push_back(Base);
}
CBase33& refBaseUV = m_baseVectors[dwBaseUVIndex];
refBaseUV.u = refBaseUV.u + inU;
refBaseUV.v = refBaseUV.v + inV;
//no mesh is perfect
if (inU.x != 0.0f || inU.y != 0.0f || inU.z != 0.0f)
{
assert(refBaseUV.u.x != 0.0f || refBaseUV.u.y != 0.0f || refBaseUV.u.z != 0.0f);
}
// no mesh is perfect
if (inV.x != 0.0f || inV.y != 0.0f || inV.z != 0.0f)
{
assert(refBaseUV.v.x != 0.0f || refBaseUV.v.y != 0.0f || refBaseUV.v.z != 0.0f);
}
return dwBaseUVIndex;
}
void CTangentSpaceCalculation::AddNormal2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap, const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inNormal)
{
CBaseIndex Indx;
Indx.m_posIndex = indwPosNo;
Indx.m_normIndex = indwNormNo;
std::multimap<CBaseIndex, uint32, CBaseIndexOrder>::iterator iFind = inMap.find(Indx);
uint32 dwBaseNIndex;
if (iFind != inMap.end())
{
dwBaseNIndex = (*iFind).second;
}
else
{
CBase33 Base(Vec3(0, 0, 0), Vec3(0, 0, 0), Vec3(0, 0, 0));
dwBaseNIndex = m_baseVectors.size();
inMap.insert(std::pair<CBaseIndex, uint32>(Indx, dwBaseNIndex));
m_baseVectors.push_back(Base);
}
CBase33& refBaseN = m_baseVectors[dwBaseNIndex];
refBaseN.n = refBaseN.n + inNormal;
}
void CTangentSpaceCalculation::GetBase(const uint32 indwPos, float* outU, float* outV, float* outN)
{
CBase33& base = m_baseVectors[indwPos];
outU[0] = base.u.x;
outV[0] = base.v.x;
outN[0] = base.n.x;
outU[1] = base.u.y;
outV[1] = base.v.y;
outN[1] = base.n.y;
outU[2] = base.u.z;
outV[2] = base.v.z;
outN[2] = base.n.z;
}
void CTangentSpaceCalculation::GetTriangleBaseIndices(const uint32 indwTriNo, uint32 outdwBase[3])
{
assert(indwTriNo < m_trianglesBaseAssigment.size());
CTriBaseIndex& indx = m_trianglesBaseAssigment[indwTriNo];
for (uint32 i = 0; i < 3; i++)
{
outdwBase[i] = indx.p[i];
}
}
size_t CTangentSpaceCalculation::GetBaseCount()
{
return m_baseVectors.size();
}
static int MikkGetNumFaces(const SMikkTSpaceContext* pContext)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
return mikkMesh->mikkNumFaces;
}
static int MikkGetNumVerticesOfFace([[maybe_unused]] const SMikkTSpaceContext* pContext, [[maybe_unused]] const int iFace)
{
return 3;
}
static void MikkGetPosition(const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec3& pos = mikkMesh->mikkVerts[face.vertexOffset + iVert].pos;
fvPosOut[0] = pos.x;
fvPosOut[1] = pos.y;
fvPosOut[2] = pos.z;
}
static void MikkGetNormal(const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec3& normal = mikkMesh->mikkVerts[face.vertexOffset + iVert].norm;
fvNormOut[0] = normal.x;
fvNormOut[1] = normal.y;
fvNormOut[2] = normal.z;
}
static void MikkGetTexCoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec2& tan = mikkMesh->mikkVerts[face.vertexOffset + iVert].texc;
fvTexcOut[0] = tan.x;
fvTexcOut[1] = tan.y;
}
static void MikkSetTSpace(const SMikkTSpaceContext* pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, [[maybe_unused]] const tbool bIsOrientationPreserving, const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const int index = face.vertexOffset + iVert;
mikkMesh->mikkVerts[index].tang = Vec3(fvTangent[0], fvTangent[1], fvTangent[2]);
mikkMesh->mikkVerts[index].bitang = Vec3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]);
mikkMesh->mikkVerts[index].magST.x = fMagS;
mikkMesh->mikkVerts[index].magST.y = fMagT;
}
eCalculateTangentSpaceErrorCode CTangentSpaceCalculation::CalculateTangentSpaceMikk(const ITriangleInputProxy& proxy, string& errorMessage)
{
const uint32 numFaces = proxy.GetTriangleCount();
// prepare the working mesh for mikkelsen algorithm
// when custom normals are specified, we'll use them
SMikkMesh mikkMesh;
mikkMesh.mikkNumFaces = numFaces;
mikkMesh.mikkVerts.resize(numFaces * 3);
mikkMesh.mikkFaces.resize(numFaces);
for (uint32 f = 0; f < numFaces; ++f)
{
uint32 outdwPos[3];
uint32 outdwNorm[3];
uint32 outdwUV[3];
proxy.GetTriangleIndices(f, outdwPos, outdwNorm, outdwUV);
mikkMesh.mikkFaces[f].vertexOffset = f * 3;
mikkMesh.mikkFaces[f].nrOriginalFace = f;
for (uint32 vId = 0; vId < 3; ++vId)
{
SMikkVertex& vert(mikkMesh.mikkVerts[mikkMesh.mikkFaces[f].vertexOffset + vId]);
proxy.GetPos(outdwPos[vId], vert.pos);
proxy.GetNorm(f, vId, vert.norm);
proxy.GetUV(outdwUV[vId], vert.texc);
vert.tang = Vec3(1.0f, 0.0f, 0.0f);
vert.bitang = Vec3(0.0f, 1.0f, 0.0f);
}
}
// prepare mikkelsen interface
SMikkTSpaceInterface mikkInterface;
memset(&mikkInterface, 0, sizeof(SMikkTSpaceInterface));
mikkInterface.m_getNumFaces = MikkGetNumFaces;
mikkInterface.m_getNumVerticesOfFace = MikkGetNumVerticesOfFace;
mikkInterface.m_getPosition = MikkGetPosition;
mikkInterface.m_getNormal = MikkGetNormal;
mikkInterface.m_getTexCoord = MikkGetTexCoord;
mikkInterface.m_setTSpace = MikkSetTSpace;
SMikkTSpaceContext mikkContext;
memset(&mikkContext, 0, sizeof(SMikkTSpaceContext));
mikkContext.m_pUserData = &mikkMesh;
mikkContext.m_pInterface = &mikkInterface;
// generate tangent basis
bool res = genTangSpaceDefault(&mikkContext) != 0;
if (!res)
{
errorMessage = "Failed to allocate memory for Mikkelsen Tangent Basis algorithm.";
return MEMORY_ALLOCATION_FAILED;
}
m_baseVectors.clear();
m_trianglesBaseAssigment.clear();
m_trianglesBaseAssigment.resize(proxy.GetTriangleCount());
std::map<CBase33, int, CBase33PredicateLess> uniqueBaseVectors;
std::map<CBase33, int, CBase33PredicateLess>::const_iterator it;
// remove tangent basis duplicates and add them to the mesh
for (int f = 0; f < mikkMesh.mikkNumFaces; ++f)
{
const SMikkFace& face = mikkMesh.mikkFaces[f];
CTriBaseIndex tbi;
for (int ii = 0; ii < 3; ++ii)
{
const int index = face.vertexOffset + ii;
const SMikkVertex& vert = mikkMesh.mikkVerts[index];
CBase33 base;
base.u = vert.tang;
base.v = vert.bitang;
float fNorm[3];
MikkGetNormal(&mikkContext, &fNorm[0], face.nrOriginalFace, ii);
base.n.x = fNorm[0];
base.n.y = fNorm[1];
base.n.z = fNorm[2];
int val;
it = uniqueBaseVectors.find(base);
if (it != uniqueBaseVectors.end())
{
val = it->second;
}
else
{
val = m_baseVectors.size();
m_baseVectors.push_back(base);
uniqueBaseVectors[base] = val;
}
tbi.p[ii] = val;
}
m_trianglesBaseAssigment[face.nrOriginalFace] = tbi;
}
return CALCULATE_TANGENT_SPACE_NO_ERRORS;
}
@@ -1,112 +0,0 @@
/*
* 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.
// Description : calculated the tangent space base vector for a given mesh
// Dependencies : none
// Documentation : "How to calculate tangent base vectors.doc"
#ifndef CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_TANGENTSPACECALCULATION_H
#define CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_TANGENTSPACECALCULATION_H
#pragma once
enum eCalculateTangentSpaceErrorCode
{
CALCULATE_TANGENT_SPACE_NO_ERRORS,
BROKEN_TEXTURE_COORDINATES,
VERTICES_SHARING_COORDINATES,
ALL_VERTICES_ON_THE_SAME_VECTOR,
MEMORY_ALLOCATION_FAILED
};
class ITriangleInputProxy
{
public:
virtual ~ITriangleInputProxy(){}
virtual uint32 GetTriangleCount() const = 0;
virtual void GetTriangleIndices(const uint32 indwTriNo, uint32 outdwPos[3], uint32 outdwNorm[3], uint32 outdwUV[3]) const = 0;
virtual void GetPos(const uint32 indwPos, Vec3& outfPos) const = 0;
virtual void GetUV(const uint32 indwPos, Vec2& outfUV) const = 0;
virtual void GetNorm(const uint32 indwTriNo, const uint32 indwVertNo, Vec3& outfNorm) const = 0;
};
class CTangentSpaceCalculation
{
public:
//! /param inInput - the normals are only used as smoothing input - we calculate the normals ourself
eCalculateTangentSpaceErrorCode CalculateTangentSpace(const ITriangleInputProxy& inInput, const bool bUseCustomNormals, string& errorMessage);
size_t GetBaseCount();
void GetTriangleBaseIndices(const uint32 indwTriNo, uint32 outdwBase[3]);
//! returns a orthogonal base (perpendicular and normalized)
void GetBase(const uint32 indwPos, float* outU, float* outV, float* outN);
private:
struct CBase33
{
CBase33();
CBase33(const Vec3& Uval, const Vec3& Vval, const Vec3& Nval);
Vec3 u;
Vec3 v;
Vec3 n; // part of the tangent base but can be used also as vertex normal
};
struct CVec3PredicateLess
{
bool operator() (const Vec3& first, const Vec3& second) const;
};
struct CBase33PredicateLess
{
bool operator() (const CBase33& first, const CBase33& second) const;
};
struct CBaseIndex
{
// position index in the positions stream
uint32 m_posIndex;
// normal index in the vertex normals stream
uint32 m_normIndex;
};
struct CBaseIndexOrder
{
bool operator() (const CBaseIndex& a, const CBaseIndex& b) const;
};
struct CTriBaseIndex
{
uint32 p[3]; //!< index in m_BaseVectors
};
// [dwTriangleCount]
std::vector<CTriBaseIndex> m_trianglesBaseAssigment;
// [0..] generated output data
std::vector<CBase33> m_baseVectors;
eCalculateTangentSpaceErrorCode CalculateTangentSpaceMikk(const ITriangleInputProxy& inInput, string& errorMessage);
CBase33& GetBase(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap, const uint32 indwPosNo, const uint32 indwNormNo);
uint32 AddUV2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap, const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inU, const Vec3& inV, const Vec3& inNormN);
void AddNormal2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap, const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inNormal);
Vec3 Rotate(const Vec3& vFrom, const Vec3& vTo, const Vec3& vInput);
void DebugMesh(const ITriangleInputProxy& inInput) const;
float CalcAngleBetween(const Vec3& invA, const Vec3& invB);
};
#endif // CRYINCLUDE_CRY3DENGINE_MESHCOMPILER_TANGENTSPACECALCULATION_H

Some files were not shown because too many files have changed in this diff Show More