void makeVisportal(const cmd::ArgumentList& args) { BrushPtrVector brushes = getSelectedBrushes(); if (brushes.size() <= 0) { gtkutil::errorDialog(_("No brushes selected."), GlobalMainFrame().getTopLevelWindow()); return; } // Create a scoped undocmd object UndoableCommand cmd("brushMakeVisportal"); for (std::size_t i = 0; i < brushes.size(); i++) { Brush& brush = brushes[i]->getBrush(); // don't allow empty brushes if (brush.getNumFaces() == 0) continue; // Set all faces to nodraw first brush.setShader(GlobalRegistry().get(RKEY_NODRAW_SHADER)); // Find the largest face (in terms of area) LargestFaceFinder finder; brush.forEachFace(finder); finder.getLargestFace().setShader(GlobalRegistry().get(RKEY_VISPORTAL_SHADER)); } }
void makeRoomForSelectedBrushes(const cmd::ArgumentList& args) { UndoableCommand undo("brushMakeRoom"); // Find all brushes BrushPtrVector brushes = selection::algorithm::getSelectedBrushes(); // Cycle through the brushes and hollow them // We assume that all these selected brushes are visible as well. for (std::size_t i = 0; i < brushes.size(); ++i) { hollowBrush(brushes[i], true); } SceneChangeNotify(); }
void subtractBrushesFromUnselected(const cmd::ArgumentList& args) { if (registry::getValue<bool>(RKEY_EMIT_CSG_SUBTRACT_WARNING)) { wxutil::Messagebox::Show(_("This Is Not Dromed Warning"), _("Note: be careful when using the CSG tool, as you might end up\n" "with an unnecessary number of tiny brushes and/or leaks.\n" "This popup will not be shown again."), ui::IDialog::MESSAGE_CONFIRM); // Disable this warning registry::setValue(RKEY_EMIT_CSG_SUBTRACT_WARNING, false); } // Collect all selected brushes BrushPtrVector brushes = selection::algorithm::getSelectedBrushes(); if (brushes.empty()) { rMessage() << _("CSG Subtract: No brushes selected.") << std::endl; wxutil::Messagebox::ShowError(_("CSG Subtract: No brushes selected.")); return; } rMessage() << "CSG Subtract: Subtracting " << brushes.size() << " brushes.\n"; UndoableCommand undo("brushSubtract"); // subtract selected from unselected std::size_t before = 0; std::size_t after = 0; // instantiate a scoped walker class { SubtractBrushesFromUnselected walker(brushes, before, after); GlobalSceneGraph().root()->traverse(walker); } rMessage() << "CSG Subtract: Result: " << after << " fragment" << (after == 1 ? "" : "s") << " from " << before << " brush" << (before == 1 ? "" : "es") << ".\n"; SceneChangeNotify(); }
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(); }
// Returns true if fragments have been inserted into the given ret_fragments list bool Brush_subtract(const BrushNodePtr& brush, const Brush& other, BrushPtrVector& ret_fragments) { if (brush->getBrush().localAABB().intersects(other.localAABB())) { BrushPtrVector fragments; fragments.reserve(other.getNumFaces()); BrushNodePtr back = std::dynamic_pointer_cast<BrushNode>(brush->clone()); for (Brush::const_iterator i(other.begin()); i != other.end(); ++i) { const Face& face = *(*i); if (!face.contributes()) continue; BrushSplitType split = Brush_classifyPlane(back->getBrush(), face.plane3()); if (split.counts[ePlaneFront] != 0 && split.counts[ePlaneBack] != 0) { fragments.push_back(std::dynamic_pointer_cast<BrushNode>(back->clone())); FacePtr newFace = fragments.back()->getBrush().addFace(face); if (newFace != 0) { newFace->flipWinding(); } back->getBrush().addFace(face); } else if (split.counts[ePlaneBack] == 0) { return false; } } ret_fragments.insert(ret_fragments.end(), fragments.begin(), fragments.end()); return true; } return false; }
// Try to create a CM from the selected entity void createCMFromSelection(const cmd::ArgumentList& args) { // Check the current selection state const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); if (info.totalCount == info.entityCount && info.totalCount == 1) { // Retrieve the node, instance and entity const scene::INodePtr& entityNode = GlobalSelectionSystem().ultimateSelected(); // Try to retrieve the group node scene::GroupNodePtr groupNode = Node_getGroupNode(entityNode); // Remove the entity origin from the brushes if (groupNode != NULL) { groupNode->removeOriginFromChildren(); // Deselect the node Node_setSelected(entityNode, false); // Select all the child nodes NodeSelector visitor; entityNode->traverse(visitor); BrushPtrVector brushes = algorithm::getSelectedBrushes(); // Create a new collisionmodel on the heap using a shared_ptr cmutil::CollisionModelPtr cm(new cmutil::CollisionModel()); // Add all the brushes to the collision model for (std::size_t i = 0; i < brushes.size(); i++) { cm->addBrush(brushes[i]->getBrush()); } ui::ModelSelectorResult modelAndSkin = ui::ModelSelector::chooseModel("", false, false); std::string basePath = GlobalGameManager().getModPath(); std::string modelPath = basePath + modelAndSkin.model; std::string newExtension = "." + GlobalRegistry().get(RKEY_CM_EXT); // Set the model string to correctly associate the clipmodel cm->setModel(modelAndSkin.model); try { // create the new autosave filename by changing the extension Path cmPath = boost::filesystem::change_extension( Path(modelPath, boost::filesystem::native), newExtension ); // Open the stream to the output file std::ofstream outfile(cmPath.string().c_str()); if (outfile.is_open()) { // Insert the CollisionModel into the stream outfile << *cm; // Close the file outfile.close(); globalOutputStream() << "CollisionModel saved to " << cmPath.string() << std::endl; } else { gtkutil::errorDialog( (boost::format("Couldn't save to file: %s") % cmPath.string()).str(), GlobalMainFrame().getTopLevelWindow()); } } catch (boost::filesystem::filesystem_error f) { globalErrorStream() << "CollisionModel: " << f.what() << std::endl; } // De-select the child brushes GlobalSelectionSystem().setSelectedAll(false); // Re-add the origin to the brushes groupNode->addOriginToChildren(); // Re-select the node Node_setSelected(entityNode, true); } } else { gtkutil::errorDialog( _(ERRSTR_WRONG_SELECTION.c_str()), GlobalMainFrame().getTopLevelWindow()); } }
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); } } }
// greebo: TODO: Make this a member method of the Brush class bool Brush_merge(Brush& brush, const BrushPtrVector& in, bool onlyshape) { // gather potential outer faces typedef std::vector<const Face*> Faces; Faces faces; for (BrushPtrVector::const_iterator i(in.begin()); i != in.end(); ++i) { (*i)->getBrush().evaluateBRep(); for (Brush::const_iterator j((*i)->getBrush().begin()); j != (*i)->getBrush().end(); ++j) { if (!(*j)->contributes()) { continue; } const Face& face1 = *(*j); bool skip = false; // test faces of all input brushes //!\todo SPEEDUP: Flag already-skip faces and only test brushes from i+1 upwards. for (BrushPtrVector::const_iterator k(in.begin()); !skip && k != in.end(); ++k) { if (k != i) { // don't test a brush against itself for (Brush::const_iterator l((*k)->getBrush().begin()); !skip && l != (*k)->getBrush().end(); ++l) { const Face& face2 = *(*l); // face opposes another face if (face1.plane3() == -face2.plane3()) { // skip opposing planes skip = true; break; } } } } // check faces already stored for (Faces::const_iterator m = faces.begin(); !skip && m != faces.end(); ++m) { const Face& face2 = *(*m); // face equals another face if (face1.plane3() == face2.plane3()) { // if the texture/shader references should be the same but are not if (!onlyshape && !shader_equal( face1.getFaceShader().getMaterialName(), face2.getFaceShader().getMaterialName() )) { return false; } // skip duplicate planes skip = true; break; } // face1 plane intersects face2 winding or vice versa if (Winding::planesConcave(face1.getWinding(), face2.getWinding(), face1.plane3(), face2.plane3())) { // result would not be convex return false; } } if (!skip) { faces.push_back(&face1); } } } for (Faces::const_iterator i = faces.begin(); i != faces.end(); ++i) { if (!brush.addFace(*(*i))) { // result would have too many sides return false; } } brush.removeEmptyFaces(); return true; }