Region-selection of objects

Hi everyone,

It often happens that the need arises to select multiple objects at once, usually by drawing a rectangle around them. This can be useful for selecting units in a strategy game for example, but also for manipulating models in an editor-type application.

Although several attempts have already been made to implement this so-called “box-selection” in Panda3D, I’d like to present my own solution as I believe it is more precise and offers more options than those other methods.

My implementation of “region-selection”, as I prefer to call it, does make use of shaders which require support of GLSL version 4.20.

The code can be found in this Gist.
There you should find the main script, main.py, which imports the RegionSelector class from the other Python module to be found there as well, called region_selection.py.

The reason I call it “region”-selection is because the shape used for the selection is not limited to a rectangle (“box”) here; you have the choice between these predefined shapes:

  • rectangle;
  • square;
  • ellipse;
  • circle.

Each of the above comes in two flavours:
one which is drawn from one corner to a diametrically opposed one, and another which is drawn from the centre outwards.

Then there are also three “freehand”-style shapes:

  • lasso;
  • fence (point-to-point);
  • “paint” (using resizable circle as “brush”).

The code uses default keys for specific actions (each of which has an event-ID, noted below between parentheses):

  • start drawing the selection shape (set_first_region_point): mouse1;
  • finalize selection (or set next point of shape in case of fence-selection) (set_next_region_point): mouse1-up;
  • cancel selection (cancel_region_select): escape, mouse3;
  • remove last point of fence-shape (undo_fence_point): backspace;
  • finalize fence-selection (finalize_fence): enter;
  • increase “paint brush” size (incr_brush_size): wheel_up-up, +, +-repeat;
  • decrease “paint brush” size (decr_brush_size): wheel_down-up, -, --repeat.

But all of these key-bindings can be redefined at any time.
The provided main script contains an example that uses keyboard keys to draw a shape instead of mouse buttons when the latter are used by the default camera-controller.
Multiple keys can be associated with one and the same action if so desired.

One final option you have is to restrict selection to only those objects that fall completely within the drawn shape. Just set RegionSelector.enclose to True or False to enable or disable this feature.

Despite the complexity of this system, using it is very straightforward:

  • instantiate RegionSelector;
  • define RegionSelector.deselect_func and RegionSelector.select_func to process the selectable objects;
  • add each selectable object to the above instance by calling RegionSelector.add(obj);
    (it is possible to remove added objects by calling RegionSelector.remove(obj) or RegionSelector.clear());
  • use the default or customized controls to perform the selection.

Note that the selection system merely determines which objects are within the selection shape and passes these to RegionSelector.deselect_func and RegionSelector.select_func, as defined by the application-code; the actual “selecting” is entirely the responsibility of those functions.

It can happen that objects in a custom DisplayRegion need to be selected. My system should be able to handle this situation, as long as the appropriate objects are passed into the RegionSelector constructor for the following parameters:

  • display_region: the target DisplayRegion;
  • node2d: the pixel-aligned 2D root-node to attach the selection-shape to (default base.pixel2d);
  • cam2d: the camera that renders the selection-shape (default base.cam2d);
  • cam3d: the camera that renders the selectable objects in the 3D-scene (default base.cam);
  • mouse_watcher: the MouseWatcher bound to the target DisplayRegion (default base.mouseWatcherNode).

You should see a working example of this if you set self.use_secondary_display_region in main.py to True.

Have fun with this! And I hope it will prove useful. :slight_smile:

6 Likes

That’s pretty cool! It seems like a neat and potentially-useful tool indeed! :slight_smile:

1 Like