projectM-visualizer / projectm

projectM - Cross-platform Music Visualization Library. Open-source and Milkdrop-compatible.
https://discord.gg/mMrxAqaa3W
GNU Lesser General Public License v2.1
3.22k stars 364 forks source link

Support Milkdrop user sprites #734

Open kblaschke opened 9 months ago

kblaschke commented 9 months ago

Milkdrop supports drawing user-defined "sprites", which are simple rectangular images with an optional color key (for transparency in image formats which don't have an alpha channel). These sprites can optionally be animated via expression code, using a few input values like preset timers and the beat detection values.

projectM should also support rendering these sprites, as they're very useful for streamers and making videos with watermarks or additional animated logos on top to save post-processing work and sync the image movement exactly the the visuals.

Milkdrop Sprite Definition File Format

The sprite def file is very similar to the .milk preset format, as it also uses INI syntax. Each sprite is defined in its own section named [imgXX], where XX is a two-digit number. Each section can contain the following lines:

The following expression variables are available in the code_N expressions, updated with the current frame data:

The following output variables are used to draw the sprite:

Default values are used if the expressions don't explicitly set a value. No q or t variables are available to sprite expressions. The regXX vars, gmegabuf and megabuf are individual per-sprite contents, not shared between sprites or with any running presets.

The blending modes are using this "effect" matrix:

// blendmodes                                      src alpha:        dest alpha:
// 0   blend      r,g,b=modulate     a=opacity     SRCALPHA          INVSRCALPHA
// 1   decal      r,g,b=modulate     a=modulate    D3DBLEND_ONE      D3DBLEND_ZERO
// 2   additive   r,g,b=modulate     a=modulate    D3DBLEND_ONE      D3DBLEND_ONE
// 3   srccolor   r,g,b=no effect    a=no effect   SRCCOLOR          INVSRCCOLOR
// 4   colorkey   r,g,b=modulate     a=no effect   

Implementation Notes

Sprite positioning and aspect-correction calculations could be done in a sprite-specific vertex shader, as well as implementing the blending modes in the fragment shader. This should make the C++ code a bit cleaner.

When implementing user sprite support in projectM, these sprites must be drawn after the whole preset rendering process and any transition blending is done, effectively being the last step before returning the render_frame method. This makes sure that sprites are only drawn once and continue working even after introducing new preset formats besides Milkdrop.

Burning-in stuff into presets is currently not implemented. Could be implemented easily by adding a method to the Preset interface class, returning a reference to the framebuffer holding the current texture which is passed to the next frame, with this texture being the only color attachment. Instead of drawing into the final output framebuffer, the sprite can be drawn here. Note that during blending, this must be done with both running presets!

Put all sprite-related API functions into a separate header, e.g. sprites.h and include the header in projectm.h.

Adding sprites should be done via a new API function, which takes the sprite definition as text (const char*) and also could have an additional "type" parameter so we can later add additional sprite types which differ from the Milkdrop format. Sprite data should be formatted in the same way Milkdrop accepts them (see below).

In Milkdrop, sprites are "launched" or killed by the user entering a two-digit number. The sprites exists either until the sprite sets the done flag or the user actively kills the sprite. Since libprojectM doesn't handle any keyboard input, we need API methods to start or kill specific sprites.

There should also be a method to clear all loaded sprites, and optionally additional methods to enumerate and replace/unload specific sprites. Do not confuse this with the launch/kill methods above, which control whether a sprite is drawn or not.

libprojectM must not search for sprite definition files on its own - adding sprites is solely application-controlled.

Milkdrop Source References

Here's a list of relevant code parts in the Milkdrop sources related to sprite rendering: