3DS Homebrew Tips and Tricks (III)
This is the 3rd post on the series 3DS Homebrew Tips and Tricks
3DS Homebrew Tips and Tricks (I)
3DS Homebrew Tips and Tricks (II)
3DS Homebrew Tips and Tricks (III)
3DS Homebrew Tips and Tricks (I)
3DS Homebrew Tips and Tricks (II)
3DS Homebrew Tips and Tricks (III)
6. Adding sound permissions to 3ds files
Searching on the forums about sound you will find posts saying you cannot have sound using 3ds or 3dsx format (for example take a look af the F.A.Q in the blargsnes site or their wiki). So I spent a few days implementing my sound driver using the cia format that was supposed to be the only one supporting sound
That was hell. Everytime I needed to change anything I had to create the cia, transfer that to the console somehow (microsd management turned out to be the best option), delete the previous cia and install the new one. There wasn't anyway that I was aware of to automatize the whole big process.
Then I found this post clearly stating that the only thing you needed for having sound support in 3ds files is adding ns:s, am:u and csnd:SND service access on your rsf. Take a look here.
With the new ninjhax 2.0 released it seems that sound is also supported now on 3dsx files
7. Avoid GPU_TRIANGLE_FAN
It took me a few weeks to catch a random crash (the best of them) on one of the tests I was doing. At the end it turned out it was caused by using GPU_TRIANGLE_FAN somewhere on my code. After replacing that with just GPU_TRIANGLES it never happened again.
Talking about this on #3dsdev on the irc channel there seemed to be at least one other person having the same issue. Probably there is still something wrong on the ctrulib with them so I would recommend not using them if you can.
8. Using RomFS
RomFS is the file system used by 3ds and cia files to store resources inside the rom itselt. Although there are some tools available for extracting it from dumped roms and then putting it back, the ctrulib doesn't have support for it yet.
I thought it would be great if I could distribute my apps without any external dependencies so I took a look into it.
There are two ways to include a romfs into your builds:
- The first is creating it with an external tool (like RomFS Builder) then using the -romfs option when calling makerom.
- The second hasn't been fully implemented (according to 3dbrew) but it worked for my particular case. On the rsf file you should add
RomFs:
# Specifies the root path of the file system to include in the ROM.
RootPath : "../bin/romfs"
The rootpath is relative to your makefile's location
With any of these options you will have a rom that includes a file system with the resources. Now, how do you get access to them? Take a look at the code I posted on this thread.
This code only gives you access to the romfs. Now because we don't have support for this filesystem we have two options:
- Implement it
- User another known Filesystem
I went for the second one. Since I worked a lot with .zip files when I ported the engine to Android I decided to use the same thing here. So basically, my filesystem is a zip file containing all my resoures placed inside a romfs.
Since this zip is the only file available on my romfs I just do a search of the string "PK" from the beginning of the romfs and there is my zip file located. Hacky, but works pretty well
9. Painting steps: clean, render, transfer and swap
As I said before my starting point was the gpu example. If you spend some time trying to understand how it works you'll soon realize this is what happens after the GPUCMD_Finalize() command:
- GPUCMD_FlushAndRun is called and the command list is processed making the rendering
- GX_SetDisplayTransfer is called after that and the content of the framebuffer is trasnferred to the specified LCD framebuffer
- GX_SetMemoryFill is called, cleaning the color and depth buffer
- gfxSwapBuffersGpu is finally called, swapping buffers (because the ctrulib by default uses double buffer)
This process happens for a single render, if you are using the 3D effect it will happen twice, once for each screen (I am just simplifying it)
So basically: we render the current frame, transfer it to the proper LCD, clean the frame and then do the double buffer swap. Anything strange here? We are cleaning the screen for the next frame before the next frame actually happens, and that's a bad idea because:
- At some points on my code I was changing the clear color and that was delayed one frame.
- At some points on my code I wasn't interested on cleaning the color buffer for a specific frame and it was being cleaned in advance
This is actually easy to fix if you move that call to GX_SetMemoryFill to the beginning (before calling GPUCMD_FlushAndRun). Don't forget to move also the call to gspWaitForPSC0, and ensure that you actually want to clear the current color buffer or depth buffer.
10. Improving the 3D steresocopic effect
Another problem that I found with the gpu example is that no matter how you place the 3D slider on the console it is very hard to the get the feelling of 3d. Taking a look at the code I found these calculations being done for each eye:
mtx44 m;
loadIdentity44((float*)m);
translateMatrix((float*)m, interaxial*0.5f, 0.0f, 0.0f);
gsAdjustBufferMatrices(m);
what this is doing is adding a translation to the whole render process for each eye. So basically moving the camera to the left for the left eye and to the right to the right eye. It just doesn't work well. I changed it into this
mtx44 m;
loadIdentity44((float*)m);
rotateMatrixY((float*)m, -slider * 0.06f, false);
gsAdjustBufferMatrices(m);
which rotates the camera instead of displacing it for each eye. This works way more better :)
And that's it. I have concluded these series of posts. I just hope anything I had been writing these days will be useful for someone and I would like to see more homebrews released on this amazing console. Specially games.
Happy coding!