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).
Frustum
The six planes bounding what a camera can see, for skipping what it cannot. A Scene submits every box it holds, every frame, whether or not the box is on screen -- so a large world costs the same to draw as a small one, and all of it is spent on geometry the viewer will never see. Testing a box against six planes first is a few dozen arithmetic operations against a draw call plus a matrix multiply plus two uniform uploads. Scene does this for you and has it on by default. Reach for Frustum directly only for content Scene does not hold. ## Where the planes come from Straight out of the view-projection matrix. Clip space is the region where every coordinate lies between -w and +w, and each of those six inequalities is a plane in world space once written in terms of the matrix's rows -- so no separate description of the camera is needed, and a frustum built this way automatically matches whatever projection is in use, perspective or orthographic. The planes are normalised, so a plane test yields an actual distance. That costs six square roots per rebuild and makes the box test exact rather than scaled by whatever magnitude the matrix happened to have. ## Conservative, on purpose The box test can keep a box that is fully outside -- a large box near a corner can be outside the view while remaining on the inner side of all six planes individually. It never DISCARDS one that is partly visible, which is the direction that matters: a false keep costs a draw call, a false discard is a hole in the picture.
Example
frustum := Frustum->New();
frustum->SetFromMatrix(view_projection);
if(frustum->ContainsBox(centre, half)) {
# ... draw it ...
};
Operations
ContainsBox #
Whether an axis-aligned box is at least partly inside. Projects the box's half-extents onto each plane's normal, which is the tightest test available without checking corners individually -- and unlike wrapping the box in a sphere it does not inflate a long thin box into something far larger than it is.
method : public : ContainsBox(center:Vector3, half:Vector3) ~ Bool
Parameters
| Name | Type | Description |
|---|
| center | Vector3 | the box's centre |
| half | Vector3 | its half-extents on each axis |
Return
| Type | Description |
|---|
| Bool | true when any part of it could be visible |
ContainsSphere #
Whether a sphere is at least partly inside.
method : public : ContainsSphere(x:Float, y:Float, z:Float, radius:Float) ~ Bool
Parameters
Return
| Type | Description |
|---|
| Bool | true when any part of it could be visible |
IsReady #
method : public : IsReady() ~ Bool
Return
| Type | Description |
|---|
| Bool | true once SetFromMatrix has succeeded; a frustum that is not ready contains everything, so nothing is wrongly culled |
SetFromMatrix #
Rebuild from a view-projection matrix. Call once per frame, after the camera has moved.
method : public : SetFromMatrix(view_projection:Float[]) ~ Nil
Parameters
| Name | Type | Description |
|---|
| view_projection | Float | projection times view, column-major |