Documents the 2 levels of 3D abstraction layers in the game. Below a "quick" write-up about the system.
TL;DR: Easy 3D model loading and drawing API.
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:
Loading a model with textures, animation, and texture animation
Binding them all together
Updating the animations
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:
Easy3DObject giratinaObj;
Easy3DModel giratinaModel;
Easy3DAnim giratinaAnim; // Model Animation
Easy3DAnim giratinaTexAnim; // Texture Animation
NNSFndAllocator allocator; // Needed for Animations
Next we load all of the data:
// 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.
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:
// 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);
Documents the 2 levels of 3D abstraction layers in the game. Below a "quick" write-up about the system.
TL;DR: Easy 3D model loading and drawing API.
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: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 viaEasy3D_Init
(WithEasy3D_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: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: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:
Next we load all of the data:
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.
Now we can actually render the model:
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: