#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 <stdlib.h>
#include <stdio.h>
#include <vector>
#include <math.h>
#include <fstream>
#include <sstream>
using namespace std;
struct Point {
float x, y, z;
};
int windowWidth = 640, windowHeight = 640;
//Параметры фигуры:
int floorsNum = 40;
int faceNum = 60;
vector<Point> vertices;
vector<unsigned int> indices;
bool polygonFill = true;
GLfloat rotx = 0, roty = 0, rotz = 0;
GLfloat scale = 0.25;
Point conusPos {0, 0, 0};
unsigned int vbo, ebo;
GLuint shader;
glm::mat4 project;
void drawConus();
void calculateVertices();
void frame_callback(GLFWwindow *window, int width, int hight);
void error_callback(int error, const char *description);
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods);
GLuint createShader(const char *vertexSh, const char *fragmentSh);
int main() {
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, "Koshkina", NULL, NULL);
if (!window) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glewInit();
glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, frame_callback);
glEnable(GL_DEPTH_TEST);
GLfloat teta = (float)M_PI * 30 / 180;
GLfloat matrix[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
-cosf(teta), -sinf(teta), -1, 0,
0, 0, 0, 1
};
project = glm::make_mat4x4(matrix);
shader = createShader("../vert.glsl", "../frag.glsl");
glUseProgram(shader);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
calculateVertices();
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix);
drawConus();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
int ind(int i, int j) {
if (i < floorsNum - 1) {
return i*faceNum + j + 1;
}
return i*faceNum + j + 2;
}
//конический цилиндр (рассчет вершин)
void calculateVertices() {
vertices.resize(floorsNum*faceNum + 2);
indices.clear();
float alpha = (float)M_PI*2/(faceNum - 1);
float hstep = 0.9f /floorsNum;
vertices[0] = {0, 0, 0};
for (int i = 0; i < floorsNum; i++) {
for (int j = 0; j < faceNum; j++) {
//Координаты вершины
vertices[ind(i, j)].x = ((1 - i * hstep) * cosf(alpha * j));
vertices[ind(i, j)].y = (hstep * i);
vertices[ind(i, j)].z = ((1 - i * hstep) * sinf(alpha * j));
if (i < floorsNum - 1) {
indices.push_back(ind(i, j));
indices.push_back(ind(i + 1, j));
}
}
}
vertices[ind(floorsNum - 1, 0) - 1] = {0, vertices[ind(floorsNum - 1, 0)].y,0};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Point), &vertices[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(unsigned int), &indices[0], GL_DYNAMIC_DRAW);
GLuint posAttr = glGetAttribLocation(shader, "pos");;
glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(Point), (GLvoid*)(0*sizeof(GLfloat)));
glEnableVertexAttribArray(posAttr);
}
//конический цилиндр (усеченный конус)
void drawConus() {
glMatrixMode(GL_MODELVIEW);
glm::mat4 model = glm::translate(glm::mat4(1.f), { conusPos.x, conusPos.y, conusPos.z });
model *= glm::rotate(model, glm::radians(rotx), {1, 0, 0});
model *= glm::rotate(model, glm::radians(roty), {0, 1, 0});
model *= glm::rotate(model, glm::radians(rotz), {0, 0, 1});
model = glm::scale(model, {scale, scale, scale});
if (!polygonFill) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glUseProgram(shader);
glUniformMatrix4fv(glGetUniformLocation(shader, "matrix"), 1, GL_FALSE, glm::value_ptr(project * model));
glDrawArrays(GL_TRIANGLE_FAN, 0, faceNum + 1);
glDrawArrays(GL_TRIANGLE_FAN, ind(floorsNum - 1, 0) - 1, faceNum + 1);
glDrawElements(GL_TRIANGLE_STRIP, indices.size(), GL_UNSIGNED_INT, nullptr);
glLoadIdentity();
}
GLuint compile(const char *path, GLenum type, char *infoLog) {
std::ifstream source(path);
std::ostringstream oss;
oss << source.rdbuf();
std::string str = oss.str();
const char* charStr = str.c_str();
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &charStr, nullptr);
glCompileShader(shader);
return shader;
}
GLuint createShader(const char *vertexSh, const char *fragmentSh) {
GLuint vertexShader = compile(vertexSh, GL_VERTEX_SHADER, nullptr);
GLuint fragmentShader = compile(fragmentSh, GL_FRAGMENT_SHADER, nullptr);
GLuint id = glCreateProgram();
glAttachShader(id, vertexShader);
glAttachShader(id, fragmentShader);
glLinkProgram(id);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return id;
}
void error_callback(int error, const char *description) {
fputs(description, stderr);
}
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
switch (key) {
case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GL_TRUE); break; //закрытие окна
case GLFW_KEY_ENTER: polygonFill = !polygonFill; break; //каркас
case GLFW_KEY_W: rotx += 5; break;
case GLFW_KEY_S: rotx -= 5; break;
case GLFW_KEY_A: roty -= 5; break;
case GLFW_KEY_D: roty += 5; break;
case GLFW_KEY_Q: rotz += 5; break;
case GLFW_KEY_E: rotz -= 5; break;
case GLFW_KEY_MINUS: //уменьшение
if (scale >= 0.1)
scale -= 0.1;
break;
case GLFW_KEY_EQUAL: //увеличение
scale += 0.1;
break;
case GLFW_KEY_UP: conusPos.y += 0.1; break;
case GLFW_KEY_DOWN: conusPos.y -= 0.1; break;
case GLFW_KEY_LEFT: conusPos.x -= 0.1; break;
case GLFW_KEY_RIGHT: conusPos.x += 0.1; break;
case GLFW_KEY_M: //увеличение количества граней
faceNum += 1;
calculateVertices();
break;
case GLFW_KEY_L: //уменьшение количества граней
if (faceNum > 3) {
faceNum -= 1;
calculateVertices();
}
break;
case GLFW_KEY_J: //уменьшение количества этажей
if (floorsNum > 3) {
floorsNum -= 1;
calculateVertices();
}
break;
case GLFW_KEY_K: //увеличение количества этажей
floorsNum += 1;
calculateVertices();
break;
}
}
}
//Масштабирование окна
void frame_callback(GLFWwindow *window, int width, int hight) {
if (width <= hight) {
glViewport(0, 0, width, width);
} else glViewport(0, 0, hight, hight);
}