Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436#include "instanceselectionmode.hpp"
#include <cmath>#include <string>#include <vector>
#include <apps/opencs/model/doc/document.hpp>#include <apps/opencs/model/world/data.hpp>#include <apps/opencs/model/world/universalid.hpp>#include <apps/opencs/view/render/mask.hpp>#include <apps/opencs/view/render/selectionmode.hpp>
#include <osg/Array>#include <osg/GL>#include <osg/Geometry>#include <osg/Group>#include <osg/Math>#include <osg/PositionAttitudeTransform>#include <osg/PrimitiveSet>#include <osg/StateAttribute>#include <osg/StateSet>#include <osg/Vec3d>#include <osg/Vec3f>#include <osg/Vec4f>#include <osg/ref_ptr>
#include <QAction>#include <QMenu>#include <QPoint>
#include "../../model/world/commands.hpp"#include "../../model/world/idtable.hpp"
#include "instancedragmodes.hpp"#include "object.hpp"#include "worldspacewidget.hpp"
namespace CSVWidget{ class SceneToolbar;}
namespace CSVRender{ class TagBase;
InstanceSelectionMode::InstanceSelectionMode( CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget, osg::Group* cellNode) : SelectionMode(parent, worldspaceWidget, Mask_Reference) , mParentNode(cellNode) { mSelectSame = new QAction("Extend selection to instances with same object ID", this); mDeleteSelection = new QAction("Delete selected instances", this);
connect(mSelectSame, &QAction::triggered, this, &InstanceSelectionMode::selectSame); connect(mDeleteSelection, &QAction::triggered, this, &InstanceSelectionMode::deleteSelection); }
InstanceSelectionMode::~InstanceSelectionMode() { if (mBaseNode) mParentNode->removeChild(mBaseNode); }
void InstanceSelectionMode::setDragStart(const osg::Vec3d& dragStart) { mDragStart = dragStart; }
const osg::Vec3d& InstanceSelectionMode::getDragStart() { return mDragStart; }
void InstanceSelectionMode::dragEnded(const osg::Vec3d& dragEndPoint, DragMode dragMode) { float dragDistance = (mDragStart - dragEndPoint).length(); if (mBaseNode) mParentNode->removeChild(mBaseNode); if (getCurrentId() == "cube-centre") { osg::Vec3d pointA(mDragStart[0] - dragDistance, mDragStart[1] - dragDistance, mDragStart[2] - dragDistance); osg::Vec3d pointB(mDragStart[0] + dragDistance, mDragStart[1] + dragDistance, mDragStart[2] + dragDistance); getWorldspaceWidget().selectInsideCube(pointA, pointB, dragMode); } else if (getCurrentId() == "cube-corner") { getWorldspaceWidget().selectInsideCube(mDragStart, dragEndPoint, dragMode); } else if (getCurrentId() == "sphere") { getWorldspaceWidget().selectWithinDistance(mDragStart, dragDistance, dragMode); } }
void InstanceSelectionMode::drawSelectionCubeCentre(const osg::Vec3f& mousePlanePoint) { float dragDistance = (mDragStart - mousePlanePoint).length(); drawSelectionCube(mDragStart, dragDistance); }
void InstanceSelectionMode::drawSelectionCubeCorner(const osg::Vec3f& mousePlanePoint) { drawSelectionBox(mDragStart, mousePlanePoint); }
void InstanceSelectionMode::drawSelectionBox(const osg::Vec3d& pointA, const osg::Vec3d& pointB) { if (mBaseNode) mParentNode->removeChild(mBaseNode); mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->setPosition(pointA);
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
osg::Vec3Array* vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3f(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3f(0.0f, 0.0f, pointB[2] - pointA[2])); vertices->push_back(osg::Vec3f(0.0f, pointB[1] - pointA[1], 0.0f)); vertices->push_back(osg::Vec3f(0.0f, pointB[1] - pointA[1], pointB[2] - pointA[2]));
vertices->push_back(osg::Vec3f(pointB[0] - pointA[0], 0.0f, 0.0f)); vertices->push_back(osg::Vec3f(pointB[0] - pointA[0], 0.0f, pointB[2] - pointA[2])); vertices->push_back(osg::Vec3f(pointB[0] - pointA[0], pointB[1] - pointA[1], 0.0f)); vertices->push_back(osg::Vec3f(pointB[0] - pointA[0], pointB[1] - pointA[1], pointB[2] - pointA[2]));
geometry->setVertexArray(vertices);
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
// top primitives->push_back(2); primitives->push_back(1); primitives->push_back(0);
primitives->push_back(3); primitives->push_back(1); primitives->push_back(2);
// bottom primitives->push_back(4); primitives->push_back(5); primitives->push_back(6);
primitives->push_back(6); primitives->push_back(5); primitives->push_back(7);
// sides primitives->push_back(1); primitives->push_back(4); primitives->push_back(0);
primitives->push_back(4); primitives->push_back(1); primitives->push_back(5);
primitives->push_back(4); primitives->push_back(2); primitives->push_back(0);
primitives->push_back(6); primitives->push_back(2); primitives->push_back(4);
primitives->push_back(6); primitives->push_back(3); primitives->push_back(2);
primitives->push_back(7); primitives->push_back(3); primitives->push_back(6);
primitives->push_back(1); primitives->push_back(3); primitives->push_back(5);
primitives->push_back(5); primitives->push_back(3); primitives->push_back(7);
geometry->addPrimitiveSet(primitives);
osg::Vec4Array* colours = new osg::Vec4Array;
colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.5f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f));
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
geometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
mBaseNode->addChild(geometry); mParentNode->addChild(mBaseNode); }
void InstanceSelectionMode::drawSelectionCube(const osg::Vec3d& point, float radius) { if (mBaseNode) mParentNode->removeChild(mBaseNode); mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->setPosition(point);
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
osg::Vec3Array* vertices = new osg::Vec3Array; for (int i = 0; i < 2; ++i) { float height = i ? -radius : radius; vertices->push_back(osg::Vec3f(height, -radius, -radius)); vertices->push_back(osg::Vec3f(height, -radius, radius)); vertices->push_back(osg::Vec3f(height, radius, -radius)); vertices->push_back(osg::Vec3f(height, radius, radius)); }
geometry->setVertexArray(vertices);
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
// top primitives->push_back(2); primitives->push_back(1); primitives->push_back(0);
primitives->push_back(3); primitives->push_back(1); primitives->push_back(2);
// bottom primitives->push_back(4); primitives->push_back(5); primitives->push_back(6);
primitives->push_back(6); primitives->push_back(5); primitives->push_back(7);
// sides primitives->push_back(1); primitives->push_back(4); primitives->push_back(0);
primitives->push_back(4); primitives->push_back(1); primitives->push_back(5);
primitives->push_back(4); primitives->push_back(2); primitives->push_back(0);
primitives->push_back(6); primitives->push_back(2); primitives->push_back(4);
primitives->push_back(6); primitives->push_back(3); primitives->push_back(2);
primitives->push_back(7); primitives->push_back(3); primitives->push_back(6);
primitives->push_back(1); primitives->push_back(3); primitives->push_back(5);
primitives->push_back(5); primitives->push_back(3); primitives->push_back(7);
geometry->addPrimitiveSet(primitives);
osg::Vec4Array* colours = new osg::Vec4Array;
colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.5f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f)); colours->push_back(osg::Vec4f(0.3f, 0.3f, 0.4f, 0.2f)); colours->push_back(osg::Vec4f(0.9f, 0.9f, 1.0f, 0.2f));
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
geometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
mBaseNode->addChild(geometry); mParentNode->addChild(mBaseNode); }
void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3f& mousePlanePoint) { float dragDistance = (mDragStart - mousePlanePoint).length(); drawSelectionSphere(mDragStart, dragDistance); }
void InstanceSelectionMode::drawSelectionSphere(const osg::Vec3d& point, float radius) { if (mBaseNode) mParentNode->removeChild(mBaseNode); mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->setPosition(point);
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
osg::Vec3Array* vertices = new osg::Vec3Array; constexpr int resolution = 32; float radiusPerResolution = radius / resolution; float reciprocalResolution = 1.0f / resolution; float doubleReciprocalRes = reciprocalResolution * 2;
osg::Vec4Array* colours = new osg::Vec4Array;
for (int i = 0; i <= resolution; i += 2) { float iShifted = (static_cast<float>(i) - resolution / 2.0f); // i - 16 = -16 ... 16 float xPercentile = iShifted * doubleReciprocalRes; float x = xPercentile * radius; float thisRadius = sqrt(radius * radius - x * x);
// the next row float iShifted2 = (static_cast<float>(i + 1) - resolution / 2.0f); float xPercentile2 = iShifted2 * doubleReciprocalRes; float x2 = xPercentile2 * radius; float thisRadius2 = sqrt(radius * radius - x2 * x2);
for (int j = 0; j < resolution; ++j) { float vertexX = thisRadius * sin(j * reciprocalResolution * osg::PI * 2); float vertexY = i * radiusPerResolution * 2 - radius; float vertexZ = thisRadius * cos(j * reciprocalResolution * osg::PI * 2); float heightPercentage = (vertexZ + radius) / (radius * 2); vertices->push_back(osg::Vec3f(vertexX, vertexY, vertexZ)); colours->push_back(osg::Vec4f(heightPercentage, heightPercentage, heightPercentage, 0.3f));
float vertexNextRowX = thisRadius2 * sin(j * reciprocalResolution * osg::PI * 2); float vertexNextRowY = (i + 1) * radiusPerResolution * 2 - radius; float vertexNextRowZ = thisRadius2 * cos(j * reciprocalResolution * osg::PI * 2); float heightPercentageNextRow = (vertexZ + radius) / (radius * 2); vertices->push_back(osg::Vec3f(vertexNextRowX, vertexNextRowY, vertexNextRowZ)); colours->push_back( osg::Vec4f(heightPercentageNextRow, heightPercentageNextRow, heightPercentageNextRow, 0.3f)); } }
geometry->setVertexArray(vertices);
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, 0);
for (int i = 0; i < resolution; ++i) { // Even for (int j = 0; j < resolution * 2; ++j) { if (i * resolution * 2 + j > static_cast<int>(vertices->size()) - 1) continue; primitives->push_back(i * resolution * 2 + j); } if (i * resolution * 2 > static_cast<int>(vertices->size()) - 1) continue; primitives->push_back(i * resolution * 2); primitives->push_back(i * resolution * 2 + 1);
// Odd for (int j = 1; j < resolution * 2 - 2; j += 2) { if ((i + 1) * resolution * 2 + j - 1 > static_cast<int>(vertices->size()) - 1) continue; primitives->push_back((i + 1) * resolution * 2 + j - 1); primitives->push_back(i * resolution * 2 + j + 2); } if ((i + 2) * resolution * 2 - 2 > static_cast<int>(vertices->size()) - 1) continue; primitives->push_back((i + 2) * resolution * 2 - 2); primitives->push_back(i * resolution * 2 + 1); primitives->push_back((i + 1) * resolution * 2); }
geometry->addPrimitiveSet(primitives);
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
geometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
mBaseNode->addChild(geometry); mParentNode->addChild(mBaseNode); }
bool InstanceSelectionMode::createContextMenu(QMenu* menu) { if (menu) { SelectionMode::createContextMenu(menu);
menu->addAction(mSelectSame); menu->addAction(mDeleteSelection); }
return true; }
void InstanceSelectionMode::selectSame() { getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference); }
void InstanceSelectionMode::deleteSelection() { std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
CSMWorld::IdTable& referencesTable = dynamic_cast<CSMWorld::IdTable&>( *getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References));
for (std::vector<osg::ref_ptr<TagBase>>::iterator iter = selection.begin(); iter != selection.end(); ++iter) { CSMWorld::DeleteCommand* command = new CSMWorld::DeleteCommand( referencesTable, static_cast<ObjectTag*>(iter->get())->mObject->getReferenceId());
getWorldspaceWidget().getDocument().getUndoStack().push(command); } }}