# HG changeset patch # User František Kučera # Date 1701207933 -3600 # Node ID fb65455622b93d9d63080f9ebb81896eca7fa9db # Parent bb715a82a8f11d9ac7e64f33bd04591b3735a526 load textures from PNG, JPEG etc. files using ImageMagick only one (last) texture is currently displayed diff -r bb715a82a8f1 -r fb65455622b9 AllocatedBuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AllocatedBuffer.h Tue Nov 28 22:45:33 2023 +0100 @@ -0,0 +1,38 @@ +/** + * 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 . + */ + +#pragma once + +#include +#include + +#include "Buffer.h" + +class AllocatedBuffer : public Buffer { +public: + + AllocatedBuffer(size_t size) { + this->size = size; + data = (char*) malloc(size); + if (!data) throw std::invalid_argument("unable to allocate buffer"); + } + + virtual ~AllocatedBuffer() { + free(data); + } + +}; diff -r bb715a82a8f1 -r fb65455622b9 ImageLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ImageLoader.cpp Tue Nov 28 22:45:33 2023 +0100 @@ -0,0 +1,43 @@ +/** + * 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 . + */ + +#include + +#include + +#include "ImageLoader.h" +#include "AllocatedBuffer.h" + +ImageLoader::ImageBuffer* ImageLoader::loadImage(const Buffer& input) { + Magick::Blob inputBlob(input.getData(), input.getSize()); + Magick::Image image(inputBlob); + + // Magick::PixelPacket* parsedPixels = parsedImage.getPixels( + // 0, 0, parsedImage.size().width(), parsedImage.size().height()); + + Magick::Blob outputBlob; + image.magick("RGBA"); + image.write(&outputBlob); + + ImageBuffer* outputBuffer = new ImageBuffer( + (void*) outputBlob.data(), + outputBlob.length(), + image.size().width(), + image.size().height()); + + return outputBuffer; +} \ No newline at end of file diff -r bb715a82a8f1 -r fb65455622b9 ImageLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ImageLoader.h Tue Nov 28 22:45:33 2023 +0100 @@ -0,0 +1,44 @@ +/** + * 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 . + */ + +#pragma once + +#include + +#include "Buffer.h" +#include "AllocatedBuffer.h" + +class ImageLoader { +public: + + class ImageBuffer : public AllocatedBuffer { + public: + int width; + int height; + + ImageBuffer(void* buffer, const size_t size, + const int width, const int height) : + AllocatedBuffer(width * height * 4), + width(width), height(height) { + if (size == width * height * 4) memcpy(data, buffer, size); + else throw std::invalid_argument("invalid image size"); + } + + }; + + ImageBuffer* loadImage(const Buffer& inputBuffer); +}; diff -r bb715a82a8f1 -r fb65455622b9 Makefile --- a/Makefile Sun Nov 26 16:27:50 2023 +0100 +++ b/Makefile Tue Nov 28 22:45:33 2023 +0100 @@ -20,18 +20,22 @@ clean: rm -rf build -run: build/shader-shark build/jazz.rgba - $(<) --texture build/jazz.rgba +run: build/shader-shark build/jazz.png + $(<) --texture build/jazz.png build: mkdir -p $(@) -SRC=Shark.cpp shader-shark.cpp +SRC=Shark.cpp shader-shark.cpp ImageLoader.cpp build/shader-shark: $(SRC) build *.h $(CXX) -std=c++20 -g -o $(@) $(SRC) $$(pkg-config --cflags --libs \ - epoxy x11 glu glm) + epoxy x11 glu glm Magick++) +build/jazz.png: textures/jazz.png + mkdir -p build + cp --reflink=auto $(<) $(@) + build/jazz.rgba: textures/jazz.png mkdir -p build convert -format rgba $(<) $(@) diff -r bb715a82a8f1 -r fb65455622b9 Shark.cpp --- a/Shark.cpp Sun Nov 26 16:27:50 2023 +0100 +++ b/Shark.cpp Tue Nov 28 22:45:33 2023 +0100 @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #include "Shark.h" Shark::Shark(const Configuration& configuration) : @@ -65,8 +67,8 @@ // Load GLSL shaders: GLuint shaderProgram = loadShaders(); + loadTextures(shaderProgram); loadVertices(); - loadTextures(shaderProgram); auto toggleFullscreen = [&]() { full = setFullscreen(dpy, win, !full); @@ -267,77 +269,82 @@ } void Shark::loadVertices() { - const std::vector vertices = { - // Vertex XYZ Texture XY - -0.80f * TEX.ratio, +0.80f, +0.0, /**/ 0.0, 0.0, - +0.80f * TEX.ratio, +0.80f, +0.0, /**/ 1.0, 0.0, - -0.80f * TEX.ratio, -0.80f, +0.0, /**/ 0.0, 1.0, + for (int i = 0; i < textures.size(); i++) { + const Texture& tex = textures[i]; + GLfloat ratio = tex.getRatio(); + const std::vector 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 * TEX.ratio, -0.80f, +0.0, /**/ 0.0, 1.0, - +0.80f * TEX.ratio, -0.80f, +0.0, /**/ 1.0, 1.0, - +0.80f * TEX.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, /**/ 1.0, 1.0, + +0.80f * ratio, +0.80f, +0.0, /**/ 1.0, 0.0, - // viz glDrawArrays(), kde vybereme počátek a počet hodnot - }; + // viz glDrawArrays(), kde vybereme počátek a počet hodnot + }; - // Vertex data: - glVertexAttribPointer(ProgAttr.vertexXYZ, 3, // vertex items - GL_FLOAT, GL_FALSE, 5 * sizeof (float), - (void*) 0); - glEnableVertexAttribArray(ProgAttr.vertexXYZ); + // Vertex data: + glVertexAttribPointer(ProgAttr.vertexXYZ, 3, // vertex items + GL_FLOAT, GL_FALSE, 5 * sizeof (float), + (void*) 0); + glEnableVertexAttribArray(ProgAttr.vertexXYZ); - // Texture positions: - glVertexAttribPointer(ProgAttr.textureXY, 2, // texture items - GL_FLOAT, GL_FALSE, 5 * sizeof (float), - (void*) (3 * sizeof (float))); - glEnableVertexAttribArray(ProgAttr.textureXY); + // Texture positions: + glVertexAttribPointer(ProgAttr.textureXY, 2, // texture items + GL_FLOAT, GL_FALSE, 5 * sizeof (float), + (void*) (3 * sizeof (float))); + glEnableVertexAttribArray(ProgAttr.textureXY); - 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. + 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 + // see also glBindBuffer(GL_ARRAY_BUFFER, vbo); where we set current VBO + } } -GLuint Shark::loadTexture(const std::string& fileName, int width, int height) { - MappedFile file(fileName); - if (file.getSize() == width * height * 4) { - GLuint textureID; - glGenTextures(1, &textureID); - glBindTexture(GL_TEXTURE_2D, textureID); - auto GLT2D = GL_TEXTURE_2D; - glTexImage2D(GLT2D, 0, GL_RGBA, - width, height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, - file.getData()); - glTexParameteri(GLT2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GLT2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GLT2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GLT2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glGenerateMipmap(GLT2D); - std::cerr << "loadTexture(\"" << fileName.c_str() << "\", " - << width << ", " << height << ") = " << textureID << std::endl; - checkError(&std::cerr); - return textureID; - } else { - throw std::invalid_argument("wrong texture file size"); - } +Shark::Texture Shark::loadTexture(const std::string& fileName) { + Texture tex; + tex.fileName = fileName; + MappedFile file(tex.fileName); + + std::shared_ptr img(imageLoader.loadImage(file)); + + tex.width = img->width; + tex.height = img->height; + + glGenTextures(1, &tex.id); + glBindTexture(GL_TEXTURE_2D, tex.id); + auto GLT2D = GL_TEXTURE_2D; + glTexImage2D(GLT2D, 0, GL_RGBA, + tex.width, tex.height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, + img->getData()); + glTexParameteri(GLT2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GLT2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GLT2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GLT2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GLT2D); + std::cerr << "loadTexture(\"" << fileName.c_str() << "\", " + << tex.width << ", " << tex.height << ") = " << tex.id << std::endl; + checkError(&std::cerr); + return tex; } void Shark::loadTextures(GLuint shaderProgram) { for (const Configuration::Texture& tex : cfg.textures) { - GLuint jazz = loadTexture(tex.fileName, TEX.width, TEX.height); - // FIXME: decode PNG, JPEG etc. formats - // FIXME: read width and height from the file + textures.push_back(loadTexture(tex.fileName)); // TODO: review texture loading and binding // works even without this - default texture // glUniform1i(ProgAttr.jazz, jazz); diff -r bb715a82a8f1 -r fb65455622b9 Shark.h --- a/Shark.h Sun Nov 26 16:27:50 2023 +0100 +++ b/Shark.h Tue Nov 28 22:45:33 2023 +0100 @@ -34,6 +34,7 @@ #include "EPoll.h" #include "Logger.h" #include "MappedFile.h" +#include "ImageLoader.h" class Shark { private: @@ -155,11 +156,21 @@ } initialCtx, ctx; - const struct { - int width = 640; - int height = 398; - GLfloat ratio = (GLfloat) width / (GLfloat) height; - } TEX; + class Texture { + public: + GLuint id; + std::string fileName; + int width; + int height; + + GLfloat getRatio() const { + return (GLfloat) width / (GLfloat) height; + } + }; + + ImageLoader imageLoader; + + std::vector textures; Configuration cfg; std::ostream& logOutput = std::cerr; @@ -170,7 +181,7 @@ void log(LogLevel level, std::string message); int setNonBlocking(int fd); void loadVertices(); - GLuint loadTexture(const std::string& fileName, int width, int height); + Texture loadTexture(const std::string& fileName); void loadTextures(GLuint shaderProgram); GLuint loadShaders(); public: diff -r bb715a82a8f1 -r fb65455622b9 nbproject/configurations.xml --- a/nbproject/configurations.xml Sun Nov 26 16:27:50 2023 +0100 +++ b/nbproject/configurations.xml Tue Nov 28 22:45:33 2023 +0100 @@ -2,6 +2,7 @@ + ImageLoader.cpp Shark.cpp shader-shark.cpp @@ -25,6 +26,9 @@ false false + + + @@ -33,15 +37,33 @@ ${MAKE} -f Makefile ${MAKE} -f Makefile clean build/shader-shark + + + /usr/include/x86_64-linux-gnu/ImageMagick-6 + /usr/include/ImageMagick-6 + + + MAGICKCORE_HDRI_ENABLE=0 + MAGICKCORE_QUANTUM_DEPTH=16 + + . + + + + + + + +