Minimalist base-project for interactive graphical applications (2D/3D)
Written in plain C (compatible with C++)
Pure-software (no hardware acceleration of any kind)
Focuses on simplicity, ease of use and brain-dead setup (single header file, no dependencies)
Built on: SlimApp
Used in: SlimTracin and SlimRaster
SlimEngine is platform-agnostic by design, though currently only supports Windows.
The platform layer only uses operating-system headers - no standard library used.
The application layer itself has no 3rd-party dependencies - only uses standard math headers.
It is just a library that the platform layer uses - it has no knowledge of the platform.
More details on this architecture here.
The single header file variant includes everything.
Otherwise, specific headers can be included from the directory of headers.
The main entry point for the app needs to be defined explicitly (see SlimApp).
SlimEngine comes with pre-configured CMake targets for all examples.
For manual builds on Windows, the typical system libraries need to be linked
(winmm.lib, gdi32.lib, shell32.lib, user32.lib) and the SUBSYSTEM needs to be set to WINDOWS
All examples were tested in all combinations of:
Source: Single header file (SlimEngine.h), Directory of headers (specific includes)
Compiler: MSVC, MinGW, CLang
Language: C, C++
CPU Arch: x86, x64
SlimEngine extends SlimApp and so inherits all it's features
Additional features include facilities for interactive 3D applications:
- A scene with cameras, meshes and parametric curves
- Scene selection and interactive transformations (moving, rotating and scaling)
- 3D Viewport with a HUD and rich mouse/keyboard navigation
- 3D Line drawing for wireframe rendering (optionally multi-sampled for very clean lines)
Well documented example applications cover the features:
-
3D Viewports:
Viewports come with a HUD that can be toggled on/off
SlimEngine comes with an empty scene, apart from a default camera bound to a default viewport.
The window content is bound to the default viewport, so they resize together.
Customizing the scene/viewport can be done in callbacks that get invoked when they're ready.
The HUD is fully customizable, and can hold any number of lines set at viewport-initialization time. -
Viewport: Navigation
Viewport navigation actions can be composed to set-up WASD(FPS)/DCC(orb) navigation style(s).
Keyboard navigation is agnostic to key-binding and is trivial to map keys to.
It supports moving forward, backwards, left, right, up and down and turning left or right.
Mouse navigation controls the viewport's camera and is more granular.
It can pan, zoom, dolly, orient or orbit the camera around a target. -
Cameras: Viewport binding
The scene can have any number of cameras which can be bound to any viewport dynamically.
The camera can be drawn as part of the drawing of the scene -
Shapes: Setup and drawing parametric primitives (Grid, Box, Curves, etc.)
SlimEngine allocates and initializes the scene based on quantities set at app-initialization time.
Wireframe drawing of the scene to the viewport can be set-up to occur on window-redraw. -
Scene: Object selection and manipulation (Translation, Rotation and Scale)
Scene objects can be dragged around the screen, moving them parallel to the viewing plane.
Selecting an object displays it's bouding box, enabling for per-axis translation, rotation and scaling. -
Scene: Mesh loading from file, instancing and wire-frame drawing
Meshes can be loaded from binary files, with vertex positions, normals and texture coordinates.
They can be moves and transformed just like any other kind of primitive.
A mesh can have multiple instances, each with their own transformation and colors.
Simply have multiple mesh primitives all set with same mesh id.
Memory is allocated automatically for the meshes by reading their headers before loading. -
Scene: Saving to and loading from
.scene
files
Scenes can be saved to a file and later loaded back in-place. -
obj2mesh: Also privided is a separate CLI tool for converting
.obj
files to.mesh
files.
It is also written in plain C (so is compatible with C++)
Usage:./obj2mesh src.obj trg.mesh
- invert_winding_order : Reverses the vertex ordering (for objs exported with clockwise order)
- invert_winding_order : Reverses the vertex ordering (for objs exported with clockwise order)
SlimEngine does not come with any GUI functionality at this point.
Some example apps have an optional HUD (heads up display) that shows additional information.
It can be toggled on or off using thetab
key.
All examples are interactive using SlimEngine's facilities having 2 interaction modes:
- FPS navigation (WASD + mouse look + zooming)
- DCC application (default)
Double clicking the left mouse button
anywhere within the window toggles between these 2 modes.
Entering FPS mode captures the mouse movement for the window and hides the cursor.
Navigation is then as in a typical first-person game (plus lateral movement and zooming):
Move the mouse
to freely look around (even if the cursor would leave the window border)
Scroll the mouse wheel
to zoom in and out (changes the field of view of the perspective)
Hold W
to move forward
Hold S
to move backward
Hold A
to move left
Hold D
to move right
Hold R
to move up
Hold F
to move down
Exit this mode by double clicking the left mouse button
.
The default interaction mode is similar to a typical DCC application (i.e: Maya):
The mouse is not captured to the window and the cursor is visible.
Holding the right mouse button
and dragging the mouse orbits the camera around a target.
Holding the middle mouse button
and dragging the mouse pans the camera (left, right, up and down).
Scrolling the mouse wheel
dollys the camera forward and backward.
Clicking the left mouse button
selects an object in the scene that is under the cursor.
Holding the left mouse button
while hovering an object and then dragging the mouse,
moves the object parallel to the screen.
Holding alt
highlights the currently selecte object by drawing a bounding box around it.
While alt
is still held, if the cursor hovers the selected object's bounding box,
mouse interaction transforms the object along the plane of the bounding box that the cursor hovers on:
Holding the left mouse button
and dragging the mouse moves the object.
Holding the right mouse button
and dragging the mouse rotates the object.
Holding the middle mouse button
and dragging the mouse scales the object.
(mouse wheel
interaction is disabled while alt
is held)
In addition to examples of specific features, a more real-world application is also included here:
It is the full source code used for all interactive visualizations in a video about Perspective Projection
The video was a submission to 3Blue1Brown's Summer of Math Explainers (a.k.a: SoME1) competition.
It covers how perspective projection works when using graphics hardware, in its native 4D projective space.
Since it's using SlimEngine it has no dependencies and is structures as a single compilation unit (Unity Build).
It can therefore be easilly compiled with any C/C++ compiler by just compiling the 'perspective_projection.c' file.
As with the other examples, a CMake target is already set up for it here.
There are 5 separate visualizations:
- Intro : A rotating cube being projected onto a projection plane.
- Projective Space : A projective space centered on the secondary camera.
- Space Warping : The volumetric warping of space and the transformations of the frustum-bounds.
- View Frustum : Interactive view frustum with clipping planes and an NDC box centered on the secondary camera.
- Projection Matrix : The main section of the video deriving the transformation matrices geometrically.
In all visualizations:
- Camera controls are exactly as detailed above for the rest of the examples.
- The
space
key cycles to the next visualization. - The
R
key toggles on/off a slow orbiting of the main camera about its target position. - The
Tab
key toggles on/off a secondary viewport showing what the secondary camera sees, except for the last visualization where it toggles the Matrix HUD instead.
- The
3
key (re)starts the transition of 'lines through the origin' emanating from the secondary camera's origin.
- The
1
key transitions the frustum from view-space directly to NDC-space (box centered at the camera). - The
2
key transitions the frustum from view-space to clip-space and then to NDC-space (only works in 2-step mode). - The
3
toggles on/off the 2-step mode and the display of the frustum of the intermediary clip-space. - The
4
toggles on/off the volumetric visualization of space-warping transformation (using a 3D grid of locators).
- The
1
key transitions the frustum from view-space to NDC-space taking the clipped geometry with it. - Holding
ctrl
the mouse wheel moved the near clipping plane back and forth. - Holding
shift
the mouse wheel moved the far clipping plane back and forth. - Holding
alt
the mouse wheel zooms the camera in/out by increasing/decreasing the focal length. - Clicking anywhere within the bounds of the secondary viewport (when it's visible, toggled by
Tab
)
make the secondary viewport 'active' so that all camera controls affect the secondary camera.
Clicking anywhere outside the bounds makes the primary camera active again.
- Holding
ctrl
the mouse wheel moved the near clipping plane back and forth. - Holding
shift
the mouse wheel moved the far clipping plane back and forth. - Holding
alt
the mouse wheel zooms the camera in/out by increasing/decreasing the focal length. - The X, Y/W and Z basis vectors can be manipulated (by moving their arrow-heads) thereby transforming the space.
This also correspondingly updates the current manual matrix (top-left corner of the matrix HUD, if visible).
Just click-dragging them moves them parallel to the view, but: holdingctrl
constrains the movement to only be along the Z axis
holdingshift
constrains the movement to only be along the Y (or W) axis, and
holdingshift+ctrl
constrains the movement to only be along the X axis, and - The
T
key shows the transition of the next transformation step and adds its matrix to the HUD.
shift+T
adds the matrix of the current 'manual' transformation instead.
ctrl+T
removed the last-added transformation matrix. - The
O
key toggles between working with an NDC space of a full-cube (OpenGL/Vulkan) of a half-cube (Direct3D). - The
V
key toggles the visibility of the geometry (yellow box and white grid). - The
L
key toggles the visibility of the NDC box's vertex coordinates (labels). - The
Z
key toggles the visibility of the auxiliary plane that controls the view-frustum's shape. - The
F
key toggles the visibility of the focal-length's ratio (labels). - The
A
key toggles the visibility of the aspect ratio's similar triangles (labels). - The
X
key toggles the visibility of the diagonals of the perfectly-diagonal pyramid. - The
S
key toggles the visibility of the arrows showing the X and Y scaling factors (labels). - The
G
key toggles the visibility of grids representing the XY and ZY planes. - The
1
key transitions the frustum from view-space to NDC-space taking the clipped geometry with it. - The
2
key slices the view of 4D space to XZW thereafter. - The
3
key reveals the projective space (centered at the origin). - The
4
key reveals a projective point (a line through the origin). - The
5
key reveals the projective reference plane at a W height of 1. - The
4
key (second time) moves a projective point (a line through the origin) along the ref. plane. - The
5
key (second time) shows a projective normalization, pushing a projective point down to the ref. plane. - The
6
key reveals the projective points (lines through the origin) of the NDC rectangular slice's corners. - The
7
key reveals the projective trajectories possible for the corners of the view frustum's slice's corners. - The
8
key reveals the wobbling quadrangle formed by the target corner-positions on the projective points. - The
9
key pauses the wobbling and sliding of the target projective points along their lines through the origin. - The
0
key drops the wobbled quadrangle by projectively normalizing the corners down to the ref. plane. - The
D
key reveals vectors showing the diagonal of the lines through the origin and the NDC corners. - The
C
key reveals the chosen trajectory for the view frustum's corners onto the NDC's projective points. - The
B
key reveals the labels of the chosen trajectory, showing the measures of transformations needed. - The
H
key reveals the top-view's labels for the Z-scaling (after the shear). - The
K
key toggles the 'alternative' Z-scale when in cube-NDC mode (OpenGL). - The
J
key toggles the Z-flip mode (OpenGL). - The
M
key toggles the 'final' matrix in the top-left corner of the matrix HUD (instead of the custum one).