⬆️ ⬇️

Fixed point math in Marmalade SDK

Not so long ago on Habré was the post "Plunge into 3D with the help of Marmalade SDK" , which left me a lot of questions. First of all, this concerned magic hexadecimal numbers that were passed to functions, i.e. fixed point calculations. This topic is described badly enough on the Internet, so I had to experiment. If interested - welcome under cat.



First part. Integer style coordinates


Having many years of experience in commercial game development, I quickly threw a torch (to the origin of coordinates) onto the stage in Maya and exported it with a marmalade plugin. This was followed by a more fascinating question - which integer magic numbers should be set for the view matrix and the perspective matrix. The answer depends primarily on how the model is loaded and how the float coordinates of the maya are converted into integer values. Of course, I first thought about the macro IW_FIXED_FROM_FLOAT, which by the way translates 1.f to 0x1000 (this knowledge is useful for understanding what a normalized vector is, that is, a vector with a length of 0x1000). I will not torture you and tell about your attempts, I will tell the result. When importing, no such macros are applied, but the float is brought to a whole, i.e. just cut everything after the comma. Therefore, we return to Maya and make the torus more ... more, no more, I want awesome accuracy. Well, I did tens of thousands. And it was not there! All broke off (more precisely, cut) a promising matrix. It turns out that IwGxSetFarZNearZ, although it takes int32 as arguments, but allows the maximum zFar to be only 0xffff, imposing restrictions on the size of objects. So, how to choose a balance between sufficient accuracy and limiting zFar? In my opinion, the optimal size of objects will be in the range from 100 to 1000 (it is possible from 1000 to 10,000 for closed spaces). This is in good agreement with the dimension of maya, where the unit is centimeter. We make, for example, a person 180 cm and this will be sufficient accuracy for most scenes. Of course, it’s impossible to model a detailed face with an accuracy of 1 cm, but fortunately (or unfortunately) on mobile platforms at the moment you don’t have to see a person’s face in the scene and at the same time a ship three hundred meters from us.



The second part of Marlezonsky ballet. Integer style corners


Everything is easier with corners. As well as for coordinates, there is an appropriate macro IW_ANGLE_FROM_DEGREES for translating corners into integers. The question immediately arises where problems may appear when using integers for rotation matrices. As a simple test showed, the animation from maya is exported without problems, all turns in float are converted into corresponding matrices with integer values. The only place where you need to remember to transform manually - when transferring the matrix to the shader. By the way, there is no support for shaders in marmalade, you need to write everything yourself.

')

The third part. Criticism (optional reading)


And in conclusion, I want to slightly criticize the post I mentioned at the very beginning of the article. In general, I am grateful to the author for the inspiration to research and write this article.

But it’s still impossible to post a small piece of the program causing tons of questions, and even with the postscript “The code is very simple even for an inexperienced programmer.” Although maybe I'm not quite an inexperienced programmer. The first lines raised questions

CIwSVec3 dd(0xFF, 0xFF, 0xFF); IwGxSetLightDirn(1, &dd); 


Why all the components of the vector 0xFF, and not just (1,1,1)? Is this something conscious or the cost of copy-paste? Can the vector be normalized? By the way, the last letter n in the function name seems to hint ... we read the description and indeed - the vector needs to be transferred normalized. Obviously, the vector (0xFF, 0xFF, 0xFF) is not normalized, I have already mentioned above that the unit in the fixed marmalade system is 0x1000. The length of the vector from the example is clearly less. Add normalization - and voila, the lighting starts to work correctly. By the way, I knowingly exported from Mayi a torus, and not a box - the efforts to create are the same, and on the torus you immediately see problems of lighting, accuracy of coordinates, etc. This is how the lighting changed after correcting this annoying error.





A little further in the text is a mega comment, after which I almost slipped under the table

Also, special attention should be paid to the IwGxSetPerspMul (...) function. This is a kind of setting the degree of the fish-eye effect. If you leave this option by default, your scenes around the edges of the screen will create the impression of viewing the bottom of a bottle through a toilet hole .



The author’s mathematical approach is amazing. The method of scientific poking in all its glory.

Help describes the only parameter of this function as "The distance from the camera to the viewing plane." And what is not FOV - horizontal viewing angle, as all white people ask? Almighty Google suggested what marmalade means by “viewing plane”, which means we have a relatively simple formula for moving FOV to this distance:

  int32 perspMul = IwGxGetDeviceWidth() / (2*tan(fov/2)); IwGxSetPerspMul(perspMul); 


Having a little thought over my brains, I had an assumption as to why not FOV - mobile devices, unlike PCs, can have both portrait and landscape views. Moreover, the gyroscope can switch it in real time. This approach saves us from having to manually handle the change in view and change the perspective matrix each time.



Go ahead ...

  CIwMat view = CIwMat::g_Identity; view.tz = -80; view.ty = 80; view.tx = -60; view.LookAt(view.GetTrans(), CIwVec3(0, 0, 0), -CIwVec3::g_AxisY); 


There is no sensible explanation from the author, although a certain redundancy of the code immediately catches the eye. Why not just write

 view.LookAt(CIwVec3(-60,80,-80), CIwVec3(0, 0, 0), -CIwVec3::g_AxisY); 


I wrote. But that doesn't work. Help said that the function of the LookAt function does not change position, but creates only a rotation matrix. Those. The “translate” component must be installed separately. Here you want to throw a big stone in the garden of marmalade developers - if you call the LookAt function, then you can do its job in the same way as D3DXMatrixLookAt and gluLookAt - we're used to the devil. I am sure that it was not me alone who did not understand for a long time why it was not working. Indeed, what to read help, when for many years you know this feature :)

PS A little messy out, so if you have questions - ask. I am not an expert on marmalade, but I will still try to answer.



Update: forgot to write that for multiplication, division, trigonometric functions and other things you need to apply the corresponding macros IW_FIXED_MUL (IW_FIXED_MUL_SAFE) IW_FIXED_DIV (IW_FIXED_DIV_SAFE), IW_GEOM_COS, IW_GEOM_SIN etc. Safe versions of macros are used for int64 calculations to avoid overflow during calculations. Although, the result of multiplication, of course, can still overflow the buffer. Thanks to habraouzer zigmar for reminding.

Source: https://habr.com/ru/post/123816/



All Articles