basic imgui

Всичко за програмирането на игри - архитектура, графика, звук, изкуствен интелект, мрежи.
Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

basic imgui

Мнение от stoiko » 31 май 2014 16:32

ето това е моята IMGUI "библиотека". тук е имплементната само една "контрола": ClickRect. всички бутони викат тази.

Код: Избери всички

typedef struct {
    v2_t position, size;
    int  handle;
} uiRect_t;

typedef enum {
    UIMBS_NONE,
    UIMBS_DOWN = 1 << 0,
    UIMBS_UP   = 1 << 1,
} uiMouseButtonState;

static uiMouseButtonState ui_mouseButton;

// a stack of rects to check for hits
#define MAX_RECTS 256
static int              ui_numRects;
static uiRect_t         ui_rects[MAX_RECTS];

// this widget is rolled over
static int              ui_hotWidget;

// this widget is usually pressed but not released yet
static int              ui_activeWidget;

static bool_t UI_CursorInRect( v2_t position, v2_t size ) {
    v2_t mouse = v2c2( SYS_MousePos() );
    return mouse.x >= position.x && 
           mouse.y >= position.y && 
           mouse.x < position.x + size.x &&
           mouse.y < position.y + size.y;
}

// this callback might be called multiple times inside a single frame
bool_t UI_OnMouseButton( bool_t down ) {
    if ( down ) {
        ui_mouseButton |= UIMBS_DOWN;
    } else {
        ui_mouseButton |= UIMBS_UP;
    }
    return ( ui_hotWidget || ui_activeWidget );
}

void UI_TestHits( void ) {
    if ( ui_activeWidget ) {
        int i;

        // check if <active> widget is still present on screen
        for ( i = 0; i < ui_numRects; i++ ) {
            if ( ui_rects[i].handle == ui_activeWidget ) {
                break;
            }
        }

        // <active> is no longer drawn/tested
        if ( i == ui_numRects ) {
            ui_activeWidget = 0;
        }
    }

    // <hot> widgets are obtained each frame
    ui_hotWidget = 0;
    
#define BUTTON_RELEASED() ((ui_mouseButton & UIMBS_UP)||ui_mouseButton==UIMBS_NONE)

    // go back to front in the rectangles stack and check for hits
    for ( int i = ui_numRects - 1; i >= 0; i-- ) {
        uiRect_t *r = &ui_rects[i];

        if ( UI_CursorInRect( r->position, r->size ) ) {
            if ( BUTTON_RELEASED() ) {
                // there is only one hot widget
                ui_activeWidget = 0;
                ui_hotWidget = r->handle;
            } else if ( ! ui_activeWidget ) {
                ui_activeWidget = r->handle;
            }

            break;
        } 
    }

    if ( BUTTON_RELEASED() ) {
        ui_activeWidget = 0;
    }

    // clear rects stack
    ui_numRects = 0;

    // handle mouse buttons specially
    if ( ui_mouseButton & UIMBS_UP ) {
        ui_mouseButton = UIMBS_NONE;
    }
}

static void UI_PushRect( v2_t position, v2_t size, int handle ) {
    uiRect_t *r;

    if ( ui_numRects == MAX_RECTS ) {
        CON_Printf( "UI_PushRect: out of widgets\n" );
        return;
    }

    r = &ui_rects[ui_numRects];
    r->position = position;
    r->size = size;
    r->handle = handle;
    ui_numRects++;
}

typedef enum {
    BR_IDLE,
    BR_HOT,
    BR_ACTIVE,
    BR_RELEASED,
    BR_NUM_RESULTS,
} buttonResult_t;

buttonResult_t UI_ClickRect_wg( v2_t position, v2_t size, int handle ) {
    UI_PushRect( position, size, handle );

    buttonResult_t res = BR_IDLE;

    if ( ui_activeWidget == handle ) {
        if ( ( ui_mouseButton & UIMBS_UP ) && UI_CursorInRect( position, size ) ) {
            res = BR_RELEASED;
        } else {
            res = BR_ACTIVE;
        }
    } else if ( handle == ui_hotWidget ) {
        res = BR_HOT;
    }

    return res;
}

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 16:45

в следващите постове ще имплементвам по една контрола, от бутон до каквото кажете. дайте заявки.

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 18:26

AppFrame се вика всеки кадър от играта. Хендъла който е последен параметър на IMGUI функциите трябва да е уникален за всяко извикване и различен от 0.

Код: Избери всички

static bool_t UI_Button( v2_t position, 
                         v2_t size, 
                         xhandle_t fontHandle, 
                         int fontRasterSize, 
                         int fontScale,
                         const color_t colors[BR_NUM_RESULTS], 
                         const char *text, 
                         int handle ) 
{ 
    buttonResult_t res = UI_ClickRect_wg( position, size, handle );
    R2D_Colorc( colors[res] );
    R2D_SolidRectv( position, size );
    v2_t center = v2Add( position, v2Scale( size, 0.5 ) );
    RT_Face( fontHandle, fontRasterSize, fontScale ); 
    R2D_Colorc( r_white );
    RT_PrintCenteredf( center.x, center.y, text );
    return res == BR_RELEASED;
}

static void AppFrame( void ) {
    R_ClearColorBuffer();

    static const color_t buttonColors[BR_NUM_RESULTS] = {
        [BR_IDLE]     = { .a = { 0, 0.3, 0.3, 1 } },
        [BR_HOT]      = { .a = { 0, 0.7, 0.7, 1 } },
        [BR_ACTIVE]   = { .a = { 0, 1, 1, 1 } },
        [BR_RELEASED] = { .a = { 0, 1, 1, 1 } },
    };

    v2_t position = v2xy( 200, 200 );
    v2_t size = v2xy( 200, 40 );

    xhandle_t font = rt_pixelBD;
    int fontRasterSize = 8;
    int fontScale = 3;

    UI_Button( position, size, font, fontRasterSize, fontScale, buttonColors, "Click Me", 1 );

    UI_TestHits();
}
Изображение

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 18:41

ето 2 бутона които правят нещо. артефакта върху цифрата е от гиф-а:

Код: Избери всички

    
    static int counter;
    if ( UI_Button( position, size, font, fontRasterSize, fontScale, buttonColors, "Increase", 1 ) ) {
        counter++;
    }
    position.y += size.y * 1.5;
    if ( UI_Button( position, size, font, fontRasterSize, fontScale, buttonColors, "Decrease", 2 ) ) {
        counter--;
    }
    v2_t v = v2Add( position, size );
    RT_Printf( v.x + 10, v.y, "counter: %d", counter );
Изображение

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 18:44

както забелязвате много от нещата се повтарят, и тук идва "принципа на кейси" за компресирането на кода от флейма в другия thread. в следващия пост ще направим подходящите промени за да минимизараме този код.

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 18:53

първо ще се преместим многото параметри в структура, после тази структура ще я сетваме преди извикването на група контроли:

Код: Избери всички

    
    UI_ButtonsContext( rt_pixelBD, 
                       RT_FONT_SZ_PIXEL_BD, 
                       3,
                       colorrgb( 0, 0.3, 0.3 ),
                       colorrgb( 0, 7, 7 ),
                       colorrgb( 0, 1, 1 ) );
    if ( UI_Button( pos, size, "Increase", 1 ) ) {
        counter++;
    }
    position.y += step;
    if ( UI_Button( position, size, "Decrease", 2 ) ) {
        counter--;
    }
Трябва да се погрижим и за неприятните хендъли в края на всяко извикване. За целта ползвам макроси

Код: Избери всички

// zero is reserved handle, thus COUNTER + 1
#define WG_GEN_ID(handle) (((handle & 0xffff)<<16)|(((__COUNTER__+1)&0xff)<<8)|(FILE_UID&0xff))
#define WG_DEFAULT_ID WG_GEN_ID(0xffff)

buttonResult_t UI_ClickRect_wg( v2_t position, v2_t size, int handle );
#define UI_ClickRect( position, size ) UI_ClickRect_wg( position, size, WG_DEFAULT_ID )

bool_t UI_Button_wg( v2_t position, v2_t size, const char *text, int handle );
#define UI_Button( position, size, text ) UI_Button_wg( position, size, text, WG_DEFAULT_ID )
резултата:

Код: Избери всички

    if ( UI_Button( position, size, "Increase" ) ) {
        counter++;
    }
    pos.y += step;
    if ( UI_Button( position, size, "Decrease" ) ) {
        counter--;
    }
В следващия пост: контролки в цикъл

BIGBUGEX
Regular User
Regular User
Мнения: 77
Регистриран: 29 мар 2004 00:42
Местоположение: Nqkyde

Re: basic imgui

Мнение от BIGBUGEX » 31 май 2014 19:23

Тоя подход е интересен но има дребен недостатък. Ако решиш да чертаеш таблица с текстови полета как ще стане? Трябва да отложиш чертането докато получиш всички текстове за колоната. За да знаеш ширината й. И всякакъв подобен подход с лайоути става проблемен ако трябва да се оразмерява динамично.

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 19:26

за да можем безболезнено да викаме контролки в цикъл дефинираме макро което все пак взима параметър хендъл, но може той може да не е уникален извън скопа на цикъла:

Код: Избери всички

#define UI_Button_l( position, size, text, loopHandle ) UI_Button_wg( position, size, text, WG_GEN_ID( loopHandle ) )
резултата:

Код: Избери всички

    for ( int i = 0; i < numButtons; i++ ) {
        position.y += step;
        UI_Button_l( position, size, va( "Button %d", i + 1 ), i );
    }
и тук трябва да се погрижим за аритметиката с позицията по "метода на кейси" с техника по наш вкус. хората ползващи езици различни от C имат много опции включително и за генериране на уникални хендъли за извикванията.
Изображение

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 19:28

BIGBUGEX написа:Тоя подход е интересен но има дребен недостатък. Ако решиш да чертаеш таблица с текстови полета как ще стане? Трябва да отложиш чертането докато получиш всички текстове за колоната. За да знаеш ширината й. И всякакъв подобен подход с лайоути става проблемен ако трябва да се оразмерява динамично.
моля, формулирай си задачата точно и аз ще се опитам да я имплементна в някой от следващите постове, proof of concept един вид

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 19:32

забележете че ВСИЧКИЯ код до тук е около 300 линии C, като изключим функциите които рисуват правоъгълници, пишат текст и т.н. дори да не може те да напишете ексел с тази система, мисля че си струва да я пробвате в игра.
в следвашия пост, по молба на петър димов -- drag

BIGBUGEX
Regular User
Regular User
Мнения: 77
Регистриран: 29 мар 2004 00:42
Местоположение: Nqkyde

Re: basic imgui

Мнение от BIGBUGEX » 31 май 2014 19:48

Картинка струваща 1000 думи:
Изображение

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 31 май 2014 20:17

ето драг контролата:

Код: Избери всички

v2_t UI_Drag_wg( v2_t position, v2_t size, const char *text, int handle ) {
    buttonResult_t res = UI_ClickRect_wg( position, size, handle );
    R2D_Colorc( ui_buttonsContext.colors[res] );
    R2D_SolidRectv( position, size );
    R2D_Colorc( r_white );
    v2_t center = v2Add( position, v2Scale( size, 0.5 ) );
    RT_PrintCenteredf( center.x, center.y, text );
    if ( res == BR_ACTIVE ) {
        return v2Add( position, v2c2( c2Sub( SYS_MousePos(), ui_oldCursorPosition ) ) );
    }
    return position;
}
...
    static v2_t position[3] = { 
        { .a = { 200, 200 } },
        { .a = { 200, 200 } },
        { .a = { 200, 200 } },
    };
    position[0] = UI_Drag_wg( position[0], size, "Drag me 1", 1 );
    position[1] = UI_Drag_wg( position[1], size, "Drag me 2", 2 );
    position[2] = UI_Drag_wg( position[2], size, "Drag me 3", 3 );
...
Изображение

gemicha
Site Admin
Site Admin
Мнения: 2930
Регистриран: 20 ное 2003 22:20
Местоположение: USA

Re: basic imgui

Мнение от gemicha » 01 юни 2014 03:23

ComboBox с autocomplete?

Имаше масив от стрингове. Сложи ги в ComboBox, който има поле, в което можеш да пишеш текст. Ако това, което пишеш съвпада с префикса на някой от стринговете подсказваш как може да завърши писането. В страни от текста за писане има бутон. Ако натиснеш бутона се показва отдолу ListControl със стринговете от които може да избереш един от показаните. ListControl-a показва максимум 5 стринга. Ако списъка има повече показваш scrollbar, ако няма не показваш scrollbar. Ако докато пиша в ComboBox-а ListControl-a е отворен курсора се мести по стринговете, които имат същия префикс с това, което пиша.

Това е доста опростен вариант на модерния ComboBox control, но е добро упражнение според мен. Покажи как изглежда с твоя подход.

pdimov
gosu
gosu
Мнения: 871
Регистриран: 02 дек 2003 01:04

Re: basic imgui

Мнение от pdimov » 01 юни 2014 19:14

Направи им го тоя комбо бокс да може и да се resize-ва и да се откача и да се мести насам-натам, да им е гадно.

edit: А също така да е прозрачен и в него да плуват още по-прозрачни рибки на фон. Да видим xml-ите им могат ли го това. :-)

edit2: После ги питай техния комбо бокс как се държи, ако множеството стрингове се променя на всеки кадър. Понеже отговаря на нещо, което в реално време се сменя. Щот сме игра, а тя си тече, не чака.

Потребителски аватар
stoiko
Power User
Power User
Мнения: 617
Регистриран: 04 дек 2003 15:44
Контакти:

Re: basic imgui

Мнение от stoiko » 01 юни 2014 19:31

pdimov написа:Направи им го тоя комбо бокс да може и да се resize-ва и да се откача и да се мести насам-натам, да им е гадно.

edit: А също така да е прозрачен и в него да плуват още по-прозрачни рибки на фон. Да видим xml-ите им могат ли го това. :-)

edit2: После ги питай техния комбо бокс как се държи, ако множеството стрингове се променя на всеки кадър. Понеже отговаря на нещо, което в реално време се сменя. Щот сме игра, а тя си тече, не чака.
хахахахха... наистина ще ги направя с рибки. аз очаквах хората да искат някакви анимирани контроли за игри, но явно ще се пише ексел...

Stilgar
Power User
Power User
Мнения: 824
Регистриран: 12 яну 2006 22:15
Контакти:

Re: basic imgui

Мнение от Stilgar » 01 юни 2014 20:05

pdimov написа:Направи им го тоя комбо бокс да може и да се resize-ва и да се откача и да се мести насам-натам, да им е гадно.

edit: А също така да е прозрачен и в него да плуват още по-прозрачни рибки на фон. Да видим xml-ите им могат ли го това. :-)

edit2: После ги питай техния комбо бокс как се държи, ако множеството стрингове се променя на всеки кадър. Понеже отговаря на нещо, което в реално време се сменя. Щот сме игра, а тя си тече, не чака.

Това е на WPF selling point-а. Правят го на конференциите в демотата:)

Отговори