Копия проекта с 06
This commit is contained in:
parent
ebc18f9d4d
commit
290f609dce
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "some_name",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/include",
|
||||||
|
"${workspaceFolder}/../dependencies/GLFW/include",
|
||||||
|
"${workspaceFolder}/../dependencies/glad/include",
|
||||||
|
"${workspaceFolder}/../dependencies/glm",
|
||||||
|
"${workspaceFolder}/../dependencies/stb",
|
||||||
|
"${workspaceFolder}/../dependencies/tinyobjloader"
|
||||||
|
],
|
||||||
|
"compilerPath": "C:/MinGW/bin/g++.exe",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++11",
|
||||||
|
"intelliSenseMode": "gcc-x86"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"fstream": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeinfo": "cpp"
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++: make сборка",
|
||||||
|
"command": "make",
|
||||||
|
"args": [
|
||||||
|
"${input:target}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceRoot}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Задача создана отладчиком."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"id": "target",
|
||||||
|
"description": "Цель сборки (all, list, clean)",
|
||||||
|
"default": "all",
|
||||||
|
"type": "promptString"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Компилятор и директория проекта
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
# С возможностью сборки x32
|
||||||
|
ifeq ($(MAKECMDGOALS), x32)
|
||||||
|
CC = C:/MinGW/bin/g++.exe
|
||||||
|
else
|
||||||
|
CC = C:/MinGW64/bin/g++.exe
|
||||||
|
endif
|
||||||
|
PROJECT_DIR = $(shell echo %cd%)
|
||||||
|
PATH_SEPARATOR = \\
|
||||||
|
# Имя исполняемого файла
|
||||||
|
EXECUTABLE = $(notdir $(strip $(PROJECT_DIR))).exe
|
||||||
|
else
|
||||||
|
CC = g++
|
||||||
|
PROJECT_DIR = $(shell pwd)
|
||||||
|
PATH_SEPARATOR = /
|
||||||
|
# Имя исполняемого файла
|
||||||
|
EXECUTABLE = $(notdir $(strip $(PROJECT_DIR)))
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Опции компилятора
|
||||||
|
CFLAGS += -c
|
||||||
|
CFLAGS += -I./include
|
||||||
|
CFLAGS += -I../dependencies/GLFW/include
|
||||||
|
CFLAGS += -I../dependencies/glad/include
|
||||||
|
CFLAGS += -I../dependencies/glm
|
||||||
|
CFLAGS += -I../dependencies/stb
|
||||||
|
CFLAGS += -I../dependencies/tinyobjloader
|
||||||
|
|
||||||
|
# Опции линкера
|
||||||
|
LDFLAGS += --std=c++11
|
||||||
|
# Архитектурозависимые опции линкера
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
# GLFW в зависимости от архитектуры
|
||||||
|
ifeq ($(MAKECMDGOALS), x32)
|
||||||
|
LDFLAGS += -L../dependencies/GLFW/lib-mingw
|
||||||
|
else
|
||||||
|
LDFLAGS += -L../dependencies/GLFW/lib-mingw-w64
|
||||||
|
endif
|
||||||
|
|
||||||
|
LDFLAGS += -static
|
||||||
|
LDFLAGS += -lglfw3dll
|
||||||
|
LDFLAGS += -lopengl32
|
||||||
|
else
|
||||||
|
LDFLAGS += -lglfw
|
||||||
|
LDFLAGS += -lGL
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Библиотека GLAD
|
||||||
|
GLAD := ../dependencies/glad/src/glad.c
|
||||||
|
GLAD_O := $(GLAD:.c=.o)
|
||||||
|
|
||||||
|
# Файлы из директории src
|
||||||
|
SOURCES_C = $(wildcard src/*.c)
|
||||||
|
SOURCES_CPP = $(wildcard src/*.cpp)
|
||||||
|
|
||||||
|
# Директория с объектными файлами
|
||||||
|
OBJ_DIR := Obj
|
||||||
|
# Объектные файлы
|
||||||
|
OBJECTS = $(addprefix $(OBJ_DIR)/,$(SOURCES_C:src/%.c=%.o) $(SOURCES_CPP:src/%.cpp=%.o))
|
||||||
|
|
||||||
|
# Для x32 сборки под Windows
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
ifeq ($(MAKECMDGOALS), x32)
|
||||||
|
x32: all
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Цель по умолчанию, зависит от EXECUTABLE
|
||||||
|
all: $(EXECUTABLE)
|
||||||
|
|
||||||
|
# Цель сборки исполняемого файла, зависит от OBJ_DIR, OBJECTS и GLAD_O
|
||||||
|
$(EXECUTABLE): $(OBJ_DIR) $(OBJECTS) $(GLAD_O)
|
||||||
|
$(CC) $(OBJECTS) $(GLAD_O) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
# Цель для сборки GLAD
|
||||||
|
$(GLAD_O): $(GLAD)
|
||||||
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
# Цель для создания директории с объектными файлами
|
||||||
|
$(OBJ_DIR):
|
||||||
|
@mkdir $(OBJ_DIR)
|
||||||
|
|
||||||
|
# Цель сборки объектных файлов
|
||||||
|
$(OBJ_DIR)/%.o: src/%.c
|
||||||
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
$(OBJ_DIR)/%.o: src/%.cpp
|
||||||
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
# Цель вывода всех файлов, учавствтующих в сборке
|
||||||
|
list:
|
||||||
|
@echo "В сборке участвуют:" $(OBJECTS)
|
||||||
|
|
||||||
|
# Очистка
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
clean: $(OBJ_DIR)
|
||||||
|
@rmdir /s /q $(OBJ_DIR)
|
||||||
|
else
|
||||||
|
clean: $(OBJ_DIR)
|
||||||
|
@rm -f $(EXECUTABLE) $(OBJECTS)
|
||||||
|
endif
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef BUFFERS_H
|
||||||
|
#define BUFFERS_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
// Объект массива вершин
|
||||||
|
class VAO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VAO(); // Создает VAO и активирует его
|
||||||
|
~VAO(); // Уничтожает VAO
|
||||||
|
VAO(const VAO & copy); // Конструктор копирования
|
||||||
|
VAO& operator=(const VAO & other); // Оператор присваивания
|
||||||
|
|
||||||
|
void use(); // Активация VAO
|
||||||
|
static void disable(); // Деактивация активного VAO
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
|
||||||
|
};
|
||||||
|
|
||||||
|
// Тип буфера
|
||||||
|
enum BUFFER_TYPE { VERTEX = GL_ARRAY_BUFFER
|
||||||
|
, ELEMENT = GL_ELEMENT_ARRAY_BUFFER
|
||||||
|
, UNIFORM = GL_UNIFORM_BUFFER
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект вершинного буфера
|
||||||
|
class BO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BO(BUFFER_TYPE type); // Создает пустой буфер заданного типа
|
||||||
|
BO(BUFFER_TYPE type, const void *data, int size); // Создает и загружает туда данные
|
||||||
|
~BO(); // Уничтожает буфер
|
||||||
|
BO(const BO & copy); // Конструктор копирования
|
||||||
|
BO& operator=(const BO & other); // Оператор присваивания
|
||||||
|
|
||||||
|
void load(const void *data, int size, GLuint mode = GL_STATIC_DRAW); // Загрузка данных в буфер
|
||||||
|
void use();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GLuint handler; // Дескриптор
|
||||||
|
BUFFER_TYPE type; // Тип буфера
|
||||||
|
private:
|
||||||
|
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект uniform-буфера
|
||||||
|
class UBO : public BO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UBO(int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO(const void *data, int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
|
||||||
|
void rebind(int binding); // Перепривязка
|
||||||
|
void loadSub(const void *data, int size, int offset = 0); // Загрузка с отступом
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BUFFERS_H
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
#include <GLM/gtx/euler_angles.hpp>
|
||||||
|
#include <GLM/gtc/matrix_transform.hpp>
|
||||||
|
#include <GLM/ext/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
// Ближняя граница области отсечения
|
||||||
|
#define CAMERA_NEAR 0.1f
|
||||||
|
// Дальняя граница области отсечения
|
||||||
|
#define CAMERA_FAR 100.0f
|
||||||
|
// Вектор, задающий верх для камеры
|
||||||
|
#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f)
|
||||||
|
// Вектор, задающий стандартный поворот углами Эйлера (в положительном направлении оси Z)
|
||||||
|
#define CAMERA_DEFAULT_ROTATION glm::vec3(0.0f, 180.0f, 0.0f)
|
||||||
|
// Стандартный угол обзора
|
||||||
|
#define CAMERA_FOVy 60.0f
|
||||||
|
// Стандартная чувствительность
|
||||||
|
#define CAMERA_DEFAULT_SENSIVITY 0.005f
|
||||||
|
|
||||||
|
class Camera : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Camera(float aspect, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION, float fovy = CAMERA_FOVy); // Конструктор камеры с проекцией перспективы
|
||||||
|
Camera(float width, float height, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION); // Конструктор ортографической камеры
|
||||||
|
Camera(const Camera& copy); // Конструктор копирования камеры
|
||||||
|
Camera& operator=(const Camera& other); // Оператор присваивания
|
||||||
|
virtual ~Camera(); // Деструктор
|
||||||
|
|
||||||
|
const glm::mat4& getVP(); // Возвращает ссылку на константную матрицу произведения матриц вида и проекции
|
||||||
|
const glm::mat4& getProjection(); // Возвращает ссылку на константную матрицу проекции
|
||||||
|
const glm::mat4& getView(); // Возвращает ссылку на константную матрицу вида
|
||||||
|
|
||||||
|
void rotate(const glm::vec2 &xyOffset); // Поворачивает камеру на dx и dy пикселей с учетом чувствительности
|
||||||
|
|
||||||
|
void setPerspective(float fov, float aspect); // Устанавливает заданную матрицу перспективы
|
||||||
|
void setOrtho(float width, float height); // Устанавливает заданную ортографическую матрицу
|
||||||
|
void setSensitivity(float sensitivity); // Изменяет чувствительность мыши
|
||||||
|
const float& getSensitivity() const; // Возвращает чувствительность мыши
|
||||||
|
|
||||||
|
void use(); // Использование этой камеры как текущей
|
||||||
|
static Camera& current(); // Ссылка на текущую используемую камеру
|
||||||
|
protected:
|
||||||
|
Camera(const glm::vec3 &position, const glm::vec3 &initialRotation); // Защищенный (protected) конструктор камеры без перспективы
|
||||||
|
|
||||||
|
glm::mat4 view; // Матрица вида
|
||||||
|
glm::mat4 projection; // Матрица проекции
|
||||||
|
glm::mat4 vp; // Матрица произведения вида и проекции
|
||||||
|
bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры
|
||||||
|
|
||||||
|
float sensitivity; // Чувствительность мыши
|
||||||
|
|
||||||
|
virtual void recalcMatrices(); // Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed
|
||||||
|
|
||||||
|
static Camera* p_current; // Указатель на текущую используемую камеру
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CAMERA_H
|
|
@ -0,0 +1,81 @@
|
||||||
|
#ifndef MODEL_H
|
||||||
|
#define MODEL_H
|
||||||
|
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
#include <GLM/gtc/quaternion.hpp>
|
||||||
|
#include <GLM/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Класс узла сцены
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Node(Node* parent = NULL); // Конструктор с заданным родителем (по умолчанию NULL)
|
||||||
|
Node(const Node& copy); // Конструктор копирования
|
||||||
|
Node& operator=(const Node& other); // Оператор присваивания
|
||||||
|
virtual ~Node();
|
||||||
|
|
||||||
|
void setParent(Node * parent); // Устанавливает родителя для узла
|
||||||
|
|
||||||
|
virtual const glm::mat4& getTransformMatrix(); // Возвращает матрицу трансформации модели
|
||||||
|
bool isChanged(); // Возвращает необходимость пересчета матрицы трансформации
|
||||||
|
|
||||||
|
const glm::vec3& c_position() const; // Константный доступ к позиции
|
||||||
|
const glm::quat& c_rotation() const; // Константный доступ к повороту
|
||||||
|
const glm::vec3& c_scale() const; // Константный доступ к масштабированию
|
||||||
|
virtual glm::vec3& e_position(); // Неконстантная ссылка для изменений позиции
|
||||||
|
virtual glm::quat& e_rotation(); // Неконстантная ссылка для изменений поворота
|
||||||
|
virtual glm::vec3& e_scale(); // Неконстантная ссылка для изменений масштабирования
|
||||||
|
|
||||||
|
Node* getParent(); // Возвращает указатель на родителя
|
||||||
|
const std::vector<Node*>& getChildren() const; // Возвращает ссылку на вектор дочерних узлов
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Node *parent; // Родительский узел
|
||||||
|
std::vector<Node*> children; // Узлы-потомки !Не должны указывать на NULL!
|
||||||
|
|
||||||
|
glm::vec3 position; // позиция модели
|
||||||
|
glm::quat rotation; // поворот модели
|
||||||
|
glm::vec3 scale; // масштабирование модели
|
||||||
|
|
||||||
|
bool changed; // Флаг необходимости пересчета матрицы трансформации
|
||||||
|
glm::mat4 transform; // Матрица трансформации модели
|
||||||
|
bool parent_changed; // Флаг изменений у родителя - необходимость пересчета итоговой трансформации
|
||||||
|
glm::mat4 result_transform; // Итоговая трансформация с учетом родительской
|
||||||
|
|
||||||
|
virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
|
||||||
|
void invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс модели
|
||||||
|
class Model : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Model(Node *parent = NULL); // Конструктор по умолчанию
|
||||||
|
Model(const Model& copy); // Конструктор копирования
|
||||||
|
Model& operator=(const Model& other); // Оператор присваивания
|
||||||
|
virtual ~Model();
|
||||||
|
|
||||||
|
void render(const GLuint &model_uniform); // Вызов отрисовки
|
||||||
|
|
||||||
|
void load_verteces(glm::vec3* verteces, GLuint count); // Загрузка вершин в буфер
|
||||||
|
void load_indices(GLuint* indices, GLuint count); // Загрузка индексов в буфер
|
||||||
|
void load_texCoords(glm::vec2* texCoords, GLuint count); // Загрузка текстурных координат в буфер
|
||||||
|
void load_normals(glm::vec3* normals, GLuint count); // Загрузка нормалей в буфер
|
||||||
|
void set_index_range(size_t first_byteOffset, size_t count); // Ограничение диапазона из буфера индексов
|
||||||
|
void set_texture(Texture& texture); // Привязка текстуры к модели
|
||||||
|
|
||||||
|
private:
|
||||||
|
VAO vao;
|
||||||
|
BO vertex_vbo, index_vbo; // вершинный и индексный буферы
|
||||||
|
BO normals_vbo, texCoords_vbo; // буферы с нормалями и текстурными координатами
|
||||||
|
GLuint verteces_count; // Количество вершин
|
||||||
|
size_t first_index_byteOffset, indices_count; // Сдвиг в байтах для первого и количество индексов
|
||||||
|
Texture texture_diffuse; // Диффузная текстура
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODEL_H
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef SCENE_H
|
||||||
|
#define SCENE_H
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
|
#define DEFAULT_MTL_DIR "./"
|
||||||
|
class Scene loadOBJtoScene(const char* filename, const char* mtl_directory = DEFAULT_MTL_DIR, const char* texture_directory = DEFAULT_MTL_DIR);
|
||||||
|
|
||||||
|
// Класс сцены
|
||||||
|
class Scene
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Scene(); // Конструктор пустой сцены
|
||||||
|
Scene(const Scene ©); // Конструктор копирования
|
||||||
|
Scene& operator=(const Scene& other); // Оператор присваивания
|
||||||
|
|
||||||
|
void render(const GLuint &model_uniform); // Рендер сцены
|
||||||
|
|
||||||
|
Node root; // Корневой узел
|
||||||
|
|
||||||
|
// Списки объектов, выступающих узлами
|
||||||
|
std::list<Node> nodes; // Список пустых узлов
|
||||||
|
std::list<Model> models; // Список моделей для рендера
|
||||||
|
std::list<Camera> cameras; // Список камер
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void rebuld_tree(const Scene& from); // Перестройка дерева после копирования или присваивания
|
||||||
|
template <class T>
|
||||||
|
void rebuild_Nodes_list(T& nodes, const Scene& from); // Перестройка узлов выбранного списка
|
||||||
|
template <class T>
|
||||||
|
void move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& this_nodes); // Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCENE_H
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef SHADER_H
|
||||||
|
#define SHADER_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Класс шейдерной программы
|
||||||
|
class ShaderProgram
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShaderProgram();
|
||||||
|
ShaderProgram(const ShaderProgram ©);
|
||||||
|
~ShaderProgram();
|
||||||
|
ShaderProgram& operator=(const ShaderProgram& other);
|
||||||
|
|
||||||
|
void use(); // Использование шейдеров
|
||||||
|
void load(GLuint type, const char* filename); // Функция для загрузки шейдеров
|
||||||
|
void link(); // Формирование программы из загруженных шейдеров
|
||||||
|
GLuint getUniformLoc(const char* name); // Возвращает местоположение uniform-переменной
|
||||||
|
void bindUniformBlock(const char* name, int binding); // Привязка uniform-блока
|
||||||
|
void bindTextures(const char* textures_base_shader_names[], int count); // Инициализация текстур на шейдере
|
||||||
|
private:
|
||||||
|
GLuint program; // Дескриптор
|
||||||
|
static std::map<int, int> handler_count; // Получение количества использований по дескриптору шейдера (Shared pointer)
|
||||||
|
std::map<const char*, GLuint> uniformLocations; // Местоположения uniform-переменных
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHADER_H
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef TEXTURE_H
|
||||||
|
#define TEXTURE_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum TexType {
|
||||||
|
TEX_DIFFUSE,
|
||||||
|
TEX_AVAILABLE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
class Texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Texture(GLuint type = TEX_AVAILABLE_COUNT, const std::string& filename = ""); // Загрузка текстуры с диска или использование "пустой"
|
||||||
|
Texture(const Texture& other); // Конструктор копирования
|
||||||
|
~Texture();
|
||||||
|
|
||||||
|
Texture& operator=(const Texture& other); // Оператор присваивания
|
||||||
|
|
||||||
|
void use(); // Привязка текстуры
|
||||||
|
static void disable(GLuint type); // Отвязка текстуры по типу
|
||||||
|
GLuint getType(); // Возвращает тип текстуры
|
||||||
|
private:
|
||||||
|
GLuint handler; // Дескриптор текстуры
|
||||||
|
GLuint type; // Тип текстуры, соответствует её слоту
|
||||||
|
static std::map<std::string, int> filename_handler; // Получение дескриптора текстуры по её имени
|
||||||
|
static std::map<int, int> handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTURE_H
|
|
@ -0,0 +1,13 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
uniform sampler2D tex_diffuse;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
color = texture(tex_diffuse, texCoord);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 pos;
|
||||||
|
layout(location = 1) in vec2 inTexCoord;
|
||||||
|
layout(location = 2) in vec3 normals;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform mat4 model;
|
||||||
|
|
||||||
|
out vec2 texCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = projection * view * model * vec4(pos, 1.0);
|
||||||
|
texCoord = inTexCoord;
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
// Счетчики использований дескрипторов
|
||||||
|
std::map<GLuint, GLuint> VAO::handler_count;
|
||||||
|
std::map<GLuint, GLuint> BO::handler_count;
|
||||||
|
|
||||||
|
// Создает VAO и активирует его
|
||||||
|
VAO::VAO()
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &handler); // Генерация одного объекта массива вершин
|
||||||
|
glBindVertexArray(handler); // Привязка для использования
|
||||||
|
handler_count[handler] = 1; // Инициализация счетчика для дескриптора
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожает VAO
|
||||||
|
VAO::~VAO()
|
||||||
|
{
|
||||||
|
// Если дескриптор никем не используется - освободим его
|
||||||
|
if (!--handler_count[handler])
|
||||||
|
{
|
||||||
|
glDeleteVertexArrays(1, &handler);
|
||||||
|
handler_count.erase(handler); // Удаление из словаря
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
VAO::VAO(const VAO & copy) : handler(copy.handler)
|
||||||
|
{
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
VAO& VAO::operator=(const VAO & other)
|
||||||
|
{
|
||||||
|
// Если это разные дескрипторы
|
||||||
|
if (handler != other.handler)
|
||||||
|
{ // то следуюет удалить текущий перед заменой
|
||||||
|
this->~VAO();
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Активация VAO
|
||||||
|
void VAO::use()
|
||||||
|
{
|
||||||
|
glBindVertexArray(handler); // Привязка VAO для использования
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деактивация активного VAO
|
||||||
|
void VAO::disable()
|
||||||
|
{
|
||||||
|
glBindVertexArray(0); // Отключение VAO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой буфер заданного типа
|
||||||
|
BO::BO(BUFFER_TYPE t) : type(t)
|
||||||
|
{
|
||||||
|
glGenBuffers(1, &handler); // Генерация одного объекта буфера
|
||||||
|
handler_count[handler] = 1;
|
||||||
|
use(); // Привязка буфера
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает и загружает туда данные
|
||||||
|
BO::BO(BUFFER_TYPE t, const void *data, int size) : BO(t)
|
||||||
|
{
|
||||||
|
load(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Уничтожает буфер
|
||||||
|
BO::~BO()
|
||||||
|
{
|
||||||
|
if (handler) // Если буфер был создан
|
||||||
|
{
|
||||||
|
// Если дескриптор никем не используется - освободим его
|
||||||
|
if (!--handler_count[handler])
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &handler);
|
||||||
|
handler_count.erase(handler); // Удаление из словаря
|
||||||
|
}
|
||||||
|
handler = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
BO::BO(const BO & copy) : handler(copy.handler), type(copy.type)
|
||||||
|
{
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
BO& BO::operator=(const BO & other)
|
||||||
|
{
|
||||||
|
// Если это разные дескрипторы
|
||||||
|
if (handler != other.handler)
|
||||||
|
{ // то следуюет удалить текущий перед заменой
|
||||||
|
this->~BO();
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
// Изменим тип
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка вершин в буфер
|
||||||
|
void BO::load(const void *data, int size, GLuint mode)
|
||||||
|
{
|
||||||
|
use(); // Привязка буфера
|
||||||
|
glBufferData(type, size, data, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BO::use()
|
||||||
|
{
|
||||||
|
glBindBuffer(type, handler); // Привязка элементного буфера
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO::UBO(int size, int binding) : BO(UNIFORM, 0, size)
|
||||||
|
{
|
||||||
|
rebind(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
|
||||||
|
UBO::UBO(const void *data, int size, int binding) : BO(UNIFORM, data, size)
|
||||||
|
{
|
||||||
|
rebind(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// перепривязка
|
||||||
|
void UBO::rebind(int binding)
|
||||||
|
{
|
||||||
|
glBindBufferBase(type, binding, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка с отступом
|
||||||
|
void UBO::loadSub(const void *data, int size, int offset)
|
||||||
|
{
|
||||||
|
use();
|
||||||
|
glBufferSubData(type, offset, size, data);
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
|
// Указатель на текущую используемую камеру
|
||||||
|
Camera* Camera::p_current = NULL;
|
||||||
|
|
||||||
|
// Защищенный (protected) конструктор камеры без перспективы
|
||||||
|
Camera::Camera(const glm::vec3 &pos, const glm::vec3 &initialRotation) : Node(NULL) // Пусть по умолчанию камера не относится к сцене
|
||||||
|
{
|
||||||
|
sensitivity = CAMERA_DEFAULT_SENSIVITY;
|
||||||
|
position = pos; // задаем позицию
|
||||||
|
// Определяем начальный поворот
|
||||||
|
glm::quat rotationAroundX = glm::angleAxis( glm::radians(initialRotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
glm::quat rotationAroundY = glm::angleAxis(-glm::radians(initialRotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
glm::quat rotationAroundZ = glm::angleAxis( glm::radians(initialRotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
rotation = rotationAroundX * rotationAroundY * rotationAroundZ;
|
||||||
|
// Признак изменения
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор камеры с проекцией перспективы
|
||||||
|
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec3 &initialRotation, float fovy)
|
||||||
|
: Camera(position, initialRotation)
|
||||||
|
{
|
||||||
|
setPerspective(fovy, aspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор ортографической камеры
|
||||||
|
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec3 &initialRotation)
|
||||||
|
: Camera(position, initialRotation)
|
||||||
|
{
|
||||||
|
setOrtho(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования камеры
|
||||||
|
Camera::Camera(const Camera& copy)
|
||||||
|
: Node(copy), projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity)
|
||||||
|
{
|
||||||
|
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
|
||||||
|
if (!requiredRecalcVP)
|
||||||
|
vp = copy.vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Camera& Camera::operator=(const Camera& other)
|
||||||
|
{
|
||||||
|
Node::operator=(other); // Вызов родительского оператора= для переноса узла
|
||||||
|
|
||||||
|
projection = other.projection;
|
||||||
|
requiredRecalcVP = other.requiredRecalcVP;
|
||||||
|
sensitivity = other.sensitivity;
|
||||||
|
|
||||||
|
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
|
||||||
|
if (!requiredRecalcVP)
|
||||||
|
vp = other.vp;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деструктор
|
||||||
|
Camera::~Camera()
|
||||||
|
{
|
||||||
|
if (p_current == this)
|
||||||
|
p_current = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу проекции
|
||||||
|
const glm::mat4& Camera::getProjection()
|
||||||
|
{
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу вида
|
||||||
|
const glm::mat4& Camera::getView()
|
||||||
|
{
|
||||||
|
recalcMatrices();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на константную матрицу вида
|
||||||
|
const glm::mat4& Camera::getVP()
|
||||||
|
{
|
||||||
|
recalcMatrices();
|
||||||
|
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает заданную матрицу перспективы
|
||||||
|
void Camera::setPerspective(float fovy, float aspect)
|
||||||
|
{
|
||||||
|
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает заданную ортографическую матрицу
|
||||||
|
void Camera::setOrtho(float width, float height)
|
||||||
|
{
|
||||||
|
const float aspect = width / height;
|
||||||
|
projection = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, CAMERA_NEAR, CAMERA_FAR);
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Изменяет чувствительность мыши
|
||||||
|
void Camera::setSensitivity(float sens)
|
||||||
|
{
|
||||||
|
sensitivity = sens;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает чувствительность мыши
|
||||||
|
const float& Camera::getSensitivity() const
|
||||||
|
{
|
||||||
|
return sensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed
|
||||||
|
void Camera::recalcMatrices()
|
||||||
|
{
|
||||||
|
if (changed || parent_changed)
|
||||||
|
{
|
||||||
|
glm::vec3 _position = position;
|
||||||
|
glm::quat _rotation = rotation;
|
||||||
|
if (parent) // Если есть родитель
|
||||||
|
{
|
||||||
|
glm::mat4 normalized_transform = parent->getTransformMatrix();
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
glm::vec3 axis = glm::vec3(normalized_transform[i]);
|
||||||
|
normalized_transform[i] = glm::vec4(glm::normalize(axis), normalized_transform[i].w);
|
||||||
|
}
|
||||||
|
glm::vec4 tmp = normalized_transform * glm::vec4(_position, 1.0f);
|
||||||
|
tmp /= tmp.w;
|
||||||
|
_position = glm::vec3(tmp);
|
||||||
|
_rotation = glm::quat_cast(normalized_transform) * _rotation;
|
||||||
|
}
|
||||||
|
glm::mat4 rotationMatrix = glm::mat4_cast(glm::conjugate(_rotation));
|
||||||
|
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), -_position);
|
||||||
|
view = rotationMatrix * translationMatrix;
|
||||||
|
requiredRecalcVP = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::recalcMatrices();
|
||||||
|
|
||||||
|
if (requiredRecalcVP)
|
||||||
|
{
|
||||||
|
vp = projection * view;
|
||||||
|
requiredRecalcVP = false; // Изменения применены
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поворачивает камеру на dx и dy пикселей с учетом чувствительности
|
||||||
|
void Camera::rotate(const glm::vec2 &xyOffset)
|
||||||
|
{
|
||||||
|
// xyOffset - сдвиги координат мыши, xyOffset.x означает поворот вокруг оси Y, а xyOffset.y - поворот вокруг оси X
|
||||||
|
|
||||||
|
// Вращение вокруг оси Y
|
||||||
|
glm::quat qY = glm::angleAxis(-xyOffset.x * sensitivity, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
|
||||||
|
// Вращение вокруг оси X
|
||||||
|
glm::quat qX = glm::angleAxis(xyOffset.y * sensitivity, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
// Сначала применяем вращение вокруг Y, затем вокруг X
|
||||||
|
rotation = qY * rotation * qX;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Использование этой камеры как текущей
|
||||||
|
void Camera::use()
|
||||||
|
{
|
||||||
|
p_current = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ссылка на текущую используемую камеру
|
||||||
|
Camera& Camera::current()
|
||||||
|
{
|
||||||
|
static Camera default_cam(800.0f/600.0f);
|
||||||
|
|
||||||
|
if (!p_current)
|
||||||
|
return default_cam;
|
||||||
|
else
|
||||||
|
return *p_current;
|
||||||
|
}
|
|
@ -0,0 +1,381 @@
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// Конструктор с заданным родителем (по умолчанию NULL)
|
||||||
|
Node::Node(Node* parent_) : parent(parent_), result_transform(1), parent_changed(false),
|
||||||
|
position(0), rotation(1.0f, 0.0f, 0.0f, 0.0f), scale(1), changed(false), transform(1)
|
||||||
|
{
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
// Запишем себя в потомки
|
||||||
|
parent->children.push_back(this);
|
||||||
|
parent_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Node::Node(const Node& copy): position(copy.position), rotation(copy.rotation), scale(copy.scale),
|
||||||
|
parent(copy.parent), changed(copy.changed), parent_changed(copy.parent_changed), transform(1), result_transform(1)
|
||||||
|
{
|
||||||
|
// Запишем себя в потомки
|
||||||
|
if (parent)
|
||||||
|
parent->children.push_back(this);
|
||||||
|
// Если у оригинала не было изменений - перепишем матрицу трансформации
|
||||||
|
if (!changed)
|
||||||
|
transform = copy.transform;
|
||||||
|
// Если у родителя не было изменений для оригинала - перепишем результирующую матрицу трансформации
|
||||||
|
if (!parent_changed)
|
||||||
|
result_transform = copy.result_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::~Node()
|
||||||
|
{
|
||||||
|
setParent(NULL); // Удаляем себя из потомков
|
||||||
|
// Сообщаем потомкам об удалении родителя
|
||||||
|
for (Node* child : children)
|
||||||
|
child->setParent(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает необходимость пересчета матрицы трансформации
|
||||||
|
bool Node::isChanged()
|
||||||
|
{
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Константный доступ к позиции
|
||||||
|
const glm::vec3& Node::c_position() const
|
||||||
|
{
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Константный доступ к повороту
|
||||||
|
const glm::quat& Node::c_rotation() const
|
||||||
|
{
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Константный доступ к масштабированию
|
||||||
|
const glm::vec3& Node::c_scale() const
|
||||||
|
{
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Неконстантная ссылка для изменений позиции
|
||||||
|
glm::vec3& Node::e_position()
|
||||||
|
{
|
||||||
|
changed = true; // Флаг о изменении
|
||||||
|
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Неконстантная ссылка для изменений поворота
|
||||||
|
glm::quat& Node::e_rotation()
|
||||||
|
{
|
||||||
|
changed = true; // Флаг о изменении
|
||||||
|
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Неконстантная ссылка для изменений масштабирования
|
||||||
|
glm::vec3& Node::e_scale()
|
||||||
|
{
|
||||||
|
changed = true; // Флаг о изменении
|
||||||
|
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает матрицу трансформации модели
|
||||||
|
const glm::mat4& Node::getTransformMatrix()
|
||||||
|
{
|
||||||
|
// Если требуется - пересчитаем матрицу
|
||||||
|
recalcMatrices();
|
||||||
|
|
||||||
|
return result_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересчет матрицы трансформации модели, если это требуется
|
||||||
|
void Node::recalcMatrices()
|
||||||
|
{
|
||||||
|
// Если было изменение по векторам позиции, поворота и масштабирования
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
transform = glm::mat4(1.0f);
|
||||||
|
// Перемещение модели
|
||||||
|
transform = glm::translate(transform, position);
|
||||||
|
// Поворот модели
|
||||||
|
transform = transform * glm::mat4_cast(rotation);
|
||||||
|
// Масштабирование
|
||||||
|
transform = glm::scale(transform, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если собственная или родительская матрицы менялись - необходимо пересчитать итоговую
|
||||||
|
if (changed || parent_changed)
|
||||||
|
{
|
||||||
|
if (parent) // Если есть родитель
|
||||||
|
result_transform = parent->getTransformMatrix() * transform;
|
||||||
|
else // Если нет родителя
|
||||||
|
result_transform = transform;
|
||||||
|
|
||||||
|
parent_changed = changed = false; // Изменения применены
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проход потомков в глубину с изменением флага parent_changed
|
||||||
|
void Node::invalidateParent()
|
||||||
|
{
|
||||||
|
// Цикл по потомкам
|
||||||
|
for (Node* child : children)
|
||||||
|
{
|
||||||
|
child->parent_changed = true; // Флаг
|
||||||
|
child->invalidateParent(); // Рекурсивный вызов для потомков выбранного потомка
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливает родителя для узла
|
||||||
|
void Node::setParent(Node * parent)
|
||||||
|
{
|
||||||
|
// Если замена происходит на другого родителя
|
||||||
|
if (parent != this->parent)
|
||||||
|
{
|
||||||
|
Node* tmp = parent;
|
||||||
|
// Проверка на зацикливание об самого себя
|
||||||
|
while (tmp)
|
||||||
|
{
|
||||||
|
if (tmp == this)
|
||||||
|
return; // Можно выдать exception
|
||||||
|
tmp = tmp->parent;
|
||||||
|
}
|
||||||
|
// Если есть старый родитель - удалим себя из его потомков
|
||||||
|
if (this->parent)
|
||||||
|
{
|
||||||
|
// Поиск в списке родительских потомков
|
||||||
|
auto position = std::find(this->parent->children.begin(), this->parent->children.end(), this);
|
||||||
|
// Если итератор указывает в конец - ничего не найдено
|
||||||
|
if (position != this->parent->children.end())
|
||||||
|
this->parent->children.erase(position); // Само удаление
|
||||||
|
}
|
||||||
|
|
||||||
|
this->parent = parent; // Заменяем указатель на родителя
|
||||||
|
// Если родитель не NULL - добавляем себя в детей
|
||||||
|
if (parent)
|
||||||
|
parent->children.push_back(this);
|
||||||
|
// В любом случае необходимо пересчитать собственную итоговую матрицу
|
||||||
|
parent_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает указатель на родителя
|
||||||
|
Node* Node::getParent()
|
||||||
|
{
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает ссылку на вектор дочерних узлов
|
||||||
|
const std::vector<Node*>& Node::getChildren() const
|
||||||
|
{
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Node& Node::operator=(const Node& other)
|
||||||
|
{
|
||||||
|
position = other.position;
|
||||||
|
rotation = other.rotation;
|
||||||
|
scale = other.scale;
|
||||||
|
changed = other.changed;
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
transform = other.transform;
|
||||||
|
|
||||||
|
setParent(other.parent);
|
||||||
|
|
||||||
|
// Если у other флаг parent_changed == false, то можно переписать матрицу результата с него
|
||||||
|
if (!other.parent_changed)
|
||||||
|
{
|
||||||
|
result_transform = other.result_transform;
|
||||||
|
parent_changed = false; // Сбрасываем флаг после смены родителя
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор по умолчанию
|
||||||
|
Model::Model(Node *parent) : Node(parent), verteces_count(0), first_index_byteOffset(0), indices_count(0),
|
||||||
|
vertex_vbo(VERTEX), index_vbo(ELEMENT), normals_vbo(VERTEX), texCoords_vbo(VERTEX)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Model::Model(const Model& copy) : Node(copy),
|
||||||
|
vao(copy.vao),
|
||||||
|
verteces_count(copy.verteces_count), first_index_byteOffset(copy.first_index_byteOffset), indices_count(copy.indices_count),
|
||||||
|
vertex_vbo(copy.vertex_vbo), index_vbo(copy.index_vbo), normals_vbo(copy.normals_vbo), texCoords_vbo(copy.texCoords_vbo),
|
||||||
|
texture_diffuse(copy.texture_diffuse)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Model& Model::operator=(const Model& other)
|
||||||
|
{
|
||||||
|
Node::operator=(other); // Явный вызов родительского оператора копирования
|
||||||
|
|
||||||
|
vao = other.vao;
|
||||||
|
verteces_count = other.verteces_count;
|
||||||
|
first_index_byteOffset = other.first_index_byteOffset;
|
||||||
|
indices_count = other.indices_count;
|
||||||
|
|
||||||
|
vertex_vbo = other.vertex_vbo;
|
||||||
|
index_vbo = other.index_vbo;
|
||||||
|
texCoords_vbo = other.texCoords_vbo;
|
||||||
|
|
||||||
|
texture_diffuse = other.texture_diffuse;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Model::~Model()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызов отрисовки
|
||||||
|
void Model::render(const GLuint &model_uniform)
|
||||||
|
{
|
||||||
|
// Загрузим матрицу трансформации
|
||||||
|
glUniformMatrix4fv(model_uniform, 1, GL_FALSE, &getTransformMatrix()[0][0]);
|
||||||
|
|
||||||
|
// Подключаем текстуры
|
||||||
|
texture_diffuse.use();
|
||||||
|
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
// Если есть индексы - рисуем с их использованием
|
||||||
|
if (indices_count)
|
||||||
|
{
|
||||||
|
index_vbo.use();
|
||||||
|
glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index_byteOffset));
|
||||||
|
}
|
||||||
|
// Если есть вершины - рисуем на основании массива вершин
|
||||||
|
else if (verteces_count)
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void vertex_attrib_config()
|
||||||
|
{
|
||||||
|
// Определим спецификацию атрибута
|
||||||
|
glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 3 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // необходимость нормировать значения
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка вершин в буфер
|
||||||
|
void Model::load_verteces(glm::vec3* verteces, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO и вершинный буфер
|
||||||
|
vao.use();
|
||||||
|
vertex_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
vertex_vbo.load(verteces, sizeof(glm::vec3)*count);
|
||||||
|
vertex_attrib_config();
|
||||||
|
// Запоминаем количество вершин для отрисовки
|
||||||
|
verteces_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка индексов в буфер
|
||||||
|
void Model::load_indices(GLuint* indices, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO и индексный буфер
|
||||||
|
vao.use();
|
||||||
|
index_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
index_vbo.load(indices, sizeof(GLuint)*count);
|
||||||
|
// Запоминаем количество вершин для отрисовки
|
||||||
|
indices_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void texCoords_attrib_config()
|
||||||
|
{
|
||||||
|
// Определим спецификацию атрибута
|
||||||
|
glVertexAttribPointer( 1 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 2 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // необходимость нормировать значения
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка текстурных координат в буфер
|
||||||
|
void Model::load_texCoords(glm::vec2* texCoords, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
|
||||||
|
texCoords_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
texCoords_vbo.load(texCoords, sizeof(glm::vec2)*count);
|
||||||
|
texCoords_attrib_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для конфигурации атрибута вершинного буфера
|
||||||
|
void normals_attrib_config()
|
||||||
|
{
|
||||||
|
// Устанавливаем связь между VAO и привязанным VBO
|
||||||
|
glVertexAttribPointer( 2 // индекс атрибута, должен совпадать с Layout шейдера
|
||||||
|
, 3 // количество компонент одного элемента
|
||||||
|
, GL_FLOAT // тип
|
||||||
|
, GL_FALSE // необходимость нормировать значения
|
||||||
|
, 0 // шаг
|
||||||
|
, (void *)0 // отступ с начала массива
|
||||||
|
);
|
||||||
|
// Включаем необходимый атрибут у выбранного VAO
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка нормалей в буфер
|
||||||
|
void Model::load_normals(glm::vec3* normals, GLuint count)
|
||||||
|
{
|
||||||
|
// Подключаем VAO
|
||||||
|
vao.use();
|
||||||
|
|
||||||
|
normals_vbo.use();
|
||||||
|
|
||||||
|
// Загрузка вершин в память буфера
|
||||||
|
normals_vbo.load(normals, sizeof(glm::vec3)*count);
|
||||||
|
normals_attrib_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ограничение диапазона из буфера индексов
|
||||||
|
void Model::set_index_range(size_t first_byteOffset, size_t count)
|
||||||
|
{
|
||||||
|
first_index_byteOffset = first_byteOffset;
|
||||||
|
indices_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры к модели
|
||||||
|
void Model::set_texture(Texture& texture)
|
||||||
|
{
|
||||||
|
GLuint type = texture.getType();
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TEX_DIFFUSE:
|
||||||
|
texture_diffuse = texture;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
#include "Scene.h"
|
||||||
|
|
||||||
|
// Конструктор пустой сцены
|
||||||
|
Scene::Scene()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Scene::Scene(const Scene ©): root(copy.root),
|
||||||
|
nodes(copy.nodes), models(copy.models), cameras(copy.cameras)
|
||||||
|
{
|
||||||
|
rebuld_tree(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Scene& Scene::operator=(const Scene& other)
|
||||||
|
{
|
||||||
|
root = other.root;
|
||||||
|
nodes = other.nodes;
|
||||||
|
models = other.models;
|
||||||
|
cameras = other.cameras;
|
||||||
|
|
||||||
|
rebuld_tree(other);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рендер сцены
|
||||||
|
void Scene::render(const GLuint &model_uniform)
|
||||||
|
{
|
||||||
|
for (auto & model : models)
|
||||||
|
model.render(model_uniform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перестройка узлов выбранного списка
|
||||||
|
template <class T>
|
||||||
|
void Scene::rebuild_Nodes_list(T& nodes, const Scene& from)
|
||||||
|
{
|
||||||
|
for (auto it = nodes.begin(); it != nodes.end(); it++)
|
||||||
|
{
|
||||||
|
// Берем родителя, который указывает на оригинальный объект
|
||||||
|
Node* parent = it->getParent();
|
||||||
|
|
||||||
|
// Если родитель - оригинальный корневой узел, то меняем на собственный корневой узел
|
||||||
|
if (parent == &from.root)
|
||||||
|
{
|
||||||
|
it->setParent(&root);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если можно привести к модели, то ищем родителя среди моделей
|
||||||
|
if (dynamic_cast<Model*>(parent))
|
||||||
|
move_parent(*it, from.models, this->models);
|
||||||
|
else
|
||||||
|
// Иначе проверяем на принадлежность к камерам
|
||||||
|
if (dynamic_cast<Camera*>(parent))
|
||||||
|
move_parent(*it, from.cameras, this->cameras);
|
||||||
|
// Иначе это пустой узел
|
||||||
|
else
|
||||||
|
move_parent(*it, from.nodes, this->nodes);
|
||||||
|
|
||||||
|
// Не нашли родителя - значит он не часть этой сцены
|
||||||
|
// и изменений по нему не требуется
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
|
||||||
|
template <class T>
|
||||||
|
void Scene::move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& this_nodes)
|
||||||
|
{
|
||||||
|
// Возьмем адрес родителя
|
||||||
|
Node* parent = for_node.getParent();
|
||||||
|
// Цикл по элементам списков для перемещения родителя
|
||||||
|
// Списки в процессе копирования идеинтичные, вторая проверка не требуется
|
||||||
|
for (auto it_from = from_nodes.begin(), it_this = this_nodes.begin(); it_from != from_nodes.end(); ++it_from, ++it_this)
|
||||||
|
// Если адрес объекта, на который указывает итератор, совпадает с родителем - меняем родителя по второму итератору (it_this)
|
||||||
|
if (&(*it_from) == parent)
|
||||||
|
for_node.setParent(&(*it_this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перестройка дерева после копирования или присваивания
|
||||||
|
void Scene::rebuld_tree(const Scene& from)
|
||||||
|
{
|
||||||
|
// Восстановим родителей в пустых узлах для копии
|
||||||
|
rebuild_Nodes_list(nodes, from);
|
||||||
|
rebuild_Nodes_list(models, from);
|
||||||
|
rebuild_Nodes_list(cameras, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
|
#include "tiny_obj_loader.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
inline void hash_combine(std::size_t& seed) { }
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
||||||
|
std::hash<T> hasher;
|
||||||
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||||
|
hash_combine(seed, rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene loadOBJtoScene(const char* filename, const char* mtl_directory, const char* texture_directory)
|
||||||
|
{
|
||||||
|
Scene result;
|
||||||
|
Model model;
|
||||||
|
// Все модели образованные на основании этой модели будут иметь общего родителя
|
||||||
|
model.setParent(&result.root);
|
||||||
|
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
|
||||||
|
// Если в процессе загрузки возникли ошибки - выдадим исключение
|
||||||
|
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, mtl_directory))
|
||||||
|
throw std::runtime_error(err);
|
||||||
|
|
||||||
|
std::vector<GLuint> indices; // индексы модели
|
||||||
|
std::vector<glm::vec3> verteces; // вершины
|
||||||
|
std::vector<glm::vec3> normals; // нормали
|
||||||
|
std::vector<glm::vec2> texCords; // текстурные координаты
|
||||||
|
size_t hash; // Для уникальных вершин
|
||||||
|
std::map <int, int> uniqueVerteces; // словарь для уникальных вершин: ключ - хеш, значение - индекс вершины
|
||||||
|
|
||||||
|
int last_material_index = 0; // индекс последнего материала (для группировки моделей)
|
||||||
|
int count = 0, offset; // для индексов начала и конца в индексном буфере
|
||||||
|
std::vector<int> materials_range; // хранилище индексов
|
||||||
|
std::vector<int> materials_ids; // индексы материалов
|
||||||
|
|
||||||
|
materials_range.push_back(count); // Закидываем начало отрезка в индексном буфере
|
||||||
|
// Цикл по считанным моделям
|
||||||
|
for (const auto& shape : shapes)
|
||||||
|
{
|
||||||
|
offset = count; // Переменная для
|
||||||
|
last_material_index = shape.mesh.material_ids[(count - offset)/3]; // Запоминаем индекс материала
|
||||||
|
|
||||||
|
// Цикл по индексам модели
|
||||||
|
for (const auto& index : shape.mesh.indices)
|
||||||
|
{
|
||||||
|
hash = 0;
|
||||||
|
hash_combine( hash
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 0], attrib.vertices[3 * index.vertex_index + 1], attrib.vertices[3 * index.vertex_index + 2]
|
||||||
|
, attrib.normals[3 * index.normal_index + 0], attrib.normals[3 * index.normal_index + 1], attrib.normals[3 * index.normal_index + 2]
|
||||||
|
, attrib.texcoords[2 * index.texcoord_index + 0], attrib.texcoords[2 * index.texcoord_index + 1]);
|
||||||
|
|
||||||
|
if (!uniqueVerteces.count(hash))
|
||||||
|
{
|
||||||
|
uniqueVerteces[hash] = verteces.size();
|
||||||
|
|
||||||
|
// группируем вершины в массив на основании индексов
|
||||||
|
verteces.push_back({ attrib.vertices[3 * index.vertex_index + 0]
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 1]
|
||||||
|
, attrib.vertices[3 * index.vertex_index + 2]
|
||||||
|
});
|
||||||
|
// группируем нормали в массив на основании индексов
|
||||||
|
normals.push_back({ attrib.normals[3 * index.normal_index + 0]
|
||||||
|
, attrib.normals[3 * index.normal_index + 1]
|
||||||
|
, attrib.normals[3 * index.normal_index + 2]
|
||||||
|
});
|
||||||
|
// группируем текстурные координаты в массив на основании индексов
|
||||||
|
texCords.push_back({ attrib.texcoords[2 * index.texcoord_index + 0]
|
||||||
|
, 1-attrib.texcoords[2 * index.texcoord_index + 1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Сохраняем индекс в массив
|
||||||
|
indices.push_back(uniqueVerteces[hash]);
|
||||||
|
|
||||||
|
// Если индекс последнего материала изменился, то необходимо сохранить его
|
||||||
|
if (last_material_index != shape.mesh.material_ids[(count - offset)/3])
|
||||||
|
{
|
||||||
|
materials_range.push_back(count); // как конец отрезка
|
||||||
|
materials_ids.push_back(last_material_index); // как используемый материал
|
||||||
|
last_material_index = shape.mesh.material_ids[(count - offset)/3];
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
} // for (const auto& index : shape.mesh.indices)
|
||||||
|
|
||||||
|
// Если последний материал не загружен - загружаем его
|
||||||
|
if (materials_range[materials_range.size()-1] != count-1)
|
||||||
|
{
|
||||||
|
materials_range.push_back(count); // последний конец отрезка
|
||||||
|
materials_ids.push_back(last_material_index); // последний используемый материал
|
||||||
|
}
|
||||||
|
} // for (const auto& shape : shapes)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Загрузка в буферы
|
||||||
|
model.load_verteces (&verteces[0], verteces.size());
|
||||||
|
model.load_normals (&normals[0], normals.size());
|
||||||
|
model.load_texCoords(&texCords[0], texCords.size());
|
||||||
|
// Загрузка индексного буфера
|
||||||
|
model.load_indices (&indices[0], indices.size());
|
||||||
|
|
||||||
|
|
||||||
|
// Создаем копии модели, которые будут рендериться в заданном диапазоне
|
||||||
|
// И присваиваем текстуры копиям на основании материала
|
||||||
|
for (int i = 0; i < materials_range.size()-1; i++)
|
||||||
|
{
|
||||||
|
result.models.push_back(model); // Создание копии с общим VAO
|
||||||
|
auto s = --result.models.end();
|
||||||
|
s->set_index_range(materials_range[i]*sizeof(GLuint), materials_range[i+1]-materials_range[i]);
|
||||||
|
|
||||||
|
Texture diffuse(TEX_DIFFUSE, texture_directory + materials[materials_ids[i]].diffuse_texname);
|
||||||
|
s->set_texture(diffuse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
std::map<int, int> ShaderProgram::handler_count; // Получение количества использований по дескриптору ШП (Shared pointer)
|
||||||
|
|
||||||
|
ShaderProgram::ShaderProgram()
|
||||||
|
{
|
||||||
|
program = glCreateProgram();
|
||||||
|
handler_count[program] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProgram::ShaderProgram(const ShaderProgram ©) : program(copy.program)
|
||||||
|
{
|
||||||
|
handler_count[program]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProgram::~ShaderProgram()
|
||||||
|
{
|
||||||
|
if (!--handler_count[program]) // Если количество ссылок = 0
|
||||||
|
{
|
||||||
|
// Удаление шейдерной программы
|
||||||
|
glDeleteProgram(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
ShaderProgram& ShaderProgram::operator=(const ShaderProgram& other)
|
||||||
|
{
|
||||||
|
// Если это разные шейдерные программы
|
||||||
|
if (program != other.program)
|
||||||
|
{
|
||||||
|
this->~ShaderProgram(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
program = other.program;
|
||||||
|
handler_count[program]++;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Использование шейдеров
|
||||||
|
void ShaderProgram::use()
|
||||||
|
{
|
||||||
|
glUseProgram(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция чтения шейдера из файла
|
||||||
|
std::string readFile(const char* filename)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
std::ifstream file(filename, std::ios::in); // Открываем файл на чтение
|
||||||
|
// Если файл доступен и успешно открыт
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
std::stringstream sstr; // Буфер для чтения
|
||||||
|
sstr << file.rdbuf(); // Считываем файл
|
||||||
|
text = sstr.str(); // Преобразуем буфер к строке
|
||||||
|
file.close(); // Закрываем файл
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для загрузки шейдеров
|
||||||
|
void ShaderProgram::load(GLuint type, const char* filename)
|
||||||
|
{
|
||||||
|
// Создание дескрипторов шейдера
|
||||||
|
GLuint handler = glCreateShader(type);
|
||||||
|
|
||||||
|
// Переменные под результат компиляции
|
||||||
|
GLint result = GL_FALSE;
|
||||||
|
int infoLogLength;
|
||||||
|
|
||||||
|
// Считываем текст вершинного шейдера
|
||||||
|
std::string code = readFile(filename);
|
||||||
|
const char* pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк
|
||||||
|
|
||||||
|
// Компиляция кода вершинного шейдера
|
||||||
|
glShaderSource(handler, 1, &pointer, NULL);
|
||||||
|
glCompileShader(handler);
|
||||||
|
|
||||||
|
// Проверка результата компиляции
|
||||||
|
glGetShaderiv(handler, GL_COMPILE_STATUS, &result);
|
||||||
|
glGetShaderiv(handler, GL_INFO_LOG_LENGTH, &infoLogLength);
|
||||||
|
if (infoLogLength > 0)
|
||||||
|
{
|
||||||
|
char* errorMessage = new char[infoLogLength + 1];
|
||||||
|
glGetShaderInfoLog(handler, infoLogLength, NULL, errorMessage);
|
||||||
|
std::cout << errorMessage;
|
||||||
|
delete[] errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка скомпилированного шейдера
|
||||||
|
glAttachShader(program, handler);
|
||||||
|
|
||||||
|
// Освобождение дескриптора шейдера
|
||||||
|
glDeleteShader(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Формирование программы из загруженных шейдеров
|
||||||
|
void ShaderProgram::link()
|
||||||
|
{
|
||||||
|
// Переменные под результат компиляции
|
||||||
|
GLint result = GL_FALSE;
|
||||||
|
int infoLogLength;
|
||||||
|
|
||||||
|
// Формирование программы из привязанных шейдеров
|
||||||
|
glLinkProgram(program);
|
||||||
|
|
||||||
|
// Проверка программы
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &result);
|
||||||
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
|
||||||
|
if (infoLogLength > 0)
|
||||||
|
{
|
||||||
|
char* errorMessage = new char[infoLogLength + 1];
|
||||||
|
glGetProgramInfoLog(program, infoLogLength, NULL, errorMessage);
|
||||||
|
std::cout << errorMessage;
|
||||||
|
delete[] errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Используем шейдерную программу объекта из которого вызван метод
|
||||||
|
this->use();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает местоположение uniform-переменной
|
||||||
|
GLuint ShaderProgram::getUniformLoc(const char* name)
|
||||||
|
{
|
||||||
|
GLuint result; // Результат
|
||||||
|
// Если такую переменную ещё не искали - найдем, иначе вернем уже известный дескриптор
|
||||||
|
if (!uniformLocations.count(name))
|
||||||
|
uniformLocations[name] = result = glGetUniformLocation(program, name);
|
||||||
|
else
|
||||||
|
result = uniformLocations[name];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка uniform-блока
|
||||||
|
void ShaderProgram::bindUniformBlock(const char* name, int binding)
|
||||||
|
{
|
||||||
|
glUniformBlockBinding( program
|
||||||
|
, glGetUniformBlockIndex(program, name)
|
||||||
|
, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация текстур на шейдере
|
||||||
|
void ShaderProgram::bindTextures(const char* textures_base_shader_names[], int count)
|
||||||
|
{
|
||||||
|
// Цикл по всем доступным текстурам
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
glUniform1i(getUniformLoc(textures_base_shader_names[i]), i);
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb_image.h>
|
||||||
|
|
||||||
|
std::map<std::string, int> Texture::filename_handler; // Получение дескриптора текстуры по её имени
|
||||||
|
std::map<int, int> Texture::handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
|
||||||
|
|
||||||
|
// Загрузка текстуры с диска или использование "пустой"
|
||||||
|
Texture::Texture(GLuint t, const std::string& filename) : type(t)
|
||||||
|
{
|
||||||
|
if (!filename_handler.count(filename))
|
||||||
|
{
|
||||||
|
std::string empty = "";
|
||||||
|
int width, height, channels; // Ширина, высота и цветовые каналы текстуры
|
||||||
|
unsigned char* image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения
|
||||||
|
// Если изображение успешно счиитано с диска или отсутствует пустая текстура
|
||||||
|
if (image || !filename_handler.count(empty))
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glGenTextures(1, &handler); // Генерация одной текстуры
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
|
||||||
|
|
||||||
|
filename_handler[filename] = handler; // Запоминим её дескриптор для этого имени файла
|
||||||
|
handler_count[handler] = 0; // Создадим счетчик использований дескриптора, который будет изменен в конце
|
||||||
|
|
||||||
|
// Если изображение успешно считано
|
||||||
|
if (image)
|
||||||
|
{
|
||||||
|
// Загрузка данных с учетом прозрачности
|
||||||
|
if (channels == 3) // RGB
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
|
||||||
|
else if (channels == 4) // RGBA
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
||||||
|
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D); // Генерация мипмапа для активной текстуры
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка активной текстуры
|
||||||
|
|
||||||
|
stbi_image_free(image); // Освобождение оперативной памяти
|
||||||
|
}
|
||||||
|
// Иначе изображение не считано и надо создать пустую текстуру
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = new unsigned char[3] {255,255,255}; // RGB по 1 байту на
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Загрузка данных на видеокарту
|
||||||
|
delete[] image; // Освобождение оперативной памяти
|
||||||
|
|
||||||
|
filename_handler[empty] = handler; // Запоминим дополнительно её дескриптор для NULL-строки
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Иначе используем существующую пустую текстуру (текстура не загружена, пустую создавать не нужно)
|
||||||
|
else
|
||||||
|
handler = filename_handler[empty];
|
||||||
|
}
|
||||||
|
// Иначе используем уже существующую по имени файла
|
||||||
|
else
|
||||||
|
handler = filename_handler[filename];
|
||||||
|
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конструктор копирования
|
||||||
|
Texture::Texture(const Texture& other) : handler(other.handler), type(other.type)
|
||||||
|
{
|
||||||
|
// Делаем копию и увеличиваем счетчик
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оператор присваивания
|
||||||
|
Texture& Texture::operator=(const Texture& other)
|
||||||
|
{
|
||||||
|
// Если это разные текстуры
|
||||||
|
if (handler != other.handler)
|
||||||
|
{
|
||||||
|
this->~Texture(); // Уничтожаем имеющуюся
|
||||||
|
// Заменяем новой
|
||||||
|
handler = other.handler;
|
||||||
|
handler_count[handler]++;
|
||||||
|
}
|
||||||
|
type = other.type;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::~Texture()
|
||||||
|
{
|
||||||
|
if (!--handler_count[handler]) // Если количество ссылок = 0
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &handler); // Удаление текстуры
|
||||||
|
// Удаление из словаря имен файлов и дескрипторов
|
||||||
|
for (auto it = filename_handler.begin(); it != filename_handler.end();)
|
||||||
|
{
|
||||||
|
if (it->second == handler)
|
||||||
|
it = filename_handler.erase(it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка текстуры
|
||||||
|
void Texture::use()
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отвязка текстуры по типу
|
||||||
|
void Texture::disable(GLuint type)
|
||||||
|
{
|
||||||
|
glActiveTexture(type + GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка текстуры
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращает тип текстуры
|
||||||
|
GLuint Texture::getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <GLM/glm.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "Scene.h"
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#define WINDOW_WIDTH 800
|
||||||
|
#define WINDOW_HEIGHT 600
|
||||||
|
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
|
||||||
|
|
||||||
|
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
|
||||||
|
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||||
|
{
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool firstMouse = true;
|
||||||
|
float lastX, lastY;
|
||||||
|
|
||||||
|
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||||
|
{
|
||||||
|
if (firstMouse)
|
||||||
|
{
|
||||||
|
lastX = xpos;
|
||||||
|
lastY = ypos;
|
||||||
|
firstMouse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 offset(xpos - lastX, lastY - ypos);
|
||||||
|
lastX = xpos;
|
||||||
|
lastY = ypos;
|
||||||
|
|
||||||
|
Camera::current().rotate(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
GLFWwindow* window; // Указатель на окно GLFW3
|
||||||
|
|
||||||
|
// Инициализация GLFW3
|
||||||
|
if (!glfwInit())
|
||||||
|
{
|
||||||
|
std::cout << "GLFW init error\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Завершение работы с GLFW3 перед выходом
|
||||||
|
atexit(glfwTerminate);
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
|
||||||
|
|
||||||
|
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
|
||||||
|
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
std::cout << "GLFW create window error\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Установка основного контекста окна
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
// Установка callback-функции для изменения размеров окна и буфера кадра
|
||||||
|
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||||
|
|
||||||
|
glfwSwapInterval(1); // Вертикальная синхронизация
|
||||||
|
|
||||||
|
// Установка callback-функции для мыши и камеры
|
||||||
|
glfwSetCursorPosCallback(window, mouse_callback);
|
||||||
|
|
||||||
|
// Загрузка функций OpenGL с помощью GLAD
|
||||||
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||||
|
{
|
||||||
|
std::cout << "GLAD load GL error\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Включаем проверку по буферу глубины
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
// Базовый шейдер
|
||||||
|
ShaderProgram base;
|
||||||
|
// Загрузка и компиляция шейдеров
|
||||||
|
base.load(GL_VERTEX_SHADER, "shaders/shader.vert");
|
||||||
|
base.load(GL_FRAGMENT_SHADER, "shaders/shader.frag");
|
||||||
|
base.link();
|
||||||
|
// Установим значения текстур
|
||||||
|
const char* textures_base_shader_names[] = {"tex_diffuse"};
|
||||||
|
base.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*));
|
||||||
|
|
||||||
|
// Загрузка сцены из obj файла
|
||||||
|
Scene scene = loadOBJtoScene("../resources/models/cubes.obj", "../resources/models/", "../resources/textures/");
|
||||||
|
|
||||||
|
// Установка цвета очистки буфера цвета
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// Расположение Uniform-переменной
|
||||||
|
GLuint model_uniform = base.getUniformLoc("model");
|
||||||
|
|
||||||
|
// Uniform-буферы
|
||||||
|
UBO cameraUB(sizeof(glm::mat4)*2, 0);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Использование уменьшенных версий mipmap
|
||||||
|
|
||||||
|
// Пока не произойдет событие запроса закрытия окна
|
||||||
|
while(!glfwWindowShouldClose(window))
|
||||||
|
{
|
||||||
|
// Загрузка данных о камере
|
||||||
|
cameraUB.loadSub(&Camera::current().getProjection(), sizeof(glm::mat4), 0);
|
||||||
|
cameraUB.loadSub(&Camera::current().getView(), sizeof(glm::mat4), sizeof(glm::mat4));
|
||||||
|
|
||||||
|
// Очистка буфера цвета
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Тут производится рендер
|
||||||
|
scene.render(model_uniform);
|
||||||
|
|
||||||
|
// Представление содержимого буфера цепочки показа на окно
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
// Обработка системных событий
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue