Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.
ztellman edited this page Sep 13, 2010 · 13 revisions

Basic conventions

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

Drawing primitives

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

Display lists

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