Generic Geometry Selection
From K-3D
Introduction
The switch to Generic Geometric Primitives requires that we address a set of parallel issues in mesh selections, particularly: how does the system generate and manipulate mesh selections in a world where new geometric primitives can be created at runtime? Relative to the legacy mesh primitives, there are several new requirements:
- The k3d::selection::type enumeration currently contains a hard-coded list of identifiers for each primitive type (point group, linear curve, cubic curve, bilinear patch, polyhedron, etc). The list will have to be converted into a set of generic identifiers that can be used to implement meaningful selection behavior at a UI level, but without being specific to any one primitive type (so they can be used by new primitives without code changes).
- The k3d::mesh_selection data structure will have to be expanded.
- Currently, it contains a hard-coded set of k3d::mesh_selection::record structures for each primitive type. This list will have to become a dynamic collection.
- In addition to supporting new primitive types, generic mesh primitives allow a mesh to contain multiple instances of a particular primitive type - so k3d::mesh_selection will have to handle this.
- A fundamental selection-related operation is storing / merging k3d::mesh_selection objects with k3d::mesh objects. Mesh selection arrays are normally k3d::typed_array<k3d::double_t>, so there will be a need to "tag" selection arrays, presumably using the array metadata system.
- The user-interface layer contains a lot of hard-coded primitive-specific logic to handle selection conversions. It seems as-if many of these conversions probably can't be automated, since automation would require some higher-level understanding of how the arrays in a primitive relate to each other. However, we can at-least repackage these conversions as application-level plugins.
Selection Operations
The complete, end-to-end selection process is based on a set of logical "building block" data structures and operations, as follows:
- SDK
- k3d::selection::record - stores the ID and depth of an entity (a geometric primitive fragment, tool, widget, etc). Status: unchanged by generic primitives.
- k3d::selection::records - stores a collection of selection records. Status: unchanged by generic primitives.
- k3d::gl::selection_state - filters the types of selection records that should be returned for later processing. Status: updated to match generic primitive selection types.
- k3d::mesh_selection - stores a change in the selection state of a mesh. Status: stores generic primitive selection data.
- k3d::mesh_selection::store() - copies the current selection state of a mesh into a mesh selection object. Status: not implemented for generic primitives, may not be needed at all.
- k3d::mesh_selection::merge() - merges a mesh selection with the current selection state of a mesh. Status: merges generic primitive selections.
- NGUI
- viewport::control::get_selection() - returns a set of k3d::selection::records that intersect a region in screen space and match the generic criteria provided by a k3d::gl::selection_state object. Status: unchanged by generic primitives.
- viewport::control::get_XXX_selectables() - returns a set of k3d::selection::records that intersect a region in screen space and match criteria for specific classes of selection (nodes, points, uniform, etc). Status: updated to match generic primitive selection types.
- viewport::control::pick_XXX() - given a set of selection records, chooses a specific class of entity that is closest to a set of screen coordinates. Status: These operations require implicit conversions between different selection classes, e.g: when picking points, it is not sufficient to simply render point records, then run a distance metric. We have to render faces, and if a face record is returned, iterate over the points in the face to find the "best" point. Status: selection conversions between generic primitives aren't implemented, and require either a new plugin type, or additional metadata in the generic primitive data structures.
- Selection Conversion - when a user changes selection "mode", any current component selections are converted - i.e. when converting from a point selection to a uniform selection, any uniform components adjacent to a selected point should become selected. When converting from a uniform selection to a point selection, any points adjacent to a selected uniform component should become selected, and-so-on. This requires either a new plugin type, or additional metadata in the generic primitive data structures.
Component Types
Probably the most important task in generic mesh selection will be defining an ontology of mesh component types that will be intuitive for users and generic enough to work with arbitrary primitive types. Here is one proposal for a hierarchy of selection types:
- Node - Selecting a node.
- Mesh - New If MeshInstance was a sink for multiple meshes (it currently isn't), this would allow you to select one specific mesh from the bunch.
- Vertex - Selecting points from a mesh.
- Primitive - New Selecting a specific primitive from a mesh containing multiple primitives.
- Constant - New Selecting individual groups from a primitive containing multiple groups (such as linear curve groups, cubic curve groups, NURBS curve groups, etc).
- Uniform - Selecting individual faces, individual patches, individual curves, individual quadrics. Note that this lumps faces, patches, and curves together whereas today they are in separate "face" and "line" categories in the UI.
- Varying - New Selecting individual control points in a patch, curve. Note that selecting a control point isn't the same as selecting a vertex.
- FaceVarying - New Selecting per-face control points in a polyhedron. Note that selecting a control point isn't the same as selecting a vertex.
- Edge - Selecting individual split-edges in a polyhedron face.
- Parameter - We should support ways to select parametric curves within patches. We probably need to make a distinction between selecting "real" curves that are defined by real control-points, and "virtual" curves that are defined at an arbitrary parameter value.
- Mesh - New If MeshInstance was a sink for multiple meshes (it currently isn't), this would allow you to select one specific mesh from the bunch.
The names for these selection types are meaningful within the context of geometric primitives, but need to be mapped into something semantically useful before they can appear in the UI layer. Some rough suggestions for a revised set of human-visible selection types:
- Node - "Select Objects"
- Example: Users click on a node to select it. Clicking on geometry selects the MeshInstance that rendered it. This is the current behavior and wouldn't change.
- Vertex - "Select Points"
- Example: Users click on a mesh point to select it (the point). This is the current behavior and wouldn't change.
- Primitive - "Select ___?"
- Example: Users can click on individual primitives within a mesh, i.e. they could select one polyhedron from a mesh that contained more-than-one. This would be a new selection capability.
- Constant - "Select Groups?"
- Example: Some primitives "group" their components together. Examples include linear curves, cubic curves, and NURBS curves. This would allow the user to select one group out of many, and would be a new selection capability.
- Uniform - "Select Faces / Patches / Curves / Quadrics?"
- Example: Users can select individual faces, patches, curves, spheres, tori, etc. This is comparable to current behavior, but it treats faces/patches and curves as equals, instead of current behavior that distinguishes between "faces" and "lines".
- Edge - "Select Edges"
- Example: Users can select individual polyhedron split-edges. This is comparable to current behavior, except that it treats edges as distinct from curves, instead of current behavior that groups edges and curves together as "lines".
- FaceVarying - "Select Facevarying?"
- Example: Users can select individual polygon vertices. This allows attributes to be manipulated on individual polygons without affecting adjacent polygons. This would be a new selection capability.