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).

Light

One light. Directional, point or spot. Build one with the factory that matches what you want, since each kind needs different things and a single constructor taking all of them would ask for values that do not apply: A single Light can be handed straight to a shader with ApplyTo, which is the common case. For more than one, put them in a LightRig. ## Which way a direction points TOWARD the light, not the way the light travels. A directional light of (0, 1, 0) is overhead, so upward-facing surfaces are brightest. This is the convention that makes the shader's dot product read naturally and it is the one people get backwards -- a scene lit from underneath is the symptom. A SPOT's aim is the opposite: the direction it shines, which is what you would point a torch along.

Example

sun := Light->Directional(Vector3->New(-0.3, 1.0, 0.4));
lamp := Light->Point(Vector3->New(0.0, 3.0, 0.0), 12.0);
torch := Light->Spot(position, aim, 15.0, 20.0, 30.0);

Operations

ApplyAt #

Write this light into one slot of a shader's light arrays. Used by LightRig; call that rather than this unless you are writing your own rig.

method : public : ApplyAt(shader:Shader, index:Int) ~ Nil

Parameters

NameTypeDescription
shaderShaderthe program
indexIntwhich slot

ApplyTo #

Write this light into a shader as its ONLY light, with a default ambient. The single-light case, and what most scenes want. For more than one, or to set the ambient yourself, use a LightRig.

method : public : ApplyTo(shader:Shader) ~ Nil

Parameters

NameTypeDescription
shaderShaderthe program to write into

Directional # function

A light infinitely far away: the sun.

function : Directional(direction:Vector3) ~ Light

Parameters

NameTypeDescription
directionVector3toward the light

Return

TypeDescription
Lightthe light

GetInnerCosine #

The cosine of the inner cone half-angle. Cosines rather than angles because that is what the shader compares against a dot product.

method : public : GetInnerCosine() ~ Float

Return

TypeDescription
Floatthe cosine

GetOuterCosine #

The cosine of the outer cone half-angle.

method : public : GetOuterCosine() ~ Float

Return

TypeDescription
Floatthe cosine

New # constructor

A white light from above with a little ambient, which is a reasonable starting point for seeing a scene at all.

New()

Point # function

A light at a position, fading to nothing at its range. The falloff reaches ZERO at the range rather than trailing off forever the way an inverse square does. A light that stops where it says it stops is what lets a scene have several without every one of them contributing to every surface.

function : Point(position:Vector3, range:Float) ~ Light

Parameters

NameTypeDescription
positionVector3where it is
rangeFloathow far its light reaches, in world units

Return

TypeDescription
Lightthe light

SetAmbient #

The light every surface gets regardless of which way it faces, used when this light is applied on its own. Without it, a face turned away is pure black -- correct for one light in a vacuum and wrong-looking for anything else, since real surfaces are lit by everything around them. In a LightRig the rig's ambient is used instead.

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

Parameters

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

SetAxis #

method : public : SetAxis(aim:Vector3) ~ Nil

Parameters

NameTypeDescription
aimVector3the direction a spot shines

SetColor #

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

Parameters

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

SetCone #

A spot's cone, in degrees from its axis. Stored as cosines, because the shader compares a dot product and converting back would cost an acos per fragment per light.

method : public : SetCone(inner_degrees:Float, outer_degrees:Float) ~ Nil

Parameters

NameTypeDescription
inner_degreesFloathalf-angle of the fully lit centre
outer_degreesFloathalf-angle where it reaches nothing

SetDirection #

method : public : SetDirection(direction:Vector3) ~ Nil

Parameters

NameTypeDescription
directionVector3toward the light, for a directional light

SetDirection #

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

Parameters

NameTypeDescription
xFloattoward the light, x
yFloattoward the light, y
zFloattoward the light, z

SetIntensity #

Scale the colour, for a light that pulses or dims.

method : public : SetIntensity(scale:Float) ~ Nil

Parameters

NameTypeDescription
scaleFloatmultiplied into the colour

SetPosition #

method : public : SetPosition(position:Vector3) ~ Nil

Parameters

NameTypeDescription
positionVector3where a point or spot light is

SetRange #

method : public : SetRange(range:Float) ~ Nil

Parameters

NameTypeDescription
rangeFloathow far a point or spot light reaches

Spot # function

A light at a position, shining along an axis within a cone.

function : Spot(position:Vector3, aim:Vector3, range:Float, inner_degrees:Float, outer_degrees:Float) ~ Light

Parameters

NameTypeDescription
positionVector3where it is
aimVector3the direction it shines -- the opposite sense to a directional light's direction, because this is where you point it
rangeFloathow far its light reaches
inner_degreesFloatthe half-angle of the fully lit centre
outer_degreesFloatthe half-angle where it reaches nothing; must be larger than inner_degrees or the edge has no width to fade across

Return

TypeDescription
Lightthe light