v2026.9.4
All Bundles
Bundle OpenGL 3.3 core rendering on top of SDL2. Builds on sdl2.obl for the window and context; this bundle is the GL side. Compile with -lib sdl2. Targets the **3.3 core profile**, forward-compatible. That is the highest common denominator across the platforms Objeck ships: Windows, Linux and macOS desktop all reach it, and macOS caps at 4.1 core so nothing above 4.1 is portable there. GLES-only hardware (Raspberry Pi and similar) is out of scope by construction. Layers, from thinnest to friendliest -- all public, so nothing is capped: * GLWindow -- a window and a 3.3 core context, opened and paced for you. * GL -- static state operations. Thin, but never per-vertex. * Shader -- a linked program; Flat/Textured/TexturedFog/Normals built in, so the common cases need no GLSL at all. * Mesh -- geometry uploaded once into buffer objects, drawn many times, with Cube/Plane/Quad/Sphere built in, OBJ loading, and instancing. * Texture2D -- a texture, from a file or generated; Solid/Checker built in. * Light -- one directional light plus ambient, written into a shader. * RenderTarget -- draw into a texture instead of the window, then sample it. * Material -- a surface: its texture, tint and shininess. * Frustum -- the six planes of a view, for skipping what is off screen. * ShadowMap -- shadows from a directional or spot light, depth pass and all. * PointShadow -- shadows from a point light, in every direction, via a cube map. * Overlay -- text and rectangles over the scene, positioned in pixels. * Transform -- position, rotation and scale, with a cached matrix. * Box, Scene -- a world of boxes: draws itself, and answers collision. ## Why the API is shaped this way The native call boundary is expensive: the VM resolves each native symbol by string on EVERY call (GetProcAddress/dlsym) and boxes every argument into a fresh holder. A 1:1 mapping of OpenGL onto that would be thousands of lookups and allocations per frame. So each call here does real work -- 'compile a program from two sources' is one call, not the five GL calls it decomposes into -- and bulk data crosses as whole arrays, never element by element. This happens to be exactly what GL 3.3 core wants anyway: upload geometry once, then draw with few calls. ## Adding a call One `void fn(VMContext&)` in the OpenGL section of core/lib/sdl/sdl.cpp, and one method here that names it. See that file's header for the two rules that matter (positional slot indices, and keeping each call coarse).

Skybox

The sky: a gradient with a sun in it, drawn behind everything else. ## Why this is not a textured cube map The usual skybox is six images. That needs a colour cube map, which needs a native upload path this library does not have, and it needs the caller to find or make six matched images before they can see anything at all. This one is computed in the fragment shader from the view direction, so it needs no files, no native additions, and no assets in a release. An outdoor scene gets a horizon, a zenith and a sun by saying what colour they are. A photographic sky is a different feature and would want the cube map. ## How it draws Three things have to be true or a skybox goes wrong in a way that looks like something else broke: no translation The view matrix's translation is stripped, so the sky does not move when the camera walks. Leave it in and the sky slides past like painted scenery on a nearby wall. inside the cube Mesh->CubeInterior winds its triangles inward. Using Mesh->Cube instead leaves every face back-facing and the sky vanishes the moment culling is on. behind Drawn first with depth WRITES off, so everything after it covers it without the sky having to be far away. Draw takes the Camera rather than a matrix, because all three of those are easy to get wrong and none of them are interesting. sky := Skybox->New(); sky->SetHorizon(0.55, 0.62, 0.70); sky->SetZenith(0.12, 0.26, 0.55); sky->SetSun(0.3, 0.55, -0.75); ... window->BeginFrame(); sky->Draw(camera); # first, before the scene scene->Draw(view_projection);

Implements: Freeable

Operations

ClearSun #

Turn the sun off and leave only the gradient.

method : public : ClearSun() ~ Nil

Draw #

Draw the sky. Call FIRST, before the scene, and after BeginFrame has cleared. The depth buffer is left untouched, so everything drawn afterwards covers this regardless of distance. Restores depth writing and the cull mode on the way out, because a pass that quietly leaves state changed is the kind of bug that surfaces three draw calls later in unrelated geometry.

method : public : Draw(camera:Camera) ~ Nil

Parameters

NameTypeDescription
cameraCamerathe eye. Its rotation is used and its position is not.

Free #

Release the shader, and the mesh if this sky made it.

method : public : Free() ~ Nil

GetError #

method : public : GetError() ~ String

Return

TypeDescription
Stringwhy this sky cannot draw, or an empty string

IsOk #

method : public : IsOk() ~ Bool

Return

TypeDescription
Booltrue when the shader built and the geometry uploaded

New # constructor

A daylight sky, ready to draw.

New()

New # constructor

A sky drawn with geometry the caller owns.

New(mesh:Mesh)

Parameters

NameTypeDescription
meshMeshan INWARD-facing cube. Mesh->CubeInterior makes one; Mesh->Cube does not and will be invisible under back-face culling.

SetHorizon #

The colour at eye level.

method : public : SetHorizon(r:Float, g:Float, b:Float) ~ Nil

Parameters

NameTypeDescription
rFloatred, 0..1
gFloatgreen, 0..1
bFloatblue, 0..1

SetSun #

Where the sun is, as a DIRECTION from the viewer -- not a position. It is normalised here, so any non-zero length will do. Point this the same way as the scene's directional Light and the shadows will agree with the sky. Nothing enforces that: they are two separate settings, and a sun in one corner with shadows from the other is a mistake this class cannot see.

method : public : SetSun(x:Float, y:Float, z:Float) ~ Nil

Parameters

NameTypeDescription
xFloatdirection x
yFloatdirection y, positive is up
zFloatdirection z

SetSunColor #

The sun's colour.

method : public : SetSunColor(r:Float, g:Float, b:Float) ~ Nil

Parameters

NameTypeDescription
rFloatred, 0..1
gFloatgreen, 0..1
bFloatblue, 0..1

SetSunSize #

How tight the sun's disc is. This is the cosine of its angular radius, so LARGER is smaller: 0.9999 is a pinpoint, 0.99 is a soft glow, and anything at or below 0.0 fills half the sky. Clamped below 1.0, because exactly 1.0 is a sun of zero size that flickers as it crosses a pixel centre.

method : public : SetSunSize(cosine:Float) ~ Nil

Parameters

NameTypeDescription
cosineFloat0..1, nearer 1 for a smaller sun

SetZenith #

The colour straight up.

method : public : SetZenith(r:Float, g:Float, b:Float) ~ Nil

Parameters

NameTypeDescription
rFloatred, 0..1
gFloatgreen, 0..1
bFloatblue, 0..1