I have used the wonderfull IA agent Le Chat from Mistral for to give you a view of a tetrahedron glued with a blue, green and red tetrahedrons on his externals faces.
(the entry face of the initial tetrahedron is display in gray)
Note that this use now VAO et VBO instead glBegin(GL_TRIANGLES) / numerous glVertex3fv() / glEnd()
(this have loose some genericity compared to the glTetra3fv() way but I think correct this after a good studing / IA analyze of this code for to see where/how correct this)
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>
// Variables pour la gestion de la souris et de la fenêtre
float lastX = 400.0f, lastY = 300.0f;
bool firstMouse = true;
bool mousePressed = false;
float fov = 45.0f;
float sensitivity = 0.5f;
float rotationAngleX = 30.0f;
float rotationAngleY = 45.0f;
int windowWidth = 800;
int windowHeight = 600;
// Structure pour stocker les sommets d'un tétraèdre
struct Tetrahedron {
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> colors;
std::vector<GLuint> indices;
};
// Fonction pour ajouter un tétraèdre à un ensemble de sommets et indices
void addTetrahedron(std::vector<glm::vec3>& vertices, std::vector<glm::vec3>& colors, std::vector<GLuint>& indices,
const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3, const glm::vec3& v4,
const glm::vec3& color) {
GLuint baseIndex = vertices.size();
vertices.push_back(v1);
vertices.push_back(v2);
vertices.push_back(v3);
vertices.push_back(v4);
colors.push_back(color);
colors.push_back(color);
colors.push_back(color);
colors.push_back(color);
// Ajouter les indices pour les 4 faces du tétraèdre
indices.push_back(baseIndex + 0); indices.push_back(baseIndex + 1); indices.push_back(baseIndex + 2); // Face 1
indices.push_back(baseIndex + 0); indices.push_back(baseIndex + 1); indices.push_back(baseIndex + 3); // Face 2
indices.push_back(baseIndex + 0); indices.push_back(baseIndex + 2); indices.push_back(baseIndex + 3); // Face 3
indices.push_back(baseIndex + 1); indices.push_back(baseIndex + 2); indices.push_back(baseIndex + 3); // Face 4
}
// Vertex shader (GLSL)
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 ourColor;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
ourColor = aColor;
}
)";
// Fragment shader (GLSL)
const char* fragmentShaderSource = R"(
#version 330 core
in vec3 ourColor;
out vec4 FragColor;
void main() {
FragColor = vec4(ourColor, 1.0);
}
)";
// Fonction pour compiler un shader
GLuint compileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(shader, 512, NULL, infoLog);
std::cerr << "Erreur de compilation du shader : " << infoLog << std::endl;
}
return shader;
}
// Fonction pour créer un programme de shaders
GLuint createShaderProgram() {
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
int success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(program, 512, NULL, infoLog);
std::cerr << "Erreur de linkage du programme : " << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
// Callback pour le mouvement de la souris
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
if (!mousePressed) return;
if (firstMouse) {
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
xoffset *= sensitivity;
yoffset *= sensitivity;
rotationAngleY += xoffset;
rotationAngleX += yoffset;
}
// Callback pour le clic de la souris
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
mousePressed = true;
firstMouse = true;
} else if (action == GLFW_RELEASE) {
mousePressed = false;
}
}
}
// Callback pour la molette de la souris
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
if (fov >= 1.0f && fov <= 45.0f)
fov -= yoffset;
if (fov <= 1.0f)
fov = 1.0f;
if (fov >= 45.0f)
fov = 45.0f;
}
// Callback pour le redimensionnement de la fenêtre
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
windowWidth = width;
windowHeight = height;
glViewport(0, 0, width, height);
}
int main() {
// Initialisation de GLFW
if (!glfwInit()) {
std::cerr << "Échec de l'initialisation de GLFW" << std::endl;
return -1;
}
// Configuration de la fenêtre
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Création de la fenêtre
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, "Tétraèdres avec arêtes épaisses - YANNOO", NULL, NULL);
if (!window) {
std::cerr << "Échec de la création de la fenêtre" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Configuration des callbacks
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// Initialisation de GLEW
if (glewInit() != GLEW_OK) {
std::cerr << "Échec de l'initialisation de GLEW" << std::endl;
return -1;
}
// Activation du test de profondeur
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
// Création du programme de shaders
GLuint shaderProgram = createShaderProgram();
// Définition des sommets du tétraèdre principal
glm::vec3 v1(-0.5f, -0.5f, 0.0f);
glm::vec3 v2(0.5f, -0.5f, 0.0f);
glm::vec3 v3(0.0f, 0.5f, 0.0f);
glm::vec3 v4(0.0f, 0.0f, 0.707f);
// Définition des sommets des sous-tétraèdres
glm::vec3 v5 = (v1 + v2 + v4) / 2.0f + glm::normalize(glm::cross(v2 - v1, v4 - v1)) * 0.2f; // Sommet pour le tétraèdre rouge
glm::vec3 v6 = (v2 + v3 + v4) / 2.0f + glm::normalize(glm::cross(v3 - v2, v4 - v2)) * 0.2f; // Sommet pour le tétraèdre vert
glm::vec3 v7 = (v3 + v1 + v4) / 2.0f + glm::normalize(glm::cross(v1 - v3, v4 - v3)) * 0.2f; // Sommet pour le tétraèdre bleu
glm::vec3 v8 = (v1 + v2 + v3) / 2.0f + glm::normalize(glm::cross(v2 - v1, v3 - v1)) * 0.2f; // Sommet pour le tétraèdre gris
// Vecteurs pour stocker les sommets, couleurs et indices
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> colors;
std::vector<GLuint> indices;
// Ajout du tétraèdre principal
addTetrahedron(vertices, colors, indices, v1, v2, v3, v4, glm::vec3(0.5f, 0.5f, 0.5f));
// Ajout des sous-tétraèdres
addTetrahedron(vertices, colors, indices, v1, v2, v4, v5, glm::vec3(1.0f, 0.0f, 0.0f)); // Rouge
addTetrahedron(vertices, colors, indices, v2, v3, v4, v6, glm::vec3(0.0f, 1.0f, 0.0f)); // Vert
addTetrahedron(vertices, colors, indices, v3, v1, v4, v7, glm::vec3(0.0f, 0.0f, 1.0f)); // Bleu
addTetrahedron(vertices, colors, indices, v1, v2, v3, v8, glm::vec3(0.5f, 0.5f, 0.5f)); // Gris
// Création des buffers pour les faces
GLuint VBO, VAO, EBO, colorVBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glGenBuffers(1, &colorVBO);
// Binding du VAO
glBindVertexArray(VAO);
// Binding et remplissage du VBO pour les sommets
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
// Binding et remplissage du VBO pour les couleurs
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), &colors[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
// Binding et remplissage de l'EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
// Création des buffers pour les arêtes
GLuint edgeVBO, edgeVAO;
glGenVertexArrays(1, &edgeVAO);
glGenBuffers(1, &edgeVBO);
// Définition des arêtes
std::vector<glm::vec3> edgeVertices;
std::vector<GLuint> edgeIndices;
// Ajout des arêtes du tétraèdre principal
edgeVertices.insert(edgeVertices.end(), {v1, v2, v3, v4});
edgeIndices = {
0, 1, // Arête v1-v2
1, 2, // Arête v2-v3
2, 0, // Arête v3-v1
0, 3, // Arête v1-v4
1, 3, // Arête v2-v4
2, 3 // Arête v3-v4
};
// Ajout des arêtes des sous-tétraèdres
GLuint baseIndex = edgeVertices.size();
edgeVertices.insert(edgeVertices.end(), {v1, v2, v4, v5});
edgeIndices.insert(edgeIndices.end(), {
baseIndex + 0, baseIndex + 1, // Arête v1-v2
baseIndex + 1, baseIndex + 2, // Arête v2-v4
baseIndex + 2, baseIndex + 0, // Arête v4-v1
baseIndex + 0, baseIndex + 3, // Arête v1-v5
baseIndex + 1, baseIndex + 3, // Arête v2-v5
baseIndex + 2, baseIndex + 3 // Arête v4-v5
});
baseIndex = edgeVertices.size();
edgeVertices.insert(edgeVertices.end(), {v2, v3, v4, v6});
edgeIndices.insert(edgeIndices.end(), {
baseIndex + 0, baseIndex + 1, // Arête v2-v3
baseIndex + 1, baseIndex + 2, // Arête v3-v4
baseIndex + 2, baseIndex + 0, // Arête v4-v2
baseIndex + 0, baseIndex + 3, // Arête v2-v6
baseIndex + 1, baseIndex + 3, // Arête v3-v6
baseIndex + 2, baseIndex + 3 // Arête v4-v6
});
baseIndex = edgeVertices.size();
edgeVertices.insert(edgeVertices.end(), {v3, v1, v4, v7});
edgeIndices.insert(edgeIndices.end(), {
baseIndex + 0, baseIndex + 1, // Arête v3-v1
baseIndex + 1, baseIndex + 2, // Arête v1-v4
baseIndex + 2, baseIndex + 0, // Arête v4-v3
baseIndex + 0, baseIndex + 3, // Arête v3-v7
baseIndex + 1, baseIndex + 3, // Arête v1-v7
baseIndex + 2, baseIndex + 3 // Arête v4-v7
});
baseIndex = edgeVertices.size();
edgeVertices.insert(edgeVertices.end(), {v1, v2, v3, v8});
edgeIndices.insert(edgeIndices.end(), {
baseIndex + 0, baseIndex + 1, // Arête v1-v2
baseIndex + 1, baseIndex + 2, // Arête v2-v3
baseIndex + 2, baseIndex + 0, // Arête v3-v1
baseIndex + 0, baseIndex + 3, // Arête v1-v8
baseIndex + 1, baseIndex + 3, // Arête v2-v8
baseIndex + 2, baseIndex + 3 // Arête v3-v8
});
// Binding du VAO pour les arêtes
glBindVertexArray(edgeVAO);
// Binding et remplissage du VBO pour les arêtes
glBindBuffer(GL_ARRAY_BUFFER, edgeVBO);
glBufferData(GL_ARRAY_BUFFER, edgeVertices.size() * sizeof(glm::vec3), &edgeVertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
// Création d'un buffer pour les indices des arêtes
GLuint edgeEBO;
glGenBuffers(1, &edgeEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edgeEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, edgeIndices.size() * sizeof(GLuint), &edgeIndices[0], GL_STATIC_DRAW);
// Récupérer les emplacements des matrices dans le shader
GLuint modelLoc = glGetUniformLocation(shaderProgram, "model");
GLuint viewLoc = glGetUniformLocation(shaderProgram, "view");
GLuint projectionLoc = glGetUniformLocation(shaderProgram, "projection");
// Boucle de rendu
while (!glfwWindowShouldClose(window)) {
// Quitter avec la touche Échap
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Matrice de projection
glm::mat4 projection = glm::perspective(glm::radians(fov), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Matrice de vue
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -4.0f));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
// Matrice de modèle avec rotation
glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(rotationAngleX), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(rotationAngleY), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Dessiner les faces des tétraèdres
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
// Dessiner les arêtes des tétraèdres en noir
glUniform3f(glGetUniformLocation(shaderProgram, "ourColorOverride"), 0.0f, 0.0f, 0.0f);
glBindVertexArray(edgeVAO);
glLineWidth(3.0f); // Épaisseur des arêtes
glDrawElements(GL_LINES, edgeIndices.size(), GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
// Nettoyage
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteBuffers(1, &colorVBO);
glDeleteVertexArrays(1, &edgeVAO);
glDeleteBuffers(1, &edgeVBO);
glDeleteBuffers(1, &edgeEBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
I compile this code on my linux box with this :
g++ tetrahedrons.cpp -lglfw -lGLEW -lGL -o tetrahedrons
https://drive.google.com/file/d/1kkjJLiLOmuVtsuaF951FC4bfPkjhgQVV/view?usp=sharing