AntonioND / nitro-engine

3D engine for the Nintendo DS
154 stars 12 forks source link

Suggestion: Add some Quaternion support or maybe a NE_ModelDraw function variant or some data access for extra control over 3D model #30

Open rVicio opened 1 month ago

rVicio commented 1 month ago

Hi, I'm trying to rotate a 3D model using quaternions, depending on the camera's rotation, maybe there's already a way for this but I can't find anything in the current NE documentation. The quaternion library I'm using worked really well with the camera by calculating the "to" and "up" transversal for vertical rotation by calculating those vectors new positions and using them th NE_CameraSet, and now I want the same with the model.

I thought it would work by just calculating the camera vectors Euler angles to set the model rotation, however, I can't get it to work properly. After some searching online and asking microsoft copilot, it seems the problem happens because of the glRotate functions used by NE_ModelDraw for the matrix rotation, causing the gimball lock problem (wich made me remeber Fewnity's counter strike DS video, when he tried to implement weapon model rotation and the same happened).

For what I could understand, the way it could work with quaternions properly is by calculating an orientation quaternion, rotating it with the quaterinon rotation functions in the library, transforming the new calculated rotation orientation quaternion to a 4x4 matrix and then applying it to the mesh 4x4 matrix multiplication during the render process.

One way for this could be writing my own render function, but the code won't be able to access some of NE variables/pointers used for control since they are static, and I don't want to duplicate or modifying the NE one because I'm still learning C while making a somewhat simple 3D space game based on Galaxy on Fire 2 (specifically this one since it started as a j2me game back in 2008, and as far as I know, there's nothing like that on nintendo DS, at least in terms of gameplay, mechanics and content).

So, I think a draw function variant that needs a 4x4 matrix as input alongside the model might allow some things like quaternions support just by the matrix multiplication, not that I know how much that), or maybe having some access to some NE data or variables/pointers for more specific things not yet officially implemented in NE.

AntonioND commented 1 week ago

Sorry for the late reply. Yes, this could be done. Maybe I can just add to each NE_Model a pointer to a 4x3 matrix so that users can define the transformation manually if they want, and other models won't have a full matrix in their data. I don't think 4x4 is necessary, right?

rVicio commented 1 week ago

Well, like I mentioned, I'm learnig C while attempting to create a basic GOF2 clone on the Nintendo DS, and for what I can understand about using quaternions for model rotation is what I mentioned, and yes, it seems a 4x4 matrix is needed.

The transformation matrix in libnds is 4x4, and to apply a rotation using quaternion, a "rotation quaternion" is needed to calculate the rotation, then it is translated to a 4x4 matrix which is used in just one matrix multiplication using the graphics matrix functions (like glMultMatrix4x4 in libnds).

Some examples on OpenGL I've seen show more or less the same steps for free 3D rotation.

So I think it would just need to input one 4x4 rotation matrix for rotating the model, I can calculate this matrix on my own with the quaternion library I'm using.

I think it could work this way because I've been reading the code of another 3D DS programming library to get an idea of an implementation of my own. This library is somewhat old by now and incompatible with recent versions of devkitpro and libnds, it's called DS Game Maker library (dsgm), which does support quaternions, but the way the functions are structures for create a game are quite confusing, at least, for me.

This library implements free rotation by creating a 4x4 matrix from a calculated rotation quaternion and using this matrix in glMultMatrix4x4 during the render process, that's what I can see in the example's code. If you are curious about it, the library is in github: https://github.com/CTurt/dsgmLib

The files with the functions are DSGM_quaternion.c and DSGM_3D.c, and the example is Galaxy (supposedly, a mario galaxy's gravity demo, the nds roms with 3d graphics doesn't work in my DSi XL, they might through a flashcard).

AntonioND commented 1 week ago

A 4x4 matrix may not be needed. A quaternion is basically like a 3x3 matrix plus a translation vector, right? I think a 4x3 matrix may be able to do the trick and use less memory. https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm

And yeah, I've just checked the code, and you only need a 4x3 matrix: https://github.com/CTurt/dsgmLib/blob/4e57cf65f93c8c585ddc558945555105fc3c009d/source/DSGM_quaternion.c#L162

Are you using that library and need compatibility for it? Or are you just using it as an example?

AntonioND commented 1 week ago

I've pushed two commits to add support for manually-defined matrices:

https://github.com/AntonioND/nitro-engine/commit/4642bdf982483121305aea3461fd849f11cd3997

https://github.com/AntonioND/nitro-engine/commit/fe6e4a0dcc9f6b925d6abd8e1cb38975297d7036

Would this work for you? There's time to change it.

rVicio commented 6 days ago

Yeah, you're right, a 4x3 matrix should be enough, although I just want to specify the matrix for the rotation only, since it's the only one with a problem (gimbal lock) because of the way it is calculated, but if it is easier for you to implement it this way, then I'm fine with it, I'll just include the scale and the translation in the matrix in my code. Plus, this might be useful for some people, from what I've seen, quaternions are specially used in animation interpolations.

About the dsgm lib, I'm not actually "using" it, I was just reading it to get a better idea of implementing quaternions on my own. Though some years ago I tried to use it but couldn't beacuse of devkitpro compatibility issues, but now I'm glad I couldn't, I'm not familiar in writing code the same way as those examples, you're library it way more clear and straight forward at least for me, and also you have your tools for converting asssets file formats.

AntonioND commented 6 days ago

To be fair, part of the reason why I've picked 4x3 is that I was thinking about storing the matrix internally instead of doing all the operations every time the model is rendered. If the current system works for you, I think I'll just keep it like this (and probably modify the inner workings of the engine in a way that doesn't affect the function prototypes). It shouldn't be needed to calculate a rotation matrix every time a model is drawn if it hasn't changed...

Ok, I understand. And yes, I also use quaternions to animate models in NE, I definitely agree that they are useful: https://github.com/AntonioND/nitro-engine/blob/fe6e4a0dcc9f6b925d6abd8e1cb38975297d7036/source/dsma/dsma.c#L16-L19