Fixed Point Math in the Marmalade SDK

    Not so long ago on a habr there was a post "We are immersed in 3D by means of Marmalade SDK" which left me a lot of questions. First of all, this concerned magic hexadecimal numbers, which were transferred to functions, i.e. fixed point computing. This topic is poorly described on the Internet, so I had to experiment. If interested - welcome to cat.

    First part. Integer style coordinates

    Having many years of experience in commercial game development, I quickly threw a torus (to the origin) in May and exported it with a marmalade plug-in. This was followed by a more fascinating question - which magic integer numbers to set the view matrix and the perspective matrix. The answer depends primarily on how the model is loaded and how the Maya float coordinates are converted to integer values. Of course, at first I 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, i.e. a vector with a length of 0x1000). I will not torment you and talk about my attempts, I will tell you the result. When importing, no such macros are applied, but the float is simply reduced to the whole, i.e. just cut off everything after the decimal point. Therefore, we return to Maya and make a larger torus ... even more, no more, I want awesome accuracy. Well, I did tens of thousands. But it wasn’t here! All broken off (or rather cut off) a promising matrix. It turns out that although IwGxSetFarZNearZ accepts int32 as arguments, it only allows maximum zFar 0xffff, imposing restrictions on the size of objects. So, how do you choose the balance between sufficient accuracy and zFar limitation? In my opinion, the optimal size of the objects will be in the range from 100 to 1000 (it is possible from 1000 to 10000 for enclosed spaces). This is in good agreement with the dimension of maya, where the unit is centimeter. We make, for example, a person of 180 cm and this will be sufficient accuracy for most scenes. Of course, a detailed face cannot be modeled with an accuracy of 1 cm,

    The second part of the Marlezon Ballet. Integer-style corners

    It’s easier with corners. As with coordinates, there is a corresponding macro, IW_ANGLE_FROM_DEGREES, for converting angles to integers. The question immediately arises where problems may arise when using integers for rotation matrices. As a simple test showed, animation from Maya is exported without problems, all turns in the float are converted to the corresponding matrices with integer values. The only place where you must remember to convert manually is 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)

    And in conclusion, I want to criticize a little the post I mentioned at the very beginning of the article. In general, I am grateful to the author for the inspiration for research and writing this article.
    But still you can’t post a small piece of the program that causes 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 are all the components of the vector 0xFF, and not just (1,1,1)? Is it something conscious or copy-paste costs? Maybe the vector should be normalized? By the way, the last letter n in the name of the function as it hints ... we read the description and really - the vector needs to be transferred normalized. Obviously, the vector (0xFF, 0xFF, 0xFF) is not normalized, I 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, it was not in vain that I exported a torus from Maya, not a box - the efforts to create are the same, and on the torus I immediately see problems of lighting, coordinate accuracy, etc. Here's how the lighting changed after fixing this annoying bug.


    A little further along the text is a mega comment, after which I almost slipped under the table
    You should also pay special attention to the function IwGxSetPerspMul (...). This is a kind of setting the degree of the fisheye effect. If you leave this parameter as default, then your scenes along the edges of the screen will give the impression of viewing the bottom of the bottle through the toilet hole .

    The mathematical approach of the author is amazing. The method of scientific poking in all its glory.
    The help describes the only parameter of this function as “The distance from the camera to the viewing plane”. And why 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 translating FOV into this distance:
        int32 perspMul =  IwGxGetDeviceWidth() / (2*tan(fov/2));
        IwGxSetPerspMul(perspMul);
    

    With a little thought in my brain, I had an assumption 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 eliminates the need to manually process the view change and change the perspective matrix each time.

    Going further ...
    		CIwMat view = CIwMat::g_Identity;
    		view.t.z = -80;
    		view.t.y = 80;
    		view.t.x = -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 is immediately evident. 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 LookAt does not change position, but creates only a rotation matrix. Those. the translate component needs to be installed separately. Here I want to throw a big stone in the garden of marmalade developers - if you call the LookAt function, then you have to make it work similarly to D3DXMatrixLookAt and gluLookAt - we are used to hell. I am sure that it was not I who did not understand for a long time why it did not work. Indeed, why read the help when you have known this feature for many years :)
    PS It turned out a bit messy, so if you have questions, ask. I'm not a specialist in marmalade, but still try to answer.

    Update:I forgot to write that for multiplication, division, trigonometric functions and other things, you need to use 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 the zigmar habrayuzer for the reminder.

    Also popular now: