// SPDX-License-Identifier: CC0-1.0 // // SPDX-FileContributor: Antonio Niño Díaz, 2008-2024 // SPDX-FileContributor: NightFox, 2009-2011 // // This file is part of Nitro Engine // NightFox’s Lib (NFlib) is a library that can supports the 2D hardware of the // NDS. It's ideal to complement Nitro Engine, as it only supports the 3D // hardware. The latest version can be found at the following link: // // https://github.com/knightfox75/nds_nflib // // A few notes: // // - Remember to avoid using 3D features of NFlib. // // - Don't use the dual 3D mode in Nitro Engine. // // - You are free to use any features of NFlib in the secondary screen, but you // need to assign VRAM C and D to NFlib to get space for sprites and // backgrounds. // // - You could load 2D background and sprites in the same screen as the 3D // rendering, but you will have to give up VRAM banks A and B. If you also use // banks C and D for the secondary screen, it will leave you without any VRAM // for textures. #include #include #include #include #include #include #include typedef struct { NE_Camera *Camera; NE_Model *Model; NE_Animation *Animation; NE_Material *Material; } SceneData; void Draw3DScene(void *arg) { SceneData *Scene = arg; NE_CameraUse(Scene->Camera); NE_PolyFormat(31, 0, NE_LIGHT_0, NE_CULL_NONE, 0); NE_ModelDraw(Scene->Model); } void WaitLoop(void) { while (1) swiWaitForVBlank(); } void LoadAndSetupGraphics3D(SceneData *Scene) { // When using nflib for the 2D sub screen engine, banks C and H are used for // backgrounds and banks D and I are used for sprites. Nitro Engine only // uses bank E for palettes, so the only thing we need to do is to tell // Nitro Engine to only use banks A and B and leave C and D unused. NE_Init3D(); NE_TextureSystemReset(0, 0, NE_VRAM_AB); // Create objects Scene->Model = NE_ModelCreate(NE_Animated); Scene->Camera = NE_CameraCreate(); Scene->Material = NE_MaterialCreate(); Scene->Animation = NE_AnimationCreate(); // Load assets from the filesystem if (NE_ModelLoadDSMFAT(Scene->Model, "robot.dsm") == 0) { consoleDemoInit(); printf("Couldn't load model..."); WaitLoop(); } if (NE_AnimationLoadFAT(Scene->Animation, "robot_wave.dsa") == 0) { consoleDemoInit(); printf("Couldn't load animation..."); WaitLoop(); } if (NE_MaterialTexLoadFAT(Scene->Material, NE_A1RGB5, 256, 256, NE_TEXGEN_TEXCOORD, "texture_tex.bin") == 0) { consoleDemoInit(); printf("Couldn't load texture..."); WaitLoop(); } // Assign material to the model NE_ModelSetMaterial(Scene->Model, Scene->Material); // Assign animation to the model and start it NE_ModelSetAnimation(Scene->Model, Scene->Animation); NE_ModelAnimStart(Scene->Model, NE_ANIM_LOOP, floattof32(0.1)); // Setup light NE_LightSet(0, NE_White, 0, -1, -1); // Setup background color NE_ClearColorSet(NE_Black, 31, 63); // Setup camera NE_CameraSet(Scene->Camera, 6, 3, -4, 0, 3, 0, 0, 1, 0); } void LoadAndSetupGraphics2D(void) { // Initialize sub 2D engine NF_Set2D(1, 0); // Initialize sprites for sub screen (it uses VRAM D and I) NF_InitSpriteBuffers(); NF_InitSpriteSys(1); // Load assets from filesystem to RAM NF_LoadSpriteGfx("sprite/personaje", 1, 64, 64); NF_LoadSpritePal("sprite/personaje", 1); // Copy all frames to VRAM NF_VramSpriteGfx(1, 1, 0, false); NF_VramSpritePal(1, 1, 0); // Display sprite on the screen NF_CreateSprite(1, 0, 0, 0, 0, 0); // Initialize tiled backgrounds and text systems for the 2D sub engine (it // uses VRAM C and H) NF_InitTiledBgBuffers(); NF_InitTiledBgSys(1); NF_InitTextSys(1); // Load assets from filesystem to RAM NF_LoadTiledBg("bg/bg3", "capa_3", 256, 256); NF_LoadTiledBg("bg/bg1", "capa_1", 1536, 256); NF_LoadTiledBg("bg/bg0", "capa_0", 2048, 256); NF_LoadTextFont("fnt/default", "normal", 256, 256, 0); // Create tiled backgrounds NF_CreateTiledBg(1, 3, "capa_3"); NF_CreateTiledBg(1, 2, "capa_1"); NF_CreateTiledBg(1, 1, "capa_0"); // Create text layer NF_CreateTextLayer(1, 0, 0, "normal"); } int main(int argc, char *argv[]) { SceneData Scene = { 0 }; // Initialize nitroFS before doing anything else NF_Set2D(0, 0); NF_Set2D(1, 0); consoleDemoInit(); printf("Starting nitroFS...\n"); if (!nitroFSInit(NULL)) { printf("Failed to start nitroFS\n"); printf("Press START to exit\n"); while (1) { swiWaitForVBlank(); scanKeys(); if (keysHeld() & KEY_START) return -1; } } swiWaitForVBlank(); // Set the root folder to the nitroFS filesystem NF_SetRootFolder("NITROFS"); // Setup interrupt handlers irqEnable(IRQ_HBLANK); irqSet(IRQ_VBLANK, NE_VBLFunc); irqSet(IRQ_HBLANK, NE_HBLFunc); // Load and setup graphics LoadAndSetupGraphics3D(&Scene); LoadAndSetupGraphics2D(); // Initialize variables to control the sprite int pj_x = 0; int pj_y = 127; unsigned int pj_frame = 0; unsigned int pj_anim_ticks = 0; int pj_speed = 1; // Initialize variables to control the backgrounds int bg_x[4] = {0, 0, 0}; // Print instructions for the user NF_WriteText(1, 0, 1, 1, "PAD: Rotate model"); NF_WriteText(1, 0, 1, 2, "L/R: Scroll background"); NF_WriteText(1, 0, 1, 3, "START: Exit"); NF_UpdateTextLayers(); while (1) { NE_WaitForVBL(NE_UPDATE_ANIMATIONS); // At this point we are in the vertical blank period. This is where 2D // elements have to be updated to avoid flickering. // Update the scroll of the backgrounds for (int n = 0; n < 3; n ++) NF_ScrollBg(1, n + 1, bg_x[n], 0); // Copy shadow OAM copy to the OAM of the 2D sub engine oamUpdate(&oamSub); // Start processing a new frame after the 2D elements have been updated. scanKeys(); uint32_t keys = keysHeld(); if (keys & KEY_START) break; if (keys & KEY_RIGHT) NE_ModelRotate(Scene.Model, 0, 2, 0); if (keys & KEY_LEFT) NE_ModelRotate(Scene.Model, 0, -2, 0); if (keys & KEY_UP) NE_ModelRotate(Scene.Model, 0, 0, 2); if (keys & KEY_DOWN) NE_ModelRotate(Scene.Model, 0, 0, -2); if (keys & KEY_L) { bg_x[0] -= 2; if (bg_x[0] < 0) bg_x[0] = 0; } if (keys & KEY_R) { bg_x[0] += 2; if (bg_x[0] > 1152) bg_x[0] = 1152; } // For the parallax effect, calculate the scroll of the second layer // based on the scroll of the first one. bg_x[1] = bg_x[0] / 2; // Move sprite pj_x += pj_speed; if ((pj_x < 0) || (pj_x > 191)) { pj_speed = -pj_speed; if (pj_speed > 0) NF_HflipSprite(1, 0, false); else NF_HflipSprite(1, 0, true); } NF_MoveSprite(1, 0, pj_x, pj_y); // Animate sprite pj_anim_ticks++; if (pj_anim_ticks > 5) { pj_anim_ticks = 0; pj_frame++; if (pj_frame > 11) pj_frame = 0; NF_SpriteFrame(1, 0, pj_frame); } // Refresh shadow OAM copy NF_SpriteOamSet(1); // Draw 3D scene NE_ProcessArg(Draw3DScene, &Scene); } return 0; }