Files
projectm/src/libprojectM/Renderer/Renderer.cpp

902 lines
26 KiB
C++

#include "Renderer.hpp"
#include "wipemalloc.h"
#include "math.h"
#include "TextureManager.hpp"
#include "MilkdropWaveform.hpp"
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cassert>
#include "omptl/omptl_algorithm"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <chrono>
#include <ctime>
#include "stb_image_write.h"
using namespace std::chrono;
class Preset;
Renderer::Renderer(int viewportWidth, int viewportHeight, int meshX, int meshY,
BeatDetect* beatDetect, std::vector<std::string>& textureSearchPaths)
: m_perPixelMesh(meshX, meshY)
, m_beatDetect(beatDetect)
, m_viewportWidth(viewportWidth)
, m_viewportHeight(viewportHeight)
, m_textureSearchPaths(textureSearchPaths)
{
ResetPerPointMeshBuffer();
m_renderContext.programID_v2f_c4f = m_shaderEngine.programID_v2f_c4f;
m_renderContext.programID_v2f_c4f_t2f = m_shaderEngine.programID_v2f_c4f_t2f;
m_renderContext.uniform_v2f_c4f_vertex_transformation = m_shaderEngine.uniform_v2f_c4f_vertex_transformation;
m_renderContext.uniform_v2f_c4f_vertex_point_size = m_shaderEngine.uniform_v2f_c4f_vertex_point_size;
m_renderContext.uniform_v2f_c4f_t2f_vertex_transformation = m_shaderEngine.uniform_v2f_c4f_t2f_vertex_transformation;
m_renderContext.uniform_v2f_c4f_t2f_frag_texture_sampler = m_shaderEngine.uniform_v2f_c4f_t2f_frag_texture_sampler;
// Interpolation VAO/VBO's
glGenBuffers(1, &m_vboInterpolation);
glGenVertexArrays(1, &m_vaoInterpolation);
glBindVertexArray(m_vaoInterpolation);
glBindBuffer(GL_ARRAY_BUFFER, m_vboInterpolation);
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_vboCompositeOutput);
glGenVertexArrays(1, &m_vaoCompositeOutput);
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_vaoCompositeOutput);
glBindBuffer(GL_ARRAY_BUFFER, m_vboCompositeOutput);
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_vboCompositeShaderOutput);
glGenVertexArrays(1, &m_vaoCompositeShaderOutput);
glBindVertexArray(m_vaoCompositeShaderOutput);
glBindBuffer(GL_ARRAY_BUFFER, m_vboCompositeShaderOutput);
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);
ResetTextures();
}
void Renderer::SetPipeline(Pipeline& pipeline)
{
m_currentPipeline = &pipeline;
m_shaderEngine.reset();
try {
m_shaderEngine.loadPresetShaders(pipeline);
}
catch(const ShaderException& ex)
{
throw RenderException("Shader compilation error: " + ex.message());
}
}
void Renderer::ResetTextures()
{
m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths, m_textureSizeX, m_textureSizeY);
m_shaderEngine.setParams(m_textureSizeX, m_textureSizeY, m_fAspectX, m_fAspectY, m_beatDetect, m_textureManager.get());
}
void Renderer::SetTextureSearchPaths(std::vector<std::string>& textureSearchPaths)
{
m_textureSearchPaths = textureSearchPaths;
ResetTextures();
}
void Renderer::SetPerPixelMeshSize(int meshX, int meshY)
{
m_perPixelMesh = PerPixelMesh(meshX, meshY);
ResetPerPointMeshBuffer();
}
void Renderer::SetupPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
totalframes++;
glViewport(0, 0, m_textureSizeX, m_textureSizeY);
m_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)
{
m_renderContext.time = pipelineContext.time;
m_renderContext.texsize = nearestPower2(std::max(m_textureSizeX, m_textureSizeY));
m_renderContext.viewportSizeX = m_viewportWidth;
m_renderContext.viewportSizeY = m_viewportHeight;
m_renderContext.aspectX = m_fAspectX;
m_renderContext.aspectY = m_fAspectY;
m_renderContext.invAspectX = m_fInvAspectX;
m_renderContext.invAspectY = m_fInvAspectY;
m_renderContext.textureManager = m_textureManager.get();
m_renderContext.beatDetect = m_beatDetect;
for (std::vector<RenderItem*>::const_iterator pos = pipeline.drawables.begin(); pos != pipeline.drawables.end(); ++pos)
{
if (*pos != nullptr)
(*pos)->Draw(m_renderContext);
}
// If we have touch waveforms, render them.
if (m_waveformList.size() >= 1) {
RenderTouch(pipeline,pipelineContext);
}
}
void Renderer::RenderTouch(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
Pipeline pipelineTouch;
MilkdropWaveform wave;
for(std::size_t x = 0; x < m_waveformList.size(); x++){
pipelineTouch.drawables.push_back(&wave);
wave = m_waveformList[x];
// Render waveform
for (std::vector<RenderItem*>::const_iterator pos = pipelineTouch.drawables.begin(); pos != pipelineTouch.drawables.end(); ++pos)
{
if (*pos != nullptr)
(*pos)->Draw(m_renderContext);
}
}
}
void Renderer::FinishPass1()
{
m_textureManager->updateMainTexture();
if (writeNextFrameToFile)
{
debugWriteMainTextureToFile();
writeNextFrameToFile = false;
frameDumpOutputFile = "";
}
}
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 */
glViewport(0, 0, m_viewportWidth, m_viewportHeight);
if (m_shaderEngine.enableCompositeShader(m_currentPipeline->compositeShader, pipeline, pipelineContext))
{
CompositeShaderOutput(pipeline, pipelineContext);
}
else
{
CompositeOutput(pipeline, pipelineContext);
}
}
void Renderer::RenderFrameOnlyPass1(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
m_shaderEngine.RenderBlurTextures(pipeline, pipelineContext);
SetupPass1(pipeline, pipelineContext);
Interpolation(pipeline, pipelineContext);
RenderItems(pipeline, pipelineContext);
FinishPass1();
}
void Renderer::RenderFrameOnlyPass2(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
Pass2(pipeline, pipelineContext);
}
void Renderer::Interpolation(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_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 = (m_perPixelMesh.height - 1) * m_perPixelMesh.width * 4 * 2;
if (pipeline.staticPerPixel)
{
for (int j = 0; j < m_perPixelMesh.height - 1; j++)
{
int base = j * m_perPixelMesh.width * 2 * 4;
for (int i = 0; i < m_perPixelMesh.width; i++)
{
int strip = base + i * 8;
m_perPointMeshBuffer[strip + 2] = pipeline.x_mesh[i][j];
m_perPointMeshBuffer[strip + 3] = pipeline.y_mesh[i][j];
m_perPointMeshBuffer[strip + 6] = pipeline.x_mesh[i][j + 1];
m_perPointMeshBuffer[strip + 7] = pipeline.y_mesh[i][j + 1];
}
}
}
else
{
m_perPixelMesh.Reset();
Pipeline *cp = m_currentPipeline;
omptl::transform(m_perPixelMesh.p.begin(), m_perPixelMesh.p.end(), m_perPixelMesh.identity.begin(), m_perPixelMesh.p.begin(),
[cp](PixelPoint point, PerPixelContext &context) {
return cp->PerPixel(point, context);
});
for (int j = 0; j < m_perPixelMesh.height - 1; j++)
{
int base = j * m_perPixelMesh.width * 2 * 4;
for (int i = 0; i < m_perPixelMesh.width; i++)
{
int strip = base + i * 8;
int index = j * m_perPixelMesh.width + i;
int index2 = (j + 1) * m_perPixelMesh.width + i;
m_perPointMeshBuffer[strip + 2] = m_perPixelMesh.p[index].x;
m_perPointMeshBuffer[strip + 3] = m_perPixelMesh.p[index].y;
m_perPointMeshBuffer[strip + 6] = m_perPixelMesh.p[index2].x;
m_perPointMeshBuffer[strip + 7] = m_perPixelMesh.p[index2].y;
}
}
}
glBindBuffer(GL_ARRAY_BUFFER, m_vboInterpolation);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, nullptr, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, m_perPointMeshBuffer, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_shaderEngine.enableWarpShader(m_currentPipeline->warpShader, pipeline, pipelineContext, m_renderContext.mat_ortho);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, pipeline.screenDecay);
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
glBindVertexArray(m_vaoInterpolation);
for (int j = 0; j < m_perPixelMesh.height - 1; j++)
glDrawArrays(GL_TRIANGLE_STRIP, j * m_perPixelMesh.width * 2, m_perPixelMesh.width * 2);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, 0);
}
Renderer::~Renderer()
{
free(m_perPointMeshBuffer);
glDeleteBuffers(1, &m_vboInterpolation);
glDeleteVertexArrays(1, &m_vaoInterpolation);
glDeleteBuffers(1, &m_vboCompositeOutput);
glDeleteVertexArrays(1, &m_vaoCompositeOutput);
glDeleteTextures(1, &textureRenderToTexture);
}
void Renderer::reset(int viewportWidth, int viewportHeight)
{
m_viewportWidth = viewportWidth;
m_viewportHeight = viewportHeight;
glCullFace(GL_BACK);
#ifndef GL_TRANSITION
glEnable(GL_LINE_SMOOTH);
#endif
glClearColor(0, 0, 0, 0);
glViewport(0, 0, viewportWidth, viewportHeight);
glEnable(GL_BLEND);
m_textureSizeX = viewportWidth;
m_textureSizeY = viewportHeight;
// snap to 16x16 blocks
m_textureSizeX = ((m_textureSizeX - 15) / 16) * 16;
m_textureSizeY = ((m_textureSizeY - 15) / 16) * 16;
m_fAspectX = (m_textureSizeY > m_textureSizeX) ? static_cast<float>(m_textureSizeX) / static_cast<float>(m_textureSizeY) : 1.0f;
m_fAspectY = (m_textureSizeX > m_textureSizeY) ? static_cast<float>(m_textureSizeY) / static_cast<float>(m_textureSizeX) : 1.0f;
m_fInvAspectX = 1.0f / m_fAspectX;
m_fInvAspectY = 1.0f / m_fAspectY;
InitCompositeShaderVertex();
ResetTextures();
m_shaderEngine.reset();
try
{
if (m_currentPipeline)
{
m_shaderEngine.loadPresetShaders(*m_currentPipeline);
}
}
catch(const ShaderException& ex)
{
throw RenderException("Shader compilation error: " + ex.message());
}
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, m_textureSizeX, m_textureSizeY, 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;
}
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;
}
}
// If we touched on the renderer where there is an existing waveform.
bool Renderer::touchedWaveform(float x, float y, std::size_t i)
{
if (m_waveformList[i].x > (x-0.05f) && m_waveformList[i].x < (x+0.05f) // if x +- 0.5f
&& ((m_waveformList[i].y > (y-0.05f) && m_waveformList[i].y < (y+0.05f)) // and y +- 0.5f
|| m_waveformList[i].mode == Line || m_waveformList[i].mode == DoubleLine || m_waveformList[i].mode == DerivativeLine ) // OR it's a line (and y doesn't matter)
)
{
return true;
}
return false;
}
// Render a waveform when a touch event is triggered.
void Renderer::touch(float x, float y, int pressure, int type = 0)
{
for (std::size_t i = 0; i < m_waveformList.size(); i++) {
if (touchedWaveform(x, y, i))
{
// if we touched an existing waveform with left click, drag it and don't continue with adding another.
touchDrag(x, y, pressure);
return;
}
}
m_touchX = x;
m_touchY = y;
// Randomly select colours on touch
m_touchR = ((double)rand() / (RAND_MAX));
m_touchB = ((double)rand() / (RAND_MAX));
m_touchG = ((double)rand() / (RAND_MAX));
m_touchA = ((double)rand() / (RAND_MAX));
MilkdropWaveform wave;
if (type == 0) {
// If we touched randomly, then assign type to a random number between 0 and 8
wave.mode = static_cast<MilkdropWaveformMode>((rand() % last));
}
else {
wave.mode = static_cast<MilkdropWaveformMode>(type);
}
wave.additive = true;
wave.modOpacityEnd = 1.1;
wave.modOpacityStart = 0.0;
wave.maximizeColors = true;
wave.modulateAlphaByVolume = false;
wave.r = m_touchR;
wave.g = m_touchG;
wave.b = m_touchB;
wave.a = m_touchA;
wave.x = m_touchX;
wave.y = m_touchY;
// add new waveform to waveformTouchList
m_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 (std::size_t i = 0; i < m_waveformList.size(); i++) {
if (touchedWaveform(x, y, i))
{
m_waveformList[i].x = x;
m_waveformList[i].y = y;
}
}
}
// 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 (std::size_t i = 0; i < m_waveformList.size(); i++) {
if (touchedWaveform(x, y, i))
{
m_waveformList.erase(m_waveformList.begin() + i);
}
}
}
// Remove all waveforms
void Renderer::touchDestroyAll()
{
m_waveformList.clear();
}
void Renderer::debugWriteMainTextureToFile() const
{
std::string outputFile = frameDumpOutputFile;
if (outputFile.empty())
{
static constexpr char prefix[] = "frame_texture_contents-";
static constexpr auto prefixLen = sizeof(prefix) - 1;
constexpr auto fileNameMaxLength = 150;
constexpr auto fileExtensionLength = 4;
std::vector<char> fileNameBuffer;
fileNameBuffer.resize(fileNameMaxLength);
std::memcpy(fileNameBuffer.data(), prefix, prefixLen);
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
const auto bytesWritten = std::strftime(fileNameBuffer.data() + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm);
const auto offset = prefixLen + bytesWritten;
const auto spaceLeft = fileNameMaxLength - offset;
std::snprintf(fileNameBuffer.data() + offset, spaceLeft - fileExtensionLength, "-%d.bmp", totalframes);
outputFile = std::string(fileNameBuffer.data());
}
GLuint fbo;
auto mainTexture = m_textureManager->getMainTexture();
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0);
auto dataSize = mainTexture->width * mainTexture->height * 3;
GLubyte* pixels = new GLubyte[dataSize];
glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
stbi_write_bmp(outputFile.c_str(), mainTexture->width, mainTexture->height, 3, pixels);
delete[] pixels;
}
void Renderer::UpdateContext(PipelineContext& context)
{
context.pixelsx = m_viewportWidth;
context.pixelsy = m_viewportHeight;
// It's actually the inverse of the aspect ratio.
context.aspectx = m_fInvAspectX;
context.aspecty = m_fInvAspectY;
}
void Renderer::CompositeOutput(const Pipeline& pipeline, const PipelineContext& pipelineContext)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textureManager->getMainTexture()->texID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
m_renderContext.mat_ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -40.0f, 40.0f);
m_shaderEngine.enableCompositeShader(m_currentPipeline->compositeShader, pipeline, pipelineContext);
glUniformMatrix4fv(m_shaderEngine.uniform_v2f_c4f_t2f_vertex_transformation, 1, GL_FALSE,
value_ptr(m_renderContext.mat_ortho));
glUniform1i(m_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_vaoCompositeOutput);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (auto drawable : pipeline.compositeDrawables)
drawable->Draw(m_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_compositeVertices, 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_compositeVertices[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_compositeIndices[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_compositeVertices[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_compositeVertices[m_compositeIndices[src_idx++]];
}
glBindBuffer(GL_ARRAY_BUFFER, m_vboCompositeShaderOutput);
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_vaoCompositeShaderOutput);
// Now do the final composite blit, fullscreen;
glDrawArrays(GL_TRIANGLES, 0, primCount);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void Renderer::ResetPerPointMeshBuffer()
{
if (m_perPointMeshBuffer != nullptr)
{
wipefree(m_perPointMeshBuffer);
}
int size = (m_perPixelMesh.height - 1) * m_perPixelMesh.width * 4 * 2;
m_perPointMeshBuffer = static_cast<float *>(wipemalloc(size * sizeof(float)));
for (int j = 0; j < m_perPixelMesh.height - 1; j++)
{
int base = j * m_perPixelMesh.width * 4 * 2;
for (int i = 0; i < m_perPixelMesh.width; i++)
{
int index = j * m_perPixelMesh.width + i;
int index2 = (j + 1) * m_perPixelMesh.width + i;
int strip = base + i * 8;
m_perPointMeshBuffer[strip + 0] = m_perPixelMesh.identity[index].x;
m_perPointMeshBuffer[strip + 1] = m_perPixelMesh.identity[index].y;
m_perPointMeshBuffer[strip + 4] = m_perPixelMesh.identity[index2].x;
m_perPointMeshBuffer[strip + 5] = m_perPixelMesh.identity[index2].y;
}
}
}