Implement grid snapping and angle snapping in OpenMW-CS

Adds configurable snap settings for instance movement, rotation and scaling, used with the secondary edit button
pull/3226/head
Vidi_Aquam 3 years ago
parent 964f288c13
commit 6d7c8f25fc

@ -243,6 +243,9 @@ void CSMPrefs::State::declare()
secondarySelectAction.add (SelectOnly).add (SelectAdd).add (SelectRemove).add (selectInvert);
declareCategory ("3D Scene Editing");
declareDouble("gridsnap-movement", "Grid snap size", 16);
declareDouble("gridsnap-rotation", "Angle snap size", 15);
declareDouble("gridsnap-scale", "Scale snap size", 0.25);
declareInt ("distance", "Drop Distance", 50).
setTooltip ("If an instance drop can not be placed against another object at the "
"insert point, it will be placed by this distance from the insert point instead");

@ -12,7 +12,10 @@ namespace CSVRender
DragMode_Select_Only,
DragMode_Select_Add,
DragMode_Select_Remove,
DragMode_Select_Invert
DragMode_Select_Invert,
DragMode_Move_Snap,
DragMode_Rotate_Snap,
DragMode_Scale_Snap
};
}
#endif

@ -64,6 +64,11 @@ osg::Quat CSVRender::InstanceMode::eulerToQuat(const osg::Vec3f& euler) const
return zr * yr * xr;
}
float CSVRender::InstanceMode::roundFloatToMult(const float val, const double mult) const
{
return round(val / mult) * mult;
}
osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector<osg::ref_ptr<TagBase> >& selection) const
{
osg::Vec3f center = osg::Vec3f(0, 0, 0);
@ -156,15 +161,13 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
"<ul><li>Use {scene-edit-primary} to rotate instances freely</li>"
"<li>Use {scene-edit-secondary} to rotate instances within the grid</li>"
"<li>The center of the view acts as the axis of rotation</li>"
"</ul>"
"<font color=Red>Grid rotate not implemented yet</font color>");
"</ul>");
mSubMode->addButton (":scenetoolbar/transform-scale", "scale",
"Scale selected instances"
"<ul><li>Use {scene-edit-primary} to scale instances freely</li>"
"<li>Use {scene-edit-secondary} to scale instances along the grid</li>"
"<li>The scaling rate is based on how close the start of a drag is to the center of the screen</li>"
"</ul>"
"<font color=Red>Grid scale not implemented yet</font color>");
"</ul>");
mSubMode->setButton (mSubModeId);
@ -351,10 +354,81 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
bool CSVRender::InstanceMode::secondaryEditStartDrag (const QPoint& pos)
{
if (mLocked)
if (mDragMode != DragMode_None || mLocked)
return false;
return false;
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getSelection(Mask_Reference);
if (selection.empty())
{
// 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())
{
getWorldspaceWidget().clearSelection(Mask_Reference);
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*> (hit.tag.get()))
{
CSVRender::Object* object = objectTag->mObject;
object->setSelected(true);
}
}
selection = getWorldspaceWidget().getSelection(Mask_Reference);
if (selection.empty())
return false;
}
mObjectsAtDragStart.clear();
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()))
{
if (mSubModeId == "move")
{
objectTag->mObject->setEdited(Object::Override_Position);
float x = objectTag->mObject->getPosition().pos[0];
float y = objectTag->mObject->getPosition().pos[1];
float z = objectTag->mObject->getPosition().pos[2];
osg::Vec3f thisPoint(x, y, z);
mDragStart = getMousePlaneCoords(pos, getProjectionSpaceCoords(thisPoint));
mObjectsAtDragStart.emplace_back(thisPoint);
mDragMode = DragMode_Move_Snap;
}
else if (mSubModeId == "rotate")
{
objectTag->mObject->setEdited(Object::Override_Rotation);
mDragMode = DragMode_Rotate_Snap;
}
else if (mSubModeId == "scale")
{
objectTag->mObject->setEdited(Object::Override_Scale);
mDragMode = DragMode_Scale_Snap;
// Calculate scale factor
std::vector<osg::ref_ptr<TagBase> > editedSelection = getWorldspaceWidget().getEdited(Mask_Reference);
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
int widgetHeight = getWorldspaceWidget().height();
float dx = pos.x() - center.x();
float dy = (widgetHeight - pos.y()) - center.y();
mUnitScaleDist = std::sqrt(dx * dx + dy * dy);
}
}
}
if (CSVRender::ObjectMarkerTag* objectTag = dynamic_cast<CSVRender::ObjectMarkerTag*> (hit.tag.get()))
{
mDragAxis = objectTag->mAxis;
}
else
mDragAxis = -1;
return true;
}
bool CSVRender::InstanceMode::primarySelectStartDrag (const QPoint& pos)
@ -400,8 +474,8 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (Mask_Reference);
if (mDragMode == DragMode_Move) {}
else if (mDragMode == DragMode_Rotate)
if (mDragMode == DragMode_Move || mDragMode == DragMode_Move_Snap) {}
else if (mDragMode == DragMode_Rotate || mDragMode == DragMode_Rotate_Snap)
{
osg::Vec3f eye, centre, up;
getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);
@ -465,7 +539,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
rotation = osg::Quat(angle, axis);
}
else if (mDragMode == DragMode_Scale)
else if (mDragMode == DragMode_Scale || mDragMode == DragMode_Scale_Snap)
{
osg::Vec3f center = getScreenCoords(getSelectionCenter(selection));
@ -507,7 +581,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))
{
if (mDragMode == DragMode_Move)
if (mDragMode == DragMode_Move || mDragMode == DragMode_Move_Snap)
{
ESM::Position position = objectTag->mObject->getPosition();
osg::Vec3f mousePos = getMousePlaneCoords(pos, getProjectionSpaceCoords(mDragStart));
@ -518,6 +592,13 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
position.pos[1] = mObjectsAtDragStart[i].y() + addToY;
position.pos[2] = mObjectsAtDragStart[i].z() + addToZ;
if (mDragMode == DragMode_Move_Snap)
{
position.pos[0] = CSVRender::InstanceMode::roundFloatToMult(position.pos[0], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble());
position.pos[1] = CSVRender::InstanceMode::roundFloatToMult(position.pos[1], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble());
position.pos[2] = CSVRender::InstanceMode::roundFloatToMult(position.pos[2], CSMPrefs::get()["3D Scene Editing"]["gridsnap-movement"].toDouble());
}
// XYZ-locking
if (mDragAxis != -1)
{
@ -530,7 +611,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
objectTag->mObject->setPosition(position.pos);
}
else if (mDragMode == DragMode_Rotate)
else if (mDragMode == DragMode_Rotate || mDragMode == DragMode_Rotate_Snap)
{
ESM::Position position = objectTag->mObject->getPosition();
@ -546,9 +627,16 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
position.rot[2] = euler.z();
}
if (mDragMode == DragMode_Rotate_Snap)
{
position.rot[0] = CSVRender::InstanceMode::roundFloatToMult(position.rot[0], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble()));
position.rot[1] = CSVRender::InstanceMode::roundFloatToMult(position.rot[1], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble()));
position.rot[2] = CSVRender::InstanceMode::roundFloatToMult(position.rot[2], osg::DegreesToRadians(CSMPrefs::get()["3D Scene Editing"]["gridsnap-rotation"].toDouble()));
}
objectTag->mObject->setRotation(position.rot);
}
else if (mDragMode == DragMode_Scale)
else if (mDragMode == DragMode_Scale || mDragMode == DragMode_Scale_Snap)
{
// Reset scale
objectTag->mObject->setEdited(0);
@ -557,6 +645,11 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
float scale = objectTag->mObject->getScale();
scale *= offset.x();
if (mDragMode == DragMode_Scale_Snap)
{
scale = CSVRender::InstanceMode::roundFloatToMult(scale, CSMPrefs::get()["3D Scene Editing"]["gridsnap-scale"].toDouble());
}
objectTag->mObject->setScale (scale);
}
}
@ -593,7 +686,9 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
handleSelectDrag(pos);
return;
break;
case DragMode_Move_Snap: description = "Move Instances"; break;
case DragMode_Rotate_Snap: description = "Rotate Instances"; break;
case DragMode_Scale_Snap: description = "Scale Instances"; break;
case DragMode_None: break;
}
@ -621,7 +716,7 @@ void CSVRender::InstanceMode::dragAborted()
void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
{
if (mDragMode==DragMode_Move)
if (mDragMode==DragMode_Move || mDragMode==DragMode_Move_Snap)
{
osg::Vec3f eye;
osg::Vec3f centre;

@ -53,6 +53,8 @@ namespace CSVRender
osg::Vec3f quatToEuler(const osg::Quat& quat) const;
osg::Quat eulerToQuat(const osg::Vec3f& 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);

@ -6,7 +6,6 @@ CSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent)
"Move selected instances"
"<ul><li>Use {scene-edit-primary} to move instances around freely</li>"
"<li>Use {scene-edit-secondary} to move instances around within the grid</li>"
"</ul>"
"<font color=Red>Grid move not implemented yet</font color>",
"</ul>",
parent)
{}

Loading…
Cancel
Save