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

Material

How a surface looks: its texture, a colour multiplier, and how shiny it is. Shininess and specular strength live HERE rather than on the lights, which is where I first put them and was wrong: they describe the SURFACE. A polished ball and a matte floor lit by the same lamp need different values, and a property that cannot differ between objects in one scene is not a property of the object. ## The tint Multiplied into the final colour, so one texture can dress many objects. A tint of exactly black counts as "untinted" rather than as black -- see the note in the shader: GL leaves an unset uniform at zero, and without that rule a forgotten tint turns everything black, while an object tinted to pure black is indistinguishable from one that is not drawn. ## Not owned A Material does not own its texture. Several materials sharing one texture is the normal case, so freeing the material does not free the texture -- the same rule Scene follows for the mesh and shader it is handed.

Example

metal := Material->New(texture);
metal->SetSpecular(96.0, 0.9);

stone := Material->New(texture);
stone->SetTint(0.7, 0.68, 0.62);   # same texture, different colour

box->SetMaterial(metal);

Operations

ClearEmissive #

Stop emitting.

method : public : ClearEmissive() ~ Nil

Colored # function

A material with a tint and no texture, for flat-coloured objects drawn through a textured shader.

function : Colored(r:Float, g:Float, b:Float) ~ Material

Parameters

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

Return

TypeDescription
Materialthe material

GetNormalMap #

method : public : GetNormalMap() ~ Texture2D

Return

TypeDescription
Texture2Dthe normal map, or Nil

GetOpacity #

method : public : GetOpacity() ~ Float

Return

TypeDescription
Floatthe opacity, 1.0 for solid

HasEmissive #

method : public : HasEmissive() ~ Bool

Return

TypeDescription
Booltrue when this material emits any light of its own

HasNormalMap #

method : public : HasNormalMap() ~ Bool

Return

TypeDescription
Booltrue when ApplyTo will bind a normal map

HasTexture #

Whether this material carries a texture of its own. A material without one does NOT bind anything, so whatever a previous draw left on the unit is what gets sampled. Scene uses this to fall back to the box's own texture rather than let the result depend on draw order.

method : public : HasTexture() ~ Bool

Return

TypeDescription
Booltrue when ApplyTo will bind a texture

IsOpaque #

method : public : IsOpaque() ~ Bool

Return

TypeDescription
Booltrue when this material needs no sorting

New # constructor

An untinted, matte material.

New(texture:Texture2D)

Parameters

NameTypeDescription
textureTexture2Dthe texture to draw with; may be Nil

SetEmissive #

Light this surface gives off on its own. Added AFTER the lighting rather than multiplied into it, which is the whole point: SetTint scales what the lights deliver, so a tinted surface can never be brighter than the light falling on it. That is why a relic in an unlit corner is invisible however you tint it, and why a creature standing between the player and their own lamp came out darker than the wall behind it. Emissive is also the right tool when the base texture is the wrong hue. Tinting a GOLD texture blue gives green, because tint multiplies; an emissive term does not care what the texture underneath is. This does not light anything else -- there is no bloom and no light bleed. It makes the surface itself bright.

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

Parameters

NameTypeDescription
rFloatred, 0..1 and beyond if you want it to bloom out
gFloatgreen
bFloatblue

SetOpacity #

How opaque this material is. 1.0 is solid and is the default. Anything less needs blending on and the object drawn after the solid ones -- Scene does both once it knows where the camera is; a program drawing by hand does it with GL->BeginTransparency and GL->EndTransparency. This MULTIPLIES the texture's own alpha rather than replacing it, so a cut-out texture stays cut out at 0.5 opacity instead of turning into a uniformly half-visible rectangle.

method : public : SetOpacity(opacity:Float) ~ Nil

Parameters

NameTypeDescription
opacityFloat0.0 invisible to 1.0 solid

SetSpecular #

How tight the highlight is and how strong. Strength defaults to zero, so a material is matte until asked -- a diffuse surface is the right default and an unexpected highlight reads as a bug.

method : public : SetSpecular(shininess:Float, strength:Float) ~ Nil

Parameters

NameTypeDescription
shininessFloatthe exponent; 8 is a broad sheen, 128 a tight glint
strengthFloat0 matte, around 0.5 plastic, 1.0 something wet

SetTexture #

method : public : SetTexture(texture:Texture2D) ~ Nil

Parameters

NameTypeDescription
textureTexture2Dthe texture, or Nil for none