3D Engine
The OpenRocket 3D engine lives under info.openrocket.swing.gui.figure3d in the swing module.
The interactive 3D design view and Photo Studio share the same OpenGL host, scene graph, geometry code,
and renderer.
Overview
The main data and rendering flow is:
RocketFigure3d / PhotoPanel
-> SharedCanvasRenderScheduler
-> GLScenePanel (lwjgl3-awt AWTGLCanvas)
-> Scene3DOrchestrator
-> Scene + controllers
-> RocketSceneSynchronizer
model thread: RocketMeshBuilder.buildSnapshot()
-> immutable RocketSceneSnapshot
GL thread: RocketMeshBuilder.prepareSnapshot()
-> PreparedSnapshot.commitTo(SceneView)
-> SceneObject and GPU resources
-> RealisticRenderer
-> resolved scene texture
-> AWT default framebuffer -> HUD -> buffer swap
The model and Swing controls do not make OpenGL calls. Context-owned work is queued through the orchestrator and executed while the canvas context is current on the shared render thread.
Entry Points
The main entry points are:
swing/src/main/java/info/openrocket/swing/gui/figure3d/RocketFigure3d.javaswing/src/main/java/info/openrocket/swing/gui/figure3d/photo/PhotoPanel.javaswing/src/main/java/info/openrocket/swing/gui/figure3d/photo/PhotoFrame.javaswing/src/main/java/info/openrocket/swing/gui/figure3d/ui/GLScenePanel.java
RocketFigure3dEmbeds a 3D canvas in the design window. It handles selection, the HUD, zoom state, and the three user-facing display modes. The UI’s Figure mode maps to
DisplaySettings.RenderMode.XRAY; the other modes map toUNFINISHEDandFINISHED.PhotoPanel/PhotoFrameHost Photo Studio. They configure the shared engine for finished rendering, Photo Studio camera controls, backgrounds, lighting, motion blur, and exhaust effects.
GLScenePanelSubclasses lwjgl3-awt’s
AWTGLCanvas. Each panel owns one OpenGL context, its context-local resources, and aScene3DOrchestrator.
GL Host and Render Scheduling
GLScenePanel requests the highest supported double-buffered OpenGL context that is at least
version 3.1 through lwjgl3-awt. OpenGL 3.1 has no core/compatibility profile selector, so the profile is
left unspecified. Shader attribute and output locations are bound before program linking instead of
depending on GLSL 3.30 syntax. The requested and effective GLData are recorded by
GLContextDiagnostics during initialization. The AWT default framebuffer is deliberately
single-sampled; scene MSAA is implemented by RealisticRenderer in its own off-screen render target
and resolved before presentation.
SharedCanvasRenderScheduler serializes every active AWTGLCanvas onto one background thread named
figure3d-render. This avoids concurrent JAWT rendering across design windows and Photo Studio. Both
views render only after they have been marked dirty by input, settings, model changes, resize, lifecycle,
or export activity. Photo Studio’s exhaust effects are frozen snapshots, so an idle panel does not need
a render loop.
Important lifecycle rules are:
Add and remove canvases on the Swing event dispatch thread.
Create and resize GL resources only while the owning context is current.
GLScenePanel.disposeGLreleases them during lwjgl3-awt’s context-current canvas-disposal callback.Keep GPU object caches scoped to a canvas or renderer; contexts do not share object identifiers.
Restore the canvas’s LWJGL capabilities before rendering because capabilities are thread-local and the shared scheduler switches contexts between canvases.
Treat the window size and the native framebuffer size as separate values, especially on HiDPI displays.
On Windows, GLScenePanel first requests a robust context so it can detect graphics resets and ask its
wrapper to rebuild the canvas. Because WGL robustness is optional, context creation retries once without
those attributes when the robust attempt fails. Startup redraw recovery handles cases where a valid
off-screen frame has not yet appeared in the AWT framebuffer.
Scene Orchestration and Threading
The orchestration layer is centered on:
swing/src/main/java/info/openrocket/swing/gui/figure3d/scene/orchestration/Scene3DOrchestrator.javaswing/src/main/java/info/openrocket/swing/gui/figure3d/scene/orchestration/RocketSceneSynchronizer.javaswing/src/main/java/info/openrocket/swing/gui/figure3d/scene/graph/Scene.java
Scene3DOrchestrator owns the runtime Scene, camera and input controllers, renderer, viewport,
decal cache, and the queue for work that requires a current GL context. Higher-level code uses it to
change the camera, update rendering configuration, rebuild the rocket, and request image export.
RocketSceneSynchronizer listens for component changes and chooses the least expensive safe update:
Appearance-only changes are coalesced and update existing appearances on the GL thread.
Structural, geometry, visibility, and selected-configuration changes coalesce at the tail of the Swing event queue and build the newest
RocketSceneSnapshotthere. A queued GL task then replaces the rocket-derived scene objects and particle emitters.
RocketMeshBuilder.buildSnapshot is CPU-only. RocketMeshBuilder.prepareSnapshot creates
SceneObject instances, appearances, textures, motors, particle emitters, and other context-owned
resources without changing the live scene. The orchestrator commits that prepared replacement only
after all allocations succeed. Keeping these phases separate prevents the render thread from reading a
rocket while it is being edited and preserves the old scene if resource preparation fails.
Geometry and Materials
Rocket geometry is generated by:
swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/RocketMeshBuilder.javathe generators under
swing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/basic/andswing/src/main/java/info/openrocket/swing/gui/figure3d/geometry/components/swing/src/main/java/info/openrocket/swing/gui/figure3d/materials/AppearanceFactory.java
RocketMeshBuilder traverses the active and extra render instances in the selected
FlightConfiguration, chooses a generator for each component, applies instance transforms, and adds
motors and their optional particle-emitter plans.
AppearanceFactory converts the core appearance model into Appearance3D data, including color,
opacity, decals, and textures. Mesh shape or placement problems generally start in RocketMeshBuilder
or a component generator; material, texture, or transparency problems generally start in
AppearanceFactory, TransparencyPolicy, the material binder, or the shaders.
Rendering Configuration
swing/src/main/java/info/openrocket/swing/gui/figure3d/scene/properties/RenderingConfiguration.java
groups the mutable runtime settings:
DisplaySettingsDisplay mode, x-ray opacity, and internal-surface visibility.
GraphicsQualitySettingsOverall quality, MSAA, FXAA, shadows, ambient occlusion, surface roughness, culling, and the option to reduce expensive effects during interaction.
VisualEffectsSettingsCarets and helper markers, particle effects, motion blur, rocket-drag behavior, and related controls.
Application and document preferences are mapped onto this configuration by Figure3DPreferences.
See Graphics for the user-facing defaults.
Render Pipeline
The main renderer is
swing/src/main/java/info/openrocket/swing/gui/figure3d/rendering/RealisticRenderer.java. A normal
display frame proceeds as follows:
Update the camera, fixed GL state, and flame-driven dynamic lights.
Render the shadow map when shadows are enabled.
Clear the main off-screen target, render the background, and render opaque geometry.
Resolve opaque MSAA into the single-sample target.
Render translucent geometry with weighted blended order-independent transparency and composite it over the opaque result.
Render sparks, smoke, flames, CG/CP carets, and the camera point-of-interest marker as enabled.
Apply ambient occlusion, motion blur, outlines, and FXAA as configured.
Copy the final post-processed image back into the renderer’s resolved target when necessary.
Let
GLScenePanelpresent the resolved texture to the AWT default framebuffer, draw the HUD, and swap buffers.
When interaction-effect reduction is active, shadows, ambient occlusion, motion blur, and outlines are skipped while the user is dragging, scrolling, or resizing. Image export reads the resolved scene before the GPU HUD is drawn. Design-view captures then composite the same HUD through Java2D; Photo Studio captures intentionally contain only the scene.
Render-pass implementations live under
swing/src/main/java/info/openrocket/swing/gui/figure3d/rendering/passes/. Important passes and
helpers include ShadowPass, BackgroundPass, GeometryPass,
WeightedBlendedTransparency, CaretsPass, CameraPointOfInterestPass,
AmbientOcclusionPass, MotionBlurPass, OutlinePass, and FXAAPass. Shaders are stored under
swing/src/main/resources/shaders/.
Design View and Photo Studio
The design view adds selection and picking, CG/CP carets, an AWT-rendered HUD, and figure/unfinished/ finished display modes. It uses demand-driven rendering so inactive design windows do not consume a continuous share of the render thread.
Photo Studio uses the same scene and renderer in finished mode, but supplies its own camera semantics, background and light settings, and optional flame, smoke, spark, and motion-blur effects. Those effects are deterministic snapshots; input and settings changes mark the panel dirty and schedule a fresh frame.
Multi-Window and Platform Notes
Every canvas has an independent context and resource lifetime, but all canvases render through the shared scheduler. Code that adds GL state or caching must therefore be both context-local and safe when the next scheduled frame belongs to another canvas.
On Linux, lwjgl3-awt follows LWJGL’s context selection: X11 sessions use GLX, while Wayland sessions use EGL with the X11 window supplied by AWT under XWayland. This is not a native Wayland surface. OpenRocket leaves the Linux swap interval unspecified so GLX implementations without EXT or MESA swap control can still create a context; other platforms request a zero swap interval to avoid serializing the shared render thread on vertical refresh. The macOS backend does not accept every optional context attribute, so the host does not request a debug context or an sRGB default framebuffer there. Robust context reset detection is requested only on supported Windows configurations. Raspberry Pi 5’s Mesa V3D driver exposes desktop OpenGL 3.1, so it can use the normal renderer without pretending to support OpenGL 3.3 or routing desktop GL through Zink.
Package Map
The tracked packages under info.openrocket.swing.gui.figure3d are:
figure3d
├── animation # flight pose and playback helpers
├── constants # camera and rendering constants
├── geometry
│ ├── basic # reusable primitive generators
│ └── components # rocket-component mesh generators
├── input # keyboard, drag, and input state
├── materials # appearances, textures, and conversion
├── math # raycasting helpers
├── particles
│ ├── flame
│ ├── smoke
│ └── spark
├── photo
│ └── sky
│ └── builtin
├── rendering
│ ├── backgrounds
│ └── passes
├── scene
│ ├── controllers
│ ├── events
│ ├── graph
│ ├── orchestration
│ └── properties
├── ui # GL canvas and HUD
└── utils # GL, color, and vector helpers
Where To Start
New component geometry:
RocketMeshBuilderandgeometry/components.Materials, decals, or transparency:
AppearanceFactory,TransparencyPolicy,DefaultMaterialBinder,TextureStateManager, and the shaders.Camera, orbit, pan, picking, or selection: the scene controllers,
Scene3DOrchestrator, andGLScenePanelinput handling.Render ordering or post-processing:
RealisticRendererandrendering/passes.Photo Studio behavior:
PhotoPanel,PhotoFrame, andPhotoSettings.Context creation, resize, blank-frame, or multi-window failures:
GLScenePanel,SharedCanvasRenderScheduler, and the two wrapper panels.