From f9a7ce4203b9d72fd1de55631bdf559fcef26fae Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 23 Apr 2026 11:33:49 +0900 Subject: [PATCH 1/3] auto-scale indices display based on camera distance The indices text now maintains a constant screen-space size regardless of zoom level and scene dimensions, removing the need to manually adjust showIndicesScale per scene. - Compute per-vertex scale from the projection matrix and camera depth in GlText::textureDraw_Indices - Handle both perspective and orthographic projections - Add alpha blending for smoother text rendering - Update all callers to pass a dimensionless multiplier (default 1.0) instead of bbox-dependent world-space size --- .../PolynomialRestShapeSpringsForceField.inl | 4 +- .../statecontainer/MechanicalObject.inl | 6 +-- .../dynamic/PointSetGeometryAlgorithms.inl | 9 +---- .../component/visual/VisualPointCloud.inl | 5 +-- Sofa/GL/src/sofa/gl/glText.cpp | 39 +++++++++++++++++-- .../Common/src/sofa/gui/common/BaseViewer.cpp | 3 +- .../Common/src/sofa/gui/common/BaseViewer.h | 2 +- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Sofa/Component/SolidMechanics/Spring/src/sofa/component/solidmechanics/spring/PolynomialRestShapeSpringsForceField.inl b/Sofa/Component/SolidMechanics/Spring/src/sofa/component/solidmechanics/spring/PolynomialRestShapeSpringsForceField.inl index 2703c5d3690..a46099cc16f 100644 --- a/Sofa/Component/SolidMechanics/Spring/src/sofa/component/solidmechanics/spring/PolynomialRestShapeSpringsForceField.inl +++ b/Sofa/Component/SolidMechanics/Spring/src/sofa/component/solidmechanics/spring/PolynomialRestShapeSpringsForceField.inl @@ -40,7 +40,7 @@ PolynomialRestShapeSpringsForceField::PolynomialRestShapeSpringsForce , d_recomputeIndices(initData(&d_recomputeIndices, false, "recompute_indices", "Recompute indices (should be false for BBOX)")) , d_drawSpring(initData(&d_drawSpring,false,"drawSpring","draw Spring")) , d_springColor(initData(&d_springColor, sofa::type::RGBAColor(0.0f, 1.0f, 0.0f, 1.0f), "springColor","spring color")) - , d_showIndicesScale(initData(&d_showIndicesScale, (float)0.02, "showIndicesScale", "Scale for indices display. (default=0.02)")) + , d_showIndicesScale(initData(&d_showIndicesScale, (float)1.0, "showIndicesScale", "Multiplier for indices display size. Indices are auto-scaled to maintain a constant screen size.")) , d_zeroLength(initData(&d_zeroLength,"initialLength","initial virtual length of the spring")) , d_smoothShift(initData(&d_smoothShift,static_cast(0.0),"smoothShift","denominator correction adding shift value")) , d_smoothScale(initData(&d_smoothScale,static_cast(1.0),"smoothScale","denominator correction adding scale")) @@ -432,7 +432,7 @@ void PolynomialRestShapeSpringsForceField::draw(const core::visual::V // draw connected point indices - Real scale = (vparams->sceneBBox().maxBBox() - vparams->sceneBBox().minBBox()).norm() * d_showIndicesScale.getValue(); + Real scale = d_showIndicesScale.getValue(); type::vector positions; for (sofa::Index i = 0; i < indices.size(); i++) { diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.inl b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.inl index 45f23586500..2cfc45c8378 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.inl +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.inl @@ -151,7 +151,7 @@ MechanicalObject::MechanicalObject() , showObject(initData(&showObject, (bool) false, "showObject", "Show objects. (default=false)")) , showObjectScale(initData(&showObjectScale, 0.1f, "showObjectScale", "Scale for object display. (default=0.1)")) , showIndices(initData(&showIndices, (bool) false, "showIndices", "Show indices. (default=false)")) - , showIndicesScale(initData(&showIndicesScale, 0.02f, "showIndicesScale", "Scale for indices display. (default=0.02)")) + , showIndicesScale(initData(&showIndicesScale, 1.0f, "showIndicesScale", "Multiplier for indices display size. Indices are auto-scaled to maintain a constant screen size.")) , showVectors(initData(&showVectors, (bool) false, "showVectors", "Show velocity. (default=false)")) , showVectorsScale(initData(&showVectorsScale, 0.0001f, "showVectorsScale", "Scale for vectors display. (default=0.0001)")) , drawMode(initData(&drawMode,0,"drawMode","The way vectors will be drawn:\n- 0: Line\n- 1:Cylinder\n- 2: Arrow.\n\nThe DOFS will be drawn:\n- 0: point\n- >1: sphere. (default=0)")) @@ -2630,14 +2630,12 @@ SReal MechanicalObject::getConstraintJacobianTimesVecDeriv(unsigned i template inline void MechanicalObject::drawIndices(const core::visual::VisualParams* vparams) { - const float scale = (float)((vparams->sceneBBox().maxBBox() - vparams->sceneBBox().minBBox()).norm() * showIndicesScale.getValue()); - std::vector positions; positions.reserve(d_size.getValue()); for (int i = 0; i drawTool()->draw3DText_Indices(positions, scale, d_color.getValue()); + vparams->drawTool()->draw3DText_Indices(positions, showIndicesScale.getValue(), d_color.getValue()); } template diff --git a/Sofa/Component/Topology/Container/Dynamic/src/sofa/component/topology/container/dynamic/PointSetGeometryAlgorithms.inl b/Sofa/Component/Topology/Container/Dynamic/src/sofa/component/topology/container/dynamic/PointSetGeometryAlgorithms.inl index 5b51db3f59a..051d065c0f8 100644 --- a/Sofa/Component/Topology/Container/Dynamic/src/sofa/component/topology/container/dynamic/PointSetGeometryAlgorithms.inl +++ b/Sofa/Component/Topology/Container/Dynamic/src/sofa/component/topology/container/dynamic/PointSetGeometryAlgorithms.inl @@ -34,7 +34,7 @@ using sofa::core::objectmodel::ComponentState; template PointSetGeometryAlgorithms< DataTypes >::PointSetGeometryAlgorithms() : GeometryAlgorithms() - , d_showIndicesScale (core::objectmodel::Base::initData(&d_showIndicesScale, (float) 0.02, "showIndicesScale", "Debug : scale for view topology indices")) + , d_showIndicesScale (core::objectmodel::Base::initData(&d_showIndicesScale, (float) 1.0, "showIndicesScale", "Debug : multiplier for view topology indices size. Indices are auto-scaled to maintain a constant screen size.")) , d_showPointIndices (core::objectmodel::Base::initData(&d_showPointIndices, (bool) false, "showPointIndices", "Debug : view Point indices")) , d_tagMechanics( initData(&d_tagMechanics,std::string(),"tagMechanics","Tag of the Mechanical Object")) , l_topology(initLink("topology", "link to the topology container")) @@ -85,12 +85,7 @@ void PointSetGeometryAlgorithms< DataTypes >::reinit() template float PointSetGeometryAlgorithms< DataTypes >::getIndicesScale() const { - const sofa::type::BoundingBox& bbox = this->getContext()->f_bbox.getValue(); - const float bbDiff = float((bbox.maxBBox() - bbox.minBBox()).norm()); - if (std::isinf(bbDiff)) - return d_showIndicesScale.getValue(); - else - return bbDiff * d_showIndicesScale.getValue(); + return d_showIndicesScale.getValue(); } diff --git a/Sofa/Component/Visual/src/sofa/component/visual/VisualPointCloud.inl b/Sofa/Component/Visual/src/sofa/component/visual/VisualPointCloud.inl index d3a508faba0..57fc964ff1f 100644 --- a/Sofa/Component/Visual/src/sofa/component/visual/VisualPointCloud.inl +++ b/Sofa/Component/Visual/src/sofa/component/visual/VisualPointCloud.inl @@ -158,10 +158,7 @@ void VisualPointCloud::doDrawVisual(const core::visual::VisualParams* template void VisualPointCloud::drawIndices(const core::visual::VisualParams* vparams) const { - const float scale = static_cast( - (vparams->sceneBBox().maxBBox() - vparams->sceneBBox().minBBox()).norm() * - d_indicesScale.getValue()); - vparams->drawTool()->draw3DText_Indices(convertCoord(), scale, d_indicesColor.getValue()); + vparams->drawTool()->draw3DText_Indices(convertCoord(), d_indicesScale.getValue(), d_indicesColor.getValue()); } template diff --git a/Sofa/GL/src/sofa/gl/glText.cpp b/Sofa/GL/src/sofa/gl/glText.cpp index 6fd0263b2a9..750bf13c94a 100644 --- a/Sofa/GL/src/sofa/gl/glText.cpp +++ b/Sofa/GL/src/sofa/gl/glText.cpp @@ -184,13 +184,32 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons static const float worldHeight = 1.0; static const float worldWidth = 0.5; + // Auto-scaling: retrieve projection matrix and viewport to maintain + // a constant screen-space text size regardless of camera distance + GLfloat projMatrix[16]; + GLint viewport[4]; + glGetFloatv(GL_PROJECTION_MATRIX, projMatrix); + glGetIntegerv(GL_VIEWPORT, viewport); + + const float viewportHeight = static_cast(viewport[3]); + // Column-major P[1][1] = cot(fov_y/2) for perspective + const float p11 = projMatrix[5]; + // Column-major P[3][3]: 0 for perspective, ~1 for orthographic + const bool isPerspective = (projMatrix[15] < 0.5f); + // Base text height in pixels (before user multiplier) + static const float baseFontPixelHeight = 30.0f; + + if (p11 == 0.0f || viewportHeight == 0.0f) + return; + glPushAttrib(GL_TEXTURE_BIT); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // multiply tex color with glColor - //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // only tex color (no glColor) glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); s_asciiTexture->bind(); @@ -216,10 +235,23 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons type::Vec3f temp(positions[i][0], positions[i][1], positions[i][2]); temp = modelviewM.transform(temp); + // Compute auto-scale: one pixel in world units = 2*depth / (p11 * viewportHeight) + float autoScale; + if (isPerspective) + { + float depth = -temp[2]; + if (depth < 1e-5f) depth = 1e-5f; + autoScale = baseFontPixelHeight * scale * 2.0f * depth / (p11 * viewportHeight); + } + else + { + autoScale = baseFontPixelHeight * scale * 2.0f / (p11 * viewportHeight); + } + glLoadIdentity(); //translate a little bit to center the text on the position (instead of starting from a top-left position) - glTranslatef(temp[0] - (worldWidth*length*scale)*0.5f, temp[1] + worldHeight*scale*0.5f, temp[2]); - glScalef(scale, scale, scale); + glTranslatef(temp[0] - (worldWidth*length*autoScale)*0.5f, temp[1] + worldHeight*autoScale*0.5f, temp[2]); + glScalef(autoScale, autoScale, autoScale); glRotatef(180.0, 1, 0, 0); for (std::size_t j = 0; j < length; j++) { @@ -262,6 +294,7 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons s_asciiTexture->unbind(); glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); glPopAttrib(); diff --git a/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.cpp b/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.cpp index 4ecdad8bbc3..22297b87293 100644 --- a/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.cpp +++ b/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.cpp @@ -472,8 +472,7 @@ void BaseViewer::drawSelection(sofa::core::visual::VisualParams* vparams) if(m_showSelectedObjectIndices && !positions.empty() && validBox) { - const float scale = (box.maxBBox() - box.minBBox()).norm() * m_visualScaling; - drawTool->draw3DText_Indices(positions, scale, m_selectionColor); + drawTool->draw3DText_Indices(positions, m_visualScaling, m_selectionColor); } continue; diff --git a/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.h b/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.h index 3a989cc27b4..2e14241df36 100644 --- a/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.h +++ b/Sofa/GUI/Common/src/sofa/gui/common/BaseViewer.h @@ -133,7 +133,7 @@ class SOFA_GUI_COMMON_API BaseViewer bool m_showSelectedObjectVolumes {false}; bool m_showSelectedObjectIndices {false}; type::RGBAColor m_selectionColor {type::RGBAColor::purple()}; - float m_visualScaling {0.2}; + float m_visualScaling {1.0}; protected: void drawIndices(const sofa::type::BoundingBox& bbox, const std::vector& positions); From 9d12d57d8da6c5238a2832daec6a0a15c392bc67 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 23 Apr 2026 12:59:43 +0900 Subject: [PATCH 2/3] improve indices text rendering quality - Add drop shadow (black, 70% opacity, 1.5px offset) behind each label for readability against any background - Disable depth test so indices always render on top of geometry - Enable mipmaps on the ASCII texture atlas for cleaner downsampling --- Sofa/GL/src/sofa/gl/glText.cpp | 43 +++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/Sofa/GL/src/sofa/gl/glText.cpp b/Sofa/GL/src/sofa/gl/glText.cpp index 750bf13c94a..9fd7bc3d5b1 100644 --- a/Sofa/GL/src/sofa/gl/glText.cpp +++ b/Sofa/GL/src/sofa/gl/glText.cpp @@ -38,7 +38,7 @@ void GlText::initTexture() } if (s_asciiTexture == nullptr && s_asciiImage != nullptr) { - s_asciiTexture = new sofa::gl::Texture(s_asciiImage, false, true, false ); + s_asciiTexture = new sofa::gl::Texture(s_asciiImage, false, true, true ); } } @@ -210,9 +210,15 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons glAlphaFunc(GL_GREATER, 0.0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); s_asciiTexture->bind(); + // Save the caller-set color for the main text pass + GLfloat textColor[4]; + glGetFloatv(GL_CURRENT_COLOR, textColor); + for (std::size_t i = 0; i < positions.size(); i++) { std::ostringstream oss; @@ -223,8 +229,6 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons std::vector vertices; std::vector UVs; - glDisable(GL_LIGHTING); - glPushMatrix(); // Makes text always face the viewer by removing the scene rotation @@ -248,11 +252,7 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons autoScale = baseFontPixelHeight * scale * 2.0f / (p11 * viewportHeight); } - glLoadIdentity(); - //translate a little bit to center the text on the position (instead of starting from a top-left position) - glTranslatef(temp[0] - (worldWidth*length*autoScale)*0.5f, temp[1] + worldHeight*autoScale*0.5f, temp[2]); - glScalef(autoScale, autoScale, autoScale); - glRotatef(180.0, 1, 0, 0); + // Build quads for this label for (std::size_t j = 0; j < length; j++) { Vec3f vertex_up_left = Vec3f(j*worldWidth, worldHeight, 0.0f); @@ -281,6 +281,31 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons UVs.push_back(uv_up_right); } + // Shadow offset: 1.5 pixels in view space + const float shadowOffset = 1.5f * autoScale / baseFontPixelHeight; + + // Drop shadow pass (dark, offset down-right) + glColor4f(0.0f, 0.0f, 0.0f, textColor[3] * 0.7f); + glLoadIdentity(); + glTranslatef(temp[0] - (worldWidth*length*autoScale)*0.5f + shadowOffset, + temp[1] + worldHeight*autoScale*0.5f - shadowOffset, + temp[2]); + glScalef(autoScale, autoScale, autoScale); + glRotatef(180.0, 1, 0, 0); + glBegin(GL_QUADS); + for (std::size_t j = 0; j < vertices.size(); j++) + { + glTexCoord2fv(UVs[j].data()); + glVertex3fv(vertices[j].data()); + } + glEnd(); + + // Main text pass + glColor4fv(textColor); + glLoadIdentity(); + glTranslatef(temp[0] - (worldWidth*length*autoScale)*0.5f, temp[1] + worldHeight*autoScale*0.5f, temp[2]); + glScalef(autoScale, autoScale, autoScale); + glRotatef(180.0, 1, 0, 0); glBegin(GL_QUADS); for (std::size_t j = 0; j < vertices.size(); j++) { @@ -295,9 +320,9 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons s_asciiTexture->unbind(); glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); glPopAttrib(); - glEnable(GL_LIGHTING); } From 71d1c8801c52ac88c1be6b44ed3abf502f38af60 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 24 Apr 2026 15:49:29 +0900 Subject: [PATCH 3/3] set an option to render with depth test (true by default) --- Sofa/GL/src/sofa/gl/glText.cpp | 7 +++++-- Sofa/GL/src/sofa/gl/glText.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Sofa/GL/src/sofa/gl/glText.cpp b/Sofa/GL/src/sofa/gl/glText.cpp index 9fd7bc3d5b1..c3cf65e2752 100644 --- a/Sofa/GL/src/sofa/gl/glText.cpp +++ b/Sofa/GL/src/sofa/gl/glText.cpp @@ -170,7 +170,7 @@ void GlText::textureDraw_Overlay(const char* text, const double scale) } -void GlText::textureDraw_Indices(const type::vector& positions, const float& scale) +void GlText::textureDraw_Indices(const type::vector& positions, const float& scale, bool enableDepthTest) { if (!s_asciiTexture) { @@ -210,7 +210,10 @@ void GlText::textureDraw_Indices(const type::vector& positions, cons glAlphaFunc(GL_GREATER, 0.0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); + + if(!enableDepthTest) + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); s_asciiTexture->bind(); diff --git a/Sofa/GL/src/sofa/gl/glText.h b/Sofa/GL/src/sofa/gl/glText.h index 970dfbc9fff..1628e6b9af3 100644 --- a/Sofa/GL/src/sofa/gl/glText.h +++ b/Sofa/GL/src/sofa/gl/glText.h @@ -75,7 +75,7 @@ class SOFA_GL_API GlText static void draw ( const T& text, const type::Vec3& position = type::Vec3(0.0,0.0,0.0), const double& scale = 1.0); static void textureDraw_Overlay(const char* text, const double scale = 1.0); - static void textureDraw_Indices(const type::vector& positions, const float& scale); + static void textureDraw_Indices(const type::vector& positions, const float& scale, bool enableDepthTest = true); private: static void initTexture();