Skip to content

Commit 7956e03

Browse files
committed
viewer: add camera trajectory color
1 parent a4d652a commit 7956e03

File tree

6 files changed

+165
-53
lines changed

6 files changed

+165
-53
lines changed

apps/Viewer/Renderer.cpp

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ using namespace VIEWER;
3939
Renderer::Renderer()
4040
: pointCount(0)
4141
, pointNormalCount(0)
42-
, cameraIndexCount(0)
42+
, cameraPointIndexCount(0)
43+
, cameraLineIndexCount(0)
4344
, imageOverlayIndexCount(0)
4445
, selectionPrimitiveCount(0)
4546
, selectionOverlayVertexCount(0)
@@ -102,7 +103,8 @@ void Renderer::Reset() {
102103
// Reset scene-dependent primitive counts
103104
pointCount = 0;
104105
pointNormalCount = 0;
105-
cameraIndexCount = 0;
106+
cameraPointIndexCount = 0;
107+
cameraLineIndexCount = 0;
106108
imageOverlayIndexCount = 0;
107109
selectionPrimitiveCount = 0;
108110
boundsPrimitiveCount = 0;
@@ -725,13 +727,15 @@ static std::array<Point3f, 4> ComputeCameraFrustumCorners(const MVS::Image& imag
725727
return worldCorners;
726728
}
727729

728-
// Helper function to create camera frustum geometry for a single camera
729-
// Returns the vertices, colors, and indices for the camera wireframe
730-
static void CreateCameraFrustumGeometry(
730+
// Helper function to create camera frustum geometry for a single camera;
731+
// generate the vertices, colors, and indices for the camera wireframe and
732+
// returns the number of indices added
733+
static uint32_t CreateCameraFrustumGeometry(
731734
const MVS::Image& imageData,
732735
float depth,
733-
const Eigen::Vector3f& centerColor,
734-
const Eigen::Vector3f& frustumColor,
736+
bool showLookAt,
737+
const Pixel32F& centerColor,
738+
const Pixel32F& frustumColor,
735739
std::vector<float>& vertices,
736740
std::vector<float>& colors,
737741
std::vector<uint32_t>& indices,
@@ -740,31 +744,17 @@ static void CreateCameraFrustumGeometry(
740744
// Camera center (apex of the pyramid)
741745
const Point3f center = imageData.camera.C;
742746
vertices.insert(vertices.end(), {center.x, center.y, center.z});
743-
colors.insert(colors.end(), {centerColor.x(), centerColor.y(), centerColor.z()});
747+
colors.insert(colors.end(), {centerColor.c2, centerColor.c1, centerColor.c0});
744748

745-
// Get frustum corners using the helper function
746-
std::array<Point3f, 4> worldCorners = ComputeCameraFrustumCorners(imageData, depth);
747-
748-
// Add the 4 corners to vertices and colors
749-
for (int j = 0; j < 4; ++j) {
750-
const Point3f& worldCorner = worldCorners[j];
749+
// Get frustum corners using the helper function,
750+
// add the 4 corners to vertices and colors
751+
for (const Point3f& worldCorner : ComputeCameraFrustumCorners(imageData, depth)) {
751752
vertices.insert(vertices.end(), {worldCorner.x, worldCorner.y, worldCorner.z});
752-
colors.insert(colors.end(), {frustumColor.x(), frustumColor.y(), frustumColor.z()});
753+
colors.insert(colors.end(), {frustumColor.c2, frustumColor.c1, frustumColor.c0});
753754
}
754755

755-
// Add principal center point (green) - point on the image plane at the principal point
756-
const Point2 pp = imageData.camera.GetPrincipalPoint();
757-
const Point3f worldPrincipalPoint = imageData.camera.TransformPointI2W(Point3(pp.x, pp.y, depth));
758-
vertices.insert(vertices.end(), {worldPrincipalPoint.x, worldPrincipalPoint.y, worldPrincipalPoint.z});
759-
colors.insert(colors.end(), {0.f, 1.f, 0.f}); // Green
760-
761-
// Add upwards direction indicator (blue) - line showing camera's up direction
762-
const Point3f worldUpPoint = imageData.camera.TransformPointI2W(Point3(pp.x, pp.y - imageData.height * 0.5f, depth)); // Half way up from center
763-
vertices.insert(vertices.end(), {worldUpPoint.x, worldUpPoint.y, worldUpPoint.z});
764-
colors.insert(colors.end(), {0.f, 0.f, 1.f}); // Blue
765-
766-
// Create indices for wireframe lines
767-
// Lines from camera center to each corner (4 lines)
756+
// Create indices for wireframe lines,
757+
// lines from camera center to each corner (4 lines)
768758
for (int j = 0; j < 4; ++j) {
769759
indices.push_back(baseIndex); // camera center
770760
indices.push_back(baseIndex + 1 + j); // corner j
@@ -775,56 +765,102 @@ static void CreateCameraFrustumGeometry(
775765
indices.push_back(baseIndex + 1 + j); // current corner
776766
indices.push_back(baseIndex + 1 + ((j + 1) % 4)); // next corner
777767
}
768+
if (!showLookAt)
769+
return 16; // 4 lines from center + 4 lines for rectangle = 8 lines = 16 indices
778770

779-
// Line from camera center to principal point (green indicator)
771+
// Add principal center point (green) - point on the image plane at the principal point
772+
const Point2 pp = imageData.camera.GetPrincipalPoint();
773+
const Point3f worldPrincipalPoint = imageData.camera.TransformPointI2W(Point3(pp.x, pp.y, depth));
774+
vertices.insert(vertices.end(), {worldPrincipalPoint.x, worldPrincipalPoint.y, worldPrincipalPoint.z});
775+
colors.insert(colors.end(), {0.f, 1.f, 0.f}); // Green
776+
777+
// Add upwards direction indicator (blue) - line showing camera's up direction
778+
const Point3f worldUpPoint = imageData.camera.TransformPointI2W(Point3(pp.x, pp.y - imageData.height * 0.25f, depth)); // Quarter way up from center
779+
vertices.insert(vertices.end(), {worldUpPoint.x, worldUpPoint.y, worldUpPoint.z});
780+
colors.insert(colors.end(), {0.f, 0.f, 1.f}); // Blue
781+
782+
// Line from camera center to principal point (look-at indicator).
780783
indices.push_back(baseIndex); // camera center
781784
indices.push_back(baseIndex + 5); // principal point (index 5)
782785

783-
// Line from principal point to up direction point (blue indicator)
784-
indices.push_back(baseIndex + 5); // principal point
785-
indices.push_back(baseIndex + 6); // up direction point (index 6)
786+
// Line from principal point to upwards direction indicator.
787+
indices.push_back(baseIndex + 5); // principal point (index 5)
788+
indices.push_back(baseIndex + 6); // upwards direction indicator (index 6)
789+
790+
return 20; // 4 lines from center + 4 lines for rectangle + 2 lines for look-at = 10 lines = 20 indices
786791
}
787792

788793
void Renderer::UploadCameras(const Window& window) {
794+
cameraPointIndexCount = cameraLineIndexCount = imageOverlayIndexCount = 0;
789795
if (window.GetScene().GetImages().empty())
790796
return;
791797
const float depth = window.GetCamera().GetSceneDistance() * window.cameraSize;
798+
const bool useJetColors = window.cameraDisplayColor == Window::CAMERA_COLOR_JET;
799+
const bool displayDots = window.cameraDisplayType == Window::CAMERA_DISPLAY_DOT;
800+
const bool showLookAt = window.showCameraLookAt;
801+
const MVS::ImageArr& images = window.GetScene().GetScene().images;
802+
const ImageArr& viewerImages = window.GetScene().GetImages();
803+
const size_t imageCount = viewerImages.size();
792804

793805
// Generate camera frustum geometry
794-
cameraIndexCount = 0;
795806
std::vector<float> cameraVertices;
796807
std::vector<float> cameraColors;
797-
std::vector<uint32_t> cameraIndices;
798-
// Use white colors for normal camera rendering
799-
const Eigen::Vector3f centerColor(1.f, 1.f, 1.f); // White for center
800-
const Eigen::Vector3f frustumColor(1.f, 1.f, 0.f); // Yellow for frustum
801-
for (const auto& image : window.GetScene().GetImages()) {
802-
const MVS::Image& imageData = window.GetScene().GetScene().images[image.idx];
808+
std::vector<uint32_t> cameraPointIndices;
809+
std::vector<uint32_t> cameraLineIndices;
810+
for (size_t cameraIdx = 0; cameraIdx < imageCount; ++cameraIdx) {
811+
const MVS::Image& imageData = images[viewerImages[cameraIdx].idx];
803812
ASSERT(imageData.IsValid());
804-
// Create frustum vertices in camera coordinate system
805-
size_t baseIndex = cameraVertices.size() / 3;
806-
// Create camera frustum geometry using the shared function
807-
CreateCameraFrustumGeometry(
813+
const float colorValue = imageCount > 1 ? ((float)cameraIdx / (float)(imageCount - 1)) : 0.5f;
814+
const Pixel32F cameraColor = useJetColors ? Pixel32F::gray2color(colorValue) : Pixel32F::YELLOW;
815+
816+
// Dot mode: draw only camera centers.
817+
if (displayDots) {
818+
const uint32_t baseIndex = (uint32_t)(cameraVertices.size() / 3);
819+
const Point3f center = imageData.camera.C;
820+
cameraVertices.insert(cameraVertices.end(), {center.x, center.y, center.z});
821+
cameraColors.insert(cameraColors.end(), {cameraColor.c2, cameraColor.c1, cameraColor.c0});
822+
cameraPointIndices.push_back((uint32_t)baseIndex);
823+
++cameraPointIndexCount;
824+
if (showLookAt) {
825+
const Point2 pp = imageData.camera.GetPrincipalPoint();
826+
const Point3f worldPrincipalPoint = imageData.camera.TransformPointI2W(Point3(pp.x, pp.y, depth));
827+
cameraVertices.insert(cameraVertices.end(), {worldPrincipalPoint.x, worldPrincipalPoint.y, worldPrincipalPoint.z});
828+
cameraColors.insert(cameraColors.end(), {0.f, 1.f, 0.f}); // Green look-at target
829+
cameraLineIndices.push_back(baseIndex);
830+
cameraLineIndices.push_back(baseIndex + 1);
831+
cameraLineIndexCount += 2;
832+
}
833+
continue;
834+
}
835+
836+
// Frustum mode: draw full camera wireframe.
837+
const size_t baseIndex = cameraVertices.size() / 3;
838+
cameraLineIndexCount += CreateCameraFrustumGeometry(
808839
imageData,
809840
depth,
810-
centerColor,
811-
frustumColor,
841+
showLookAt,
842+
cameraColor,
843+
cameraColor,
812844
cameraVertices,
813845
cameraColors,
814-
cameraIndices,
846+
cameraLineIndices,
815847
baseIndex
816848
);
817-
cameraIndexCount += 20; // 10 lines * 2 indices per line
818849
}
850+
851+
std::vector<uint32_t> cameraIndices;
852+
cameraIndices.reserve(cameraPointIndices.size() + cameraLineIndices.size());
853+
cameraIndices.insert(cameraIndices.end(), cameraPointIndices.begin(), cameraPointIndices.end());
854+
cameraIndices.insert(cameraIndices.end(), cameraLineIndices.begin(), cameraLineIndices.end());
855+
819856
// Upload camera geometry to GPU buffers
820-
if (cameraIndexCount) {
857+
if (!cameraIndices.empty()) {
821858
cameraVBO->SetData(cameraVertices);
822859
cameraColorVBO->SetData(cameraColors);
823860
cameraEBO->SetData(cameraIndices);
824861
}
825862

826863
// Collect all overlay geometry for images with valid textures
827-
imageOverlayIndexCount = 0;
828864
std::vector<float> allVertices;
829865
std::vector<uint32_t> allIndices;
830866
for (const auto& image : window.GetScene().GetImages()) {
@@ -1167,15 +1203,25 @@ void Renderer::RenderMesh(const Window& window) {
11671203
}
11681204

11691205
void Renderer::RenderCameras(const Window& window) {
1170-
if (cameraIndexCount == 0)
1206+
if (cameraPointIndexCount == 0 && cameraLineIndexCount == 0)
11711207
return;
11721208

11731209
cameraShader->Use();
11741210

11751211
cameraVAO->Bind();
11761212
cameraEBO->Bind();
11771213

1178-
GL_CHECK(glDrawElements(GL_LINES, cameraIndexCount, GL_UNSIGNED_INT, 0));
1214+
if (window.cameraDisplayType == Window::CAMERA_DISPLAY_DOT) {
1215+
if (cameraPointIndexCount > 0)
1216+
GL_CHECK(glDrawElements(GL_POINTS, static_cast<GLsizei>(cameraPointIndexCount), GL_UNSIGNED_INT, 0));
1217+
if (window.showCameraLookAt && cameraLineIndexCount > 0) {
1218+
const void* lineOffset = reinterpret_cast<const void*>(cameraPointIndexCount * sizeof(uint32_t));
1219+
GL_CHECK(glDrawElements(GL_LINES, static_cast<GLsizei>(cameraLineIndexCount), GL_UNSIGNED_INT, lineOffset));
1220+
}
1221+
} else {
1222+
if (cameraLineIndexCount > 0)
1223+
GL_CHECK(glDrawElements(GL_LINES, static_cast<GLsizei>(cameraLineIndexCount), GL_UNSIGNED_INT, 0));
1224+
}
11791225

11801226
cameraVAO->Unbind();
11811227
}

apps/Viewer/Renderer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ class Renderer {
9292
std::unique_ptr<Shader> cameraShader;
9393
std::unique_ptr<VAO> cameraVAO;
9494
std::unique_ptr<VBO> cameraVBO, cameraEBO, cameraColorVBO;
95-
size_t cameraIndexCount;
95+
size_t cameraPointIndexCount;
96+
size_t cameraLineIndexCount;
9697

9798
// 3D image overlay rendering (pre-computed for all images with valid textures)
9899
std::unique_ptr<Shader> imageOverlayShader;

apps/Viewer/UI.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ void UI::ShowCameraControls(Window& window) {
490490
if (!showCameraControls) return;
491491

492492
ImGui::SetNextWindowPos(ImVec2(1044, 100), ImGuiCond_FirstUseEver);
493-
ImGui::SetNextWindowSize(ImVec2(224, 296), ImGuiCond_FirstUseEver);
493+
ImGui::SetNextWindowSize(ImVec2(224, 360), ImGuiCond_FirstUseEver);
494494
if (ImGui::Begin("Camera Controls", &showCameraControls)) {
495495
Camera& camera = window.GetCamera();
496496

@@ -526,6 +526,38 @@ void UI::ShowCameraControls(Window& window) {
526526
if (ImGui::IsItemHovered())
527527
ImGui::SetTooltip("Adjust camera size");
528528

529+
// Camera display color mode
530+
ImGui::TextUnformatted("Camera Display Color");
531+
int displayColor = (int)window.cameraDisplayColor;
532+
bool cameraDisplayChanged = false;
533+
if (ImGui::RadioButton("Solid##CameraDisplayColor", &displayColor, (int)Window::CAMERA_COLOR_SOLID))
534+
cameraDisplayChanged = true;
535+
ImGui::SameLine();
536+
if (ImGui::RadioButton("Jet##CameraDisplayColor", &displayColor, (int)Window::CAMERA_COLOR_JET))
537+
cameraDisplayChanged = true;
538+
539+
// Camera display type
540+
ImGui::TextUnformatted("Camera Display Type");
541+
int displayType = (int)window.cameraDisplayType;
542+
if (ImGui::RadioButton("Frustum##CameraDisplayType", &displayType, (int)Window::CAMERA_DISPLAY_FRUSTUM))
543+
cameraDisplayChanged = true;
544+
ImGui::SameLine();
545+
if (ImGui::RadioButton("Dot##CameraDisplayType", &displayType, (int)Window::CAMERA_DISPLAY_DOT))
546+
cameraDisplayChanged = true;
547+
548+
if (ImGui::Checkbox("Show Camera LookAt", &window.showCameraLookAt))
549+
cameraDisplayChanged = true;
550+
if (ImGui::IsItemHovered())
551+
ImGui::SetTooltip("Show look-at direction indicator for each camera");
552+
553+
if (cameraDisplayChanged) {
554+
window.cameraDisplayColor = (Window::CameraDisplayColor)displayColor;
555+
window.cameraDisplayType = (Window::CameraDisplayType)displayType;
556+
window.GetRenderer().UploadCameras(window);
557+
window.GetRenderer().UploadSelection(window);
558+
window.RequestRedraw();
559+
}
560+
529561
// Arcball sensitivity controls (only show when in arcball mode)
530562
if (window.GetControlMode() == Window::CONTROL_ARCBALL) {
531563
ImGui::Separator();
@@ -2989,6 +3021,21 @@ void SettingsReadLine(ImGuiContext*, ImGuiSettingsHandler* handler, void* entry,
29893021
else if (sscanf(line, "CameraSize=%f", &x) == 1) {
29903022
window.cameraSize = x;
29913023
}
3024+
else if (sscanf(line, "CameraDisplayColor=%d", &intVal) == 1) {
3025+
window.cameraDisplayColor =
3026+
intVal == (int)Window::CAMERA_COLOR_JET ? Window::CAMERA_COLOR_JET : Window::CAMERA_COLOR_SOLID;
3027+
}
3028+
else if (sscanf(line, "CameraDisplayType=%d", &intVal) == 1) {
3029+
window.cameraDisplayType =
3030+
intVal == (int)Window::CAMERA_DISPLAY_DOT ? Window::CAMERA_DISPLAY_DOT : Window::CAMERA_DISPLAY_FRUSTUM;
3031+
}
3032+
else if (sscanf(line, "ShowCameraLookAt=%d", &intVal) == 1) {
3033+
window.showCameraLookAt = (intVal != 0);
3034+
}
3035+
else if (sscanf(line, "ShowCameraCoordinateSystem=%d", &intVal) == 1) {
3036+
// Backward compatibility with older saved settings key.
3037+
window.showCameraLookAt = (intVal != 0);
3038+
}
29923039
else if (sscanf(line, "PointSize=%f", &x) == 1) {
29933040
window.pointSize = x;
29943041
}
@@ -3041,6 +3088,9 @@ void SettingsWriteAll(ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuf
30413088
window.clearColor[0], window.clearColor[1],
30423089
window.clearColor[2], window.clearColor[3]);
30433090
buf->appendf("CameraSize=%f\n", window.cameraSize);
3091+
buf->appendf("CameraDisplayColor=%d\n", (int)window.cameraDisplayColor);
3092+
buf->appendf("CameraDisplayType=%d\n", (int)window.cameraDisplayType);
3093+
buf->appendf("ShowCameraLookAt=%d\n", window.showCameraLookAt ? 1 : 0);
30443094
buf->appendf("PointSize=%f\n", window.pointSize);
30453095
buf->appendf("EstimateSfMNormals=%d\n",
30463096
window.GetScene().estimateSfMNormals ? 1 : 0);

apps/Viewer/Window.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ Window::Window()
6363
, minViews(2)
6464
, userFontScale(1.f)
6565
, cameraSize(0.1f)
66+
, cameraDisplayColor(CAMERA_COLOR_SOLID)
67+
, cameraDisplayType(CAMERA_DISPLAY_FRUSTUM)
68+
, showCameraLookAt(true)
6669
, pointSize(3.f)
6770
, pointNormalLength(0.02f)
6871
, imageOverlayOpacity(0.5f)

apps/Viewer/Window.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ class Window {
5353
CONTROL_BBOX_EDIT,
5454
CONTROL_NONE
5555
};
56+
enum CameraDisplayColor {
57+
CAMERA_COLOR_SOLID = 0,
58+
CAMERA_COLOR_JET
59+
};
60+
enum CameraDisplayType {
61+
CAMERA_DISPLAY_FRUSTUM = 0,
62+
CAMERA_DISPLAY_DOT
63+
};
5664

5765
private:
5866
GLFWwindow* window;
@@ -104,6 +112,9 @@ class Window {
104112
MVS::IIndex minViews;
105113
float userFontScale; // UI font scale
106114
float cameraSize;
115+
CameraDisplayColor cameraDisplayColor;
116+
CameraDisplayType cameraDisplayType;
117+
bool showCameraLookAt;
107118
float pointSize;
108119
float pointNormalLength;
109120
float imageOverlayOpacity;

apps/Viewer/shaders/camera.vert

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ layout (std140) uniform ViewProjection {
1616
void main() {
1717
gl_Position = viewProjection * vec4(aPos, 1.0);
1818
vertexColor = aColor;
19+
gl_PointSize = 6.0;
1920
}
2021
)glsl"

0 commit comments

Comments
 (0)