Example #1
0
void mergeSelectedBrushes(const cmd::ArgumentList& args)
{
	// Get the current selection
	BrushPtrVector brushes = selection::algorithm::getSelectedBrushes();

	if (brushes.empty()) {
		rMessage() << _("CSG Merge: No brushes selected.") << std::endl;
		wxutil::Messagebox::ShowError(_("CSG Merge: No brushes selected."));
		return;
	}

	if (brushes.size() < 2) {
		rMessage() << "CSG Merge: At least two brushes have to be selected.\n";
		wxutil::Messagebox::ShowError("CSG Merge: At least two brushes have to be selected.");
		return;
	}

	rMessage() << "CSG Merge: Merging " << brushes.size() << " brushes." << std::endl;

	UndoableCommand undo("mergeSelectedBrushes");

	// Take the last selected node as reference for layers and parent
	scene::INodePtr merged = GlobalSelectionSystem().ultimateSelected();

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

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

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

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

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

	// Attempt to merge the selected brushes into the new one
	if (!Brush_merge(*brush, brushes, true))
	{
		rWarning() << "CSG Merge: Failed - result would not be convex." << std::endl;
		return;
	}

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

	// Remove the original brushes
	for (BrushPtrVector::iterator i = brushes.begin(); i != brushes.end(); ++i)
	{
		scene::removeNodeFromParent(*i);
	}

	// Select the new brush
	Node_setSelected(node, true);

	rMessage() << "CSG Merge: Succeeded." << std::endl;
	SceneChangeNotify();
}
Example #2
0
void BrushByPlaneClipper::visit(const scene::INodePtr& node) const {
	// Don't clip invisible nodes
	if (!node->visible()) {
		return;
	}

	// Try to cast the instance onto a brush
	Brush* brush = Node_getBrush(node);

	// Return if not brush
	if (brush == NULL) {
		return;
	}

	Plane3 plane(_p0, _p1, _p2);
	if (!plane.isValid()) {
		return;
	}

	// greebo: Analyse the brush to find out which shader is the most used one
	getMostUsedTexturing(brush);

	BrushSplitType split = Brush_classifyPlane(*brush, _split == eFront ? -plane : plane);

	if (split.counts[ePlaneBack] && split.counts[ePlaneFront]) {
		// the plane intersects this brush
		if (_split == eFrontAndBack) {
			scene::INodePtr brushNode = GlobalBrushCreator().createBrush();

			assert(brushNode != NULL);

			Brush* fragment = Node_getBrush(brushNode);
			assert(fragment != NULL);
			fragment->copy(*brush);

			FacePtr newFace = fragment->addPlane(_p0, _p1, _p2, _mostUsedShader, _mostUsedProjection);

			if (newFace != NULL && _split != eFront) {
				newFace->flipWinding();
			}

			fragment->removeEmptyFaces();
			ASSERT_MESSAGE(!fragment->empty(), "brush left with no faces after split");

			// Mark this brush for insertion
			_insertList.insert(InsertMap::value_type(brushNode, node->getParent()));
		}

		FacePtr newFace = brush->addPlane(_p0, _p1, _p2, _mostUsedShader, _mostUsedProjection);

		if (newFace != NULL && _split == eFront) {
			newFace->flipWinding();
		}

		brush->removeEmptyFaces();
		ASSERT_MESSAGE(!brush->empty(), "brush left with no faces after split");
	}
	// the plane does not intersect this brush
	else if (_split != eFrontAndBack && split.counts[ePlaneBack] != 0) {
		// the brush is "behind" the plane
		_deleteList.insert(node);
	}
}
void BrushByPlaneClipper::split(const BrushPtrVector& brushes)
{
	Plane3 plane(_p0, _p1, _p2);

	if (!plane.isValid())
	{
		return;
	}

	for (BrushPtrVector::const_iterator i = brushes.begin(); i != brushes.end(); ++i)
	{
		const BrushNodePtr& node = *i;

		// Don't clip invisible nodes
		if (!node->visible())
		{
			continue;
		}

		Brush& brush = node->getBrush();

		scene::INodePtr parent = node->getParent();

		if (!parent)
		{
			continue;
		}

		// greebo: Analyse the brush to find out which shader is the most used one
		getMostUsedTexturing(brush);

		BrushSplitType split = Brush_classifyPlane(brush, _split == eFront ? -plane : plane);

		if (split.counts[ePlaneBack] && split.counts[ePlaneFront])
		{
			// the plane intersects this brush
			if (_split == eFrontAndBack)
			{
				scene::INodePtr fragmentNode = GlobalBrushCreator().createBrush();

				assert(fragmentNode != NULL);

				// Put the fragment in the same layer as the brush it was clipped from
				// Do this before adding the fragment to the parent, since there is an
				// update algorithm setting the visibility of the fragment right there.
				scene::assignNodeToLayers(fragmentNode, node->getLayers());

				// greebo: For copying the texture scale the new node needs to be inserted in the scene
				// otherwise the shaders cannot be captured and the scale is off

				// Insert the child into the designated parent
				scene::addNodeToContainer(fragmentNode, parent);

				// Select the child
				Node_setSelected(fragmentNode, true);

				Brush* fragment = Node_getBrush(fragmentNode);
				assert(fragment != NULL);
				fragment->copy(brush);

				FacePtr newFace = fragment->addPlane(_p0, _p1, _p2, _mostUsedShader, _mostUsedProjection);

				if (newFace != NULL && _split != eFront)
				{
					newFace->flipWinding();
				}

				fragment->removeEmptyFaces();
				ASSERT_MESSAGE(!fragment->empty(), "brush left with no faces after split");
			}

			FacePtr newFace = brush.addPlane(_p0, _p1, _p2, _mostUsedShader, _mostUsedProjection);

			if (newFace != NULL && _split == eFront) {
				newFace->flipWinding();
			}

			brush.removeEmptyFaces();
			ASSERT_MESSAGE(!brush.empty(), "brush left with no faces after split");
		}
		// the plane does not intersect this brush
		else if (_split != eFrontAndBack && split.counts[ePlaneBack] != 0)
		{
			// the brush is "behind" the plane
			// Remove the node from the scene
			scene::removeNodeFromParent(node);
		}
	}
}