Я разрабатываю небольшой игровой движок для Android с Android NDK и opengl es 2.0, в последнее время проект набирает обороты, и мне нужно реорганизовать некоторый код, и я не смог найти подходящий шаблон дизайна для следующей проблемы.
На Android, когда приложение достигает состояния OnPause (), контекст opengl уничтожается, но состояние переменных и объектов в java и c ++ сохраняется. поэтому каждый раз, когда проигрыватель делает паузу и возобновляет работу приложения, мне приходится заново инициализировать часть opengl, буферы, шейдеры, вершины и т. д.
У меня есть классы типа «Квадрат», которые создают «квадратные объекты», и у каждого есть свои собственные атрибуты, и каждый «квадратный объект» может быть нарисован, так что квадраты могут получить доступ к статическим (opengl) членам класса, которые используются быть должным образом оказанным. Поэтому эти статические члены должны быть инициализированы до того, как объекты могут быть нарисованы, я делаю это, когда контекст opengl создается или воссоздается.
Более того, каждый класс имеет свои собственные атрибуты opengl, поэтому каждый класс инициализируется индивидуально со своими собственными параметрами, поэтому я хочу, чтобы каждый класс мог устанавливать некоторые начальные параметры, передавать или перехватывать эти параметры для инициализации статических членов класса ( забыл сказать, что эти параметры являются частными). Но, как я уже говорил, эти параметры необходимо повторно инициализировать каждый раз, когда приложение возобновляется.
В настоящее время я инициализирую эти элементы индивидуально, как
Square::init(/*hardcoded parameters*/);
Circle::init(/*hardcoded parameters*/);
Triangle::init(/*hardcoded parameters*/);
Polygon::init(/*hardcoded parameters*/);
Shape::init(/*hardcoded parameters*/);
.
.
.
.
// Many other inits.....
.
и я хочу написать что-то вроде
// here all the classes with opengl part are initialized
// all init methods of each class are called here, with their respective parameters
Opengl_Initializer::init(); // <--- magic way, no other init calls
Поэтому я хочу установить некоторые (статические / скрытые) переменные для класса, а затем, когда будет создан контекст opengl, класс будет инициализирован «магическим» способом, и при этом не будет необходимости кодировать вызов метода init для каждого учебный класс.
Я пытался использовать наследование, но проблема в том, что мне нужно инициализировать класс, а не объект, также пытался реализовать статический объект и инициализировать этот объект в файле cpp и сохранять указатель на объект в векторе, когда это создается в его конструкторе, в векторе, который находится в собственном классе объекта, но этот дизайн доставил мне много проблем.
Кто-нибудь знает какой-нибудь дизайн, который может мне помочь?
РЕДАКТИРОВАТЬ: структура моих классов
init()
функция действительно большая, потому что shader
а также frag
параметры — это файлы путей, и я выполняю над ними какую-то задачу, передаю результат этого выполнения в opengl и возвращает мне ID
то есть статическая переменная программы, все предложения с частью opengl реализуют этот же процесс, параметр camera
это просто прикрепить его в камеру
class Square {
// static variable all classes have
static GLuint program;
// other glparameters not in common, initialized in the same static init() method
static GLint uniform1;
static GLint uniform2;
public;
// the static init function has the same header on all the classes
static init(const char* shader, const char* frag, const char *camera);
}
и может быть какая-то структура, которую я хотел бы
class Square {
static GLuint program;
static const char *vertex = "hardcode";
static const char *frag = "hardcode";
static const char *cam = "harcode";
static init();
/// or somethig like
static Initializer init(
"harcode shader", "hardcode frag", "hardcode camera",
[&] (void) ->void {
//this is the init function
}
);
public:
}
Это еще одно решение, как ваша задача может быть решена. Идея состоит в том, чтобы иметь некоторый список инициализации (std :: vector) функций, которые должны вызываться в yout Opengl_Initializer :: init ():
std::vector<std::function<void()>> initializer_list;
Если мы сможем поместить все ваши функции квадрата / круга / треугольника … init в этот список, ваша задача станет тривиальной — просто переберите список и вызовите все функции:
// inside Opengl_Initializer::init()
for (auto fn : initializer_list)
fn();
Вы можете добавить функции вручную, например, из int main ():
initializer_list.push_back(&Square::init);
...
Но я полагаю, что вам нужен некоторый дизайн архитектуры, который позволит вам добавлять функции в список инициализаторов без изменения основного или любого другого глобального кода.
Для решения этой задачи мы можем создать небольшой вспомогательный класс, который будет автоматически регистрировать ваши функции инициализации:
struct OpenGLHelper_initializer
{
OpenGLHelper_initializer(std::function<void()> fn)
{
initializer_list.push_back(fn);
}
};
Таким образом, вы можете объявить экземпляр этого класса в вашем Square / Circle:
struct Square
{
static OpenGLHelper_initializer __initializer;
};
И в вашем файле Square.cpp:
OpenGLHelper_initializer Square::__initializer(&Square::init);
Таким образом, когда программа загружается, весь этот инициализатор будет создан, и вся ваша функция «init» будет зарегистрирована в initializer_list.
Это похоже на больше кода, но оно позволит вам добавлять столько фигур, сколько вам нужно, без изменения Opengl_Initializer :: init (); или main.cpp или любой другой глобальный код
Теперь вы можете удалить функции инициализации, если они вам не нравятся и вы используете лямбды:
// in square.cpp
OpenGLHelper_initializer Square::__initializer([](){
std::cout << "Square is initialized now" << std::endl;
});
Вот полный исходный код (обновлен с использованием статической функции) (но без файлов cpp — все в одном):
#include <iostream>
#include <memory>
#include <vector>
using namespace std;/////////////////////////////////////////
// opengl_helper.h
// this is some manager class that knows what should be initialized later
struct OpenGLHelper
{
typedef std::function<void()> function_type;
static std::vector<function_type>& get_initialization_list();
static void register_initializer(function_type fn);
static void run_init();
};
// helper class that will register some function at construction time
struct OpenGLHelper_initializer
{
OpenGLHelper_initializer(OpenGLHelper::function_type fn)
{
OpenGLHelper::register_initializer(fn);
}
};
/////////////////////////////////////////
//opengl_helper.cpp
// using this function we will make our initializer_list be constructued
// before adding anything into it
std::vector<OpenGLHelper::function_type>& OpenGLHelper::get_initialization_list()
{
static std::vector<function_type> initializer_list;
return initializer_list;
}
// function that puts initializer into a list.
void OpenGLHelper::register_initializer(OpenGLHelper::function_type fn)
{
get_initialization_list().push_back(fn);
}
void OpenGLHelper::run_init()
{
for (auto fn : get_initialization_list())
fn();
}
/////////////////////////////////////////
// figure.h
// here is sample class that will be registered for initialization
struct Square
{
static int to_be_initialized;
// static member that will register Square class to be initialized
static OpenGLHelper_initializer __initializer;
};
/////////////////////////////////////////
// Square.cpp
int Square::to_be_initialized = 0;
// this is the most interesting part - register square into initializer list
OpenGLHelper_initializer Square::__initializer([](){
Square::to_be_initialized = 15;
std::cout << "Called Square::init: " << to_be_initialized << std::endl;
});
int main()
{
std::cout << "Before initialization : " << Square::to_be_initialized << std::endl;
OpenGLHelper::run_init();
std::cout << "After initialization : " << Square::to_be_initialized << std::endl;
return 0;
}
Выход:
Before initialization : 0
Called Square::init: 15
After initialization : 15
Кстати, такой способ инициализации используется системой метатипов QT — он использует макросы для упрощения кода
ОБНОВИТЬ:
Как предположил Бен, мы можем устранить небольшую утечку памяти из-за динамического распределения ссылок, если поместим список инициализации в статическую функцию. Вот новый код
Я предлагаю систему управления версиями, чтобы инициализация могла выполняться автоматически во время использования, но таким способом, который пропускает ее очень дешево, когда инициализация уже выполнена. Что-то вроде
int global_gl_generation = 0; // increment each time you recreate the context
inline bool check_gl_generation(int& local_generation)
{
if (local_generation == global_gl_generation)
return false;
local_generation = global_gl_generation;
return true;
}
а затем в каждом классе,
class Square
{
// static variable all classes have
static int generation_inited;
static GLuint program;
static GLint uniform1;
static GLint uniform2;
static init(const char* shader, const char* frag, const char *camera);
public;
void draw() override
{
if (check_gl_generation(generation_inited)) init(...);
// use program, uniform1, uniform2
}
};