Citro2D - How do I make the viewport move around a tiled background?

Discussion in '3DS - Homebrew Development and Emulators' started by Archerite, Sep 18, 2018.

  1. Archerite
    OP

    Archerite Advanced Member

    Newcomer
    3
    Sep 16, 2018
    Netherlands
    I have taken the gpusprites example to start from and I succeed in replacing the sprites and move them with the buttons and things like that. I am now at the stage of implementing a tiled background and moving the player sprite around the screen which works fine, but when the sprites move out of the screen they disappear instantly! I figured out that changing the "center" moves the point at which they disappear but it still happens. Looking around in the citro2d and citro3d documentation and sources on github did not give me much info on why this happens either, but I did find the viewport function and while that moves the 400x240 screen sized portion out of the visible area on screen it seems that nothing is being rendered outside of it.

    Since I am new here on the forum I think I should explain what I have been doing the last few months....

    Years ago I wanted to recreate a 2D platform game that used the game engine from Jazz Jackrabbit 2 but I never really understood how the graphics and levels were loaded from the game files. A few months ago I finally found this information and I started prototyping the whole thing in Python and used Pygame to render the graphics on screen. This prototype is in an advanced enough state that it can read the levels, tiles, sprites and even the menu graphics. Originally I wanted to write the game for the GameCube and/or Wii since those where the systems I was moding for homebrew...but the thought of needing to use a 3D library for a 2D game scared me a bit. I did write a working demo for the Sega MegaDrive a couple of years ago so using Tiles, Tilemaps and sprites was familiar to me. While the prototype in Python evolved further I started modifing the demos for the NDS and got the menu system of my game working. But the low resolution annoyed me and the tiles and sprites looked out of place so I started looking at the 3DS...

    Then the same issue came up that I had on the GameCube and Wii: No 2D hardware graphics on the GPU meant no easy tiles and tilemaps with sprites! The most logical thing for me was to look at all the SNES and MegaDrive emulators for the Wii and 3DS but it still did not make much sense to me. As said above I started from the gpusprites example and played around with changing it to load my own graphics and moving the sprites with the buttons. I also started porting my python code into C so I can eventually load the game graphics directly on 3DS. For now I just dump to PNG and write the t3s file to convert it all into textures and load them into the modified demo.

    I hope after that long story someone can help me figure out the issue with the viewport, or whatever the problem might be.

    Thanks
     
  2. StuntHacks

    StuntHacks Member

    Newcomer
    2
    Jan 19, 2017
    Austria
    Vienna
    According to this issue, viewport-transformations are planned but not yet implemented (in Citro2D).
     
  3. Archerite
    OP

    Archerite Advanced Member

    Newcomer
    3
    Sep 16, 2018
    Netherlands
    Thank you for pointing out the issue on github again. I did read it a few days ago but back then I did not even know what a viewport matrix was. I still don't really understand the math behind it but the concept is getting a bit more clear, i think.

    The get an idea of how thing should work on the PC with opengl I have tried a few demo scripts in python and used the bare minimum in a script of my own. From those and a few other tutorials and examples I figured out how to load a texture from a PNG and calculate the correct tile position to map to a single 32x32 pixel quad. I know it's not real pixels but vertexes scaled to make it look that way. I played with the depth values and Perspective vs Orthogonal projection. Not sure what each function is doing exactly to make it work yet, but for simple testing and getting the numbers to scale the textures that's fine.

    I did notice in my experimental python script the "clipping" or "culling" or whatever the correct term is does not happen. Each quad has smooth scrolling in all directions and stay on screen until the last pixel moves out of view. I do not know if it still get's rendered outside of the viewport and takes a bit of time but the complete tileset of 1024 tiles moves a little slower than when there are just say 6 or something. Ofcourse running this in python takes a performance hit but from what I understand using vertex buffer object and other tricks are more efficient to speed things up. Also rendering groups of tiles into a larger sprite is something I see recommended a lot

    From all I have learned from my little research so far I suspect the Vertex Shader or the projection matrix might be the cause of the issue I have. Even though shaders are a kind of black magic to me right now I do think I get the basic difference between a vertex(position) and fragment(color) shader. I saw a nice trick to do a complete tilemap rendering with only a few textures and shaders somewhere, and when I think of how I would do such a trick in normal code on a CPU I perfectly understand how it is done. But on a shader I would not even know where to begin doing something like that.

    Sorry for the long post ;)
     
  4. Archerite
    OP

    Archerite Advanced Member

    Newcomer
    3
    Sep 16, 2018
    Netherlands
    I have found the source of the issue and a workaround that will get me what I want for now...

    The sprites not leaving on the left and top edge is because the quad calculation function "C2Di_CalcQuad" doing something to the x and y position. The standard C function fabs() is used to make any negative number positive for whatever reason (i suspect a hardware limitation of the PICA200 GPU) see here: (new members can not post links it seems but it's on line 246 of base.c in the citro2d sources.)
    Code:
      const float h = fabs(params->pos.h);
      const float w = fabs(params->pos.w);
    
    Playing with opengl in python I found that glTranslatef() does what I want for my tilemap to scroll. So searching through both Cirtro2D and Citro3D sources I found the Mtx_Translate() function. Hoping this would be the function to use I traced the structures of Citro2D that contains the projection matrix. In short I copied the "internal.h" header into my project so I could use C2Di_GetContext() and have access to the "C2D->projMtx" pointer. I do need to figure out the correct float value to use for pixel perfect movement.....but this is doing exactly what I need!

    I am sure this is not the best way to do it in the long run, so please correct me if there is another way to do this. When I got the numbers right and there is interest in it I will post the critical parts here. Or maybe add it to the issue in Citro2D if that is the correct place.
     
    Last edited by Archerite, Sep 20, 2018
  5. Archerite
    OP

    Archerite Advanced Member

    Newcomer
    3
    Sep 16, 2018
    Netherlands
    In case someone has a similar need as I did here is an incomplete example of what I added to my code:

    Code:
    #include <c2d_internal.h>  //copy of the internal.h header file of citro2d
    ....
    int main()
    {
       //normal init stuff here
       ...
       C2Di_Context* C2D = C2Di_GetContext();
       ...
       //after C2D_SceneBegin
       Mtx_Translate(&C2D->projMtx,offset_in_pixels_x/120,offset_in_pixels_y/200,0.0,false);
    
       //Draw your sprites and graphics into the pixel address in that region and it will show up.
       //NOTE: I do not know if anything outside that region is still rendered by the GPU.
       ...
    }
    
    Anyone is free to use the code above at your own risk, I am just sharing what I found worked for me.
     
    MrHuu likes this.
Loading...