Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapting new code to the old functions that didn't use "context" (ImGui integration) #97

Open
Abalieno opened this issue Aug 8, 2021 · 17 comments
Assignees
Labels

Comments

@Abalieno
Copy link

Abalieno commented Aug 8, 2021

I found someone on the reddit roguelike that was able to integrate ImGui with libtcod, that was one of the things I was really hoping someone would solve, since I have very low technical skills and couldn't realistically figure it out on my own.

Now the problem is that all his code is made for the new version of libtcod, and it all relies on "context" passed as argument to a bunch of functions.

I have no "context" in my code, and due to my lack of skill, custom fonts and everything, I don't want (and cannot) rewrite the whole thing from scratch.

I'm trying to figure out some solution.

What we're trying to do is separate the accumulate_context and renderer_present in order to plug the ImGui own stuff so that it can be drawn on top before the screen is updated.

Problem is, again, that all my code is set up with TCODConsole::flush(); whereas his code is all about TCOD_context_present that is always dependent on a "context" passed as an argument, that I instead don't have in my code.

I've tried on my own despite the lack of skill to look up the code and see if I could manually split that flush command. But I got completely lost. The reference seems to start in console_.cpp and it seems to redirect to a TCOD_console_flush which seems to be defined inside console_etc.c, here AGAIN it is redirected to a TCOD_console_flush_ex. But inside this function another one is called TCOD_context_present... that once again uses a "context" of an internal type, defined mysteriously.

So even the standard, old "flush" function seems to be dependent on a "context", but this context isn't the one explicitly built in the user program with the standard initializations, but seems to be internally defined in some obscure way.

I'm completely stuck here, and probably all I wrote is already all wrong guesswork.

The code I'm trying to use is this:

bool renderer_accumulate_context(TCOD_Context* context, TCOD_Console* console) {
    struct TCOD_RendererGL2* renderer = context->contextdata_;
    int window_width;
    int window_height;
    SDL_GL_GetDrawableSize(TCOD_context_get_sdl_window(context), &window_width,
                           &window_height);
    glViewport(0, 0, window_width, window_height);
    glClearColor((float)DEFAULT_VIEWPORT.clear_color.r / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.g / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.b / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.a / 255.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    TCOD_Error err =
        context->c_accumulate_(context, console, &DEFAULT_VIEWPORT);
    if (err != TCOD_E_OK) {
        fprintf(stderr, "Accumulate error: %s\n", TCOD_get_error());
        return false;
    }
    return true;
}

void renderer_present(struct TCOD_Context* context) {
    SDL_GL_SwapWindow(TCOD_context_get_sdl_window(context));
}

In between these two functions there's ImGui render code, but that part works. I need some way to REPLACE my TCODConsole::flush(); with those two functions I've pasted here. Problem is: I have no goddamned CONTEXT anywhere.

I need to pass this thing a context, but there's no context in the old library code. Is there a way to pass some sort of fast conversion that grabs my current stuff and creates a context on the fly I can use? How the hell can the flush function USE A CONTEXT if a context is nowhere to be found in the old code?

Sorry for the frustration, but I spent already many, MANY hours trying to figure this out, and the new library standards redoing all the core functions wasn't something that helped...

@HexDecimal HexDecimal self-assigned this Aug 8, 2021
@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

Here's also the barebone code I'm using for ImGui rendering.
imag

This works, but it needs to be plugged into the flush function so that the UI is rendered on top of everything else, before calling SDL_GL_SwapWindow(TCOD_context_get_sdl_window(context));.

This one function needs to come after the debug_ui_render, and that's all...

@HexDecimal
Copy link
Collaborator

HexDecimal commented Aug 8, 2021

The reason you're having such a hard time backporting this to the old API is because it's impossible with the old API. While the new contexts have an unofficial way to separate the rendering process from a screen update which is required for ImGUI the older rendering system had no method of doing this at all.

The good news is that even if you're not officially using the new API, the new contexts are still being used under the hood. You should be able to access the internal context used by the old API via TCOD_ctx.engine after importing <libtcod/libtcod_int.h>. This would be a short term solution since the old API is still deprecated and any code using <libtcod/libtcod_int.h> would not be stable.

@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

An example would be helpful because while it compiles, the linker gives an error:

fc

This is where we began discussing the whole thing, if more information might be useful:
https://www.reddit.com/r/roguelikedev/comments/osgnrg/roguelikedev_does_the_complete_roguelike_tutorial/h72hz13/

@HexDecimal
Copy link
Collaborator

If I had to guess. GCC is enforcing visibility rules for TCOD_ctx and hiding it from external programs.

I can add a public function to return the context this way. How are you compiling libtcod right now?

@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

This time I used vcpkg under msys2/mingw64.

Using these instructions:
https://github.com/microsoft/vcpkg/blob/master/docs/users/mingw.md

And then:
vcpkg install libtcod sdl2 glad imgui[opengl3-glad-binding,sdl2-binding]

@HexDecimal
Copy link
Collaborator

int TCOD_sys_accumulate_console(const TCOD_Console* console) also has the context->c_accumulate_ behavior for the internal context.

@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

Well, I need examples. I cannot figure this stuff on my own without a lot more detail. I said many times I'm not an expert.

@HexDecimal
Copy link
Collaborator

Sorry for that. I think you can replace the context specific functions with older API functions like this:

bool renderer_accumulate_context(TCOD_Console* console) {
    int window_width;
    int window_height;
    SDL_GL_GetDrawableSize(TCOD_sys_get_sdl_window(), &window_width,
                           &window_height);
    glViewport(0, 0, window_width, window_height);
    glClearColor((float)DEFAULT_VIEWPORT.clear_color.r / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.g / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.b / 255.0f,
                 (float)DEFAULT_VIEWPORT.clear_color.a / 255.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    int err = TCOD_sys_accumulate_console(console);
    if (err != TCOD_E_OK) {
        fprintf(stderr, "Accumulate error: %s\n", TCOD_get_error());
        return false;
    }
    return true;
}

void renderer_present() {
    SDL_GL_SwapWindow(TCOD_sys_get_sdl_window());
}

@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

Yes, that makes sense... But I still have problems with the syntax because with the old code I don't create a console manually.

All I use is TCODConsole::setCustomFont and TCODConsole::initRoot All the consoles I create are of the subconsole type, to use then with blit. And of course there's a TCODConsole::root is there a way to pass this one as an argument for the function?

@HexDecimal
Copy link
Collaborator

Contexts don't like to given the root console. You can make a console with the same size as the root console, blit to it then pass the pointer to the new console into the function. It should be something like this:

  TCODConsole clone(TCODConsole::root->getWidth(), TCODConsole::root->getHeight());
  TCODConsole::blit(TCODConsole::root, 0, 0, 0, 0, &clone, 0, 0);
  renderer_accumulate_context(clone.get_data());

If you want something like a root console with contexts then it's recommended to make your own global console variable.

@HexDecimal
Copy link
Collaborator

That said. TCOD_sys_accumulate_console does accept NULL as the root console.

@Abalieno
Copy link
Author

Abalieno commented Aug 8, 2021

So yes, I was getting a weird problem because once again there are different APIs, it seems. I set up TCODConsole, whereas the function uses TCOD_Console, with the underscore. These look similar but are different...

Your example works, but I had to include console_init.h, because it looks for those other functions.

Now I have more problems because of course this function doesn't use the timings that are built in with "flush", so the program goes as fast as it can and uses up all CPU it can get... But I guess I'll try to figure out the rest. I really need to break down all of the flush command to preserve all it did.

(is there anything else that the "flush" does beside timings? Event related or something? Maybe more things depend on that to work, and will stop working if I use that custom function?)

@Abalieno Abalieno changed the title Trying to adapt some new code example, to the old functions that didn't use "context" Adapting new code to the old functions that didn't use "context" (ImGui integration) Aug 8, 2021
@HexDecimal
Copy link
Collaborator

HexDecimal commented Aug 8, 2021

TCOD_Console will eventually replace TCODConsole completely. It will be easier to use TCOD_Console in the next release of libtcod. The get_data() method of TCODConsole always returns TCOD_Console*.

There's an unreleased tcod::Timer class being developed which will handle timing without relying on the old API. Until then you'll have to use SDL_Delay and SDL_GetTicks yourself to handle timing.

The simplest way to conserve resource usage would be to enable VSync. For the old API you can force it on by setting the environment variable TCOD_VSYNC=1 before initializing the root console or running the program. I'm not sure how to set that variable in a cross-platform way from C++.

@HexDecimal
Copy link
Collaborator

HexDecimal commented Aug 8, 2021

Since you're using MinGW you probably only need to know about setenv.

setenv("TCOD_VSYNC", "1", 0);  // Before calling TCODConsole::initRoot.

@Abalieno
Copy link
Author

Abalieno commented Aug 9, 2021

My hope was to look up again the code of "flush" and take from there the part about timings? Wouldn't it work?

The problem with vsync is that the faster the refresh, the faster the "movement" in the roguelike. I prefer keep a fixed screen update to have things consistent.

How deep goes the rabbit hole if I tried to update the code? Right now I use:
TCODConsole::setCustomFont("terminal.png",TCOD_FONT_LAYOUT_ASCII_INCOL | TCOD_FONT_TYPE_GREYSCALE,16,256);

It's a big png, but the big deal is that I use multiple "fonts" of varied grids, created by assembiling two or four basic cells. So libtcod encodes a standard 8x8 font, but then I use TCODConsole::mapAsciiCodeToFont to map manually cells and then "assemble" the other fonts. This is for example for 8x16 font:

loc->putChar(where_x + linestart, where_y + line_n, 1001+step, TCOD_BKGND_SET);
loc->putChar(where_x + linestart, where_y+1 + line_n, 1001+(step+1), TCOD_BKGND_SET);

Is there support to all of this under the new API? Can I create a TCOD_Tileset* tileset that retains the same features or functions? Or, is there some function that converts TCODConsole::setCustomFont to TCOD_Tileset, as it seems to exist to convert TCODConsoles to TCOD_Console?

The fact that the documentation is sparse and lacking example isn't helping, but the problem is I'd need examples to convert the old code across all these changes, and I don't think this sort of documentation/tutorial exists. I wonder if I should just stay on the old version of the library since I'm just adding more and more overhead to all of this.

@HexDecimal
Copy link
Collaborator

My hope was to look up again the code of "flush" and take from there the part about timings? Wouldn't it work?

The timing code for the old API is here:

/**
* Keep track of time and wait if the frame-rate is faster than the set FPS.
*/
void sync_time_(void) {
static uint32_t old_time = 0;
static uint32_t new_time = 0;
static uint32_t elapsed = 0;
int32_t frame_time;
int32_t time_to_wait;
old_time = new_time;
new_time = TCOD_sys_elapsed_milli();
/* If TCOD has been terminated and restarted. */
if (old_time > new_time) old_time = elapsed = 0;
if (new_time / 1000 != elapsed) {
/* update fps every second */
fps = cur_fps;
cur_fps = 0;
elapsed = new_time / 1000;
}
/* if too fast, wait */
frame_time = (new_time - old_time);
last_frame_length = frame_time * 0.001f;
cur_fps++;
time_to_wait = min_frame_length - frame_time;
if (old_time > 0 && time_to_wait > 0) {
TCOD_sys_sleep_milli(time_to_wait);
new_time = TCOD_sys_elapsed_milli();
frame_time = (new_time - old_time);
}
last_frame_length = frame_time * 0.001f;
}

Where TCOD_sys_sleep_milli is just SDL_Delay and TCOD_sys_elapsed_milli is just SDL_GetTicks.
You can find min_frame_length in TCOD_sys_set_fps:
void TCOD_sys_set_fps(int val) {
if (val == 0)
min_frame_length = 0;
else
min_frame_length = 1000 / val;
}

The problem with vsync is that the faster the refresh, the faster the "movement" in the roguelike.

Not if your code uses delta time, then VSync just makes sure you don't render a frame which won't be seen by the player.

How deep goes the rabbit hole if I tried to update the code?

Pretty deep I assume. I've just now started to really get into refactoring the C++ API so functions are being developed right now to fit your needs rather than them already existing for me to explain them. It makes things harder if you're not using libtcod from the develop branch like in this template. If you were you could mess around with the new tcod::Timer class instead of having to make your own timing system.

1001+step

Please use the Private Use Areas for custom glyphs.

TCOD_FONT_LAYOUT_ASCII_INCOL

Column-major tilesets were deprecated and the new API doesn't support them. This won't be a problem if you've already been manually mapping them, and it's easy to remap all the n<0x7F codepoints. It'd be better to switch to a row-major tileset or use a script to rearrange them to be row-major.

Is there support to all of this under the new API? Can I create a TCOD_Tileset* tileset that retains the same features or functions?

Whatever you're doing in C++ can be recreated with the C API, which will be more forward compatible with how new API is being developed.

While you can reassign the codepoints afterwards. The idea of TCOD_tileset_load or tcod::load_tilesheet is that you provide a large array with all of the character mappings when you first load the tileset.

The fact that the documentation is sparse and lacking example isn't helping, but the problem is I'd need examples to convert the old code across all these changes, and I don't think this sort of documentation/tutorial exists.

One doesn't exist, and making inline code examples is something I've just figured out how to do with the new documentation generator. Right now I'm trying to update the samples with the new API. I should be able to make examples for you if you have anything specific you want to know,

@HexDecimal
Copy link
Collaborator

(is there anything else that the "flush" does beside timings? Event related or something? Maybe more things depend on that to work, and will stop working if I use that custom function?)

I missed this edit until now. It's not really just "flush" that does a lot special stuff, but the giant global singleton hidden behind "initializing the root console". The new context system removes the singleton and disables anything depending on it. The functions which would break were cataloged in the Python-tcod docs here in the note, which should give you an idea of which C/C++ functions will also break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants