mirror of
https://github.com/pret/pokeplatinum.git
synced 2026-03-22 18:26:14 -05:00
123 lines
5.2 KiB
Markdown
123 lines
5.2 KiB
Markdown
## 3D Rendering
|
|
The following is a brief overview and introduction to the 3D rendering system in Pokemon Platinum and how to use it.
|
|
|
|
## Abstraction Layers
|
|
The lowest level 3D is interacting directly with the hardware via NitroSDK. NitroSystem provides a slight abstraction over this with `G3D`.
|
|
|
|
The game has 2 additional layers of abstraction over this:
|
|
### 1 - Easy3D
|
|
The first layer of abstraction is the Easy3D System (`easy3d.h`). Which provides basic functionality for:
|
|
- Loading Models and Textures
|
|
- Binding textures to models
|
|
- Drawing models with any transform
|
|
- Setting up and releasing a 3D graphics state
|
|
|
|
To elaborate on the last point: Before using any 3D graphics, a "3D Graphics State" must be initialized. The struct for this is currently called `GenericPointerData`. The Easy3D system provides an easy method to set up a simple gfx state via `Easy3D_Init` (With `Easy3D_Shutdown` as its counterpart), which works for most scenarios.
|
|
|
|
### 2 - Easy3DObject
|
|
The second layer of abstraction is `Easy3DObject` (`easy3d_object.h`). This API provides a streamlined interface for:
|
|
- Loading models, textures, and animations
|
|
- Binding a texture to a model
|
|
- Binding one or more animations to a model (both bone animations and texture animations)
|
|
- Updating the objects transform (the object itself keeps track of that)
|
|
- Updating animation state
|
|
- Drawing objects
|
|
- Cleanup
|
|
|
|
One thing to keep in mind is that this system by itself does not establish a 3D GFX State.
|
|
This system is also not used everywhere. In a lot of places Easy3D is used directly instead of this object oriented interface.
|
|
|
|
## Example
|
|
The following is an example for using the `Easy3DObject` API for:
|
|
1. Loading a model with textures, animation, and texture animation
|
|
2. Binding them all together
|
|
3. Updating the animations
|
|
4. Drawing the model
|
|
|
|
I will be using the Giratina model from the title screen for this.
|
|
|
|
### Loading the data
|
|
The first thing needed is some static storage to hold our data:
|
|
```c
|
|
Easy3DObject giratinaObj;
|
|
Easy3DModel giratinaModel;
|
|
Easy3DAnim giratinaAnim; // Model Animation
|
|
Easy3DAnim giratinaTexAnim; // Texture Animation
|
|
NNSFndAllocator allocator; // Needed for Animations
|
|
```
|
|
Next we load all of the data:
|
|
```c
|
|
// Open the title NARC
|
|
NARC *narc = NARC_ctor(NARC_INDEX_DEMO__TITLE__TITLEDEMO, HEAP_ID_FIELD);
|
|
|
|
// Load the model from the title screen NARC. Member index 1 is the model data.
|
|
// There is also Easy3DModel_Load which takes a NARC index and a member index.
|
|
Easy3DModel_LoadFrom(&giratinaModel, narc, 1, HEAP_ID_FIELD);
|
|
Easy3DObject_Init(&giratinaObj, &giratinaModel);
|
|
|
|
// Initialize the Allocator used by the animations
|
|
Heap_FndInitAllocatorForExpHeap(&allocator, HEAP_ID_FIELD, 4);
|
|
|
|
// Load the model animation with member index 2.
|
|
Easy3DAnim_LoadFrom(&giratinaModelAnim, &giratinaModel, narc, 2, HEAP_ID_FIELD, &allocator);
|
|
// Bind the animation to the object
|
|
Easy3DObject_AddAnim(&giratinaObj, &giratinaModelAnim);
|
|
|
|
// Do the same for the texture animation
|
|
Easy3DAnim_LoadFrom(&giratinaTexAnim, &giratinaModel, narc, 0, HEAP_ID_FIELD, &allocator);
|
|
Easy3DObject_AddAnim(&giratinaObj, &giratinaTexAnim);
|
|
|
|
NARC_dtor(narc);
|
|
```
|
|
This is all that needs to be done in terms of setup, now the model is ready to draw (provided a 3D GFX State has been set up).
|
|
|
|
### Drawing the Model
|
|
Before drawing there's a few things that should be configured on the object, one of them being the position of the model obviously. For simplicity's sake I will just set the models position to the player's position.
|
|
```c
|
|
const VecFx32 *pos = PlayerAvatar_PosVector(fieldSystem->playerAvatar);
|
|
Easy3DObject_SetPosition(&giratinaObj, pos->x, pos->y, pos->z);
|
|
|
|
// Make sure the model actually gets rendered
|
|
Easy3DObject_SetVisibility(&giratinaObj, TRUE);
|
|
|
|
// The model is pretty big so scale it to half its size
|
|
Easy3DObject_SetScale(&giratinaObj, FX32_CONST(0.5), FX32_CONST(0.5), FX32_CONST(0.5));
|
|
```
|
|
Now we can actually render the model:
|
|
```c
|
|
// Update the animations
|
|
// Here we advance the animation by one frame.
|
|
// There is also Easy3DAnim_Update which does not loop the animation
|
|
Easy3DAnim_UpdateLooped(&giratinaModelAnim, FX32_ONE);
|
|
Easy3DAnim_UpdateLooped(&giratinaTexAnim, FX32_ONE);
|
|
|
|
// Draw the model
|
|
Easy3DObject_Draw(&giratinaObj);
|
|
```
|
|
All of that results in the following:
|
|
|
|
https://github.com/pret/pokeplatinum/assets/60443001/ba162c62-e64a-4cd6-850f-414774f19bfd
|
|
|
|
Obviously the rotation and resizing is not outlined above. The code for this is the following:
|
|
```c
|
|
u16 angle = Easy3DObject_GetRotation(&giratinaObj, ROTATION_AXIS_Y);
|
|
angle = (angle + 100) % 0xFFFF;
|
|
Easy3DObject_SetRotation(&giratinaObj, angle, ROTATION_AXIS_Y);
|
|
|
|
// Resize using L and R
|
|
if (gCoreSys.heldKeys & PAD_BUTTON_R) {
|
|
VecFx32 scale;
|
|
Easy3DObject_GetScale(&giratinaObj, &scale.x, &scale.y, &scale.z);
|
|
scale.x = FX_Mul(scale.x, FX32_CONST(1.01));
|
|
scale.y = FX_Mul(scale.y, FX32_CONST(1.01));
|
|
scale.z = FX_Mul(scale.z, FX32_CONST(1.01));
|
|
Easy3DObject_SetScale(&giratinaObj, scale.x, scale.y, scale.z);
|
|
} else if (gCoreSys.heldKeys & PAD_BUTTON_L) {
|
|
VecFx32 scale;
|
|
Easy3DObject_GetScale(&giratinaObj, &scale.x, &scale.y, &scale.z);
|
|
scale.x = FX_Mul(scale.x, FX32_CONST(0.99));
|
|
scale.y = FX_Mul(scale.y, FX32_CONST(0.99));
|
|
scale.z = FX_Mul(scale.z, FX32_CONST(0.99));
|
|
Easy3DObject_SetScale(&giratinaObj, scale.x, scale.y, scale.z);
|
|
}
|
|
``` |