osgx core
August 25, 2026 · View on GitHub
Reference for the always-available, non-namespaced parts of osgx.hpp — the classes and helpers
that live directly in osgx::, one section per header. For the opt-in, explicitly-#included
subsystems (each with its own C++ namespace), see DEBUG.md, IMGUI.md,
PLATFORM.md, and GLTF.md instead.
Note
Most types below are built via a static Type::create(...) rather than osgx::make_ref<Type>(...).
make_ref is for a plain constructor call; create() is for a factory that assembles several
real OSG objects — a camera, its FBO attachments, a depth-only Program, an SSBO and its
uniforms — and hands back the finished bundle as one struct, work no single constructor could do.
Every osgx-owned factory converges on this one call shape (create(), or a distinctly-named verb
like ::load()/::prepare() only when the operation genuinely isn't "build one" — see
PBRIBLEnvironment in GLTF.md) rather than a taxonomy of create*/make* free
functions — one predictable shape to remember beats memorizing which verb applies where. A
function returning a type osgx doesn't own (osg::Camera, osg::TextureCubeMap, ...) stays a
free function, since there's no class to hang a static method on.
osgx/Core.hpp
OSGX_DISABLE_WARNINGS/OSGX_ENABLE_WARNINGSsilence noisy OSG headers with compiler-specific diagnostic push/pop macros (__clang__branch first, then__GNUC__, then a no-op fallback).ObjectPath— astd::list<std::string>dotted-path accumulator (.str()joins with.), used byDescribeSceneVisitorto track scene-graph name hierarchies.vec_t(osg::Vec3::value_type) and the_v/_sz/_zliterals provide compact OSG scalar andstd::size_tliterals.OSGReferenced,OSGArray, andOSGDrawElementsare concepts that constrain helpers to the expected OSG base types.make_ref<T>(args...)/make_ref<T>(nullptr)andmake_nref<T>(name, args...)createosg::ref_ptrobjects, optionally naming the object immediately.tickandcall(func, args...)provide lightweightosg::Timer-based timing around arbitrary callables, returningstd::pair<optional<Result>, delta_ticks>.getFirstParent<T>()walks an OSG parent chain and returns the first parent matching a requested type.ring_buffer<T, N>andaring_buffer<T, N>keep fixed-size recent samples, with the arithmetic version adding.average()(all valid samples) and.average(count)(last N).RING_BUFFER_T(T, N)exposes protected members in subclasses viausing.findDataFile()wraps theosgDBfile utils, letting you specify multiple paths/suffixes in one call.
osgx/Visitors.hpp
LambdaVisitor<Node>—NodeVisitorwrappingstd::function<void(Node&)>.IndexedVisitor— tracks traversal depth in_i; useitraverse()instead oftraverse().NameVisitor— auto-assigns$ClassName_Nnames to unnamed nodes (CLASS/PATH/FORCEoptions).DescribeSceneVisitor— prints the scene tree to stdout with indentation and dot-path tracking.VisitorEventHandler<Visitor, Viewer>— runs a visitor against the scene root when a key is pressed.LambdaKeyHandler— wires one or multiple keys to a lambda(ea, aa[, key]) -> bool.FilterNotifyHandler— anosg::NotifyHandlerthat suppresses known-noisy OSG messages by regex (draw()/cull() spam, BufferObject release messages) before writing the rest to stderr.
osgx/Array.hpp
Array<BaseArray>wrapsosg::Vec2Array/Vec3Array/Vec4Array/FloatArray(aliased directly under those names inosgx::) with:- Constructors from initializer-list, an input range, variadic args (each convertible to the element type), or — since 2026-08-18 — an element count (
explicit Array(std::size_t count), preallocates default-constructed elements, matchingBaseArray's own sized constructor). Forosgx::FloatArrayspecifically, a bareint/unsignedliteral still prefers the variadic single-element constructor over the sized one (exact-match template deduction beats theint→size_tconversion) — pass an actualstd::size_t(e.g. the_szUDL) to select the sized constructor unambiguously there. Non-arithmetic element types (Vec2/3/4Array) have no such ambiguity. append_range(),append_n<N>()(compile-time count),append_n(value, n)(runtime count).span()/span(start, count)—std::spanviews (mutable or const).view()/view(start, count)—std::ranges::subrangeviews.static create(...)factory returningosg::ref_ptr<Array>.- Fully interchangeable with native
osg::*Array— same layout, same serialization,dynamic_cast/static_castcompatible in both directions (seeexamples/osgx-array.cpp).
- Constructors from initializer-list, an input range, variadic args (each convertible to the element type), or — since 2026-08-18 — an element count (
DrawElements<BaseElements>wrapsosg::DrawElementsU{Byte,Short,Int}(aliased asDrawElementsUByte/UShort/UInt) with:- Constructors from
GLenum mode+ initializer-list / range / variadic. append(),append_range(),checked_push()(bounds-checked, throws on overflow).- Static factories:
triangles(),lines(),strip(),fan(). span(),view(),static create(...).
- Constructors from
osgx/Callbacks.hpp
CallbacksGroup<Callback>— composite callback that fans out to multiple registered callbacks, in list order, side by side (not chained viaCallback::setNestedCallback). Aliased asCameraDrawCallbacksGroup,NodeCallbacksGroup,DrawableDrawCallbacksGroup.add(cb)/remove(cb)— identity-based.size()/get(i)/set(i, cb)/removeAt(i)/insert(i, cb)— index-based, added so bindings can expose a real sequence proxy instead ofpy::dynamic_attr().
LambdaCallbackBase<Callback, Fn>+ concreteCameraDrawLambdaCallback/NodeLambdaCallback— adapt a lambda to the matching OSG callback type.WriteTextureCallback— aCamera::DrawCallbackthat asynchronously writes a texture to disk on demand (.write(filename), atomic-bool-flag triggered).
osgx/Picking.hpp
Texture-based object-ID picking: an RTT camera renders a flat "pick ID" shader (1-based; 0 = background), encoded as 32-bit RGBA.
makePickCamera(w, h, image*)/makePickCamera(w, h, Texture2D*)— assembles the pick camera (shader,BlendFunc, small-feature culling disabled,ABSOLUTE_RF).decodePickID(px)— decodes all 4 RGBA bytes into a 32-bit ID.PickRule(std::function<uint32_t(const uint8_t*, int)>) —spiralPick(default),pickCenter,pickMostCoverage,pickNearestToCenter.PickReadback— shared atomic state (onPick/onEnter/onLeave, mouse position, last ID);PickReadbackSync/PickReadbackAsyncare the SYNC (osg::Imagereadback) and ASYNC (Texture2D+ PBO +glGetTexImage) variants, each withMode::CLICK/Mode::CONTINUOUS.PickCameraSync— syncs the pick camera's view/projection from the viewer camera every update traversal (with an optional 1×1 sub-frustum for continuous hover).PickHoverCallback— pollslastID()on the update thread and firesonEnter/onLeaveon transitions; the correct way to trigger scene-graph mutation from a hover event.PickHandler— routes click/move events to the readback (continuous=truefor hover,consumeEvents=truefor exclusive picking).
See examples/osgx-picking.cpp (full SYNC/ASYNC × click/continuous matrix) and examples/osgx-hover.cpp (onEnter/onLeave driving real scene mutation).
osgx/Manipulators.hpp
MultiCameraManipulator— composite manipulator routing input to one activeTarget(name, manipulator, optional dedicated camera/scene, optionalsetActivecallback);addTarget(),activate(index)/next(),getActiveIndex()/getNumTargets(), key-toggle viasetToggleKey()(default'x').Ortho2DManipulator— orthographic 2D camera manipulator owning both view and projection matrices. Pan (drag), geometric zoom (scroll), pixel-nudge zoom (Shift+scroll), optional Ctrl-drag 3D tilt (yaw/pitch tracked as independent angles, pitch clamped to ±89°), automatic near/far. Unrotated plane configurable viasetPlaneNormal()/setScreenUp()(default XY, +Z normal).OrbitAxisManipulator— a "turntable" manipulator: orbits a fixed vertical guide line through the model's bounds, always looking level, dollying on zoom. Mouse move/drag is always active (no button needed), bounded like a trackpad by the screen edge unless composed withosgx::platform::PointerCaptureviaorbitByDelta()/setLiveOrbitEnabled(false). Orientation configurable viasetUpAxis()/setHomeDirection()(default Z-up, from -Y).CameraManipulator<Base=osgGA::TrackballManipulator>— wraps anyosgGA::CameraManipulatorwithaddUpdateCameraCallback(osg::Callback*, runOnce)/removeUpdateCameraCallback(), an async-apply queue (safe to mutate mid-callback-iteration), andcurrentTime()sourced from the FRAME event (not a polledosg::Timer) forosgx::CameraIntentsto read.
osgx/CameraIntents.hpp
Plain osg::Callback subclasses meant to be attached via CameraManipulator<Base>::addUpdateCameraCallback(), driven by real osgAnimation::Motion/CompositeMotion timing rather than hand-rolled elapsed/duration math.
Viewpoint—{eye, center, up}(updefaults to+Z).FlyToCallback— animates the camera through one or moreViewpointlegs (eye lerped, orientation slerped — never lerping twolookAt()centers directly).osgAnimation::Motion::CLAMP(default): on arrival, writes the exact final pose, resyncs the manipulator viasetByMatrix(), then goes permanently inert.Motion::LOOP: the whole path repeats forever (author a waypoint list whose ends coincide for a seamless loop — same convention asosg::AnimationPath).easeis anyfloat(float)callable (defaultEase=InOutCubicFunction), shared across every leg.ShakeCallback— decaying rotational jitter, right-multiplied onto whatever's already in the camera's view matrix (never touches the manipulator's own state).CLAMP(default) decays once and goes inert;LOOPrepeats for a persistent "idle rumble."
See examples/osgx-manipulator.cpp for patrol (LOOP) and arrival-latch usage, and docs/PLATFORM.md for composing OrbitAxisManipulator with PointerCapture.
osgx/Grid.hpp
Griddraws a procedurally generated, antialiased grid as either a screen-space overlay or a perspective ground plane. The shader is adapted from Ben Golus's The Best Darn Grid Shader (Yet) (credited insrc/Grid.cpp/src/osgx/Grid.hppas well).
osgx/Shapes.hpp
VertexLayout— the generic-attribute locations (position/normal/uv) a generatedPolyhedroninstalls, both through Geometry's conventional arrays (bounds/compatibility) and as explicit generic attributes (core-profile shaders).Polyhedron— anosg::Geometrybuilt fromvertices+Facelist (each face: vertex indices + optional per-corner UVs).rebuild()mutates the existing backing arrays in place rather than replacing them (same "don't reallocate every frame" lessonosgx::LightMarkersreuses). Per-face custom attributes viasetFaceVertexAttribute()/setFaceAttribute()/removeAttribute();faceNormal()/faceUp()/restingOffset()/faceRestingOffset()for placement queries;isometricFaceUV()static helper.Cube,Tetrahedron,Octahedron,Icosahedron,Dodecahedron,PentagonalTrapezohedron— concretePolyhedronsubclasses, each constructible from(center, radius, layout)(Cubealso takes(center, size, layout)).
osgx/Shader.hpp
Generic, line-oriented GLSL library expansion — a reusable snippet catalog rather than a
project-specific search-and-replace pass. Register one or more catalogs (osgx::registerShaderLibs()),
then expand #pragma directives in shader source via osgx::resolveShaderLibs(). Registered
namespace/library names are case-insensitive; a pragma accepts comma-separated library names,
optional GLSL-function aliases, and * to expand an entire catalog in registration order. See the
worked example in the main README.
This header also holds Hook/HookList/applyHooks() — the shader-object substitution
counterpart to the text-splicing above. A Program-building call site (e.g. osgx::gltf::pbribl:: PBRIBLScene::create(), see GLTF.md) declares which Hook slots it supports as a
defaults HookList; a caller overrides only the slots it cares about via its own HookList,
via one shared enum/mechanism instead of each call site growing its own osg::Shader* someHook=nullptr parameter. applyHooks() guarantees exactly one shader ends up attached per
supported slot, always — never zero, never two (GLSL permits one body per function, so an override
substitutes the built-in rather than competing with it).
osgx/PBR.hpp
Reusable BRDF GLSL snippets and typed direct lights, living flat in osgx:: (not its own
namespace — registerPBRShaderLibs()'s "osgx::pbr" catalog tag is a conventional shader-lib key
only, unrelated to the C++ namespace; see Namespaces below).
- GLSL snippets — GGX distribution, Schlick Fresnel, Smith geometry — plain function-body
snippets, concatenated into a consuming fragment shader via
registerPBRShaderLibs()/resolveShaderLibs(), not full shaders of their own. LightSet— aStateAttributeowning astd430-SSBO-backed array of typed direct lights (LightType::Point/Directional/Spot; a sphere light is aPoint/Spotwith non-zerosourceRadius, not a fourth type) and itsosgx_lightCountuniform. Construct it, then attach it throughStateSet::setAttributeAndModes()(sizeMAX_LIGHTS, zero-initialized, everything off untilsetCount()+setPoint()/setDirectional()/setSpot()). Itsapply()binds the SSBO and forwards the owned count uniform through OSG's shader-composition uniform path, so callers cannot desynchronize the two. Each slot also has anenabledflag, sosetEnabled()can toggle a configured light without changing its count or packed data. Typed setters/getters (getType(),getPosIntensity(),getColor(),getSourceRadius(),getDirection(),getSpotAngles(), …) replace the old parallel-osg::Uniform-array contract.OrbitLightRig— the animated counterpart: anosg::NodeCallbackthat writes orbiting position/intensity into aLightSetevery update traversal (for the subset of lights that should move; aLightSetcan be shared between a static rig and an orbiting one).
osgx/Gizmos.hpp
Debug visualization for LightSet lights — deliberately not part of osgx::debug
(that's GL_KHR_debug integration specifically, not scene gizmos).
LightMarkers— anosg::Groupof depth-tested, real scene-space markers for point/sphere/spot lights (up toMAX_LIGHTS), rebuilt in place every update traversal from the liveLightSet. Three orthogonal wireframe circles for a point/sphere light (sized tomax(sourceRadius, minMarkerRadius)); a wireframe cone (ring + spokes) for a spot light, sized by its outer cone angle andspotConeLength. A directional light has no position and is never drawn here.LightGizmos— bundlesLightMarkerswith a non-depth-testedPOST_RENDERoverlay camera for directional lights (a wireframe plane + direction arrow, sized off the target scene's bounding sphere) into one addableosg::Group:auto gizmos = osgx::make_ref<osgx::LightGizmos>(lights, scene, minMarkerRadius, spotConeLength); root->addChild(gizmos);getMarkers()/getOverlay()give access to the two pieces individually, for the (rarer) case where they need different parents in the scene graph.
See examples/osgx-lights.cpp for one shaded object cycling through every light type with live gizmo feedback.
osgx/IBL.hpp, CaptureCubeMap.hpp, GGXPrefilter.hpp, LambertianBake.hpp
Reusable IBL GLSL snippets (registerIBLShaderLibs()'s "osgx::ibl" catalog tag, same
flat-namespace/conventional-tag split as PBR.hpp above) plus environment-map loading, BRDF-LUT
baking, SH9/Lambertian diffuse irradiance, and cubemap readback helpers.
SharedBRDFLUT::create(lutSize)— the process-wide BRDF-LUT cache. Deliberately namedcreate()despite sometimes returning an existing cached LUT rather than baking a fresh one — see its own doc comment for the cache-or-create contract.readCubeMapFaces()/BRDFLUTReadback— CPU readback helpers for a baked cubemap/LUT.CaptureCubeMap.hpp—CaptureCubeMapScene, the low-level frame-driven reflection-probe primitive (six ordered FBO cameras capturing a caller-owned scene into a radiance cubemap).CaptureCubeMapScene::create()/::recapture().GGXPrefilter.hpp— GPU GGX prefilter scene construction, rebaking, and readback (GGXPrefilterScene::create()/::rebake(),GGXPrefilterReadback::finish()).LambertianBake.hpp— frame-driven GPU Lambertian/diffuse cubemap baking and readback (LambertianBakeScene::create()/::rebake(),LambertianCubeReadback::finish()).
glTF-specific material and rendering integration lives with the loader in osgx::gltf —
generic osgx does not depend on or duplicate its public shader interface.
osgx/Shadow.hpp
Directional shadow mapping, shared by any LightSet-lit scene (nothing here is glTF/PBR-specific;
osgx::gltf::pbribl consumes it as an optional parameter — see GLTF.md). Lives flat in
osgx:: — registerShadowShaderLibs()'s "osgx::shadow" catalog tag is a conventional shader-lib
key only, same split as PBR.hpp/IBL.hpp above.
Only ONE light — the key/directional light — is ever shadowed; point/spot-light shadows need a cubemap and meaningfully different frustum math, and remain a separate, unimplemented feature (see TODO.md's Shadow section).
ShadowMapOptions—size(shadow-map resolution),extent(half-width of the orthographic frustum's box;0derives it fromsceneBoundRadius * margin),margin(scales both the derivedextentand near/far — keeps near:far bounded to a healthy ratio regardless of scene scale, avoiding depth-precision collapse on a large scene),bias,strength(0= no effect,1= fully black).ShadowMap— owns thePRE_RENDERdepth-onlycamera(add it to the scene graph) plus the uniformsDIRECT_LIGHTING_HOOK_SHADOWEDreads every frame (shadowMatrix,bias,strength,casterIndex— whichLightSetindex this shadow is cast by/matched against, default0).ShadowMap::create(lightDirection, sceneBoundCenter, sceneBoundRadius, options={})— builds an orthographic depth-only camera (the physically-correct frustum shape for a directional, parallel-ray light) with its own minimal depth-onlyProgram(ON|OVERRIDE) — a caster's own, potentially expensive, main-renderProgramnever runs during the shadow pass. Not glTF-alpha-mask aware by design; a caller needing alpha-cutout shadows overrides the Program on that geometry's own StateSet.updateMatrix()— recomputesshadowMatrixfromlightView/lightProjafter mutating either directly.reposition(lightDirection, sceneBoundCenter, sceneBoundRadius, options={})— repositions an existingShadowMapin place (no new camera/FBO/depth-texture allocation), cheap enough to call every frame for an interactively-moving light (e.g. an ImGui-dragged direction).create()remains the right call for a light fixed at scene-build time.
DIRECT_LIGHTING_HOOK_SHADOWED— a drop-in replacement forPBR.hpp'sDIRECT_LIGHTING_HOOK_DEFAULT: identical per-light dispatch loop, except the light atosgx_shadowCasterIndexhas its contribution multiplied byosgx_ShadowFactor()(world-space PCF 3×3 shadow test). Both defineosgx_DirectLighting()with the same signature, so swapping hooks is the only shader change needed — seeexamples/osgx-shadow.cppfor the full A/B wiring (presssto toggle).
See examples/osgx-shadow.cpp (standalone LightSet + ShadowMap proof, live-draggable light
direction) and examples/osgx-gbuffer.cpp (the same shadow map plugged into the deferred pipeline
below).
osgx/GBuffer.hpp
Generic deferred G-buffer camera setup — not PBR/glTF-specific. osgx::gltf::pbribl's own deferred
split (PBRIBLGBuffer::create(), see GLTF.md) is built on top of this, not a separate
mechanism, and a non-PBR deferred shader can use it directly too. Lives flat in osgx::, same
reasoning as Shadow.hpp above.
-
AttachmentFormat— texture internal-format presets for one G-buffer color attachment:RGBA8(ordinary LDR color/albedo),RGB16F(signed[-1,1]data, e.g. a view-space normal, needing no encode/decode remap),RGBA16F(HDR color, e.g. emissive, which can exceed1.0before tonemapping),RGBA32F(real eye-space position, written straight from the vertex shader rather than reconstructed from depth — seePBRIBLGBuffer::positionTexture's note in GLTF.md for why that reconstruction is unreliable across nestedPRE_RENDERcameras). -
GBuffer—camerais thePRE_RENDERFBO pass that writescolorTextures(indexed exactly as passed tocreate()) plusdepthTexture. The caller still owns addingcamerato the scene graph.GBuffer::create(node, width, height, colorFormats, referenceFrame=RELATIVE_RF)—nodeis a real 3D scene subgraph (a geometry pass, not a fullscreen quad), writingcolorFormats.size()simultaneous color attachments (COLOR_BUFFER0..N, requiringlayout(location = n) outdeclarations in the caller's fragment shader) plus a realGL_DEPTH_COMPONENT24depth attachment.referenceFramedefaults toRELATIVE_RF(composes withnode's own ancestor transforms/camera); passABSOLUTE_RFfor a camera that should own its own fixed view/projection instead (ShadowMap::create()builds its own camera directly rather than going through this helper, since it's depth-only with no color attachments at all).
-
SSAO— hemisphere-kernel screen-space ambient occlusion, reading any G-buffer's view-space normal + position channels directly and nothing else (glTF-material-shaped, hand-authored, or otherwise). Ported fromOpenSceneGraph.py/examples/pyosg-lighting/11-sketchfab.py's proven-live implementation: a 16-sample hemisphere kernel + a small tiled tangent-space noise-rotation texture, a raw RTT pass, then a small box-blur RTT pass denoising it.radius/biasare liveosg::Uniforms — set them at any time, no pass rebuild needed.aoTexture(the blurred, single-channelGL_R8result) plugs directly intoPBRIBLLightingPassOptions::aoTexture(GLTF.md) or any other consumer wanting a generic occlusion mask.SSAO::create(normalTexture, positionTexture, projectionMatrix, width, height, radius=0.5, bias=0.02)—projectionMatrixis a caller-owned uniform this pass reads every draw; keep it refreshed from the same per-frame callback that updatesPBRIBLLightingScene's own view-matrix uniforms (see that type's own doc comment, GLTF.md, for why it must be aPRE_RENDERpreDrawCallback).radius/biasare scale-dependent (a sane radius is a small fraction of the scene's own bounding radius, not a fixed constant) — compute them from the scene being rendered.
See examples/osgx-gbuffer.cpp for the full deferred pipeline (PBRIBLGBuffer +
PBRIBLLightingScene, both built on this), live SSAO wired into PBRIBLLightingPassOptions::aoTexture
with live ImGui radius/bias sliders, and a channel-by-channel G-buffer visualizer (press 0-6,
6 being SSAO's own output). Python: osgx.GBuffer/osgx.SSAO.
Namespaces
osgx::pbr, osgx::ibl, osgx::shadow, and osgx::gbuffer used to exist as separate C++
namespaces; as of 2026-08-20 every symbol they held lives directly under osgx::. The rule: a
namespace exists only for a genuinely separate opt-in subsystem — its own #include outside the
osgx.hpp umbrella AND its own CMake link target (exactly debug/imgui/platform/gltf (+ its
own gltf::pbribl sub-target)/ktx2). Everything that compiles unconditionally into libosgx
stays flat, no matter how "topic-shaped" it feels — this is also why picking/grid/
manipulators/shadow/gbuffer never got their own namespace. The "osgx::pbr"/"osgx::ibl"/
"osgx::shadow" strings passed to registerPBRShaderLibs()/registerIBLShaderLibs()/
registerShadowShaderLibs() are shader-lib registry catalog tags only — conventional, #pragma-
addressable names, unrelated to the (now-flat) C++ namespace.
osgx/Version.hpp
OSGX_VERSION_MAJOR/MINOR/PATCH and the OSGX_VERSION string, generated from the CMake
project version. Included by Core.hpp, so it's available transitively almost everywhere.