#include "stdafx.h"
#include "EnvironmentMeshObject.h"
#include "MapTile.h"
#include "MapUtils.h"
EnvironmentMeshObject::EnvironmentMeshObject()
{
}
void EnvironmentMeshObject::ConfigureLava()
{
mIsWater = false;
const float DFLT_SURF_LEVEL = 0.82f;
const float DFLT_SURF_TRANSLUCENCY = 1.0f;
const float DFLT_SURF_WAVE_WIDTH = 0.76f;
const float DFLT_SURF_WAVE_HEIGHT = 0.15f;
const float DFLT_SURF_WAVE_FREQ = 2.5f;
Params params;
{
params.mTranslucency = DFLT_SURF_TRANSLUCENCY;
params.mWaveWidth = DFLT_SURF_WAVE_WIDTH;
params.mWaveHeight = DFLT_SURF_WAVE_HEIGHT;
params.mWaveFreq = DFLT_SURF_WAVE_FREQ;
params.mWaterlineHeight = DFLT_SURF_LEVEL;
}
// debug info
mDebugColor = mIsWater ? COLOR_CYAN : COLOR_ORANGE;
ConfigureParams(params);
}
void EnvironmentMeshObject::ConfigureWater()
{
mIsWater = true;
const float DFLT_SURF_LEVEL = 0.92f;
const float DFLT_SURF_TRANSLUCENCY = 0.65f;
const float DFLT_SURF_WAVE_WIDTH = 0.0f;
const float DFLT_SURF_WAVE_HEIGHT = 0.0f;
const float DFLT_SURF_WAVE_FREQ = 2.4f;
Params params;
{
params.mTranslucency = DFLT_SURF_TRANSLUCENCY;
params.mWaveWidth = DFLT_SURF_WAVE_WIDTH;
params.mWaveHeight = DFLT_SURF_WAVE_HEIGHT;
params.mWaveFreq = DFLT_SURF_WAVE_FREQ;
params.mWaterlineHeight = DFLT_SURF_LEVEL;
}
// debug info
mDebugColor = mIsWater ? COLOR_CYAN : COLOR_ORANGE;
ConfigureParams(params);
}
void EnvironmentMeshObject::ConfigureParams(const Params& params)
{
mParams = params;
}
void EnvironmentMeshObject::ConfigureMapTiles(cxx::span mapTiles)
{
if (mapTiles.empty())
{
RemoveMapTiles();
return;
}
// remove old
const size_t prevSize = mCoveredTiles.size();
cxx::erase_if(mCoveredTiles, [&mapTiles](const MapTile* srcTile)
{
return !cxx::contains(mapTiles, srcTile);
});
if (prevSize != mCoveredTiles.size())
{
InvalidateMesh();
}
// add new
for (MapTile* roller: mapTiles)
{
if (!cxx::contains(mCoveredTiles, roller))
{
mCoveredTiles.push_back(roller);
InvalidateMesh();
}
}
RefreshLocalBounds();
}
void EnvironmentMeshObject::AppendMapTiles(cxx::span mapTiles)
{
for (MapTile* roller: mapTiles)
{
if (!cxx::contains(mCoveredTiles, roller))
{
mCoveredTiles.push_back(roller);
InvalidateMesh();
}
}
RefreshLocalBounds();
}
void EnvironmentMeshObject::RemoveMapTiles(cxx::span mapTiles)
{
// remove old
const size_t prevSize = mCoveredTiles.size();
cxx::erase_if(mCoveredTiles, [&mapTiles](const MapTile* srcTile)
{
return !cxx::contains(mapTiles, srcTile);
});
if (prevSize != mCoveredTiles.size())
{
InvalidateMesh();
}
RefreshLocalBounds();
}
void EnvironmentMeshObject::RemoveMapTiles()
{
if (mCoveredTiles.empty())
return;
mCoveredTiles.clear();
InvalidateMesh();
RefreshLocalBounds();
}
void EnvironmentMeshObject::OnRecycle()
{
SceneObject::OnRecycle();
mMeshDirty = false;
mCoveredTiles.clear();
mMeshTris.clear();
mMeshVerts.clear();
mGpuVertexBuffer.reset();
mGpuIndexBuffer.reset();
}
void EnvironmentMeshObject::UpdateFrame(float deltaTime)
{
// animate waves
mParams.mWaveTime += mParams.mWaveFreq * deltaTime;
}
void EnvironmentMeshObject::RegisterForRendering(SceneRenderLists& renderLists, float distanceToCamera2)
{
if (mCoveredTiles.empty()) return;
eRenderPass targetPass = (mParams.mTranslucency < 1.0f) ? eRenderPass_Translucent : eRenderPass_Opaque;
renderLists.Register(targetPass, this, distanceToCamera2);
}
void EnvironmentMeshObject::PrepareRenderdata()
{
if (!mMeshDirty)
return;
mMeshDirty = false;
ReBuildMesh();
if (mMeshTris.empty() || mMeshVerts.empty())
return;
// uploading tris to gpu
const unsigned int Sizeof_Triangle = sizeof(glm::ivec3);
const unsigned int TrisBufferLength = Sizeof_Triangle * mMeshTris.size();
// allocate new
if (!mGpuIndexBuffer)
{
mGpuIndexBuffer = gRenderDevice.CreateIndexBuffer(eBufferUsage_Static, TrisBufferLength);
cxx_assert(mGpuIndexBuffer);
if (mGpuIndexBuffer && !mGpuIndexBuffer->SubData(0, TrisBufferLength, mMeshTris.data()))
{
cxx_assert(false);
}
}
// refresh
else if (!mGpuIndexBuffer->Create(eBufferUsage_Static, TrisBufferLength, mMeshTris.data()))
{
cxx_assert(false);
}
// uploading verts to gpu
const unsigned int VertsBufferLength = Sizeof_WaterLavaVertex * mMeshVerts.size();
// allocate new
if (!mGpuVertexBuffer)
{
mGpuVertexBuffer = gRenderDevice.CreateVertexBuffer(eBufferUsage_Static, VertsBufferLength);
if (mGpuVertexBuffer && mGpuVertexBuffer->SubData(0, VertsBufferLength, mMeshVerts.data()))
{
mGpuVertexBuffer->ConfigureVertexDefinitions(WaterLavaVertex3D_Format::Get());
}
else
{
cxx_assert(false);
}
}
// refresh
else if (!mGpuVertexBuffer->Create(eBufferUsage_Static, VertsBufferLength, mMeshVerts.data()))
{
cxx_assert(false);
}
}
void EnvironmentMeshObject::RefreshLocalBounds()
{
bool wasActive = IsObjectActive();
// force apply bounds
SetObjectActive(false);
cxx::aabbox bounds;
bounds.set_to_zero();
if (!mCoveredTiles.empty())
{
MapPoint2D minTilePos = mCoveredTiles.front()->mLocation;
MapPoint2D maxTilePos = mCoveredTiles.front()->mLocation;
for (MapTile* currentTile: mCoveredTiles)
{
minTilePos.x = std::min(minTilePos.x, currentTile->mLocation.x);
minTilePos.y = std::min(minTilePos.y, currentTile->mLocation.y);
maxTilePos.x = std::max(maxTilePos.x, currentTile->mLocation.x);
maxTilePos.y = std::max(maxTilePos.y, currentTile->mLocation.y);
}
bounds = MapUtils::ComputeBlockBounds(minTilePos);
bounds.extend(MapUtils::ComputeBlockBounds(maxTilePos));
}
SetLocalBoundingBox(bounds);
SetObjectActive(wasActive);
}
void EnvironmentMeshObject::InvalidateMesh()
{
mMeshDirty = true;
}
void EnvironmentMeshObject::ReBuildMesh()
{
// process data
mMeshTris.clear();
mMeshVerts.clear();
if (mCoveredTiles.empty())
return;
// generate geometry
for (MapTile* tile : mCoveredTiles)
{
const MapPoint2D& mapPos = tile->mLocation;
const glm::vec3 middlep = { mapPos.x * 1.0f, 0.0f, mapPos.y * 1.0f };
const glm::vec3 positions[9] = {
{middlep.x - MAP_TILE_HALF_SIZE, middlep.y, middlep.z - MAP_TILE_HALF_SIZE},
{middlep.x, middlep.y, middlep.z - MAP_TILE_HALF_SIZE},
{middlep.x + MAP_TILE_HALF_SIZE, middlep.y, middlep.z - MAP_TILE_HALF_SIZE},
{middlep.x - MAP_TILE_HALF_SIZE, middlep.y, middlep.z},
middlep,
{middlep.x + MAP_TILE_HALF_SIZE, middlep.y, middlep.z},
{middlep.x - MAP_TILE_HALF_SIZE, middlep.y, middlep.z + MAP_TILE_HALF_SIZE},
{middlep.x, middlep.y, middlep.z + MAP_TILE_HALF_SIZE},
{middlep.x + MAP_TILE_HALF_SIZE, middlep.y, middlep.z + MAP_TILE_HALF_SIZE},
};
const glm::vec2 tcoordsp = { mapPos.x * 1.0f, mapPos.y * 1.0f };
const glm::vec2 texturecoords[9] = {
tcoordsp,
{tcoordsp.x + 0.5f, tcoordsp.y},
{tcoordsp.x + 1.0f, tcoordsp.y},
{tcoordsp.x, tcoordsp.y + 0.5f},
{tcoordsp.x + 0.5f, tcoordsp.y + 0.5f},
{tcoordsp.x + 1.0, tcoordsp.y + 0.5f},
{tcoordsp.x, tcoordsp.y + 1.0f},
{tcoordsp.x + 0.5f, tcoordsp.y + 1.0f},
{tcoordsp.x + 1.0f, tcoordsp.y + 1.0f},
};
int32_t tile_vert_indices[9];
// process vertices
for (int ipoint = 0; ipoint < 9; ++ipoint)
{
const glm::vec3& pointPos = positions[ipoint];
// find same vertex
// todo: optimize
int32_t vidx = cxx::get_first_index_if(mMeshVerts, [&pointPos](const WaterLavaVertex& v)
{
return v.mPosition == pointPos;
});
if (vidx == -1)
{
// allocate new
tile_vert_indices[ipoint] = static_cast(mMeshVerts.size());
WaterLavaVertex& meshVertex = mMeshVerts.emplace_back();
meshVertex.mPosition = positions[ipoint];
meshVertex.mTexcoord = texturecoords[ipoint];
}
else
{
// reuse vertex
tile_vert_indices[ipoint] = vidx;
}
}
// create triangles
const glm::ivec3 pointindices[8] = {
{3, 4, 0}, {4, 1, 0}, // 1
{4, 2, 1}, {4, 5, 2}, // 2
{6, 4, 3}, {6, 7, 4}, // 3
{7, 8, 4}, {8, 5, 4}, // 4
};
for (const glm::ivec3& pointindex : pointindices)
{
mMeshTris.emplace_back(
tile_vert_indices[pointindex.x],
tile_vert_indices[pointindex.y],
tile_vert_indices[pointindex.z]);
}
}
int bp = 0;
}