1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-30 07:45:39 +00:00

Merge branch 'sm-snap-to-reference' into 'master'

Open-CS: Snap to Reference

See merge request OpenMW/openmw!2484
This commit is contained in:
psi29a 2022-11-08 15:30:26 +00:00
commit fa50890633
17 changed files with 257 additions and 19 deletions

View file

@ -397,6 +397,8 @@ void CSMPrefs::State::declare()
declareShortcut("scene-select-primary", "Primary Select", QKeySequence(Qt::MiddleButton));
declareShortcut(
"scene-select-secondary", "Secondary Select", QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton));
declareShortcut(
"scene-select-tertiary", "Tertiary Select", QKeySequence(Qt::ShiftModifier | (int)Qt::MiddleButton));
declareModifier("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift);
declareShortcut("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete));
declareShortcut("scene-instance-drop-terrain", "Drop to terrain level", QKeySequence(Qt::Key_G));

View file

@ -599,6 +599,18 @@ bool CSVRender::Cell::isDeleted() const
return mDeleted;
}
osg::ref_ptr<CSVRender::TagBase> CSVRender::Cell::getSnapTarget(unsigned int elementMask) const
{
osg::ref_ptr<TagBase> result;
if (elementMask & Mask_Reference)
for (auto& obj : mObjects)
if (obj.second->getSnapTarget())
return obj.second->getTag();
return result;
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::Cell::getSelection(unsigned int elementMask) const
{
std::vector<osg::ref_ptr<TagBase>> result;

View file

@ -162,6 +162,8 @@ namespace CSVRender
bool isDeleted() const;
osg::ref_ptr<TagBase> getSnapTarget(unsigned int elementMask) const;
std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const;
std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const;

View file

@ -48,6 +48,8 @@ void CSVRender::EditMode::primarySelectPressed(const WorldspaceHitResult& hit) {
void CSVRender::EditMode::secondarySelectPressed(const WorldspaceHitResult& hit) {}
void CSVRender::EditMode::tertiarySelectPressed(const WorldspaceHitResult& hit) {}
bool CSVRender::EditMode::primaryEditStartDrag(const QPoint& pos)
{
return false;

View file

@ -57,6 +57,9 @@ namespace CSVRender
/// Default-implementation: Ignored.
virtual void secondarySelectPressed(const WorldspaceHitResult& hit);
/// Default-implementation: Ignored.
virtual void tertiarySelectPressed(const WorldspaceHitResult& hit);
/// Default-implementation: ignore and return false
///
/// \return Drag accepted?

View file

@ -96,9 +96,37 @@ osg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const
float CSVRender::InstanceMode::roundFloatToMult(const float val, const double mult) const
{
if (mult == 0)
return val;
return round(val / mult) * mult;
}
osg::Vec3 CSVRender::InstanceMode::calculateSnapPositionRelativeToTarget(osg::Vec3 initalPosition,
osg::Vec3 targetPosition, osg::Vec3 targetRotation, osg::Vec3 translation, double snap) const
{
auto quatTargetRotation
= osg::Quat(targetRotation[0], osg::X_AXIS, targetRotation[1], osg::Y_AXIS, targetRotation[2], osg::Z_AXIS);
// Break object world coords into snap target space
auto localWorld = osg::Matrix::translate(initalPosition)
* osg::Matrix::inverse(osg::Matrix::translate(targetPosition)) * osg::Matrix::rotate(quatTargetRotation);
osg::Vec3 localPosition = localWorld.getTrans();
osg::Vec3 newTranslation;
newTranslation[0] = CSVRender::InstanceMode::roundFloatToMult(localPosition[0] + translation[0], snap);
newTranslation[1] = CSVRender::InstanceMode::roundFloatToMult(localPosition[1] + translation[1], snap);
newTranslation[2] = CSVRender::InstanceMode::roundFloatToMult(localPosition[2] + translation[2], snap);
// rebuild object's world coordinates (note: inverse operations from local construction)
auto newObjectWorld = osg::Matrix::translate(newTranslation)
* osg::Matrix::inverse(osg::Matrix::rotate(quatTargetRotation)) * osg::Matrix::translate(targetPosition);
osg::Vec3 newObjectPosition = newObjectWorld.getTrans();
return newObjectPosition;
}
osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector<osg::ref_ptr<TagBase>>& selection) const
{
osg::Vec3f center = osg::Vec3f(0, 0, 0);
@ -320,6 +348,27 @@ void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult&
}
}
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
{
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
if (snapTarget)
{
snapTarget->mObject->setSnapTarget(false);
}
if (hit.tag)
{
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
{
// hit an Object, toggle its selection state
CSVRender::Object* object = objectTag->mObject;
object->setSnapTarget(!object->getSnapTarget());
return;
}
}
}
bool CSVRender::InstanceMode::primaryEditStartDrag(const QPoint& pos)
{
if (mDragMode != DragMode_None || mLocked)
@ -525,6 +574,7 @@ void CSVRender::InstanceMode::drag(const QPoint& pos, int diffX, int diffY, doub
osg::Quat rotation;
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getEdited(Mask_Reference);
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
if (mDragMode == DragMode_Move || mDragMode == DragMode_Move_Snap)
{
@ -649,9 +699,25 @@ void CSVRender::InstanceMode::drag(const QPoint& pos, int diffX, int diffY, doub
if (mDragMode == DragMode_Move_Snap)
{
double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble();
position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap);
position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap);
position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap);
if (snapTarget)
{
osg::Vec3 translation(addToX, addToY, addToZ);
auto snapTargetPosition = snapTarget->mObject->getPosition();
auto newPosition = calculateSnapPositionRelativeToTarget(mObjectsAtDragStart[i],
snapTargetPosition.asVec3(), snapTargetPosition.asRotationVec3(), translation, snap);
position.pos[0] = newPosition[0];
position.pos[1] = newPosition[1];
position.pos[2] = newPosition[2];
}
else
{
position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap);
position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap);
position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap);
}
}
// XYZ-locking
@ -709,6 +775,8 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
{
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getEdited(Mask_Reference);
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description;
@ -755,6 +823,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
CSMWorld::CommandMacro macro(undoStack, description);
// Is this even supposed to be here?
for (std::vector<osg::ref_ptr<TagBase>>::iterator iter(selection.begin()); iter != selection.end(); ++iter)
{
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(iter->get()))
@ -763,12 +832,29 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
{
ESM::Position position = objectTag->mObject->getPosition();
double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble();
float xOffset = 0;
float yOffset = 0;
float zOffset = 0;
if (snapTarget)
{
auto snapTargetPosition = snapTarget->mObject->getPosition();
auto rotation = snapTargetPosition.rot;
if (rotation)
{
xOffset = remainder(rotation[0], osg::DegreesToRadians(snap));
yOffset = remainder(rotation[1], osg::DegreesToRadians(snap));
zOffset = remainder(rotation[2], osg::DegreesToRadians(snap));
}
}
position.rot[0]
= CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(snap));
= CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(snap)) + xOffset;
position.rot[1]
= CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(snap));
= CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(snap)) + yOffset;
position.rot[2]
= CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(snap));
= CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(snap)) + zOffset;
objectTag->mObject->setRotation(position.rot);
}
@ -802,6 +888,8 @@ void CSVRender::InstanceMode::dragWheel(int diff, double speedFactor)
offset *= diff * speedFactor;
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getEdited(Mask_Reference);
auto snapTarget
= dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
int j = 0;
@ -810,15 +898,32 @@ void CSVRender::InstanceMode::dragWheel(int diff, double speedFactor)
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(iter->get()))
{
ESM::Position position = objectTag->mObject->getPosition();
auto preMovedObjectPosition = position.asVec3();
for (int i = 0; i < 3; ++i)
position.pos[i] += offset[i];
if (mDragMode == DragMode_Move_Snap)
{
double snap = CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble();
position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap);
position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap);
position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap);
if (snapTarget)
{
osg::Vec3 translation(snap, snap, snap);
auto snapTargetPosition = snapTarget->mObject->getPosition();
auto newPosition = calculateSnapPositionRelativeToTarget(preMovedObjectPosition,
snapTargetPosition.asVec3(), snapTargetPosition.asRotationVec3(), translation, snap);
position.pos[0] = newPosition[0];
position.pos[1] = newPosition[1];
position.pos[2] = newPosition[2];
}
else
{
position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], snap);
position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], snap);
position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], snap);
}
}
objectTag->mObject->setPosition(position.pos);

View file

@ -9,8 +9,7 @@
#include <osg/Group>
#include <osg/Node>
#include <osg/Quat>
#include <osg/Vec3d>
#include <osg/Vec3f>
#include <osg/Vec3>
#include <osg/ref_ptr>
#include "editmode.hpp"
@ -59,23 +58,25 @@ namespace CSVRender
bool mLocked;
float mUnitScaleDist;
osg::ref_ptr<osg::Group> mParentNode;
osg::Vec3f mDragStart;
std::vector<osg::Vec3f> mObjectsAtDragStart;
osg::Vec3 mDragStart;
std::vector<osg::Vec3> mObjectsAtDragStart;
int getSubModeFromId(const std::string& id) const;
osg::Vec3f quatToEuler(const osg::Quat& quat) const;
osg::Quat eulerToQuat(const osg::Vec3f& euler) const;
osg::Vec3 quatToEuler(const osg::Quat& quat) const;
osg::Quat eulerToQuat(const osg::Vec3& euler) const;
float roundFloatToMult(const float val, const double mult) const;
osg::Vec3f getSelectionCenter(const std::vector<osg::ref_ptr<TagBase>>& selection) const;
osg::Vec3f getScreenCoords(const osg::Vec3f& pos);
osg::Vec3f getProjectionSpaceCoords(const osg::Vec3f& pos);
osg::Vec3f getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart);
osg::Vec3 getSelectionCenter(const std::vector<osg::ref_ptr<TagBase>>& selection) const;
osg::Vec3 getScreenCoords(const osg::Vec3& pos);
osg::Vec3 getProjectionSpaceCoords(const osg::Vec3& pos);
osg::Vec3 getMousePlaneCoords(const QPoint& point, const osg::Vec3d& dragStart);
void handleSelectDrag(const QPoint& pos);
void dropInstance(CSVRender::Object* object, float dropHeight);
float calculateDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight);
osg::Vec3 calculateSnapPositionRelativeToTarget(osg::Vec3 initalPosition, osg::Vec3 targetPosition,
osg::Vec3 targetRotation, osg::Vec3 translation, double snap) const;
public:
InstanceMode(
@ -97,6 +98,8 @@ namespace CSVRender
void secondarySelectPressed(const WorldspaceHitResult& hit) override;
void tertiarySelectPressed(const WorldspaceHitResult& hit) override;
bool primaryEditStartDrag(const QPoint& pos) override;
bool secondaryEditStartDrag(const QPoint& pos) override;

View file

@ -487,11 +487,17 @@ void CSVRender::Object::setSelected(bool selected)
{
mSelected = selected;
if (mSnapTarget)
{
setSnapTarget(false);
}
mOutline->removeChild(mBaseNode);
mRootNode->removeChild(mOutline);
mRootNode->removeChild(mBaseNode);
if (selected)
{
mOutline->setWireframeColor(osg::Vec4f(1, 1, 1, 1));
mOutline->addChild(mBaseNode);
mRootNode->addChild(mOutline);
}
@ -507,6 +513,36 @@ bool CSVRender::Object::getSelected() const
return mSelected;
}
void CSVRender::Object::setSnapTarget(bool isSnapTarget)
{
mSnapTarget = isSnapTarget;
if (mSelected)
{
setSelected(false);
}
mOutline->removeChild(mBaseNode);
mRootNode->removeChild(mOutline);
mRootNode->removeChild(mBaseNode);
if (isSnapTarget)
{
mOutline->setWireframeColor(osg::Vec4f(1, 1, 0, 1));
mOutline->addChild(mBaseNode);
mRootNode->addChild(mOutline);
}
else
mRootNode->addChild(mBaseNode);
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
updateMarker();
}
bool CSVRender::Object::getSnapTarget() const
{
return mSnapTarget;
}
osg::ref_ptr<osg::Group> CSVRender::Object::getRootNode()
{
return mRootNode;

View file

@ -87,6 +87,7 @@ namespace CSVRender
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
osg::ref_ptr<osgFX::Scribe> mOutline;
bool mSelected;
bool mSnapTarget;
osg::Group* mParentNode;
Resource::ResourceSystem* mResourceSystem;
bool mForceBaseToZero;
@ -140,6 +141,11 @@ namespace CSVRender
bool getSelected() const;
/// Mark Object as "snap target"
void setSnapTarget(bool isSnapTarget);
bool getSnapTarget() const;
/// Get object node with GUI graphics
osg::ref_ptr<osg::Group> getRootNode();

View file

@ -844,6 +844,22 @@ void CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights()
cell.second->resetAlteredHeights();
}
osg::ref_ptr<CSVRender::TagBase> CSVRender::PagedWorldspaceWidget::getSnapTarget(unsigned int elementMask) const
{
osg::ref_ptr<CSVRender::TagBase> result;
for (auto& [coords, cell] : mCells)
{
auto snapTarget = cell->getSnapTarget(elementMask);
if (snapTarget)
{
return snapTarget;
}
}
return result;
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::PagedWorldspaceWidget::getSelection(
unsigned int elementMask) const
{

View file

@ -159,6 +159,8 @@ namespace CSVRender
void resetAllAlteredHeights();
osg::ref_ptr<TagBase> getSnapTarget(unsigned int elementMask) const override;
std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const override;
std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const override;

View file

@ -189,6 +189,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const CSMWorld::Cel
return mCell.get();
}
osg::ref_ptr<CSVRender::TagBase> CSVRender::UnpagedWorldspaceWidget::getSnapTarget(unsigned int elementMask) const
{
return mCell->getSnapTarget(elementMask);
}
std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget::getSelection(
unsigned int elementMask) const
{

View file

@ -89,6 +89,8 @@ namespace CSVRender
Cell* getCell(const CSMWorld::CellCoordinates& coords) const override;
osg::ref_ptr<TagBase> getSnapTarget(unsigned int elementMask) const override;
std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const override;
std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const override;

View file

@ -126,6 +126,10 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
connect(secondarySelectShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this,
&WorldspaceWidget::secondarySelect);
CSMPrefs::Shortcut* tertiarySelectShortcut = new CSMPrefs::Shortcut("scene-select-tertiary", this);
connect(tertiarySelectShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this,
&WorldspaceWidget::tertiarySelect);
CSMPrefs::Shortcut* abortShortcut = new CSMPrefs::Shortcut("scene-edit-abort", this);
connect(abortShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &WorldspaceWidget::abortDrag);
@ -690,6 +694,8 @@ void CSVRender::WorldspaceWidget::handleInteractionPress(const WorldspaceHitResu
editMode.primarySelectPressed(hit);
else if (type == InteractionType_SecondarySelect)
editMode.secondarySelectPressed(hit);
else if (type == InteractionType_TertiarySelect)
editMode.tertiarySelectPressed(hit);
else if (type == InteractionType_PrimaryOpen)
editMode.primaryOpenPressed(hit);
}
@ -719,6 +725,11 @@ void CSVRender::WorldspaceWidget::secondarySelect(bool activate)
handleInteraction(InteractionType_SecondarySelect, activate);
}
void CSVRender::WorldspaceWidget::tertiarySelect(bool activate)
{
handleInteraction(InteractionType_TertiarySelect, activate);
}
void CSVRender::WorldspaceWidget::speedMode(bool activate)
{
mSpeedMode = activate;

View file

@ -113,6 +113,7 @@ namespace CSVRender
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_TertiarySelect,
InteractionType_PrimaryOpen,
InteractionType_None
};
@ -196,6 +197,8 @@ namespace CSVRender
virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const = 0;
virtual osg::ref_ptr<TagBase> getSnapTarget(unsigned int elementMask) const = 0;
virtual std::vector<osg::ref_ptr<TagBase>> getSelection(unsigned int elementMask) const = 0;
virtual std::vector<osg::ref_ptr<TagBase>> getEdited(unsigned int elementMask) const = 0;
@ -293,6 +296,8 @@ namespace CSVRender
void secondarySelect(bool activate);
void tertiarySelect(bool activate);
void speedMode(bool activate);
protected slots:

View file

@ -0,0 +1,25 @@
Cell View
########
This window deals with the manipulation of instances within one or more cells.
Grid Snapping
**********************
When manipulating one or more instances within the cell view, whether it's position,
rotation, or scale, the instances can be snapped to specific values configured within
the Edit->Preferences->3D Scene editing menu.
To begin snapping an instance, hold down CTRL when transforming the instance, whether
it's with the gizmos or by dragging the mouse, and the instance will snap to the closest
value as you manipulate the instance.
Snap to reference
======================
If you want to snap instances relative to another instance, you can select a snap target
with SHIFT + Middle Mouse Button to select a snap target. This will highlight the
instance in a yellow wireframe. Then, just with regular snapping, you hold down CTRL
when manipulating the instance(s) and the transformed values will be snapped to
the snap values relative to the snap target's world space.

View file

@ -29,3 +29,4 @@ few chapters to familiarise yourself with the new interface.
tables-assets
record-types
record-filters
cell-view