mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 18:21:16 +02:00
Merge pull request #4442 from Atlante45/edit_entities_box_selection
editEntities - select all in/touching box
This commit is contained in:
commit
fa27b76183
5 changed files with 110 additions and 7 deletions
|
@ -766,6 +766,10 @@ function setupModelMenus() {
|
||||||
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||||
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A",
|
||||||
|
afterItem: "Allow Selecting of Lights" });
|
||||||
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T",
|
||||||
|
afterItem: "Select All Entities In Box" });
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||||
|
@ -795,6 +799,8 @@ function cleanupModelMenus() {
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||||
|
Menu.removeMenuItem("Edit", "Select All Entities In Box");
|
||||||
|
Menu.removeMenuItem("Edit", "Select All Entities Touching Box");
|
||||||
|
|
||||||
Menu.removeSeparator("File", "Models");
|
Menu.removeSeparator("File", "Models");
|
||||||
Menu.removeMenuItem("File", "Export Entities");
|
Menu.removeMenuItem("File", "Export Entities");
|
||||||
|
@ -830,6 +836,45 @@ Script.update.connect(function (deltaTime) {
|
||||||
selectionDisplay.checkMove();
|
selectionDisplay.checkMove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function insideBox(center, dimensions, point) {
|
||||||
|
return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0))
|
||||||
|
&& (Math.abs(point.y - center.y) <= (dimensions.y / 2.0))
|
||||||
|
&& (Math.abs(point.z - center.z) <= (dimensions.z / 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) {
|
||||||
|
if (selectionManager.hasSelection()) {
|
||||||
|
// Get all entities touching the bounding box of the current selection
|
||||||
|
var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition,
|
||||||
|
Vec3.multiply(selectionManager.worldDimensions, 0.5));
|
||||||
|
var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions);
|
||||||
|
|
||||||
|
if (!keepIfTouching) {
|
||||||
|
var isValid;
|
||||||
|
if (selectionManager.localPosition === null) {
|
||||||
|
isValid = function(position) {
|
||||||
|
return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isValid = function(position) {
|
||||||
|
var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation),
|
||||||
|
Vec3.subtract(position,
|
||||||
|
selectionManager.localPosition));
|
||||||
|
return insideBox({ x: 0, y: 0, z: 0 }, selectionManager.localDimensions, localPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < entities.length; ++i) {
|
||||||
|
var properties = Entities.getEntityProperties(entities[i]);
|
||||||
|
if (!isValid(properties.position)) {
|
||||||
|
entities.splice(i, 1);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectionManager.setSelections(entities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function deleteSelectedEntities() {
|
function deleteSelectedEntities() {
|
||||||
if (SelectionManager.hasSelection()) {
|
if (SelectionManager.hasSelection()) {
|
||||||
print(" Delete Entities");
|
print(" Delete Entities");
|
||||||
|
@ -888,6 +933,10 @@ function handeMenuEvent(menuItem) {
|
||||||
}
|
}
|
||||||
} else if (menuItem == "Entity List...") {
|
} else if (menuItem == "Entity List...") {
|
||||||
entityListTool.toggleVisible();
|
entityListTool.toggleVisible();
|
||||||
|
} else if (menuItem == "Select All Entities In Box") {
|
||||||
|
selectAllEtitiesInCurrentSelectionBox(false);
|
||||||
|
} else if (menuItem == "Select All Entities Touching Box") {
|
||||||
|
selectAllEtitiesInCurrentSelectionBox(true);
|
||||||
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
|
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
|
||||||
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,8 +204,7 @@ EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center
|
||||||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||||
_entityTree->unlock();
|
_entityTree->unlock();
|
||||||
if (closestEntity) {
|
if (closestEntity) {
|
||||||
result.id = closestEntity->getID();
|
result = closestEntity->getEntityItemID();
|
||||||
result.isKnownID = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -227,10 +226,25 @@ QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& ce
|
||||||
QVector<const EntityItem*> entities;
|
QVector<const EntityItem*> entities;
|
||||||
_entityTree->findEntities(center, radius, entities);
|
_entityTree->findEntities(center, radius, entities);
|
||||||
_entityTree->unlock();
|
_entityTree->unlock();
|
||||||
|
|
||||||
foreach (const EntityItem* entity, entities) {
|
foreach (const EntityItem* entity, entities) {
|
||||||
EntityItemID thisEntityItemID(entity->getID(), UNKNOWN_ENTITY_TOKEN, true);
|
result << entity->getEntityItemID();
|
||||||
result << thisEntityItemID;
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<EntityItemID> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
|
||||||
|
QVector<EntityItemID> result;
|
||||||
|
if (_entityTree) {
|
||||||
|
_entityTree->lockForRead();
|
||||||
|
AABox box(corner, dimensions);
|
||||||
|
QVector<EntityItem*> entities;
|
||||||
|
_entityTree->findEntities(box, entities);
|
||||||
|
_entityTree->unlock();
|
||||||
|
|
||||||
|
foreach (const EntityItem* entity, entities) {
|
||||||
|
result << entity->getEntityItemID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -87,10 +87,14 @@ public slots:
|
||||||
/// will return a EntityItemID.isKnownID = false if no models are in the radius
|
/// will return a EntityItemID.isKnownID = false if no models are in the radius
|
||||||
/// this function will not find any models in script engine contexts which don't have access to models
|
/// this function will not find any models in script engine contexts which don't have access to models
|
||||||
Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const;
|
Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const;
|
||||||
|
|
||||||
/// finds models within the search sphere specified by the center point and radius
|
/// finds models within the search sphere specified by the center point and radius
|
||||||
/// this function will not find any models in script engine contexts which don't have access to models
|
/// this function will not find any models in script engine contexts which don't have access to models
|
||||||
Q_INVOKABLE QVector<EntityItemID> findEntities(const glm::vec3& center, float radius) const;
|
Q_INVOKABLE QVector<EntityItemID> findEntities(const glm::vec3& center, float radius) const;
|
||||||
|
|
||||||
|
/// finds models within the search sphere specified by the center point and radius
|
||||||
|
/// this function will not find any models in script engine contexts which don't have access to models
|
||||||
|
Q_INVOKABLE QVector<EntityItemID> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
|
||||||
|
|
||||||
/// If the scripting context has visible entities, this will determine a ray intersection, the results
|
/// If the scripting context has visible entities, this will determine a ray intersection, the results
|
||||||
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
|
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
|
||||||
|
|
|
@ -528,6 +528,35 @@ void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*>& foundEnt
|
||||||
foundEntities.swap(args._foundEntities);
|
foundEntities.swap(args._foundEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FindEntitiesInBoxArgs {
|
||||||
|
public:
|
||||||
|
FindEntitiesInBoxArgs(const AABox& box)
|
||||||
|
: _box(box), _foundEntities() {
|
||||||
|
}
|
||||||
|
|
||||||
|
AABox _box;
|
||||||
|
QVector<EntityItem*> _foundEntities;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool EntityTree::findInBoxOperation(OctreeElement* element, void* extraData) {
|
||||||
|
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
|
||||||
|
if (element->getAACube().touches(args->_box)) {
|
||||||
|
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||||
|
entityTreeElement->getEntities(args->_box, args->_foundEntities);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: assumes caller has handled locking
|
||||||
|
void EntityTree::findEntities(const AABox& box, QVector<EntityItem*>& foundEntities) {
|
||||||
|
FindEntitiesInBoxArgs args(box);
|
||||||
|
// NOTE: This should use recursion, since this is a spatial operation
|
||||||
|
recurseTreeWithOperation(findInBoxOperation, &args);
|
||||||
|
// swap the two lists of entity pointers instead of copy
|
||||||
|
foundEntities.swap(args._foundEntities);
|
||||||
|
}
|
||||||
|
|
||||||
EntityItem* EntityTree::findEntityByID(const QUuid& id) {
|
EntityItem* EntityTree::findEntityByID(const QUuid& id) {
|
||||||
EntityItemID entityID(id);
|
EntityItemID entityID(id);
|
||||||
return findEntityByEntityItemID(entityID);
|
return findEntityByEntityItemID(entityID);
|
||||||
|
|
|
@ -111,12 +111,18 @@ public:
|
||||||
/// \param foundEntities[out] vector of const EntityItem*
|
/// \param foundEntities[out] vector of const EntityItem*
|
||||||
/// \remark Side effect: any initial contents in foundEntities will be lost
|
/// \remark Side effect: any initial contents in foundEntities will be lost
|
||||||
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
|
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
|
||||||
|
|
||||||
/// finds all entities that touch a cube
|
/// finds all entities that touch a cube
|
||||||
/// \param cube the query cube in world-frame (meters)
|
/// \param cube the query cube in world-frame (meters)
|
||||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||||
/// \remark Side effect: any initial contents in entities will be lost
|
/// \remark Side effect: any initial contents in entities will be lost
|
||||||
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
|
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
|
||||||
|
|
||||||
|
/// finds all entities that touch a box
|
||||||
|
/// \param box the query box in world-frame (meters)
|
||||||
|
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||||
|
/// \remark Side effect: any initial contents in entities will be lost
|
||||||
|
void findEntities(const AABox& box, QVector<EntityItem*>& foundEntities);
|
||||||
|
|
||||||
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||||
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||||
|
@ -173,6 +179,7 @@ private:
|
||||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||||
|
static bool findInBoxOperation(OctreeElement* element, void* extraData);
|
||||||
static bool sendEntitiesOperation(OctreeElement* element, void* extraData);
|
static bool sendEntitiesOperation(OctreeElement* element, void* extraData);
|
||||||
|
|
||||||
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
||||||
|
|
Loading…
Reference in a new issue