1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-20 17:23:53 +00:00
openmw/apps/opencs/view/render/terrainselection.cpp
florent.teppe 65cdd489fb create a specific esm reader function for RefID to avoid allocation for string and then again for RefId
Fixed some types

removed useless header

applied clang format

fixed compile tests

fixed clang tidy, and closer to logic before this MR

Removed hardcoded refids

unless there is a returned value we don't use static RefIds
can use == between RefId and hardcoded string

Fix clang format

Fixed a few instances where std::string was used, when only const std::string& was needed

removed unused variable
2022-12-27 19:15:57 +01:00

350 lines
14 KiB
C++

#include "terrainselection.hpp"
#include <algorithm>
#include <cmath>
#include <osg/GL>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/PrimitiveSet>
#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Vec3f>
#include <apps/opencs/model/doc/document.hpp>
#include <apps/opencs/model/world/cellcoordinates.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 <components/esm3/loadland.hpp>
#include "cell.hpp"
#include "worldspacewidget.hpp"
namespace CSMWorld
{
struct Cell;
}
CSVRender::TerrainSelection::TerrainSelection(
osg::Group* parentNode, WorldspaceWidget* worldspaceWidget, TerrainSelectionType type)
: mParentNode(parentNode)
, mWorldspaceWidget(worldspaceWidget)
, mSelectionType(type)
{
mGeometry = new osg::Geometry();
mSelectionNode = new osg::Group();
mSelectionNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mSelectionNode->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin");
mSelectionNode->addChild(mGeometry);
activate();
}
CSVRender::TerrainSelection::~TerrainSelection()
{
deactivate();
}
std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const
{
return mSelection;
}
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>>& localPositions)
{
mSelection = localPositions;
update();
}
void CSVRender::TerrainSelection::addSelect(const std::vector<std::pair<int, int>>& localPositions)
{
handleSelection(localPositions, SelectionMethod::AddSelect);
}
void CSVRender::TerrainSelection::removeSelect(const std::vector<std::pair<int, int>>& localPositions)
{
handleSelection(localPositions, SelectionMethod::RemoveSelect);
}
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>>& localPositions)
{
handleSelection(localPositions, SelectionMethod::ToggleSelect);
}
void CSVRender::TerrainSelection::clearTemporarySelection()
{
mTemporarySelection.clear();
}
void CSVRender::TerrainSelection::activate()
{
if (!mParentNode->containsNode(mSelectionNode))
mParentNode->addChild(mSelectionNode);
}
void CSVRender::TerrainSelection::deactivate()
{
mParentNode->removeChild(mSelectionNode);
}
void CSVRender::TerrainSelection::update()
{
mSelectionNode->removeChild(mGeometry);
mGeometry = new osg::Geometry();
const osg::ref_ptr<osg::Vec3Array> vertices(new osg::Vec3Array);
switch (mSelectionType)
{
case TerrainSelectionType::Texture:
drawTextureSelection(vertices);
break;
case TerrainSelectionType::Shape:
drawShapeSelection(vertices);
break;
}
mGeometry->setVertexArray(vertices);
osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);
drawArrays->setCount(vertices->size());
if (vertices->size() != 0)
mGeometry->addPrimitiveSet(drawArrays);
mSelectionNode->addChild(mGeometry);
}
void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
for (std::pair<int, int>& localPos : mSelection)
{
int x(localPos.first);
int y(localPos.second);
float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1),
calculateLandHeight(x, y - 1)));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord,
calculateLandHeight(x - 1, y)));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1),
calculateLandHeight(x, y + 1)));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord,
calculateLandHeight(x + 1, y)));
}
}
}
void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE)
/ (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?
const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE;
for (std::pair<int, int>& localPos : mSelection)
{
int x(localPos.first);
int y(localPos.second);
// convert texture selection to global vertex coordinates at selection box corners
int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge;
int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge;
int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge;
int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge;
// Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)
// Check adjancent selections, draw lines only to edges of the selection
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
for (int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x)
+ (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x)
+ i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(
osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1),
calculateLandHeight(x1 + (i - 1), y2)));
vertices->push_back(
osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1),
calculateLandHeight(x1 + i, y2)));
}
}
const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1));
if (south == mSelection.end())
{
for (int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x)
+ (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x)
+ i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(
osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y),
calculateLandHeight(x1 + (i - 1), y1)));
vertices->push_back(osg::Vec3f(drawCurrentX,
CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1 + i, y1)));
}
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
for (int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y)
+ (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y)
+ i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1),
drawPreviousY, calculateLandHeight(x2, y1 + (i - 1))));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1),
drawCurrentY, calculateLandHeight(x2, y1 + i)));
}
}
const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y));
if (west == mSelection.end())
{
for (int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y)
+ (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y)
+ i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x),
drawPreviousY, calculateLandHeight(x1, y1 + (i - 1))));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x),
drawCurrentY, calculateLandHeight(x1, y1 + i)));
}
}
}
}
}
void CSVRender::TerrainSelection::handleSelection(
const std::vector<std::pair<int, int>>& localPositions, SelectionMethod selectionMethod)
{
for (auto const& localPos : localPositions)
{
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
switch (selectionMethod)
{
case SelectionMethod::OnlySelect:
break;
case SelectionMethod::AddSelect:
if (iter == mSelection.end())
{
mSelection.emplace_back(localPos);
}
break;
case SelectionMethod::RemoveSelect:
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
break;
case SelectionMethod::ToggleSelect:
{
const auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
if (iterTemp == mTemporarySelection.end())
{
if (iter == mSelection.end())
{
mSelection.emplace_back(localPos);
}
else
{
mSelection.erase(iter);
}
}
mTemporarySelection.emplace_back(localPos);
break;
}
default:
break;
}
}
update();
}
bool CSVRender::TerrainSelection::noCell(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Cell>& cellCollection = document.getData().getCells();
return cellCollection.searchId(cellId) == -1;
}
bool CSVRender::TerrainSelection::noLand(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();
return landCollection.searchId(cellId) == -1;
}
bool CSVRender::TerrainSelection::noLandLoaded(const std::string& cellId)
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
const CSMWorld::IdCollection<CSMWorld::Land>& landCollection = document.getData().getLand();
return !landCollection.getRecord(ESM::RefId::stringRefId(cellId)).get().isDataLoaded(ESM::Land::DATA_VNML);
}
bool CSVRender::TerrainSelection::isLandLoaded(const std::string& cellId)
{
if (!noCell(cellId) && !noLand(cellId) && !noLandLoaded(cellId))
return true;
return false;
}
int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
{
int cellX = std::floor(static_cast<float>(x) / (ESM::Land::LAND_SIZE - 1));
int cellY = std::floor(static_cast<float>(y) / (ESM::Land::LAND_SIZE - 1));
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);
CSMWorld::CellCoordinates coords(cellX, cellY);
float landHeight = 0.f;
if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))
{
landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);
}
else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY)))
{
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
const ESM::Land::LandData* landData = document.getData()
.getLand()
.getRecord(ESM::RefId::stringRefId(cellId))
.get()
.getLandData(ESM::Land::DATA_VHGT);
return landData->mHeights[localY * ESM::Land::LAND_SIZE + localX];
}
return landHeight;
}