Skip to content

Instantly share code, notes, and snippets.

@hydrodog
Created January 8, 2025 21:24
Show Gist options
  • Save hydrodog/b8b54b9a26fb0c1af978eda7bfbd32d2 to your computer and use it in GitHub Desktop.
Save hydrodog/b8b54b9a26fb0c1af978eda7bfbd32d2 to your computer and use it in GitHub Desktop.
#include <GL/glew.h>
#include "common/common.hh"
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <numbers>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <string>
using namespace std;
using namespace glm;
using namespace std::numbers;
class Sphere {
private:
uint32_t progid; // handle to the shader code
uint32_t vao; // array object container for vbo and indices
uint32_t vbo; // handle to the point data on the graphics card
uint32_t lbo; // handle to buffer of indices for lines for wireframe sphere
uint32_t latRes, lonRes;
uint32_t resolution;
uint32_t indexSize;
public:
/**
* @brief Construct a sphere
*
* @param r radius of the sphere
* @param latRes resolution of the grid in latitude
* @param lonRes resolution of the grid in latitude
* @param texturePath path to the texture image
*/
Sphere(double r, uint32_t latRes, uint32_t lonRes);
~Sphere() { cleanup(); }
void render(mat4& trans, GLuint textureID);
void cleanup();
};
Sphere::Sphere(double r, uint32_t latRes, uint32_t lonRes) : latRes(latRes), lonRes(lonRes),
resolution((2*latRes-1)*lonRes + 2) {
progid = loadShaders("06b_texturepoints.vert", "06b_textures.frag");
// progid = loadShaders("03gouraud.vert", "03gouraud.frag");
double dlon = 2.0*numbers::pi / lonRes, dlat = numbers::pi / (2*latRes);
double z;
double lat = -numbers::pi/2 + dlat; // latitude in radians
double rcircle;
float vert[resolution*5]; // x,y,z,u,v
uint32_t c = 0;
for (uint32_t j = 0; j < 2*latRes-1; j++, lat += dlat) {
//what is the radius of hte circle at that height?
rcircle = r* cos(lat); // size of the circle at this latitude
z = r * sin(lat); // height of each circle
cout << "z=" << z << endl;
double t = 0;
for (uint32_t i = 0; i < lonRes; i++, t += dlon) {
vert[c++] = rcircle * cos(t), vert[c++] = rcircle * sin(t);
vert[c++] = z;
vert[c++] = 0.5 + 0.5 * cos(t) / lonRes;
vert[c++] = 0.5 - 0.5 * sin(t) / lonRes;
}
cout << endl;
}
// south pole
vert[c++] = 0;
vert[c++] = 0;
vert[c++] = -r;
vert[c++] = 0.5;
vert[c++] = 0.5;
// north pole
vert[c++] = 0;
vert[c++] = 0;
vert[c++] = r;
vert[c++] = 0.5;
vert[c++] = 0.5;
cout << "resolution: " << resolution << endl;
cout << "predicted num vert components: " << resolution*5 << endl;
cout << "actual num vert components: " << c << endl;
indexSize = resolution * 2 + (2*latRes-1) + lonRes;
//TODO: North and South Poles aren't used
uint32_t indices[indexSize]; // connect every point in circles or latitude and longitude
c = 0;
for (uint32_t j = 0; j < 2*latRes - 2; j++) {
for (uint32_t i = 0; i < lonRes; i++) {
uint32_t current = j * lonRes + i;
uint32_t next = (j + 1) * lonRes + i;
indices[c++] = current;
indices[c++] = next;
}
// Add degenerate triangles to connect strips
indices[c++] = (j + 1) * lonRes + (lonRes - 1);
indices[c++] = (j + 1) * lonRes;
}
cout << "predicted grid points: " << indexSize << endl;
cout << "actual grid indices: " << c << endl;
// Print index data
cout << "Index data: " ;
for (size_t i = 0; i < indexSize; i += 2) {
cout << '(' << indices[i] << ", " << indices[i+1] << ") ";
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, resolution*5*sizeof(float), vert, GL_STATIC_DRAW);
glGenBuffers(1, &lbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize*sizeof(uint32_t), indices, GL_STATIC_DRAW);
glBindVertexArray(0);
}
void Sphere::render(mat4& trans, GLuint textureID) {
glUseProgram(progid); // Use the shader
uint32_t matrixID = glGetUniformLocation(progid, "transform");
glUniformMatrix4fv(matrixID, 1, GL_FALSE, &trans[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
glUniform1i(glGetUniformLocation(progid, "textureSampler"), 0);
glBindVertexArray(vao);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); // Position
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); // Texture coordinates
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, lonRes*2);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
}
void Sphere::cleanup() {
glDeleteBuffers(1, &vbo); // remove vbo memory from graphics card
glDeleteBuffers(1, &lbo); // remove lbo (line indices)
glDeleteVertexArrays(1, &vao); // remove vao from graphics card
glDeleteProgram(progid);
}
using namespace std;
using namespace numbers;
void glmain() {
win = createWindow(800, 800, "Sphere demo");
glClearColor(0.0f, 0.0f, 0.4f, 0.0f); // Dark blue background
GLuint textureID = loadWebPTexture("earth.webp"); // Load the texture
Sphere sphere(1.0, 3, 3);
float rotAngle = 0, dRotAngle = 0.0052;
mat4 northup = rotate(mat4(1.0f), float(-numbers::pi/4), vec3(1, 0, 0));
// mat4 northup = mat4(1.0f);
vec3 up(0, 1, 0); // normal OpenGL coordinates, x positive to right, y is up (z positive out of screen)
do {
mat trans = rotate(northup, radians(23.5f), vec3(0, 0, 1)); // tilt axis
trans = rotate(trans, rotAngle, vec3(0, 1, 0)); // spin on axis
rotAngle += dRotAngle;
glClear(GL_COLOR_BUFFER_BIT); // Clear the screen
glDisable(GL_DEPTH_TEST);
sphere.render(trans, textureID);
glfwSwapBuffers(win); // double buffer
glfwPollEvents();
} while (glfwGetKey(win, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(win) == 0);
glDeleteTextures(1, &textureID); // Clean up the texture
}
#version 330 core
layout(location = 0) in vec3 pos; // Position (x, y, z)
layout(location = 1) in vec2 texCoord; // Texture coordinates (u, v)
uniform mat4 transform;
out vec2 TexCoord;
void main() {
gl_Position = transform * vec4(pos, 1.0);
TexCoord = texCoord;
}
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D textureSampler;
void main() {
FragColor = texture(textureSampler, TexCoord);
}
#include <stdio.h>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <sstream>
#include <webp/decode.h>
using namespace std;
#include <stdlib.h>
#include <string.h>
#include <GL/glew.h>
#include "common.hh"
GLFWwindow* win = nullptr;
GLuint loadShaders(const char vertexPath[], const char * fragmentPath) {
// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Read the Vertex Shader code from the file
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertexPath, std::ios::in);
if(VertexShaderStream.is_open()){
std::stringstream sstr;
sstr << VertexShaderStream.rdbuf();
VertexShaderCode = sstr.str();
VertexShaderStream.close();
}else{
printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertexPath);
getchar();
return 0;
}
// Read the Fragment Shader code from the file
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragmentPath, std::ios::in);
if(FragmentShaderStream.is_open()){
std::stringstream sstr;
sstr << FragmentShaderStream.rdbuf();
FragmentShaderCode = sstr.str();
FragmentShaderStream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
printf("Compiling shader : %s\n", vertexPath);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
// Compile Fragment Shader
printf("Compiling shader : %s\n", fragmentPath);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
// Link the program
printf("Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(ProgramID, VertexShaderID);
glDetachShader(ProgramID, FragmentShaderID);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
return ProgramID;
}
GLFWwindow* createWindow(uint32_t w, uint32_t h, const char title[]) {
// Initialise GLFW
if( !glfwInit() ) {
throw "Failed to initialize GLFW";
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Open a window and create its OpenGL context
GLFWwindow* win = glfwCreateWindow(w, h, title, nullptr, nullptr);
if (win == nullptr) {
glfwTerminate();
throw "Failed to open GLFW window";
}
glfwMakeContextCurrent(win); // create OpenGL context
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
throw "Failed to initialize GLEW";
}
// Ensure we can capture the escape key to quit
glfwSetInputMode(win, GLFW_STICKY_KEYS, GL_TRUE);
return win;
}
/*
standardized main to catch errors.
In this simplified version each error is just reported as a string
It would be better to also track which file and line number the error
happened in, but that would take an exception object.
For now, keeping it simple
*/
int main(int argc, char* argv[]) {
try {
glmain();
glfwTerminate(); // Close OpenGL window and terminate GLFW
} catch (const char* msg) {
cerr << msg << '\n';
exit(-1);
}
return 0;
}
void dump(glm::mat4& mat) {
// TODO: I suspect we are printing the matrix transposed
const float* m = &mat[0][0];
cerr << setprecision(7);
for (int i = 0, c = 0; i < 4; i++) {
for (int j = 0; j < 4; j++, c++)
cerr << setw(14) << m[c];
cerr << '\n';
}
}
void transpt(glm::mat4& m, double x, double y, double z) {
cerr << "orig=(" << x << "," << y << "," << z << ") transformed: (" <<
(m[0][0] * x + m[1][0] * y + m[2][0] * z + m[3][0]) << "," <<
(m[0][1] * x + m[1][1] * y + m[2][1] * z + m[3][1]) << "," <<
(m[0][2] * x + m[1][2] * y + m[2][2] * z + m[3][2]) << ")\t(";
cerr <<
(m[0][0] * x + m[0][1] * y + m[0][2] * z + m[0][3]) << "," <<
(m[1][0] * x + m[1][1] * y + m[1][2] * z + m[1][3]) << "," <<
(m[2][0] * x + m[2][1] * y + m[2][2] * z + m[2][3]) << ")\n";
}
GLuint loadWebPTexture(const char* filePath) {
// Read the file into a buffer
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Failed to open WebP file: " << filePath << std::endl;
return 0;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Failed to read WebP file: " << filePath << std::endl;
return 0;
}
// Decode the WebP image
int width, height;
uint8_t* data = WebPDecodeRGBA(reinterpret_cast<uint8_t*>(buffer.data()), size, &width, &height);
if (!data) {
std::cerr << "Failed to decode WebP image: " << filePath << std::endl;
return 0;
}
// Generate and bind a texture
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
// Upload the texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Free the image data
WebPFree(data);
return textureID;
}
#include <GL/glew.h> // OpenGL API
#include <GLFW/glfw3.h> // Window API
#include <glm/glm.hpp> // Matrix and vector math for OpenGL
#include <glm/ext.hpp>
// all demos use a window, declared globally in common.cc
extern GLFWwindow* win;
GLFWwindow* createWindow(uint32_t w, uint32_t h, const char title[]);
GLuint loadShaders(const char vertexPath[], const char * fragmentPath);
// the signature of the function to write, automatically called by main
void glmain();
/*
we provide a standardized main, because it's always the same
It catches exceptions and quits if there is a problem
you write glmain instead
*/
int main(int argc, char* argv[]);
void dump(glm::mat4& mat);
void transpt(glm::mat4& m, double x, double y, double z);
GLuint loadWebPTexture(const char* filePath);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment