mirror of
https://github.com/OpenMW/openmw.git
synced 2025-10-17 05:46:37 +00:00
Merge branch 'too-many-markers' into 'master'
FEAT(CS): Replace selection markers with a real one (#8139) Closes #8139 See merge request OpenMW/openmw!4349
This commit is contained in:
commit
a12a0916ed
23 changed files with 31523 additions and 431 deletions
|
@ -88,7 +88,7 @@ opencs_units (view/render
|
||||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
||||||
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
|
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands objectmarker
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (view/render
|
opencs_units (view/render
|
||||||
|
|
|
@ -180,7 +180,10 @@ void CSMPrefs::State::declare()
|
||||||
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic Projection Size Parameter")
|
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic Projection Size Parameter")
|
||||||
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
|
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
|
||||||
.setRange(10, 10000);
|
.setRange(10, 10000);
|
||||||
declareDouble(mValues->mRendering.mObjectMarkerAlpha, "Object Marker Transparency").setPrecision(2).setRange(0, 1);
|
declareDouble(mValues->mRendering.mObjectMarkerScale, "Object Marker Scale Factor")
|
||||||
|
.setPrecision(2)
|
||||||
|
.setRange(.01f, 100.f)
|
||||||
|
.setTooltip("Multiplier for the size of object selection markers.");
|
||||||
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
|
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
|
||||||
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
|
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
|
||||||
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
|
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
|
||||||
|
@ -376,6 +379,7 @@ void CSMPrefs::State::declare()
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneScaleSubmode, "Scale Object Submode");
|
declareShortcut(mValues->mKeyBindings.mSceneScaleSubmode, "Scale Object Submode");
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneRotateSubmode, "Rotate Object Submode");
|
declareShortcut(mValues->mKeyBindings.mSceneRotateSubmode, "Rotate Object Submode");
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneCameraCycle, "Cycle Camera Mode");
|
declareShortcut(mValues->mKeyBindings.mSceneCameraCycle, "Cycle Camera Mode");
|
||||||
|
declareShortcut(mValues->mKeyBindings.mSceneToggleMarker, "Toggle Selection Marker");
|
||||||
|
|
||||||
declareSubcategory("1st/Free Camera");
|
declareSubcategory("1st/Free Camera");
|
||||||
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
|
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
|
||||||
|
|
|
@ -258,7 +258,7 @@ namespace CSMPrefs
|
||||||
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
|
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
|
||||||
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
|
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
|
||||||
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
|
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
|
||||||
Settings::SettingValue<double> mObjectMarkerAlpha{ mIndex, sName, "object-marker-alpha", 0.5 };
|
Settings::SettingValue<double> mObjectMarkerScale{ mIndex, sName, "object-marker-scale", 5.0 };
|
||||||
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
|
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
|
||||||
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
|
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
|
||||||
"#6e7880" };
|
"#6e7880" };
|
||||||
|
@ -491,7 +491,7 @@ namespace CSMPrefs
|
||||||
Settings::SettingValue<std::string> mSceneScaleSubmode{ mIndex, sName, "scene-submode-scale", "V" };
|
Settings::SettingValue<std::string> mSceneScaleSubmode{ mIndex, sName, "scene-submode-scale", "V" };
|
||||||
Settings::SettingValue<std::string> mSceneRotateSubmode{ mIndex, sName, "scene-submode-rotate", "R" };
|
Settings::SettingValue<std::string> mSceneRotateSubmode{ mIndex, sName, "scene-submode-rotate", "R" };
|
||||||
Settings::SettingValue<std::string> mSceneCameraCycle{ mIndex, sName, "scene-cam-cycle", "Tab" };
|
Settings::SettingValue<std::string> mSceneCameraCycle{ mIndex, sName, "scene-cam-cycle", "Tab" };
|
||||||
Settings::SettingValue<std::string> mSceneToggleMarkers{ mIndex, sName, "scene-toggle-markers", "F4" };
|
Settings::SettingValue<std::string> mSceneToggleMarker{ mIndex, sName, "scene-toggle-marker", "F4" };
|
||||||
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
|
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
|
||||||
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
|
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
|
||||||
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
|
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
|
||||||
|
|
|
@ -25,9 +25,10 @@
|
||||||
#include "cellwater.hpp"
|
#include "cellwater.hpp"
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
#include "mask.hpp"
|
#include "mask.hpp"
|
||||||
#include "object.hpp"
|
#include "objectmarker.hpp"
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "terrainstorage.hpp"
|
#include "terrainstorage.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
|
|
||||||
#include <apps/opencs/model/world/cell.hpp>
|
#include <apps/opencs/model/world/cell.hpp>
|
||||||
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
||||||
|
@ -107,9 +108,6 @@ bool CSVRender::Cell::addObjects(int start, int end)
|
||||||
|
|
||||||
auto object = std::make_unique<Object>(mData, mCellNode, id, false);
|
auto object = std::make_unique<Object>(mData, mCellNode, id, false);
|
||||||
|
|
||||||
if (mSubModeElementMask & Mask_Reference)
|
|
||||||
object->setSubMode(mSubMode);
|
|
||||||
|
|
||||||
mObjects.insert(std::make_pair(id, object.release()));
|
mObjects.insert(std::make_pair(id, object.release()));
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
@ -168,9 +166,10 @@ void CSVRender::Cell::unloadLand()
|
||||||
mCellBorder.reset();
|
mCellBorder.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::Cell::Cell(
|
CSVRender::Cell::Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode,
|
||||||
CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted, bool isExterior)
|
const std::string& id, bool deleted, bool isExterior)
|
||||||
: mData(document.getData())
|
: mSelectionMarker(selectionMarker)
|
||||||
|
, mData(document.getData())
|
||||||
, mId(ESM::RefId::stringRefId(id))
|
, mId(ESM::RefId::stringRefId(id))
|
||||||
, mDeleted(deleted)
|
, mDeleted(deleted)
|
||||||
, mSubMode(0)
|
, mSubMode(0)
|
||||||
|
@ -466,7 +465,10 @@ void CSVRender::Cell::setSelection(int elementMask, Selection mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
iter->second->setSelected(selected);
|
iter->second->setSelected(selected);
|
||||||
|
if (selected)
|
||||||
|
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||||
{
|
{
|
||||||
|
@ -506,8 +508,10 @@ void CSVRender::Cell::selectAllWithSameParentId(int elementMask)
|
||||||
if (!iter->second->getSelected() && ids.find(iter->second->getReferenceableId()) != ids.end())
|
if (!iter->second->getSelected() && ids.find(iter->second->getReferenceableId()) != ids.end())
|
||||||
{
|
{
|
||||||
iter->second->setSelected(true);
|
iter->second->setSelected(true);
|
||||||
|
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
||||||
|
@ -520,6 +524,9 @@ void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
||||||
|
|
||||||
else if (dragMode == DragMode_Select_Invert)
|
else if (dragMode == DragMode_Select_Invert)
|
||||||
object->setSelected(!object->getSelected());
|
object->setSelected(!object->getSelected());
|
||||||
|
|
||||||
|
if (object->getSelected())
|
||||||
|
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)
|
void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)
|
||||||
|
@ -542,6 +549,8 @@ void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)
|
void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)
|
||||||
|
@ -555,6 +564,8 @@ void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distan
|
||||||
if (distanceFromObject < distance)
|
if (distanceFromObject < distance)
|
||||||
handleSelectDrag(object.second, dragMode);
|
handleSelectDrag(object.second, dragMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::setCellArrows(int mask)
|
void CSVRender::Cell::setCellArrows(int mask)
|
||||||
|
@ -625,9 +636,11 @@ void CSVRender::Cell::selectFromGroup(const std::vector<std::string>& group)
|
||||||
if (objectName == object->getReferenceId())
|
if (objectName == object->getReferenceId())
|
||||||
{
|
{
|
||||||
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
|
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
|
||||||
|
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::unhideAll()
|
void CSVRender::Cell::unhideAll()
|
||||||
|
@ -673,8 +686,7 @@ void CSVRender::Cell::setSubMode(int subMode, unsigned int elementMask)
|
||||||
mSubModeElementMask = elementMask;
|
mSubModeElementMask = elementMask;
|
||||||
|
|
||||||
if (elementMask & Mask_Reference)
|
if (elementMask & Mask_Reference)
|
||||||
for (std::map<std::string, Object*>::const_iterator iter(mObjects.begin()); iter != mObjects.end(); ++iter)
|
mSelectionMarker->setSubMode(subMode);
|
||||||
iter->second->setSubMode(subMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::reset(unsigned int elementMask)
|
void CSVRender::Cell::reset(unsigned int elementMask)
|
||||||
|
@ -685,3 +697,11 @@ void CSVRender::Cell::reset(unsigned int elementMask)
|
||||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||||
mPathgrid->resetIndicators();
|
mPathgrid->resetIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::Cell::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
if (auto iter = mObjects.find(Misc::StringUtils::lowerCase(referenceId)); iter != mObjects.end())
|
||||||
|
return iter->second;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#include <osg/Vec3d>
|
#include <osg/Vec3d>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include "../../model/doc/document.hpp"
|
|
||||||
#include "../../model/world/cellcoordinates.hpp"
|
#include "../../model/world/cellcoordinates.hpp"
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
#include <components/esm/refid.hpp>
|
#include <components/esm/refid.hpp>
|
||||||
#include <components/misc/algorithm.hpp>
|
#include <components/misc/algorithm.hpp>
|
||||||
|
|
||||||
|
@ -44,8 +44,11 @@ namespace CSVRender
|
||||||
class CellBorder;
|
class CellBorder;
|
||||||
class CellMarker;
|
class CellMarker;
|
||||||
|
|
||||||
|
class ObjectMarker;
|
||||||
|
|
||||||
class Cell
|
class Cell
|
||||||
{
|
{
|
||||||
|
ObjectMarker* const mSelectionMarker;
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
ESM::RefId mId;
|
ESM::RefId mId;
|
||||||
osg::ref_ptr<osg::Group> mCellNode;
|
osg::ref_ptr<osg::Group> mCellNode;
|
||||||
|
@ -90,8 +93,8 @@ namespace CSVRender
|
||||||
public:
|
public:
|
||||||
/// \note Deleted covers both cells that are deleted and cells that don't exist in
|
/// \note Deleted covers both cells that are deleted and cells that don't exist in
|
||||||
/// the first place.
|
/// the first place.
|
||||||
Cell(CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted = false,
|
Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode, const std::string& id,
|
||||||
bool isExterior = false);
|
bool deleted = false, bool isExterior = false);
|
||||||
|
|
||||||
~Cell();
|
~Cell();
|
||||||
|
|
||||||
|
@ -182,6 +185,8 @@ namespace CSVRender
|
||||||
/// true state.
|
/// true state.
|
||||||
void reset(unsigned int elementMask);
|
void reset(unsigned int elementMask);
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId);
|
||||||
|
|
||||||
friend class CellNodeCallback;
|
friend class CellNodeCallback;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,7 +362,29 @@ CSVRender::InstanceMode::InstanceMode(
|
||||||
|
|
||||||
for (const char axis : "xyz")
|
for (const char axis : "xyz")
|
||||||
connect(new CSMPrefs::Shortcut(std::string("scene-axis-") + axis, worldspaceWidget),
|
connect(new CSMPrefs::Shortcut(std::string("scene-axis-") + axis, worldspaceWidget),
|
||||||
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] { this->setDragAxis(axis); });
|
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] {
|
||||||
|
this->setDragAxis(axis);
|
||||||
|
std::string axisStr(1, toupper(axis));
|
||||||
|
switch (getSubMode())
|
||||||
|
{
|
||||||
|
case (Object::Mode_Move):
|
||||||
|
axisStr += "_Axis";
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Rotate):
|
||||||
|
axisStr += "_Axis_Rot";
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Scale):
|
||||||
|
axisStr += "_Axis_Scale";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||||
|
|
||||||
|
if (mDragAxis != -1)
|
||||||
|
selectionMarker->updateMarkerHighlight(axisStr, axis - 'x');
|
||||||
|
else
|
||||||
|
selectionMarker->resetMarkerHighlight();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)
|
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)
|
||||||
|
@ -460,52 +482,58 @@ void CSVRender::InstanceMode::secondaryEditPressed(const WorldspaceHitResult& hi
|
||||||
|
|
||||||
void CSVRender::InstanceMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
if (hit.tag)
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
|
|
||||||
|
if (!hit.tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
// hit an Object, select it
|
||||||
{
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
// hit an Object, select it
|
object->setSelected(true);
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
object->setSelected(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
if (hit.tag)
|
if (!hit.tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
// hit an Object, toggle its selection state
|
||||||
{
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
// hit an Object, toggle its selection state
|
object->setSelected(!object->getSelected());
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
|
||||||
object->setSelected(!object->getSelected());
|
const auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||||
return;
|
|
||||||
}
|
if (object->getSelected())
|
||||||
|
selectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
|
|
||||||
|
selectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
|
if (auto* snapTarget
|
||||||
|
= dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get()))
|
||||||
if (snapTarget)
|
|
||||||
{
|
{
|
||||||
snapTarget->mObject->setSnapTarget(false);
|
snapTarget->mObject->setSnapTarget(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hit.tag)
|
if (!hit.tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
// hit an Object, toggle its selection state
|
||||||
{
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
// hit an Object, toggle its selection state
|
object->setSnapTarget(!object->getSnapTarget());
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
|
||||||
object->setSnapTarget(!object->getSnapTarget());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,23 +542,26 @@ bool CSVRender::InstanceMode::primaryEditStartDrag(const QPoint& pos)
|
||||||
if (mDragMode != DragMode_None || mLocked)
|
if (mDragMode != DragMode_None || mLocked)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
{
|
{
|
||||||
// Only change selection at the start of drag if no object is already selected
|
// Only change selection at the start of drag if no object is already selected
|
||||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(true);
|
object->setSelected(true);
|
||||||
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -591,23 +622,26 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
||||||
if (mDragMode != DragMode_None || mLocked)
|
if (mDragMode != DragMode_None || mLocked)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
{
|
{
|
||||||
// Only change selection at the start of drag if no object is already selected
|
// Only change selection at the start of drag if no object is already selected
|
||||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(true);
|
object->setSelected(true);
|
||||||
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -641,10 +675,10 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
||||||
mDragMode = DragMode_Scale_Snap;
|
mDragMode = DragMode_Scale_Snap;
|
||||||
|
|
||||||
// Calculate scale factor
|
// Calculate scale factor
|
||||||
std::vector<osg::ref_ptr<TagBase>> editedSelection = getWorldspaceWidget().getEdited(Mask_Reference);
|
std::vector<osg::ref_ptr<TagBase>> editedSelection = worldspaceWidget.getEdited(Mask_Reference);
|
||||||
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
|
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
|
||||||
|
|
||||||
int widgetHeight = getWorldspaceWidget().height();
|
int widgetHeight = worldspaceWidget.height();
|
||||||
|
|
||||||
float dx = pos.x() - center.x();
|
float dx = pos.x() - center.x();
|
||||||
float dy = (widgetHeight - pos.y()) - center.y();
|
float dy = (widgetHeight - pos.y()) - center.y();
|
||||||
|
|
|
@ -18,25 +18,11 @@
|
||||||
#include <apps/opencs/model/world/universalid.hpp>
|
#include <apps/opencs/model/world/universalid.hpp>
|
||||||
#include <apps/opencs/view/render/tagbase.hpp>
|
#include <apps/opencs/view/render/tagbase.hpp>
|
||||||
|
|
||||||
#include <osg/Array>
|
|
||||||
#include <osg/BoundingSphere>
|
|
||||||
#include <osg/GL>
|
|
||||||
#include <osg/Geometry>
|
|
||||||
#include <osg/Group>
|
|
||||||
#include <osg/Math>
|
|
||||||
#include <osg/Node>
|
|
||||||
#include <osg/PositionAttitudeTransform>
|
|
||||||
#include <osg/PrimitiveSet>
|
|
||||||
#include <osg/Quat>
|
#include <osg/Quat>
|
||||||
#include <osg/Shape>
|
|
||||||
#include <osg/ShapeDrawable>
|
#include <osg/ShapeDrawable>
|
||||||
#include <osg/StateAttribute>
|
|
||||||
#include <osg/StateSet>
|
|
||||||
#include <osg/Vec3>
|
|
||||||
|
|
||||||
#include <osgFX/Scribe>
|
#include <osgFX/Scribe>
|
||||||
|
|
||||||
#include "../../model/prefs/state.hpp"
|
|
||||||
#include "../../model/world/cellcoordinates.hpp"
|
#include "../../model/world/cellcoordinates.hpp"
|
||||||
#include "../../model/world/commandmacro.hpp"
|
#include "../../model/world/commandmacro.hpp"
|
||||||
#include "../../model/world/commands.hpp"
|
#include "../../model/world/commands.hpp"
|
||||||
|
@ -63,11 +49,6 @@ namespace ESM
|
||||||
struct Light;
|
struct Light;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float CSVRender::Object::MarkerShaftWidth = 30;
|
|
||||||
const float CSVRender::Object::MarkerShaftBaseLength = 70;
|
|
||||||
const float CSVRender::Object::MarkerHeadWidth = 50;
|
|
||||||
const float CSVRender::Object::MarkerHeadLength = 50;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -95,12 +76,6 @@ QString CSVRender::ObjectTag::getToolTip(bool /*hideBasics*/, const WorldspaceHi
|
||||||
return QString::fromUtf8(mObject->getReferenceableId().c_str());
|
return QString::fromUtf8(mObject->getReferenceableId().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
|
||||||
: ObjectTag(object)
|
|
||||||
, mAxis(axis)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::clear() {}
|
void CSVRender::Object::clear() {}
|
||||||
|
|
||||||
void CSVRender::Object::update()
|
void CSVRender::Object::update()
|
||||||
|
@ -204,238 +179,6 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const
|
||||||
return mData.getReferences().getRecord(mReferenceId).get();
|
return mData.getReferences().getRecord(mReferenceId).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::updateMarker()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
if (mMarker[i])
|
|
||||||
{
|
|
||||||
mRootNode->removeChild(mMarker[i]);
|
|
||||||
mMarker[i] = osg::ref_ptr<osg::Node>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mSelected)
|
|
||||||
{
|
|
||||||
if (mSubMode == 0)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
else if (mSubMode == 1)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeRotateMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
else if (mSubMode == 2)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker(int axis)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
|
|
||||||
|
|
||||||
float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius();
|
|
||||||
|
|
||||||
// shaft
|
|
||||||
osg::Vec3Array* vertices = new osg::Vec3Array;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i)
|
|
||||||
{
|
|
||||||
float length = i ? shaftLength : MarkerShaftWidth;
|
|
||||||
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
|
||||||
}
|
|
||||||
|
|
||||||
// head backside
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
|
|
||||||
// head
|
|
||||||
vertices->push_back(getMarkerPosition(0, 0, shaftLength + MarkerHeadLength, axis));
|
|
||||||
|
|
||||||
geometry->setVertexArray(vertices);
|
|
||||||
|
|
||||||
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
|
|
||||||
|
|
||||||
// shaft
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
int i2 = i == 3 ? 0 : i + 1;
|
|
||||||
primitives->push_back(i);
|
|
||||||
primitives->push_back(4 + i);
|
|
||||||
primitives->push_back(i2);
|
|
||||||
|
|
||||||
primitives->push_back(4 + i);
|
|
||||||
primitives->push_back(4 + i2);
|
|
||||||
primitives->push_back(i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cap
|
|
||||||
primitives->push_back(0);
|
|
||||||
primitives->push_back(1);
|
|
||||||
primitives->push_back(2);
|
|
||||||
|
|
||||||
primitives->push_back(2);
|
|
||||||
primitives->push_back(3);
|
|
||||||
primitives->push_back(0);
|
|
||||||
|
|
||||||
// head, backside
|
|
||||||
primitives->push_back(0 + 8);
|
|
||||||
primitives->push_back(1 + 8);
|
|
||||||
primitives->push_back(2 + 8);
|
|
||||||
|
|
||||||
primitives->push_back(2 + 8);
|
|
||||||
primitives->push_back(3 + 8);
|
|
||||||
primitives->push_back(0 + 8);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
primitives->push_back(12);
|
|
||||||
primitives->push_back(8 + (i == 3 ? 0 : i + 1));
|
|
||||||
primitives->push_back(8 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry->addPrimitiveSet(primitives);
|
|
||||||
|
|
||||||
osg::Vec4Array* colours = new osg::Vec4Array;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
colours->push_back(
|
|
||||||
osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency));
|
|
||||||
|
|
||||||
for (int i = 8; i < 8 + 4 + 1; ++i)
|
|
||||||
colours->push_back(
|
|
||||||
osg::Vec4f(axis == 0 ? 1.0f : 0.0f, axis == 1 ? 1.0f : 0.0f, axis == 2 ? 1.0f : 0.0f, mMarkerTransparency));
|
|
||||||
|
|
||||||
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
|
|
||||||
|
|
||||||
setupCommonMarkerState(geometry);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> group(new osg::Group);
|
|
||||||
group->addChild(geometry);
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker(int axis)
|
|
||||||
{
|
|
||||||
const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
|
|
||||||
const float OuterRadius = InnerRadius + MarkerShaftWidth;
|
|
||||||
|
|
||||||
const float SegmentDistance = 100.f;
|
|
||||||
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
|
|
||||||
const size_t VerticesPerSegment = 4;
|
|
||||||
const size_t IndicesPerSegment = 24;
|
|
||||||
|
|
||||||
const size_t VertexCount = SegmentCount * VerticesPerSegment;
|
|
||||||
const size_t IndexCount = SegmentCount * IndicesPerSegment;
|
|
||||||
|
|
||||||
const float Angle = 2 * osg::PI / SegmentCount;
|
|
||||||
|
|
||||||
const unsigned short IndexPattern[IndicesPerSegment]
|
|
||||||
= { 0, 4, 5, 0, 5, 1, 2, 6, 4, 2, 4, 0, 3, 7, 6, 3, 6, 2, 1, 5, 7, 1, 7, 3 };
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);
|
|
||||||
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
|
|
||||||
osg::ref_ptr<osg::DrawElementsUShort> primitives
|
|
||||||
= new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, IndexCount);
|
|
||||||
|
|
||||||
// prevent some depth collision issues from overlaps
|
|
||||||
osg::Vec3f offset = getMarkerPosition(0, MarkerShaftWidth / 4, 0, axis);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < SegmentCount; ++i)
|
|
||||||
{
|
|
||||||
size_t index = i * VerticesPerSegment;
|
|
||||||
|
|
||||||
float innerX = InnerRadius * std::cos(i * Angle);
|
|
||||||
float innerY = InnerRadius * std::sin(i * Angle);
|
|
||||||
|
|
||||||
float outerX = OuterRadius * std::cos(i * Angle);
|
|
||||||
float outerY = OuterRadius * std::sin(i * Angle);
|
|
||||||
|
|
||||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
colors->at(0)
|
|
||||||
= osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < SegmentCount; ++i)
|
|
||||||
{
|
|
||||||
size_t indices[IndicesPerSegment];
|
|
||||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
|
||||||
{
|
|
||||||
indices[j] = i * VerticesPerSegment + j;
|
|
||||||
|
|
||||||
if (indices[j] >= VertexCount)
|
|
||||||
indices[j] -= VertexCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t elementOffset = i * IndicesPerSegment;
|
|
||||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
|
||||||
{
|
|
||||||
primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry->setVertexArray(vertices);
|
|
||||||
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
|
|
||||||
geometry->addPrimitiveSet(primitives);
|
|
||||||
|
|
||||||
setupCommonMarkerState(geometry);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> group = new osg::Group();
|
|
||||||
group->addChild(geometry);
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::StateSet> state = geometry->getOrCreateStateSet();
|
|
||||||
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
|
||||||
state->setMode(GL_BLEND, osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3f CSVRender::Object::getMarkerPosition(float x, float y, float z, int axis)
|
|
||||||
{
|
|
||||||
switch (axis)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
return osg::Vec3f(x, y, z);
|
|
||||||
case 0:
|
|
||||||
return osg::Vec3f(z, x, y);
|
|
||||||
case 1:
|
|
||||||
return osg::Vec3f(y, z, x);
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
throw std::logic_error("invalid axis for marker geometry");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CSVRender::Object::Object(
|
CSVRender::Object::Object(
|
||||||
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
|
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
|
||||||
: mData(data)
|
: mData(data)
|
||||||
|
@ -446,8 +189,6 @@ CSVRender::Object::Object(
|
||||||
, mForceBaseToZero(forceBaseToZero)
|
, mForceBaseToZero(forceBaseToZero)
|
||||||
, mScaleOverride(1)
|
, mScaleOverride(1)
|
||||||
, mOverrideFlags(0)
|
, mOverrideFlags(0)
|
||||||
, mSubMode(-1)
|
|
||||||
, mMarkerTransparency(0.5f)
|
|
||||||
{
|
{
|
||||||
mRootNode = new osg::PositionAttitudeTransform;
|
mRootNode = new osg::PositionAttitudeTransform;
|
||||||
|
|
||||||
|
@ -476,7 +217,6 @@ CSVRender::Object::Object(
|
||||||
|
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::Object::~Object()
|
CSVRender::Object::~Object()
|
||||||
|
@ -506,9 +246,6 @@ void CSVRender::Object::setSelected(bool selected, const osg::Vec4f& color)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRootNode->addChild(mBaseNode);
|
mRootNode->addChild(mBaseNode);
|
||||||
|
|
||||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::Object::getSelected() const
|
bool CSVRender::Object::getSelected() const
|
||||||
|
@ -536,9 +273,6 @@ void CSVRender::Object::setSnapTarget(bool isSnapTarget)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRootNode->addChild(mBaseNode);
|
mRootNode->addChild(mBaseNode);
|
||||||
|
|
||||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::Object::getSnapTarget() const
|
bool CSVRender::Object::getSnapTarget() const
|
||||||
|
@ -566,7 +300,6 @@ bool CSVRender::Object::referenceableDataChanged(const QModelIndex& topLeft, con
|
||||||
{
|
{
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +347,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
||||||
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
|
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
|
||||||
|
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -626,7 +358,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
||||||
void CSVRender::Object::reloadAssets()
|
void CSVRender::Object::reloadAssets()
|
||||||
{
|
{
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CSVRender::Object::getReferenceId() const
|
std::string CSVRender::Object::getReferenceId() const
|
||||||
|
@ -720,12 +451,6 @@ void CSVRender::Object::setScale(float scale)
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::setMarkerTransparency(float value)
|
|
||||||
{
|
|
||||||
mMarkerTransparency = value;
|
|
||||||
updateMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
||||||
{
|
{
|
||||||
const CSMWorld::RefCollection& collection = mData.getReferences();
|
const CSMWorld::RefCollection& collection = mData.getReferences();
|
||||||
|
@ -796,18 +521,8 @@ void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
||||||
mOverrideFlags = 0;
|
mOverrideFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::setSubMode(int subMode)
|
|
||||||
{
|
|
||||||
if (subMode != mSubMode)
|
|
||||||
{
|
|
||||||
mSubMode = subMode;
|
|
||||||
updateMarker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::reset()
|
void CSVRender::Object::reset()
|
||||||
{
|
{
|
||||||
mOverrideFlags = 0;
|
mOverrideFlags = 0;
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,14 +58,6 @@ namespace CSVRender
|
||||||
QString getToolTip(bool hideBasics, const WorldspaceHitResult& hit) const override;
|
QString getToolTip(bool hideBasics, const WorldspaceHitResult& hit) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectMarkerTag : public ObjectTag
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectMarkerTag(Object* object, int axis);
|
|
||||||
|
|
||||||
int mAxis;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Object
|
class Object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -76,12 +68,22 @@ namespace CSVRender
|
||||||
Override_Scale = 4
|
Override_Scale = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
enum SubMode
|
||||||
static const float MarkerShaftWidth;
|
{
|
||||||
static const float MarkerShaftBaseLength;
|
Mode_Move,
|
||||||
static const float MarkerHeadWidth;
|
Mode_Rotate,
|
||||||
static const float MarkerHeadLength;
|
Mode_Scale,
|
||||||
|
Mode_None,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Axis
|
||||||
|
{
|
||||||
|
Axis_X,
|
||||||
|
Axis_Y,
|
||||||
|
Axis_Z
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
ESM::RefId mReferenceId;
|
ESM::RefId mReferenceId;
|
||||||
ESM::RefId mReferenceableId;
|
ESM::RefId mReferenceableId;
|
||||||
|
@ -96,9 +98,6 @@ namespace CSVRender
|
||||||
ESM::Position mPositionOverride;
|
ESM::Position mPositionOverride;
|
||||||
float mScaleOverride;
|
float mScaleOverride;
|
||||||
int mOverrideFlags;
|
int mOverrideFlags;
|
||||||
osg::ref_ptr<osg::Node> mMarker[3];
|
|
||||||
int mSubMode;
|
|
||||||
float mMarkerTransparency;
|
|
||||||
std::unique_ptr<Actor> mActor;
|
std::unique_ptr<Actor> mActor;
|
||||||
|
|
||||||
/// Not implemented
|
/// Not implemented
|
||||||
|
@ -120,16 +119,6 @@ namespace CSVRender
|
||||||
/// Throws an exception if *this was constructed with referenceable
|
/// Throws an exception if *this was constructed with referenceable
|
||||||
const CSMWorld::CellRef& getReference() const;
|
const CSMWorld::CellRef& getReference() const;
|
||||||
|
|
||||||
void updateMarker();
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> makeMoveOrScaleMarker(int axis);
|
|
||||||
osg::ref_ptr<osg::Node> makeRotateMarker(int axis);
|
|
||||||
|
|
||||||
/// Sets up a stateset with properties common to all marker types.
|
|
||||||
void setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry);
|
|
||||||
|
|
||||||
osg::Vec3f getMarkerPosition(float x, float y, float z, int axis);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Object(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, bool referenceable,
|
Object(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, bool referenceable,
|
||||||
bool forceBaseToZero = false);
|
bool forceBaseToZero = false);
|
||||||
|
@ -199,8 +188,6 @@ namespace CSVRender
|
||||||
/// Apply override changes via command and end edit mode
|
/// Apply override changes via command and end edit mode
|
||||||
void apply(CSMWorld::CommandMacro& commands);
|
void apply(CSMWorld::CommandMacro& commands);
|
||||||
|
|
||||||
void setSubMode(int subMode);
|
|
||||||
|
|
||||||
/// Erase all overrides and restore the visual representation of the object to its
|
/// Erase all overrides and restore the visual representation of the object to its
|
||||||
/// true state.
|
/// true state.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
307
apps/opencs/view/render/objectmarker.cpp
Normal file
307
apps/opencs/view/render/objectmarker.cpp
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include <osg/ClipPlane>
|
||||||
|
#include <osg/Material>
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/sceneutil/visitor.hpp>
|
||||||
|
|
||||||
|
#include "../../model/prefs/state.hpp"
|
||||||
|
#include "objectmarker.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class FindMaterialVisitor : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FindMaterialVisitor(CSVRender::NodeMap& map)
|
||||||
|
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mMap(map)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Geometry& node) override
|
||||||
|
{
|
||||||
|
osg::StateSet* state = node.getStateSet();
|
||||||
|
if (state->getAttribute(osg::StateAttribute::MATERIAL))
|
||||||
|
mMap.emplace(node.getName(), &node);
|
||||||
|
|
||||||
|
traverse(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CSVRender::NodeMap& mMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ToCamera : public SceneUtil::NodeCallback<ToCamera, osg::Node*, osgUtil::CullVisitor*>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ToCamera(osg::ref_ptr<osg::ClipPlane> clipPlane)
|
||||||
|
: mClipPlane(std::move(clipPlane))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||||
|
{
|
||||||
|
osg::Vec3f normal = cv->getEyePoint();
|
||||||
|
mClipPlane->setClipPlane(normal.x(), normal.y(), normal.z(), 0);
|
||||||
|
traverse(node, cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osg::ClipPlane> mClipPlane;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto addTagToActiveMarkerNodes = [](CSVRender::NodeMap& mMarkerNodes, CSVRender::Object* object,
|
||||||
|
std::initializer_list<std::string> suffixes) {
|
||||||
|
for (const auto& markerSuffix : suffixes)
|
||||||
|
{
|
||||||
|
for (char axis = 'X'; axis <= 'Z'; ++axis)
|
||||||
|
mMarkerNodes[axis + markerSuffix]->setUserData(new CSVRender::ObjectMarkerTag(object, axis - 'X'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVRender
|
||||||
|
{
|
||||||
|
ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
||||||
|
: ObjectTag(object)
|
||||||
|
, mAxis(axis)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectMarker::ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem)
|
||||||
|
: mWorldspaceWidget(worldspaceWidget)
|
||||||
|
, mResourceSystem(resourceSystem)
|
||||||
|
, mMarkerScale(CSMPrefs::get()["Rendering"]["object-marker-scale"].toDouble())
|
||||||
|
, mSubMode(Object::Mode_None)
|
||||||
|
{
|
||||||
|
mBaseNode = new osg::PositionAttitudeTransform;
|
||||||
|
mBaseNode->setNodeMask(Mask_Reference);
|
||||||
|
mBaseNode->setScale(osg::Vec3f(mMarkerScale, mMarkerScale, mMarkerScale));
|
||||||
|
|
||||||
|
mRootNode = new osg::PositionAttitudeTransform;
|
||||||
|
mRootNode->addChild(mBaseNode);
|
||||||
|
worldspaceWidget->setSelectionMarkerRoot(mRootNode);
|
||||||
|
|
||||||
|
QFile file(":render/selection-marker");
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
throw std::runtime_error("Failed to open selection marker file");
|
||||||
|
|
||||||
|
auto markerData = file.readAll();
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->loadSelectionMarker(mBaseNode, markerData.data(), markerData.size());
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> baseNodeState = mBaseNode->getOrCreateStateSet();
|
||||||
|
baseNodeState->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||||
|
baseNodeState->setRenderBinDetails(1000, "RenderBin");
|
||||||
|
|
||||||
|
FindMaterialVisitor matMapper(mMarkerNodes);
|
||||||
|
|
||||||
|
mBaseNode->accept(matMapper);
|
||||||
|
|
||||||
|
for (const auto& [name, node] : mMarkerNodes)
|
||||||
|
{
|
||||||
|
osg::StateSet* state = node->getStateSet();
|
||||||
|
osg::Material* mat = static_cast<osg::Material*>(state->getAttribute(osg::StateAttribute::MATERIAL));
|
||||||
|
osg::Vec4f emis = mat->getEmission(osg::Material::FRONT_AND_BACK);
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, emis / 4);
|
||||||
|
mOriginalColors.emplace(name, emis);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneUtil::NodeMap sceneNodes;
|
||||||
|
SceneUtil::NodeMapVisitor nodeMapper(sceneNodes);
|
||||||
|
mBaseNode->accept(nodeMapper);
|
||||||
|
|
||||||
|
mMarkerNodes.insert(sceneNodes.begin(), sceneNodes.end());
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> rotateMarkers = mMarkerNodes["rotateMarkers"];
|
||||||
|
osg::ClipPlane* clip = new osg::ClipPlane(0);
|
||||||
|
rotateMarkers->setCullCallback(new ToCamera(clip));
|
||||||
|
rotateMarkers->getStateSet()->setAttributeAndModes(clip, osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::toggleVisibility()
|
||||||
|
{
|
||||||
|
bool isVisible = mBaseNode->getNodeMask() == Mask_Reference;
|
||||||
|
mBaseNode->setNodeMask(isVisible ? Mask_Hidden : Mask_Reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateScale(const float scale)
|
||||||
|
{
|
||||||
|
mMarkerScale = scale;
|
||||||
|
mBaseNode->setScale(osg::Vec3f(scale, scale, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::setSubMode(const int subMode)
|
||||||
|
{
|
||||||
|
if (subMode == mSubMode)
|
||||||
|
return;
|
||||||
|
mSubMode = subMode;
|
||||||
|
resetMarkerHighlight();
|
||||||
|
updateSelectionMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectMarker::hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera)
|
||||||
|
{
|
||||||
|
if (mSubMode != Object::Mode_Rotate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
osg::Vec3d center, eye, forwardVector, _;
|
||||||
|
std::vector<osg::Node*> rotMark = mMarkerNodes["rotateMarkers"]->getParentalNodePaths()[0];
|
||||||
|
const osg::Vec3f markerPos = osg::computeLocalToWorld(rotMark).getTrans();
|
||||||
|
|
||||||
|
camera->getViewMatrixAsLookAt(eye, center, _);
|
||||||
|
forwardVector = center - eye;
|
||||||
|
forwardVector.normalize();
|
||||||
|
|
||||||
|
return (hitPos - markerPos) * forwardVector > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectMarker::attachMarker(const std::string& refId)
|
||||||
|
{
|
||||||
|
const auto& object = mWorldspaceWidget->getObjectByReferenceId(refId);
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
removeFromSelectionHistory(refId);
|
||||||
|
|
||||||
|
if (!object || !object->getSelected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!object->getRootNode()->addChild(mRootNode))
|
||||||
|
throw std::runtime_error("Failed to add marker to object");
|
||||||
|
|
||||||
|
std::string parentMarkerNode;
|
||||||
|
|
||||||
|
switch (mSubMode)
|
||||||
|
{
|
||||||
|
case (Object::Mode_Rotate):
|
||||||
|
parentMarkerNode = "rotateMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Rot" });
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Scale):
|
||||||
|
parentMarkerNode = "scaleMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Scale", "_Wall_Scale" });
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Move):
|
||||||
|
default:
|
||||||
|
parentMarkerNode = "moveMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis", "_Wall" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMarkerNodes[parentMarkerNode]->asGroup()->setNodeMask(Mask_Reference);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::detachMarker()
|
||||||
|
{
|
||||||
|
for (std::size_t index = mRootNode->getNumParents(); index > 0;)
|
||||||
|
mRootNode->getParent(--index)->removeChild(mRootNode);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> widgetRoot = mMarkerNodes["unitArrows"]->asGroup();
|
||||||
|
for (std::size_t index = widgetRoot->getNumChildren(); index > 0;)
|
||||||
|
widgetRoot->getChild(--index)->setNodeMask(Mask_Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::addToSelectionHistory(const std::string& refId, bool update)
|
||||||
|
{
|
||||||
|
auto foundObject = std::find_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||||
|
[&refId](const std::string& objId) { return objId == refId; });
|
||||||
|
|
||||||
|
if (foundObject == mSelectionHistory.end())
|
||||||
|
mSelectionHistory.push_back(refId);
|
||||||
|
else
|
||||||
|
std::rotate(foundObject, foundObject + 1, mSelectionHistory.end());
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
updateSelectionMarker(refId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::removeFromSelectionHistory(const std::string& refId)
|
||||||
|
{
|
||||||
|
mSelectionHistory.erase(std::remove_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||||
|
[&refId](const std::string& objId) { return objId == refId; }),
|
||||||
|
mSelectionHistory.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateSelectionMarker(const std::string& refId)
|
||||||
|
{
|
||||||
|
if (mSelectionHistory.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
detachMarker();
|
||||||
|
|
||||||
|
if (refId.empty())
|
||||||
|
{
|
||||||
|
for (std::size_t index = mSelectionHistory.size(); index > 0;)
|
||||||
|
if (attachMarker(mSelectionHistory[--index]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
attachMarker(refId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::resetMarkerHighlight()
|
||||||
|
{
|
||||||
|
if (mLastHighlightedNodes.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto& [nodeName, mat] : mLastHighlightedNodes)
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, mat->getEmission(osg::Material::FRONT_AND_BACK) / 4);
|
||||||
|
|
||||||
|
mLastHighlightedNodes.clear();
|
||||||
|
mLastHitNode.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateMarkerHighlight(const std::string_view hitNode, const int axis)
|
||||||
|
{
|
||||||
|
if (hitNode == mLastHitNode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
resetMarkerHighlight();
|
||||||
|
|
||||||
|
std::string colorName;
|
||||||
|
|
||||||
|
switch (axis)
|
||||||
|
{
|
||||||
|
case Object::Axis_X:
|
||||||
|
colorName = "red";
|
||||||
|
break;
|
||||||
|
case Object::Axis_Y:
|
||||||
|
colorName = "green";
|
||||||
|
break;
|
||||||
|
case Object::Axis_Z:
|
||||||
|
colorName = "blue";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid axis for highlighting: " + std::to_string(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> targetMaterials = { colorName + "-material" };
|
||||||
|
|
||||||
|
if (mSubMode != Object::Mode_Rotate)
|
||||||
|
targetMaterials.emplace_back(colorName + "_alpha-material");
|
||||||
|
|
||||||
|
for (const auto& materialNodeName : targetMaterials)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Node> matNode = mMarkerNodes[materialNodeName];
|
||||||
|
osg::StateSet* state = matNode->getStateSet();
|
||||||
|
osg::StateAttribute* matAttr = state->getAttribute(osg::StateAttribute::MATERIAL);
|
||||||
|
|
||||||
|
osg::Material* mat = static_cast<osg::Material*>(matAttr);
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, mOriginalColors[materialNodeName]);
|
||||||
|
|
||||||
|
mLastHighlightedNodes.emplace(std::make_pair(matNode->getName(), mat));
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastHitNode = hitNode;
|
||||||
|
}
|
||||||
|
}
|
77
apps/opencs/view/render/objectmarker.hpp
Normal file
77
apps/opencs/view/render/objectmarker.hpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef OPENCS_VIEW_OBJECT_MARKER_H
|
||||||
|
#define OPENCS_VIEW_OBJECT_MARKER_H
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Camera;
|
||||||
|
class Material;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVRender
|
||||||
|
{
|
||||||
|
using NodeMap = std::unordered_map<std::string, osg::ref_ptr<osg::Node>>;
|
||||||
|
class WorldspaceWidget;
|
||||||
|
|
||||||
|
class ObjectMarkerTag : public ObjectTag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjectMarkerTag(Object* object, int axis);
|
||||||
|
|
||||||
|
int mAxis;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObjectMarker
|
||||||
|
{
|
||||||
|
friend class WorldspaceWidget;
|
||||||
|
|
||||||
|
WorldspaceWidget* mWorldspaceWidget;
|
||||||
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
|
NodeMap mMarkerNodes;
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mRootNode;
|
||||||
|
std::unordered_map<std::string, osg::Vec4f> mOriginalColors;
|
||||||
|
std::vector<std::string> mSelectionHistory;
|
||||||
|
std::string mLastHitNode;
|
||||||
|
std::unordered_map<std::string, osg::Material*> mLastHighlightedNodes;
|
||||||
|
float mMarkerScale;
|
||||||
|
int mSubMode;
|
||||||
|
|
||||||
|
ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem);
|
||||||
|
|
||||||
|
static std::unique_ptr<ObjectMarker> create(WorldspaceWidget* widget, Resource::ResourceSystem* resourceSystem)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<ObjectMarker>(new ObjectMarker(widget, resourceSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool attachMarker(const std::string& refId);
|
||||||
|
|
||||||
|
void removeFromSelectionHistory(const std::string& refId);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ObjectMarker(ObjectMarker&) = delete;
|
||||||
|
ObjectMarker(ObjectMarker&&) = delete;
|
||||||
|
ObjectMarker& operator=(const ObjectMarker&) = delete;
|
||||||
|
ObjectMarker& operator=(ObjectMarker&&) = delete;
|
||||||
|
|
||||||
|
void toggleVisibility();
|
||||||
|
|
||||||
|
bool hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera);
|
||||||
|
|
||||||
|
void detachMarker();
|
||||||
|
|
||||||
|
void addToSelectionHistory(const std::string& refId, bool update = true);
|
||||||
|
|
||||||
|
void updateSelectionMarker(const std::string& refId = std::string());
|
||||||
|
|
||||||
|
void resetMarkerHighlight();
|
||||||
|
|
||||||
|
void updateMarkerHighlight(const std::string_view hitNode, const int axis);
|
||||||
|
|
||||||
|
void setSubMode(const int subMode);
|
||||||
|
|
||||||
|
void updateScale(const float scale);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // OPENCS_VIEW_OBJECT_MARKER_H
|
|
@ -86,8 +86,8 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
|
||||||
{
|
{
|
||||||
modified = true;
|
modified = true;
|
||||||
|
|
||||||
auto cell
|
auto cell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode,
|
||||||
= std::make_unique<Cell>(mDocument, mRootNode, iter->first.getId(mWorldspace), deleted, true);
|
iter->first.getId(mWorldspace), deleted, true);
|
||||||
|
|
||||||
delete iter->second;
|
delete iter->second;
|
||||||
iter->second = cell.release();
|
iter->second = cell.release();
|
||||||
|
@ -465,7 +465,8 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene(const CSMWorld::CellCoordi
|
||||||
|
|
||||||
bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted;
|
bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted;
|
||||||
|
|
||||||
auto cell = std::make_unique<Cell>(mDocument, mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
auto cell = std::make_unique<Cell>(
|
||||||
|
getDocument(), mSelectionMarker.get(), mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
||||||
EditMode* editMode = getEditMode();
|
EditMode* editMode = getEditMode();
|
||||||
cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask());
|
cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask());
|
||||||
|
|
||||||
|
@ -750,6 +751,7 @@ void CSVRender::PagedWorldspaceWidget::clearSelection(int elementMask)
|
||||||
iter->second->setSelection(elementMask, Cell::Selection_Clear);
|
iter->second->setSelection(elementMask, Cell::Selection_Clear);
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mSelectionMarker->detachMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::PagedWorldspaceWidget::invertSelection(int elementMask)
|
void CSVRender::PagedWorldspaceWidget::invertSelection(int elementMask)
|
||||||
|
@ -907,6 +909,7 @@ void CSVRender::PagedWorldspaceWidget::setSubMode(int subMode, unsigned int elem
|
||||||
{
|
{
|
||||||
for (std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator iter = mCells.begin(); iter != mCells.end(); ++iter)
|
for (std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator iter = mCells.begin(); iter != mCells.end(); ++iter)
|
||||||
iter->second->setSubMode(subMode, elementMask);
|
iter->second->setSubMode(subMode, elementMask);
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::PagedWorldspaceWidget::reset(unsigned int elementMask)
|
void CSVRender::PagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||||
|
@ -986,3 +989,12 @@ void CSVRender::PagedWorldspaceWidget::loadSouthCell()
|
||||||
{
|
{
|
||||||
addCellToSceneFromCamera(0, -1);
|
addCellToSceneFromCamera(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::PagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
for (const auto& [_, cell] : mCells)
|
||||||
|
if (const auto& object = cell->getObjectByReferenceId(referenceId))
|
||||||
|
return object;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -174,6 +174,8 @@ namespace CSVRender
|
||||||
/// Erase all overrides and restore the visual representation to its true state.
|
/// Erase all overrides and restore the visual representation to its true state.
|
||||||
void reset(unsigned int elementMask) override;
|
void reset(unsigned int elementMask) override;
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool) override;
|
void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool) override;
|
||||||
|
|
||||||
|
|
|
@ -445,6 +445,32 @@ namespace CSVRender
|
||||||
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
|
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
|
||||||
mCamPositionSet = true;
|
mCamPositionSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSelectionMarkerNode)
|
||||||
|
{
|
||||||
|
osg::MatrixList worldMats = mSelectionMarkerNode->getWorldMatrices();
|
||||||
|
if (!worldMats.empty())
|
||||||
|
{
|
||||||
|
osg::Matrixd markerWorldMat = worldMats[0];
|
||||||
|
|
||||||
|
osg::Vec3f eye, _;
|
||||||
|
mView->getCamera()->getViewMatrix().getLookAt(eye, _, _);
|
||||||
|
osg::Vec3f cameraLocalPos = eye * osg::Matrixd::inverse(markerWorldMat);
|
||||||
|
|
||||||
|
bool isInFrontRightQuadrant = (cameraLocalPos.x() > 0.1f) && (cameraLocalPos.y() > 0.1f);
|
||||||
|
bool isSignificantlyBehind = (cameraLocalPos.x() < 1.f) && (cameraLocalPos.y() < 1.f);
|
||||||
|
|
||||||
|
if (!isInFrontRightQuadrant && isSignificantlyBehind)
|
||||||
|
{
|
||||||
|
osg::Quat current = mSelectionMarkerNode->getAttitude();
|
||||||
|
mSelectionMarkerNode->setAttitude(current * osg::Quat(osg::PI, osg::Vec3f(0, 0, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = (markerWorldMat.getTrans() - eye).length();
|
||||||
|
float scale = std::max(distance / 75.0f, 1.0f);
|
||||||
|
mSelectionMarkerNode->setScale(osg::Vec3(scale, scale, scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneWidget::settingChanged(const CSMPrefs::Setting* setting)
|
void SceneWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/Vec4f>
|
#include <osg/Vec4f>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
@ -105,6 +106,11 @@ namespace CSVRender
|
||||||
|
|
||||||
void setExterior(bool isExterior);
|
void setExterior(bool isExterior);
|
||||||
|
|
||||||
|
void setSelectionMarkerRoot(osg::ref_ptr<osg::PositionAttitudeTransform> selectionMarker)
|
||||||
|
{
|
||||||
|
mSelectionMarkerNode = selectionMarker;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setLighting(Lighting* lighting);
|
void setLighting(Lighting* lighting);
|
||||||
///< \attention The ownership of \a lighting is not transferred to *this.
|
///< \attention The ownership of \a lighting is not transferred to *this.
|
||||||
|
@ -122,6 +128,7 @@ namespace CSVRender
|
||||||
|
|
||||||
Lighting* mLighting;
|
Lighting* mLighting;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mSelectionMarkerNode;
|
||||||
osg::ref_ptr<osg::Camera> mGradientCamera;
|
osg::ref_ptr<osg::Camera> mGradientCamera;
|
||||||
osg::Vec4f mDefaultAmbient;
|
osg::Vec4f mDefaultAmbient;
|
||||||
bool mHasDefaultAmbient;
|
bool mHasDefaultAmbient;
|
||||||
|
|
|
@ -79,7 +79,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget(
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
mCell = std::make_unique<Cell>(document, mRootNode, mCellId);
|
mCell = std::make_unique<Cell>(document, mSelectionMarker.get(), mRootNode, mCellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
@ -127,7 +127,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop(
|
||||||
|
|
||||||
mCellId = universalIdData.begin()->getId();
|
mCellId = universalIdData.begin()->getId();
|
||||||
|
|
||||||
mCell = std::make_unique<Cell>(getDocument(), mRootNode, mCellId);
|
mCell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode, mCellId);
|
||||||
mCamPositionSet = false;
|
mCamPositionSet = false;
|
||||||
mOrbitCamControl->reset();
|
mOrbitCamControl->reset();
|
||||||
|
|
||||||
|
@ -141,6 +141,7 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection(int elementMask)
|
||||||
{
|
{
|
||||||
mCell->setSelection(elementMask, Cell::Selection_Clear);
|
mCell->setSelection(elementMask, Cell::Selection_Clear);
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mSelectionMarker->detachMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::invertSelection(int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::invertSelection(int elementMask)
|
||||||
|
@ -218,6 +219,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget
|
||||||
void CSVRender::UnpagedWorldspaceWidget::setSubMode(int subMode, unsigned int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::setSubMode(int subMode, unsigned int elementMask)
|
||||||
{
|
{
|
||||||
mCell->setSubMode(subMode, elementMask);
|
mCell->setSubMode(subMode, elementMask);
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::reset(unsigned int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||||
|
@ -383,3 +385,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget:
|
||||||
return ignored;
|
return ignored;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::UnpagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
return mCell->getObjectByReferenceId(referenceId);
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,8 @@ namespace CSVRender
|
||||||
/// Erase all overrides and restore the visual representation to its true state.
|
/// Erase all overrides and restore the visual representation to its true state.
|
||||||
void reset(unsigned int elementMask) override;
|
void reset(unsigned int elementMask) override;
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) override;
|
void referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) override;
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
#include "cameracontroller.hpp"
|
#include "cameracontroller.hpp"
|
||||||
#include "instancemode.hpp"
|
#include "instancemode.hpp"
|
||||||
#include "mask.hpp"
|
#include "mask.hpp"
|
||||||
#include "object.hpp"
|
|
||||||
#include "pathgridmode.hpp"
|
#include "pathgridmode.hpp"
|
||||||
|
|
||||||
CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidget* parent)
|
CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidget* parent)
|
||||||
|
@ -74,8 +73,8 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
||||||
, mToolTipPos(-1, -1)
|
, mToolTipPos(-1, -1)
|
||||||
, mShowToolTips(false)
|
, mShowToolTips(false)
|
||||||
, mToolTipDelay(0)
|
, mToolTipDelay(0)
|
||||||
, mInConstructor(true)
|
|
||||||
, mSelectedNavigationMode(0)
|
, mSelectedNavigationMode(0)
|
||||||
|
, mSelectionMarker(ObjectMarker::create(this, document.getData().getResourceSystem().get()))
|
||||||
{
|
{
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
@ -145,13 +144,14 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
||||||
&WorldspaceWidget::unhideAll);
|
&WorldspaceWidget::unhideAll);
|
||||||
|
|
||||||
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
[this] { this->clearSelection(Mask_Reference); });
|
[this] { clearSelection(Mask_Reference); });
|
||||||
|
|
||||||
CSMPrefs::Shortcut* switchPerspectiveShortcut = new CSMPrefs::Shortcut("scene-cam-cycle", this);
|
CSMPrefs::Shortcut* switchPerspectiveShortcut = new CSMPrefs::Shortcut("scene-cam-cycle", this);
|
||||||
connect(switchPerspectiveShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
connect(switchPerspectiveShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
&WorldspaceWidget::cycleNavigationMode);
|
&WorldspaceWidget::cycleNavigationMode);
|
||||||
|
|
||||||
mInConstructor = false;
|
connect(new CSMPrefs::Shortcut("scene-toggle-marker", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
|
[this]() { mSelectionMarker->toggleVisibility(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* setting)
|
void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||||
|
@ -162,17 +162,8 @@ void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* settin
|
||||||
mDragWheelFactor = setting->toDouble();
|
mDragWheelFactor = setting->toDouble();
|
||||||
else if (*setting == "3D Scene Input/drag-shift-factor")
|
else if (*setting == "3D Scene Input/drag-shift-factor")
|
||||||
mDragShiftFactor = setting->toDouble();
|
mDragShiftFactor = setting->toDouble();
|
||||||
else if (*setting == "Rendering/object-marker-alpha" && !mInConstructor)
|
else if (*setting == "Rendering/object-marker-scale")
|
||||||
{
|
mSelectionMarker->updateScale(setting->toDouble());
|
||||||
float alpha = setting->toDouble();
|
|
||||||
// getSelection is virtual, thus this can not be called from the constructor
|
|
||||||
auto selection = getSelection(Mask_Reference);
|
|
||||||
for (osg::ref_ptr<TagBase> tag : selection)
|
|
||||||
{
|
|
||||||
if (auto objTag = dynamic_cast<ObjectTag*>(tag.get()))
|
|
||||||
objTag->mObject->setMarkerTransparency(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*setting == "Tooltips/scene-delay")
|
else if (*setting == "Tooltips/scene-delay")
|
||||||
mToolTipDelay = setting->toInt();
|
mToolTipDelay = setting->toInt();
|
||||||
else if (*setting == "Tooltips/scene")
|
else if (*setting == "Tooltips/scene")
|
||||||
|
@ -396,8 +387,29 @@ CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument()
|
||||||
return mDocument;
|
return mDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
template <typename Tag>
|
||||||
const QPoint& localPos, unsigned int interactionMask) const
|
std::optional<CSVRender::WorldspaceHitResult> CSVRender::WorldspaceWidget::checkTag(
|
||||||
|
const osgUtil::LineSegmentIntersector::Intersection& intersection) const
|
||||||
|
{
|
||||||
|
for (auto* node : intersection.nodePath)
|
||||||
|
{
|
||||||
|
if (auto* tag = dynamic_cast<Tag*>(node->getUserData()))
|
||||||
|
{
|
||||||
|
WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||||
|
if (intersection.indexList.size() >= 3)
|
||||||
|
{
|
||||||
|
hit.index0 = intersection.indexList[0];
|
||||||
|
hit.index1 = intersection.indexList[1];
|
||||||
|
hit.index2 = intersection.indexList[2];
|
||||||
|
}
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> CSVRender::WorldspaceWidget::getStartEndDirection(
|
||||||
|
int pointX, int pointY) const
|
||||||
{
|
{
|
||||||
// may be okay to just use devicePixelRatio() directly
|
// may be okay to just use devicePixelRatio() directly
|
||||||
QScreen* screen = SceneWidget::windowHandle() && SceneWidget::windowHandle()->screen()
|
QScreen* screen = SceneWidget::windowHandle() && SceneWidget::windowHandle()->screen()
|
||||||
|
@ -405,8 +417,8 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
: QGuiApplication::primaryScreen();
|
: QGuiApplication::primaryScreen();
|
||||||
|
|
||||||
// (0,0) is considered the lower left corner of an OpenGL window
|
// (0,0) is considered the lower left corner of an OpenGL window
|
||||||
int x = localPos.x() * screen->devicePixelRatio();
|
int x = pointX * screen->devicePixelRatio();
|
||||||
int y = height() * screen->devicePixelRatio() - localPos.y() * screen->devicePixelRatio();
|
int y = height() * screen->devicePixelRatio() - pointY * screen->devicePixelRatio();
|
||||||
|
|
||||||
// Convert from screen space to world space
|
// Convert from screen space to world space
|
||||||
osg::Matrixd wpvMat;
|
osg::Matrixd wpvMat;
|
||||||
|
@ -418,6 +430,13 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
osg::Vec3d start = wpvMat.preMult(osg::Vec3d(x, y, 0));
|
osg::Vec3d start = wpvMat.preMult(osg::Vec3d(x, y, 0));
|
||||||
osg::Vec3d end = wpvMat.preMult(osg::Vec3d(x, y, 1));
|
osg::Vec3d end = wpvMat.preMult(osg::Vec3d(x, y, 1));
|
||||||
osg::Vec3d direction = end - start;
|
osg::Vec3d direction = end - start;
|
||||||
|
return { start, end, direction };
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
|
const QPoint& localPos, unsigned int interactionMask) const
|
||||||
|
{
|
||||||
|
auto [start, end, direction] = getStartEndDirection(localPos.x(), localPos.y());
|
||||||
|
|
||||||
// Get intersection
|
// Get intersection
|
||||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||||
|
@ -430,51 +449,46 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
|
|
||||||
mView->getCamera()->accept(visitor);
|
mView->getCamera()->accept(visitor);
|
||||||
|
|
||||||
// Get relevant data
|
auto intersections = intersector->getIntersections();
|
||||||
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
|
|
||||||
it != intersector->getIntersections().end(); ++it)
|
|
||||||
{
|
|
||||||
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
|
|
||||||
|
|
||||||
// reject back-facing polygons
|
std::vector<osgUtil::LineSegmentIntersector::Intersection> validIntersections
|
||||||
if (direction * intersection.getWorldIntersectNormal() > 0)
|
= { intersections.begin(), intersections.end() };
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<osg::Node*>::iterator nodeIter = intersection.nodePath.begin();
|
const auto& removeBackfaces = [direction = direction](const osgUtil::LineSegmentIntersector::Intersection& i) {
|
||||||
nodeIter != intersection.nodePath.end(); ++nodeIter)
|
return direction * i.getWorldIntersectNormal() > 0;
|
||||||
{
|
};
|
||||||
osg::Node* node = *nodeIter;
|
|
||||||
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
|
||||||
{
|
|
||||||
WorldspaceHitResult hit = { true, std::move(tag), 0, 0, 0, intersection.getWorldIntersectPoint() };
|
|
||||||
if (intersection.indexList.size() >= 3)
|
|
||||||
{
|
|
||||||
hit.index0 = intersection.indexList[0];
|
|
||||||
hit.index1 = intersection.indexList[1];
|
|
||||||
hit.index2 = intersection.indexList[2];
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something untagged, probably terrain
|
validIntersections.erase(std::remove_if(validIntersections.begin(), validIntersections.end(), removeBackfaces),
|
||||||
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
validIntersections.end());
|
||||||
if (intersection.indexList.size() >= 3)
|
|
||||||
{
|
|
||||||
hit.index0 = intersection.indexList[0];
|
|
||||||
hit.index1 = intersection.indexList[1];
|
|
||||||
hit.index2 = intersection.indexList[2];
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default placement
|
// Default placement
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
|
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
|
||||||
|
|
||||||
WorldspaceHitResult hit = { false, nullptr, 0, 0, 0, start + direction };
|
if (validIntersections.empty())
|
||||||
|
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||||
|
|
||||||
|
const auto& firstHit = validIntersections.front();
|
||||||
|
|
||||||
|
for (const auto& hit : validIntersections)
|
||||||
|
if (const auto& markerHit = checkTag<ObjectMarkerTag>(hit))
|
||||||
|
{
|
||||||
|
if (mSelectionMarker->hitBehindMarker(markerHit->worldPos, mView->getCamera()))
|
||||||
|
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||||
|
else
|
||||||
|
return *markerHit;
|
||||||
|
}
|
||||||
|
if (auto hit = checkTag<TagBase>(firstHit))
|
||||||
|
return *hit;
|
||||||
|
|
||||||
|
// Something untagged, probably terrain
|
||||||
|
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, firstHit.getWorldIntersectPoint() };
|
||||||
|
if (firstHit.indexList.size() >= 3)
|
||||||
|
{
|
||||||
|
hit.index0 = firstHit.indexList[0];
|
||||||
|
hit.index1 = firstHit.indexList[1];
|
||||||
|
hit.index2 = firstHit.indexList[2];
|
||||||
|
}
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,6 +646,41 @@ void CSVRender::WorldspaceWidget::elementSelectionChanged()
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::updateOverlay() {}
|
void CSVRender::WorldspaceWidget::updateOverlay() {}
|
||||||
|
|
||||||
|
void CSVRender::WorldspaceWidget::handleMarkerHighlight(const int x, const int y)
|
||||||
|
{
|
||||||
|
auto [start, end, _] = getStartEndDirection(x, y);
|
||||||
|
|
||||||
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||||
|
new osgUtil::LineSegmentIntersector(osgUtil::Intersector::MODEL, start, end));
|
||||||
|
|
||||||
|
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
|
||||||
|
osgUtil::IntersectionVisitor visitor(intersector);
|
||||||
|
|
||||||
|
visitor.setTraversalMask(Mask_Reference);
|
||||||
|
|
||||||
|
mView->getCamera()->accept(visitor);
|
||||||
|
|
||||||
|
bool hitMarker = false;
|
||||||
|
for (const auto& intersection : intersector->getIntersections())
|
||||||
|
{
|
||||||
|
if (mSelectionMarker->hitBehindMarker(intersection.getWorldIntersectPoint(), mView->getCamera()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const auto& node : intersection.nodePath)
|
||||||
|
{
|
||||||
|
if (const auto& marker = dynamic_cast<ObjectMarkerTag*>(node->getUserData()))
|
||||||
|
{
|
||||||
|
hitMarker = true;
|
||||||
|
mSelectionMarker->updateMarkerHighlight(node->getName(), marker->mAxis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitMarker)
|
||||||
|
mSelectionMarker->resetMarkerHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent()).mouseMoveEvent(event);
|
dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent()).mouseMoveEvent(event);
|
||||||
|
@ -685,6 +734,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QPointF& pos = event->localPos();
|
||||||
|
handleMarkerHighlight(pos.x(), pos.y());
|
||||||
SceneWidget::mouseMoveEvent(event);
|
SceneWidget::mouseMoveEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <apps/opencs/view/render/tagbase.hpp>
|
#include <apps/opencs/view/render/tagbase.hpp>
|
||||||
|
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
|
#include "objectmarker.hpp"
|
||||||
#include "scenewidget.hpp"
|
#include "scenewidget.hpp"
|
||||||
|
|
||||||
class QDragEnterEvent;
|
class QDragEnterEvent;
|
||||||
|
@ -89,7 +90,6 @@ namespace CSVRender
|
||||||
QPoint mToolTipPos;
|
QPoint mToolTipPos;
|
||||||
bool mShowToolTips;
|
bool mShowToolTips;
|
||||||
int mToolTipDelay;
|
int mToolTipDelay;
|
||||||
bool mInConstructor;
|
|
||||||
int mSelectedNavigationMode;
|
int mSelectedNavigationMode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -186,6 +186,12 @@ namespace CSVRender
|
||||||
|
|
||||||
virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;
|
virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
std::optional<WorldspaceHitResult> checkTag(
|
||||||
|
const osgUtil::LineSegmentIntersector::Intersection& intersection) const;
|
||||||
|
|
||||||
|
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> getStartEndDirection(int pointX, int pointY) const;
|
||||||
|
|
||||||
/// Return the next intersection with scene elements matched by
|
/// Return the next intersection with scene elements matched by
|
||||||
/// \a interactionMask based on \a localPos and the camera vector.
|
/// \a interactionMask based on \a localPos and the camera vector.
|
||||||
/// If there is no such intersection, instead a point "in front" of \a localPos will be
|
/// If there is no such intersection, instead a point "in front" of \a localPos will be
|
||||||
|
@ -216,7 +222,14 @@ namespace CSVRender
|
||||||
|
|
||||||
EditMode* getEditMode();
|
EditMode* getEditMode();
|
||||||
|
|
||||||
|
virtual CSVRender::Object* getObjectByReferenceId(const std::string& id) = 0;
|
||||||
|
|
||||||
|
ObjectMarker* getSelectionMarker() { return mSelectionMarker.get(); }
|
||||||
|
const ObjectMarker* getSelectionMarker() const { return mSelectionMarker.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
const std::unique_ptr<CSVRender::ObjectMarker> mSelectionMarker;
|
||||||
|
|
||||||
/// Visual elements in a scene
|
/// Visual elements in a scene
|
||||||
/// @note do not change the enumeration values, they are used in pre-existing button file names!
|
/// @note do not change the enumeration values, they are used in pre-existing button file names!
|
||||||
enum ButtonId
|
enum ButtonId
|
||||||
|
@ -252,6 +265,10 @@ namespace CSVRender
|
||||||
void cycleNavigationMode();
|
void cycleNavigationMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool hitBehindMarker(const osg::Vec3d& hitPos) const;
|
||||||
|
|
||||||
|
void handleMarkerHighlight(const int x, const int y);
|
||||||
|
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
|
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
|
|
|
@ -129,7 +129,7 @@ add_component_dir (vfs
|
||||||
|
|
||||||
add_component_dir (resource
|
add_component_dir (resource
|
||||||
scenemanager keyframemanager imagemanager animblendrulesmanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem
|
scenemanager keyframemanager imagemanager animblendrulesmanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem
|
||||||
resourcemanager stats animation foreachbulletobject errormarker cachestats bgsmfilemanager
|
resourcemanager stats animation foreachbulletobject errormarker selectionmarker cachestats bgsmfilemanager
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (shader
|
add_component_dir (shader
|
||||||
|
|
|
@ -975,6 +975,14 @@ namespace Resource
|
||||||
return loadNonNif(errorMarker, file, mImageManager);
|
return loadNonNif(errorMarker, file, mImageManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneManager::loadSelectionMarker(
|
||||||
|
osg::ref_ptr<osg::Group> parentNode, const char* markerData, long long markerSize) const
|
||||||
|
{
|
||||||
|
Files::IMemStream file(markerData, markerSize);
|
||||||
|
constexpr VFS::Path::NormalizedView selectionMarker("selectionmarker.osgt");
|
||||||
|
parentNode->addChild(loadNonNif(selectionMarker, file, mImageManager));
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> SceneManager::cloneErrorMarker()
|
osg::ref_ptr<osg::Node> SceneManager::cloneErrorMarker()
|
||||||
{
|
{
|
||||||
if (!mErrorMarker)
|
if (!mErrorMarker)
|
||||||
|
|
|
@ -136,6 +136,9 @@ namespace Resource
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture> getOpaqueDepthTex(size_t frame);
|
osg::ref_ptr<osg::Texture> getOpaqueDepthTex(size_t frame);
|
||||||
|
|
||||||
|
void loadSelectionMarker(
|
||||||
|
osg::ref_ptr<osg::Group> parentNode, const char* markerData, long long markerSize) const;
|
||||||
|
|
||||||
enum class UBOBinding
|
enum class UBOBinding
|
||||||
{
|
{
|
||||||
// If we add more UBO's, we should probably assign their bindings dynamically according to the current count
|
// If we add more UBO's, we should probably assign their bindings dynamically according to the current count
|
||||||
|
|
|
@ -160,4 +160,7 @@
|
||||||
<file alias="brush-circle">brush-circle.svg</file>
|
<file alias="brush-circle">brush-circle.svg</file>
|
||||||
<file alias="brush-custom">brush-custom.svg</file>
|
<file alias="brush-custom">brush-custom.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
<qresource prefix="/render">
|
||||||
|
<file alias="selection-marker">selectionmarker.osgt</file>
|
||||||
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
30805
files/opencs/selectionmarker.osgt
Normal file
30805
files/opencs/selectionmarker.osgt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue