Skip to content

Instantly share code, notes, and snippets.

@misterhat
Last active December 4, 2022 19:47
Show Gist options
  • Save misterhat/5045d79731c7229ca94da919bd12ed1e to your computer and use it in GitHub Desktop.
Save misterhat/5045d79731c7229ca94da919bd12ed1e to your computer and use it in GitHub Desktop.
/devkitpro/examples/3ds/graphics/gpu/textured_cube/ modified to dump framebuffer to ppm file
const drawIndex = 0;
const yOffsets = [1, 4, 5, 16, 17, 20, 21, 64];
const xOffsets = [2, 8, 10, 32, 34, 40, 42, 1920];
const expandedYOffsets = [0];
let test = 0;
for (let i = 0; i < 30; i++) {
let last = 0;
for (let j = 0; j < 8; j++) {
last = test + yOffsets[j];
expandedYOffsets.unshift(last);
}
test = last;
}
const expandedXOffsets = [0];
test = 0;
for (let i = 0; i < 40; i++) {
let last = 0;
for (let j = 0; j < 8; j++) {
last = test + xOffsets[j];
expandedXOffsets.push(last);
}
test = last;
}
expandedYOffsets.shift();
expandedXOffsets.pop();
const yOffset = Math.floor(drawIndex / 320);
const xOffset = drawIndex - (yOffset * 320);
const framebufferIndex = expandedYOffsets[yOffset] + expandedXOffsets[xOffset];
console.log(framebufferIndex);
console.log(JSON.stringify(expandedXOffsets),
JSON.stringify(expandedYOffsets));
#include <3ds.h>
#include <citro3d.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <tex3ds.h>
#include "vshader_shbin.h"
#include "kitten_t3x.h"
#define CLEAR_COLOR 0x68B0D8FF
#define DISPLAY_TRANSFER_FLAGS \
(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
int framebufferOffsetsX[] = {
0, 2, 8, 10, 32, 34, 40, 42, 1920, 1922, 1928,
1930, 1952, 1954, 1960, 1962, 3840, 3842, 3848, 3850, 3872, 3874,
3880, 3882, 5760, 5762, 5768, 5770, 5792, 5794, 5800, 5802, 7680,
7682, 7688, 7690, 7712, 7714, 7720, 7722, 9600, 9602, 9608, 9610,
9632, 9634, 9640, 9642, 11520, 11522, 11528, 11530, 11552, 11554, 11560,
11562, 13440, 13442, 13448, 13450, 13472, 13474, 13480, 13482, 15360, 15362,
15368, 15370, 15392, 15394, 15400, 15402, 17280, 17282, 17288, 17290, 17312,
17314, 17320, 17322, 19200, 19202, 19208, 19210, 19232, 19234, 19240, 19242,
21120, 21122, 21128, 21130, 21152, 21154, 21160, 21162, 23040, 23042, 23048,
23050, 23072, 23074, 23080, 23082, 24960, 24962, 24968, 24970, 24992, 24994,
25000, 25002, 26880, 26882, 26888, 26890, 26912, 26914, 26920, 26922, 28800,
28802, 28808, 28810, 28832, 28834, 28840, 28842, 30720, 30722, 30728, 30730,
30752, 30754, 30760, 30762, 32640, 32642, 32648, 32650, 32672, 32674, 32680,
32682, 34560, 34562, 34568, 34570, 34592, 34594, 34600, 34602, 36480, 36482,
36488, 36490, 36512, 36514, 36520, 36522, 38400, 38402, 38408, 38410, 38432,
38434, 38440, 38442, 40320, 40322, 40328, 40330, 40352, 40354, 40360, 40362,
42240, 42242, 42248, 42250, 42272, 42274, 42280, 42282, 44160, 44162, 44168,
44170, 44192, 44194, 44200, 44202, 46080, 46082, 46088, 46090, 46112, 46114,
46120, 46122, 48000, 48002, 48008, 48010, 48032, 48034, 48040, 48042, 49920,
49922, 49928, 49930, 49952, 49954, 49960, 49962, 51840, 51842, 51848, 51850,
51872, 51874, 51880, 51882, 53760, 53762, 53768, 53770, 53792, 53794, 53800,
53802, 55680, 55682, 55688, 55690, 55712, 55714, 55720, 55722, 57600, 57602,
57608, 57610, 57632, 57634, 57640, 57642, 59520, 59522, 59528, 59530, 59552,
59554, 59560, 59562, 61440, 61442, 61448, 61450, 61472, 61474, 61480, 61482,
63360, 63362, 63368, 63370, 63392, 63394, 63400, 63402, 65280, 65282, 65288,
65290, 65312, 65314, 65320, 65322, 67200, 67202, 67208, 67210, 67232, 67234,
67240, 67242, 69120, 69122, 69128, 69130, 69152, 69154, 69160, 69162, 71040,
71042, 71048, 71050, 71072, 71074, 71080, 71082, 72960, 72962, 72968, 72970,
72992, 72994, 73000, 73002, 74880, 74882, 74888, 74890, 74912, 74914, 74920,
74922};
int framebufferOffsetsY[] = {
1877, 1876, 1873, 1872, 1861, 1860, 1857, 1856, 1813, 1812, 1809, 1808,
1797, 1796, 1793, 1792, 1749, 1748, 1745, 1744, 1733, 1732, 1729, 1728,
1685, 1684, 1681, 1680, 1669, 1668, 1665, 1664, 1621, 1620, 1617, 1616,
1605, 1604, 1601, 1600, 1557, 1556, 1553, 1552, 1541, 1540, 1537, 1536,
1493, 1492, 1489, 1488, 1477, 1476, 1473, 1472, 1429, 1428, 1425, 1424,
1413, 1412, 1409, 1408, 1365, 1364, 1361, 1360, 1349, 1348, 1345, 1344,
1301, 1300, 1297, 1296, 1285, 1284, 1281, 1280, 1237, 1236, 1233, 1232,
1221, 1220, 1217, 1216, 1173, 1172, 1169, 1168, 1157, 1156, 1153, 1152,
1109, 1108, 1105, 1104, 1093, 1092, 1089, 1088, 1045, 1044, 1041, 1040,
1029, 1028, 1025, 1024, 981, 980, 977, 976, 965, 964, 961, 960,
917, 916, 913, 912, 901, 900, 897, 896, 853, 852, 849, 848,
837, 836, 833, 832, 789, 788, 785, 784, 773, 772, 769, 768,
725, 724, 721, 720, 709, 708, 705, 704, 661, 660, 657, 656,
645, 644, 641, 640, 597, 596, 593, 592, 581, 580, 577, 576,
533, 532, 529, 528, 517, 516, 513, 512, 469, 468, 465, 464,
453, 452, 449, 448, 405, 404, 401, 400, 389, 388, 385, 384,
341, 340, 337, 336, 325, 324, 321, 320, 277, 276, 273, 272,
261, 260, 257, 256, 213, 212, 209, 208, 197, 196, 193, 192,
149, 148, 145, 144, 133, 132, 129, 128, 85, 84, 81, 80,
69, 68, 65, 64, 21, 20, 17, 16, 5, 4, 1, 0};
typedef struct { float position[3]; float texcoord[2]; float normal[3]; } vertex;
static const 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} },
// Second face (MZ)
// 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} },
// Third face (PX)
// First triangle
{ {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
{ {+0.5f, +0.5f, -0.5f}, {1.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
// Second triangle
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
{ {+0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
{ {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
// Fourth face (MX)
// First triangle
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
{ {-0.5f, -0.5f, +0.5f}, {1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
// Second triangle
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
// Fifth face (PY)
// First triangle
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
// Second triangle
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
{ {+0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
// Sixth face (MY)
// First triangle
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {+0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
// Second triangle
{ {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
{ {-0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
};
#define vertex_list_count (sizeof(vertex_list)/sizeof(vertex_list[0]))
static DVLB_s* vshader_dvlb;
static shaderProgram_s program;
static int uLoc_projection, uLoc_modelView;
static int uLoc_lightVec, uLoc_lightHalfVec, uLoc_lightClr, uLoc_material;
static C3D_Mtx projection;
static 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
}
};
static void* vbo_data;
static C3D_Tex kitten_tex;
static float angleX = 0.0, angleY = 0.0;
// Helper function for loading a texture from memory
static bool loadTextureFromMem(C3D_Tex* tex, C3D_TexCube* cube, const void* data, size_t size)
{
Tex3DS_Texture t3x = Tex3DS_TextureImport(data, size, tex, cube, false);
if (!t3x)
return false;
// Delete the t3x object since we don't need it
Tex3DS_TextureFree(t3x);
return true;
}
static void sceneInit(void)
{
// 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);
// Get the location of the uniforms
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_AspectRatioBot, 0.01f, 1000.0f, false);
// Create the VBO (vertex buffer object)
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(vertex), 3, 0x210);
// Load the texture and bind it to the first texture unit
if (!loadTextureFromMem(&kitten_tex, NULL, kitten_t3x, kitten_t3x_size))
svcBreak(USERBREAK_PANIC);
C3D_TexSetFilter(&kitten_tex, GPU_LINEAR, GPU_NEAREST);
C3D_TexBind(0, &kitten_tex);
// 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_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
}
static void sceneRender(void)
{
// Calculate the modelView matrix
C3D_Mtx modelView;
Mtx_Identity(&modelView);
Mtx_Translate(&modelView, 0.0, 0.0, -2.0 + 0.5*sinf(angleX), true);
Mtx_RotateX(&modelView, angleX, true);
Mtx_RotateY(&modelView, angleY, true);
// Rotate the cube each frame
angleX += M_PI / 180;
angleY += M_PI / 360;
// 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, vertex_list_count);
}
static void sceneExit(void)
{
// Free the texture
C3D_TexDelete(&kitten_tex);
// Free the VBO
linearFree(vbo_data);
// Free the shader program
shaderProgramFree(&program);
DVLB_Free(vshader_dvlb);
}
/* translate (y * width) + index into internal framebuffer index */
int translateFramebufferIndex(int index) {
int y_offset = floor(index / 320);
int x_offset = index - (y_offset * 320);
return framebufferOffsetsX[x_offset] + framebufferOffsetsY[y_offset];
}
void dumpFramebuffer(C3D_RenderTarget *target) {
uint8_t *colourBuf = (uint8_t*)(target->frameBuf.colorBuf);
FILE *ppm = fopen("./framebuffer.ppm", "w");
fprintf(ppm, "P3\n320 240\n255\n");
int index = 0;
for (int x = 0; x < 320; x++) {
for (int y = 0; y < 240; y++) {
int fb_index = translateFramebufferIndex(index) * 4;
int a = colourBuf[fb_index++];
int b = colourBuf[fb_index++];
int g = colourBuf[fb_index++];
int r = colourBuf[fb_index++];
fprintf(ppm, "%d %d %d\n", r, g, b);
index++;
}
}
fclose(ppm);
}
int main()
{
// Initialize graphics
gfxInitDefault();
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
// Initialize the render target
C3D_RenderTarget* target = C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
C3D_RenderTargetSetOutput(target, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
// Initialize the scene
sceneInit();
// Main loop
while (aptMainLoop())
{
hidScanInput();
// Respond to user input
u32 kDown = hidKeysDown();
if (kDown & KEY_SELECT) {
dumpFramebuffer(target);
}
if (kDown & KEY_START)
break; // break in order to return to hbmenu
// Render the scene
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
C3D_RenderTargetClear(target, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
C3D_FrameDrawOn(target);
sceneRender();
C3D_FrameEnd(0);
}
// Deinitialize the scene
sceneExit();
// Deinitialize graphics
C3D_Fini();
gfxExit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment