Skip to content

Commit 5ef0b86

Browse files
jdumasclaude
andcommitted
add split viewport support with independent cameras
Adds support for split viewports with independent per-viewport camera state and arbitrary grid layouts (1x1, 1x2, 2x1, 2x2). Each viewport owns its own scene framebuffers, swapped with the engine during rendering so all existing rendering paths work without modification. Addresses #387. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0c3dd68 commit 5ef0b86

10 files changed

Lines changed: 1095 additions & 10 deletions

File tree

examples/demo-app/demo_app.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "polyscope/surface_mesh.h"
1717
#include "polyscope/types.h"
1818
#include "polyscope/view.h"
19+
#include "polyscope/viewport.h"
1920
#include "polyscope/volume_grid.h"
2021
#include "polyscope/volume_mesh.h"
2122

@@ -954,6 +955,23 @@ void callback() {
954955
addSparseVolumeGrid();
955956
}
956957

958+
// Split viewport controls (per-viewport settings are in the built-in View panel)
959+
if (ImGui::Button("Single")) {
960+
polyscope::setSingleViewport();
961+
}
962+
ImGui::SameLine();
963+
if (ImGui::Button("V-Split")) {
964+
polyscope::setVerticalSplitViewport();
965+
}
966+
ImGui::SameLine();
967+
if (ImGui::Button("H-Split")) {
968+
polyscope::setHorizontalSplitViewport();
969+
}
970+
ImGui::SameLine();
971+
if (ImGui::Button("Quad")) {
972+
polyscope::setQuadViewport();
973+
}
974+
957975
// ImPlot
958976
// dummy data
959977
if (ImGui::TreeNode("ImPlot")) {

include/polyscope/context.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <polyscope/pick.h>
66
#include <polyscope/types.h>
7+
#include <polyscope/viewport.h>
78
#include <polyscope/weak_handle.h>
89

910
#include <glm/glm.hpp>
@@ -119,6 +120,16 @@ struct Context {
119120
bool pointCloudEfficiencyWarningReported = false;
120121
FloatingQuantityStructure* globalFloatingQuantityStructure = nullptr;
121122

123+
// ======================================================
124+
// === Viewport grid
125+
// ======================================================
126+
127+
int viewportGridRows = 1;
128+
int viewportGridCols = 1;
129+
std::vector<std::unique_ptr<Viewport>> viewports;
130+
Viewport* activeViewport = nullptr; // the viewport currently receiving mouse input (null when no button held)
131+
Viewport* lastActiveViewport = nullptr; // the most recently interacted viewport (persists after mouse release)
132+
122133
// ======================================================
123134
// === Other various global lists
124135
// ======================================================

include/polyscope/polyscope.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "polyscope/structure.h"
2222
#include "polyscope/transformation_gizmo.h"
2323
#include "polyscope/utilities.h"
24+
#include "polyscope/viewport.h"
2425
#include "polyscope/weak_handle.h"
2526
#include "polyscope/widget.h"
2627

@@ -196,6 +197,26 @@ void buildPickGui();
196197
void buildUserGuiAndInvokeCallback();
197198

198199

200+
// === Viewport management
201+
202+
// Set the viewport grid layout (e.g. 1x1, 1x2, 2x1, 2x2)
203+
void setViewportGridLayout(int rows, int cols);
204+
int getViewportGridRows();
205+
int getViewportGridCols();
206+
207+
// Convenience presets
208+
void setSingleViewport();
209+
void setVerticalSplitViewport(); // 1 row, 2 columns (left | right)
210+
void setHorizontalSplitViewport(); // 2 rows, 1 column (top / bottom)
211+
void setQuadViewport(); // 2 rows, 2 columns
212+
213+
// Get a specific viewport (nullptr if out of range)
214+
Viewport* getViewport(int row, int col);
215+
Viewport* getActiveViewport();
216+
217+
// Internal: update viewport layouts after window resize
218+
void updateViewportLayouts();
219+
199220
// === Utility
200221

201222
// Execute one iteration of the main loop

include/polyscope/viewport.h

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run
2+
3+
#pragma once
4+
5+
#include <array>
6+
#include <memory>
7+
#include <string>
8+
9+
#include "polyscope/types.h"
10+
#include "polyscope/view.h"
11+
12+
namespace polyscope {
13+
14+
// Forward declarations
15+
namespace render {
16+
class FrameBuffer;
17+
class TextureBuffer;
18+
class ShaderProgram;
19+
} // namespace render
20+
21+
// A snapshot of the global view state, used for push/pop when rendering viewports
22+
struct ViewStateSnapshot {
23+
glm::mat4x4 viewMat;
24+
float fov;
25+
glm::vec3 viewCenter;
26+
NavigateStyle navigateStyle;
27+
UpDir upDir;
28+
FrontDir frontDir;
29+
ProjectionMode projectionMode;
30+
float nearClip, farClip;
31+
float moveScale;
32+
ViewRelativeMode viewRelativeMode;
33+
std::array<float, 4> bgColor;
34+
int bufferWidth, bufferHeight;
35+
int windowWidth, windowHeight;
36+
37+
// Flight state
38+
bool midflight;
39+
float flightStartTime, flightEndTime;
40+
glm::dualquat flightTargetViewR, flightInitialViewR;
41+
glm::vec3 flightTargetViewT, flightInitialViewT;
42+
float flightTargetFov, flightInitialFov;
43+
};
44+
45+
// Save/restore the current global view state
46+
ViewStateSnapshot saveViewState();
47+
void restoreViewState(const ViewStateSnapshot& snapshot);
48+
49+
50+
class Viewport {
51+
public:
52+
Viewport(std::string name, int gridRow, int gridCol);
53+
~Viewport();
54+
55+
// === Identity
56+
std::string name;
57+
int gridRow, gridCol;
58+
59+
// === Camera state (independent per viewport)
60+
glm::mat4x4 viewMat;
61+
float fov;
62+
glm::vec3 viewCenter;
63+
NavigateStyle navigateStyle;
64+
UpDir upDir;
65+
FrontDir frontDir;
66+
ProjectionMode projectionMode;
67+
float nearClip, farClip;
68+
float moveScale;
69+
ViewRelativeMode viewRelativeMode;
70+
std::array<float, 4> bgColor;
71+
72+
// Flight state
73+
bool midflight;
74+
float flightStartTime, flightEndTime;
75+
glm::dualquat flightTargetViewR, flightInitialViewR;
76+
glm::vec3 flightTargetViewT, flightInitialViewT;
77+
float flightTargetFov, flightInitialFov;
78+
79+
// === Pixel region (computed from grid layout + window size)
80+
int pixelX, pixelY; // lower-left corner in buffer coords
81+
int pixelWidth, pixelHeight; // size in buffer pixels
82+
int windowX, windowY; // lower-left corner in window coords
83+
int windowW, windowH; // size in window pixels
84+
85+
// === Framebuffers (per-viewport)
86+
std::shared_ptr<render::FrameBuffer> sceneBuffer;
87+
std::shared_ptr<render::FrameBuffer> sceneBufferFinal;
88+
std::shared_ptr<render::FrameBuffer> sceneDepthMinFrame;
89+
std::shared_ptr<render::TextureBuffer> sceneColor, sceneColorFinal, sceneDepth, sceneDepthMin;
90+
std::shared_ptr<render::ShaderProgram> compositePeel;
91+
92+
// === Methods
93+
94+
// Push this viewport's camera state into the global view:: variables
95+
void pushViewState();
96+
97+
// Store the current global view state back into this viewport
98+
void pullViewState();
99+
100+
// Recompute pixel dimensions from grid layout and window size
101+
void updateLayout(int totalBufferW, int totalBufferH, int totalWindowW, int totalWindowH, int gridRows, int gridCols);
102+
103+
// Create or resize per-viewport framebuffers
104+
void ensureBuffersAllocated();
105+
void resizeBuffers();
106+
107+
// Reset camera to the home view for this viewport
108+
void resetCameraToHomeView();
109+
110+
// Set navigate style with proper camera adjustment (use instead of setting navigateStyle directly)
111+
void setNavigateStyle(NavigateStyle newStyle, bool animateFlight = false);
112+
113+
// Set up direction with proper camera adjustment (use instead of setting upDir directly)
114+
void setUpDir(UpDir newUpDir, bool animateFlight = false);
115+
116+
// Check if screen coordinates (in window space) fall within this viewport
117+
bool containsScreenCoords(float x, float y) const;
118+
};
119+
120+
} // namespace polyscope

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ SET(SRCS
165165
group.cpp
166166
utilities.cpp
167167
view.cpp
168+
viewport.cpp
168169
screenshot.cpp
169170
messages.cpp
170171
pick.cpp
@@ -352,6 +353,7 @@ SET(HEADERS
352353
${INCLUDE_ROOT}/types.h
353354
${INCLUDE_ROOT}/utilities.h
354355
${INCLUDE_ROOT}/view.h
356+
${INCLUDE_ROOT}/viewport.h
355357
${INCLUDE_ROOT}/vector_quantity.h
356358
${INCLUDE_ROOT}/vector_quantity.ipp
357359
${INCLUDE_ROOT}/volume_mesh.h

0 commit comments

Comments
 (0)