Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


OpenGL 3.3+ demos framework

To use the entire framework just include "mammoth3d.hpp".

Made it to do demos and to experiment

This framework use C++11 features to bypass the boring stuff


There is a sample project in the "project" folder, this can be used as a starting point.

The sample project should compile easily on Windows/Linux using the codelite .workspace, there is configurations for Windows and Linux.

The "lib" folder contain all the libraries the engine use, some are mingw32 x86 pre-compiled so it compile easily under Windows

This framework is specifically made for demos; texture, shader, geometry etc can (and should) be created through a general purpose loader, with the "loader" there is no need to explicitely create any objects and releasing the memory, all stuff needed for a demo can be created this way, anything used from the framework does not need to be released explicitely or created.

Step by step guide

Opening a window (set false to true for fullscreen, last argument is MSAA level)

auto app = window::Window::getInstance();
app->open("example", 800, 600, false, 2);

By default the mouse cursor will be hidden and vsync will be requested

Loading GLSL files and compiling a program:

auto program = loader::Loader::getInstance()->getProgram("simple.vert", "simple.frag");

Creating a texture from an image file (only the PNG image format is supported):

auto texture = loader::Loader::getInstance()->getTexture("image.png");

Creating a quad with the texture on the first slot (all textures assignated will be registered as t[id] in the shader (thus uniform sampler2D t0; to access the texture within the shader for this example):

auto quad = loader::Loader::getInstance()->getNewQuad({{texture, 0}});

Creating a purple BitmapText mesh from a bitmap font file:

auto ldr = loader::Loader::getInstance();
auto text = ldr->getNewBitmapText("font.png", "Hello World!", 255, 0, 255, 0.25f); // last argument is the alpha treshold, increasing this value make the characters bolder, decreasing it make it thinner
text->setScreenAlign(true); // set this to false to manipulate the text quad, to allow for rotations, scaling etc otherwise by default it is true and the text will be screen aligned, always visible

The text, color, treshold, screen alignment can be updated in real-time.

Creating a camera and setting up its position/target:

auto camera = loader::Loader::getInstance()->getNewCamera(camera::PERSPECTIVE, 75, screenWidth / screenHeight);
camera->lookAt(0, 0, -100.0f, 0, 0, 0, 0, 1, 0);

Using the camera and adding the quad and text to the scene:

auto renderer = renderer::Renderer::getInstance();

Main loop:

do {
} while(app->running() && !audio->isMusicFinished()); // see below for audio setup

Audio -> Visual synchronization

There is a built-in synctracker (value/event sequencer) which can be used in real-time with a gamepad or similar device to control any aspects of a demo (with chunk looping/backward/forward feature), it is possible to animate an entire demo that way in real-time without any code, generally the sync data will be imported first from an existing song but it is not necessary, there is a tool to convert Renoise XRNS Song.xml data to allow perfect synchronization (with additional real-time edits needed, because it generate a "blank" event per note) of a song made in Renoise with the visuals.

Loading an audio file and playing it (only the OGG format is supported):

auto audio = audio::Audio::getInstance()
audio->playMusic(true, 0.5f); // loop play, volume halved

Setting up the synctracker:

auto synctracker = sync::SyncTracker::getInstance();
// the XRNS song.xml converter is needed to get a ".mms" sync file from a song
// those two lines is only needed if there is a need to adjust the bpm/lpb because no sync file is used, otherwise the .mms set them when it is loaded
// setup the synchronized values, one track for each values, it will also create a track if the track name is not found
float sync_value = 0.0f;
int sync_value2 = 0;
synctracker->setTracks({{"track_name_1", &sync_value}, {"track_name_2", &sync_value2}});
// if you want to edit the sync in real-time with a gamepad (or similar device)
auto synctracker_controller = sync::SyncTrackerController::getInstance();

Now, in the main loop:

// use sync_value and sync_value2 as you wish
double song_time = audio->getMusicTime();

There is no graphical interface to edit the synctracker data but there is still text output in the console reflecting any actions you do.

More stuff

It is easy to pass stuff to a shader program using uniform blocks:

auto ublock = loader::Loader::getInstance()->getNewUniformBlock("BlockName", {{"r", 1.0f}, {"g", 1.0f}, {"b", 1.0f}, {"a", 1.0f}, {"stuff", 1.0f}}, program);

There is only "float" uniform type available at the moment.

Then in the GLSL shader (name does not matter here):

layout (std140) uniform BlockName {
    vec4 color;
    float stuff;

Or you can pass large set of data using a TBO:

auto texture_buffer = loader::Loader::getInstance()->getNewTextureBuffer(std::vector<float> {1.0f, 0.0f, 0.0f, 0.0f});
mesh->setTexture(texture_buffer, 1);

If you need to modify the TBO data you will need to call ldr->getNewTbo to get the TBO object then passing the pointer to getNewTextureBuffer and use tbo->updateData function to update the data

Then in the GLSL shader:

uniform samplerBuffer t1; // access data using texelFetch function

It is easy to do instancing, example if you want a huge amount of objects of the same type:

auto ldr = loader::Loader::getInstance();
auto particle_texture = ldr->getTexture("particle.png");
auto particle = ldr->getNewQuad({{particle_texture, 0}});
auto particles = particle->enableInstancing(1000); // 1000 objects will be drawn in a single call, particles is a vector of Mesh, you can change individual attributes from it for each mesh (note: only transform attributes are passed per instance right now)

An attribute location for instances transform matrix should be specified in the vertex shader as layout(location = 4) in mat4 imvp;

Then in the vertex shader you can use gl_InstanceID to get the object id, combine it with a TBO and texelFetch function in the shader for any more per instance stuff like colors, uvs...

There is also a fast way to display huge amount of static objects by merging geometries, it will be difficult to apply effects on each of them but may be faster than instancing:

unsigned int object_count = 1000;
auto geom = ldr->getNewGeometry();
for (unsigned int i = 0; i < object_count; i++) {
    auto quad = new object::Quad();
    // setup object position, rotation, scale here
    core::geometryutils::merge(geom, quad);
    delete quad;
auto ldr = loader::Loader::getInstance();
auto quad_mesh = ldr->getNewMesh(geom, ldr->getNewMaterial());
// rendering quad_mesh will be very fast now


The engine use a binary format to load scenes/meshes, converters are available in the "tools" folder

assimp2bin load and convert any meshes files that the Assimp library support

dae2bin is deprecated, export Collada (.dae) only and only the geometry (UV/normal export is buggy)

Others platforms

Just look at the .project settings in the project folder, the framework require: PortAudio libogg/libvorbis glfw3

Additionally some others libraries (GLM, lodepng, GLEW, tinyxml2) are provided and used, they can be found in the "lib" or "include" (GLM) folder, they can be compiled with the project

The assimp2bin tool require the Assimp library


Do what you want.

Screenshots from experiments

Alt text

Alt text


No releases published


No packages published