Skip to content

Commit

Permalink
#5344 & #5336: CSG Merge now only handles brushes sharing the same pa…
Browse files Browse the repository at this point in the history
…rent entity.

This also fixes the crash in #5336.
  • Loading branch information
codereader committed Sep 27, 2020
1 parent 36b5bab commit dc1df25
Showing 1 changed file with 68 additions and 28 deletions.
96 changes: 68 additions & 28 deletions radiantcore/brush/csg/CSG.cpp
Expand Up @@ -395,56 +395,96 @@ bool Brush_merge(Brush& brush, const BrushPtrVector& in, bool onlyshape) {
void mergeSelectedBrushes(const cmd::ArgumentList& args)
{
// Get the current selection
BrushPtrVector brushes = selection::algorithm::getSelectedBrushes();
auto brushes = selection::algorithm::getSelectedBrushes();

if (brushes.empty())
{
throw cmd::ExecutionNotPossible(_("CSG Merge: No brushes selected."));
}

if (brushes.size() < 2)
// Group the brushes by their parents
std::map<scene::INodePtr, BrushPtrVector> brushesByEntity;

for (const auto& brushNode : brushes)
{
throw cmd::ExecutionNotPossible(_("CSG Merge: At least two brushes have to be selected."));
auto parent = brushNode->getParent();

if (brushesByEntity.find(parent) == brushesByEntity.end())
{
brushesByEntity[parent] = BrushPtrVector();
}

brushesByEntity[parent].emplace_back(brushNode);
}

rMessage() << "CSG Merge: Merging " << brushes.size() << " brushes." << std::endl;
bool selectionIsSuitable = false;
// At least one group should have more than two members
for (const auto& pair : brushesByEntity)
{
if (pair.second.size() >= 2)
{
selectionIsSuitable = true;
break;
}
}

if (!selectionIsSuitable)
{
throw cmd::ExecutionNotPossible(_("CSG Merge: At least two brushes sharing of the same entity have to be selected."));
}

UndoableCommand undo("mergeSelectedBrushes");

// Take the last selected node as reference for layers and parent
scene::INodePtr merged = GlobalSelectionSystem().ultimateSelected();
bool anythingMerged = false;
for (const auto& pair : brushesByEntity)
{
if (pair.second.size() < 2)
{
continue;
}

// Take the last selected node as reference for layers and parent
auto lastBrush = pair.second.back();
auto parent = lastBrush->getParent();

scene::INodePtr parent = merged->getParent();
assert(parent != NULL);
assert(Node_isEntity(parent));

// Create a new BrushNode
scene::INodePtr node = GlobalBrushCreator().createBrush();
// Create a new BrushNode
auto newBrush = GlobalBrushCreator().createBrush();

// Insert the newly created brush into the (same) parent entity
parent->addChildNode(node);
// Insert the newly created brush into the same parent entity
parent->addChildNode(newBrush);

// Move the new brush to the same layers as the merged one
node->assignToLayers(merged->getLayers());
// Move the new brush to the same layers as the merged one
newBrush->assignToLayers(lastBrush->getLayers());

// Get the contained brush
Brush* brush = Node_getBrush(node);
// Get the contained brush
Brush* brush = Node_getBrush(newBrush);

// Attempt to merge the selected brushes into the new one
if (!Brush_merge(*brush, brushes, true))
{
throw cmd::ExecutionFailure(_("CSG Merge: Failed - result would not be convex"));
}
// Attempt to merge the selected brushes into the new one
if (!Brush_merge(*brush, pair.second, true))
{
continue;
}

ASSERT_MESSAGE(!brush->empty(), "brush left with no faces after merge");
anythingMerged = true;

// Remove the original brushes
for (BrushPtrVector::iterator i = brushes.begin(); i != brushes.end(); ++i)
{
scene::removeNodeFromParent(*i);
ASSERT_MESSAGE(!brush->empty(), "brush left with no faces after merge");

// Remove the original brushes
for (const auto& brush : pair.second)
{
scene::removeNodeFromParent(brush);
}

// Select the new brush
Node_setSelected(newBrush, true);
}

// Select the new brush
Node_setSelected(node, true);
if (!anythingMerged)
{
throw cmd::ExecutionFailure(_("CSG Merge: Failed - result would not be convex"));
}

rMessage() << "CSG Merge: Succeeded." << std::endl;
SceneChangeNotify();
Expand Down

0 comments on commit dc1df25

Please sign in to comment.