Skip to content

Commit

Permalink
Move noise texture creation into MilkdropNoise class.
Browse files Browse the repository at this point in the history
No need to create an instance of the class as all generators are static. Use std::vector instead of C-style pointers as pixel buffer.
  • Loading branch information
kblaschke committed Jan 15, 2024
1 parent 04bf637 commit 0e8a5c2
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 106 deletions.
146 changes: 119 additions & 27 deletions src/libprojectM/Renderer/MilkdropNoise.cpp
Original file line number Diff line number Diff line change
@@ -1,42 +1,131 @@
#include "MilkdropNoise.hpp"

#include "projectM-opengl.h"

#include <chrono>
#include <random>

// Missing in macOS SDK. Query will most certainly fail, but then use the default format.
#ifndef GL_TEXTURE_IMAGE_FORMAT
#define GL_TEXTURE_IMAGE_FORMAT 0x828F
#endif

namespace libprojectM {
namespace Renderer {

MilkdropNoise::MilkdropNoise()
auto MilkdropNoise::LowQuality() -> std::shared_ptr<Texture>
{
GLuint texture{};
{
auto textureData = generate2D(256, 1);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}
return std::make_shared<Texture>("noise_lq", texture, GL_TEXTURE_2D, 256, 256, false);
}

auto MilkdropNoise::LowQualityLite() -> std::shared_ptr<Texture>
{
GLuint texture{};

{
auto textureData = generate2D(32, 1);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}

return std::make_shared<Texture>("noise_lq_lite", texture, GL_TEXTURE_2D, 32, 32, false);
}

auto MilkdropNoise::MediumQuality() -> std::shared_ptr<Texture>
{
GLuint texture{};

{
auto textureData = generate2D(256, 4);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}
return std::make_shared<Texture>("noise_mq", texture, GL_TEXTURE_2D, 256, 256, false);
}

auto MilkdropNoise::HighQuality() -> std::shared_ptr<Texture>
{
GLuint texture{};

{
auto textureData = generate2D(256, 8);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}

return std::make_shared<Texture>("noise_hq", texture, GL_TEXTURE_2D, 256, 256, false);
}

auto MilkdropNoise::LowQualityVolume() -> std::shared_ptr<Texture>
{
GLuint texture{};

{
auto textureData = generate3D(32, 1);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_3D, texture);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, 32, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}

return std::make_shared<Texture>("noisevol_lq", texture, GL_TEXTURE_3D, 32, 32, false);
}

auto MilkdropNoise::HighQualityVolume() -> std::shared_ptr<Texture>
{
generate2D(256, 1, &noise_lq);
generate2D(32, 1, &noise_lq_lite);
generate2D(256, 4, &noise_mq);
generate2D(256, 8, &noise_hq);
GLuint texture{};

{
auto textureData = generate3D(32, 4);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_3D, texture);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, 32, 0, GetPreferredInternalFormat(), GL_UNSIGNED_BYTE, textureData.data());
}

generate3D(32, 1, &noise_lq_vol);
generate3D(32, 4, &noise_hq_vol);
return std::make_shared<Texture>("noisevol_hq", texture, GL_TEXTURE_3D, 32, 32, false);
}

MilkdropNoise::~MilkdropNoise()
auto MilkdropNoise::GetPreferredInternalFormat() -> int
{
delete[] noise_lq;
delete[] noise_lq_lite;
delete[] noise_mq;
delete[] noise_hq;
delete[] noise_lq_vol;
delete[] noise_hq_vol;
#if !USE_GLES
// Query preferred internal texture format. GLES 3 only supports GL_RENDERBUFFER here, no texture targets.
// That's why we use GL_BGRA as default, as this is the best general-use format according to Khronos.
GLint preferredInternalFormat{GL_BGRA};
glGetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_TEXTURE_IMAGE_FORMAT, sizeof(preferredInternalFormat), &preferredInternalFormat);
#else
// GLES only supports GL_RGB and GL_RGBA, so we always use the latter.
GLint preferredInternalFormat{GL_RGBA};
#endif

return preferredInternalFormat;
}

void MilkdropNoise::generate2D(int size, int zoomFactor, uint32_t** textureData)
auto MilkdropNoise::generate2D(int size, int zoomFactor) -> std::vector<uint32_t>
{
auto randomSeed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine randomGenerator(randomSeed);
std::uniform_int_distribution<int> randomDistribution(0, INT32_MAX);

*textureData = new uint32_t[size * size]();
std::vector<uint32_t> textureData;
textureData.resize(size * size);

// write to the bits...
auto dst = *textureData;
auto dst = textureData.data();
auto RANGE = (zoomFactor > 1) ? 216 : 256;
for (auto y = 0; y < size; y++)
{
Expand All @@ -62,7 +151,7 @@ void MilkdropNoise::generate2D(int size, int zoomFactor, uint32_t** textureData)
// smoothing
if (zoomFactor > 1)
{
dst = *textureData;
dst = textureData.data();

// first go ACROSS, blending cubically on X, but only on the main lines.
for (auto y = 0; y < size; y += zoomFactor)
Expand All @@ -78,7 +167,7 @@ void MilkdropNoise::generate2D(int size, int zoomFactor, uint32_t** textureData)
auto y2 = dst[base_y + ((base_x + zoomFactor) % size)];
auto y3 = dst[base_y + ((base_x + zoomFactor * 2) % size)];

auto t = static_cast<float>(x % zoomFactor) / static_cast<float>( zoomFactor);
auto t = static_cast<float>(x % zoomFactor) / static_cast<float>(zoomFactor);

auto result = dwCubicInterpolate(y0, y1, y2, y3, t);

Expand Down Expand Up @@ -108,23 +197,26 @@ void MilkdropNoise::generate2D(int size, int zoomFactor, uint32_t** textureData)
}
}
}

}

return textureData;
}

void MilkdropNoise::generate3D(int size, int zoomFactor, uint32_t** textureData)
auto MilkdropNoise::generate3D(int size, int zoomFactor) -> std::vector<uint32_t>
{
auto randomSeed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine randomGenerator(randomSeed);
std::uniform_int_distribution<int> randomDistribution(0, INT32_MAX);

*textureData = new uint32_t[size * size * size]();

std::vector<uint32_t> textureData;
textureData.resize(size * size * size);

// write to the bits...
int RANGE = (zoomFactor > 1) ? 216 : 256;
for (auto z = 0; z < size; z++)
{
auto dst = (*textureData) + z * size * size;
auto dst = (textureData.data()) + z * size * size;
for (auto y = 0; y < size; y++)
{
for (auto x = 0; x < size; x++)
Expand All @@ -151,7 +243,7 @@ void MilkdropNoise::generate3D(int size, int zoomFactor, uint32_t** textureData)
if (zoomFactor > 1)
{
// first go ACROSS, blending cubically on X, but only on the main lines.
auto dst = *textureData;
auto dst = textureData.data();
for (auto z = 0; z < size; z += zoomFactor)
{
for (auto y = 0; y < size; y += zoomFactor)
Expand Down Expand Up @@ -228,8 +320,9 @@ void MilkdropNoise::generate3D(int size, int zoomFactor, uint32_t** textureData)
}
}
}

}

return textureData;
}

float MilkdropNoise::fCubicInterpolate(float y0, float y1, float y2, float y3, float t)
Expand All @@ -254,8 +347,7 @@ uint32_t MilkdropNoise::dwCubicInterpolate(uint32_t y0, uint32_t y1, uint32_t y2
static_cast<float>((y1 >> shift) & 0xFF) / 255.0f,
static_cast<float>((y2 >> shift) & 0xFF) / 255.0f,
static_cast<float>((y3 >> shift) & 0xFF) / 255.0f,
t
);
t);
if (f < 0)
{
f = 0;
Expand Down
53 changes: 39 additions & 14 deletions src/libprojectM/Renderer/MilkdropNoise.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#pragma once

#include <Renderer/Texture.hpp>

#include <cstdint>
#include <memory>
#include <vector>

namespace libprojectM {
Expand All @@ -24,36 +27,58 @@ namespace Renderer {
class MilkdropNoise
{
public:
MilkdropNoise() = delete;

/**
* Constructor. Generates all noise textures.
* Low-quality (high frequency) 2D noise texture, 256x256 with zoom level 1
* @return A new noise texture ready for use in rendering.
*/
MilkdropNoise();
static auto LowQuality() -> std::shared_ptr<Texture>;

/**
* Destructor. Deletes the allocated noise textures.
* Low-quality (high frequency) 2D noise texture, 32x32 with zoom level 1
* @return A new noise texture ready for use in rendering.
*/
~MilkdropNoise();
static auto LowQualityLite() -> std::shared_ptr<Texture>;

uint32_t* noise_lq{ nullptr }; //!< Low-quality (high frequency) 2D noise texture, 256x256 with zoom level 1
uint32_t* noise_lq_lite{ nullptr }; //!< Low-quality (high frequency) 2D noise texture, 32x32 with zoom level 1
uint32_t* noise_mq{ nullptr }; //!< Medium-quality (medium frequency) 2D noise texture, 256x256 with zoom level 4
uint32_t* noise_hq{ nullptr }; //!< High-quality (low frequency) 2D noise texture, 256x256 with zoom level 8
/**
* Medium-quality (medium frequency) 2D noise texture, 256x256 with zoom level 4
* @return A new noise texture ready for use in rendering.
*/
static auto MediumQuality() -> std::shared_ptr<Texture>;

uint32_t* noise_lq_vol{ nullptr }; //!< Low-quality (high frequency) 3D noise texture, 32x32 with zoom level 1
uint32_t* noise_hq_vol{ nullptr }; //!< High-quality (low frequency) 3D noise texture, 32x32 with zoom level 4
/**
* High-quality (low frequency) 2D noise texture, 256x256 with zoom level 8
* @return A new noise texture ready for use in rendering.
*/
static auto HighQuality() -> std::shared_ptr<Texture>;

/**
* Low-quality (high frequency) 3D noise texture, 32x32 with zoom level 1
* @return A new noise texture ready for use in rendering.
*/
static auto LowQualityVolume() -> std::shared_ptr<Texture>;

/**
* High-quality (low frequency) 3D noise texture, 32x32 with zoom level 4
* @return A new noise texture ready for use in rendering.
*/
static auto HighQualityVolume() -> std::shared_ptr<Texture>;

protected:

static auto GetPreferredInternalFormat() -> int;

/**
* @brief Milkdrop 2D noise algorithm
*
* Creates a different, smoothed noise texture in each of the four color channels.
*
* @param size Texture size in pixels.
* @param zoomFactor Zoom factor. Higher values give a more smoothed/interpolated look.
* @param textureData A pointer to the data structure that will receive the texture data. Must have size² elements.
* @return A vector with the texture data. Contains size² elements.
*/
static void generate2D(int size, int zoomFactor, uint32_t** textureData);
static auto generate2D(int size, int zoomFactor) -> std::vector<uint32_t>;

/**
* @brief Milkdrop §D noise algorithm
Expand All @@ -62,9 +87,9 @@ class MilkdropNoise
*
* @param size Texture size in pixels.
* @param zoomFactor Zoom factor. Higher values give a more smoothed/interpolated look.
* @param textureData A pointer to the data structure that will receive the texture data. Must have size³ elements.
* @return A vector with the texture data. Contains size³ elements.
*/
static void generate3D(int size, int zoomFactor, uint32_t** textureData);
static auto generate3D(int size, int zoomFactor) -> std::vector<uint32_t>;

static float fCubicInterpolate(float y0, float y1, float y2, float y3, float t);

Expand Down
Loading

0 comments on commit 0e8a5c2

Please sign in to comment.