|
|
|
@ -6,6 +6,8 @@
|
|
|
|
|
#include <osg/Plane>
|
|
|
|
|
|
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
#include <components/esm/esmterrain.hpp>
|
|
|
|
|
#include <components/esm4/loadland.hpp>
|
|
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
|
|
|
#include <components/misc/strings/algorithm.hpp>
|
|
|
|
|
#include <components/vfs/manager.hpp>
|
|
|
|
@ -16,26 +18,29 @@ namespace ESMTerrain
|
|
|
|
|
class LandCache
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject>> Map;
|
|
|
|
|
typedef std::map<ESM::ExteriorCellLocation, osg::ref_ptr<const LandObject>> Map;
|
|
|
|
|
Map mMap;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LandObject::LandObject()
|
|
|
|
|
: mLand(nullptr)
|
|
|
|
|
, mLoadFlags(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LandObject::LandObject(const ESM4::Land* land, int loadFlags)
|
|
|
|
|
: mLand(nullptr)
|
|
|
|
|
, mData(*land, loadFlags)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LandObject::LandObject(const ESM::Land* land, int loadFlags)
|
|
|
|
|
: mLand(land)
|
|
|
|
|
, mLoadFlags(loadFlags)
|
|
|
|
|
, mData(*land, loadFlags)
|
|
|
|
|
{
|
|
|
|
|
mLand->loadData(mLoadFlags, &mData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LandObject::LandObject(const LandObject& copy, const osg::CopyOp& copyop)
|
|
|
|
|
: mLand(nullptr)
|
|
|
|
|
, mLoadFlags(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -55,7 +60,7 @@ namespace ESMTerrain
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max)
|
|
|
|
|
bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max)
|
|
|
|
|
{
|
|
|
|
|
assert(size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell");
|
|
|
|
|
|
|
|
|
@ -63,15 +68,15 @@ namespace ESMTerrain
|
|
|
|
|
|
|
|
|
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
|
|
|
|
int cellY = static_cast<int>(std::floor(origin.y()));
|
|
|
|
|
osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
|
|
|
|
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
|
|
|
|
const int landSize = ESM::getLandSize(worldspace);
|
|
|
|
|
int startRow = (origin.x() - cellX) * landSize;
|
|
|
|
|
int startColumn = (origin.y() - cellY) * landSize;
|
|
|
|
|
|
|
|
|
|
int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE;
|
|
|
|
|
int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE;
|
|
|
|
|
|
|
|
|
|
int endRow = startRow + size * (ESM::Land::LAND_SIZE - 1) + 1;
|
|
|
|
|
int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1;
|
|
|
|
|
int endRow = startRow + size * (landSize - 1) + 1;
|
|
|
|
|
int endColumn = startColumn + size * (landSize - 1) + 1;
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY);
|
|
|
|
|
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
min = std::numeric_limits<float>::max();
|
|
|
|
@ -80,7 +85,7 @@ namespace ESMTerrain
|
|
|
|
|
{
|
|
|
|
|
for (int col = startColumn; col < endColumn; ++col)
|
|
|
|
|
{
|
|
|
|
|
float h = data->mHeights[col * ESM::Land::LAND_SIZE + row];
|
|
|
|
|
float h = data->getHeights()[col * landSize + row];
|
|
|
|
|
if (h > max)
|
|
|
|
|
max = h;
|
|
|
|
|
if (h < min)
|
|
|
|
@ -95,73 +100,80 @@ namespace ESMTerrain
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache)
|
|
|
|
|
void Storage::fixNormal(
|
|
|
|
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
|
|
|
|
{
|
|
|
|
|
while (col >= ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
|
|
|
|
|
const int landSize = ESM::getLandSize(cellLocation.mWorldspace);
|
|
|
|
|
|
|
|
|
|
while (col >= landSize - 1)
|
|
|
|
|
{
|
|
|
|
|
++cellY;
|
|
|
|
|
col -= ESM::Land::LAND_SIZE - 1;
|
|
|
|
|
++cellLocation.mY;
|
|
|
|
|
col -= landSize - 1;
|
|
|
|
|
}
|
|
|
|
|
while (row >= ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
while (row >= landSize - 1)
|
|
|
|
|
{
|
|
|
|
|
++cellX;
|
|
|
|
|
row -= ESM::Land::LAND_SIZE - 1;
|
|
|
|
|
++cellLocation.mX;
|
|
|
|
|
row -= landSize - 1;
|
|
|
|
|
}
|
|
|
|
|
while (col < 0)
|
|
|
|
|
{
|
|
|
|
|
--cellY;
|
|
|
|
|
col += ESM::Land::LAND_SIZE - 1;
|
|
|
|
|
--cellLocation.mY;
|
|
|
|
|
col += landSize - 1;
|
|
|
|
|
}
|
|
|
|
|
while (row < 0)
|
|
|
|
|
{
|
|
|
|
|
--cellX;
|
|
|
|
|
row += ESM::Land::LAND_SIZE - 1;
|
|
|
|
|
--cellLocation.mX;
|
|
|
|
|
row += landSize - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const LandObject* land = getLand(cellX, cellY, cache);
|
|
|
|
|
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;
|
|
|
|
|
const LandObject* land = getLand(cellLocation, cache);
|
|
|
|
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;
|
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
normal.x() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3];
|
|
|
|
|
normal.y() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1];
|
|
|
|
|
normal.z() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2];
|
|
|
|
|
normal.x() = data->getNormals()[col * landSize * 3 + row * 3];
|
|
|
|
|
normal.y() = data->getNormals()[col * landSize * 3 + row * 3 + 1];
|
|
|
|
|
normal.z() = data->getNormals()[col * landSize * 3 + row * 3 + 2];
|
|
|
|
|
normal.normalize();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
normal = osg::Vec3f(0, 0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::averageNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache)
|
|
|
|
|
void Storage::averageNormal(
|
|
|
|
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
|
|
|
|
{
|
|
|
|
|
osg::Vec3f n1, n2, n3, n4;
|
|
|
|
|
fixNormal(n1, cellX, cellY, col + 1, row, cache);
|
|
|
|
|
fixNormal(n2, cellX, cellY, col - 1, row, cache);
|
|
|
|
|
fixNormal(n3, cellX, cellY, col, row + 1, cache);
|
|
|
|
|
fixNormal(n4, cellX, cellY, col, row - 1, cache);
|
|
|
|
|
fixNormal(n1, cellLocation, col + 1, row, cache);
|
|
|
|
|
fixNormal(n2, cellLocation, col - 1, row, cache);
|
|
|
|
|
fixNormal(n3, cellLocation, col, row + 1, cache);
|
|
|
|
|
fixNormal(n4, cellLocation, col, row - 1, cache);
|
|
|
|
|
normal = (n1 + n2 + n3 + n4);
|
|
|
|
|
normal.normalize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::fixColour(osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache)
|
|
|
|
|
void Storage::fixColour(
|
|
|
|
|
osg::Vec4ub& color, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
|
|
|
|
{
|
|
|
|
|
if (col == ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
|
|
|
|
|
const int landSize = ESM::getLandSize(cellLocation.mWorldspace);
|
|
|
|
|
|
|
|
|
|
if (col == landSize - 1)
|
|
|
|
|
{
|
|
|
|
|
++cellY;
|
|
|
|
|
++cellLocation.mY;
|
|
|
|
|
col = 0;
|
|
|
|
|
}
|
|
|
|
|
if (row == ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
if (row == landSize - 1)
|
|
|
|
|
{
|
|
|
|
|
++cellX;
|
|
|
|
|
++cellLocation.mX;
|
|
|
|
|
row = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const LandObject* land = getLand(cellX, cellY, cache);
|
|
|
|
|
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;
|
|
|
|
|
const LandObject* land = getLand(cellLocation, cache);
|
|
|
|
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;
|
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
color.r() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3];
|
|
|
|
|
color.g() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1];
|
|
|
|
|
color.b() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2];
|
|
|
|
|
color.r() = data->getColors()[col * landSize * 3 + row * 3];
|
|
|
|
|
color.g() = data->getColors()[col * landSize * 3 + row * 3 + 1];
|
|
|
|
|
color.b() = data->getColors()[col * landSize * 3 + row * 3 + 2];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -171,7 +183,7 @@ namespace ESMTerrain
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center,
|
|
|
|
|
void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace,
|
|
|
|
|
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
|
|
|
|
osg::ref_ptr<osg::Vec4ubArray> colours)
|
|
|
|
|
{
|
|
|
|
@ -182,8 +194,10 @@ namespace ESMTerrain
|
|
|
|
|
|
|
|
|
|
int startCellX = static_cast<int>(std::floor(origin.x()));
|
|
|
|
|
int startCellY = static_cast<int>(std::floor(origin.y()));
|
|
|
|
|
const int landSize = ESM::getLandSize(worldspace);
|
|
|
|
|
const int LandSizeInUnits = ESM::getCellSize(worldspace);
|
|
|
|
|
|
|
|
|
|
size_t numVerts = static_cast<size_t>(size * (ESM::Land::LAND_SIZE - 1) / increment + 1);
|
|
|
|
|
size_t numVerts = static_cast<size_t>(size * (landSize - 1) / increment + 1);
|
|
|
|
|
|
|
|
|
|
positions->resize(numVerts * numVerts);
|
|
|
|
|
normals->resize(numVerts * numVerts);
|
|
|
|
@ -198,22 +212,24 @@ namespace ESMTerrain
|
|
|
|
|
LandCache cache;
|
|
|
|
|
|
|
|
|
|
bool alteration = useAlteration();
|
|
|
|
|
|
|
|
|
|
bool validHeightDataExists = false;
|
|
|
|
|
float vertY_ = 0; // of current cell corner
|
|
|
|
|
for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY)
|
|
|
|
|
{
|
|
|
|
|
float vertX_ = 0; // of current cell corner
|
|
|
|
|
for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX)
|
|
|
|
|
{
|
|
|
|
|
const LandObject* land = getLand(cellX, cellY, cache);
|
|
|
|
|
const ESM::Land::LandData* heightData = nullptr;
|
|
|
|
|
const ESM::Land::LandData* normalData = nullptr;
|
|
|
|
|
const ESM::Land::LandData* colourData = nullptr;
|
|
|
|
|
ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace);
|
|
|
|
|
const LandObject* land = getLand(cellLocation, cache);
|
|
|
|
|
const ESM::LandData* heightData = nullptr;
|
|
|
|
|
const ESM::LandData* normalData = nullptr;
|
|
|
|
|
const ESM::LandData* colourData = nullptr;
|
|
|
|
|
if (land)
|
|
|
|
|
{
|
|
|
|
|
heightData = land->getData(ESM::Land::DATA_VHGT);
|
|
|
|
|
normalData = land->getData(ESM::Land::DATA_VNML);
|
|
|
|
|
colourData = land->getData(ESM::Land::DATA_VCLR);
|
|
|
|
|
validHeightDataExists = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rowStart = 0;
|
|
|
|
@ -227,12 +243,12 @@ namespace ESMTerrain
|
|
|
|
|
rowStart += increment;
|
|
|
|
|
|
|
|
|
|
// Only relevant for chunks smaller than (contained in) one cell
|
|
|
|
|
rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE;
|
|
|
|
|
colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE;
|
|
|
|
|
int rowEnd = std::min(static_cast<int>(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1),
|
|
|
|
|
static_cast<int>(ESM::Land::LAND_SIZE));
|
|
|
|
|
int colEnd = std::min(static_cast<int>(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1),
|
|
|
|
|
static_cast<int>(ESM::Land::LAND_SIZE));
|
|
|
|
|
rowStart += (origin.x() - startCellX) * landSize;
|
|
|
|
|
colStart += (origin.y() - startCellY) * landSize;
|
|
|
|
|
int rowEnd = std::min(
|
|
|
|
|
static_cast<int>(rowStart + std::min(1.f, size) * (landSize - 1) + 1), static_cast<int>(landSize));
|
|
|
|
|
int colEnd = std::min(
|
|
|
|
|
static_cast<int>(colStart + std::min(1.f, size) * (landSize - 1) + 1), static_cast<int>(landSize));
|
|
|
|
|
|
|
|
|
|
vertY = vertY_;
|
|
|
|
|
for (int col = colStart; col < colEnd; col += increment)
|
|
|
|
@ -240,27 +256,27 @@ namespace ESMTerrain
|
|
|
|
|
vertX = vertX_;
|
|
|
|
|
for (int row = rowStart; row < rowEnd; row += increment)
|
|
|
|
|
{
|
|
|
|
|
int srcArrayIndex = col * ESM::Land::LAND_SIZE * 3 + row * 3;
|
|
|
|
|
int srcArrayIndex = col * landSize * 3 + row * 3;
|
|
|
|
|
|
|
|
|
|
assert(row >= 0 && row < ESM::Land::LAND_SIZE);
|
|
|
|
|
assert(col >= 0 && col < ESM::Land::LAND_SIZE);
|
|
|
|
|
assert(row >= 0 && row < landSize);
|
|
|
|
|
assert(col >= 0 && col < landSize);
|
|
|
|
|
|
|
|
|
|
assert(vertX < numVerts);
|
|
|
|
|
assert(vertY < numVerts);
|
|
|
|
|
|
|
|
|
|
float height = defaultHeight;
|
|
|
|
|
if (heightData)
|
|
|
|
|
height = heightData->mHeights[col * ESM::Land::LAND_SIZE + row];
|
|
|
|
|
height = heightData->getHeights()[col * landSize + row];
|
|
|
|
|
if (alteration)
|
|
|
|
|
height += getAlteredHeight(col, row);
|
|
|
|
|
(*positions)[static_cast<unsigned int>(vertX * numVerts + vertY)]
|
|
|
|
|
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
|
|
|
|
|
(vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height);
|
|
|
|
|
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits,
|
|
|
|
|
(vertY / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits, height);
|
|
|
|
|
|
|
|
|
|
if (normalData)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
|
normal[i] = normalData->mNormals[srcArrayIndex + i];
|
|
|
|
|
normal[i] = normalData->getNormals()[srcArrayIndex + i];
|
|
|
|
|
|
|
|
|
|
normal.normalize();
|
|
|
|
|
}
|
|
|
|
@ -268,13 +284,12 @@ namespace ESMTerrain
|
|
|
|
|
normal = osg::Vec3f(0, 0, 1);
|
|
|
|
|
|
|
|
|
|
// Normals apparently don't connect seamlessly between cells
|
|
|
|
|
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
fixNormal(normal, cellX, cellY, col, row, cache);
|
|
|
|
|
if (col == landSize - 1 || row == landSize - 1)
|
|
|
|
|
fixNormal(normal, cellLocation, col, row, cache);
|
|
|
|
|
|
|
|
|
|
// some corner normals appear to be complete garbage (z < 0)
|
|
|
|
|
if ((row == 0 || row == ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
&& (col == 0 || col == ESM::Land::LAND_SIZE - 1))
|
|
|
|
|
averageNormal(normal, cellX, cellY, col, row, cache);
|
|
|
|
|
if ((row == 0 || row == landSize - 1) && (col == 0 || col == landSize - 1))
|
|
|
|
|
averageNormal(normal, cellLocation, col, row, cache);
|
|
|
|
|
|
|
|
|
|
assert(normal.z() > 0);
|
|
|
|
|
|
|
|
|
@ -283,7 +298,7 @@ namespace ESMTerrain
|
|
|
|
|
if (colourData)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
|
color[i] = colourData->mColours[srcArrayIndex + i];
|
|
|
|
|
color[i] = colourData->getColors()[srcArrayIndex + i];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -295,8 +310,8 @@ namespace ESMTerrain
|
|
|
|
|
adjustColor(col, row, heightData, color); // Does nothing by default, override in OpenMW-CS
|
|
|
|
|
|
|
|
|
|
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
|
|
|
|
|
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1)
|
|
|
|
|
fixColour(color, cellX, cellY, col, row, cache);
|
|
|
|
|
if (col == landSize - 1 || row == landSize - 1)
|
|
|
|
|
fixColour(color, cellLocation, col, row, cache);
|
|
|
|
|
|
|
|
|
|
color.a() = 255;
|
|
|
|
|
|
|
|
|
@ -313,39 +328,50 @@ namespace ESMTerrain
|
|
|
|
|
assert(vertX_ == numVerts); // Ensure we covered whole area
|
|
|
|
|
}
|
|
|
|
|
assert(vertY_ == numVerts); // Ensure we covered whole area
|
|
|
|
|
|
|
|
|
|
if (!validHeightDataExists && ESM::isEsm4Ext(worldspace))
|
|
|
|
|
{
|
|
|
|
|
for (unsigned int iVert = 0; iVert < numVerts * numVerts; iVert++)
|
|
|
|
|
{
|
|
|
|
|
(*positions)[static_cast<unsigned int>(iVert)] = osg::Vec3f(0.f, 0.f, 0.f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache& cache)
|
|
|
|
|
Storage::UniqueTextureId Storage::getVtexIndexAt(
|
|
|
|
|
ESM::ExteriorCellLocation cellLocation, const LandObject* land, int x, int y, LandCache& cache)
|
|
|
|
|
{
|
|
|
|
|
// For the first/last row/column, we need to get the texture from the neighbour cell
|
|
|
|
|
// to get consistent blending at the borders
|
|
|
|
|
--x;
|
|
|
|
|
ESM::ExteriorCellLocation cellLocationIn = cellLocation;
|
|
|
|
|
if (x < 0)
|
|
|
|
|
{
|
|
|
|
|
--cellX;
|
|
|
|
|
--cellLocation.mX;
|
|
|
|
|
x += ESM::Land::LAND_TEXTURE_SIZE;
|
|
|
|
|
}
|
|
|
|
|
while (x >= ESM::Land::LAND_TEXTURE_SIZE)
|
|
|
|
|
{
|
|
|
|
|
++cellX;
|
|
|
|
|
++cellLocation.mX;
|
|
|
|
|
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
|
|
|
|
}
|
|
|
|
|
while (
|
|
|
|
|
y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
|
|
|
|
|
{
|
|
|
|
|
++cellY;
|
|
|
|
|
++cellLocation.mY;
|
|
|
|
|
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cellLocation != cellLocationIn)
|
|
|
|
|
land = getLand(cellLocation, cache);
|
|
|
|
|
|
|
|
|
|
assert(x < ESM::Land::LAND_TEXTURE_SIZE);
|
|
|
|
|
assert(y < ESM::Land::LAND_TEXTURE_SIZE);
|
|
|
|
|
|
|
|
|
|
const LandObject* land = getLand(cellX, cellY, cache);
|
|
|
|
|
|
|
|
|
|
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;
|
|
|
|
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;
|
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
|
|
|
|
int tex = data->getTextures()[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
|
|
|
|
if (tex == 0)
|
|
|
|
|
return std::make_pair(0, 0); // vtex 0 is always the base texture, regardless of plugin
|
|
|
|
|
return std::make_pair(tex, land->getPlugin());
|
|
|
|
@ -375,7 +401,7 @@ namespace ESMTerrain
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
|
|
|
|
std::vector<Terrain::LayerInfo>& layerList)
|
|
|
|
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
|
|
|
|
|
{
|
|
|
|
|
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f);
|
|
|
|
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
|
|
|
@ -393,12 +419,15 @@ namespace ESMTerrain
|
|
|
|
|
|
|
|
|
|
LandCache cache;
|
|
|
|
|
std::map<UniqueTextureId, unsigned int> textureIndicesMap;
|
|
|
|
|
ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace);
|
|
|
|
|
|
|
|
|
|
const LandObject* land = getLand(cellLocation, cache);
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < blendmapSize; y++)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < blendmapSize; x++)
|
|
|
|
|
{
|
|
|
|
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x + rowStart, y + colStart, cache);
|
|
|
|
|
UniqueTextureId id = getVtexIndexAt(cellLocation, land, x + rowStart, y + colStart, cache);
|
|
|
|
|
std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);
|
|
|
|
|
if (found == textureIndicesMap.end())
|
|
|
|
|
{
|
|
|
|
@ -442,27 +471,29 @@ namespace ESMTerrain
|
|
|
|
|
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Storage::getHeightAt(const osg::Vec3f& worldPos)
|
|
|
|
|
float Storage::getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace)
|
|
|
|
|
{
|
|
|
|
|
int cellX = static_cast<int>(std::floor(worldPos.x() / float(Constants::CellSizeInUnits)));
|
|
|
|
|
int cellY = static_cast<int>(std::floor(worldPos.y() / float(Constants::CellSizeInUnits)));
|
|
|
|
|
const float cellSize = ESM::getCellSize(worldspace);
|
|
|
|
|
int cellX = static_cast<int>(std::floor(worldPos.x() / cellSize));
|
|
|
|
|
int cellY = static_cast<int>(std::floor(worldPos.y() / cellSize));
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY);
|
|
|
|
|
osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
|
|
|
|
|
if (!land)
|
|
|
|
|
return defaultHeight;
|
|
|
|
|
return ESM::isEsm4Ext(worldspace) ? std::numeric_limits<float>::lowest() : defaultHeight;
|
|
|
|
|
|
|
|
|
|
const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT);
|
|
|
|
|
const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT);
|
|
|
|
|
if (!data)
|
|
|
|
|
return defaultHeight;
|
|
|
|
|
const int landSize = data->getLandSize();
|
|
|
|
|
|
|
|
|
|
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
|
|
|
|
|
|
|
|
|
|
// Normalized position in the cell
|
|
|
|
|
float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);
|
|
|
|
|
float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);
|
|
|
|
|
float nX = (worldPos.x() - (cellX * cellSize)) / cellSize;
|
|
|
|
|
float nY = (worldPos.y() - (cellY * cellSize)) / cellSize;
|
|
|
|
|
|
|
|
|
|
// get left / bottom points (rounded down)
|
|
|
|
|
float factor = ESM::Land::LAND_SIZE - 1.0f;
|
|
|
|
|
float factor = landSize - 1.0f;
|
|
|
|
|
float invFactor = 1.0f / factor;
|
|
|
|
|
|
|
|
|
|
int startX = static_cast<int>(nX * factor);
|
|
|
|
@ -470,8 +501,8 @@ namespace ESMTerrain
|
|
|
|
|
int endX = startX + 1;
|
|
|
|
|
int endY = startY + 1;
|
|
|
|
|
|
|
|
|
|
endX = std::min(endX, ESM::Land::LAND_SIZE - 1);
|
|
|
|
|
endY = std::min(endY, ESM::Land::LAND_SIZE - 1);
|
|
|
|
|
endX = std::min(endX, landSize - 1);
|
|
|
|
|
endY = std::min(endY, landSize - 1);
|
|
|
|
|
|
|
|
|
|
// now get points in terrain space (effectively rounding them to boundaries)
|
|
|
|
|
float startXTS = startX * invFactor;
|
|
|
|
@ -491,10 +522,10 @@ namespace ESMTerrain
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Build all 4 positions in normalized cell space, using point-sampled height
|
|
|
|
|
osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits));
|
|
|
|
|
osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits));
|
|
|
|
|
osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits));
|
|
|
|
|
osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits));
|
|
|
|
|
osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / cellSize);
|
|
|
|
|
osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / cellSize);
|
|
|
|
|
osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / cellSize);
|
|
|
|
|
osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / cellSize);
|
|
|
|
|
// define this plane in terrain space
|
|
|
|
|
osg::Plane plane;
|
|
|
|
|
// FIXME: deal with differing triangle alignment
|
|
|
|
@ -520,23 +551,22 @@ namespace ESMTerrain
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Solve plane equation for z
|
|
|
|
|
return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z()
|
|
|
|
|
* Constants::CellSizeInUnits;
|
|
|
|
|
return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * cellSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache)
|
|
|
|
|
const LandObject* Storage::getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache)
|
|
|
|
|
{
|
|
|
|
|
LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY));
|
|
|
|
|
LandCache::Map::iterator found = cache.mMap.find(cellLocation);
|
|
|
|
|
if (found != cache.mMap.end())
|
|
|
|
|
return found->second;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first;
|
|
|
|
|
found = cache.mMap.insert(std::make_pair(cellLocation, getLand(cellLocation))).first;
|
|
|
|
|
return found->second;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Storage::adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const {}
|
|
|
|
|
void Storage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const {}
|
|
|
|
|
|
|
|
|
|
float Storage::getAlteredHeight(int col, int row) const
|
|
|
|
|
{
|
|
|
|
@ -591,14 +621,14 @@ namespace ESMTerrain
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Storage::getCellWorldSize()
|
|
|
|
|
float Storage::getCellWorldSize(ESM::RefId worldspace)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<float>(ESM::Land::REAL_SIZE);
|
|
|
|
|
return static_cast<float>(ESM::getCellSize(worldspace));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Storage::getCellVertices()
|
|
|
|
|
int Storage::getCellVertices(ESM::RefId worldspace)
|
|
|
|
|
{
|
|
|
|
|
return ESM::Land::LAND_SIZE;
|
|
|
|
|
return ESM::getLandSize(worldspace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Storage::getBlendmapScale(float chunkSize)
|
|
|
|
|