From db5418e4850abae66717dcd405a8568b4e2a21d3 Mon Sep 17 00:00:00 2001 From: Jacob Williams Date: Sat, 6 Jun 2026 18:08:11 -0500 Subject: [PATCH 1/2] added setShader to CurveArtice and associated c/fortran interface --- include/OpenFrames/CurveArtist.hpp | 2 ++ include/OpenFrames/OF_Interface.h | 11 ++++++++ src/CurveArtist.cpp | 43 ++++++++++++++++++++++++++++++ src/OF_Interface.cpp | 13 +++++++++ src/OpenFrames.f90 | 5 ++++ src/TrajectoryArtist.cpp | 12 +++++++++ 6 files changed, 86 insertions(+) diff --git a/include/OpenFrames/CurveArtist.hpp b/include/OpenFrames/CurveArtist.hpp index 96be0d3..a82f5e1 100644 --- a/include/OpenFrames/CurveArtist.hpp +++ b/include/OpenFrames/CurveArtist.hpp @@ -67,6 +67,7 @@ namespace OpenFrames void setColor(float r, float g, float b); void setWidth(float width); void setPattern(GLint factor, GLushort pattern); + bool setShader(const std::string &fname); /** Data was cleared from or added to the trajectory. Inherited from TrajectoryArtist */ @@ -88,6 +89,7 @@ namespace OpenFrames osg::ref_ptr _lineWidth; osg::ref_ptr _linePattern; osg::ref_ptr _lineColors; + osg::ref_ptr _fragShader; // Line fragment shader mutable bool _dataValid; // If trajectory supports required data mutable bool _dataZero; // If we are just drawing at the origin diff --git a/include/OpenFrames/OF_Interface.h b/include/OpenFrames/OF_Interface.h index 83df94e..496e425 100644 --- a/include/OpenFrames/OF_Interface.h +++ b/include/OpenFrames/OF_Interface.h @@ -1768,6 +1768,17 @@ OF_EXPORT void OF_FCN(ofcurveartist_setwidth)(float *width); */ OF_EXPORT void OF_FCN(ofcurveartist_setpattern)(int *factor, unsigned short *pattern); +/* + * \brief Set GLSL fragment shader used to draw the curve, overriding any existing shader. + * + * If an empty string is provided, the shader is reset to default. + * + * This applies to the current active CurveArtist. + * + * \param fname File containing the shader source. + */ +OF_EXPORT void OF_FCN(ofcurveartist_setshader)(OF_CHARARG(fname)); + /***************************************************************** SegmentArtist Functions A SegmentArtist is a type of TrajectoryArtist that allows arbitrary diff --git a/src/CurveArtist.cpp b/src/CurveArtist.cpp index fc32ce3..4768c3d 100644 --- a/src/CurveArtist.cpp +++ b/src/CurveArtist.cpp @@ -21,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include namespace OpenFrames @@ -108,6 +112,10 @@ class CurveArtistUpdateCallback : public osg::Callback _vertexLow->dirty(); } + // Keep of_NumVertices uniform in sync so traveling-pulse shaders know the line length + osg::Uniform* numVertsUniform = _ca->getOrCreateStateSet()->getUniform("of_NumVertices"); + if(numVertsUniform) numVertsUniform->set((float)_drawArrays->getCount()); + // Continue traversing as needed return traverse(object, data); } @@ -205,6 +213,12 @@ CurveArtist::CurveArtist(const Trajectory *traj) stateset->setAttribute(_lineWidth.get()); stateset->setAttributeAndModes(_linePattern.get()); + // Initialize shader for custom line effects (optional, added to program on demand) + _fragShader = new osg::Shader(osg::Shader::FRAGMENT); + + // Uniform holding total vertex count; used by traveling-pulse shaders + stateset->addUniform(new osg::Uniform("of_NumVertices", 0.0f)); + // Initialize colors // Currently we use one color for the whole trajectory, but this can be // changed later for per-vertex colors @@ -296,6 +310,35 @@ void CurveArtist::setPattern( GLint factor, GLushort pattern ) _linePattern->setPattern(pattern); } +bool CurveArtist::setShader(const std::string &fname) +{ + // Remove shader if empty filename + if(fname.length() == 0) + { + _program->removeShader(_fragShader); + return true; + } + + // Load shader source from file using the non-deprecated osgDB API + osg::ref_ptr tmpShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, fname); + if(!tmpShader.valid()) + { + OSG_WARN << "OpenFrames::CurveArtist ERROR: Shader file \'" << fname << "\' not properly loaded!" << std::endl; + return false; + } + _fragShader->setShaderSource(tmpShader->getShaderSource()); + + // Re-attach shader to program if it was previously removed + bool attached = false; + for(unsigned int i = 0; i < _program->getNumShaders(); ++i) + { + if(_program->getShader(i) == _fragShader.get()) { attached = true; break; } + } + if(!attached) _program->addShader(_fragShader); + + return true; +} + void CurveArtist::dataCleared(const Trajectory* traj) { verifyData(); diff --git a/src/OF_Interface.cpp b/src/OF_Interface.cpp index 0bf183a..66f5a38 100644 --- a/src/OF_Interface.cpp +++ b/src/OF_Interface.cpp @@ -2549,6 +2549,19 @@ void OF_FCN(ofcurveartist_setpattern)(int *factor, unsigned short *pattern) } } +OF_EXPORT void OF_FCN(ofcurveartist_setshader)(OF_CHARARG(fname)) +{ + CurveArtist *artist = dynamic_cast(_objs->_currArtist); + if (artist) { + // Convert given character string and length to a proper C string + std::string temp(OF_STRING(fname)); + _objs->_intVal = !artist->setShader(temp); + } + else { + _objs->_intVal = -2; + } +} + /************************************************ SegmentArtist Functions ************************************************/ diff --git a/src/OpenFrames.f90 b/src/OpenFrames.f90 index e697dc8..0e3686c 100644 --- a/src/OpenFrames.f90 +++ b/src/OpenFrames.f90 @@ -902,6 +902,11 @@ SUBROUTINE ofcurveartist_setpattern(factor, pattern) INTEGER(2), INTENT(IN) :: pattern END SUBROUTINE + SUBROUTINE ofcurveartist_setshader(fname) + !DEC$ ATTRIBUTES DLLIMPORT,C,REFERENCE :: ofcurveartist_setshader + CHARACTER(LEN=*), INTENT(IN) :: fname + END SUBROUTINE + ! SegmentArtist functions SUBROUTINE ofsegmentartist_create(name) diff --git a/src/TrajectoryArtist.cpp b/src/TrajectoryArtist.cpp index 62761df..0d08cb5 100644 --- a/src/TrajectoryArtist.cpp +++ b/src/TrajectoryArtist.cpp @@ -27,6 +27,7 @@ namespace OpenFrames // Implement vertex shader for Rendering Relative to Eye using GPU static const char *OFTA_VertSource = { "#version 120\n" + "#extension GL_EXT_gpu_shader4 : enable\n" // Enables gl_VertexID "uniform mat4 osg_ProjectionMatrix;\n" // ModelView matrix with zero translation component @@ -40,6 +41,13 @@ static const char *OFTA_VertSource = { // High part comes in through gl_Vertex "attribute vec4 of_VertexLow;\n" + // Vertex location along line (0 or 1 for alternating vertices) + // Used by custom shaders like Line_Pulse.frag + "varying float vertexLocation;\n" + + // Raw vertex index, used with of_NumVertices uniform for full-line position + "varying float vertexRawID;\n" + "void main(void)\n" "{\n" // Low part of vertex - eye and associated numerical error @@ -57,6 +65,10 @@ static const char *OFTA_VertSource = { " gl_Position = osg_ProjectionMatrix*of_RTEModelViewMatrix*vec4(diffHigh+diffLow, 1.0);\n" " gl_FrontColor = gl_Color;\n" " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + + // Compute vertex location for line shaders (0 or 1 for alternating vertices) + " vertexLocation = mod(float(gl_VertexID), 2.0);\n" + " vertexRawID = float(gl_VertexID);\n" "}\n" }; From 76e2072fb080d0bf41f54f9677b21859f528fd44 Mon Sep 17 00:00:00 2001 From: Jacob Williams Date: Sat, 6 Jun 2026 18:16:10 -0500 Subject: [PATCH 2/2] added two example line pulse shaders --- Demos/Shaders/Line_Pulse_Thickness.frag | 29 +++++++++++++++++++++++ Demos/Shaders/Line_Pulse_Traveling.frag | 31 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 Demos/Shaders/Line_Pulse_Thickness.frag create mode 100644 Demos/Shaders/Line_Pulse_Traveling.frag diff --git a/Demos/Shaders/Line_Pulse_Thickness.frag b/Demos/Shaders/Line_Pulse_Thickness.frag new file mode 100644 index 0000000..e2efadd --- /dev/null +++ b/Demos/Shaders/Line_Pulse_Thickness.frag @@ -0,0 +1,29 @@ +// FRAGMENT SHADER +// Creates a pulsing intensity effect that simulates line thickness variation + +// Use GLSL 1.20 (OpenGL 2.1) +#version 120 + +uniform float osg_FrameTime; // From OSG +varying float vertexLocation; // From vertex shader (alternates 0/1 for line endpoints) + +const float pulseSpeed = 2.0; // Speed of pulsing (higher = faster) +const float minIntensity = 0.4; // Minimum brightness (0-1) +const float maxIntensity = 1.0; // Maximum brightness (0-1) + +void main(void) +{ + vec4 baseColor = gl_Color; + + // Create smooth pulsing wave using sine + float pulse = sin(osg_FrameTime * pulseSpeed) * 0.5 + 0.5; // 0 to 1 + + // Scale pulse between min and max intensity + float intensity = mix(minIntensity, maxIntensity, pulse); + + // Apply intensity to color and alpha for a "glowing" pulse effect + vec4 finalColor = baseColor * intensity; + finalColor.a = baseColor.a * intensity; // Also pulse the alpha + + gl_FragColor = finalColor; +} diff --git a/Demos/Shaders/Line_Pulse_Traveling.frag b/Demos/Shaders/Line_Pulse_Traveling.frag new file mode 100644 index 0000000..12727f1 --- /dev/null +++ b/Demos/Shaders/Line_Pulse_Traveling.frag @@ -0,0 +1,31 @@ +// FRAGMENT SHADER +// Draws a pulse that travels from the start of the trajectory to the end + +// Use GLSL 1.20 (OpenGL 2.1) +#version 120 + +uniform float osg_FrameTime; // From OSG +uniform float of_NumVertices; // Total vertex count, set by CurveArtist +varying float vertexRawID; // Raw vertex index from vertex shader + +const float pulseDuration = 2.0; // Seconds for one full pass along the line +const float pulseWidth = 0.05; // Width of pulse as fraction of total line length + +void main(void) +{ + vec4 color = gl_Color; + vec4 colorInv = vec4(1.0 - gl_Color.rgb, 1.0); + + // Normalize vertex index to [0, 1] over the full line + float linePos = vertexRawID / max(of_NumVertices - 1.0, 1.0); + + // Pulse center travels from 0 (start) to 1 (end) over pulseDuration seconds + float pulseCenter = fract(osg_FrameTime / pulseDuration); + + // Create a smooth 0-1-0 bump centered at the pulse location + float y1 = smoothstep(pulseCenter - pulseWidth, pulseCenter, linePos); + float y2 = smoothstep(pulseCenter, pulseCenter + pulseWidth, linePos); + float pulseVal = y1 - y2; + + gl_FragColor = mix(color, colorInv, pulseVal); +}