Homebrew Citro3D - Loading png into C3D_Tex

PrintHello

Active Member
OP
Newcomer
Joined
Feb 10, 2016
Messages
37
Trophies
0
Age
28
XP
149
Country
Solved! See below for details

I'm trying to write a simple GPU-based text renderer that will load a png file on the SD card into a C3D_Tex and display it on the screen.

I've tried using libpng from the precompiled portlibs, and a png loader PicoPNG but both of them produce a texture like this:

aIJ5zK9.png


Mostly copy paste from the textured cube example
Code:
std::streamsize size, size_2;
    std::vector<unsigned char> buffer, image, bin_image;

    std::ifstream file("sdmc:test_game/test.png");
    if (file.seekg(0, std::ios::end).good()) size = file.tellg();
    if (file.seekg(0, std::ios::beg).good()) size -= file.tellg();

    std::ifstream file_2("sdmc:test_game/test.bin");
    if (file_2.seekg(0, std::ios::end).good()) size_2 = file_2.tellg();
    if (file_2.seekg(0, std::ios::beg).good()) size_2 -= file_2.tellg();

    //read contents of the file into the vector
    if (size > 0)
    {
        buffer.resize((size_t)size);
        file.read((char*)(&buffer[0]), size);
    }
    else buffer.clear();

    if (size_2 > 0)
    {
        bin_image.resize((size_t)size_2);
        file_2.read((char*)(&bin_image[0]), size_2);
    }
    else buffer.clear();

    unsigned long w, h;
    int error = decodePNG(image, w, h, buffer.empty() ? 0 : &buffer[0], (unsigned long)buffer.size());
    if (error != 0) {
        char info[30];
        sprintf(info, "error:%i", error);
        util::debug_print(info);
    }

    /* Upload to shader */
  
    C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);

    // Initialize the render target
    C3D_RenderTarget* target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
    C3D_RenderTargetSetClear(target, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
    C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);

    static const util::vertex vertex_list[] =
    {
        // First face (PZ)
        // First triangle
        { { -0.5f, -0.5f, +0.5f },{ 0.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } },
        { { +0.5f, -0.5f, +0.5f },{ 1.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } },
        { { +0.5f, +0.5f, +0.5f },{ 1.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        // Second triangle
        { { +0.5f, +0.5f, +0.5f },{ 1.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        { { -0.5f, +0.5f, +0.5f },{ 0.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        { { -0.5f, -0.5f, +0.5f },{ 0.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } }
    };

    DVLB_s* vshader_dvlb;
    shaderProgram_s program;
    int uLoc_projection, uLoc_modelView;
    int uLoc_lightVec, uLoc_lightHalfVec, uLoc_lightClr, uLoc_material;
    C3D_Mtx projection;
    C3D_Mtx material =
    {
        {
            { { 0.0f, 0.2f, 0.2f, 0.2f } }, // Ambient
            { { 0.0f, 0.4f, 0.4f, 0.4f } }, // Diffuse
            { { 0.0f, 0.8f, 0.8f, 0.8f } }, // Specular
            { { 1.0f, 0.0f, 0.0f, 0.0f } }, // Emission
        }
    };

    // Load the vertex shader, create a shader program and bind it
    vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size);
    shaderProgramInit(&program);
    shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);
    C3D_BindProgram(&program);

    uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
    uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView");
    uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec");
    uLoc_lightHalfVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightHalfVec");
    uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr");
    uLoc_material = shaderInstanceGetUniformLocation(program.vertexShader, "material");

    // Configure attributes for use with the vertex shader
    C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
    AttrInfo_Init(attrInfo);
    AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
    AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord
    AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal

    // Compute the projection matrix
    Mtx_PerspTilt(&projection, C3D_AngleFromDegrees(80.0f), C3D_AspectRatioTop, 0.01f, 1000.0f, false);

    // Create the VBO (vertex buffer object)
    void* vbo_data = linearAlloc(sizeof(vertex_list));
    memcpy(vbo_data, vertex_list, sizeof(vertex_list));

    // Configure buffers
    C3D_BufInfo* bufInfo = C3D_GetBufInfo();
    BufInfo_Init(bufInfo);
    BufInfo_Add(bufInfo, vbo_data, sizeof(util::vertex), 3, 0x210);

    C3D_Tex png_texture;
    // Load the texture and bind it to the first texture unit
    C3D_TexInit(&png_texture, 128, 128, GPU_TEXCOLOR::GPU_RGBA8);
    C3D_TexUpload(&png_texture, &image[0]);
    C3D_TexSetFilter(&png_texture, GPU_LINEAR, GPU_NEAREST);
    C3D_TexBind(0, &png_texture);

    // Configure the first fragment shading substage to blend the texture color with
    // the vertex color (calculated by the vertex shader using a lighting algorithm)
    // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
    C3D_TexEnv* env = C3D_GetTexEnv(0);
    C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
    C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
    C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);

    while (true) {

        C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
        C3D_FrameDrawOn(target);
      
        C3D_Mtx modelView;
        Mtx_Identity(&modelView);
        Mtx_Translate(&modelView, 0.0, 0.0, -2.0, true);

        // Update the uniforms
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f);

        // Draw the VBO
        C3D_DrawArrays(GPU_TRIANGLES, 0, sizeof(vertex_list) / sizeof(util::vertex));

        C3D_FrameEnd(0);

    }

Both the Tex and the png have the same dimensions (128x128). I've used the png to bin converter tool and loaded the bin as the texture and that still shows up this garbled mess. Strangely the kitten.bin from the examples does load properly so I have no clue what it would be, probably something simple :sleep:.

I've had a look at sfillib and it seems to do what I want but according to the github page it is deprecated.

Any help is appreciated

Cheers
 
Last edited by PrintHello,

bee395

Member
Newcomer
Joined
Jun 26, 2016
Messages
16
Trophies
0
Age
25
XP
94
Country
Netherlands
When I replace the kitten.bin with the logo.bin (from the Cirto3d test project) I get a good looking image. But when when I convert the logo64.png to a bin format, which should be the same, I get the same garbled mess. So I think it's something to do with the file format.
You can maybe fix it with C3D_SafeDisplayTransfer but I'm not really sure.

It's maybe easier to look at the gpusprites example, since they use a pngconverter so you don't have to worry about different bin formats.

https://github.com/devkitPro/3ds-examples/blob/master/graphics/gpu/gpusprites/source/main.c#L182
 
  • Like
Reactions: PrintHello

PrintHello

Active Member
OP
Newcomer
Joined
Feb 10, 2016
Messages
37
Trophies
0
Age
28
XP
149
Country
upload_2017-7-23_21-1-12.png

It's upside down and flipped horizontally but that did it :lol: Thanks!
Here's the code for anyone else who comes across the thread (I made a new array to store the rgba to tiled format texture, and coped the rest from the link)
Code:
std::streamsize size;
    std::vector<unsigned char> buffer, image;

    std::ifstream file("sdmc:test_game/test.png");
    if (file.seekg(0, std::ios::end).good()) size = file.tellg();
    if (file.seekg(0, std::ios::beg).good()) size -= file.tellg();

    //read contents of the file into the vector
    if (size > 0)
    {
        buffer.resize((size_t)size);
        file.read((char*)(&buffer[0]), size);
    }
    else buffer.clear();

    unsigned long w, h;
    int error = decodePNG(image, w, h, buffer.empty() ? 0 : &buffer[0], (unsigned long)buffer.size());
    if (error != 0) {
        char info[30];
        sprintf(info, "error:%i", error);
        util::debug_print(info);
    }

    u8 *gpusrc = (u8*) linearAlloc(w*h * 4);
    u8 *img_fix = (u8*)linearAlloc(w*h * 4);

    // GX_DisplayTransfer needs input buffer in linear RAM
    u8* src = &image[0]; u8 *dst = gpusrc;
    

    // lodepng outputs big endian rgba so we need to convert
    for (int i = 0; i< w * h; i++) {
        int r = *src++;
        int g = *src++;
        int b = *src++;
        int a = *src++;

        *dst++ = a;
        *dst++ = b;
        *dst++ = g;
        *dst++ = r;
    }

    /* Upload to shader */
    
    C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);

    // Initialize the render target
    C3D_RenderTarget* target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
    C3D_RenderTargetSetClear(target, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
    C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);

    static const util::vertex vertex_list[] =
    {
        // First face (PZ)
        // First triangle
        { { -0.5f, -0.5f, +0.5f },{ 0.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } },
        { { +0.5f, -0.5f, +0.5f },{ 1.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } },
        { { +0.5f, +0.5f, +0.5f },{ 1.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        // Second triangle
        { { +0.5f, +0.5f, +0.5f },{ 1.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        { { -0.5f, +0.5f, +0.5f },{ 0.0f, 1.0f },{ 0.0f, 0.0f, +1.0f } },
        { { -0.5f, -0.5f, +0.5f },{ 0.0f, 0.0f },{ 0.0f, 0.0f, +1.0f } }
    };

    DVLB_s* vshader_dvlb;
    shaderProgram_s program;
    int uLoc_projection, uLoc_modelView;
    int uLoc_lightVec, uLoc_lightHalfVec, uLoc_lightClr, uLoc_material;
    C3D_Mtx projection;
    C3D_Mtx material =
    {
        {
            { { 0.0f, 0.2f, 0.2f, 0.2f } }, // Ambient
            { { 0.0f, 0.4f, 0.4f, 0.4f } }, // Diffuse
            { { 0.0f, 0.8f, 0.8f, 0.8f } }, // Specular
            { { 1.0f, 0.0f, 0.0f, 0.0f } }, // Emission
        }
    };

    // Load the vertex shader, create a shader program and bind it
    vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size);
    shaderProgramInit(&program);
    shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);
    C3D_BindProgram(&program);

    uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
    uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView");
    uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec");
    uLoc_lightHalfVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightHalfVec");
    uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr");
    uLoc_material = shaderInstanceGetUniformLocation(program.vertexShader, "material");

    // Configure attributes for use with the vertex shader
    C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
    AttrInfo_Init(attrInfo);
    AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
    AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord
    AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal

    // Compute the projection matrix
    Mtx_PerspTilt(&projection, C3D_AngleFromDegrees(80.0f), C3D_AspectRatioTop, 0.01f, 1000.0f, false);

    // Create the VBO (vertex buffer object)
    void* vbo_data = linearAlloc(sizeof(vertex_list));
    memcpy(vbo_data, vertex_list, sizeof(vertex_list));

    // Configure buffers
    C3D_BufInfo* bufInfo = C3D_GetBufInfo();
    BufInfo_Init(bufInfo);
    BufInfo_Add(bufInfo, vbo_data, sizeof(util::vertex), 3, 0x210);

    C3D_Tex png_texture;
    C3D_TexInit(&png_texture, w, h, GPU_TEXCOLOR::GPU_RGBA8);

    // Load the texture and bind it to the first texture unit
    GSPGPU_FlushDataCache(gpusrc, w*h * 4);
    C3D_SafeDisplayTransfer((u32*)gpusrc, GX_BUFFER_DIM(w, h), (u32*)img_fix, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS);
    gspWaitForPPF();

    
    C3D_TexUpload(&png_texture, &img_fix[0]);
    C3D_TexSetFilter(&png_texture, GPU_LINEAR, GPU_NEAREST);
    C3D_TexBind(0, &png_texture);

    // Configure the first fragment shading substage to blend the texture color with
    // the vertex color (calculated by the vertex shader using a lighting algorithm)
    // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
    C3D_TexEnv* env = C3D_GetTexEnv(0);
    C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
    C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
    C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);

    while (true) {

        C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
        C3D_FrameDrawOn(target);
        
        C3D_Mtx modelView;
        Mtx_Identity(&modelView);
        Mtx_Translate(&modelView, 0.0, 0.0, -2.0, true);

        // Update the uniforms
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_material, &material);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec, 0.0f, 0.0f, -1.0f, 0.0f);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightHalfVec, 0.0f, 0.0f, -1.0f, 0.0f);
        C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr, 1.0f, 1.0f, 1.0f, 1.0f);

        // Draw the VBO
        C3D_DrawArrays(GPU_TRIANGLES, 0, sizeof(vertex_list) / sizeof(util::vertex));

        C3D_FrameEnd(0);

    }
 
  • Like
Reactions: jockep and bee395

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    K3Nv2 @ K3Nv2: Least they got head in the end