{"id":50,"date":"2023-08-18T15:31:17","date_gmt":"2023-08-18T15:31:17","guid":{"rendered":"https:\/\/nocoffei.com\/?p=50"},"modified":"2023-09-17T16:42:20","modified_gmt":"2023-09-17T16:42:20","slug":"musings-on-citro3d","status":"publish","type":"post","link":"https:\/\/nocoffei.com\/?p=50","title":{"rendered":"Musings on citro3d"},"content":{"rendered":"\n<p>The following content is a document I\u2019ve passed around for a couple years on pastebin, hastebin and Discord. Inevitably it ends up becoming unavailable or deleted from wherever I hosted it, and in no case has it ever been search-indexable. Now that I have a website it\u2019s time for it to finally have a proper home.<\/p>\n\n\n\n<p>The document describes principles and practices for how to learn to program the Nintendo 3DS GPU using the poorly-documented <a href=\"\">citro3d<\/a> library. Today with the advent of <a href=\"\">citro2d<\/a> and <a href=\"\">L\u00d6VE Potion<\/a> aspiring homebrew devs have a variety of ways to make things look pretty, but at the time it was written, learning to use this library was the only way to output even the simplest of graphics on the system when writing homebrew, so a lot of people were fumbling around in the dark trying to learn how it ticked. The aforementioned two libraries are also 2D-only, meaning C3D remains the only option for 3D graphics on the console.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Original Content Below<\/h3>\n\n\n\n<p>Quite often I find myself repeating the same information about getting started with 3DS GPU development, mostly due to the unique quirks of the PICA200 GPU and the citro3d driver. This isn\u2019t really meant to be any kind of \u201cformal\u201d introduction to citro3d, but instead a compilation of the various pieces of documentation and informal tutorials I\u2019ve put together over the course of my time using it. I\u2019m definitely not an expert, but I hope that with this compilation I can point people here without regurgitating the same info every time someone comes into the Nintendo Homebrew Discord asking about it.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>I have no knowledge of graphics programming and want to use citro3d. What do I do first?<\/strong><\/h5>\n\n\n\n<p>Go <a href=\"\">here<\/a> to learn the fundamentals of graphics programming and OpenGL, then continue.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>But why should I do that? Can\u2019t I just \u201cfigure it out\u201d?<\/strong><\/h5>\n\n\n\n<p>Perhaps this conversation will answer that question. It\u2019s related to Wii development, and I attempt to explain to this person why it doesn\u2019t make sense to do just that. Unrelated parts of the conversation have been cropped out.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Swiftloke08\/23\/2018\n@Magicrafter1308 Then I'm guessing they never wrote a 2D lib for the Wii. The thing is that graphics programming just isn't simple. It's not stupid, it's just the way it is. There's probably a ton of 3rd party stuff out there (akin to pp2d\/sf2d but not shit) that homebrew apps used but isn't official; dig deeper.\nC2D is just a wrapper for the underlying 3D code, and it's complicated to anyone who doesn't understand graphics programming\n\nMagicrafter1308\/23\/2018\nOh I'm not saying the way it's done is stupid\nI mean the example\nit puts all this code there, expecting you to know how to utilize it, or at least to be able to figure it out.\nand yeah if I could find something like pp2d or sf2d which I've used both that would be nice\nand for the record I thought they worked fine\n\nSwiftloke08\/23\/2018\nBoth 3DS and Wii could use a readme saying \"you should probably know graphics programming to use this stuff\"\n\nSwiftloke08\/23\/2018\nBut like\nAs someone who knows OpenGL\nI can read those Wii examples just fine- and I've never seen GX code before.\nExcept of the awareness of its fragment shading system.\nThat's the killer on 3DS- and why I've written documentation for it.\nIf you're interested in learning...\nhttp:&#47;&#47;learnopengl.com\/\n^Best site for learning graphics programming period.\n\n\nMagicrafter1308\/23\/2018\nIsn't OpenGL primarily used for 3d though?\n\nSwiftloke08\/23\/2018\nYou don't have to play with 3D at all\nIt's a 3D API\nBut you can ignore the 3D math and use it for 2D without any issue at all\nIn the same way C2D uses C3D for its 2D stuff\nAnd this thing: https:\/\/github.com\/Swiftloke\/ModMoon\/blob\/master\/source\/sdraw.hpp (my own UI engine for 3DS)\n\nMagicrafter1308\/23\/2018\nlike, I modified the gx example to display one image I made, instead of the whole cluster fuck of balls everywhere, but I have no idea how I would go about drawing a second png file. And I'd rather not combine them into a sprite sheet, since I seemly can't tell it that one sprite starts at x and y pixel, but rather in the example, the second ball starts at 0.5, 0.0. or in other words 50% into width of the sheet. But if I start combing sprites of different sizes that's going to be hell to calculate those floats.\n\nSwiftloke08\/23\/2018\nOK so either find an alternative\nOr\nRead sDraw\nSince it's based on that same kind of example originally and uses the same spritesheet math\nhttps:\/\/github.com\/Swiftloke\/ModMoon\/blob\/master\/source\/sdraw.hpp https:\/\/github.com\/Swiftloke\/ModMoon\/blob\/master\/source\/sdraw.cpp\n&#91;sDraw was completely overhauled since this was written, and is now a really great example of how to use citro3d properly. The points below about starting graphics programming without understanding it remain.]\nDo the texcoord calculation at sdraw_stex construction time though\nTBH it needs the floorboards ripped out; the core was thrown together when I didn't know anything about OpenGL and its poor design decisions have remained ever since\nI know that today now more than ever when I needed to add some basic functionality and it was hell to add to the engine\n\n\nMagicrafter1308\/23\/2018\nyou know what, I'll take my modification of the Wii GX example, and study it as much as I can, at least as much as I can wrap my head around. And I'll put comments in the code to show what I understand, what I don't understand, and what I might need help with. That way I can actually ask for help with specifics, and that way I might also know what to Google.\n\nSwiftloke08\/23\/2018\nLearning graphics programming will be required to progress, or you're not going to get anywhere\n\nMagicrafter1308\/23\/2018\nI didn't learn anything about graphics programming while figuring out how to move my game over to Citro 2D. Though keep in mind I'm not saying you're wrong - because you're not. Just pointing out that I can accomplish some things at my current level of knowledge.\n\nSwiftloke08\/23\/2018\nC2D abstracts completely from the underlying C3D (read: graphics programming) code. It can be used with ease by someone who doesn't know about this stuff- that's the entire point of its existence. GX on the other hand is even lower level than OpenGL, and you're going to HAVE to know how to work with it, based on what I've read and my extensive experience with C3D (especially the times where I fucked with it without knowing how it worked)\nhttps:\/\/devkitpro.org\/wiki\/libogc\/GX\n\nMagicrafter1308\/23\/2018\nwell I commented out a function called GX_SetScissor at line 86 (86 in the example that is) and compiled it and ran it and it appears to work fine. So I question why it's there.\nwait a second. I just removed the SetViewport command... I couldn't explain what a viewport is if someone asked but I know it's pretty much where you position the camera right? (I guess that is an explanation)\nI'm going to chalk this up to:\nit probably uses a default viewport if one isn't set\ninteresting\n\nSwiftloke08\/23\/2018\nIf you can't explain what a viewport is and what it does then you probably shouldn't be removing it or anything else for that matter\nYou're aware that this information is out there right? It doesn't have to be trial and error, actually learning the stuff gives you the advantages of knowing what to edit and why your edits \/ new code will work the way they do\n\nMagicrafter1308\/23\/2018\nI don't even know why I asked for help in the first place, I shouldn't have honestly.\n\nSwiftloke08\/23\/2018\nI would hate to have imparted that idea on to you... Why? Was it something I said?\n\nMagicrafter1308\/23\/2018\nWhich idea?\nAnd for the record, I'm not mad at you, I'm mad at myself.\n\nSwiftloke08\/23\/2018\nThe idea that you shouldn't have asked for help\n\nMagicrafter1308\/23\/2018\nOh, well I mean I shouldn't have right? As in, shouldn't the first thing I had done in that situation be to try and find my answers via Google, then if I couldn't figure it out - at that point start asking people?\n\nSwiftloke08\/23\/2018\nNah, in this example where you have no idea WTF you're looking at it's perfectly fine to ask for someone who does know this stuff and can give you \/ tell you about you the relevant info\nI sat at the other end of this exact conversation with fincs about a year and a month ago... It took me another 2 months to crack open the OpenGL tutorial he gave me, and I built sDraw's core stubbornly without knowing anything about graphics programming even though I knew the info was out there waiting for me, so I can relate pretty well\nThe best thing to do is to just open that NeHe tutorial. &#91;NOTE: The NeHe tutorials are for legacy OpenGL. In the examples for the Wii, they are all ported to GX so you can learn with those tutorials- a pretty neat idea. DON'T USE THESE TUTORIALS IF YOU DON'T INTEND ON WII DEVELOPMENT. See below in the conversation.] Right now. Just do it and it'll be easy to get going from there. Don't put it off, and save yourself the pain of trial and error, because that's only going to lead to pain immediately and in the long term where you have a shitty engine core based off of poor design choices you didn't know were poor\n\nSwiftloke08\/23\/2018\nStart with lesson 1 and work from there. Lessons 1-19 are ported to GX in the Wii examples so you'll learn (old) graphics programming in OpenGL and GX simultaneously\n\nMagicrafter1308\/23\/2018\nI'll be honest, this homebrew app I'm trying to make for the Wii is a one off, I don't plan to do this again. I'm just trying to make something stupid to give me and my friends (and possibly the internet) a good laugh. I'm gonna try to incorporate the Wii Balance Board into it, I'm sure something interesting can come of that. I don't need fancy graphics for this project. I actually don't care if it looks like shit to be honest.\n\nSwiftloke08\/23\/2018\nThen figure out libwiisprites\n\nMagicrafter1308\/23\/2018\nha, okay.\nShould I still check out the tutorials?\nOr should I wait for bigger projects\n\nSwiftloke08\/23\/2018\nIf you don't plan on playing with GX no because it's outdated\nThat GPU has been around since 2001\n\nMagicrafter1308\/23\/2018\nJesus\nwait what model is it?\n\nSwiftloke08\/23\/2018\nThe rules have changed, and the way to do things is much better these days\n\nMagicrafter1308\/23\/2018\nlooks like it's called Hollywood\n\nSwiftloke08\/23\/2018\nNo it's called the Flipper, I think it was renamed Hollywood with minimal modifications for the Wii\nIt's the GameCube's custom GPU\nWay ahead of it's time but today? Not so much.\n\nMagicrafter1308\/23\/2018\nwell yes but I'm making this for the Wii, not the gamecube\nbut I guess it makes sense that the Wii wouldn't use something from 01\n\nSwiftloke08\/23\/2018\nLiterally the exact same thing though\nAnyway learnopengl.com is the way to go if you want to learn graphics programming that is useful outside of developing for old platforms like the Wii\n\nMagicrafter1308\/23\/2018\nk\n\nSwiftloke08\/23\/2018\nIt teaches modern graphics programming<\/code><\/pre>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>I know graphics programming and want to use citro3d. What\/Why\/How do I use it\/etc.?<\/strong><\/h5>\n\n\n\n<p>Now we\u2019re getting to the meat of things\u2026 The following is about a year\u2019s worth of Discord and IRC logs. This is what \u201cinformal\u201d means. It is a collection of my responses to many different people asking the same question as you, the reader, and IRC logs between fincs and myself asking about various pieces of C3D functionality.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Swiftloke 07\/11\/2018\nHey guys, managed to get my docs up on GitHub :) https:\/\/github.com\/Swiftloke\/citro3d\/commits\/next\n\nTexEnv, BufInfo, uniforms and framebuffers are currently documented.<\/code><\/pre>\n\n\n\n<p>^Official formal documentation. My own work, and it\u2019s not in master because it\u2019s not completed. Will be finished Someday(TM).<\/p>\n\n\n\n<p>Various comments on Citro3D versus OpenGL, and getting started with it. Basically a lot of the same stuff repeated in a different way.<br>The good news is that it\u2019s not hard to get the basics if you know OpenGL- configuring buffers, writing shaders etc. is not hard<br>What\u2019s hard is doing some of the fancier PICA200 specific stuff without any documentation<\/p>\n\n\n\n<p><strong>Should you be using citro3d, or should you be using citro2d?<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Swiftloke05\/27\/2018\ncitro3d has no finished documentation. I have some WIP docs that I need to release. Need to learn git-fu\u2026. &#91;Since then, I actually did this :P]\nBut generally if you know OpenGL you'll understand C3D just fine\nFor the most part, if you want to make UI\/2D games with ease, C2D is what you want, it's oriented towards people who don't know &amp; don't care to know graphics programming, whereas C3D is just OpenGL for those who need\/want typical graphics programming\n\nAnother small thing\u2026 It's not \"beginner friendly\" because it caters to people who already know graphics programming. This isn't a library that will guide you through making 3D stuff step-by-step; it's not supposed to and OpenGL doesn't either.<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Rude Granny ?03\/31\/2018\nI see\nI'm slowly reading about 3ds development incl. citro3d and slowly understanding very little\n\nChroma Ryu ? ? ?03\/31\/2018\n:thumbsup: @Rude Granny ?\n\nRude Granny ?03\/31\/2018\nLike I can make a basic \"press buttons and get text output\" system but nothing useful\n:notlikeblob:\nIs there some \"beginner-friendly\" documentation anywhere?\nAll the docs are fine until the actual development part\n\nbernardogiordano03\/31\/2018\nno docs for citro3d yet\nlook at examples\n\nSwiftloke03\/31\/2018\ncitro3d by principle is not beginner friendly honestly; if you don't know how graphics programming works already you're not going to get far\nEven with the docs, that won't change<\/code><\/pre>\n\n\n\n<p>The following is the meat of this tutorial. It is a complete tutorial for the PICA200\u2019s TexEnv, which is important to understand if you want to do Anything At All.<\/p>\n\n\n\n<p><strong>A basic \u201cIf you know OpenGL, here\u2019s how you do shit with C3D, beyond the obvious from the examples\u201d-<\/strong><\/p>\n\n\n\n<p><br>(From logs with someone who wanted to learn citro3d. I gave a very thorough explanation.)<br>Swiftloke \u2013 03\/17\/2018<br>OK, so I\u2019m assuming you\u2019ve gotten through the getting started section of that tutorial I\u2019ve linked you, given your evident \u201cknowing-what-you\u2019re-doing\u201d aura.<br>So the 3DS has vertex and geometry shaders only. Shaders are written in PICA200 assembly because there\u2019s no high-level compiler written, although there\u2019s one being worked on.(edited)<br>https:\/\/github.com\/fincs\/picasso\/blob\/master\/Manual.md https:\/\/www.3dbrew.org\/wiki\/GPU\/Shader_Instruction_Set#Instructions<\/p>\n\n\n\n<p>There\u2019s some resources for the instruction set.<br>For fragment shading, there\u2019s the \u201ccombiner stages\u201d unit, more commonly known as TexEnv.<br>You get 6 stages of simple instructions to calculate colors. You take source values, perform some really simple operations on them (such as inversion, or setting the entire RGB vector equal to a specific part of it- for example, color.rgb = color.rrr) then perform some kind of function, such as interpolation, addition, or multiplication. Stages are run from 0 to 5. Alpha can be configured separately but is the same concept.(edited)<br>https:\/\/github.com\/citra-emu\/citra\/blob\/master\/src\/video_core\/swrasterizer\/rasterizer.cpp#L406 &lt;- read this for a great (but VERY thorough) explanation.<br>So, to pass stuff from the vertex shader to the TexEnv, we can use the color output register as defined here: https:\/\/github.com\/fincs\/picasso\/blob\/master\/Manual.md#out<\/p>\n\n\n\n<p>Let\u2019s jump right in with a basic example- multiplying your vertex color and your texture color.<br>In your shader:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.alias v(attribute ID) incolor\n...\n.out - color\n...\ncolor = incolor<\/code><\/pre>\n\n\n\n<p><br>Then, on the CPU, you\u2019ll configure your TexEnv. Here\u2019s how you\u2019d like it to be done (multiplication- we\u2019ll actually interpolate in this case). Enum values are all found in http:\/\/smealum.github.io\/ctrulib\/enums_8h.html, keep it handy at all times.(edited)<br>In this case, we\u2019ll interpolate between the texture color and vertex color based on the GPU_CONSTANT value, which you can provide as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define RGBA8(r, g, b, a) ((((r)&amp;0xFF)&lt;&lt;0) | (((g)&amp;0xFF)&lt;&lt;8) | (((b)&amp;0xFF)&lt;&lt;16) | (((a)&amp;0xFF)&lt;&lt;24)) \/\/My favorite color packing define\nC3D_TexEnv* tev = C3D_GetTexEnv(0); \/\/Get TexEnv stage 0- use a different number for a different stage\nC3D_TexEnvSrc(tev, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_CONSTANT); \/\/C3D has default arguments for the second and third sources\/ops. You must define the first one though.\nC3D_TexEnvOp(tev, C3D_RGB, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_ONE_MINUS_SRC_ALPHA); \/\/Passthrough, passthrough, set it equal to one minus the alpha of the constant (to provide the interpolation results we expect)\nC3D_TexEnvOp(tev, C3D_Alpha, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_A_SRC_ALPHA, GPU_TEVOP_ONE_MINUS_SRC_ALPHA); \/\/Same, but different enum values so we need a different call\nC3D_TexEnvFunc(tev, C3D_Both, GPU_INTERPOLATE); \/\/Set GPU_INTERPOLATE for both color and alpha\nC3D_TexEnvColor(tev, RGBA8(0, 0, 0, ourinterpolationfactor)); \/\/Set GPU_CONSTANT<\/code><\/pre>\n\n\n\n<p><strong>A really interesting TexEnv example, found in the below conversation, but while we\u2019re on the topic of TexEnv we should see it.<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PabloMK704\/09\/2018\n@Swiftloke https:\/\/pastebin.com\/yY2G7Pxs\nThat uses the combiner to calculate the greyscale version of a texture\n&#91;Want to understand how that works? Check this out for an explanation: https:\/\/xorshaders.weebly.com\/tutorials\/black-and-white-shader]\n&#91;That code is a bit outdated and would need to be updated for the latest citro3d, because \"0\" is no longer a valid value for the TexEnvX functions. But that's only a small change that would be needed.]\n&#91;Also, that example uses TexEnvBuf. You should read the below conversations about that particular PICA feature.]<\/code><\/pre>\n\n\n\n<p><strong>Some stuff on TexEnvBuf, what it is, and how it\u2019s used.<\/strong><\/p>\n\n\n\n<p><br>The TexEnvBuf set of functions are functions to deal with the GPU_PREVIOUS_BUFFER source for TexEnv.<br>Open this up, and read the docs: https:\/\/github.com\/Swiftloke\/citro3d\/blob\/next\/include\/c3d\/texenv.h#L60<br>So basically this is a buffer where you can save the result of a stage for later use. To set what stages write to this buffer, you use C3D_TexEnvBufUpdate and the GPU_TEV_BUFFER_WRITE_CONFIG macro. You can also set what the initial value of the buffer is using C3D_TexEnvBufColor, which could be useful as a second constant. An interesting thing to note is that the TexEnvBuf that\u2019s updated in one stage isn\u2019t actually available until two stages after that stage. So when the buffer is written to in stage 0, that value won\u2019t be available until stage 2. (which makes sense- in stage 1 you\u2019d just use GPU_PREVIOUS.)<\/p>\n\n\n\n<p><strong>Procedural Texture Generation:<\/strong><\/p>\n\n\n\n<p><br>Proctex is a really interesting and fun idea completely unique to the PICA200. I\u2019d give a tutorial on it, but there\u2019s something much, much better already out there: a presentation about the ideas and principles behind it (and the rest of the PICA200) and how to use it, given by the developers of the GPU who work at Digital Media Professionals (the company that produced it). Once you read this, you should have a basic idea of how everything works, and be able to understand most of how the example works. (More in-depth explanations on implementation details in C3D to do) <a href=\"\">https:\/\/www.4gamer.net\/games\/017\/G001762\/20120822007\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unspecific commentary on a homebrew graphics API.<\/p>\n","protected":false},"author":2,"featured_media":71,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-50","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts\/50","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=50"}],"version-history":[{"count":0,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts\/50\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/media\/71"}],"wp:attachment":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=50"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=50"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=50"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}