The SoROI and SoROIManip nodes only support an axis-aligned region of interest (cropping box). This is intentional because axis-aligned ROI is the most common use case and can be highly optimized.
It can also be useful to specify a cropping box that is not axis-aligned. This can be done using the SoVolumeClippingGroup node, which clips volume rendering against any closed shape defined by polygonal geometry. The geometry clipping feature can do much more complicated things, but in this case we just need a "box" to clip against. One way is to use an SoCube node for the clipping geometry and an SoTransform node to specify the size, position and rotation of the ROI.
The geometry inside the clipping group is not actually rendered (because it would generally hide the volume data we are trying to show to the user). So we may want to also create some "feedback" geometry outside the clipping group to show the user where the ROI is currently located. We can use SoCube again plus an SoDrawStyle node to render only the edges of the box. The feedback geometry will need its own SoTransform node, which could be a second instance of the clipping group transform node or have its fields connected to the clipping group transform node.
We may also want to allow the user to directly manipulate the rotated ROI. For an axis-aligned ROI, the SoTabBoxDragger is a good solution, but this dragger (by design) does not provide rotation. A simple solution is to use an SoTransformerDragger. This dragger provides scale, rotate and translate manipulation with nice feedback. Using the Shift key, it also provides "constrained" manipulation which is very convenient. Constrained is the default for rotation (clicking a 'green ball'), but not for scale and translate. Using shift-click for scale and translation is the most convenient way to use the dragger in this application. If you have the time, you might be able to create a better solution by creating a new composite dragger that combines multiple simple draggers.
The main issue with using any dragger with volume clipping is that the dragger geometry must _not_ be a child of the clipping group. If it is, then VolumeViz will also try to clip against the dragger geometry and the result will not be pretty. Put the dragger in a separate group and connect the draggers fields to the transform node in the clipping group.
Notes:
This technique works with volume rendering (SoVolumeRender), but does not work well with volume skin rendering (SoVolumeSkin) because the volume skin node can only render axis-aligned faces. If it's actually necessary to render just the faces of the ROI, use a box defined by an SoVolumeIndexedFaceSet node.
This basic technique provides cropping against a box like the SoROI SUB_VOLUME mode. It would be possible to emulate other useful SoROI modes, like EXCLUSION_BOX, by creating more complex geometry, but we won't show that here.
The SoROI and SoROIManip nodes conveniently allow the region of interest to be specified in "ijk" voxel coordinates. Volume clipping geometry must be specified in "xyz" 3D world coordinates. The SoVolumeData node provides voxelToXYZ() and XYZToVoxel() utility functions to help with this conversion. (Note they require converting the voxel coordinate from integer to float values first.)
The SoROIManip class has a convenient "constrained" option that automatically keeps the ROI box inside the dimensions of the volume. The same thing is possible using this technique, but requires a little more programming. Create a valueChanged callback for the dragger, check the dragger's new field values and adjust them to stay inside the volume.
SoVolumeData, SoROIManip, SoROI, SoVolumeClippingGroup