OpenGL
Penumbra currently only uses three-dimensional, double-precision functions.
-
(vertex ...)
=>glVertex3d(...)
-
(translate ...)
=>glTranslated(...)
-
(vertex ...)
,(translate ...)
will take two parameters, and default to a z-value of 0. -
(scale ...)
will take two parameters, and defaults to a z-value of 1. - All values are automatically coerced to doubles, so type-hints are not necessary.
- To render in two dimensions, use an orthographic view.
Constants are represented by keywords.
-
:texture-2d
=>GL_TEXTURE_2D
-
:lighting
=>GL_LIGHTING
- These values are transformed at compile-time. If you’re passing in a keyword as a function parameter, you can translate the keyword into a integer constant using
(penumbra.opengl.core/enum ...)
.-
(enable :lighting)
, or(#(enable (enum %)) :lighting)
.
-
Useful functions are imported without the prepended ‘gl’.
-
glEnable(...)
=>(enable ...)
-
glCullFace(...)
=>(cull-face ...)
OpenGL functions wrapped by higher-level abstractions are imported with the prepended ‘gl’.
-
(gl-tex-sub-image-2d ...)
is wrapped by(draw-to-texture ...)
. -
(gl-compile-shader ...)
is a step inside(create-program-from-source ...)
.
Objects which are bound and unbound with OpenGL calls are used via with-*
macros.
-
glBindTexture(...)
=>(with-texture tex ...)
- Behavior outside of the scope of
(with-texture ...)
with textures enabled is not well-defined. For performance reasons, a texture may still be bound. Don’t rely on specific behavior in this scenario.
- Behavior outside of the scope of
-
glUseProgram(...)
=>(with-program program ...)
Other states within OpenGL may also have with-*
macros.
-
(with-enabled ...)
and(with-disabled ...)
allow a specific flag to be set within the inner scope, and then returned to its previous value.-
(with-enabled :lighting ...)
or, for multiple arguments,(with-enabled [:lighting :texture-2d] ...)
. - Calling
(with-enabled ...)
on an already enabled flag is a null-op.
-
Each primitive type has its own wrapper macro.
-
glBegin(GL_QUADS)
/glEnd()
=>(draw-quads ...)
. -
glBegin(GL_TRIANGLE_STRIP)
/glEnd()
=>(draw-triangle-strip ...)
.
Unlike standard OpenGL, Penumbra supports intra-primitive transformations. This uses an intermediate transform step, which is only used if a transform function is called within the scope of a (draw-* ...)
macro. This can be especially useful for procedural geometry. Consider this approach to drawing a cube:
(defn quad []
(push-matrix
(translate -0.5 -0.5 0.5)
(normal 0 0 -1)
(vertex 1 1 0)
(vertex 0 1 0)
(vertex 0 0 0)
(vertex 1 0 0))))
(defn cube []
(draw-quads
(dotimes [_ 4]
(rotate 90 0 1 0)
(quad))
(rotate 90 1 0 0)
(quad)
(rotate 180 1 0 0)
(quad)))
To create a display list, use the (create-display-list ...)
macro. It will return an integer representing any geometry that was drawn within its inner scope. This display list can be called using (call-display-list ...)
.