Files
projectm/src/libprojectM/Renderer/Renderer.cpp
2020-07-15 21:04:29 +04:00

1164 lines
31 KiB
C++

#include "Renderer.hpp"
#include "wipemalloc.h"
#include "math.h"
#include "Common.hpp"
#include "KeyHandler.hpp"
#include "TextureManager.hpp"
#include "MilkdropWaveform.hpp"
#include <iostream>
#include <algorithm>
#include <sys/stat.h>
#include <cassert>
#include "omptl/omptl"
#include "omptl/omptl_algorithm"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <chrono>
#include <ctime>
Pipeline* Renderer::currentPipe;
using namespace std::chrono;
class Preset;
#ifdef USE_TEXT_MENU
void Renderer::drawText(const char* string, GLfloat x, GLfloat y, GLfloat scale,
int horizontalAlignment = GLT_LEFT, int verticalAlignment = GLT_TOP)
{
drawText(this->title_font, string, x, y, scale, horizontalAlignment, verticalAlignment);
}
void Renderer::drawText(GLTtext* text, const char* string, GLfloat x, GLfloat y, GLfloat scale,
int horizontalAlignment = GLT_LEFT, int verticalAlignment = GLT_TOP)
{
// Initialize glText
gltInit();
// Creating text
text = gltCreateText();
// Begin text drawing (this for instance calls glUseProgram)
gltBeginDraw();
// Draw text transparently first.
gltColor(0.0f, 0.0f, 0.0f, 0.0f);
gltSetText(text, string);
// Where horizontal is either:
// - GLT_LEFT (default)
// - GLT_CENTER
// - GLT_RIGHT
// Where vertical is either:
// - GLT_TOP (default)
// - GLT_CENTER
// - GLT_BOTTOM
gltDrawText2DAligned(text, x, y, scale, horizontalAlignment, verticalAlignment);
GLfloat textWidth = gltGetTextWidth(text, scale);
float windowWidth = vw;
if (horizontalAlignment == GLT_LEFT) {
// if left aligned factor in X offset
windowWidth = vw - x;
}
// if our window is greater than the text width, there is no overflow so let's display it normally.
if (windowWidth > textWidth)
{
// redraw without transparency
gltColor(1.0f, 1.0f, 1.0f, 1.0f);
gltSetText(text, string);
gltDrawText2DAligned(text, x, y, scale, horizontalAlignment, verticalAlignment);
}
else {
// if the text is greater than the window width, we have a problem.
// Option 1: Reduce text length until it fits.
// If it's a single line of text then reduce the text and add a "..." to the end.
// Before: Geiss & Sperl - Feedback (projectM idle HDR mix)
// After: Geiss & Sperl - Feed...
//
// If it's multiline then just cut the text off.
// Before: F1: This help menu
// UP: Increase Beat Sensitivity
// After : F1: This help menu
// UP: Increase Beat Sensi
std::string substring(string);
while (textWidth > windowWidth) {
substring.pop_back();
string = substring.c_str();
gltSetText(text, string);
gltDrawText2DAligned(text, x, y, scale, horizontalAlignment, verticalAlignment);
textWidth = gltGetTextWidth(text, scale);
}
// if it's not multi-line then append a ...
if (substring.find("\n") == -1) {
substring.pop_back();
substring.pop_back();
substring.pop_back();
substring += "...";
}
string = substring.c_str();
// Option 2: Reduce the scale (size) of the text until it fits.
/*
while (textWidth > windowWidth) {
scale = scale - 0.1;
gltDrawText2DAligned(text, x, y, scale, horizontalAlignment, verticalAlignment);
textWidth = gltGetTextWidth(text, scale);
}
*/
// Redraw now that the text fits.
gltColor(1.0f, 1.0f, 1.0f, 1.0f);
gltSetText(text, string);
gltDrawText2DAligned(text, x, y, scale, horizontalAlignment, verticalAlignment);
}
// Finish drawing text
gltEndDraw();
// Deleting text
gltDeleteText(text);
// Destroy glText
gltTerminate();
}
#endif /** USE_TEXT_MENU */
Renderer::Renderer(int width, int height, int gx, int gy, BeatDetect* _beatDetect, std::string _presetURL,
std::string _titlefontURL, std::string _menufontURL, const std::string& datadir) :
mesh(gx, gy), m_presetName("None"), m_datadir(datadir), vw(width), vh(height),
title_fontURL(_titlefontURL), menu_fontURL(_menufontURL), presetURL(_presetURL)
{
this->totalframes = 1;
this->lastTimeFPS = nowMilliseconds();
this->currentTimeFPS = nowMilliseconds();
this->lastTimeToast = nowMilliseconds();
this->currentTimeToast = nowMilliseconds();
this->noSwitch = false;
this->showfps = false;
this->showtoast = false;
this->showtitle = false;
this->showpreset = false;
this->showhelp = false;
this->showstats = false;
this->studio = false;
this->realfps = 0;
/* Set up the v xoffset and vy offset to 0 which is normal Only used for VR */
this->vstartx = 0;
this->vstarty = 0;
this->drawtitle = 0;
// This is the default help menu for applications that have not defined any custom menu.
const char* defaultHelpMenu = "\n"
"F1: This help menu""\n"
"F3: Show preset name""\n"
"F5: Show FPS""\n"
"L: Lock/Unlock Preset""\n"
"R: Random preset""\n"
"N: Next preset""\n"
"P: Previous preset""\n"
"UP: Increase Beat Sensitivity""\n"
"DOWN: Decrease Beat Sensitivity""\n"
"CTRL-F: Fullscreen";
this->setHelpText(defaultHelpMenu);
//this->title = "Unknown";
/** Other stuff... */
this->correction = true;
/// @bug put these on member init list
this->textureManager = nullptr;
this->beatDetect = _beatDetect;
textureRenderToTexture = 0;
int size = (mesh.height - 1) * mesh.width * 4 * 2;
p = static_cast<float *>(wipemalloc(size * sizeof(float)));
for (int j = 0; j < mesh.height - 1; j++)
{
int base = j * mesh.width * 4 * 2;
for (int i = 0; i < mesh.width; i++)
{
int index = j * mesh.width + i;
int index2 = (j + 1) * mesh.width + i;
int strip = base + i * 8;
p[strip + 0] = mesh.identity[index].x;
p[strip + 1] = mesh.identity[index].y;
p[strip + 4] = mesh.identity[index2].x;
p[strip + 5] = mesh.identity[index2].y;
}
}
renderContext.programID_v2f_c4f = shaderEngine.programID_v2f_c4f;
renderContext.programID_v2f_c4f_t2f = shaderEngine.programID_v2f_c4f_t2f;
renderContext.uniform_v2f_c4f_vertex_tranformation = shaderEngine.uniform_v2f_c4f_vertex_tranformation;
renderContext.uniform_v2f_c4f_vertex_point_size = shaderEngine.uniform_v2f_c4f_vertex_point_size;
renderContext.uniform_v2f_c4f_t2f_vertex_tranformation = shaderEngine.uniform_v2f_c4f_t2f_vertex_tranformation;
renderContext.uniform_v2f_c4f_t2f_frag_texture_sampler = shaderEngine.uniform_v2f_c4f_t2f_frag_texture_sampler;
// Interpolation VAO/VBO's
glGenBuffers(1, &m_vbo_Interpolation);
glGenVertexArrays(1, &m_vao_Interpolation);
glBindVertexArray(m_vao_Interpolation);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_Interpolation);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, static_cast<void*>(nullptr)); // Positions
glDisableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// CompositeOutput VAO/VBO's
glGenBuffers(1, &m_vbo_CompositeOutput);
glGenVertexArrays(1, &m_vao_CompositeOutput);
float composite_buffer_data[8][2] =
{
{-0.5, -0.5},
{0, 1},
{-0.5, 0.5},
{0, 0},
{0.5, 0.5},
{1, 0},
{0.5, -0.5},
{1, 1}
};
glBindVertexArray(m_vao_CompositeOutput);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeOutput);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8 * 2, composite_buffer_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, static_cast<void*>(nullptr)); // Positions
glDisableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); // Textures
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// CompositeShaderOutput VAO/VBO's
glGenBuffers(1, &m_vbo_CompositeShaderOutput);
glGenVertexArrays(1, &m_vao_CompositeShaderOutput);
glBindVertexArray(m_vao_CompositeShaderOutput);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeShaderOutput);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), static_cast<void*>(nullptr));
// Positions
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 2));
// Colors
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 6)); // UV
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(composite_shader_vertex), (void*)(sizeof(float) * 8));
// RAD ANG
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
std::string Renderer::SetPipeline(Pipeline& pipeline)
{
currentPipe = &pipeline;
shaderEngine.reset();
if (!shaderEngine.loadPresetShaders(pipeline, m_presetName))
{
return "Shader compilation error";
}
return std::string();
}
void Renderer::ResetTextures()
{
textureManager->Clear();
reset(vw, vh);
textureManager->Preload();
}
void Renderer::SetupPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
totalframes++;
/*
If FPS is displayed (by pressing F5 or by config):
- check if 250 milliseconds has passed (1/4 of a second)
- multiply the total rendered frames (totalframes) by the fraction of a second that passed to get the approximate frames per second count.
- reset the totalframes and timer (lastTime) so we don't trigger for another 250 milliseconds.
*/
if (this->showfps)
{
this->currentTimeFPS = nowMilliseconds();
if (timeCheck(this->currentTimeFPS, this->lastTimeFPS, (double)250)) {
this->realfps = totalframes * (1000 / 250);
setFPS(realfps);
totalframes = 0;
this->lastTimeFPS = nowMilliseconds();
}
}
glViewport(0, 0, texsizeX, texsizeY);
renderContext.mat_ortho = glm::ortho(0.0f, 1.0f, 0.0f, 1.0f, -40.0f, 40.0f);
}
void Renderer::RenderItems(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
renderContext.time = pipelineContext.time;
renderContext.texsize = nearestPower2(std::max(texsizeX, texsizeY));
renderContext.aspectCorrect = correction;
renderContext.aspectRatio = aspect;
renderContext.textureManager = textureManager;
renderContext.beatDetect = beatDetect;
for (std::vector<RenderItem*>::const_iterator pos = pipeline.drawables.begin(); pos != pipeline.drawables.end(); ++pos)
{
if (*pos != nullptr)
(*pos)->Draw(renderContext);
}
// If we have touch waveforms, render them.
if (waveformList.size() >= 1) {
RenderTouch(pipeline,pipelineContext);
}
}
void Renderer::RenderTouch(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
Pipeline pipelineTouch;
MilkdropWaveform wave;
for(int x = 0; x < waveformList.size(); x++){
pipelineTouch.drawables.push_back(&wave);
wave = waveformList[x];
// Render waveform
for (std::vector<RenderItem*>::const_iterator pos = pipelineTouch.drawables.begin(); pos != pipelineTouch.drawables.end(); ++pos)
{
if (*pos != nullptr)
(*pos)->Draw(renderContext);
}
}
}
void Renderer::FinishPass1()
{
draw_title_to_texture();
textureManager->updateMainTexture();
}
void Renderer::Pass2(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
//BEGIN PASS 2
//
//end of texture rendering
//now we copy the texture from the FBO or framebuffer to
//video texture memory and render fullscreen.
/** Reset the viewport size */
if (textureRenderToTexture)
{
glViewport(0, 0, texsizeX, texsizeY);
}
else
glViewport(vstartx, vstarty, this->vw, this->vh);
if (shaderEngine.enableCompositeShader(currentPipe->compositeShader, pipeline, pipelineContext))
{
CompositeShaderOutput(pipeline, pipelineContext);
}
else
{
CompositeOutput(pipeline, pipelineContext);
}
// When console refreshes, there is a chance the preset has been changed by the user
refreshConsole();
// TODO:
draw_title_to_screen(false);
if (this->showhelp == true)
draw_help();
if (this->showtitle == true)
draw_title();
if (this->showtoast == true)
draw_toast();
if (this->showfps == true)
draw_fps();
// this->realfps
if (this->showpreset == true)
draw_preset();
if (this->showstats == true)
draw_stats();
}
void Renderer::RenderFrame(const Pipeline& pipeline,
const PipelineContext& pipelineContext)
{
RenderFrameOnlyPass1(pipeline, pipelineContext);
Pass2(pipeline, pipelineContext);
}
void Renderer::RenderFrameOnlyPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
shaderEngine.RenderBlurTextures(pipeline, pipelineContext);
SetupPass1(pipeline, pipelineContext);
Interpolation(pipeline, pipelineContext);
RenderItems(pipeline, pipelineContext);
FinishPass1();
}
void Renderer::RenderFrameOnlyPass2(const Pipeline& pipeline, const PipelineContext& pipelineContext, int xoffset,
int yoffset, int eye)
{
/* draw in a certain range of the screen */
vstartx = xoffset;
vstarty = yoffset;
// ignore eye
Pass2(pipeline, pipelineContext);
vstartx = 0;
vstarty = 0;
}
void Renderer::Interpolation(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureManager->getMainTexture()->texID);
//Texture wrapping( clamp vs. wrap)
if (pipeline.textureWrap == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
int size = (mesh.height - 1) * mesh.width * 4 * 2;
if (pipeline.staticPerPixel)
{
for (int j = 0; j < mesh.height - 1; j++)
{
int base = j * mesh.width * 2 * 4;
for (int i = 0; i < mesh.width; i++)
{
int strip = base + i * 8;
p[strip + 2] = pipeline.x_mesh[i][j];
p[strip + 3] = pipeline.y_mesh[i][j];
p[strip + 6] = pipeline.x_mesh[i][j + 1];
p[strip + 7] = pipeline.y_mesh[i][j + 1];
}
}
}
else
{
mesh.Reset();
omptl::transform(mesh.p.begin(), mesh.p.end(), mesh.identity.begin(), mesh.p.begin(), &Renderer::PerPixel);
for (int j = 0; j < mesh.height - 1; j++)
{
int base = j * mesh.width * 2 * 4;
for (int i = 0; i < mesh.width; i++)
{
int strip = base + i * 8;
int index = j * mesh.width + i;
int index2 = (j + 1) * mesh.width + i;
p[strip + 2] = mesh.p[index].x;
p[strip + 3] = mesh.p[index].y;
p[strip + 6] = mesh.p[index2].x;
p[strip + 7] = mesh.p[index2].y;
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_Interpolation);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, nullptr, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, p, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
shaderEngine.enableWarpShader(currentPipe->warpShader, pipeline, pipelineContext, renderContext.mat_ortho);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, pipeline.screenDecay);
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
glBindVertexArray(m_vao_Interpolation);
for (int j = 0; j < mesh.height - 1; j++)
glDrawArrays(GL_TRIANGLE_STRIP, j * mesh.width * 2, mesh.width * 2);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, 0);
}
Renderer::~Renderer()
{
if (textureManager)
delete (textureManager);
free(p);
glDeleteBuffers(1, &m_vbo_Interpolation);
glDeleteVertexArrays(1, &m_vao_Interpolation);
glDeleteBuffers(1, &m_vbo_CompositeOutput);
glDeleteVertexArrays(1, &m_vao_CompositeOutput);
glDeleteTextures(1, &textureRenderToTexture);
}
void Renderer::reset(int w, int h)
{
aspect = static_cast<float>(h) / static_cast<float>(w);
this->vw = w;
this->vh = h;
glCullFace(GL_BACK);
#ifndef GL_TRANSITION
glEnable(GL_LINE_SMOOTH);
#endif
glClearColor(0, 0, 0, 0);
glViewport(vstartx, vstarty, w, h);
glEnable(GL_BLEND);
texsizeX = w;
texsizeY = h;
// snap to 16x16 blocks
texsizeX = ((texsizeX - 15) / 16) * 16;
texsizeY = ((texsizeY - 15) / 16) * 16;
m_fAspectX = (texsizeY > texsizeX) ? static_cast<float>(texsizeX) / static_cast<float>(texsizeY) : 1.0f;
m_fAspectY = (texsizeX > texsizeY) ? static_cast<float>(texsizeY) / static_cast<float>(texsizeX) : 1.0f;
m_fInvAspectX = 1.0f / m_fAspectX;
m_fInvAspectY = 1.0f / m_fAspectY;
InitCompositeShaderVertex();
if (textureManager != nullptr)
{
delete textureManager;
}
textureManager = new TextureManager(presetURL, texsizeX, texsizeY, m_datadir);
shaderEngine.setParams(texsizeX, texsizeY, beatDetect, textureManager);
shaderEngine.reset();
shaderEngine.loadPresetShaders(*currentPipe, m_presetName);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClear(GL_COLOR_BUFFER_BIT);
}
GLuint Renderer::initRenderToTexture()
{
if (textureRenderToTexture == 0)
{
glGenTextures(1, &textureRenderToTexture);
glBindTexture(GL_TEXTURE_2D, textureRenderToTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texsizeX, texsizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
}
return textureRenderToTexture;
}
void Renderer::draw_title_to_texture()
{
#ifdef USE_TEXT_MENU
if (this->drawtitle > 100)
{
draw_title_to_screen(true);
this->drawtitle = 0;
}
#endif /** USE_TEXT_MENU */
}
float title_y;
bool Renderer::timeCheck(const milliseconds currentTime, const milliseconds lastTime, const double difference) {
milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastTime);
double diff = ms.count();
if (diff >= difference)
{
return true;
} else {
return false;
}
}
// Render a waveform when a touch event is triggered.
void Renderer::touch(float x, float y, int pressure, int type = 0)
{
// if we left clicked in the approximate position of a waveform, then it doens't make sense to add something on top. Instead give the option to drag the existing waveform.
// For lines we don't worry about the x axis.
for (int i = 0; i < waveformList.size(); i++) {
if (waveformList[i].x > (x-0.05f) && waveformList[i].x < (x+0.05f) && ((waveformList[i].y > (y-0.05f) && waveformList[i].y < (y+0.05f)) || waveformList[i].mode == Line || waveformList[i].mode == DoubleLine || waveformList[i].mode == DerivativeLine ))
{
touchDrag(x, y, pressure);
return;
}
}
touchx = x;
touchy = y;
touchp = pressure;
// If we touched randomly, then assign type to a random number between 0 and 8
if (type == 0) {
type = (rand() % 9) + 1;
}
touchtype = type;
// Randomly select colours on touch
touchr = ((double)rand() / (RAND_MAX));
touchb = ((double)rand() / (RAND_MAX));
touchg = ((double)rand() / (RAND_MAX));
toucha = ((double)rand() / (RAND_MAX));
// Create a waveform. The touch x / y was based on where you clicked (approximately).
MilkdropWaveform wave;
switch (touchtype)
{
case 1:
wave.mode = Circle;
break;
/* Radial Blob flies off and doesn't track your mouse.
case 2:
wave.mode = RadialBlob;
break;
*/
case 3:
wave.mode = Blob2;
break;
case 4:
wave.mode = Blob3;
break;
case 5:
wave.mode = DerivativeLine;
break;
case 6:
wave.mode = Blob5;
break;
/* Disabling lines because they are not intuitive
case 7:
wave.mode = Line;
break;
case 8:
wave.mode = DoubleLine;
break;
*/
default:
wave.mode = Circle;
}
wave.additive = true;
wave.modOpacityEnd = 1.1;
wave.modOpacityStart = 0.0;
wave.maximizeColors = true;
wave.modulateAlphaByVolume = false;
wave.r = touchr;
wave.g = touchg;
wave.b = touchb;
wave.a = toucha;
wave.x = touchx;
wave.y = touchy;
// add new waveform to waveformTouchList
waveformList.push_back(wave);
}
// Move a waveform when dragging X, Y, and Pressure can change. We also extend the counters so it will stay on screen for as long as you click and drag.
void Renderer::touchDrag(float x, float y, int pressure)
{
// if we left clicked & held in the approximate position of a waveform, snap to it and adjust x / y to simulate dragging.
// For lines we don't worry about the x axis.
for (int i = 0; i < waveformList.size(); i++) {
if (waveformList[i].x > (x-0.05f) && waveformList[i].x < (x+0.05f) && ((waveformList[i].y > (y-0.05f) && waveformList[i].y < (y+0.05f)) || waveformList[i].mode == Line || waveformList[i].mode == DoubleLine || waveformList[i].mode == DerivativeLine ))
{
waveformList[i].x = x;
waveformList[i].y = y;
}
}
touchp = pressure;
}
// Remove waveform at X Y
void Renderer::touchDestroy(float x, float y)
{
// if we right clicked approximately on the position of the waveform, then remove it from the waveform list.
// For lines we don't worry about the x axis.
for (int i = 0; i < waveformList.size(); i++) {
if (waveformList[i].x > (x-0.05f) && waveformList[i].x < (x+0.05f) && ((waveformList[i].y > (y-0.05f) && waveformList[i].y < (y+0.05f)) || waveformList[i].mode == Line || waveformList[i].mode == DoubleLine || waveformList[i].mode == DerivativeLine ))
{
waveformList.erase(waveformList.begin() + i);
}
}
}
// Remove all waveforms
void Renderer::touchDestroyAll()
{
waveformList.clear();
}
void Renderer::setToastMessage(const std::string& theValue)
{
// Initialize counters
lastTimeToast= nowMilliseconds();
currentTimeToast = nowMilliseconds();
m_toastMessage = theValue;
showtoast = true;
}
// TODO:
void Renderer::draw_title_to_screen(bool flip)
{
#ifdef USE_TEXT_MENU
if (this->drawtitle > 0)
{
}
#endif /** USE_TEXT_MENU */
}
void Renderer::draw_title()
{
#ifdef USE_TEXT_MENU
// TODO: investigate possible banner text for GUI
// drawText(title_font, this->title.c_str(), 10, 20, 2.5);
#endif /** USE_TEXT_MENU */
}
void Renderer::draw_preset()
{
#ifdef USE_TEXT_MENU
drawText(this->presetName().c_str(), 30, 20, 2.5);
#endif /** USE_TEXT_MENU */
}
void Renderer::draw_help()
{
#ifdef USE_TEXT_MENU
// TODO: match winamp/milkdrop bindings
drawText(this->helpText().c_str(), 30, 20, 2.5);
#endif /** USE_TEXT_MENU */
}
// fake rounding - substr is good enough.
std::string Renderer::float_stats(float stat)
{
std::string num_text = std::to_string(stat);
std::string rounded = num_text.substr(0, num_text.find(".")+4);
return rounded;
}
// TODO
void Renderer::draw_stats()
{
#ifdef USE_TEXT_MENU
std::string stats = "\n";
std::string warpShader = (!currentPipe->warpShader.programSource.empty()) ? "ON" : "OFF";
std::string compShader = (!currentPipe->compositeShader.programSource.empty()) ? "ON" : "OFF";
stats += "Render:""\n";
stats += "Resolution: " + std::to_string(vw) + "x" + std::to_string(vh) + "\n";
stats += "Mesh X: " + std::to_string(mesh.width) + "\n";
stats += "Mesh Y: " + std::to_string(mesh.height) + "\n";
stats += "\n";
stats += "Beat Detect:""\n";
stats += "Sensitivity: " + float_stats(beatDetect->beatSensitivity) + "\n";
stats += "Bass: " + float_stats(beatDetect->bass) + "\n";
stats += "Mid Range: " + float_stats(beatDetect->mid) + "\n";
stats += "Treble: " + float_stats(beatDetect->treb) + "\n";
stats += "Volume: " + float_stats(beatDetect->vol) + "\n";
stats += "\n";
stats += "Preset:""\n";
stats += "Warp Shader: " + warpShader + "\n";
stats += "Composite Shader: " + compShader + "\n";
drawText(stats.c_str(), 30, 20, 2.5);
#endif /** USE_TEXT_MENU */
}
// TODO
void Renderer::draw_fps()
{
#ifdef USE_TEXT_MENU
drawText(this->fps().c_str(), 30, 20, 2.5);
#endif /** USE_TEXT_MENU */
}
void Renderer::draw_toast()
{
#ifdef USE_TEXT_MENU
drawText(this->toastMessage().c_str(), (vw/2), (vh/2), 2.5, GLT_CENTER, GLT_CENTER);
#endif /** USE_TEXT_MENU */
this->currentTimeToast = nowMilliseconds();
if (timeCheck(this->currentTimeToast,this->lastTimeToast,(double)(TOAST_TIME*1000))) {
this->currentTimeToast = nowMilliseconds();
this->lastTimeToast = nowMilliseconds();
this->showtoast = false;
}
}
void Renderer::CompositeOutput(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureManager->getMainTexture()->texID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
renderContext.mat_ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -40.0f, 40.0f);
shaderEngine.enableCompositeShader(currentPipe->compositeShader, pipeline, pipelineContext);
glUniformMatrix4fv(shaderEngine.uniform_v2f_c4f_t2f_vertex_tranformation, 1, GL_FALSE,
value_ptr(renderContext.mat_ortho));
glUniform1i(shaderEngine.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);
//Overwrite anything on the screen
glBlendFunc(GL_ONE, GL_ZERO);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
glBindVertexArray(m_vao_CompositeOutput);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (std::vector<RenderItem*>::const_iterator pos = pipeline.compositeDrawables.begin(); pos!= pipeline.compositeDrawables.end(); ++pos)
(*pos)->Draw(renderContext);
glBindTexture(GL_TEXTURE_2D, 0);
}
/**
* Calculates the nearest power of two to the given number using the
* appropriate rule
*/
int Renderer::nearestPower2(int value)
{
int x = value;
int power = 0;
if (x == 0)
{
return 0;
}
while ((x & 0x01) != 1)
{
x >>= 1;
}
if (x == 1)
{
return value;
}
x = value;
while (x != 0)
{
x >>= 1;
power++;
}
if (((1 << power) - value) <= (value - (1 << (power - 1))))
{
return 1 << power;
}
return 1 << (power - 1);
return 0;
}
float Renderer::SquishToCenter(float x, float fExp)
{
if (x > 0.5f)
return powf(x * 2 - 1, fExp) * 0.5f + 0.5f;
return (1 - powf(1 - x * 2, fExp)) * 0.5f;
}
void Renderer::UvToMathSpace(float u, float v, float* rad, float* ang)
{
// (screen space = -1..1 on both axes; corresponds to UV space)
// uv space = [0..1] on both axes
// "math" space = what the preset authors are used to:
// upper left = [0,0]
// bottom right = [1,1]
// rad == 1 at corners of screen
// ang == 0 at three o'clock, and increases counter-clockwise (to 6.28).
float px = (u * 2 - 1) * m_fAspectX; // probably 1.0
float py = (v * 2 - 1) * m_fAspectY; // probably <1
*rad = sqrtf(px * px + py * py) / sqrtf(m_fAspectX * m_fAspectX + m_fAspectY * m_fAspectY);
*ang = atan2f(py, px);
if (*ang < 0)
*ang += 6.2831853071796f;
}
void Renderer::InitCompositeShaderVertex()
{
// BUILD VERTEX LIST for final composite blit
memset(m_comp_verts, 0, sizeof(composite_shader_vertex) * FCGSX * FCGSY);
float fDivX = 1.0f / static_cast<float>(FCGSX - 2);
float fDivY = 1.0f / static_cast<float>(FCGSY - 2);
for (int j = 0; j < FCGSY; j++)
{
int j2 = j - j / (FCGSY / 2);
float v = j2 * fDivY;
v = SquishToCenter(v, 3.0f);
float sy = -((v) * 2 - 1);
for (int i = 0; i < FCGSX; i++)
{
int i2 = i - i / (FCGSX / 2);
float u = i2 * fDivX;
u = SquishToCenter(u, 3.0f);
float sx = (u) * 2 - 1;
composite_shader_vertex* pComp = &m_comp_verts[i + j * FCGSX];
pComp->x = sx;
pComp->y = sy;
float rad, ang;
UvToMathSpace(u, v, &rad, &ang);
// fix-ups:
if (i == FCGSX / 2 - 1)
{
if (j < FCGSY / 2 - 1)
ang = 3.1415926535898f * 1.5f;
else if (j == FCGSY / 2 - 1)
ang = 3.1415926535898f * 1.25f;
else if (j == FCGSY / 2)
ang = 3.1415926535898f * 0.75f;
else
ang = 3.1415926535898f * 0.5f;
}
else if (i == FCGSX / 2)
{
if (j < FCGSY / 2 - 1)
ang = 3.1415926535898f * 1.5f;
else if (j == FCGSY / 2 - 1)
ang = 3.1415926535898f * 1.75f;
else if (j == FCGSY / 2)
ang = 3.1415926535898f * 0.25f;
else
ang = 3.1415926535898f * 0.5f;
}
else if (j == FCGSY / 2 - 1)
{
if (i < FCGSX / 2 - 1)
ang = 3.1415926535898f * 1.0f;
else if (i == FCGSX / 2 - 1)
ang = 3.1415926535898f * 1.25f;
else if (i == FCGSX / 2)
ang = 3.1415926535898f * 1.75f;
else
ang = 3.1415926535898f * 2.0f;
}
else if (j == FCGSY / 2)
{
if (i < FCGSX / 2 - 1)
ang = 3.1415926535898f * 1.0f;
else if (i == FCGSX / 2 - 1)
ang = 3.1415926535898f * 0.75f;
else if (i == FCGSX / 2)
ang = 3.1415926535898f * 0.25f;
else
ang = 3.1415926535898f * 0.0f;
}
pComp->tu = u;
pComp->tv = v;
pComp->rad = rad;
pComp->ang = ang;
}
}
// build index list for final composite blit -
// order should be friendly for interpolation of 'ang' value!
int* cur_index = &m_comp_indices[0];
for (int y = 0; y < FCGSY - 1; y++)
{
if (y == FCGSY / 2 - 1)
continue;
for (int x = 0; x < FCGSX - 1; x++)
{
if (x == FCGSX / 2 - 1)
continue;
bool left_half = (x < FCGSX / 2);
bool top_half = (y < FCGSY / 2);
bool center_4 = ((x == FCGSX / 2 || x == FCGSX / 2 - 1) && (y == FCGSY / 2 || y == FCGSY / 2 - 1));
if ((static_cast<int>(left_half) + static_cast<int>(top_half) + static_cast<int>(center_4)) % 2)
{
*(cur_index + 0) = (y) * FCGSX + (x);
*(cur_index + 1) = (y) * FCGSX + (x + 1);
*(cur_index + 2) = (y + 1) * FCGSX + (x + 1);
*(cur_index + 3) = (y + 1) * FCGSX + (x + 1);
*(cur_index + 4) = (y + 1) * FCGSX + (x);
*(cur_index + 5) = (y) * FCGSX + (x);
}
else
{
*(cur_index + 0) = (y + 1) * FCGSX + (x);
*(cur_index + 1) = (y) * FCGSX + (x);
*(cur_index + 2) = (y) * FCGSX + (x + 1);
*(cur_index + 3) = (y) * FCGSX + (x + 1);
*(cur_index + 4) = (y + 1) * FCGSX + (x + 1);
*(cur_index + 5) = (y + 1) * FCGSX + (x);
}
cur_index += 6;
}
}
}
void Renderer::CompositeShaderOutput(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
// hue shader
float shade[4][3] = {
{1.0f, 1.0f, 1.0f},
{1.0f, 1.0f, 1.0f},
{1.0f, 1.0f, 1.0f},
{1.0f, 1.0f, 1.0f}
}; // for each vertex, then each comp.
// pick 4 colors for the 4 corners
for (int i = 0; i < 4; i++)
{
shade[i][0] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0143f + 3 + i * 21);
shade[i][1] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0107f + 1 + i * 13);
shade[i][2] = 0.6f + 0.3f * sinf(pipelineContext.time * 30.0f * 0.0129f + 6 + i * 9);
float max = ((shade[i][0] > shade[i][1]) ? shade[i][0] : shade[i][1]);
if (shade[i][2] > max) max = shade[i][2];
for (int k = 0; k < 3; k++)
{
shade[i][k] /= max;
shade[i][k] = 0.5f + 0.5f * shade[i][k];
}
}
// interpolate the 4 colors & apply to all the verts
for (int j = 0; j < FCGSY; j++)
{
for (int i = 0; i < FCGSX; i++)
{
composite_shader_vertex* pComp = &m_comp_verts[i + j * FCGSX];
float x = pComp->x * 0.5f + 0.5f;
float y = pComp->y * 0.5f + 0.5f;
float col[3] = {1, 1, 1};
for (int c = 0; c < 3; c++)
col[c] = shade[0][c] * (x) * (y) +
shade[1][c] * (1 - x) * (y) +
shade[2][c] * (x) * (1 - y) +
shade[3][c] * (1 - x) * (1 - y);
pComp->Diffuse[0] = col[0];
pComp->Diffuse[1] = col[1];
pComp->Diffuse[2] = col[2];
pComp->Diffuse[3] = 1.0;
}
}
const int primCount = (FCGSX - 2) * (FCGSY - 2) * 6;
composite_shader_vertex tempv[primCount];
memset(tempv, 0, sizeof(composite_shader_vertex) * primCount);
int src_idx = 0;
for (int i = 0; i < primCount; i++)
{
tempv[i] = m_comp_verts[m_comp_indices[src_idx++]];
}
glBindBuffer(GL_ARRAY_BUFFER, m_vbo_CompositeShaderOutput);
glBufferData(GL_ARRAY_BUFFER, sizeof(composite_shader_vertex) * primCount, nullptr, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(composite_shader_vertex) * primCount, tempv, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBlendFunc(GL_ONE, GL_ZERO);
glBindVertexArray(m_vao_CompositeShaderOutput);
// Now do the final composite blit, fullscreen;
glDrawArrays(GL_TRIANGLES, 0, primCount);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}