--- a/Shark.cpp Tue Dec 26 23:46:45 2023 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,694 +0,0 @@
-/**
- * ShaderShark
- * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <iostream>
-#include <iomanip>
-#include <string>
-#include <charconv>
-#include <memory>
-#include <functional>
-#include <sstream>
-#include <vector>
-
-#include "x11.h"
-#include "opengl.h"
-#include "EPoll.h"
-#include "Logger.h"
-#include "MappedFile.h"
-#include "ImageLoader.h"
-#include "Texture.h"
-#include "Shader.h"
-#include "Program.h"
-#include "FileMonitor.h"
-#include "XAttrs.h"
-
-#include "Shark.h"
-
-class Shark::Impl {
-public:
-
- struct {
- GLint aVertexXYZ = -2;
- GLint aTextureXY = -2;
-
- GLint fColor = -2;
-
- GLint uModel = -2;
- GLint uView = -2;
- GLint uProjection = -2;
- GLint uTexture = -2;
- GLint uTextureScale = -2;
- } ProgAttr;
-
- struct {
- float yaw = -90.f;
- float pitch = 0.f;
- float roll = 0.f;
- float fov = 45.0f;
- glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
- glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
- glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
-
- void adjustFov(float diff) {
- fov += diff;
- if (fov < 1.0f) fov = 1.0f;
- else if (fov > 120.0f) fov = 120.0f;
- std::cerr << "field of view: " << fov << " °" << std::endl;
- }
-
- void moveForward(const float cameraSpeed) {
- cameraPos += cameraSpeed * cameraFront;
- }
-
- void moveBackward(const float cameraSpeed) {
- cameraPos -= cameraSpeed * cameraFront;
- }
-
- void moveLeft(const float cameraSpeed) {
- cameraPos -= glm::normalize(
- glm::cross(cameraFront, cameraUp)) * cameraSpeed;
- }
-
- void moveRight(const float cameraSpeed) {
- cameraPos += glm::normalize(
- glm::cross(cameraFront, cameraUp)) * cameraSpeed;
- }
-
- void moveUp(const float cameraSpeed) {
- cameraPos += cameraSpeed * glm::normalize(cameraUp);
- }
-
- void moveDown(const float cameraSpeed) {
- cameraPos -= cameraSpeed * glm::normalize(cameraUp);
- }
-
- void updateCameraFrontAndUp() {
- std::cerr << "--- updateCameraFrontAndUp() --------" << std::endl;
- dump("pitch, yaw, roll", glm::vec3(pitch, yaw, roll));
- dump("cameraPos", cameraPos);
- dump("cameraFront", cameraFront);
- const auto pitchR = glm::radians(pitch); // around X axis
- const auto yawR = glm::radians(yaw); // around Y axis
- const auto rollR = glm::radians(roll); // around Z axis
-
- cameraFront.x = cos(pitchR) * cos(yawR);
- cameraFront.y = sin(pitchR);
- cameraFront.z = cos(pitchR) * sin(yawR);
- cameraFront = glm::normalize(cameraFront);
- dump("cameraFront", cameraFront);
- dump("cameraUp", cameraUp);
-
- // TODO: review ROLL rotation and default angle
- glm::mat4 rollMatrix = glm::rotate(
- glm::mat4(1.0f), rollR, cameraFront);
- cameraUp = glm::mat3(rollMatrix) * glm::vec3(0., 1., 0.);
- dump("cameraUp", cameraUp);
- std::cerr << "-------------------------------------" << std::endl;
- }
-
- void limitPitch() {
- if (pitch > +89.0f) pitch = +89.0f;
- if (pitch < -89.0f) pitch = -89.0f;
- }
-
- void turnLeft(const float angleSpeed) {
- yaw -= angleSpeed;
- updateCameraFrontAndUp();
- }
-
- void turnRight(const float angleSpeed) {
- yaw += angleSpeed;
- updateCameraFrontAndUp();
- }
-
- void turnUp(const float angleSpeed) {
- pitch += angleSpeed;
- limitPitch();
- updateCameraFrontAndUp();
- }
-
- void turnDown(const float angleSpeed) {
- pitch -= angleSpeed;
- limitPitch();
- updateCameraFrontAndUp();
- }
-
- void rollLeft(const float angleSpeed) {
- roll += angleSpeed;
- updateCameraFrontAndUp();
- }
-
- void rollRight(const float angleSpeed) {
- roll -= angleSpeed;
- updateCameraFrontAndUp();
- }
-
- } initialCtx, ctx;
-
- Display* dpy;
- Window win;
- XVisualInfo* vi;
- GLXContext glc;
-
- FileMonitor fileMonitor;
- std::vector<WatchedFile> watchedFiles;
- ImageLoader imageLoader;
- std::vector<std::shared_ptr<Shader>> shaders;
- std::shared_ptr<Program> shaderProgram;
- std::vector<std::shared_ptr<Texture>> textures;
-
- Configuration cfg;
- std::ostream& logOutput = std::cerr;
-
- Impl(Configuration cfg) : cfg(cfg) {
- }
-
- void run();
- void clear();
- void runShaders();
- Window getRootWindow(Window defaultValue);
- void log(LogLevel level, std::string message);
- int setNonBlocking(int fd);
- void loadVertices();
- void parametrizeTexture(std::shared_ptr<Texture> tex);
- bool reloadTexture(const std::string& fileName);
- void loadTextures();
- void loadShaders();
- void updateVariableLocations();
- bool reloadShader(const std::string& fileName);
- void setTitle(const std::string& suffix = "");
- static const std::string getDefaultFile(const std::string& relativePath);
-
-};
-
-Shark::Shark(const Configuration& configuration) :
-impl(new Impl(configuration)) {
-}
-
-Shark::~Shark() {
- impl->textures.clear();
- impl->shaders.clear();
- impl->shaderProgram = nullptr;
- XFree(impl->vi);
- glXMakeCurrent(impl->dpy, None, NULL);
- glXDestroyContext(impl->dpy, impl->glc);
- XDestroyWindow(impl->dpy, impl->win);
- XCloseDisplay(impl->dpy);
- delete impl;
- // std::cerr << "~Shark()" << std::endl;
-}
-
-void Shark::Impl::setTitle(const std::string& suffix) {
- std::stringstream title;
- title << "ShaderShark";
- if (suffix.size()) title << ": " << suffix.c_str();
- XStoreName(dpy, win, title.str().c_str());
- XFlush(dpy);
-}
-
-void Shark::run() {
- impl->run();
-}
-
-void Shark::Impl::run() {
- dpy = XOpenDisplay(NULL);
-
- if (dpy == NULL) throw std::logic_error("Unable to connect to X server");
-
- GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
- vi = glXChooseVisual(dpy, 0, att);
- Window root = DefaultRootWindow(dpy);
- Window parent = cfg.rootWindow ? cfg.rootWindow : root;
-
- XSetWindowAttributes swa;
- swa.colormap = XCreateColormap(dpy, parent, vi->visual, AllocNone);
- swa.event_mask = ExposureMask | KeyPressMask | PointerMotionMask
- | ButtonPressMask
- | StructureNotifyMask;
-
- bool full = false;
- unsigned int width = 1600;
- unsigned int height = 1200;
- if (parent != root) {
- XWindowAttributes parentAttr;
- XGetWindowAttributes(dpy, parent, &parentAttr);
- width = parentAttr.width;
- height = parentAttr.height;
- }
-
- win = XCreateWindow(
- dpy, parent, 0, 0, width, height, 0,
- vi->depth, InputOutput, vi->visual,
- CWColormap | CWEventMask, &swa);
-
- XMapWindow(dpy, win);
- setTitle();
- setX11PID(dpy, win);
- // XSetWindowBackground(dpy, win, 0) vs. glClearColor()
-
- glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
- glXMakeCurrent(dpy, win, glc);
-
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- clear();
- glXSwapBuffers(dpy, win);
-
-
- // Load GLSL shaders:
- loadShaders();
- loadTextures();
- loadVertices();
-
- auto toggleFullscreen = [&]() {
- full = setFullscreen(dpy, win, !full);
- };
-
- auto resetView = [&]() {
- ctx = initialCtx;
- ctx.updateCameraFrontAndUp();
- };
-
- // root can reize our window
- // or we can listen to root resize and then resize our window ourselves
- bool listenToRootResizes = true;
- if (listenToRootResizes) XSelectInput(dpy, parent, StructureNotifyMask);
-
- bool keepRunningX11 = true;
- int x11fd = XConnectionNumber(dpy);
- EPoll epoll;
- epoll.add(x11fd);
- epoll.add(fileMonitor.getFD());
- try {
- epoll.add(setNonBlocking(STDIN_FILENO));
- } catch (const EPoll::Exception& e) {
- logOutput << "Will not monitor events on STDIN: " << e.what() << "\n";
- }
-
- // rended the 3D scene even before the first event:
- runShaders();
- glXSwapBuffers(dpy, win);
-
- for (XEvent xev; keepRunningX11;) {
- int epollEventCount = epoll.wait();
- //std::cout << "trace: epoll.wait() = " << epollEventCount << std::endl;
- for (int epollEvent = 0; epollEvent < epollEventCount; epollEvent++) {
- bool redraw = false;
- if (epoll[epollEvent].data.fd == x11fd) {
- if (!XPending(dpy)) {
- // otherwise STDIN events are held until the first X11 event
- logOutput << "trace: no pending X11 event" << std::endl;
- break;
- }
-process_x11_event:
- XWindowAttributes gwa;
- XNextEvent(dpy, &xev);
-
- if (xev.type == Expose) {
- std::cout << "XEvent: Expose" << std::endl;
- XGetWindowAttributes(dpy, win, &gwa);
- glViewport(0, 0, gwa.width, gwa.height);
- redraw = true;
- } else if (xev.type == KeyPress) {
- DecodedKey key = decodeKeycode(dpy, xev.xkey.keycode);
- std::cout << "XEvent: KeyPress:"
- << " keycode=" << key.code
- << " key=" << key.name
- << std::endl;
-
- const float cSp = 0.05f; // camera speed
- const float aSp = 5.f; // angle speed
-
- if (key.matches(XK_q, XK_Escape)) keepRunningX11 = false;
- else if (key.matches(XK_Left, XK_s)) ctx.turnLeft(aSp);
- else if (key.matches(XK_Right, XK_f)) ctx.turnRight(aSp);
- else if (key.matches(XK_Up, XK_e)) ctx.moveForward(cSp);
- else if (key.matches(XK_Down, XK_d)) ctx.moveBackward(cSp);
- else if (key.matches(XK_w)) ctx.rollLeft(aSp);
- else if (key.matches(XK_r)) ctx.rollRight(aSp);
- else if (key.matches(XK_t)) ctx.turnUp(aSp);
- else if (key.matches(XK_g)) ctx.turnDown(aSp);
- else if (key.matches(XK_m)) ctx.moveLeft(cSp);
- else if (key.matches(XK_comma)) ctx.moveRight(cSp);
- else if (key.matches(XK_l)) ctx.moveUp(cSp);
- else if (key.matches(XK_period)) ctx.moveDown(cSp);
- else if (key.matches(XK_j)) ctx.moveLeft(cSp * 5);
- else if (key.matches(XK_k)) ctx.moveRight(cSp * 5);
- else if (key.matches(XK_u)) ctx.moveLeft(cSp * 10);
- else if (key.matches(XK_i)) ctx.moveRight(cSp * 10);
- else if (key.matches(XK_x)) resetView();
- else if (key.matches(XK_F11, XK_y)) toggleFullscreen();
- redraw = true;
- } else if (xev.type == ButtonPress) {
- std::cout << "XEvent: ButtonPress:"
- << " button=" << xev.xbutton.button
- << std::endl;
- if (xev.xbutton.button == 1);
- else if (xev.xbutton.button == 4) ctx.adjustFov(-1.0);
- else if (xev.xbutton.button == 5) ctx.adjustFov(+1.0);
- else if (xev.xbutton.button == 8) resetView();
- else if (xev.xbutton.button == 9) keepRunningX11 = false;
- redraw = true;
- } else if (xev.type == MotionNotify) {
- // printCursorInfo(xev.xmotion);
- } else if (xev.type == ConfigureNotify) {
- std::cout << "XEvent: ConfigureNotify:"
- << " window=" << xev.xconfigure.window
- << " height=" << xev.xconfigure.height
- << " width=" << xev.xconfigure.width
- << std::endl;
- if (listenToRootResizes
- && xev.xconfigure.window == parent) {
- XResizeWindow(dpy, win,
- xev.xconfigure.width, xev.xconfigure.height);
- }
- } else if (xev.type == UnmapNotify) {
- std::cout << "XEvent: UnmapNotify" << std::endl;
- } else if (xev.type == DestroyNotify) {
- std::cout << "XEvent: DestroyNotify → finish" << std::endl;
- break;
- } else {
- std::cout << "XEvent: type=" << xev.type << std::endl;
- }
- if (XPending(dpy)) goto process_x11_event;
- } else if (epoll[epollEvent].data.fd == STDIN_FILENO) {
- int epollFD = epoll[epollEvent].data.fd;
- logOutput << "other event: fd=" << epollFD << " data=";
- for (char ch; read(epollFD, &ch, 1) > 0;) {
- std::stringstream msg;
- msg
- << std::hex
- << std::setfill('0')
- << std::setw(2)
- << (int) ch;
- logOutput << msg.str();
- }
- logOutput << std::endl;
-
- } else if (epoll[epollEvent].data.fd == fileMonitor.getFD()) {
- std::cout << "FileMonitor event:" << std::endl;
- for (FileEvent fe; fileMonitor.readEvent(fe);) {
- logOutput << " "
- << " file=" << fe.fileName
- << " mask=" << fe.mask
- << std::endl;
- try {
- redraw |= reloadTexture(fe.fileName);
- redraw |= reloadShader(fe.fileName);
- setTitle();
- } catch (const std::exception& e) {
- setTitle("[ERROR]");
- logOutput << "error while reloading '"
- << fe.fileName.c_str()
- << "': " << e.what() << std::endl;
- }
- }
- } else {
- logOutput
- << "error: event on an unexpected FD: "
- << epoll[epollEvent].data.fd
- << std::endl;
- }
-
- if (redraw) {
- runShaders();
- glXSwapBuffers(dpy, win);
- }
- }
- }
-}
-
-void Shark::Impl::clear() {
- glClearColor(
- (cfg.backgroundColor >> 16 & 0xFF) / 256.,
- (cfg.backgroundColor >> 8 & 0xFF) / 256.,
- (cfg.backgroundColor & 0xFF) / 256.,
- 1.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-}
-
-void Shark::Impl::runShaders() {
- shaderProgram->use();
- checkError(&std::cerr);
-
- clear();
-
- GLint viewport[4];
- glGetIntegerv(GL_VIEWPORT, viewport);
- GLfloat width = viewport[2];
- GLfloat height = viewport[3];
-
- glm::mat4 projection = glm::perspective(
- glm::radians(ctx.fov),
- width / height,
- 0.1f, 100.0f);
- glUniformMatrix4fv(ProgAttr.uProjection, 1, GL_FALSE, &projection[0][0]);
-
- glm::mat4 view = glm::lookAt(
- ctx.cameraPos,
- ctx.cameraPos + ctx.cameraFront,
- ctx.cameraUp);
- glUniformMatrix4fv(ProgAttr.uView, 1, GL_FALSE, &view[0][0]);
-
- // glBindVertexArray(vao);
-
- glm::mat4 model = glm::mat4(1.0f); // identity matrix
- glUniformMatrix4fv(ProgAttr.uModel, 1, GL_FALSE, &model[0][0]);
-
- // TODO: draw a rectangle for each texture
- glUniform1f(ProgAttr.uTextureScale, textures[0]->getScale());
-
- glDrawArrays(GL_TRIANGLES, 0, 2 * 3); // see loadVertices()
- std::cerr << "GLSL: glDrawArrays()" << std::endl;
-}
-
-void Shark::Impl::log(LogLevel level, std::string message) {
- ::log(logOutput, level, message);
-}
-
-int Shark::Impl::setNonBlocking(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- return fd;
-}
-
-void Shark::Impl::loadVertices() {
- for (int i = 0; i < textures.size(); i++) {
- std::shared_ptr<Texture> tex = textures[i];
- // TODO: draw a rectangle for each texture
- GLfloat ratio = tex->getRatio();
- const std::vector<GLfloat> vertices = {
- // Vertex XYZ Texture XY
- -0.80f * ratio, +0.80f, +0.0, /**/ 0.0, 0.0,
- +0.80f * ratio, +0.80f, +0.0, /**/ 1.0, 0.0,
- -0.80f * ratio, -0.80f, +0.0, /**/ 0.0, 1.0,
-
- -0.80f * ratio, -0.80f, +0.0, /**/ 0.0, 1.0,
- +0.80f * ratio, -0.80f, +0.0, /**/ 1.0, 1.0,
- +0.80f * ratio, +0.80f, +0.0, /**/ 1.0, 0.0,
-
- // see glDrawArrays(), where we set start offset and count
- };
-
- // Vertex data:
- glVertexAttribPointer(ProgAttr.aVertexXYZ, 3, // vertex items
- GL_FLOAT, GL_FALSE, 5 * sizeof (float),
- (void*) 0);
- glEnableVertexAttribArray(ProgAttr.aVertexXYZ);
-
- // Texture positions:
- glVertexAttribPointer(ProgAttr.aTextureXY, 2, // texture items
- GL_FLOAT, GL_FALSE, 5 * sizeof (float),
- (void*) (3 * sizeof (float)));
- glEnableVertexAttribArray(ProgAttr.aTextureXY);
-
- glBufferData(GL_ARRAY_BUFFER,
- vertices.size() * sizeof (vertices[0]),
- vertices.data(),
- GL_STATIC_DRAW);
- // GL_STATIC_DRAW:
- // The vertex data will be uploaded once
- // and drawn many times(e.g. the world).
- // GL_DYNAMIC_DRAW:
- // The vertex data will be created once, changed from
- // time to time, but drawn many times more than that.
- // GL_STREAM_DRAW:
- // The vertex data will be uploaded once and drawn once.
-
- // see also glBindBuffer(GL_ARRAY_BUFFER, vbo); where we set current VBO
- }
-}
-
-const std::string
-Shark::Impl::getDefaultFile(const std::string& relativePath) {
- const char* envName = "SHADER_SHARK_DATA_DIR";
- const char* envValue = ::getenv(envName);
- if (envValue) {
- return std::string(envValue) + "/" + relativePath;
- } else {
- throw std::invalid_argument(std::string("Configure $") + envName
- + " in order to use defaults"
- " or specify textures and shaders as parameters");
- }
-}
-
-void Shark::Impl::parametrizeTexture(std::shared_ptr<Texture> tex) {
- XAttrs xa(tex->getFileName());
- std::string magf = xa["shader-shark.texture.mag-filter"];
- std::string minf = xa["shader-shark.texture.min-filter"];
- std::string scale = xa["shader-shark.texture.scale"];
-
- auto GLT2D = GL_TEXTURE_2D;
- auto MAG = GL_TEXTURE_MAG_FILTER;
- auto MIN = GL_TEXTURE_MIN_FILTER;
-
- if (magf == "linear") glTexParameteri(GLT2D, MAG, GL_LINEAR);
- else if (magf == "nearest") glTexParameteri(GLT2D, MAG, GL_NEAREST);
-
- if (minf == "linear") glTexParameteri(GLT2D, MIN, GL_LINEAR);
- else if (minf == "nearest") glTexParameteri(GLT2D, MIN, GL_NEAREST);
-
- if (scale.size()) {
- float sc;
- if (std::from_chars(scale.data(), scale.data() + scale.size(), sc).ec
- == std::errc{}) tex->setScale(sc);
- else std::cerr << "Invalid texture scale value - expecting float\n";
- // tex->setScale(std::stof(scale)); // locale-dependent (. vs ,)
- }
-}
-
-void Shark::Impl::loadTextures() {
- // Load default texture if there is no configured:
- if (cfg.textures.empty())
- cfg.textures.push_back({getDefaultFile("textures/default.png")});
-
- for (const Configuration::Texture& tex : cfg.textures) {
- std::shared_ptr<ImageLoader::ImageBuffer>
- img(imageLoader.loadImage(MappedFile(tex.fileName)));
- textures.push_back(std::make_shared<Texture>(
- img->width, img->height, *img, tex.fileName));
- parametrizeTexture(textures.back());
- // static const uint32_t watchMask = IN_CLOSE_WRITE | IN_ATTRIB;
- // watchedFiles.push_back(fileMonitor.watch(tex.fileName, watchMask));
- watchedFiles.push_back(fileMonitor.watch(tex.fileName));
- // TODO: review texture loading and binding
- // works even without this - default texture
- // glUniform1i(..., ...);
- // checkError(&std::cerr);
- }
-}
-
-bool Shark::Impl::reloadTexture(const std::string& fileName) {
- for (std::shared_ptr<Texture> tex : textures) {
- if (tex->getFileName() == fileName) {
- std::shared_ptr<ImageLoader::ImageBuffer>
- img(imageLoader.loadImage(MappedFile(fileName)));
- tex->update(img->width, img->height, *img);
- parametrizeTexture(tex);
- loadVertices();
- return true;
- }
- }
- return false;
-}
-
-void Shark::Impl::loadShaders() {
- // Vertex Array Object (VAO)
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
- // VAO - something like context for bound data/variables
- // We can switch multiple VAOs. VAO can contain multiple VBOs.
- // what-are-vertex-array-objects
-
- // Vertex Buffer Object (VBO):
- GLuint vbo;
- glGenBuffers(1, &vbo);
- glBindBuffer(GL_ARRAY_BUFFER, vbo);
-
- {
- // Load default shaders if there are no configured:
- int vc = 0;
- int fc = 0;
- auto& ss = cfg.shaders;
- for (const auto& s : ss) if (s.type == "vertex") vc++;
- for (const auto& s : ss) if (s.type == "fragment") fc++;
- auto& d = getDefaultFile;
- if (vc == 0) ss.push_back({d("shaders/default.vert"), "vertex"});
- if (fc == 0) ss.push_back({d("shaders/default.frag"), "fragment"});
- }
-
- shaderProgram = std::make_shared<Program>();
-
- // glBindFragDataLocation(program, 0, "outColor");
- // glBindAttribLocation(program, LOC.input, "vertices");
-
- for (const Configuration::Shader definition : cfg.shaders) {
- Shader::Type type;
- std::string fileName = definition.fileName;
- if (definition.type == "fragment") type = Shader::Type::FRAGMENT;
- else if (definition.type == "vertex") type = Shader::Type::VERTEX;
- else throw std::invalid_argument("unsupported shader type");
-
- MappedFile file(fileName);
- std::shared_ptr<Shader> shader = std::make_shared<Shader>(
- type, file, fileName);
-
- shaderProgram->attachShader(*shader.get());
- shaders.push_back(shader);
- watchedFiles.push_back(fileMonitor.watch(fileName));
- std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl;
- // We may detach and delete shaders,
- // but our shaders are small, so we keep them for later reloading.
- }
-
- shaderProgram->link();
- updateVariableLocations();
- // listVariables(program);
- std::cerr << "GLSL shader count: " << shaders.size() << std::endl;
-}
-
-void Shark::Impl::updateVariableLocations() {
- // GLSL compiler does very efficient / aggressive optimization.
- // Attributes and uniforms that are not used in the shader are deleted.
- // And even if we e.g. read color from a texture and overwrite it,
- // the variable is still deleted and considered „inactive“.
- // Functions glGetAttribLocation() and glGetUniformLocation() return -1.
- ProgAttr.aVertexXYZ = shaderProgram->getAttribLocation("aVertexXYZ");
- ProgAttr.aTextureXY = shaderProgram->getAttribLocation("aTextureXY");
- ProgAttr.uModel = shaderProgram->getUniformLocation("uModel");
- ProgAttr.uView = shaderProgram->getUniformLocation("uView");
- ProgAttr.uProjection = shaderProgram->getUniformLocation("uProjection");
- ProgAttr.uTexture = shaderProgram->getUniformLocation("uTexture");
- ProgAttr.uTextureScale = shaderProgram->getUniformLocation("uTextureScale");
- ProgAttr.fColor = shaderProgram->getFragDataLocation("fColor");
- shaderProgram->bindFragDataLocation("fColor", ProgAttr.fColor);
-}
-
-bool Shark::Impl::reloadShader(const std::string& fileName) {
- for (auto shader : shaders) {
- if (shader->getFileName() == fileName) {
- shader->update(MappedFile(fileName));
- shaderProgram->link();
- updateVariableLocations();
- return true;
- }
- }
- return false;
-}