diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e2da82..ae5500e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ if(NOT ${OPENGL_GLU_FOUND}) endif() add_executable(tests_opengl - src/main.cpp src/displayers.h src/displayers.cpp) + src/main.cpp src/displayers.h src/displayers.cpp src/Texture.cpp src/Texture.h) target_link_libraries(tests_opengl ${OPENGL_LIBRARIES} diff --git a/resources/arbre.tga b/resources/arbre.tga new file mode 100644 index 0000000..6d647fb Binary files /dev/null and b/resources/arbre.tga differ diff --git a/src/Texture.cpp b/src/Texture.cpp new file mode 100644 index 0000000..e3ca94a --- /dev/null +++ b/src/Texture.cpp @@ -0,0 +1,80 @@ +// +// Created by trotfunky on 24/09/2019. +// + +#include "Texture.h" + + +Texture::Texture() : width(0), height(0), color_bits(0), image_data(nullptr), opengl_id{(uint32_t)-1} +{} + +Texture::~Texture() +{ + if (opengl_id[0] != (uint32_t)-1) + { + delete(image_data); + glDeleteTextures(1,opengl_id); + } +} + +bool Texture::load_tga(const std::string& filename, uint8_t*& data_array) +{ + std::fstream file_stream(filename,std::ios_base::in|std::ios_base::binary); + if (file_stream.fail()) + { + std::cerr << "Error while opening " << filename << std::endl; + return false; + } + + // Length of the image ID + uint8_t id_length = 0; + file_stream >> id_length; + + file_stream.seekg(1,std::ios_base::cur); + uint8_t tga_data_type = 0; + file_stream >> tga_data_type; + + // Only supported type : Raw RGB data + if (tga_data_type != 2) + { + std::cerr << "Error loading file " << filename << ": Unsupported format " << tga_data_type << std::endl; + file_stream.close(); + return false; + } + + // Place the cursor to the position of the width header data + file_stream.seekg(12,std::ios_base::beg); + file_stream.read((char*)&width,2); + file_stream.read((char*)&height,2); + file_stream >> color_bits; + + if (color_bits != 24 && color_bits != 32) + { + std::cerr << "Error loading file " << filename << ": Unsupported " << color_bits << " bits colors" << std::endl; + file_stream.close(); + return false; + } + + // Move cursor to the end to get file size + file_stream.seekg(0,std::ios_base::end); + // Size of the file minus header length minus ID length + int data_length = width*height*(color_bits/8); + + // Move cursor to the start of the data. + file_stream.seekg(18+id_length,std::ios_base::beg); + + data_array = new uint8_t[data_length]; + file_stream.read((char*)data_array,data_length); + + return true; +} + +bool Texture::load_rgb_tga(const std::string& rgb_filename) +{ + return load_tga(rgb_filename,image_data); +} + +bool Texture::load_rgba_tga(const std::string& rgb_filename,const std::string& mask_filename) +{ + return false; +} diff --git a/src/Texture.h b/src/Texture.h new file mode 100644 index 0000000..15724f5 --- /dev/null +++ b/src/Texture.h @@ -0,0 +1,38 @@ +// +// Created by trotfunky on 24/09/2019. +// + +#ifndef TESTS_OPENGL_TEXTURE_H +#define TESTS_OPENGL_TEXTURE_H + +#include +#include +#include +#include + +class Texture { +public: + uint16_t width; + uint16_t height; + + /// Number of bits describing the colors : 24 (RGB) or 32 (RGBA) + uint8_t color_bits; + + uint8_t* image_data; + uint32_t opengl_id[1]; + + Texture(); + ~Texture(); + + /// Load an RGBA image from an RGB TGA file + bool load_rgb_tga(const std::string& rgb_filename); + /// Load an RGBA image from an rgb and an alpha tga file + bool load_rgba_tga(const std::string& rgb_filename,const std::string& mask_filename); + +private: + /// Load TGA image file to an array + bool load_tga(const std::string& filename, uint8_t*& data_array); +}; + + +#endif //TESTS_OPENGL_TEXTURE_H diff --git a/src/displayers.cpp b/src/displayers.cpp index 63e70d3..a7b6fe6 100644 --- a/src/displayers.cpp +++ b/src/displayers.cpp @@ -60,17 +60,39 @@ void display_rotating_pyramid(float x, float y, float z, float c, float h, float glPopMatrix(); } -void display_tree(float x, float y, float z, float h, float w) +void display_tree(float x, float y, float z, float h, float w, const Texture& tree_texture) { + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glBindTexture(GL_TEXTURE_2D,tree_texture.opengl_id[0]); + + glColor4f(1.0f, 1.0f, 1.0f,1.00); glBegin(GL_QUADS); + + // First rectangle + glTexCoord2f(1,0); glVertex3f(x+w/2,y,z); + + glTexCoord2f(0,0); glVertex3f(x-w/2,y,z); + + glTexCoord2f(0,1); glVertex3f(x-w/2,y+h,z); + + glTexCoord2f(1,1); glVertex3f(x+w/2,y+h,z); + // Second rectangle + glTexCoord2f(1,0); glVertex3f(x,y,z+w/2); + + glTexCoord2f(0,0); glVertex3f(x,y,z-w/2); + + glTexCoord2f(0,1); glVertex3f(x,y+h,z-w/2); + + glTexCoord2f(1,1); glVertex3f(x,y+h,z+w/2); + glEnd(); } diff --git a/src/displayers.h b/src/displayers.h index 1fb7d0f..119300c 100644 --- a/src/displayers.h +++ b/src/displayers.h @@ -7,6 +7,8 @@ #include +#include "Texture.h" + /// Displays a square based pyramid from a base position, height and side length /// \param x X coordinate of the center /// \param y Y coordinate of the center @@ -19,6 +21,6 @@ void display_pyramid(float x, float y, float z, float c, float h); void display_rotating_pyramid(float x, float y, float z, float c, float h, float alpha); /// Draw a tree with two quadrilaterals -void display_tree(float x, float y, float z, float h, float w); +void display_tree(float x, float y, float z, float h, float w, const Texture& tree_texture); #endif //TESTS_OPENGL_DISPLAYERS_H diff --git a/src/main.cpp b/src/main.cpp index 00ae327..1fd81e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,12 @@ #include "GL/glut.h" +#include "GL/gl.h" #include "displayers.h" +#include "Texture.h" + volatile unsigned long long int timer_ticks = 0; +static Texture tree_texture; void display() { @@ -25,6 +29,8 @@ void display() display_rotating_pyramid(5,-2,0,1,5,angleY); display_rotating_pyramid(-2,-2,4,3,2,angleY); + display_tree(0,-5,0,3,5,tree_texture); + glutSwapBuffers(); } @@ -48,6 +54,24 @@ int main(int argc, char** argv) // glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); + // Setup OPenGL to use textures + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT/GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT/GL_CLAMP); + glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + // Load and generate tree texture + tree_texture.load_rgb_tga("resources/arbre.tga"); + glGenTextures(1,tree_texture.opengl_id); + glBindTexture(GL_TEXTURE_2D,tree_texture.opengl_id[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, tree_texture.width,tree_texture.height,0,GL_RGB,GL_UNSIGNED_BYTE,tree_texture.image_data); + gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB8,tree_texture.width,tree_texture.height,GL_RGB,GL_UNSIGNED_BYTE,tree_texture.image_data); + glViewport(0,0,glutGet(GLUT_WINDOW_WIDTH),glutGet(GLUT_WINDOW_HEIGHT)); glutIdleFunc(display);