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.

Texture2D

A 2D texture. Built from an SDL Surface, so image decoding stays SDL's job -- Game.SDL2's Image->Load already reads PNG and JPEG, and this adds no new dependency.

Implements: Freeable

Example

surface := Image->Load("crate.png");
texture := Texture2D->New(surface);
surface->Free();          # the pixels are on the GPU now

Operations

Bind #

Bind to a texture unit, to be read by a sampler uniform set to the same unit number.

method : public : Bind(unit:Int) ~ Nil

Parameters

NameTypeDescription
unitIntthe texture unit, 0 for the first

Checker # function

A checkerboard, generated rather than loaded, so a program needs no asset file to have something recognisable on a surface.

function : Checker(size:Int, squares:Int, light:Int, dark:Int) ~ Texture2D

Parameters

NameTypeDescription
sizeIntthe texture's width and height in pixels
squaresIntsquares per side
lightIntpacked 0xAARRGGBB for one colour
darkIntpacked 0xAARRGGBB for the other

Return

TypeDescription
Texture2Dthe texture; check IsOk()

Example

floor := Texture2D->Checker(64, 8, 0xFFE8E8F0, 0xFF465A82);

Checker # function

A checkerboard with an explicit wrap mode -- pass TextureWrap->GL_REPEAT to tile it across a Mesh->Plane.

function : Checker(size:Int, squares:Int, light:Int, dark:Int, wrap:Int) ~ Texture2D

Parameters

NameTypeDescription
sizeIntthe texture's width and height in pixels
squaresIntsquares per side
lightIntpacked 0xAARRGGBB for one colour
darkIntpacked 0xAARRGGBB for the other
wrapInta TextureWrap

Return

TypeDescription
Texture2Dthe texture; check IsOk()

Free #

Release the texture. Call it while the GL context is still current -- see the shutdown order on the GL class. A texture obtained from RenderTarget->GetTexture or ShadowMap->GetTexture is a BORROWED view of that target's attachment, not a texture of its own, and this does nothing for one. Freeing it used to delete the attachment out from under the target that was still using it.

method : public : Free() ~ Nil

FromFile # function

Load an image file and upload it, freeing the intermediate surface. Decoding is SDL's job -- Game.SDL2's Image->Load already reads PNG and JPEG, so this adds no dependency. Rows are flipped on upload, so the texture is the same way up as the file.

function : FromFile(file:String) ~ Texture2D

Parameters

NameTypeDescription
fileStringthe image path

Return

TypeDescription
Texture2Dthe texture; IsOk() is false when the file could not be read

FromFile # function

Load an image file with explicit sampling.

function : FromFile(file:String, filter:Int, wrap:Int, mipmap:Bool) ~ Texture2D

Parameters

NameTypeDescription
fileStringthe image path
filterInta TextureFilter
wrapInta TextureWrap
mipmapBooltrue to build a mipmap chain

Return

TypeDescription
Texture2Dthe texture; IsOk() is false when the file could not be read

IsOk #

method : public : IsOk() ~ Bool

Return

TypeDescription
Booltrue when the texture uploaded

New # constructor

Upload a surface's pixels as a texture, smoothed and clamped. The surface is converted to 32-bit RGBA internally, so any format Image->Load produces works. The PIXELS ARE COPIED, so the surface can be freed immediately afterwards -- and should be. Every example in this repo used to hold its surfaces alive until shutdown for no reason.

New(surface:Surface)

Parameters

NameTypeDescription
surfaceSurfacethe source surface; still owned by the caller

New # constructor

Upload with explicit sampling.

New(surface:Surface, filter:Int, wrap:Int, mipmap:Bool)

Parameters

NameTypeDescription
surfaceSurfacethe source surface; still owned by the caller
filterIntTextureFilter->GL_LINEAR or GL_NEAREST
wrapInta TextureWrap; GL_REPEAT to tile
mipmapBooltrue to build a mipmap chain, which is what stops a tiled floor from shimmering into noise in the distance

New # constructor

Upload with anisotropic filtering. The fix for a tiled floor that turns to mush in the distance. Anisotropy chooses better mip SAMPLES for a surface seen at a steep angle, which is exactly the case ordinary mipmapping handles worst -- so it needs `mipmap` true to do anything at all, and does nothing visible on a HUD quad seen face-on. Extension-gated, and asked for rather than assumed: a driver without GL_EXT_texture_filter_anisotropic ignores it and a level above what the hardware offers is clamped to the maximum. Check GL->GetMaxAnisotropy() first if you care which happened. 4 or 8 is the usual choice; 16 is the common ceiling.

New(surface:Surface, filter:Int, wrap:Int, mipmap:Bool, anisotropy:Float)

Parameters

NameTypeDescription
surfaceSurfacethe source surface; still owned by the caller
filterIntTextureFilter->GL_LINEAR or GL_NEAREST
wrapInta TextureWrap
mipmapBoolmust be true for anisotropy to have any effect
anisotropyFloat1.0 for none, or 2, 4, 8, 16

NewSurface # function

A blank 32-bit surface with the ARGB channel layout FillRect's packed Int assumes: 0xAARRGGBB. The four masks are the thing worth having in one place. They appeared verbatim in three separate files, each next to its own hand-written bit-shifting helper that had to agree with them.

function : NewSurface(width:Int, height:Int) ~ Surface

Parameters

NameTypeDescription
widthIntin pixels
heightIntin pixels

Return

TypeDescription
Surfacethe surface, or Nil when SDL could not make one

Rgb # function

Pack 8-bit components into the 0xAARRGGBB layout NewSurface declares.

function : Rgb(r:Int, g:Int, b:Int) ~ Int

Parameters

NameTypeDescription
rIntred, 0-255
gIntgreen, 0-255
bIntblue, 0-255

Return

TypeDescription
Intthe packed colour, fully opaque

Solid # function

A single-colour texture, one pixel. For an untextured surface that still goes through a textured shader, which is cheaper than maintaining a second shader that does not sample.

function : Solid(argb:Int) ~ Texture2D

Parameters

NameTypeDescription
argbIntpacked 0xAARRGGBB

Return

TypeDescription
Texture2Dthe texture; check IsOk()

Wrapping # function

Wrap a GL texture name this object does not own. For a texture created elsewhere -- a RenderTarget's colour attachment -- so it can be bound and sampled through the usual Texture2D API. Free() on the result would delete a texture something else is still using, so whatever created it is responsible for releasing it.

function : Wrapping(handle:Int) ~ Texture2D

Parameters

NameTypeDescription
handleInta GL texture name

Return

TypeDescription
Texture2Da Texture2D over it