v2026.9.7
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. Layers, thinnest first: GLWindow (window and context), GL (state), Shader, Mesh, Texture2D, Light, RenderTarget, Material, Frustum, ShadowMap, PointShadow, Overlay, Transform, and Box/Scene. All public, so nothing is capped. Targets the 3.3 core profile, forward-compatible: the highest common denominator across the desktop platforms Objeck ships, since macOS caps at 4.1 core. GLES-only hardware is out of scope by construction. See docs/opengl.md for setup, a minimal program, why the calls are coarse-grained, and how to add one.

Track

How a value changes over time. The other half of animation. Transform->SetParent says how parts move TOGETHER; a Track says how anything moves at all -- a lid opening, a platform rising, a light dimming, a door swinging, a camera pushing in. It holds keys and reads a value back at any time between them:

Why a track rather than a tween

A from-to pair with a duration covers the easy case and then stops: an animation that goes out, waits, and comes back is three of them plus the bookkeeping to sequence them. Keys are that for free, and reduce to the easy case when there are two of them.

What it does not do

One FLOAT. Not a vector, not a colour, not a quaternion -- three tracks make a position and four make a colour, and keeping it scalar is what lets it drive anything at all rather than the specific things somebody thought of. It has no clock of its own either: Sample takes a time, so the caller decides whether that is wall time, a paused time, or one running backwards. Rotation between two ANGLES here is not the same as rotating between two orientations -- interpolating yaw, pitch and roll separately is fine for a hinge or a turntable and wrong for anything tumbling freely, which needs quaternions this bundle does not have.

Example

swing := Track->New();
swing->Add(0.0, 0.0);
swing->Add(1.5, 90.0);
swing->SetEasing(Easing->EASE_IN_OUT);
...
door->SetYaw(swing->Sample(elapsed)->ToRadians());

Operations

Add #

Add a key. Inserted in time order, so keys can be added in any order. Adding a second key at exactly the same time replaces the first rather than creating a step nothing can land between.

method : public : Add(time:Float, value:Float) ~ Nil

Parameters

NameTypeDescription
timeFloatwhen, in seconds from the start
valueFloatwhat the track reads at that moment

GetCount #

method : public : GetCount() ~ Int

Return

TypeDescription
Inthow many keys the track holds

GetDuration #

The time of the last key, which is how long the track runs for.

method : public : GetDuration() ~ Float

Return

TypeDescription
Floatseconds, or 0 for an empty track

GetEasing #

method : public : GetEasing() ~ Int

Return

TypeDescription
Intthe current easing mode

GetKeyTime #

method : public : GetKeyTime(index:Int) ~ Float

Parameters

NameTypeDescription
indexInt0 to GetCount() - 1

Return

TypeDescription
Floatthe time of that key, or 0 if out of range

GetKeyValue #

method : public : GetKeyValue(index:Int) ~ Float

Parameters

NameTypeDescription
indexInt0 to GetCount() - 1

Return

TypeDescription
Floatthe value of that key, or 0 if out of range

IsLoop #

method : public : IsLoop() ~ Bool

Return

TypeDescription
Boolwhether the track loops

New # constructor

An empty track. Sampling one with no keys returns 0.

New()

New # constructor

A track that runs from one value to another -- the common case, spelled directly.

New(duration:Float, )

Parameters

NameTypeDescription
durationFloathow long it takes, in seconds

Sample #

Read the value at a moment. Before the first key returns the first value and after the last returns the last, so a track is safe to sample at any time without the caller clamping -- unless it loops, in which case time wraps.

method : public : Sample(time:Float) ~ Float

Parameters

NameTypeDescription
timeFloatseconds from the start

Return

TypeDescription
Floatthe value there

SetEasing #

How the value moves BETWEEN keys. LINEAR by default. The eased modes are what stop machine-driven motion reading as machine-driven: almost nothing in the world starts and stops at full speed, and EASE_IN_OUT is the difference between a lid opening and a lid teleporting through its arc.

method : public : SetEasing(easing:Int) ~ Nil

Parameters

NameTypeDescription
easingIntan Easing

SetLoop #

Whether time wraps past the last key. Off by default, which HOLDS the last value -- right for a door that opens once. On wraps back to the start, for anything cyclic. Note a looping track jumps at the wrap unless its first and last values match.

method : public : SetLoop(loop:Bool) ~ Nil

Parameters

NameTypeDescription
loopBooltrue to wrap

Shape #

Bend 0..1 according to the easing mode.

method : private : Shape() ~ Float

Parameters

NameTypeDescription