Files
4k_synthwave/src/editor.cpp
2025-06-15 10:24:37 +03:00

186 lines
4.9 KiB
C++

#include "editor.h"
#include "song.h"
#include "stdio.h"
#include "windows.h"
#include "GL/gl.h"
#include "glext.h"
using namespace Leviathan;
#define USE_MESSAGEBOX 0
Editor::Editor() : lastFrameStart(0), lastFrameStop(0), trackPosition(0.0), trackEnd(0.0), state(Playing)
{
printf("Editor opened...\n");
}
void Editor::beginFrame(const unsigned long time)
{
lastFrameStart = time;
}
void Editor::endFrame(const unsigned long time)
{
lastFrameStop = time;
}
void Editor::printFrameStatistics()
{
const int frameTime = lastFrameStop - lastFrameStart;
// calculate average fps over 'windowSize' of frames
float fps = 0.0f;
for (int i = 0; i < windowSize - 1; ++i)
{
timeHistory[i] = timeHistory[i + 1];
fps += 1.0f / static_cast<float>(timeHistory[i]);
}
timeHistory[windowSize - 1] = frameTime;
fps += 1.0f / static_cast<float>(frameTime);
fps *= 1000.0f / static_cast<float>(windowSize);
printf("%s: %0.2i:%0.2i (%i%%), frame duration: %i ms (running fps average: %2.2f) \r",
state == Playing ? "Playing" : " Paused",
// assuming y'all won't be making intros more than an hour long
int(trackPosition/60.0), int(trackPosition) % 60, int(100.0f*trackPosition/trackEnd),
frameTime, fps);
}
double Editor::handleEvents(Leviathan::Song* track, double position)
{
if (GetAsyncKeyState(VK_MENU))
{
double seek = 0.0;
if (GetAsyncKeyState(VK_DOWN))
{
state = Paused;
track->pause();
}
if (GetAsyncKeyState(VK_UP))
{
state = Playing;
track->play();
}
if (GetAsyncKeyState(VK_RIGHT) && !GetAsyncKeyState(VK_SHIFT)) seek += 1.0;
if (GetAsyncKeyState(VK_LEFT) && !GetAsyncKeyState(VK_SHIFT)) seek -= 1.0;
if (GetAsyncKeyState(VK_RIGHT) && GetAsyncKeyState(VK_SHIFT)) seek += 0.1;
if (GetAsyncKeyState(VK_LEFT) && GetAsyncKeyState(VK_SHIFT)) seek -= 0.1;
if (position + seek != position)
{
position += seek;
track->seek(position);
}
}
if (GetAsyncKeyState(VK_CONTROL) && GetAsyncKeyState('S'))
shaderUpdatePending = true;
trackPosition = position;
trackEnd = track->getLength();
return position;
}
void Editor::updateShaders(int* mainShaderPID, int* postShaderPID, bool force_update)
{
if (shaderUpdatePending || force_update)
{
// make sure the file has finished writing to disk
if (timeGetTime() - previousUpdateTime > 200)
{
// only way i can think of to clear the line without "status line" residue
printf("Refreshing shaders... \n");
Sleep(100);
int newPID = reloadShaderSource("../src/shaders/fragment.frag");
if (newPID > 0)
*mainShaderPID = newPID;
newPID = reloadShaderSource("../src/shaders/post.frag");
if (newPID > 0)
*postShaderPID = newPID;
}
previousUpdateTime = timeGetTime();
shaderUpdatePending = false;
}
}
int Editor::reloadShaderSource(const char* filename)
{
long inputSize = 0;
// we're of course opening a text file, but should be opened in binary ('b')
// longer shaders are known to cause problems by producing garbage input when read
FILE* file = fopen(filename, "rb");
if (file)
{
fseek(file, 0, SEEK_END);
inputSize = ftell(file);
rewind(file);
char* shaderString = static_cast<char*>(calloc(inputSize + 1, sizeof(char)));
fread(shaderString, sizeof(char), inputSize, file);
fclose(file);
// just to be sure...
shaderString[inputSize] = '\0';
if (!compileAndDebugShader(shaderString, filename, false))
{
free(shaderString);
// return an invalid PID value if compilation fails
return -1;
}
int pid = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &shaderString);
free(shaderString);
printf("Loaded shader from \"%s\"\n", filename);
return pid;
}
else
{
printf("Input shader file at \"%s\" not found, shader not reloaded\n", filename);
return -1;
}
}
bool Editor::compileAndDebugShader(const char* shader, const char* filename, bool kill_on_failure)
{
// try and compile the shader
int result = 0;
const int debugid = ((PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"))(GL_FRAGMENT_SHADER);
((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"))(debugid, 1, &shader, 0);
((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(debugid);
// get compile result
((PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"))(debugid, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
// display compile log on failure
static char errorBuffer[shaderErrorBufferLength];
((PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"))(debugid, shaderErrorBufferLength-1, NULL, static_cast<char*>(errorBuffer));
#if USE_MESSAGEBOX
MessageBox(NULL, errorBuffer, "", 0x00000000L);
#endif
printf("Compilation errors in %s:\n\n %s\n", filename, errorBuffer);
if (kill_on_failure)
{
ExitProcess(0);
}
else
{
return false;
}
}
else
{
((PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"))(debugid);
return true;
}
}