LayeredActors
Repository source: LayeredActors
Description¶
Demonstrates the use of two renderers in a render window. Notice that the second (and subsequent) renderers will have a transparent background.
The first layer (layer 0) contains the base object, a slab in this case. The second layer (layer 1) contains an object (axes in this case). This axes object will always be in front of the base layer object. When the program runs, the top-most layer will be the active layer, layer 1 in this case.
Two callbacks are provided, the first callback selects which layer is active:
- Pressing 0 on the keyboard will let you manipulate the objects in layer 0.
- Pressing 1 on the keyboard will let you manipulate the objects in layer 1.
The second callback allows you to orient objects in all layers using the object in the active layer.
Note
Objects in the top-most layer will always be in front of any objects in other layers.
Info
This is an extension of the TransparentBackground.cxx example, extended by adding an extra callback so that the non-active layer objects move in conjunction with the active layer objects.
Other languages
See (Python), (PythonicAPI)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
LayeredActors.cxx
#include <vtkActor.h>
#include <vtkAxesActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCubeSource.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <vtkTransform.h>
#include <array>
#include <cstdlib>
#include <iostream>
namespace {
/**
* Copy an array to the internal buffer within the vector class.
*
* @param arr - The array.
* @return - The vector corresponding to the array.
*/
template <typename T>
std::vector<T> CopyArrayToVector(T* arr, std::size_t arr_len)
{
return std::vector<T>(arr, arr + arr_len);
}
//! The positional information relating to the camera.
struct Orientation
{
std::vector<double> position{0.0, 0.0, 0.0};
std::vector<double> focalPoint{0.0, 0.0, 0.0};
std::vector<double> viewUp{0.0, 0.0, 0.0};
double distance = 0;
std::vector<double> clipplingRange{0.0, 0.0};
std::vector<double> orientation{0.0, 0.0, 0.0};
};
/**
* Select the layer to manipulate.
*/
void SelectLayer(vtkObject* caller, long unsigned int eventId, void* clientData,
void* callData);
/**
* Orient layer 0 based on the camera orientation in layer 1 or vice versa.
*/
void OrientLayer(vtkObject* caller, long unsigned int eventId, void* clientData,
void* callData);
//! Get the camera orientation.
/*
* @param ren - the renderer.
* @return The orientation parameters.
*/
Orientation GetOrientation(vtkRenderer* ren);
//! Set the camera orientation.
/*
*
* @param ren - the renderer.
* @param p - The orientation parameters.
* @return
*/
void SetOrientation(vtkRenderer* ren, Orientation const& p);
} // namespace
int main(int, char*[])
{
vtkNew<vtkNamedColors> colors;
vtkNew<vtkCubeSource> cubeSource;
cubeSource->SetXLength(4.0);
cubeSource->SetYLength(9.0);
cubeSource->SetZLength(1.0);
cubeSource->SetCenter(0.0, 0.0, 0.0);
// Make the slab and axes actors.
vtkNew<vtkPolyDataMapper> cubeMapper;
cubeMapper->SetInputConnection(cubeSource->GetOutputPort());
vtkNew<vtkProperty> back;
back->SetColor(colors->GetColor3d("Sienna").GetData());
vtkNew<vtkActor> cubeActor;
cubeActor->GetProperty()->SetDiffuseColor(
colors->GetColor3d("BurlyWood").GetData());
cubeActor->SetMapper(cubeMapper);
cubeActor->GetProperty()->EdgeVisibilityOn();
cubeActor->GetProperty()->SetLineWidth(2.0);
cubeActor->GetProperty()->SetEdgeColor(
colors->GetColor3d("PapayaWhip").GetData());
cubeActor->SetBackfaceProperty(back);
vtkNew<vtkTransform> transform;
transform->Translate(0.0, 0.0, 0.0);
vtkNew<vtkAxesActor> axes;
// The axes can be positioned with a user transform.
axes->SetUserTransform(transform);
// The renderers, render window and interactor.
std::array<vtkNew<vtkRenderer>, 2> renderers;
vtkNew<vtkRenderWindow> renWin;
for (auto&& ren : renderers)
{
renWin->AddRenderer(ren);
}
renWin->SetSize(800, 800);
renWin->SetWindowName("LayeredActors");
vtkNew<vtkRenderWindowInteractor> iRen;
iRen->SetRenderWindow(renWin);
vtkNew<vtkInteractorStyleTrackballCamera> style;
iRen->SetInteractorStyle(style);
// Define the renderers and allocate them to layers.
// Layer 0 - background not transparent.
renderers[0]->SetBackground(colors->GetColor3d("DarkSlateGray").GetData());
renderers[0]->AddActor(cubeActor);
renderers[0]->SetLayer(0);
// Layer 1 - the background is transparent,
// so we only see the layer 0 background color
renderers[1]->AddActor(axes);
renderers[1]->SetBackground(colors->GetColor3d("MidnightBlue").GetData());
renderers[1]->SetLayer(1);
// Set a common camera view for each layer.
for (const auto& renderer : renderers)
{
auto camera = renderer->GetActiveCamera();
camera->Elevation(-30.0);
camera->Azimuth(-30.0);
renderer->ResetCamera();
}
// We have two layers.
renWin->SetNumberOfLayers(static_cast<int>(renderers.size()));
renWin->Render();
vtkNew<vtkCallbackCommand> selectLayerCB;
selectLayerCB->SetCallback(SelectLayer);
iRen->AddObserver(vtkCommand::KeyPressEvent, selectLayerCB);
vtkNew<vtkCallbackCommand> orientLayerCB;
orientLayerCB->SetCallback(OrientLayer);
iRen->AddObserver(vtkCommand::EndInteractionEvent, orientLayerCB);
iRen->Start();
return EXIT_SUCCESS;
}
namespace {
void SelectLayer(vtkObject* caller, long unsigned int vtkNotUsed(eventId),
void* vtkNotUsed(clientData), void* vtkNotUsed(callData))
{
vtkRenderWindowInteractor* iRen =
static_cast<vtkRenderWindowInteractor*>(caller);
vtkRendererCollection* renderers = iRen->GetRenderWindow()->GetRenderers();
if (renderers->GetNumberOfItems() < 2)
{
std::cerr << "We need at least two renderers, we have only "
<< renderers->GetNumberOfItems() << std::endl;
return;
}
renderers->InitTraversal();
// Top item.
vtkRenderer* ren0 = renderers->GetNextItem();
// Bottom item.
vtkRenderer* ren1 = renderers->GetNextItem();
std::string key = iRen->GetKeySym();
if (key == "0" || key == "KP_0")
{
std::cout << "Selected layer: " << key << std::endl;
iRen->GetRenderWindow()
->GetInteractor()
->GetInteractorStyle()
->SetDefaultRenderer(ren0);
ren0->InteractiveOn();
ren1->InteractiveOff();
}
if (key == "1" || key == "KP_1")
{
std::cout << "Selected layer: " << key << std::endl;
iRen->GetRenderWindow()
->GetInteractor()
->GetInteractorStyle()
->SetDefaultRenderer(ren1);
ren0->InteractiveOff();
ren1->InteractiveOn();
}
}
void OrientLayer(vtkObject* caller, long unsigned int vtkNotUsed(eventId),
void* vtkNotUsed(clientData), void* vtkNotUsed(callData))
{
vtkRenderWindowInteractor* iRen =
static_cast<vtkRenderWindowInteractor*>(caller);
vtkRendererCollection* renderers = iRen->GetRenderWindow()->GetRenderers();
if (renderers->GetNumberOfItems() < 2)
{
std::cerr << "We need at least two renderers, we have only "
<< renderers->GetNumberOfItems() << std::endl;
return;
}
renderers->InitTraversal();
// Top item.
vtkRenderer* ren0 = renderers->GetNextItem();
// Bottom item.
vtkRenderer* ren1 = renderers->GetNextItem();
if (ren1->GetInteractive())
{
auto orient1 = GetOrientation(ren1);
SetOrientation(ren0, orient1);
ren0->ResetCamera();
}
else
{
auto orient0 = GetOrientation(ren0);
SetOrientation(ren1, orient0);
ren1->ResetCamera();
}
}
Orientation GetOrientation(vtkRenderer* ren)
{
Orientation p;
vtkCamera* camera = ren->GetActiveCamera();
p.position = CopyArrayToVector<double>(camera->GetPosition(), 3);
p.focalPoint = CopyArrayToVector<double>(camera->GetFocalPoint(), 3);
p.viewUp = CopyArrayToVector<double>(camera->GetViewUp(), 3);
p.distance = camera->GetDistance();
p.clipplingRange = CopyArrayToVector<double>(camera->GetClippingRange(), 2);
p.orientation = CopyArrayToVector<double>(camera->GetOrientation(), 3);
return p;
}
void SetOrientation(vtkRenderer* ren, Orientation const& p)
{
vtkCamera* camera = ren->GetActiveCamera();
camera->SetPosition(p.position.data());
camera->SetFocalPoint(p.focalPoint.data());
camera->SetViewUp(p.viewUp.data());
camera->SetDistance(p.distance);
camera->SetClippingRange(p.clipplingRange.data());
}
} // namespace
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(LayeredActors)
find_package(VTK COMPONENTS
CommonColor
CommonCore
CommonTransforms
FiltersSources
InteractionStyle
RenderingAnnotation
RenderingContextOpenGL2
RenderingCore
RenderingFreeType
RenderingGL2PSOpenGL2
RenderingOpenGL2
)
if (NOT VTK_FOUND)
message(FATAL_ERROR "LayeredActors: Unable to find the VTK build folder.")
endif()
# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(LayeredActors MACOSX_BUNDLE LayeredActors.cxx )
target_link_libraries(LayeredActors PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS LayeredActors
MODULES ${VTK_LIBRARIES}
)
Download and Build LayeredActors¶
Click here to download LayeredActors and its CMakeLists.txt file. Once the tarball LayeredActors.tar has been downloaded and extracted,
cd LayeredActors/build
If VTK is installed:
cmake ..
If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:
cmake -DVTK_DIR:PATH=/home/me/vtk_build ..
Build the project:
make
and run it:
./LayeredActors
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.