Add snap to reference

7098-improve-post-process-behavior-with-transparent-objects
Max Henzerling 2 years ago
parent 6960fc9304
commit 897cdc62d8

@ -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));

@ -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;

@ -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;

@ -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;

@ -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?

@ -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);

@ -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;

@ -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;

@ -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();

@ -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
{

@ -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;

@ -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
{

@ -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;

@ -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;

@ -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:

@ -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.

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

Loading…
Cancel
Save