### Initialize and Start a New Thread with osCreateThread and osStartThread (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Demonstrates the usage of osCreateThread to initialize a new thread and osStartThread to begin its execution. Threads are created with a specified entry point, priority, and stack, initially in a STOPPED state, requiring osStartThread to become runnable. This is fundamental for multi-threaded N64 applications. ```c #include // Thread entry function void gameThread(void *arg) { int *gameMode = (int *)arg; while(1) { // Game logic here osYieldThread(); } } // Thread setup OSThread thread; u64 threadStack[STACKSIZE/sizeof(u64)]; int gameMode = 1; void initGameThread(void) { // Create thread with ID 1, priority 10 osCreateThread( &thread, // Thread structure 1, // Thread ID gameThread, // Entry point &gameMode, // Argument passed to entry &threadStack[STACKSIZE/sizeof(u64)], // Stack pointer (top) 10 // Priority (higher = more priority) ); // Thread is now created but not running osStartThread(&thread); // Start execution } ``` -------------------------------- ### Controller Input API Source: https://context7.com/n64decomp/libreultra/llms.txt APIs for initializing and reading controller input from connected N64 controllers. Includes functions to start a read operation and retrieve the data. ```APIDOC ## osContStartReadData / osRecvMesg / osContGetReadData ### Description Initiates a read of controller state from all connected ports. This is an asynchronous operation that posts results to a message queue when complete. The `osContGetReadData` function retrieves the actual controller data after the read operation has finished. ### Method Asynchronous function calls ### Endpoint N/A (Library Functions) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c #include extern OSMesgQueue siMessageQueue; extern OSContPad controllerData[]; void readControllers(void) { OSMesg msg; // Start reading controller data osContStartReadData(&siMessageQueue); // Wait for read to complete osRecvMesg(&siMessageQueue, &msg, OS_MESG_BLOCK); // Get the actual controller data osContGetReadData(controllerData); } ``` ### Response #### Success Response (Completion) - **controllerData** (OSContPad array) - An array containing the input data for each connected controller. #### Response Example ```json { "controllerData": [ { "button": 1, "stick_x": 20, "stick_y": -10, "err": 0 }, { "button": 0, "stick_x": 0, "stick_y": 0, "err": 0 } ] } ``` ``` -------------------------------- ### Activate Threads with osStartThread (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Illustrates how osStartThread activates threads that are in a STOPPED or WAITING state, transitioning them to RUNNABLE. It also explains that a higher-priority newly started thread can preempt the currently running thread, crucial for managing task execution order. ```c #include OSThread audioThread; OSThread renderThread; u64 audioStack[STACKSIZE/sizeof(u64)]; u64 renderStack[STACKSIZE/sizeof(u64)]; void audioThreadFunc(void *arg) { while(1) { // Process audio } } void renderThreadFunc(void *arg) { while(1) { // Render frame } } void initThreads(void) { // Create high-priority audio thread osCreateThread(&audioThread, 3, audioThreadFunc, NULL, &audioStack[STACKSIZE/sizeof(u64)], 100); // Create lower-priority render thread osCreateThread(&renderThread, 4, renderThreadFunc, NULL, &renderStack[STACKSIZE/sizeof(u64)], 50); // Start audio first (preempts if needed) osStartThread(&audioThread); // Start rendering (runs when audio yields) osStartThread(&renderThread); } ``` -------------------------------- ### Start Compressed Audio Sequence with alCSPPlay Source: https://context7.com/n64decomp/libreultra/llms.txt Initiates playback of a compressed audio sequence using the `alCSPPlay` function. This is part of the audio library's event queue system, suitable for background music and complex audio sequences. It requires initialization of the audio heap and the compressed sequence player. ```c #include ALCSPlayer seqPlayer; ALCSPlayer *seqPlayerPtr = &seqPlayer; u8 audioHeap[AUDIO_HEAP_SIZE]; ALHeap heap; void initAudio(void) { // Initialize audio heap alHeapInit(&heap, audioHeap, sizeof(audioHeap)); // Initialize compressed sequence player alCSPNew(seqPlayerPtr, &heap); } // Load and play background music void playBackgroundMusic(ALCSeq *sequence, ALBank *bank) { // Set the bank (instrument samples) alCSPSetBank(seqPlayerPtr, bank); // Set the sequence (music data) alCSPSetSeq(seqPlayerPtr, sequence); // Set volume (0-127) alCSPSetVol(seqPlayerPtr, 127); // Set tempo (microseconds per quarter note) alCSPSetTempo(seqPlayerPtr, 120); // Start playback alCSPPlay(seqPlayerPtr); } // Control music during gameplay void updateMusic(int gameState) { switch(gameState) { case GAME_TITLE: alCSPSetVol(seqPlayerPtr, 127); // Full volume alCSPSetTempo(seqPlayerPtr, 100); break; case GAME_PLAYING: alCSPSetVol(seqPlayerPtr, 96); // Slightly quieter alCSPSetTempo(seqPlayerPtr, 120); break; case GAME_PAUSED: alCSPStop(seqPlayerPtr); break; case GAME_RESUME: alCSPPlay(seqPlayerPtr); // Resume from pause break; } } ``` -------------------------------- ### Initialize and Read Controller Data - C Source: https://context7.com/n64decomp/libreultra/llms.txt Provides functions to initialize the controller subsystem, read input from connected controllers, and process button presses and analog stick movements. It utilizes `osContInit`, `osContStartReadData`, `osRecvMesg`, and `osContGetReadData`. Input is read asynchronously and posted to a message queue. ```c #include #define NUM_CONTROLLERS 4 OSMesgQueue siMessageQueue; OSMesg siMessages[1]; OSContStatus statusData[NUM_CONTROLLERS]; OSContPad controllerData[NUM_CONTROLLERS]; void initControllers(void) { u8 pattern; // Create SI message queue osCreateMesgQueue(&siMessageQueue, siMessages, 1); // Set SI event to this queue osSetEventMesg(OS_EVENT_SI, &siMessageQueue, (OSMesg)0); // Initialize controller subsystem osContInit(&siMessageQueue, &pattern, statusData); // pattern now contains bitmask of connected controllers // bit 0 = controller 1, bit 1 = controller 2, etc. } // Read controller input each frame void readControllers(void) { OSMesg msg; // Start reading controller data osContStartReadData(&siMessageQueue); // Wait for read to complete osRecvMesg(&siMessageQueue, &msg, OS_MESG_BLOCK); // Get the actual controller data osContGetReadData(controllerData); } // Process controller input void handleInput(void) { readControllers(); // Check player 1 controller OSContPad *pad = &controllerData[0]; if (pad->button & CONT_A) { // A button pressed playerJump(); } if (pad->button & CONT_START) { // Start button pressed pauseGame(); } // Analog stick input (-80 to +80) if (pad->stick_x > 20) { // Move right playerX += pad->stick_x * 0.1f; } else if (pad->stick_x < -20) { // Move left playerX += pad->stick_x * 0.1f; } if (pad->stick_y > 20) { // Move forward playerZ -= pad->stick_y * 0.1f; } else if (pad->stick_y < -20) { // Move back playerZ -= pad->stick_y * 0.1f; } // Check for button combinations if ((pad->button & CONT_L) && (pad->button & CONT_R)) { // L + R pressed together resetCamera(); } } ``` -------------------------------- ### Post Messages to a Queue with osSendMesg (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Demonstrates using osSendMesg to post messages to an OSMessageQueue. It covers both blocking (OS_MESG_BLOCK) and non-blocking (OS_MESG_NOBLOCK) send operations, showing how to handle potential queue full scenarios and how messages are received by consumer threads using osRecvMesg. ```c #include #define MSG_COUNT 8 OSMesgQueue eventQueue; OSMesg eventMessages[MSG_COUNT]; typedef enum { MSG_FRAME_COMPLETE = 1, MSG_AUDIO_READY = 2, MSG_INPUT_EVENT = 3 } GameMessage; void initMessageQueue(void) { osCreateMesgQueue(&eventQueue, eventMessages, MSG_COUNT); } // Non-blocking send from interrupt handler void frameCompleteInterrupt(void) { OSMesg msg = (OSMesg)MSG_FRAME_COMPLETE; // Returns -1 if queue full, 0 if success if (osSendMesg(&eventQueue, msg, OS_MESG_NOBLOCK) == -1) { // Queue full, frame dropped } } // Blocking send from game thread void produceAudioData(void) { OSMesg msg = (OSMesg)MSG_AUDIO_READY; // Blocks until queue has space osSendMesg(&eventQueue, msg, OS_MESG_BLOCK); // Returns here when message posted } // Consumer thread void eventProcessorThread(void *arg) { OSMesg receivedMsg; while(1) { // Block until message arrives osRecvMesg(&eventQueue, &receivedMsg, OS_MESG_BLOCK); switch((GameMessage)receivedMsg) { case MSG_FRAME_COMPLETE: // Handle frame complete break; case MSG_AUDIO_READY: // Process audio break; case MSG_INPUT_EVENT: // Handle input break; } } } ``` -------------------------------- ### Video Output Configuration Source: https://context7.com/n64decomp/libreultra/llms.txt Configure the video interface mode for display output, supporting various resolutions and formats like NTSC and PAL. Changes take effect on the next vertical retrace. ```APIDOC ## osViSetMode - Configure video output mode ### Description Sets the video interface mode for display output. Supports multiple resolutions and formats including NTSC, PAL, and various bit depths. Mode change takes effect on next vertical retrace. ### Method (Implied C function call, not a standard HTTP method) ### Endpoint (N/A - This is a C function) ### Parameters (N/A - This is a C function, parameters are arguments to the function) ### Request Example ```c #include // Standard video modes (defined in SDK) extern OSViMode osViModeTable[]; void setupVideo(void) { // Set up for NTSC low-resolution mode (320x240) osViSetMode(&osViModeTable[OS_VI_NTSC_LPN1]); } // Custom video mode configuration OSViMode customMode = { .comRegs = { .ctrl = 0x3000, // Control settings .width = 320, // Horizontal resolution .burst = 0x00000000 // Color burst }, .fldRegs[0] = { .origin = 0x00000000, // Frame buffer origin .yScale = 0x04000400, // Vertical scale .xScale = 0x02000200 // Horizontal scale } }; void setupCustomVideo(void) { osViSetMode(&customMode); osViSwapBuffer((void *)0x80100000); // Frame buffer address } ``` ### Response (N/A - This is a C function, no standard HTTP response) ### Response Example (N/A - This is a C function) ``` -------------------------------- ### Configure N64 Video Output Mode (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Sets the video interface mode for display output, supporting various resolutions and formats like NTSC and PAL. The change takes effect on the next vertical retrace. This function utilizes predefined video modes or allows for custom configurations. ```c #include // Standard video modes (defined in SDK) extern OSViMode osViModeTable[]; void setupVideo(void) { // Set up for NTSC low-resolution mode (320x240) osViSetMode(&osViModeTable[OS_VI_NTSC_LPN1]); // Alternative: PAL mode // osViSetMode(&osViModeTable[OS_VI_PAL_LPN1]); // Alternative: High-resolution mode (640x480) // osViSetMode(&osViModeTable[OS_VI_NTSC_HPN1]); } // Custom video mode configuration OSViMode customMode = { .comRegs = { .ctrl = 0x3000, // Control settings .width = 320, // Horizontal resolution .burst = 0x00000000 // Color burst }, .fldRegs[0] = { .origin = 0x00000000, // Frame buffer origin .yScale = 0x04000400, // Vertical scale .xScale = 0x02000200 // Horizontal scale } }; void setupCustomVideo(void) { osViSetMode(&customMode); // Set frame buffer osViSetSpecialFeatures(OS_VI_DITHER_FILTER_ON); osViSwapBuffer((void *)0x80100000); // Frame buffer address } ``` -------------------------------- ### Generate View Matrix (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Creates a view transformation matrix defining camera position, target point, and up vector. This matrix transforms world coordinates to camera space, essential for positioning and orienting the camera in a 3D scene. ```c #include Mtx viewMatrix; void positionCamera(float frameTime) { float cameraX = 100.0f * cosf(frameTime * 0.001f); float cameraY = 50.0f; float cameraZ = 100.0f * sinf(frameTime * 0.001f); // Camera orbits around origin, looking at center guLookAt(&viewMatrix, cameraX, cameraY, cameraZ, // Eye position 0.0f, 0.0f, 0.0f, // Look-at point (origin) 0.0f, 1.0f, 0.0f); // Up vector (Y-axis) // Upload view matrix to RSP gSPMatrix(glistp++, &viewMatrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); } // First-person camera following player void firstPersonCamera(float playerX, float playerY, float playerZ, float lookAngle, float lookPitch) { Mtx fpMatrix; // Calculate look-at point from angles float lookX = playerX + sinf(lookAngle) * cosf(lookPitch); float lookY = playerY + sinf(lookPitch); float lookZ = playerZ + cosf(lookAngle) * cosf(lookPitch); guLookAt(&fpMatrix, playerX, playerY + 5.0f, playerZ, // Eye slightly above player lookX, lookY, lookZ, // Calculated target 0.0f, 1.0f, 0.0f); // World up gSPMatrix(glistp++, &fpMatrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); } ``` -------------------------------- ### Generate Perspective Projection Matrix (C) Source: https://context7.com/n64decomp/libreultra/llms.txt Creates a perspective projection matrix for 3D rendering, converting field of view and aspect ratio into a transformation matrix for the RCP graphics pipeline. This function is essential for defining the camera's viewing frustum. ```c #include Mtx projectionMatrix; u16 perspNorm; void setupCamera(void) { // Create perspective projection // fovy=45 degrees, aspect=4/3, near=10, far=1000, scale=1.0 guPerspective(&projectionMatrix, &perspNorm, 45.0f, // Field of view (degrees) 4.0f / 3.0f, // Aspect ratio (width/height) 10.0f, // Near clipping plane 1000.0f, // Far clipping plane 1.0f); // Scale factor // Upload to RSP for rendering gSPPerspNormalize(glistp++, perspNorm); gSPMatrix(glistp++, &projectionMatrix, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); } // Wide-angle camera setup void setupWideCamera(void) { Mtx wideMatrix; u16 wideNorm; // 90-degree FOV for wide angle effect guPerspective(&wideMatrix, &wideNorm, 90.0f, // Wider field of view 16.0f / 9.0f, // Widescreen aspect 5.0f, // Closer near plane 2000.0f, // Further far plane 1.0f); gSPPerspNormalize(glistp++, wideNorm); gSPMatrix(glistp++, &wideMatrix, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); } ``` -------------------------------- ### guPerspective - Generate perspective projection matrix Source: https://context7.com/n64decomp/libreultra/llms.txt Creates a perspective projection matrix for 3D rendering. Converts field of view and aspect ratio into a transformation matrix compatible with the RCP graphics pipeline. ```APIDOC ## guPerspective - Generate perspective projection matrix ### Description Creates a perspective projection matrix for 3D rendering. Converts field of view and aspect ratio into a transformation matrix compatible with the RCP graphics pipeline. ### Method (Implied C function call, not a standard HTTP method) ### Endpoint (N/A - This is a C function) ### Parameters (N/A - This is a C function, parameters are arguments to the function) ### Request Example ```c #include Mtx projectionMatrix; u16 perspNorm; void setupCamera(void) { // Create perspective projection // fovy=45 degrees, aspect=4/3, near=10, far=1000, scale=1.0 guPerspective(&projectionMatrix, &perspNorm, 45.0f, // Field of view (degrees) 4.0f / 3.0f, // Aspect ratio (width/height) 10.0f, // Near clipping plane 1000.0f, // Far clipping plane 1.0f); // Scale factor // Upload to RSP for rendering gSPPerspNormalize(glistp++, perspNorm); gSPMatrix(glistp++, &projectionMatrix, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); } ``` ### Response (N/A - This is a C function, no standard HTTP response) ### Response Example (N/A - This is a C function) ``` -------------------------------- ### DMA Cache Synchronization API Source: https://context7.com/n64decomp/libreultra/llms.txt APIs for ensuring data consistency between the CPU cache and main memory, particularly before DMA operations. This is crucial for preventing data corruption when hardware accesses memory that the CPU has modified. ```APIDOC ## osWritebackDCache ### Description Writes modified cache lines back to main memory. This function is essential before DMA operations to ensure data consistency between the CPU cache and hardware. It guarantees that any data the CPU has written to its cache is flushed to main RAM, making it available for DMA transfers. ### Method Library Function ### Endpoint N/A (Library Function) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **addr** (void*) - Required - The starting address of the memory region to write back. - **size** (size_t) - Required - The size of the memory region in bytes. ### Request Example ```c #include typedef struct { int data; } MyData; MyData *sharedData; void updateAndPrepareForDMA(void) { // Modify data in cached memory sharedData->data = 123; // Ensure modifications are written to main memory osWritebackDCache(sharedData, sizeof(MyData)); // Now it's safe to DMA sharedData } ``` ### Response #### Success Response (Completion) This is a void function; success is indicated by the absence of errors during execution. #### Response Example N/A (Void function) ``` -------------------------------- ### guLookAt - Generate view matrix from camera position Source: https://context7.com/n64decomp/libreultra/llms.txt Creates a view transformation matrix defining camera position, target point, and up vector. Produces the matrix needed to transform world coordinates to camera space. ```APIDOC ## guLookAt - Generate view matrix from camera position ### Description Creates a view transformation matrix defining camera position, target point, and up vector. Produces the matrix needed to transform world coordinates to camera space. ### Method (Implied C function call, not a standard HTTP method) ### Endpoint (N/A - This is a C function) ### Parameters (N/A - This is a C function, parameters are arguments to the function) ### Request Example ```c #include Mtx viewMatrix; void positionCamera(float frameTime) { float cameraX = 100.0f * cosf(frameTime * 0.001f); float cameraY = 50.0f; float cameraZ = 100.0f * sinf(frameTime * 0.001f); // Camera orbits around origin, looking at center guLookAt(&viewMatrix, cameraX, cameraY, cameraZ, // Eye position 0.0f, 0.0f, 0.0f, // Look-at point (origin) 0.0f, 1.0f, 0.0f); // Up vector (Y-axis) // Upload view matrix to RSP gSPMatrix(glistp++, &viewMatrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); } ``` ### Response (N/A - This is a C function, no standard HTTP response) ### Response Example (N/A - This is a C function) ``` -------------------------------- ### Play Sound Effect with alSndpPlay Source: https://context7.com/n64decomp/libreultra/llms.txt Triggers playback of a loaded sound effect using the `alSndpPlay` function. This is intended for one-shot sounds such as explosions, jumps, or UI feedback. The function allows for custom volume, panning, and priority settings. Initialization of the sound effects heap and player is required. ```c #include ALSndPlayer sndPlayer; ALSndPlayer *sndPlayerPtr = &sndPlayer; ALSndId jumpSound, explosionSound, coinSound; void initSoundEffects(void) { ALHeap heap; ALBank *sfxBank; // Initialize heap (shared with sequence player) alHeapInit(&heap, audioHeap, sizeof(audioHeap)); // Create sound player alSndpNew(sndPlayerPtr, &heap); // Set the sound effects bank alSndpSetBank(sndPlayerPtr, sfxBank); // Load sound IDs from bank jumpSound = 0; explosionSound = 1; coinSound = 2; } // Play sound with default settings void playJumpSound(void) { alSndpSetSound(sndPlayerPtr, jumpSound); alSndpPlay(sndPlayerPtr); } // Play sound with custom volume and pan void playExplosion(float posX, float listenerX) { // Calculate panning based on position float delta = posX - listenerX; ALPan pan = AL_PAN_CENTER; if (delta < -50.0f) { pan = AL_PAN_LEFT; } else if (delta > 50.0f) { pan = AL_PAN_RIGHT; } else { pan = AL_PAN_CENTER + (ALPan)(delta * 0.5f); } // Set up sound alSndpSetSound(sndPlayerPtr, explosionSound); alSndpSetVol(sndPlayerPtr, AL_VOL_FULL); alSndpSetPan(sndPlayerPtr, pan); alSndpSetPriority(sndPlayerPtr, AL_MAX_PRIORITY); // Play alSndpPlay(sndPlayerPtr); } // Play sound with distance attenuation void playCoinSound(float distance) { int volume = AL_VOL_FULL; // Attenuate based on distance if (distance > 100.0f) { return; // Too far, don't play } volume = (int)(AL_VOL_FULL * (1.0f - distance / 100.0f)); alSndpSetSound(sndPlayerPtr, coinSound); alSndpSetVol(sndPlayerPtr, volume); alSndpSetPriority(sndPlayerPtr, 64); alSndpPlay(sndPlayerPtr); } ``` -------------------------------- ### Flush Data Cache - C Source: https://context7.com/n64decomp/libreultra/llms.txt Ensures that modified data in the CPU's data cache is written back to main memory before DMA operations. This is critical for data consistency, especially when preparing geometry, display lists, or texture data for transfer to the RCP. It uses the `osWritebackDCache` function. ```c #include typedef struct { Vtx vertices[32]; u16 indices[64]; Mtx transform; } GeometryData; GeometryData *geometryBuffer; void prepareGeometryForDMA(void) { GeometryData *geo = geometryBuffer; // Modify geometry data in cached memory geo->vertices[0].v.ob[0] = 100; // X position geo->vertices[0].v.ob[1] = 50; // Y position geo->vertices[0].v.ob[2] = 0; // Z position // More vertex modifications... // Flush modified data from cache to RAM // before RSP DMA operation osWritebackDCache(geo, sizeof(GeometryData)); // Now safe to DMA to RSP gSPVertex(glistp++, geo->vertices, 32, 0); } // Loading display list via DMA void loadDisplayList(Gfx *displayList, int numCommands) { int size = numCommands * sizeof(Gfx); // Ensure CPU cache is synchronized osWritebackDCache(displayList, size); // Tell RSP to DMA the display list gSPDisplayList(glistp++, displayList); } // Preparing texture data for upload void uploadTexture(u8 *textureData, int width, int height) { int size = width * height * 2; // 16-bit texture // Write back any cached texture modifications osWritebackDCache(textureData, size); // Safe to load into TMEM via DMA gDPLoadTextureBlock(glistp++, textureData, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, 0, G_TX_WRAP, G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); } ``` -------------------------------- ### Generate Rotation Matrix with guRotate Source: https://context7.com/n64decomp/libreultra/llms.txt Creates a rotation matrix around an arbitrary axis using the `guRotate` function. The rotation angle is specified in degrees. This function is useful for applying rotations in 3D graphics. It can be used for single rotations or combined for complex transformations. ```c #include Mtx rotationMatrix; Mtx tempMatrix; void rotateObject(float angle) { // Rotate around Y-axis (vertical) guRotate(&rotationMatrix, angle, // Angle in degrees 0.0f, 1.0f, 0.0f); // Rotation axis (Y) // Apply to rendering pipeline gSPMatrix(glistp++, &rotationMatrix, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); } // Animated spinning object void renderSpinningCube(float gameTime) { Mtx rotX, rotY, rotZ, combined; float angle = gameTime * 0.1f; // Rotate on multiple axes for tumbling effect guRotate(&rotX, angle * 1.0f, 1.0f, 0.0f, 0.0f); // X-axis guRotate(&rotY, angle * 0.7f, 0.0f, 1.0f, 0.0f); // Y-axis guRotate(&rotZ, angle * 0.5f, 0.0f, 0.0f, 1.0f); // Z-axis // Combine rotations guMtxCatF(rotX, rotY, &tempMatrix); guMtxCatF(tempMatrix, rotZ, &combined); gSPMatrix(glistp++, &combined, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); // Draw cube geometry here // ... gSPPopMatrix(glistp++, G_MTX_MODELVIEW); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.