Skip to content
Snippets Groups Projects
README.md 65.22 KiB

FocusTerra Realtime Wave Simulator

This repository contains all files that make up the realtime wave simulator exhibit at FocusTerra. More information about the exhibition can be found here.

While the project was originally developed on macOS, it was later ported to Windows (Visual Studio 2019).

Dependencies are OpenGL, ImGui and SDL, and all of them are contained in this repository.

The program is optimized to run on a system containing an Nvidia Geforce RTX 3080, where it achieves roughly 90-95% GPU resource utilization. While CPU and RAM should be of reasonable performance/size, their specs are of secondary importance.

For a general overview of the application logic, one should have a look at the Architecture section. An accompanying code example is shown in Workflow: Sample Main Function.

If one desires to add custom resources (e.g. new predefined structures or a new colour palette), one should head straight to Resources section, where the resource organization and file format conventions are detailed.

For information on how the project can be built, the Building the Project section has you covered with step-by-step instructions.

In depth documentation about all classes and their members can be found in the Classes section. Enums are covered in their own Enums section.

While DearImGui is the basis for the GUI front- and backend, I had to perform some modifications in order to make it work on a touchscreen. Those changes are detailed in the section ImGui Customization.

Finally, shaders and their respective purposes are explained in the section GLSL Shaders.

Table of Contents

Impressions

Wellentisch Photo 1 Wellentisch Photo 2

Project Organization

The repo is organized in the following folders:

  • src (and subfolders) contains all .cpp
  • include (and subfolders) contains all headers
  • shaders contains all GLSL shaders
  • lib contains libraries
  • FocusTerra contains the Visual Studio project file
  • FocusTerra\x64\Release and FocusTerra\x64\Debug contain the executables
  • build is left over from the macOS days (contains a currently broken Makefile)

Resources

Runtime Resource Folder

Runtime resources are organized in a single folder called ft_top. It contains the following:

  • A folder bin that contains the executable and SDL2.dll
  • A folder fonts that contains the font used by the GUI, namely Cousine-Regular.ttf
  • A folder resource that contains
    • The colour palette in ft_palette.conf, ft_palette.texture
    • The Image Button images in the folder images
    • A folder textures that contains the predefined structure textures. There are two versions, one for the FocusTerra resolution (in rocket), and one for Pascal's resolution (in home).
  • A folder shaders that contains the used GLSL shaders.

The program accesses these resources at runtime.

File Formats

This section describes the file formats external files are expected to follow.

GUI Images

Gui Images, such as the button textures, are loaded using stbi. The exact calls can be found in GuiHandler::load_image_to_texture_. The important parts are

data = stbi_load(file.c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
//...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

Images are expected to be RGBA, i.e. to have an alpha channel. Not being very fluent in image formats, I believe this is the only limitation. To be save, I'll state that images are expected to be loadable with the above calls.

Damping Textures

Damping Textures, such as those used by PatternHandler and WaveHandler, are 2D textures and they are composed of two files:

  • a .conf file that describes several properties of the texture,
  • a .texture file that contains the texture data in RGBA32F format. These two files are expected to be in the same directory. By convention, the name of the texture does never include any file extensions. Thus a texture with name my_damping_map will consist of the two files my_damping_map.conf and my_damping_map.texture.

A sample .conf file with comments is shown below. Its screen dimensions are what the FocusTerra touchscreen supports natively. Note that the comments are not allowed in real files.

3840 //screen width in pixels
2160 //screen height in pixels
4440 //texture width in texels
3360 //texture height in texels
0    //screen offset left in texels
600  //screen offset right in texels
600  //screen offset bottom in texels
600  //screen offset top in texels

The meaning of the offsets is the following: In this application, we simulate a larger region than what's drawn on the screen. Thus what's drawn on the screen is just a rectangular region within the texture. To describe the placement of this region, we use the number of pixels between the screen region and the texture boundaries in all four directions. That's what these offsets describe.

Note that only if all damping textures agree with the dimensions specified in the application, will the program run correctly. Anything else will give you UB.

A .texture file contains 4 * texture_width * texture_height floating point values, each in the range [0,1]; 0 means this channel is fully off, while 1 means the channel is fully on. Each group of four is to be interpreted as the RGBA values of a single texel. Successive values are delimited with a single space. Each group of four float describes the colour of one texel in the format R G B A. After each 4 * texture_width values, a newline is expected. The only channel that matters for now is the red channel. Damping is multiplicative, such that more red means less damping. For more information on how damping is implemented, study how the damping texture tex_damp comes into play in the timestepping fragment shader.

Texture uploading is done via the following call:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, texture_width, texture_height, 0, GL_RGBA, GL_FLOAT, data_target.data());

One peculiarity to be aware of is the use of different axis conventions. Images typically have the origin in the lower left corner with y-axis pointing upwards, while OpenGL has the origin in the top left corner with the y-axis pointing downwards. This means that the first four floats in the file describe the top left texel, and the first 4 * texture_width values describe the top most row of texels. This is only important when using damping textures that are asymmetric with respect to the horizontal. If it is a problem, consider either flipping the image, or look into calling stbi_set_flip_vertically_on_load(true). This can also be taken care of shader side.

Colour Palettes

A colour palette, like that loaded by WaveHandler::load_palette_ and used to draw the waves in a custom colour scheme, is a 1D texture. Most of the conventions for Damping Textures also apply here. The differences are:

  • The .conf file only contains one integer, the number of texels in the texture
  • The .texture file still contains four space-denominated floats per texel (RGBA), but there is a newline after each texel.

As things stand now, the lowest wave point maps to the first texel, and the highest wave point maps to the last texel.

Similarly to 2D textures, here we upload the data via

glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, palette_.size()/4, 0, GL_RGBA, GL_FLOAT, palette_.data());

Building the Project

Windows

In order to build the project, the following steps must be followed

  • Open the project in Visual Studio 2019
  • Make sure the following files are excluded from the build (Rightclick, Properties, Excluded from Build, Yes:
    • block.hpp
    • blockchain_handler.hpp
    • block.cpp
    • blockchain_handler.cpp
    • imgui_example_sdl_opengl3.cpp
    • main.cpp
    • main_3d.cpp
    • main_refactored.cpp
  • There should be a lot of include-errors reported. To fix this, adjust the paths:
    • In the Solution Explorer, right click on FocusTerra and select Properties
    • Navigate to VCC Directories and adjust the Include Directories. They should point to
      • include
      • include\imgui
      • SDL2-2.0.14\include (e.g. in WindowsSDL, or vclib (not in project))
    • Also adjust the Library Directories. They should point to SDL2-2.0.14\lib\x64.
    • The linked libraries can be seen in Linker, Input, Additional Dependencies. They should not require change, and should include SDL2.lib, SDL2main.lib and opengl32.lib.
  • Open main_testing.cpp. This contains the main function. Adjust it for the target system:
    • Adjust the screen resolution using the #defines at the top. Predefined are the resolutions for the FocusTerra screen and for Pascal's screen.
    • Adjust the top_path to point to the resource folder (i.e. ft_top).
    • Adjust the Derived paths, where necessary. Potentially the tex_path may need adjustment.
    • Adjust the tex_offscreen_* variables to reflect the used texture dimensions.
  • Open input_handler.cpp.
    • For a touchscreen, send an event after the SDL_FINGER* cases. On non-touch devices, send events after the SDL_MOUSE* events.
  • Open slim_blockchain_handler.cpp
    • Depending on the system, uncomment or comment out the section marked Uncomment on a touchscreen.
  • Open drawing_handler.cpp
    • Depending on the system, uncomment or comment out the section marked Uncomment on a touchscreen.
  • Open gui_handler.cpp
    • Depending on the system, change the gui drawcall towards the end in GuiHandler::update.
    • Depending on the system, comment out or uncomment the two sections marked Uncomment on a touchscreen.
  • Rightclick on FocusTerra, select Build.
  • When the building is finished, create a ft_top folder according to the specifications above, and place the executable in the bin folder. If there are library issues, also place a copy of SDL2.dll in the bin folder.
  • Now the application is ready to be run.

macOS

Building on macOS is currently not supported.

Documentation

The following is a documentation for all the code used in the project. As the project is currently at development halt, there are a few known issues that can not be addressed for the time being. They are pointed out in their respective sections.

Architecture

The application is split into different modules, each module handles a subset of the total functionality. The major modules and their responsibilities are

  • WaveHandler
    • Handle the wave textures
    • Handle integration of the wave equation
    • Handle rendering the wave
    • Handle certain texture initialisations for other modules (this should be changed)
  • InputHandler
    • Interface with SDL to fetch user input
    • Translate user input to a usable format (Pevent)
    • Communicate user input to other modules
  • GuiHandler
    • Interface with ImGui
    • Draw the GUI
    • Communicate GUI input to other modules
  • SlimBlockchainHandler
    • Handle the "Spielen" functionality (place, move, remove blocks)
    • Draw to wave and damping textures to implement "Spielen"
  • DrawingHandler
    • Handle the "Zeichnen" and "Radieren" functionality (draw strokes)
    • Draw to wave and damping textures to implement "Zeichnen" and "Radieren"
  • PatternHandler
    • Handle all predefined structure placements
    • Draw structures to damping textures
  • TimeoutHandler
    • Handle the Timeout functionality (if no user input for N seconds, post reset requests for everyone)
  • Toolbox
    • Store information needed by several other modules ("global state")
    • Implement an event chain where current events are stored
    • Implement a mailbox to enable the passing of messages

Much like the name suggests, the Toolbox is passed around from one module to the next, and each can then access everything stored within it. Each module can see all information and decide how it reacts to the current combination of state, messages and events (user input).

The logical flow of the application game loop goes as follows:

  1. InputHandler fetches and writes new user input into the Toolbox event chain
  2. TimeoutHandler checks if there is any user input, and if timeout occurs posts reset requests to the Toolbox mailbox targeting all other modules
  3. GuiHandler checks if user input targets the GUI, updates the global state (e.g. change source frequency, change mouse state to "Zeichnen") and posts appropriate messages for other modules (e.g. place structure)
  4. WaveHandler checks if any messages that target it have been posted
  5. SlimBlockchainHandler reacts to its messages and user input
  6. DrawingHandler reacts to its messages and user input
  7. PatternHandler reacts to its messages and user input
  8. Now all state has been updated, all messages posted and handled and damping textures are final
  9. WaveHandler generates the new damping texture and timesteps the wave
  10. WaveHandler renders the new wave to the screen
  11. GuiHandler renders the GUI to the screen
  12. Renderbuffers are swapped and the frame ends

For an example how this is implemented in practice, see Sample Main Function

Classes

A General Note on Pre-/Postconditions

Sensible preconditions to functions are always implicit. I.e. it is generally assumed that objects are fully and correctly initialized, except when this can obviously not be the case (e.g. for a function initialize). Only "special" preconditions are listed. The same is true for postconditions.

Drawer (drawer.hpp, drawer.cpp)

Description

A Drawer is an object that can draw a single line of segments to the screen. It handles single touch of the Zeichnen functionality. Note that this class relies on the caller to its methods to take care of the Opengl state (using shaders, binding buffers, etc.). See DrawingHandler for more information on how this class is to be used. The coordinates used are typically OpenGL coordinates, i.e. in the range [-1, 1].

Usage

  • Construct object (e.g. when new finger goes down)
  • start_drawing with initial position (e.g. with finger down position)
  • Upon new location (e.g. finger motion to new position), setup Opengl state (use shader draw, bind FBO, bind VAO, bind VBO, set viewport, bind textures) and call draw. Call redraw with all required FBO/texture combinations.
  • Destruct when drawing this line is finished (e.g. finger is lifted)

Constructors and Destructors