/** * 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; }
static bool Entity_create (const std::string& name, const Vector3& origin) { EntityClass* entityClass = GlobalEntityClassManager().findOrInsert(name, true); bool revert = false; const bool isModel = (name == "misc_model"); const bool isSound = (name == "misc_sound"); const bool isParticle = (name == "misc_particle"); const bool isStartingPositionActor = (name == "info_player_start") || (name == "info_human_start") || (name == "info_alien_start") || (name == "info_civilian_start"); const bool isStartingPositionUGV = (name == "info_2x2_start"); const bool isStartingPosition = isStartingPositionActor || isStartingPositionUGV; const bool brushesSelected = map::countSelectedBrushes() != 0; if (!(entityClass->fixedsize || isModel) && !brushesSelected) { throw EntityCreationException(std::string("Unable to create entity \"") + name + "\", no brushes selected"); } UndoableCommand undo("entityCreate -class " + name); AABB workzone(AABB::createFromMinMax(GlobalSelectionSystem().getWorkZone().min, GlobalSelectionSystem().getWorkZone().max)); NodeSmartReference node(GlobalEntityCreator().createEntity(entityClass)); Node_getTraversable(GlobalSceneGraph().root())->insert(node); scene::Path entitypath(makeReference(GlobalSceneGraph().root())); entitypath.push(makeReference(node.get())); scene::Instance& instance = findInstance(entitypath); // set all mandatory fields to default values // some will be overwritten later Entity *entity = Node_getEntity(node); entity->addMandatoryKeyValues(); if (entityClass->fixedsize || (isModel && !brushesSelected)) { selection::algorithm::deleteSelection(); Transformable* transform = Instance_getTransformable(instance); if (transform != 0) { transform->setType(TRANSFORM_PRIMITIVE); if (isStartingPosition) { const int sizeX = (int) ((entityClass->maxs[0] - entityClass->mins[0]) / 2.0); const int sizeY = (int) ((entityClass->maxs[1] - entityClass->mins[1]) / 2.0); const int sizeZ = (int) ((entityClass->maxs[2] - entityClass->mins[2]) / 2.0); const int x = ((int) origin.x() / UNIT_SIZE) * UNIT_SIZE - sizeX; const int y = ((int) origin.y() / UNIT_SIZE) * UNIT_SIZE - sizeY; const int z = ((int) origin.z() / UNIT_HEIGHT) * UNIT_HEIGHT + sizeZ; const Vector3 vec(x, y, z); transform->setTranslation(vec); } else { transform->setTranslation(origin); } transform->freezeTransform(); } GlobalSelectionSystem().setSelectedAll(false); Instance_setSelected(instance, true); } else { // Add selected brushes as children of non-fixed entity Scene_parentSelectedBrushesToEntity(GlobalSceneGraph(), node); Scene_forEachChildSelectable(SelectableSetSelected(true), instance.path()); } // TODO tweaking: when right click dropping a light entity, ask for light value in a custom dialog box if (name == "light" || name == "light_spot") { ui::LightDialog dialog; dialog.show(); if (!dialog.isAborted()) { std::string intensity = dialog.getIntensity(); Vector3 color = dialog.getColor(); entity->setKeyValue("light", intensity); entity->setKeyValue("_color", color.toString()); } else revert = true; } if (isModel) { ui::ModelAndSkin modelAndSkin = ui::ModelSelector::chooseModel(); if (!modelAndSkin.model.empty()) { entity->setKeyValue("model", modelAndSkin.model); if (modelAndSkin.skin != -1) entity->setKeyValue("skin", string::toString(modelAndSkin.skin)); } else revert = true; } else if (isSound) { // Display the Sound Chooser to get a sound from the user ui::SoundChooser sChooser; std::string sound = sChooser.chooseSound(); if (!sound.empty()) entity->setKeyValue("noise", sound); else revert = true; } else if (isParticle) { ui::ParticleSelector pSelector; std::string particle = pSelector.chooseParticle(); if (!particle.empty()) entity->setKeyValue("particle", particle); else revert = true; } return revert; }