// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "precipitation_clip_grid.h"
using namespace NLMISC;
using namespace NL3D;
//CHeightGrid HeightGrid;
extern UVisualCollisionManager *CollisionManager;
/////////////////
// CHeightGrid //
/////////////////
/*
// ***********************************************************************************
void CHeightGrid::tileAdded(const CTileAddedInfo &infos)
{
// insert in point grid
_TileInfos[infos.TileID] = infos;
CVector2f corners[3];
corners[0].set(infos.Corners[0].x, infos.Corners[0].y);
corners[1].set(infos.Corners[1].x, infos.Corners[1].y);
corners[2].set(infos.Corners[2].x, infos.Corners[2].y);
addTri(corners, infos.Center.z);
corners[0].set(infos.Corners[0].x, infos.Corners[0].y);
corners[1].set(infos.Corners[2].x, infos.Corners[2].y);
corners[2].set(infos.Corners[3].x, infos.Corners[3].y);
addTri(corners, infos.Center.z);
}
// ***********************************************************************************
void CHeightGrid::tileRemoved(uint64 id)
{
CHashMap::iterator it = _TileInfos.find(id);
nlassert(it != _TileInfos.end());
CVector2f corners[3];
const CTileAddedInfo &infos = it->second;
corners[0].set(infos.Corners[0].x, infos.Corners[0].y);
corners[1].set(infos.Corners[1].x, infos.Corners[1].y);
corners[2].set(infos.Corners[2].x, infos.Corners[2].y);
removeTri(corners, infos.Center.z);
corners[0].set(infos.Corners[0].x, infos.Corners[0].y);
corners[1].set(infos.Corners[2].x, infos.Corners[2].y);
corners[2].set(infos.Corners[3].x, infos.Corners[3].y);
removeTri(corners, infos.Center.z);
}
// ***********************************************************************************
void CHeightGrid::addTri(const CVector2f corners[3], float z)
{
_Tri.Vertices.resize(3);
_Tri.Vertices[0] = corners[0];
_Tri.Vertices[1] = corners[1];
_Tri.Vertices[2] = corners[2];
sint minY;
_Tri.computeBorders(_Rasters, minY);
if (_Rasters.empty()) return;
sint numRasters = _Rasters.size();
CHeightGridWrapped::CGridElem ge;
ge.Z = z;
for(sint y = 0; y < numRasters; ++y)
{
ge.Y = y + minY;
const CPolygon2D::TRaster &r = _Rasters[y];
for (ge.X = r.first; ge.X <= r.second; ++ge.X)
{
_ZGridWrapped.insert(ge);
if (ge.X >= _PosX && ge.X < _PosX + (sint) _GridSize &&
ge.Y >= _PosY && ge.Y < _PosY + (sint) _GridSize
)
{
_ZGrid(ge.X - _PosX, ge.Y - _PosY) = std::max(z, _ZGrid(ge.X - _PosX, ge.Y - _PosY));
}
}
}
}
// ***********************************************************************************
void CHeightGrid::removeTri(const CVector2f corners[3], float z)
{
_Tri.Vertices.resize(3);
_Tri.Vertices[0] = corners[0];
_Tri.Vertices[1] = corners[1];
_Tri.Vertices[2] = corners[2];
sint minY;
_Tri.computeBorders(_Rasters, minY);
if (_Rasters.empty()) return;
sint numRasters = _Rasters.size();
CHeightGridWrapped::CGridElem ge;
ge.Z = z;
for(sint y = 0; y < numRasters; ++y)
{
ge.Y = y + minY;
const CPolygon2D::TRaster &r = _Rasters[y];
for (ge.X = r.first; ge.X <= r.second; ++ge.X)
{
_ZGridWrapped.remove(ge);
}
}
}
// ***********************************************************************************
CHeightGrid::CHeightGrid()
{
_CellSize = 0.f;
_GridSize = 0;
_MinZ = HEIGHT_GRID_MIN_Z;
_PosX = 0;
_PosY = 0;
_SizeMask = 0;
_InvCellSize = 1.f;
updateBBox();
}
// ***********************************************************************************
void CHeightGrid::init(float cellSize, uint heightGridSize, uint wrappedHeightGridSize, float minZ)
{
nlassert(cellSize > 0.f);
nlassert(heightGridSize > 0);
nlassert(NLMISC::isPowerOf2(heightGridSize));
nlassert(NLMISC::isPowerOf2(wrappedHeightGridSize));
_ZGrid.init(heightGridSize, heightGridSize, minZ);
_ZGridWrapped.init(wrappedHeightGridSize, cellSize);
_MinZ = minZ;
_CellSize = cellSize;
_GridSize = heightGridSize;
_InvCellSize = 1.f / _CellSize;
updateBBox();
}
// ***********************************************************************************
void CHeightGrid::updateBBox()
{
_BBox.setMinMax(CVector(_PosX * _CellSize, _PosY * _CellSize, _MinZ),
CVector((_PosX + _GridSize) * _CellSize, (_PosY + _GridSize) * _CellSize, -_MinZ));
}
// ***********************************************************************************
void CHeightGrid::update(const CVector &newPos)
{
if (_CellSize == 0.f) return;
sint newX = (sint) floorf(newPos.x / _CellSize) - (_GridSize >> 1);
sint newY = (sint) floorf(newPos.y / _CellSize) - (_GridSize >> 1);
if (newX == _PosX && newY == _PosY) return;
// compute displacement of the grid
sint offsetX = _PosX - newX;
sint offsetY = _PosY - newY;
// compute parts of the grid that have been discarded
_ZGrid.getDiscardRects(offsetX, offsetY, _UpdateRects);
for(uint k = 0; k < _UpdateRects.size(); ++k)
{
discardRect(_UpdateRects[k]);
}
_ZGrid.move(offsetX, offsetY);
updateBBox();
// compute parts of the grid that must be updated
_ZGrid.getUpdateRects(offsetX, offsetY, _UpdateRects);
_PosX = newX;
_PosY = newY;
for(uint k = 0; k < _UpdateRects.size(); ++k)
{
updateRect(_UpdateRects[k]);
}
}
// ***********************************************************************************
void CHeightGrid::addCollisionMesh(const UVisualCollisionManager::CMeshInstanceColInfo &colMesh)
{
const std::vector &vertices = colMesh.Mesh.getVertices();
_CurrMeshVertices.resize(vertices.size());
std::vector::const_iterator src, srcEnd;
std::vector::iterator dest;
srcEnd = _CurrMeshVertices.end();
dest = _CurrMeshVertices.begin();
// transform vertices in world
for (src = vertices.begin(); src != srcEnd; ++ src)
{
*dest = *colMesh.WorldMatrix * *src;
++ dest;
}
std::vector tris = colMesh.Mesh.getTriangles();
uint triSize = tris.size();
uint currVert = 0;
// Insert tri in the grid
// Because of low resolution, only a few tri will actually be inserted
while (currVert != triSize)
{
const CVector &v0 = _CurrMeshVertices[tris[currVert]];
const CVector &v1 = _CurrMeshVertices[tris[currVert + 1]];
const CVector &v2 = _CurrMeshVertices[tris[currVert + 2]];
// project corners in 2D
CVector2f corners[3];
corners[0].set(v0.x, v0.y);
corners[1].set(v1.x, v1.y);
corners[2].set(v2.x, v2.y);
addTri(corners, maxof(v0.z, v1.z, v2.z));
currVert += 3;
}
_Meshs[colMesh.ID] = colMesh;
}
// ***********************************************************************************
void CHeightGrid::removeCollisionMesh(uint id)
{
TColMeshMap::iterator it = _Meshs.find(id);
if (it == _Meshs.end()) return;
const std::vector &vertices = it->second.Mesh.getVertices();
_CurrMeshVertices.resize(vertices.size());
std::vector::const_iterator src, srcEnd;
std::vector::iterator dest;
srcEnd = vertices.end();
dest = _CurrMeshVertices.begin();
// transform vertices in world
const CMatrix &worldMat = *(it->second.WorldMatrix);
for (src = vertices.begin(); src != srcEnd; ++ src)
{
*dest = worldMat * *src;
++ dest;
}
std::vector tris = it->second.Mesh.getTriangles();
uint triSize = tris.size();
uint currVert = 0;
while (currVert != triSize)
{
const CVector v0 = _CurrMeshVertices[tris[currVert]];
const CVector v1 = _CurrMeshVertices[tris[currVert + 1]];
const CVector v2 = _CurrMeshVertices[tris[currVert + 2]];
// project corners in 2D
CVector2f corners[3];
corners[0].set(v0.x, v0.y);
corners[1].set(v1.x, v1.y);
corners[2].set(v2.x, v2.y);
removeTri(corners, maxof(v0.z, v1.z, v2.z));
currVert += 3;
}
_Meshs.erase(it);
}
// ***********************************************************************************
void CHeightGrid::updateRect(const NLMISC::CRect &rect)
{
// add incoming meshs into the quad grid
CAABBox bbox;
bbox.setMinMax(CVector(rect.X * _CellSize, rect.Y * _CellSize, _MinZ), CVector((rect.X + rect.Width) * _CellSize, (rect.Y + rect.Height) * _CellSize, - _MinZ));
CollisionManager->getMeshs(bbox, _UpdateMeshs);
for(uint k = 0; k < _UpdateMeshs.size(); ++k)
{
if (_Meshs.count(_UpdateMeshs[k].ID) == 0)
{
// not already inserted
addCollisionMesh(_UpdateMeshs[k]);
}
}
// Update height grid from the values in the quad grid
for (uint y = (uint) rect.Y; y < (uint) (rect.Y + rect.Height); ++y)
{
for (uint x = (uint) rect.X; x < (uint) (rect.X + rect.Width); ++x)
{
updateCell(x, y);
}
}
}
// ***********************************************************************************
void CHeightGrid::discardRect(const CRect &rect)
{
// remove meshs that are totally out of the grid
CAABBox bbox;
bbox.setMinMax(CVector(rect.X * _CellSize, rect.Y * _CellSize, _MinZ), CVector((rect.X + rect.Width) * _CellSize, (rect.Y + rect.Width) * _CellSize, - _MinZ));
CollisionManager->getMeshs(bbox, _UpdateMeshs);
for(uint k = 0; k < _UpdateMeshs.size(); ++k)
{
if (!_UpdateMeshs[k].WorldBBox->intersect(_BBox))
{
removeCollisionMesh(_UpdateMeshs[k].ID); // remove mesh (no-op if already removed)
}
}
}
// ***********************************************************************************
void CHeightGrid::updateCell(sint px, sint py)
{
sint worldX = px + _PosX;
sint worldY = py + _PosY;
float z = _MinZ;
const CHeightGridWrapped::TGridElemList &pl = _ZGridWrapped.getGridElemList(worldX, worldY);
for(CHeightGridWrapped::TGridElemList::const_iterator it = pl.begin(); it != pl.end(); ++it)
{
const CHeightGridWrapped::CGridElem &ge = *it;
if (ge.X == worldX && ge.Y == worldY)
{
z = std::max(z, ge.Z);
}
}
_ZGrid(px, py) = z;
}
// ***********************************************************************************
void CHeightGrid::display(NL3D::UDriver &drv) const
{
extern UCamera MainCam;
drv.setModelMatrix(CMatrix::Identity);
drv.setViewMatrix(MainCam.getMatrix().inverted());
drv.setFrustum(MainCam.getFrustum());
UMaterial m = drv.createMaterial();
m.initUnlit();
m.setColor(CRGBA::Red);
for (sint y = _PosY; y < (sint) (_PosY + _GridSize - 1); ++y)
{
for (sint x = _PosX; x < (sint) (_PosX + _GridSize - 1); ++x)
{
float z0 = _ZGrid(x - _PosX, y - _PosY);
float z1 = _ZGrid(x + 1 - _PosX, y - _PosY);
float z2 = _ZGrid(x + 1 - _PosX, y + 1 - _PosY);
float z3 = _ZGrid(x - _PosX, y + 1 - _PosY);
//
CVector pos[4];
CVector pos0 = gridCoordToWorld(x, y) + z0 * CVector::K;
CVector pos1 = gridCoordToWorld(x + 1, y) + z1 * CVector::K;
CVector pos2 = gridCoordToWorld(x + 1, y + 1) + z2 * CVector::K;
CVector pos3 = gridCoordToWorld(x, y + 1) + z3 * CVector::K;
drv.drawLine(CLine(pos0, pos1), m);
drv.drawLine(CLine(pos1, pos2), m);
drv.drawLine(CLine(pos2, pos3), m);
drv.drawLine(CLine(pos3, pos0), m);
}
}
m.setColor(CRGBA::Green);
// draw dots of the point grid
//for (sint y = 0; y < (sint) _PointGrid.getSize(); ++y)
//{
// for (sint x = 0; x < (sint) _PointGrid.getSize(); ++x)
// {
// const CPointGrid::TPointList &pl = _PointGrid.getPointList(x ,y);
// for(CPointGrid::TPointList::const_iterator it = pl.begin(); it != pl.end(); ++it)
// {
// drv.drawLine(CLine(*it, *it + CVector::K), m);
// }
// }
//}
drv.deleteMaterial(m);
// info : check max size of vectors
nlwarning("Max list length = %d", (int) _ZGridWrapped.getListMaxLength());
}
////////////////
// CPointGrid //
////////////////
// *********************************************************************************
CHeightGridWrapped::CHeightGridWrapped()
{
_CellSize = 0.f;
_SizeMask = 0;
_InvCellSize = 1.f;
}
// *********************************************************************************
uint CHeightGridWrapped::getListMaxLength() const
{
uint sizeMax = 0;
for(CArray2D::const_iterator it = _Grid.begin(); it != _Grid.end(); ++it)
{
sizeMax = std::max(sizeMax, (uint) it->size());
}
return sizeMax;
}
// *********************************************************************************
void CHeightGridWrapped::init(uint size, float cellSize)
{
nlassert(cellSize > 0.f)
nlassert(NLMISC::isPowerOf2(size));
uint sizePower= NLMISC::getPowerOf2(size);
_SizeMask = (1 << sizePower) - 1;
_CellSize = cellSize;
_InvCellSize = 1.f / _CellSize;
_Grid.init(size, size);
}
*/
//=====================================================================
CPrecipitationClipGrid::CPrecipitationClipGrid()
{
clear();
touch();
}
//=====================================================================
void CPrecipitationClipGrid::initGrid(uint size, float gridEltSizeX, float gridEltSizeY)
{
if (gridEltSizeX <= 0.f || gridEltSizeY <= 0.f || size == 0)
{
clear();
return;
}
_Grid.clear();
_Grid.resize((size + 1) * (size + 1));
_XPos = 0;
_YPos = 0;
_EltSizeX = gridEltSizeX;
_EltSizeY = gridEltSizeY;
_Size = size;
}
//=====================================================================
void CPrecipitationClipGrid::updateGrid(const NLMISC::CVector &/* userPos */, NLPACS::UGlobalRetriever * /* gr */)
{
// temp
return;
//
/*
if (_EltSizeX == 0.f) return;
sint newX = (sint) floorf(userPos.x / _EltSizeX) - (_Size >> 1);
sint newY = (sint) floorf(userPos.y / _EltSizeY) - (_Size >> 1);
if (_Touched)
{
#if defined(NL_CPU_INTEL) && defined(NL_DEBUG)
CSimpleClock clock;
CSimpleClock::init();
clock.start();
#endif
// recompute the whole grid
updateGridPart(newX, newY, 0, 0, _Size + 1, _Size + 1, gr);
#if defined(NL_CPU_INTEL) && defined(NL_DEBUG)
clock.stop();
// display number of millisecond needed for the update
double freq = (double) CSystemInfo::getProcessorFrequency();
double msPerTick = 1000 / (double) freq;
nldebug("Updated precipitation clip grid : time = %.2f ms", (float) (clock.getNumTicks() * msPerTick));
// display the height in the clip grid
printGridValues();
#endif
// update pos
_XPos = newX;
_YPos = newY;
_Touched = false;
return;
}
if (newX == _XPos && newY == _YPos)
{
return; // the grid hasn't changed its pos
}
// move the grid
sint offsetX = newX - _XPos;
sint offsetY = newY - _YPos;
moveGrid(- offsetX, - offsetY);
// complete new positions
#if defined(NL_CPU_INTEL) && defined(NL_DEBUG)
CSimpleClock clock;
CSimpleClock::init();
clock.start();
#endif
// Update right or left part.
if (newX > _XPos) // moved right ?
{
// the width to update
uint width = std::min((uint) offsetX, _Size + 1);
// the the grid moved top or bottom, exclude this part
sint height = _Size + 1 - abs(offsetY);
if (height > 0)
{
// complete column on the right
updateGridPart(newX + _Size + 1 - width, newY - offsetY, _Size + 1 - width, std::max(-offsetY, 0), width, height, gr);
}
}
else if (newX < _XPos) // moved left ?
{
// the width to update
uint width = std::min((uint) (- offsetX), _Size + 1);
// the the grid moved top or bottom, exclude
sint height = _Size + 1 - abs(offsetY);
if (height > 0)
{
// complete column on the right
updateGridPart(newX, newY - offsetY, 0, std::max(-offsetY, 0), width, height, gr);
}
}
// update top or bottom part
if (newY > _YPos)
{
sint height = std::min((uint) offsetY, _Size + 1);
updateGridPart(newX, newY + _Size + 1 - height, 0, _Size + 1 - height, _Size + 1, height, gr);
}
else
if (newY < _YPos)
{
sint height = std::min((uint) (- offsetY), _Size + 1);
updateGridPart(newX, newY, 0, 0, _Size + 1, height, gr);
}
#if defined(NL_CPU_INTEL) && defined(NL_DEBUG)
clock.stop();
// display number of millisecond needed for the update
double freq = (double) CSystemInfo::getProcessorFrequency();
double msPerTick = 1000 / (double) freq;
nldebug("Updated precipitation clip grid : time = %.2f ms", (float) (clock.getNumTicks() * msPerTick));
// display the height in the clip grid
printGridValues();
#endif
// update pos
_XPos = newX;
_YPos = newY;
*/
}
//=====================================================================
void CPrecipitationClipGrid::printGridValues() const
{
for(uint k = 0; k <= _Size; ++k)
{
std::string outStr;
for(uint l = 0; l <= _Size; ++l)
{
TGrid::const_iterator gridIt = getGridIt(_Grid, l, k, _Size);
outStr += toString("%5d%c ", (uint) gridIt->MeanHeight, l == _Size ? ' ' : ',');
}
nlinfo(outStr.c_str());
}
}
//=====================================================================
void CPrecipitationClipGrid::moveGrid(sint offsetX, sint offsetY)
{
static TGrid otherGrid; // the grid is likely to be rather small so it's ok to keep it static
otherGrid.resize(_Grid.size());
// get start pos in the source
uint srcX, srcY;
sint width, height;
// compute position in source & size
if (offsetX > 0)
{
srcX = 0;
width = (_Size + 1) - offsetX;
}
else
{
srcX = -offsetX;
width = (_Size + 1) - srcX;
}
if (offsetY > 0)
{
srcY = 0;
height = (_Size + 1) - offsetY;
}
else
{
srcY = -offsetY;
height = (_Size + 1) - srcY;
}
if (width > 0 && height > 0)
{
TGrid::const_iterator srcIt = getGridIt(_Grid, srcX, srcY, _Size);
TGrid::iterator dstIt = getGridIt(otherGrid, srcX + offsetX, srcY + offsetY, _Size);
do
{
// copy one row of the grid
std::copy(srcIt, srcIt + width, dstIt);
// go to next row for source & destination
srcIt += (_Size + 1);
dstIt += (_Size + 1);
}
while (--height);
_Grid.swap(otherGrid); // replace previous grid
}
}
//=====================================================================
const CPrecipitationClipGrid::CGridPoint *CPrecipitationClipGrid::get(sint x, sint y) const
{
// pos in the grid
sint gx = x - _XPos;
sint gy = y - _YPos;
if ((uint) gx > (_Size + 1) || (uint) gy > (_Size + 1)) return NULL;
return &(*getGridIt(_Grid, gx, gy, _Size));
}
//=====================================================================
void CPrecipitationClipGrid::clear()
{
_XPos = 0;
_YPos = 0;
_Size = 0;
_EltSizeX = 0;
_EltSizeY = 0;
_Grid.clear();
}
//=====================================================================
void CPrecipitationClipGrid::updateGridPart(sint worldX, sint worldY, uint gridX, uint gridY, uint width, uint height, NLPACS::UGlobalRetriever *gr)
{
nlassert(gridX <= _Size);
nlassert(gridY <= _Size);
nlassert(gridX + width >= gridX && gridX + width <= _Size + 1);
nlassert(gridY + height >= gridY && gridY + height <= _Size + 1);
NLMISC::CVector worldPos(worldX * _EltSizeX, worldY * _EltSizeY, 10000.f);
TGrid::iterator dest = getGridIt(_Grid, gridX, gridY, _Size);
// fill each row
for (uint y = 0; y < height; ++y)
{
TGrid::iterator currDest = dest;
NLMISC::CVector currWorldPos = worldPos;
// fill a row
for (uint x = 0; x < width; ++x)
{
if (!gr)
{
currDest->Clipped = true;
currDest->MeanHeight = 0.f;
}
else
{
// build a global position
NLPACS::UGlobalPosition gpos = gr->retrievePosition(currWorldPos, 20000.f);
if (gpos.InstanceId != -1)
{
// is it an interior ?
if (gr->isInterior(gpos))
{
currDest->Clipped = true;
currDest->MeanHeight = 0.f;
}
else
{
// get mean height at this point
currDest->Clipped = false;
currDest->MeanHeight = gr->getMeanHeight(gpos);
}
}
else
{
currDest->MeanHeight = 0.f;
}
}
++ currDest;
currWorldPos.x += _EltSizeX;
}
// move to next row
dest += (_Size + 1);
currWorldPos.y += _EltSizeY;
}
}
//=====================================================================
void CPrecipitationClipGrid::display(NL3D::UDriver &drv) const
{
// get render context
CMatrix modelMat = drv.getModelMatrix();
CMatrix viewMat = drv.getViewMatrix();
CFrustum f = drv.getFrustum();
drv.setMatrixMode2D(CFrustum(0, 800, 600, 0, 0, 1, false));
const uint stepX = 16;
const uint stepY = 16;
for (uint y = 0; y < _Size + 1; ++y)
{
for (uint x = 0; x < _Size + 1; ++x)
{
TGrid::const_iterator gridPoint = getGridIt(_Grid, x, y, _Size);
CRGBA col = gridPoint->Clipped ? CRGBA::Red : CRGBA::White;
drv.drawLine((float) (x * stepX - 1), (float) (y * stepY), (float) (x * stepX + 1), (float) (y * stepY), col);
drv.drawLine((float) (x * stepX), (float) (y * stepY - 1), (float) (x * stepX), (float) (y * stepY + 1), col);
}
}
// restore render context
drv.setModelMatrix(modelMat);
drv.setViewMatrix(viewMat);
drv.setFrustum(f);
}