Мини-лекция про IMGUI [лектор - Zeux]
Автор: _ShaMan_
<Zeux> типичное RMGUI приложение
<Zeux> имеет четко определенные стадии.
<Zeux> 1. инициализация контролов
<Zeux> она производится по-разному
<Zeux> либо в коде добавляются контролы
<Zeux> либо они описываются в xml файле
<Zeux> либо еще как
<Zeux> 2. обновление и синхронизация
<Zeux> тут на самом деле 3 под-пункта
<Zeux> а. запихнуть изменившиеся данные приложения в контролы
<Zeux> (labels для текста, значения слайдеров, поменявшиеся по каким-то причинам, etc.)
<Zeux> b. вызвать какую-то мега-функцию GUI
<Zeux> которая отрендерит контролы и обновит состояние
<Zeux> обработает ввод, etc.
<Zeux> c. запихнуть изменившиеся данные контролов в приложения
<Zeux> 3. деинициализация контролов
<Zeux> замечу
<Zeux> что 3. часто делают автоматически
<Zeux> умными указателями
<Zeux> 2.a. в некоторых случаях делают полуавтоматически
<Zeux> например, передавая при создании слайдера в него - указатель на данные
<Zeux> но тут варианты очень ограничены
<Zeux> 2.c часто делают в 2.b
<Zeux> вызывая колбеки
<Zeux> тем не менее
<Zeux> все эти стадии есть
<MiF> интересно минимально сформулированное отличие имгуя от рмгуя
<Zeux> сейчас попробую
<Zeux> хотя оно немного разное у всех :)
<Zeux> так вот
<Zeux> IMGUI предлагает совсем другой подход
<Zeux> замечу
<Zeux> что в RMGUI основных проблем - 2
<Zeux> первая - информация о контролах разнесена
<Zeux> потому что 1. их нужно создавать/удалять, 2. ими нужно управлять
<Zeux> часто для упрощения управления даже делают id
<Zeux> т.е. CreateButton(ID_BUTTON1, params)
<Zeux> а потом оперируют с ID_BUTTON1
<Zeux> так сделано в DXUT
<Zeux> но в любом случае у вас в одном месте - информация о контролах, в другом - о том, как ей управлять
<Zeux> это - не хорошо и не плохо
<Zeux> разделение этой информации в некоторых случаях - очень во благо
<Zeux> например, когда над ней И ТАК работают разные люди
<Zeux> тогда у них ВСЕ РАВНО есть некоторый synchronization point
<Zeux> (дизайнер и программист договариваются, "ты назовешь этот слайдер Exposure"
<Zeux> etc.)
<Zeux> и наконец вторая проблема
<Zeux> контролы в подавляющем большинстве содержат информацию в себе
<Zeux> в слайдере - есть float value
<Zeux> в чекбоксе - bool checked
<Zeux> в листбоксе - int selected_item, float scrollbar_position
<Zeux> и проблема заключается в том
<Zeux> что у вашего приложения стейты - совсем не такие
<Zeux> у вас есть enum HDRType
<Zeux> он где-то там хранится
<Zeux> конечно, не в листбоксе!
<Zeux> т.е. часть состояния дублируется
<Zeux> например, float scrollbar_position может быть и не дублируется. А может быть и дублируется - смотря что вам нужно сохранять при выходе из приложения или даже при закрытии/открытии окон
<Zeux> именно эти 2 проблемы и называются - Retained Mode
<Zeux> у вас 1. есть объекты (которые надо создавать/удалять/управлять), 2. в них хранится состояние
<Zeux> грубо говоря, чтобы не рисовать контрол - надо сделать ему setVisible(false).
<Zeux> IMGUI нацелен в основном на решение второй проблемы.
<Zeux> т.е. основное отличие IMGUI
<Zeux> это - стейты контролов, не относящиеся к внутренней информации GUI - иначе говоря, стейты контролов, которыми приложение может хотеть управлять - управляются приложением
<Zeux> хранятся приложением
<Zeux> а не GUI.
<Zeux> также IMGUI пытается решать первую проблему
<Zeux> пытается - потому что есть разные IMGUI, и не всегда ее целесообразно решать,
<Zeux> Итак, маленький пример.
<Zeux> пусть у вас есть кнопка
<Zeux> и вы хотите, чтобы при нажатии на нее - приложение закрывалось.
<Zeux> типичные способы сделать это в RMGUI.
<Zeux> 1.
<Zeux> где-то в начале приложения: scoped_ptr<Button> button; где-то в инициализации: button.reset(new Button(data));
<Zeux> button->setOnClick(&MyFunc);
<Zeux> dialog->add(button)
<Zeux> где-то в главном цикле:
<Zeux> dialog->update();
<Zeux> где-то еще:
<Zeux> void MyFunc() { exit(); }
<Zeux> 2.
<Zeux> где-то в инициализации:
<Zeux> dialog.Load("dialog.xml");
<Zeux> в dialog.xml:
<Zeux> <dialog><button id="Exit" caption="Good bye!" width=120 height=60 /></dialog>
<Zeux> в инициализации еще:
<Zeux> dialog->setCallbackById("Exit", MyFunc);
<Zeux> MyFunc аналогична
<Zeux> 3. где-то в инициализации
<Zeux> dialog->addButton(EXIT_BUTTON_ID, data);
<Zeux> (выше где-то const unsigned int EXIT_BUTTON_ID = 1;)
<Zeux> dialog->registerCallbackFunc(MyFunc);
<Zeux> где-то в главном цикле
<Zeux> dialog->update();
<Zeux> где-то еще
<Zeux> void MyFunc(unsigned int id) { switch (id) { case EXIT_BUTTON_ID: exit(); break; } }
<Zeux> типичные способы сделать это в IMGUI:
<Zeux> 1. в главном цикле: if (button(EXIT_BUTTON_ID, data)) { exit(); }
<Zeux> 2. в главном цикле: if (button(GenerateUniqueIDSomehow(), data)) { exit(); }
<Zeux> 3. в dialog.xml то, что выше
<Zeux> в инициализации dialog.Load("dialog.xml");
<Zeux> в главном цикле if (dialog.button("Exit")) { exit(); }
<Zeux> я пока ни слова не сказал про применимость, это потом
<Zeux> в целом - отличия понятны?
<Denton> да
<Denton> указывается не кудато а сразу делается то что надо. и работа идёт с данными приложения
<Zeux> да
<Zeux> собственно
<Zeux> очевидно - IMGUI - далеко не silver bullet
<Zeux> как и RMGUI
<Zeux> более того!
<Zeux> очевидно, что вообще IM vs RM - мягко говоря не ограничено GUI
<Zeux> в OpenGL есть immediate mode
<Zeux> glBegin(); отдать OpenGL СВОИ ДАННЫЕ; glEnd();
<Zeux> при этом - эти данные можно менять как угодно
<Zeux> если бы OpenGL в них писал - можно было бы свободно из них читать
<Zeux> есть и retained mode
<Zeux> glBufferData(ваши данные)
<Zeux> а потом glBindBuffer
<Zeux> чтобы изменить ваши данные
<Zeux> или прочесть их (если бы OpenGL их менял - или в случае с текстурами, ЕСЛИ он их поменял)
<Zeux> надо это сделать отдельно.
<Zeux> ну и в дополнение
<Zeux> в начале надо сделать glGenTextures
<Zeux> в конце - glDeleteTextures
<Zeux> надеюсь, я переврал меньше половины названий ф-ий.
<Zeux> очевидно, что в данном конкретном случае - IM проще
<Zeux> очевидно также
<Zeux> что в случае статических данных
<Zeux> и в случае OpenGL
<Zeux> второй подход будет быстрее
<Zeux> по очевидным причинам.
<Zeux> т.е. очевидно, что в разных ситуациях разные подходы применимы или не применимы в разной степени
<Zeux> итак
<Zeux> плюсы IMGUI - как я их вижу.
<Zeux> - простые вещи делаются просто, потому что вы пишете ТУПОЙ, ПРЯМОЛИНЕЙНЫЙ код.
<Zeux> потому что когда вам надо написать "закрыть при нажатии" - вы пишете if (button(...)) { закрыть }
<Zeux> когда вам надо сделать слайдер
<Zeux> с текстом под ним
<Zeux> который двигается со слайдером
<Zeux> и в котором меняется текст
<Zeux> вы пишете ровно это
<Zeux> float value = slider(data);
<Zeux> text(slider_pos.x + value * slider_width, slider_pos.y + someconstant, "Value: " + tostring(value), font_align_center);
<Zeux> вместо:
<Zeux> 1. создания отдельного коллбека для слайдера
<Zeux> 2. в нем поиска лейбла по ID
<Zeux> 3. в нем установки позиции и текста
<Zeux> если у вас есть пара editbox + slider, которые контролирует одно значение, то у вас не возникает проблем с синхронизацией.
<Zeux> из очевидных соображений.
<Zeux> потому что - НЕТ никакой синхронизации
<Zeux> она есть, когда данные представлены в нескольких местах
<Zeux> а здесь все данные - у вас.
<Zeux> - сложные вещи ИНОГДА делаются не сложнее, чем сама вещь
<Zeux> по тем же самым причинам
<Zeux> - не только код, использующий GUI, но и код самого GUI - прост, туп, прямолинеен. Это очень хорошо
<Zeux> потому что типично требования к GUI библиотеке меняются
<Zeux> - разделять функциональность не сложнее, чем в любом другом случае
<Zeux> это такой же код в конце концов
<Zeux> разделять - в смысле share
24 мая 2007