Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created June 19, 2025 00:40
Show Gist options
  • Save EncodeTheCode/3b5d54a032406b9956988c4a89b6494b to your computer and use it in GitHub Desktop.
Save EncodeTheCode/3b5d54a032406b9956988c4a89b6494b to your computer and use it in GitHub Desktop.
// Image2D_OpenGL.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <unordered_map>
#include <string>
///////////////////////////
// Image2D Class
///////////////////////////
class Image2D {
public:
Image2D(const std::string& path);
void draw(GLuint shader);
void setPosition(float x, float y);
void move(float dx, float dy);
void setScale(float scaleX, float scaleY);
void setSize(float newWidth, float newHeight);
void show();
void hide();
bool isVisible() const;
unsigned int getID() const;
static Image2D* getImageByID(unsigned int id);
static void removeImageByID(unsigned int id);
private:
static unsigned int nextID;
static std::unordered_map<unsigned int, Image2D*> imageRegistry;
unsigned int id;
GLuint textureID;
float x, y;
float scaleX, scaleY;
int width, height;
bool visible;
void loadFromFile(const std::string& path);
};
// Static variables
unsigned int Image2D::nextID = 1;
std::unordered_map<unsigned int, Image2D*> Image2D::imageRegistry;
// External VAO
GLuint gVAO = 0, gVBO = 0;
void InitImageRenderer() {
float quadVertices[] = {
// pos // tex
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &gVAO);
glGenBuffers(1, &gVBO);
glBindVertexArray(gVAO);
glBindBuffer(GL_ARRAY_BUFFER, gVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); // pos
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); // tex
glEnableVertexAttribArray(1);
}
Image2D::Image2D(const std::string& path) {
id = nextID++;
x = y = 0;
scaleX = scaleY = 1.0f;
visible = true;
loadFromFile(path);
imageRegistry[id] = this;
}
void Image2D::loadFromFile(const std::string& path) {
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
if (!data) {
std::cerr << "Failed to load: " << path << "\n";
textureID = 0;
return;
}
GLenum format = (channels == 4) ? GL_RGBA : GL_RGB;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
void Image2D::draw(GLuint shader) {
if (!visible || textureID == 0) return;
glm::mat4 model(1.0f);
model = glm::translate(model, glm::vec3(x, y, 0.0f));
model = glm::scale(model, glm::vec3(width * scaleX, height * scaleY, 1.0f));
GLint uTransform = glGetUniformLocation(shader, "uTransform");
GLint uTexture = glGetUniformLocation(shader, "uTexture");
glUseProgram(shader);
glUniformMatrix4fv(uTransform, 1, GL_FALSE, glm::value_ptr(model));
glUniform1i(uTexture, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
glBindVertexArray(gVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
// Position/Scaling/Visibility
void Image2D::setPosition(float newX, float newY) { x = newX; y = newY; }
void Image2D::move(float dx, float dy) { x += dx; y += dy; }
void Image2D::setScale(float sX, float sY) { scaleX = sX; scaleY = sY; }
void Image2D::setSize(float newW, float newH) {
scaleX = newW / width;
scaleY = newH / height;
}
void Image2D::show() { visible = true; }
void Image2D::hide() { visible = false; }
bool Image2D::isVisible() const { return visible; }
unsigned int Image2D::getID() const { return id; }
Image2D* Image2D::getImageByID(unsigned int id) {
auto it = imageRegistry.find(id);
return (it != imageRegistry.end()) ? it->second : nullptr;
}
void Image2D::removeImageByID(unsigned int id) {
auto it = imageRegistry.find(id);
if (it != imageRegistry.end()) {
glDeleteTextures(1, &it->second->textureID);
delete it->second;
imageRegistry.erase(it);
}
}
///////////////////////////
// Shader Example (basic)
///////////////////////////
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTex;
uniform mat4 uTransform;
out vec2 TexCoord;
void main() {
gl_Position = uTransform * vec4(aPos, 0.0, 1.0);
TexCoord = aTex;
}
)";
const char* fragmentShaderSource = R"(
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D uTexture;
void main() {
FragColor = texture(uTexture, TexCoord);
}
)";
GLuint CompileShader(GLenum type, const char* src) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
return shader;
}
GLuint CreateShaderProgram() {
GLuint vs = CompileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
///////////////////////////
// Main Example
///////////////////////////
int main() {
glfwInit();
GLFWwindow* window = glfwCreateWindow(800, 600, "Image2D Demo", nullptr, nullptr);
glfwMakeContextCurrent(window);
gladLoadGL();
InitImageRenderer();
GLuint shader = CreateShaderProgram();
Image2D* img = new Image2D("image.png");
img->setPosition(100, 150);
img->setSize(128, 128);
unsigned int id = img->getID();
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) img->move(2.0f, 0.0f);
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) img->move(-2.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
img->draw(shader);
glfwSwapBuffers(window);
}
Image2D::removeImageByID(id);
glfwTerminate();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment