1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#include "terrainstorage.hpp"
#include <components/esm3/loadltex.hpp>
#include <components/esmterrain/storage.hpp>
#include <components/resource/resourcesystem.hpp>
#include <apps/opencs/model/world/data.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <algorithm>
#include <cmath>
#include <iterator>
#include <memory>
#include <osg/Vec4ub>
#include <stdexcept>
#include <stdlib.h>
#include <string>
namespace CSVRender
{
TerrainStorage::TerrainStorage(const CSMWorld::Data& data)
: ESMTerrain::Storage(data.getResourceSystem()->getVFS())
, mData(data)
{
resetHeights();
}
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation)
{
// The cell isn't guaranteed to have Land. This is because the terrain implementation
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
const int index = mData.getLand().searchId(
ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellLocation.mX, cellLocation.mY)));
if (index == -1)
return nullptr;
const ESM::Land& land = mData.getLand().getRecord(index).get();
return new ESMTerrain::LandObject(
land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX);
}
const std::string* TerrainStorage::getLandTexture(std::uint16_t index, int plugin)
{
return mData.getLandTextures().getLandTexture(index, plugin);
}
void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height)
{
mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX]
= height - fmod(height, 8); // Limit to divisible by 8 to avoid cell seam breakage
}
void TerrainStorage::resetHeights()
{
std::fill(std::begin(mAlteredHeight), std::end(mAlteredHeight), 0);
}
float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{
float height = 0.f;
const int index
= mData.getLand().searchId(ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)));
if (index == -1) // no land!
return height;
const ESM::Land::LandData* landData = mData.getLand().getRecord(index).get().getLandData(ESM::Land::DATA_VHGT);
height = landData->mHeights[inCellY * ESM::Land::LAND_SIZE + inCellX];
return mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX] + height;
}
float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)
{
return &mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX];
}
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace)
{
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
throw std::runtime_error("getBounds not implemented");
}
int TerrainStorage::getThisHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[col * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getLeftHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[(col)*ESM::Land::LAND_SIZE + row - 1]
+ mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];
}
int TerrainStorage::getRightHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[col * ESM::Land::LAND_SIZE + row + 1]
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row + 1)];
}
int TerrainStorage::getUpHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[(col - 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col - 1) * ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getDownHeight(int col, int row, std::span<const float> heightData) const
{
return heightData[(col + 1) * ESM::Land::LAND_SIZE + row]
+ mAlteredHeight[static_cast<unsigned int>((col + 1) * ESM::Land::LAND_SIZE + row)];
}
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, std::span<const float> heightData) const
{
return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToRight(int col, int row, std::span<const float> heightData) const
{
return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToUp(int col, int row, std::span<const float> heightData) const
{
return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));
}
int TerrainStorage::getHeightDifferenceToDown(int col, int row, std::span<const float> heightData) const
{
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
}
bool TerrainStorage::leftOrUpIsOverTheLimit(
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
{
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit
|| getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
}
bool TerrainStorage::rightOrDownIsOverTheLimit(
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
{
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit
|| getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
}
void TerrainStorage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const
{
if (!heightData)
return;
// Highlight broken height changes
int heightWarningLimit = 1024;
if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights()))
|| ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1)
&& rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights())))
{
color.r() = 255;
color.g() = 0;
color.b() = 0;
}
}
float TerrainStorage::getAlteredHeight(int col, int row) const
{
return mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row)];
}
}
|