Skip to content

Commit

Permalink
#5746: More work on the interfaces, introduce IComponentSelectable in…
Browse files Browse the repository at this point in the history
…terface.
  • Loading branch information
codereader committed Sep 19, 2021
1 parent 8e5e092 commit 11159a4
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 25 deletions.
40 changes: 39 additions & 1 deletion include/itexturetoolmodel.h
Expand Up @@ -9,6 +9,8 @@
#include <sigc++/signal.h>

class Matrix3;
class IFace;
class IPatch;

namespace textool
{
Expand Down Expand Up @@ -48,6 +50,24 @@ class ITransformable
virtual void commitTransformation() = 0;
};

// A Texture Tool node that consists of one or more selectable components
class IComponentSelectable
{
public:
using Ptr = std::shared_ptr<IComponentSelectable>;

virtual ~IComponentSelectable() {}

// True if this node has at least one selected component (e.g. a vertex)
virtual bool hasSelectedComponents() const = 0;

// Unselect all components of this node
virtual void clearComponentSelection() = 0;

// Perform a selection test using the given selector and test volume
virtual void testSelectComponents(Selector& selector, SelectionTest& test) = 0;
};

// The base element of every node in the ITextureToolSceneGraph
class INode :
public ITransformable,
Expand All @@ -66,7 +86,8 @@ class INode :

// Node representing a single brush face
class IFaceNode :
public virtual INode
public virtual INode,
public IComponentSelectable
{
public:
virtual ~IFaceNode() {}
Expand All @@ -76,6 +97,19 @@ class IFaceNode :
virtual IFace& getFace() = 0;
};

// Node representing a patch
class IPatchNode :
public virtual INode,
public IComponentSelectable
{
public:
virtual ~IPatchNode() {}

using Ptr = std::shared_ptr<IPatchNode>;

virtual IPatch& getPatch() = 0;
};

/**
* The scene graph of all texture tool items. From all the selected
* items in the regular SceneGraph the texture-editable elements
Expand Down Expand Up @@ -107,6 +141,10 @@ class ITextureToolSelectionSystem :
// Collection should not be modified during iteration
virtual void foreachSelectedNode(const std::function<bool(const INode::Ptr&)>& functor) = 0;

// Iterate over every node that has at least one component selected, visiting the given functor
// Collection should not be modified during iteration
virtual void foreachSelectedComponentNode(const std::function<bool(const IComponentSelectable::Ptr&)>& functor) = 0;

virtual SelectionMode getMode() const = 0;
virtual void setMode(SelectionMode mode) = 0;
virtual sigc::signal<void, SelectionMode>& signal_selectionModeChanged() = 0;
Expand Down
15 changes: 15 additions & 0 deletions radiantcore/selection/textool/FaceNode.h
Expand Up @@ -95,6 +95,21 @@ class FaceNode :
}
}

bool hasSelectedComponents() const override
{
return false;
}

void clearComponentSelection() override
{

}

void testSelectComponents(Selector& selector, SelectionTest& test) override
{

}

void render(SelectionMode mode) override
{
glEnable(GL_BLEND);
Expand Down
23 changes: 22 additions & 1 deletion radiantcore/selection/textool/PatchNode.h
Expand Up @@ -9,7 +9,8 @@ namespace textool
{

class PatchNode :
public NodeBase
public NodeBase,
public IPatchNode
{
private:
IPatch& _patch;
Expand All @@ -27,6 +28,11 @@ class PatchNode :
});
}

IPatch& getPatch() override
{
return _patch;
}

void beginTransformation() override
{
// We call undoSave() here for consistency, but technically it's too early -
Expand Down Expand Up @@ -112,6 +118,21 @@ class PatchNode :
#endif
}

bool hasSelectedComponents() const override
{
return false;
}

void clearComponentSelection() override
{

}

void testSelectComponents(Selector& selector, SelectionTest& test) override
{

}

void render(SelectionMode mode) override
{
glEnable(GL_BLEND);
Expand Down
15 changes: 15 additions & 0 deletions radiantcore/selection/textool/TextureToolSelectionSystem.cpp
Expand Up @@ -142,6 +142,21 @@ void TextureToolSelectionSystem::foreachSelectedNode(const std::function<bool(co
});
}

void TextureToolSelectionSystem::foreachSelectedComponentNode(const std::function<bool(const IComponentSelectable::Ptr&)>& functor)
{
GlobalTextureToolSceneGraph().foreachNode([&](const INode::Ptr& node)
{
auto componentSelectable = std::dynamic_pointer_cast<IComponentSelectable>(node);

if (componentSelectable && componentSelectable->hasSelectedComponents())
{
return functor(componentSelectable);
}

return true;
});
}

std::size_t TextureToolSelectionSystem::registerManipulator(const selection::ITextureToolManipulator::Ptr& manipulator)
{
std::size_t newId = 1;
Expand Down
1 change: 1 addition & 0 deletions radiantcore/selection/textool/TextureToolSelectionSystem.h
Expand Up @@ -35,6 +35,7 @@ class TextureToolSelectionSystem :
sigc::signal<void, SelectionMode>& signal_selectionModeChanged() override;

void foreachSelectedNode(const std::function<bool(const INode::Ptr&)>& functor) override;
void foreachSelectedComponentNode(const std::function<bool(const IComponentSelectable::Ptr&)>& functor) override;

void selectPoint(SelectionTest& test, SelectionSystem::EModifier modifier) override;
void selectArea(SelectionTest& test, SelectionSystem::EModifier modifier) override;
Expand Down
97 changes: 74 additions & 23 deletions test/TextureTool.cpp
Expand Up @@ -45,6 +45,32 @@ inline textool::INode::Ptr getFirstTextureToolNode()
return returnValue;
}

std::vector<textool::INode::Ptr> getAllSelectedTextoolNodes()
{
std::vector<textool::INode::Ptr> selectedNodes;

GlobalTextureToolSelectionSystem().foreachSelectedNode([&](const textool::INode::Ptr& node)
{
selectedNodes.push_back(node);
return true;
});

return selectedNodes;
}

std::vector<textool::IComponentSelectable::Ptr> getAllSelectedComponentNodes()
{
std::vector<textool::IComponentSelectable::Ptr> selectedNodes;

GlobalTextureToolSelectionSystem().foreachSelectedComponentNode([&](const textool::IComponentSelectable::Ptr& node)
{
selectedNodes.push_back(node);
return true;
});

return selectedNodes;
}

// Checks that changing the regular scene selection will have an effect on the tex tool scene
TEST_F(TextureToolTest, SceneGraphObservesSelection)
{
Expand Down Expand Up @@ -449,7 +475,7 @@ void performPointSelection(const Vector2& texcoord, const render::View& view)
GlobalTextureToolSelectionSystem().selectPoint(test, SelectionSystem::eToggle);
}

TEST_F(TextureToolTest, TestSelectPatchByPoint)
TEST_F(TextureToolTest, TestSelectPatchSurfaceByPoint)
{
auto patchNode = setupPatchNodeForTextureTool();
auto patch = Node_getIPatch(patchNode);
Expand All @@ -467,17 +493,52 @@ TEST_F(TextureToolTest, TestSelectPatchByPoint)
performPointSelection(Vector2(bounds.origin.x(), bounds.origin.y()), view);

// Check if the node was selected
std::vector<textool::INode::Ptr> selectedNodes;
GlobalTextureToolSelectionSystem().foreachSelectedNode([&](const textool::INode::Ptr& node)
{
selectedNodes.push_back(node);
return true;
});

auto selectedNodes = getAllSelectedTextoolNodes();
EXPECT_EQ(selectedNodes.size(), 1) << "Only one patch should be selected";
EXPECT_TRUE(std::dynamic_pointer_cast<textool::IPatchNode>(selectedNodes.front())) << "Couldn't cast to special type";
}

TEST_F(TextureToolTest, TestSelectFaceByPoint)
TEST_F(TextureToolTest, TestSelectPatchVertexByPoint)
{
auto patchNode = setupPatchNodeForTextureTool();
auto patch = Node_getIPatch(patchNode);

// Get the texture space bounds of this patch
auto bounds = getTextureSpaceBounds(*patch);

// Construct a view that includes the patch UV bounds
bounds.extents *= 1.2f;

render::TextureToolView view;
view.constructFromTextureSpaceBounds(bounds, TEXTOOL_WIDTH, TEXTOOL_HEIGHT);

// Switch to vertex selection mode
GlobalTextureToolSelectionSystem().setMode(textool::SelectionMode::Vertex);

// Selecting something in the middle of the patch should not do anything
performPointSelection(Vector2(bounds.origin.x(), bounds.origin.y()), view);
EXPECT_TRUE(getAllSelectedComponentNodes().empty()) << "Test-selecting a patch in its middle should not have succeeded";

// Get the texcoords of the first vertex
auto firstVertex = patch->ctrlAt(2, 1).texcoord;
performPointSelection(firstVertex, view);

// Hitting a vertex will select the patch itself
EXPECT_EQ(getAllSelectedComponentNodes().size(), 1) << "Only one patch should be selected";

// Hitting another vertex should not de-select the patch
auto secondVertex = patch->ctrlAt(3, 0).texcoord;
performPointSelection(secondVertex, view);
EXPECT_EQ(getAllSelectedComponentNodes().size(), 1) << "Only one patch should still be selected";

// De-selecting the first and the second vertex should release the patch itself
performPointSelection(secondVertex, view);
EXPECT_EQ(getAllSelectedComponentNodes().size(), 1) << "Only one patch should still be selected";
performPointSelection(firstVertex, view);
EXPECT_TRUE(getAllSelectedComponentNodes().empty()) << "Selection should be empty now";
}

TEST_F(TextureToolTest, TestSelectFaceSurfaceByPoint)
{
auto worldspawn = GlobalMapModule().findOrInsertWorldspawn();
auto brush = algorithm::createCubicBrush(worldspawn, Vector3(0, 256, 256), "textures/numbers/1");
Expand All @@ -504,15 +565,10 @@ TEST_F(TextureToolTest, TestSelectFaceByPoint)
performPointSelection(algorithm::getFaceCentroid(faceUp), view);

// Check if the node was selected
std::vector<textool::INode::Ptr> selectedNodes;
GlobalTextureToolSelectionSystem().foreachSelectedNode([&](const textool::INode::Ptr& node)
{
selectedNodes.push_back(node);
return true;
});

auto selectedNodes = getAllSelectedTextoolNodes();
EXPECT_EQ(selectedNodes.size(), 1) << "Only one item should be selected";
EXPECT_EQ(selectedNodes.front(), textoolFace) << "The face should be selected";
EXPECT_TRUE(std::dynamic_pointer_cast<textool::IFaceNode>(selectedNodes.front())) << "Couldn't cast to special type";
}

TEST_F(TextureToolTest, TestSelectPatchByArea)
Expand All @@ -536,14 +592,9 @@ TEST_F(TextureToolTest, TestSelectPatchByArea)
GlobalTextureToolSelectionSystem().selectArea(test, SelectionSystem::eToggle);

// Check if the node was selected
std::vector<textool::INode::Ptr> selectedNodes;
GlobalTextureToolSelectionSystem().foreachSelectedNode([&](const textool::INode::Ptr& node)
{
selectedNodes.push_back(node);
return true;
});

auto selectedNodes = getAllSelectedTextoolNodes();
EXPECT_EQ(selectedNodes.size(), 1) << "Only one patch should be selected";
EXPECT_TRUE(std::dynamic_pointer_cast<textool::IPatchNode>(selectedNodes.front())) << "Couldn't cast to special type";
}

inline std::vector<Vector2> getTexcoords(const IFace* face)
Expand Down

0 comments on commit 11159a4

Please sign in to comment.