Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170#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(); }
std::shared_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 std::make_shared<const ESMTerrain::LandObject>( land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); }
const VFS::Path::Normalized* 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)]; }}