mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
16 KiB
C++
439 lines
16 KiB
C++
#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_LIGHTING, osg::StateAttribute::OFF);
|
|
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_LIGHTING, osg::StateAttribute::OFF);
|
|
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_LIGHTING, osg::StateAttribute::OFF);
|
|
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);
|
|
}
|
|
}
|
|
}
|