Skip to content

Commit

Permalink
#5746: Split off the scene selection-related functionality from Manip…
Browse files Browse the repository at this point in the history
…ulationPivot to SceneManipulationPivot
  • Loading branch information
codereader committed Sep 17, 2021
1 parent a9d347b commit 401f30a
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 125 deletions.
1 change: 1 addition & 0 deletions radiantcore/CMakeLists.txt
Expand Up @@ -241,6 +241,7 @@ add_library(radiantcore MODULE
selection/group/SelectionGroupManager.cpp
selection/group/SelectionGroupModule.cpp
selection/ManipulationPivot.cpp
selection/SceneManipulationPivot.cpp
selection/manipulators/DragManipulator.cpp
selection/manipulators/ManipulatorBase.cpp
selection/manipulators/ManipulatorComponents.cpp
Expand Down
100 changes: 0 additions & 100 deletions radiantcore/selection/ManipulationPivot.cpp
@@ -1,44 +1,14 @@
#include "ManipulationPivot.h"

#include "igrid.h"
#include "ilightnode.h"
#include "registry/registry.h"
#include "algorithm/General.h"
#include "selectionlib.h"

namespace selection
{

const std::string ManipulationPivot::RKEY_ENTITY_PIVOT_IS_ORIGIN = "user/ui/rotationPivotIsOrigin";
const std::string ManipulationPivot::RKEY_SNAP_ROTATION_PIVOT_TO_GRID = "user/ui/snapRotationPivotToGrid";
const std::string ManipulationPivot::RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES = "user/ui/defaultPivotLocationIgnoresLightVolumes";

ManipulationPivot::ManipulationPivot() :
_entityPivotIsOrigin(false),
_snapPivotToGrid(false),
_needsRecalculation(true),
_defaultPivotLocationIgnoresLightVolumes(false),
_operationActive(false),
_userLocked(false)
{}

void ManipulationPivot::initialise()
{
_entityPivotIsOrigin = registry::getValue<bool>(RKEY_ENTITY_PIVOT_IS_ORIGIN);
_snapPivotToGrid = registry::getValue<bool>(RKEY_SNAP_ROTATION_PIVOT_TO_GRID);
_defaultPivotLocationIgnoresLightVolumes = registry::getValue<bool>(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);

GlobalRegistry().signalForKey(RKEY_ENTITY_PIVOT_IS_ORIGIN).connect(
sigc::mem_fun(this, &ManipulationPivot::onRegistryKeyChanged)
);
GlobalRegistry().signalForKey(RKEY_SNAP_ROTATION_PIVOT_TO_GRID).connect(
sigc::mem_fun(this, &ManipulationPivot::onRegistryKeyChanged)
);
GlobalRegistry().signalForKey(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES).connect(
sigc::mem_fun(this, &ManipulationPivot::onRegistryKeyChanged)
);
}

// Returns the pivot-to-world transform
const Matrix4& ManipulationPivot::getMatrix4()
{
Expand Down Expand Up @@ -108,76 +78,6 @@ void ManipulationPivot::applyTranslation(const Vector3& translation)
revertToStart();

_pivot2World.translateBy(translation);

if (_snapPivotToGrid)
{
// The resulting pivot should be grid-snapped
_pivot2World.tCol().getVector3().snap(GlobalGrid().getGridSize());
}
}

void ManipulationPivot::updateFromSelection()
{
_needsRecalculation = false;
_userLocked = false;

Vector3 objectPivot;

const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo();

if (info.entityCount == 1 && info.totalCount == 1 &&
Node_getLightNode(GlobalSelectionSystem().ultimateSelected()))
{
// When a single light is selected, use the origin for rotation
objectPivot = Node_getLightNode(GlobalSelectionSystem().ultimateSelected())->getSelectAABB().origin;
}
else if (info.entityCount == 1 && info.totalCount == 1 && _entityPivotIsOrigin)
{
// Test if a single entity is selected
scene::INodePtr node = GlobalSelectionSystem().ultimateSelected();
Entity* entity = Node_getEntity(node);

if (entity != nullptr)
{
objectPivot = string::convert<Vector3>(entity->getKeyValue("origin"));
}
}
else
{
// Create a local variable where the aabb information is stored
AABB bounds;

// Traverse through the selection and update the <bounds> variable
if (GlobalSelectionSystem().Mode() == SelectionSystem::eComponent)
{
bounds = algorithm::getCurrentComponentSelectionBounds();
}
else
{
// Ignore light volumes for the pivot calculation
bounds = algorithm::getCurrentSelectionBounds(!_defaultPivotLocationIgnoresLightVolumes);
}

// the <bounds> variable now contains the AABB of the selection, retrieve the origin
objectPivot = bounds.origin;
}

if (_snapPivotToGrid)
{
objectPivot.snap(GlobalGrid().getGridSize());
}

// The pivot2world matrix is just a translation from the world origin (0,0,0) to the object pivot
setFromMatrix(Matrix4::getTranslation(objectPivot));
}

void ManipulationPivot::onRegistryKeyChanged()
{
_entityPivotIsOrigin = registry::getValue<bool>(RKEY_ENTITY_PIVOT_IS_ORIGIN);
_snapPivotToGrid = registry::getValue<bool>(RKEY_SNAP_ROTATION_PIVOT_TO_GRID);
_defaultPivotLocationIgnoresLightVolumes = registry::getValue<bool>(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);

GlobalSelectionSystem().pivotChanged();
}

}
26 changes: 4 additions & 22 deletions radiantcore/selection/ManipulationPivot.h
Expand Up @@ -9,13 +9,13 @@ namespace selection
/**
* Represents the anchor point for manipulation operations
* in the scene. Usually this is defined by the origin of the
* current selection AABB.
* current selection AABB, but it can be modified by the user.
*
* Use the getMatrix4() method to acquire a pivot-to-world matrix.
*/
class ManipulationPivot
{
private:
protected:
Matrix4 _pivot2World;

// The untransformed pivot2world matrix
Expand All @@ -24,33 +24,18 @@ class ManipulationPivot
// operation, they are applied on top of the pivot2WorldStart.
Matrix4 _pivot2WorldStart;

// Use a single Entity's "origin" keyvalue as pivot
bool _entityPivotIsOrigin;

// Whether to snap the pivot to grid after movement
bool _snapPivotToGrid;

// "dirty" flag
bool _needsRecalculation;

// Whether to consider light volumes when calculating the selection bounds
bool _defaultPivotLocationIgnoresLightVolumes;

// During operations, we want to block pivot recalculations
bool _operationActive;

// "User has modified pivot"-flag, used to block pivot recalculations
bool _userLocked;

public:
static const std::string RKEY_ENTITY_PIVOT_IS_ORIGIN;
static const std::string RKEY_SNAP_ROTATION_PIVOT_TO_GRID;
static const std::string RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES;

ManipulationPivot();

void initialise();

// Returns the pivot-to-world transform
const Matrix4& getMatrix4();

Expand Down Expand Up @@ -79,14 +64,11 @@ class ManipulationPivot
// Operation cancelled, this reverts the pivot to where we started
void cancelOperation();

void applyTranslation(const Vector3& translation);
virtual void applyTranslation(const Vector3& translation);

// Rescans the selection and calculates the pivot afresh,
// respecting the currently active settings
void updateFromSelection();

private:
void onRegistryKeyChanged();
virtual void updateFromSelection() = 0;
};

}
2 changes: 1 addition & 1 deletion radiantcore/selection/RadiantSelectionSystem.cpp
Expand Up @@ -1021,7 +1021,7 @@ void RadiantSelectionSystem::initialiseModule(const IApplicationContext& ctx)
IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Selection"));

page.appendCheckBox(_("Ignore light volume bounds when calculating default rotation pivot location"),
ManipulationPivot::RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);
SceneManipulationPivot::RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);

// Connect the bounds changed caller
GlobalSceneGraph().signal_boundsChanged().connect(
Expand Down
5 changes: 3 additions & 2 deletions radiantcore/selection/RadiantSelectionSystem.h
Expand Up @@ -12,7 +12,7 @@
#include "math/Matrix4.h"
#include "SelectedNodeList.h"

#include "ManipulationPivot.h"
#include "SceneManipulationPivot.h"

namespace selection
{
Expand All @@ -21,7 +21,8 @@ class RadiantSelectionSystem :
public SelectionSystem,
public Renderable
{
ManipulationPivot _pivot;
private:
SceneManipulationPivot _pivot;

typedef std::set<Observer*> ObserverList;
ObserverList _observers;
Expand Down
116 changes: 116 additions & 0 deletions radiantcore/selection/SceneManipulationPivot.cpp
@@ -0,0 +1,116 @@
#include "SceneManipulationPivot.h"

#include "iselection.h"
#include "ilightnode.h"
#include "ientity.h"
#include "igrid.h"
#include "selectionlib.h"
#include "registry/registry.h"
#include "selection/algorithm/General.h"

namespace selection
{

const std::string SceneManipulationPivot::RKEY_ENTITY_PIVOT_IS_ORIGIN = "user/ui/rotationPivotIsOrigin";
const std::string SceneManipulationPivot::RKEY_SNAP_ROTATION_PIVOT_TO_GRID = "user/ui/snapRotationPivotToGrid";
const std::string SceneManipulationPivot::RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES = "user/ui/defaultPivotLocationIgnoresLightVolumes";

SceneManipulationPivot::SceneManipulationPivot() :
_entityPivotIsOrigin(false),
_snapPivotToGrid(false),
_defaultPivotLocationIgnoresLightVolumes(false)
{}

void SceneManipulationPivot::initialise()
{
_entityPivotIsOrigin = registry::getValue<bool>(RKEY_ENTITY_PIVOT_IS_ORIGIN);
_snapPivotToGrid = registry::getValue<bool>(RKEY_SNAP_ROTATION_PIVOT_TO_GRID);
_defaultPivotLocationIgnoresLightVolumes = registry::getValue<bool>(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);

GlobalRegistry().signalForKey(RKEY_ENTITY_PIVOT_IS_ORIGIN).connect(
sigc::mem_fun(this, &SceneManipulationPivot::onRegistryKeyChanged)
);
GlobalRegistry().signalForKey(RKEY_SNAP_ROTATION_PIVOT_TO_GRID).connect(
sigc::mem_fun(this, &SceneManipulationPivot::onRegistryKeyChanged)
);
GlobalRegistry().signalForKey(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES).connect(
sigc::mem_fun(this, &SceneManipulationPivot::onRegistryKeyChanged)
);
}

void SceneManipulationPivot::applyTranslation(const Vector3& translation)
{
ManipulationPivot::applyTranslation(translation);

if (_snapPivotToGrid)
{
// The resulting pivot should be grid-snapped
_pivot2World.tCol().getVector3().snap(GlobalGrid().getGridSize());
}
}

void SceneManipulationPivot::updateFromSelection()
{
_needsRecalculation = false;
_userLocked = false;

Vector3 objectPivot;

const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo();

if (info.entityCount == 1 && info.totalCount == 1 &&
Node_getLightNode(GlobalSelectionSystem().ultimateSelected()))
{
// When a single light is selected, use the origin for rotation
objectPivot = Node_getLightNode(GlobalSelectionSystem().ultimateSelected())->getSelectAABB().origin;
}
else if (info.entityCount == 1 && info.totalCount == 1 && _entityPivotIsOrigin)
{
// Test if a single entity is selected
scene::INodePtr node = GlobalSelectionSystem().ultimateSelected();
Entity* entity = Node_getEntity(node);

if (entity != nullptr)
{
objectPivot = string::convert<Vector3>(entity->getKeyValue("origin"));
}
}
else
{
// Create a local variable where the aabb information is stored
AABB bounds;

// Traverse through the selection and update the <bounds> variable
if (GlobalSelectionSystem().Mode() == SelectionSystem::eComponent)
{
bounds = algorithm::getCurrentComponentSelectionBounds();
}
else
{
// Ignore light volumes for the pivot calculation
bounds = algorithm::getCurrentSelectionBounds(!_defaultPivotLocationIgnoresLightVolumes);
}

// the <bounds> variable now contains the AABB of the selection, retrieve the origin
objectPivot = bounds.origin;
}

if (_snapPivotToGrid)
{
objectPivot.snap(GlobalGrid().getGridSize());
}

// The pivot2world matrix is just a translation from the world origin (0,0,0) to the object pivot
setFromMatrix(Matrix4::getTranslation(objectPivot));
}

void SceneManipulationPivot::onRegistryKeyChanged()
{
_entityPivotIsOrigin = registry::getValue<bool>(RKEY_ENTITY_PIVOT_IS_ORIGIN);
_snapPivotToGrid = registry::getValue<bool>(RKEY_SNAP_ROTATION_PIVOT_TO_GRID);
_defaultPivotLocationIgnoresLightVolumes = registry::getValue<bool>(RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES);

GlobalSelectionSystem().pivotChanged();
}

}
42 changes: 42 additions & 0 deletions radiantcore/selection/SceneManipulationPivot.h
@@ -0,0 +1,42 @@
#pragma once

#include "ManipulationPivot.h"

namespace selection
{

/**
* ManipulationPivot specialisation tailored to suit the needs
* of the main map selection manipulations.
*/
class SceneManipulationPivot final :
public ManipulationPivot
{
private:
// Whether to snap the pivot to grid after movement
bool _snapPivotToGrid;

// Use a single Entity's "origin" keyvalue as pivot
bool _entityPivotIsOrigin;

// Whether to consider light volumes when calculating the selection bounds
bool _defaultPivotLocationIgnoresLightVolumes;

public:
static const std::string RKEY_ENTITY_PIVOT_IS_ORIGIN;
static const std::string RKEY_SNAP_ROTATION_PIVOT_TO_GRID;
static const std::string RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES;

SceneManipulationPivot();

void initialise();

void applyTranslation(const Vector3& translation) override;

void updateFromSelection() override;

private:
void onRegistryKeyChanged();
};

}

0 comments on commit 401f30a

Please sign in to comment.