--Chapter 2: Jump Deeper--

NOTE: For this chapter and beyond, you need to know the PICA200 Assembly language.

Its guide comes with this one.

One thing about the 3D space used with the PICA200 is that every shape is made out of triangles, which is why I decided to make this chapter have a triangle as its main focus.

Let’s take the code we made in the last chapter and edit it. We will work outside of the main function until the end of the file. Here’s the whole initial function plus the variables needed:


typedef struct { float x, y, z; } vertex;

static const vertex vertex_list[] =

{

{ 200.0f, 200.0f, 0.5f },

{ 100.0f, 40.0f, 0.5f },

{ 300.0f, 40.0f, 0.5f },

};

...

static DVLB_s* vshader_dvlb;

static shaderProgram_s program;

static int uLoc_projection;

static C3D_Mtx projection;

static void* vbo_data;

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");

// 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_AddFixed(attrInfo, 1); // v1=color

// Set the fixed attribute (color) to solid white

C3D_FixedAttribSet(1, 1.0, 1.0, 1.0, 1.0);

// Compute the projection matrix

Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0);

// 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), 1, 0x0);

// Configure the first fragment shading substage to just pass through the vertex color

// 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_PRIMARY_COLOR, 0, 0);

C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);

C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);

}


So now, after you’ve made your vertex shader, include it as “vshader_shbin.h” and not “vshader.v.pica” since it compiles with a different name. However, if you’re using a geometry shader with this vertex shader, name them both the same name and include the file with just the same and “_shbin”. Like if you named them both program, then you would just put “#include program_shbin.h”. Also, when naming your geometery shader file, it must end in “.g.pica Now create a struct and name it (I choose vertex).

typedef struct {

float x, y, z;

} vertex;

This struct will define the X, Y, and Z coordinates that will be used to define the vertex locations. You would create 3 vertices all with an X, Y, and Z value, but I still have to go over the coordinates, so instead, we create a new function that will initialize the scene of type static void. Inside the function, first we will load the vertex shader. The function you will need is DVLB_ParseFile. In documentation, it’s:

DVLB_s DVLB_ParseFile(u32 shbinData, u32 shbinSize);

For the first argument, you need to put “u32*” in parentheses and put vshader_shbin next to it. And for the last argument, you just need to add vshader_shbin_size. Also, since you’re declaring it as a variable, you should name it before. I named it “vshader_dvlb”. The function should look like:

vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size);

Outside the scene initialization function, create a static function like:

static shaderProgram_s program;

“shaderProgram_s” has to stay the same but “program” can change since “shaderProgram_s” is the name of the struct that you are defining. The function you need it for is the function “shaderProgramInit”. In documentation, it looks like:

Result shaderProgramInit(shaderProgram_s* sp);

And of course, you replace the “shaderProgram_s* sp” with the variable “program” but since it’s a pointer, then put a “&” before it. The function would look like:

shaderProgramInit(&program);

This function will initialize a shader program (for the vertex shader). The next function will actually set the vertex to the shader program we just made. The function in documentation is:

Result shaderProgramSetVsh(shaderProgram_s sp, DVLE_s dvle);

The first argument is “&program” and the last argument would be “&vshader_dvlb->DVLE[0]”. The function in all would be:

shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);

Next, you add the line “C3D_BindProgram(&program)”.*

*I don’t really know what this does, other than load the vertex shader program.

Then, you need to create a static int variable and name it “uLoc_projection”. You use it for the function “shaderInstanceGetUniformLocation” and what this function does is gets the location of a shader’s uniform. In documentation, it looks like:

s8 shaderInstanceGetUniformLocation(shaderInstance_s si, const char name);

You replace the “shaderInstance_s si” with “program.vertexShader” since “program “is a struct and you are going inside that struct to use a vertexShader instance. You replace the “const char name” with “projection” (include the quotes this time). That is the name of the uniform. Also, at the beginning of this function, make the variable “uLoc_projection” equal this function. The function will look like:

uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");

After you have the locations of the uniforms, you need to configure the attributes for use with the vertex shader. After you have got the location of the uniforms, you need to configure the attributes for use with the vertex shader. The first function would be “C3D_GetAttrInfo”. This function will make you create a struct like you did with ‘target’ in the first chapter. You will use this struct to initialize the attributes and use the vertex shader to load the attributes. The first function in documentation looks like:

C3D_AttrInfo* C3D_GetAttrInfo(void);

Right after the “C3D_AttrInfo*” part, name a struct after it and make it equal to “C3D_GetAttrInfo”. I should look like:

C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();

In the next function, you should put that ‘attrInfo’ in the argument so that it will initialize the attributes, making it look like:

AttrInfo_Init(attrInfo);

This next function requires you to load the position (v0) from the vertex shader and load it. The function should look like:

AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);

The first argument is the attributes info that was made earlier. The second argument is a regId which is the number of the register used (0 is in v0 which is used for this function). The third argument is what the projection array in the vertex shader is. It tells it that it’s a floating-point number like the .fvec directive made in the shader. The last argument is a count argument. I currently don’t know what it does but 3 seems to work. The next function “AttrInfo_AddFixed” will add a fixed attribute (which doesn’t change and this attribute will be color) that will be the color register (v1). The first argument is attribute info made earlier and the 2nd one is a regId but since color is v1 then the value should be 1:

AttrInfo_AddFixed(attrInfo, 1);

Next is to set the color. In this example, the color will be white. The first argument is the id of the color which is 1. The next four arguments are floats for X, Y, Z, and W (in that order) and they all equal to 1.0 which will set the color to white. The X is Red, the Y is Green, the Z is Blue, and the W is the Alpha channel (the lower the value, the more transparent it becomes) and if all the values are 0 then no shape will show up (but if the alpha channel is 1.0, then a black triangle will show up). The function should look like:

C3D_FixedAttribSet(1, 0.0, 0.0, 1.0, 0.1);

Next is to compute the projection matrix, or simply, position the camera view. You use the function “Mtx_OrthoTilt” (the only reason we’re using the tilt function is for the rotated framebuffers of the 3DS, if you used the regular one, it would appear sideways). For the first argument, you need to back and create another variable of type static. Since your naming a struct named “C3D_Mtx”, it should look like:

static C3D_Mtx projection;

And now go back to the OrthoTilt function and make the first argument “&projection” or whatever you named it. Now, the next 6 arguments are the X, Y, and Z positioning of the view. The first argument is the X positing that goes to the left. In this example, the camera goes right, so since these arguments are floats, keep it 0.0. For the next argument which is the X coordinate going right, we want to make the value 400.0. The next value is the Y coordinate going to the bottom. Since we don’t want the view to go down keep this at 0.0. The argument after that is the Y coordinate moving up. We want the view to go up 240.0. The next argument is the Z coordinate getting closer but we don’t need this right now so keep it at 0.0 and the last argument is the Z coordinate getting farther and we want this to happen a little so put it at 1.0. The function should be:

Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true);

The true or false part doesn’t matter since there’s no real notable change so just put any value. The next two functions will create a VBO, or, a vertex buffer object. The first function is “linearAlloc”. This function will allocate an aligned buffer by getting the size of the vertex list, which is the vertex points used to create 3D shapes. To create this, go to the top of the file right before the variables and create a static const variable array named “vertex_list” but put “vertex” before that since you need to use the X, Y, and Z values. Inside the variable put the values:

{ 200.0f, 200.0f, 0.5f },

{ 100.0f, 40.0f, 0.5f },

{ 300.0f, 40.0f, 0.5f },

Or change the values to correspond with the camera position, but keep the coordinates equivalent to each other like this, or else it won’t be a straight triangle. The variable should look like:

static const vertex vertex_list[] =

{

{ 200.0f, 200.0f, 0.5f },

{ 100.0f, 40.0f, 0.5f },

{ 300.0f, 40.0f, 0.5f },

};

Now go down to the variables and create a static void pointer named “vbo_data”:

static void* vbo_data;

And go down after the OrthoTilt function to create the linearAlloc function by taking your vbo_data variable and making it equivalent to linearAlloc with the C function sizeof and in it “vertex_list”:

vbo_data = linearAlloc(sizeof(vertex_list));

The second function is memcpy, which is a C function that needs string.h so include that file also. For those that don’t know, memcpy copies characters from one string to another string. We will copy the data from “vertex_list” to “vbo_data” with the size of “vertex_list”. To create this function, in memcpy’s first argument, you will define the data’s destination which will be “vbo_data” so put that for the first argument. For the second one, you will copy the data from “vertex_list” so put that as the second. The third argument requires the size of “vertex_list” so for the last one, make a sizeof function of “vertex_list”:

memcpy(vbo_data, vertex_list, sizeof(vertex_list));

The next thing you will do is configure the buffers. The first function will get the buffer info by creating a pointer variable named bufInfo and making it equivalent to “C3D_GetBufInfo”:

C3D_BufInfo* bufInfo = C3D_GetBufInfo();

Next you will initialize bufInfo with the “BufInfo_Init” function with bufInfo as its argument:

BufInfo_Init(bufInfo);

And lastly, you will add the buffer info to the homebrew with “BufInfo_Add”. The first argument in this function will load the buffer info with the “bufInfo” variable made. The second argument will load the VBO we made earlier (vbo_data). The third argument determines the stride of an array, which is the number of locations in memory between each array element, measured in bytes or the size of the array's elements. In this case, it will be the size of the “vertex” array. Just put the sizeof function with the vertex function in it. The fourth argument is the attribute count, which is how many attributes you want to use. In this case you want to use 1 since you’re only using 1 attribute which is the color (or the position). The last argument is what’s called a permutation which is the act of arranging the contents of something in different sequences or orders. In this case, we will use 0x0 since we’re not arranging anything. The end result should be:

BufInfo_Add(bufInfo, vbo_data, sizeof(vertex), 1, 0x0);

The next function will get the texture envelope to configure the first fragment shading substage to just pass through the vertex color. Like “C3D_GetBufInfo” and “C3D_GetAttrInfo”, you will create a variable that will become the texture envelope:

C3D_TexEnv* env = C3D_GetTexEnv(0);

The 0 is the ID of the texenv. Next, there’s “C3D_TexEnvSrc”, which gets the source of the texenv. The first argument is the texenv we just made (env) and the second argument is the mode which is C3D_Both. The next three arguments are for texture combiner sources. The first argument would be “GPU_PRIMARY_COLOR” for using the primary color and since we’re not using any other effects this chapter, keep the next two arguments at the value of 0:

C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, 0, 0);

The next function is “C3D_TexEnvOp” which has the same arguments as “TexEnvSrc” but keep the last three arguments at 0 for now since we won’t use any of its values:

C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);

The last function that will initialize the scene is “C3D_TexEnvFunc” which defines the functions of the TexEnv. The first two arguments are the same as the one above so just enter that data (env, C3D_Both) and there’s one more function which defines the actual function used in the “Texenv”. The last argument is a texture combiner function which edits the look of the texture (color). In this case, it will replace the texture envelope:

C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);

And that’s the initial function to creating a Citro3D homebrew. Of course, things can be added to it but we’ll go over what can be added later so for now just stick with this example.

Next, we’ll go over rendering the scene for actual view (don’t worry, this one only has 2 functions).

Here’s the function and what it should look like:


static void sceneRender(void)

{

// Update the uniforms

C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);

// Draw the VBO

C3D_DrawArrays(GPU_TRIANGLES, 0, vertex_list_count);

}


The first function is “C3D_FVUnifMtx4x4” (Floating-point Vector Uniform Matrix 4x4). What this function will do is update the uniforms. The first argument is the type of GPU shader you are using. Since you are using a vertex shader, insert GPU_VERTEX_SHADER for that function. The second argument is the ID of the uniform, or in this case the location of it, which we defined the last function as “uLoc_projection” so insert that there. The last argument is the matrix (or the camera view) which is defined as “projection” but since it’s a pointer, use “&projection”:

// Update the uniforms

C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);

The last function is “C3D_DrawArrays”. What this will do is draw the Vertex Buffer Object we made in the initialize function. The first argument is what type of shape we want to use. For now use GPU_TRIANGLES since I’ll go over the other primitives later. The second argument is the first buffer you want to use, which will be 0. The last argument needs a variable to be defined. Go back to where you created the global variables and create a new define variable that will define “vertex_list_count”. It will take the size of “vertex_list” and divide it by the size of “vertex_list” (keep the 0 in brackets though). It should look like:

define vertex_list_count (sizeof(vertex_list)/sizeof(vertex_list[0]))

Now, you can put “vertex_list_count” as the last argument:

// Draw the VBO

C3D_DrawArrays(GPU_TRIANGLES, 0, vertex_list_count);

Now we’ll input what the homebrew should do when the scene is exiting. The function should look like:


static void sceneExit(void)

{

// Free the VBO

linearFree(vbo_data);

// Free the shader program

shaderProgramFree(&program);

DVLB_Free(vshader_dvlb);

}


The first function frees the VBO which is just “vbo_data” and that’s its only argument:

linearFree(vbo_data);

The second function frees the shader program which is “&program”:

shaderProgramFree(&program);

The last function frees the vertex shader itself which we defined as “vshader_dvlb”:

DVLB_Free(vshader_dvlb);

Lastly, we will work in the main function. It should look like this in the end:


int main()

{

// Initialize graphics

gfxInitDefault();

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, 0x68B0D8FF, 0);

u32 transferFlags = GX_TRANSFER_FLIP_VERT(false)|

GX_TRANSFER_OUT_TILED(false)|

GX_TRANSFER_RAW_COPY(false)|

GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8)|

GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8)|

GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY);

C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, transferFlags);

// Initialize the scene

sceneInit();

// Main loop

while (aptMainLoop())

{

hidScanInput();

// Respond to user input

u32 kDown = hidKeysDown();

if (kDown & KEY_START)

break; // break in order to return to hbmenu

// Render the scene

C3D_FrameBegin(C3D_FRAME_SYNCDRAW);

C3D_FrameDrawOn(target);

sceneRender();

C3D_FrameEnd(0);

}

// Deinitialize the scene

sceneExit();

// Deinitialize graphics

C3D_Fini();

gfxExit();

return 0;

}


Before you start, where C3D_RenderTargetSetClear is, change 0xFF0505 to 0x68B0D8FF if you are using the function from the first chapter. It will change the background color to a light blue.

After you have initialized Citro3D in the main function already, you will declare the sceneInit function for use:

sceneInit();

In the main while loop, you render the scene by creating a function named “C3D_FrameBegin” with the argument of “C3D_FRAME_SYNCDRAW” which will make the homebrew not render a frame until the previous one is finished:

C3D_FrameBegin(C3D_FRAME_SYNCDRAW);

Next, you’ll create a function named “C3D_FrameDrawOn” which is just specifying the target we made earlier to draw on:

C3D_FrameDrawOn(target);

The next function just declares the scene render function:

sceneRender();

The last function ends the frame with “C3D_FrameEnd”. The argument should be 0 since that’s the value of “C3D_FRAME_SYNCDRAW”:

C3D_FrameEnd(0);

Lastly, outside of the main loop but inside the main function, right before you exit C3D, you will declare the scene exit function:

sceneExit();

And that’s all there is to creating a simple triangle in Citro3D. In the next chapter, we’ll go over primitives and 3D space.

results matching ""

    No results matching ""