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(); }
void GroupCycle::updateSelection() { _updateActive = true; // Do some sanity checking before we run into crashes if (_index >= 0 && _index < static_cast<int>(_list.size())) { for (std::size_t i = 0; i < _list.size(); i++) { Node_setSelected(_list[i], false); } Node_setSelected(_list[_index], true); } SceneChangeNotify(); _updateActive = false; }
void SelectionSet::deselect() { for (NodeSet::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { scene::INodePtr node = i->lock(); if (node == NULL) continue; // skip deleted nodes if (!node->visible()) continue; // skip invisible, non-instantiated nodes Node_setSelected(node, false); } }
void hollowBrush(const BrushNodePtr& sourceBrush, bool makeRoom) { // Hollow the brush using the current grid size sourceBrush->getBrush().forEachFace([&] (Face& face) { if (!face.contributes()) { return; } scene::INodePtr parent = sourceBrush->getParent(); scene::INodePtr newNode = GlobalBrushCreator().createBrush(); BrushNodePtr brushNode = std::dynamic_pointer_cast<BrushNode>(newNode); assert(brushNode); // Add the child to the same parent as the source brush parent->addChildNode(brushNode); // Move the child brushes to the same layer as their source brushNode->assignToLayers(sourceBrush->getLayers()); // Copy all faces from the source brush brushNode->getBrush().copy(sourceBrush->getBrush()); Node_setSelected(brushNode, true); FacePtr newFace = brushNode->getBrush().addFace(face); if (newFace != 0) { float offset = GlobalGrid().getGridSize(); newFace->flipWinding(); newFace->getPlane().offset(offset); newFace->planeChanged(); if (makeRoom) { // Retrieve the normal vector of the "source" face brushNode->getBrush().transform(Matrix4::getTranslation(face.getPlane().getPlane().normal() * offset)); brushNode->getBrush().freezeTransform(); } } brushNode->getBrush().removeEmptyFaces(); }); // Now unselect and remove the source brush from the scene scene::removeNodeFromParent(sourceBrush); }
// Adds the cloned nodes to their designated parents. Pass TRUE to select the nodes. void moveClonedNodes(bool select) { for (Map::iterator i = _cloned.begin(); i != _cloned.end(); ++i) { // Remove the child from the basic container first _cloneRoot->removeChildNode(i->first); // Add the node to its parent i->second->addChildNode(i->first); if (select) { Node_setSelected(i->first, true); } } }
void SelectBrush (int entitynum, int brushnum) { scene::Path path; Scene_FindEntityBrush(entitynum, brushnum, path); if (path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top()))) { Node_setSelected(path.top(), true); XYWndPtr xyView = GlobalXYWnd().getActiveXY(); if (xyView) { xyView->positionView(path.top()->worldAABB().origin); } } }
bool pre(const scene::INodePtr& node) { if (node->visible()) { Brush* brush = Node_getBrush(node); if (brush != NULL && brush->hasShader(_name)) { Node_setSelected(node, true); return false; // don't traverse brushes } // not a suitable brush, traverse further return true; } else { return false; // don't traverse invisible nodes } }
void constructPrefab(const AABB& aabb, const std::string& shader, EPatchPrefab eType, EViewType viewType, std::size_t width, std::size_t height) { GlobalSelectionSystem().setSelectedAll(false); scene::INodePtr node(GlobalPatchCreator(DEF2).createPatch()); GlobalMap().findOrInsertWorldspawn()->addChildNode(node); Patch* patch = Node_getPatch(node); patch->setShader(shader); patch->ConstructPrefab(aabb, eType, viewType, width, height); patch->controlPointsChanged(); Node_setSelected(node, true); }
/** * greebo: Creates a new entity with an attached curve * * @key: The curve type: pass either "curve_CatmullRomSpline" or "curve_Nurbs". */ void createCurve(const std::string& key) { UndoableCommand undo(std::string("createCurve: ") + key); // De-select everything before we proceed GlobalSelectionSystem().setSelectedAll(false); GlobalSelectionSystem().setSelectedAllComponents(false); std::string curveEClass = game::current::getValue<std::string>(GKEY_DEFAULT_CURVE_ENTITY); // Fallback to func_static, if nothing defined in the registry if (curveEClass.empty()) { curveEClass = "func_static"; } // Find the default curve entity IEntityClassPtr entityClass = GlobalEntityClassManager().findOrInsert( curveEClass, true ); // Create a new entity node deriving from this entityclass IEntityNodePtr curve(GlobalEntityCreator().createEntity(entityClass)); // Insert this new node into the scenegraph root GlobalSceneGraph().root()->addChildNode(curve); // Select this new curve node Node_setSelected(curve, true); // Set the model key to be the same as the name curve->getEntity().setKeyValue("model", curve->getEntity().getKeyValue("name")); // Initialise the curve using three pre-defined points curve->getEntity().setKeyValue( key, "3 ( 0 0 0 50 50 0 50 100 0 )" ); ITransformablePtr transformable = Node_getTransformable(curve); if (transformable != NULL) { // Translate the entity to the center of the current workzone transformable->setTranslation(GlobalXYWnd().getActiveXY()->getOrigin()); transformable->freezeTransform(); } }
BrushByPlaneClipper::~BrushByPlaneClipper() { for (std::set<scene::INodePtr>::iterator i = _deleteList.begin(); i != _deleteList.end(); i++) { // Remove the node from the scene scene::removeNodeFromParent(*i); } for (InsertMap::iterator i = _insertList.begin(); i != _insertList.end(); i++) { // Insert the child into the designated parent scene::addNodeToContainer(i->first, i->second); // Select the child Node_setSelected(i->first, true); } }
bool EntitySelectByClassnameWalker::pre(const scene::INodePtr& node) { // don't traverse invisible nodes if (!node->visible()) return false; Entity* entity = Node_getEntity(node); if (entity != NULL) { if (entityMatches(entity)) { // Got a matching entity Node_setSelected(node, true); } // Don't traverse entities return false; } // Not an entity, traverse return true; }
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); } } }
void createCaps(Patch& patch, const scene::INodePtr& parent, EPatchCap type, const std::string& shader) { if ((type == eCapEndCap || type == eCapIEndCap) && patch.getWidth() != 5) { rError() << "cannot create end-cap - patch width != 5" << std::endl; gtkutil::MessageBox::ShowError(_("Cannot create end-cap, patch must have a width of 5."), GlobalMainFrame().getTopLevelWindow()); return; } if ((type == eCapBevel || type == eCapIBevel) && patch.getWidth() != 3) { gtkutil::MessageBox::ShowError(_("Cannot create bevel-cap, patch must have a width of 3."), GlobalMainFrame().getTopLevelWindow()); rError() << "cannot create bevel-cap - patch width != 3" << std::endl; return; } if (type == eCapCylinder && patch.getWidth() != 9) { gtkutil::MessageBox::ShowError(_("Cannot create cylinder-cap, patch must have a width of 9."), GlobalMainFrame().getTopLevelWindow()); rError() << "cannot create cylinder-cap - patch width != 9" << std::endl; return; } assert(parent != NULL); { scene::INodePtr cap(GlobalPatchCreator(DEF2).createPatch()); parent->addChildNode(cap); Patch* capPatch = Node_getPatch(cap); assert(capPatch != NULL); patch.MakeCap(capPatch, type, ROW, true); capPatch->setShader(shader); // greebo: Avoid creating "degenerate" patches (all vertices merged in one 3D point) if (!capPatch->isDegenerate()) { Node_setSelected(cap, true); } else { parent->removeChildNode(cap); rWarning() << "Prevented insertion of degenerate patch." << std::endl; } } { scene::INodePtr cap(GlobalPatchCreator(DEF2).createPatch()); parent->addChildNode(cap); Patch* capPatch = Node_getPatch(cap); assert(capPatch != NULL); patch.MakeCap(capPatch, type, ROW, false); capPatch->setShader(shader); // greebo: Avoid creating "degenerate" patches (all vertices merged in one 3D point) if (!capPatch->isDegenerate()) { Node_setSelected(cap, true); } else { parent->removeChildNode(cap); rWarning() << "Prevented insertion of degenerate patch." << std::endl; } } }
// 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 thicken(const PatchNodePtr& sourcePatch, float thickness, bool createSeams, int axis) { // Get a shortcut to the patchcreator PatchCreator& patchCreator = GlobalPatchCreator(DEF2); // Create a new patch node scene::INodePtr node(patchCreator.createPatch()); scene::INodePtr parent = sourcePatch->getParent(); assert(parent != NULL); // Insert the node into the same parent as the existing patch parent->addChildNode(node); // Retrieve the contained patch from the node Patch* targetPatch = Node_getPatch(node); // Create the opposite patch with the given thickness = distance targetPatch->createThickenedOpposite(sourcePatch->getPatchInternal(), thickness, axis); // Select the newly created patch Node_setSelected(node, true); if (createSeams && thickness > 0.0f) { // Allocate four new patches scene::INodePtr nodes[4] = { patchCreator.createPatch(), patchCreator.createPatch(), patchCreator.createPatch(), patchCreator.createPatch() }; // Now create the four walls for (int i = 0; i < 4; i++) { // Insert each node into the same parent as the existing patch // It's vital to do this first, otherwise these patches won't have valid shaders parent->addChildNode(nodes[i]); // Retrieve the contained patch from the node Patch* wallPatch = Node_getPatch(nodes[i]); // Create the wall patch by passing i as wallIndex wallPatch->createThickenedWall(sourcePatch->getPatchInternal(), *targetPatch, i); if (!wallPatch->isDegenerate()) { // Now select the newly created patch Node_setSelected(nodes[i], true); } else { rMessage() << "Thicken: Discarding degenerate patch." << std::endl; // Remove again parent->removeChildNode(nodes[i]); } } } // Invert the target patch so that it faces the opposite direction targetPatch->InvertMatrix(); }
/** greebo: Tries to select the given node. */ void selectNode(scene::INodePtr node) { Node_setSelected(node, true); }
/** * Create an instance of the given entity at the given position, and return * the Node containing the new entity. * * @returns: the scene::INodePtr referring to the new entity. */ scene::INodePtr createEntityFromSelection(const std::string& name, const Vector3& origin) { // Obtain the structure containing the selection counts const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); IEntityClassPtr entityClass = GlobalEntityClassManager().findOrInsert(name, true); // TODO: to be replaced by inheritance-based class detection bool isModel = (info.totalCount == 0 && name == "func_static"); // Some entities are based on the size of the currently-selected primitive(s) bool primitivesSelected = info.brushCount > 0 || info.patchCount > 0; if (!(entityClass->isFixedSize() || isModel) && !primitivesSelected) { throw EntityCreationException( (boost::format(_("Unable to create entity %s, no brushes selected.")) % name).str() ); } // Get the selection workzone bounds AABB workzone = GlobalSelectionSystem().getWorkZone().bounds; // Create the new node for the entity IEntityNodePtr node(GlobalEntityCreator().createEntity(entityClass)); GlobalSceneGraph().root()->addChildNode(node); // The layer list we're moving the newly created node/subgraph into scene::LayerList targetLayers; if (entityClass->isFixedSize() || (isModel && !primitivesSelected)) { selection::algorithm::deleteSelection(); ITransformablePtr transform = Node_getTransformable(node); if (transform != 0) { transform->setType(TRANSFORM_PRIMITIVE); transform->setTranslation(origin); transform->freezeTransform(); } GlobalSelectionSystem().setSelectedAll(false); // Move the item to the active layer targetLayers.insert(GlobalLayerSystem().getActiveLayer()); Node_setSelected(node, true); } else // brush-based entity { // Add selected brushes as children of non-fixed entity node->getEntity().setKeyValue("model", node->getEntity().getKeyValue("name")); // Take the selection center as new origin Vector3 newOrigin = selection::algorithm::getCurrentSelectionCenter(); node->getEntity().setKeyValue("origin", string::to_string(newOrigin)); // If there is an "editor_material" class attribute, apply this shader // to all of the selected primitives before parenting them std::string material = node->getEntity().getEntityClass()->getAttribute("editor_material").getValue(); if (!material.empty()) { selection::algorithm::applyShaderToSelection(material); } // If we had primitives to reparent, the new entity should inherit the layer info from them if (primitivesSelected) { scene::INodePtr primitive = GlobalSelectionSystem().ultimateSelected(); targetLayers = primitive->getLayers(); } else { // Otherwise move the item to the active layer targetLayers.insert(GlobalLayerSystem().getActiveLayer()); } // Parent the selected primitives to the new node selection::algorithm::ParentPrimitivesToEntityWalker walker(node); GlobalSelectionSystem().foreachSelected(walker); walker.reparent(); // De-select the children and select the newly created parent entity GlobalSelectionSystem().setSelectedAll(false); Node_setSelected(node, true); } // Assign the layers - including all child nodes (#2864) scene::AssignNodeToLayersWalker layerWalker(targetLayers); Node_traverseSubgraph(node, layerWalker); // Set the light radius and origin if (entityClass->isLight() && primitivesSelected) { AABB bounds(Doom3Light_getBounds(workzone)); node->getEntity().setKeyValue("origin", string::to_string(bounds.getOrigin())); node->getEntity().setKeyValue("light_radius", string::to_string(bounds.getExtents())); } // Flag the map as unsaved after creating the entity GlobalMap().setModified(true); // Check for auto-setting key values. TODO: use forEachClassAttribute // directly here. eclass::AttributeList list = eclass::getSpawnargsWithPrefix( *entityClass, "editor_setKeyValue" ); if (!list.empty()) { for (eclass::AttributeList::const_iterator i = list.begin(); i != list.end(); ++i) { // Cut off the "editor_setKeyValueN " string from the key to get the spawnarg name std::string key = i->getName().substr(i->getName().find_first_of(' ') + 1); node->getEntity().setKeyValue(key, i->getValue()); } } // Return the new node return node; }
void createDecals() { for (FaceInstanceList::iterator i = _faceInstances.begin(); i != _faceInstances.end(); ++i) { // Get the winding const Winding& winding = (*i)->getFace().getWinding(); // Create a new decal patch scene::INodePtr patchNode = GlobalPatchCreator(DEF3).createPatch(); if (patchNode == NULL) { gtkutil::errorDialog(_("Could not create patch."), GlobalMainFrame().getTopLevelWindow()); return; } Patch* patch = Node_getPatch(patchNode); assert(patch != NULL); // must not fail // Set the tesselation of that 3x3 patch patch->setDims(3,3); patch->setFixedSubdivisions(true, Subdivisions(1,1)); // Set the coordinates patch->ctrlAt(0,0).vertex = winding[0].vertex; patch->ctrlAt(2,0).vertex = winding[1].vertex; patch->ctrlAt(1,0).vertex = (patch->ctrlAt(0,0).vertex + patch->ctrlAt(2,0).vertex)/2; patch->ctrlAt(0,1).vertex = (winding[0].vertex + winding[3].vertex)/2; patch->ctrlAt(2,1).vertex = (winding[1].vertex + winding[2].vertex)/2; patch->ctrlAt(1,1).vertex = (patch->ctrlAt(0,1).vertex + patch->ctrlAt(2,1).vertex)/2; patch->ctrlAt(2,2).vertex = winding[2].vertex; patch->ctrlAt(0,2).vertex = winding[3].vertex; patch->ctrlAt(1,2).vertex = (patch->ctrlAt(2,2).vertex + patch->ctrlAt(0,2).vertex)/2; // Use the texture in the clipboard, if it's a decal texture Texturable& clipboard = GlobalShaderClipboard().getSource(); if (!clipboard.empty()) { if (clipboard.getShader().find("decals") != std::string::npos) { patch->setShader(clipboard.getShader()); } } // Fit the texture on it patch->SetTextureRepeat(1,1); patch->FlipTexture(1); // Insert the patch into worldspawn scene::INodePtr worldSpawnNode = GlobalMap().getWorldspawn(); assert(worldSpawnNode != NULL); // This must be non-NULL, otherwise we won't have faces worldSpawnNode->addChildNode(patchNode); // Deselect the face instance (*i)->setSelected(SelectionSystem::eFace, false); // Select the patch Node_setSelected(patchNode, true); } }