iOS: GLKit matrix multiplication is broken as far as I can tell - ios

Here's the source code. ANSWER is the correct answer, RESULT is the actual result.
Am I blind, or is it calculating a -1 for entry 33 when it should be a 1?
Here's the code:
GLKMatrix4 a = GLKMatrix4Make(-1.000000, 0.000000, 0.000000, 0.000000,
0.000000, 1.000000, 0.000000, 0.000000,
0.000000, 0.000000, -1.000000, 0.000000,
0.000000, 0.000000, 0.000000, 1.000000);
GLKMatrix4 b = GLKMatrix4Make(1.000000, 0.000000, 0.000000, 0.000000,
0.000000, 1.000000, 0.000000, -1.000000,
0.000000, 0.000000, 1.000000, -1.000000,
0.000000, 0.000000, 0.000000, 1.000000);
GLKMatrix4 ANSWER = GLKMatrix4Make(-1.000000, 0.000000, 0.000000, 0.000000,
0.000000, 1.000000, 0.000000, -1.000000,
0.000000, 0.000000, -1.000000, 1.000000,
0.000000, 0.000000, 0.000000, 1.000000);
NSLog(#"##################################################");
GLKMatrix4 RESULT = GLKMatrix4Multiply(a,b);
NSLog(#"Result:");
NSLog(#" %f %f %f %f",RESULT.m00,RESULT.m01,RESULT.m02,RESULT.m03);
NSLog(#" %f %f %f %f",RESULT.m10,RESULT.m11,RESULT.m12,RESULT.m13);
NSLog(#" %f %f %f %f",RESULT.m20,RESULT.m21,RESULT.m22,RESULT.m23);
NSLog(#" %f %f %f %f",RESULT.m30,RESULT.m31,RESULT.m32,RESULT.m33);
NSLog(#"Answer:");
NSLog(#" %f %f %f %f",ANSWER.m00,ANSWER.m01,ANSWER.m02,ANSWER.m03);
NSLog(#" %f %f %f %f",ANSWER.m10,ANSWER.m11,ANSWER.m12,ANSWER.m13);
NSLog(#" %f %f %f %f",ANSWER.m20,ANSWER.m21,ANSWER.m22,ANSWER.m23);
NSLog(#" %f %f %f %f",ANSWER.m30,ANSWER.m31,ANSWER.m32,ANSWER.m33);
NSLog(#"##################################################");
Here's the output:
##################################################
Result:
-1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 -1.000000
0.000000 0.000000 -1.000000 -1.000000
0.000000 0.000000 0.000000 1.000000
Answer:
-1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 -1.000000
0.000000 0.000000 -1.000000 1.000000
0.000000 0.000000 0.000000 1.000000
##################################################
I've spent the last 5 hours trying to debug some OpenGL code only to find this is the problem. I must be missing something, surely. Can anyone spot what's going on, or verify this shouldn't be happening?

Um, I got the same result as your "RESULT" matrix.
I did the matrix multiplication with pen and paper, following these rules:
http://www.mathsisfun.com/algebra/matrix-multiplying.html
This is how I did it using your two matrix a and b:
OK, I think I know where you went wrong, you must have listed the numbers in the wrong position:
The way you list the number in code is horizontal e.g Coordinate(x,y,z) but in a Matrix, these numbers get moved to vertical positions.
So for example, [ X Y Z W ] should become vertically:
Now IF we were to list the your numbers in the wrong position and multiply Matrix a with Matrix b, we get the same answer as your "ANSWER" matrix:

Little bit more details on this.
OpenGL ES suppose matrices have column major order. So element order how OpenGL ES reads matrix is:
m00 m10 m20 m30
m01 m11 m21 m31
m02 m12 m22 m32
m03 m13 m23 m33
Of course Apple's GLKit Math Utilities are also built according to this column majored order. And it may be little bit confusing while you realise it. :)

Related

Eigen matrix product doesn't work correctly in ORB-SLAM3 in release mode on windows

The whole picture of the problem is as below:
I cloned the ORB-SLAM3 from https://github.com/rexdsp/ORB_SLAM3_Windows. It can be built and works well. Then I tried to evaluate its accuracy with EuRoC MH_01_easy, but found it was at least 10 times worse in release mode than the accuracy reported in the paper. After debugging several days, I found the reason was in the function below:
void EdgeSE3ProjectXYZ::linearizeOplus() {
g2o::VertexSE3Expmap * vj = static_cast<g2o::VertexSE3Expmap *>(_vertices[1]);
g2o::SE3Quat T(vj->estimate());
g2o::VertexSBAPointXYZ* vi = static_cast<g2o::VertexSBAPointXYZ*>(_vertices[0]);
Eigen::Vector3d xyz = vi->estimate();
Eigen::Vector3d xyz_trans = T.map(xyz);
double x = xyz_trans[0];
double y = xyz_trans[1];
double z = xyz_trans[2];
auto projectJac = -pCamera->projectJac(xyz_trans);
_jacobianOplusXi = projectJac * T.rotation().toRotationMatrix();
double* buf = (double*)_jacobianOplusXi.data();
Eigen::Matrix<double,3,6> SE3deriv;
SE3deriv << 0.f, z, -y, 1.f, 0.f, 0.f,
-z , 0.f, x, 0.f, 1.f, 0.f,
y , -x , 0.f, 0.f, 0.f, 1.f;
buf = (double*)SE3deriv.data();
printf("S: %f %f %f %f %f %f\n", buf[0], buf[3], buf[6], buf[9], buf[12], buf[15]);
printf("S: %f %f %f %f %f %f\n", buf[1], buf[4], buf[7], buf[10], buf[13], buf[16]);
printf("S: %f %f %f %f %f %f\n", buf[2], buf[5], buf[8], buf[11], buf[14], buf[17]);
_jacobianOplusXj = projectJac * SE3deriv;
buf = (double*)_jacobianOplusXj.data();
printf("j: %f %f %f %f %f %f\n", buf[0], buf[2], buf[4], buf[6], buf[8], buf[10]);
printf("j: %f %f %f %f %f %f\n", buf[1], buf[3], buf[5], buf[7], buf[9], buf[11]);
}
The function does not work correctly in release mode, as what is shown in the log: (Jac is the content of projectJac printed in other function)
Jac: 6.586051 0.000000 5.789042
Jac: 0.000000 6.566550 -0.414389
S: 0.000000 69.640217 -4.394719 1.000000 0.000000 0.000000
S: -69.640217 0.000000 -61.212726 0.000000 1.000000 0.000000
S: 4.394719 61.212726 0.000000 0.000000 0.000000 1.000000
j: 25.478939 354.888530 0.000000 -0.000000 -0.000000 5.797627
j: -2.092870 -29.150968 0.000000 -0.000000 -0.000000 -0.476224
The whole system works correctly in Debug mode,the accuracy is similar to the results reported in the paper. And the log of the function above is as follows:
Jac: 6.586051 0.000000 5.789042
Jac: 0.000000 6.566550 -0.414389
S: 0.000000 69.640217 -4.394719 1.000000 0.000000 0.000000
S: -69.640217 0.000000 -61.212726 0.000000 1.000000 0.000000
S: 4.394719 61.212726 0.000000 0.000000 0.000000 1.000000
j: -25.441210 -813.017008 28.943840 -6.586051 -0.000000 -5.789042
j: 459.117113 25.365882 401.956448 0.000000 -6.566550 0.414389
Have you met this kind of problem before? Could you tell me the reason of this problem? I guess it is caused by compiler settings. Could anyone tell me how to solve this problem?
Thanks a lot.
Hi, I changed the declaration of projectJac and the whole system works correctly. My changes are as follows:
original:
auto projectJac = -pCamera->projectJac(xyz_trans);
modification:
Eigen::Matrix<double, 2, 3> projectJac = -pCamera->projectJac(xyz_trans);
The definition of function projectJac is as follows:
Eigen::Matrix<double, 2, 3> Pinhole::projectJac(const Eigen::Vector3d &v3D) {
Eigen::Matrix<double, 2, 3> Jac;
Jac(0, 0) = mvParameters[0] / v3D[2];
Jac(0, 1) = 0.f;
Jac(0, 2) = -mvParameters[0] * v3D[0] / (v3D[2] * v3D[2]);
Jac(1, 0) = 0.f;
Jac(1, 1) = mvParameters[1] / v3D[2];
Jac(1, 2) = -mvParameters[1] * v3D[1] / (v3D[2] * v3D[2]);
printf("v3D: %f %f %f\n", v3D[0], v3D[1], v3D[2]);
printf("Jac: %f %f %f %f %f %f\n", Jac(0, 0), Jac(0, 1), Jac(0, 2), Jac(1, 0), Jac(1, 1), Jac(1, 2));
return Jac;
}
Now it is clear the bug is caused by "auto". Could you tell me what is reason for this? How should I changed the settings in Visual Studio 2019 to make "auto" works correctly? Thanks a lot.

Conjugate rotation transformation in OpenCV

I am trying to apply a certain rotation to an image, but it doesn't work as expected. The rotation I have is:
[0.109285 0.527975 0.000000
-0.527975 0.109285 0.000000
0.000000 0.000000 1.000000]
Which should be a rotation of ~78 degrees around the camera center (or the Z axis if you prefer).
To build a homography, as there is no translation component, I use the formula: K * R * K^-1 (infinite homography).
The code I use to transform the image (320x240) is:
cv::warpPerspective(image1, image2, K * R * K.inv(), image1.size());
where K is:
[276.666667 0.000000 160.000000
0.000000 276.666667 120.000000
0.000000 0.000000 1.000000]
The resulting matrix from K * R * K.inv() is:
[0.109285 0.527975 79.157461
-0.527975 0.109285 191.361865
0.000000 0.000000 1.000000]
The result should just be a rotation of the image, but the image gets "zoomed out" like this:
What am I doing wrong?
Apparently my rotation matrix was wrong.

Perform a FFT of a signal in Objective-C and compare results with MATLAB

I'm trying to perform the FFT of a signal in Objective-C.
The signal is composed of 180 samples. The signal is the following:
float testFFT [180] = { 0.0000000000, 0.0000432091, 0.0001739833, 0.0003786624, 0.0006653523, 0.0010579729, 0.0015538626, 0.0022084275, 0.0029233105, 0.0037457265, 0.0048239902, 0.0061722184, 0.0074678478, 0.0019116795, -0.0216427371, -0.0583566576, -0.0879606530, -0.0966484919, -0.0844414756, -0.0587274097, -0.0275895316, 0.0020122994, 0.0238022581, 0.0301328432, 0.0223602522, 0.0108049158, 0.0026056657, 0.0015842463, 0.0078394171, 0.0178532675, 0.0289764665, 0.0387034491, 0.0457277447, 0.0503653251, 0.0511394553, 0.0495931208, 0.0491546877, 0.0486858748, 0.0486131087, 0.0533984452, 0.0587649047, 0.0659925416, 0.0777209774, 0.0856716558, 0.0555341654, -0.1010383219, -0.3626580238, -0.5730977058, -0.6356759071, -0.5587731600, -0.4057232738, -0.2299835384, -0.0666809455, 0.0577908531, 0.1170516908, 0.1059532985, 0.0634752810, 0.0284998771, 0.0149525786, 0.0262031760, 0.0470281616, 0.0667090788, 0.0911477804, 0.1096955463, 0.1211226359, 0.1271539181, 0.1249239892, 0.1236920506, 0.1237676740, 0.1250490546, 0.1243178397, 0.1247144639, 0.1260439157, 0.1231973395, 0.1264088154, 0.1277698576, -0.0030977963, -0.3769126236, -0.8328822851, -1.1014552116, -1.0855975151, -0.8706340790, -0.5911422372, -0.3257140219, -0.0960753635, 0.0647964701, 0.1323186159, 0.1257916987, 0.0897259787, 0.0630174652, 0.0530946776, 0.0599082038, 0.0814570710, 0.1065363511, 0.1312004477, 0.1502322406, 0.1570600569, 0.1507443786, 0.1410262287, 0.1309887618, 0.1199141294, 0.1154099405, 0.1160193905, 0.1228033155, 0.1373534203, 0.1516684294, 0.1679655612, 0.1372354180, -0.1066413969, -0.5384752750, -0.8941160440, -0.9979212284, -0.8646196723, -0.6145014763, -0.3477802575, -0.1164548695, 0.0449491963, 0.1089080572, 0.0893236622, 0.0330192894, -0.0108435927, -0.0175555795, 0.0047061597, 0.0339520164, 0.0558914244, 0.0686639696, 0.0742127448, 0.0779310316, 0.0802902952, 0.0771160051, 0.0714451671, 0.0660981461, 0.0637993589, 0.0645155609, 0.0667568296, 0.0722958520, 0.0757482499, 0.0236506276, -0.1267153770, -0.3023732007, -0.3952195346, -0.3767756522, -0.2849329114, -0.1723687500, -0.0716818050, 0.0028261179, 0.0375415571, 0.0343864709, 0.0144051891, -0.0021018211, -0.0056255818, 0.0027398649, 0.0138968918, 0.0204720702, 0.0226374995, 0.0215674732, 0.0184285343, 0.0154026123, 0.0144131510, 0.0145750465, 0.0143866902, 0.0138069429, 0.0127558541, 0.0114914598, 0.0105097489, 0.0059011118, -0.0082570817, -0.0260418169, -0.0354200974, -0.0332562923, -0.0243141986, -0.0146232471, -0.0073889960, -0.0028967261, -0.0005872814, 0.0001664309, 0.0001607906, 0.0000362319, 0.0000018391, 0.0000000001};
In my project I've imported the Accelerate Framework and the code I used to perform the FFT is the following :
// -- Number of Sample
int numSamples = 256; // i use 256 because if i use 128 i don't considered 52 values
// -- Init FFT
// - Setup the length
vDSP_Length log2n = log2f(numSamples);
FFTSetup fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);
int nOver2 = numSamples/2;
// - Define complex buffer
COMPLEX_SPLIT A;
A.realp = (float *) malloc(nOver2*sizeof(float));
A.imagp = (float *) malloc(nOver2*sizeof(float));
// - Pack samples:
vDSP_ctoz((COMPLEX*)testFFT, 2, &A, 1, numSamples/2);
// -- Run FFT
vDSP_fft_zrip(fftSetup, &A, 1, log2n, FFT_FORWARD);
// -- Convert COMPLEX_SPLIT A result to magnitudes
float amp[256];
amp[0] = A.realp[0]/(numSamples*2);
for(int i=1; i<numSamples; i++) {
amp[i]=sqrt(A.realp[i]*A.realp[i]+A.imagp[i]*A.imagp[i]);
}
Then my output magnitude vector is :
gainFFTTest = [ -0.022274, 8.984702, 4.144551, 0.809363, 0.941445, 2.347171, 7.364040, 16.668034, 23.730299, 21.198763, 11.301848, 3.042607, 1.467086, 2.339780, 4.816690, 10.835640, 17.146097, 17.648199, 11.441965, 4.918188, 2.744555, 2.347548, 3.812189, 7.538163, 12.398130, 14.082799, 10.534361, 5.591731, 3.525628, 2.503175, 2.460673, 3.326554, 5.311335, 6.440503, 5.227306, 2.916733, 1.815048, 1.607442, 1.587724, 1.587670, 2.045040, 2.519320, 2.300229, 1.566618, 0.928839, 0.690373, 0.905959, 1.130702, 1.337159, 1.626698, 1.553285, 1.114764, 0.753150, 0.591139, 0.653807, 0.852154, 0.836321, 0.835025, 0.851975, 0.664267, 0.443333, 0.324886, 0.312793, 0.420463, 0.429140, 0.355254, 0.340838, 0.290471, 0.214697, 0.174454, 0.153486, 0.190136, 0.208401, 0.164705, 0.138394, 0.115397, 0.075669, 0.065181, 0.077329, 0.110584, 0.143098, 0.131150, 0.086918, 0.063639, 0.051252, 0.064340, 0.077155, 0.072525, 0.080241, 0.088438, 0.070221, 0.051208, 0.040207, 0.028501, 0.025500, 0.029908, 0.042129, 0.045339, 0.033957, 0.019551, 0.015710, 0.025570, 0.028711, 0.017500, 0.006169, 0.013390, 0.013368, 0.007762, 0.005007, 0.008410, 0.010856, 0.009631, 0.012508, 0.018056, 0.019384, 0.014050, 0.006475, 0.005373, 0.006383, 0.006624, 0.006779, 0.006790, 0.005996, 0.004189, 0.002242, 0.001977, 0.002130, 0.001396, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000];
Often i obtained NAN i INF values why ???
Then I put this in MATLAB (to check the result ) and perform :
plot(gainFFTTest)
and, after I zoomed in as much, I see this plot :
After this i Put the testFFT array in MATLAB with this command:
testFFT = [ 0.0000000000, 0.0000432091, 0.0001739833, 0.0003786624, 0.0006653523, 0.0010579729, 0.0015538626, 0.0022084275, 0.0029233105, 0.0037457265, 0.0048239902, 0.0061722184, 0.0074678478, 0.0019116795, -0.0216427371, -0.0583566576, -0.0879606530, -0.0966484919, -0.0844414756, -0.0587274097, -0.0275895316, 0.0020122994, 0.0238022581, 0.0301328432, 0.0223602522, 0.0108049158, 0.0026056657, 0.0015842463, 0.0078394171, 0.0178532675, 0.0289764665, 0.0387034491, 0.0457277447, 0.0503653251, 0.0511394553, 0.0495931208, 0.0491546877, 0.0486858748, 0.0486131087, 0.0533984452, 0.0587649047, 0.0659925416, 0.0777209774, 0.0856716558, 0.0555341654, -0.1010383219, -0.3626580238, -0.5730977058, -0.6356759071, -0.5587731600, -0.4057232738, -0.2299835384, -0.0666809455, 0.0577908531, 0.1170516908, 0.1059532985, 0.0634752810, 0.0284998771, 0.0149525786, 0.0262031760, 0.0470281616, 0.0667090788, 0.0911477804, 0.1096955463, 0.1211226359, 0.1271539181, 0.1249239892, 0.1236920506, 0.1237676740, 0.1250490546, 0.1243178397, 0.1247144639, 0.1260439157, 0.1231973395, 0.1264088154, 0.1277698576, -0.0030977963, -0.3769126236, -0.8328822851, -1.1014552116, -1.0855975151, -0.8706340790, -0.5911422372, -0.3257140219, -0.0960753635, 0.0647964701, 0.1323186159, 0.1257916987, 0.0897259787, 0.0630174652, 0.0530946776, 0.0599082038, 0.0814570710, 0.1065363511, 0.1312004477, 0.1502322406, 0.1570600569, 0.1507443786, 0.1410262287, 0.1309887618, 0.1199141294, 0.1154099405, 0.1160193905, 0.1228033155, 0.1373534203, 0.1516684294, 0.1679655612, 0.1372354180, -0.1066413969, -0.5384752750, -0.8941160440, -0.9979212284, -0.8646196723, -0.6145014763, -0.3477802575, -0.1164548695, 0.0449491963, 0.1089080572, 0.0893236622, 0.0330192894, -0.0108435927, -0.0175555795, 0.0047061597, 0.0339520164, 0.0558914244, 0.0686639696, 0.0742127448, 0.0779310316, 0.0802902952, 0.0771160051, 0.0714451671, 0.0660981461, 0.0637993589, 0.0645155609, 0.0667568296, 0.0722958520, 0.0757482499, 0.0236506276, -0.1267153770, -0.3023732007, -0.3952195346, -0.3767756522, -0.2849329114, -0.1723687500, -0.0716818050, 0.0028261179, 0.0375415571, 0.0343864709, 0.0144051891, -0.0021018211, -0.0056255818, 0.0027398649, 0.0138968918, 0.0204720702, 0.0226374995, 0.0215674732, 0.0184285343, 0.0154026123, 0.0144131510, 0.0145750465, 0.0143866902, 0.0138069429, 0.0127558541, 0.0114914598, 0.0105097489, 0.0059011118, -0.0082570817, -0.0260418169, -0.0354200974, -0.0332562923, -0.0243141986, -0.0146232471, -0.0073889960, -0.0028967261, -0.0005872814, 0.0001664309, 0.0001607906, 0.0000362319, 0.0000018391, 0.0000000001];
Then i perform the FFT and plot the gain :
gain = abs(fft(testFFT));
plot(gain)
And the plot now is correct :
Where am I wrong ?? how can I get the same results in Objective-C?
Please help me.
Your Objective-C code does not match the Matlab code:
There is no windowing in the matlab code. In addition, the test data you provided seems to be already multiplied by a window function.
You seem to use an Radix2 implementation in the Objective-C code. Matlab does not use Radix2 if the length of the signal does not match a power of 2. Refer to http://mathworks.com/help/matlab/ref/fft.html , section "algorithm" for details.
Your Objective-C code seems strange. Radix2 is not defined for non power of 2 signals. The number of samples supplied (180) does not match numSamples. What are the implications of this? Does the algorithm fill the remaining part of the signal with zeros (zero-padding), or will it just read the memory following your array?
I Resolved this issue by putting the NAN and INF value at zero and by using 256 samples instead 180.

Using projection matrix on GLKMatrixStack

Given this setup:
GLKMatrixStackRef transformHierarchy = GLKMatrixStackCreate (kCFAllocatorDefault);
float aspect = (float)self.drawableWidth / (float)self.drawableHeight;
float fov = PI * 0.125;
float height = 10.0f;
float cameraOffset = -height / (2.0f * tanf (fov / 2.0f));
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective (fov, aspect, 0.01f, 1000.0f);
GLKMatrix4 T = GLKMatrix4MakeTranslation (0.0f, 0.0f, cameraOffset);
I would expect these two options to create an identical matrix on the top of the stack. Option A:
GLKMatrixStackLoadMatrix4 (transformHierarchy, projectionMatrix);
GLKMatrixStackTranslate (transformHierarchy, 0.0f, 0.0f, cameraOffset);
Option B:
GLKMatrixStackLoadMatrix4 (transformHierarchy, GLKMatrix4Multiply (projectionMatrix, T));
Option A gives me a black screen:
3.351560 0.000000 0.000000 0.000000
0.000000 5.027339 0.000000 0.000000
0.000000 0.000000 -1.000020 25.117201
0.000000 0.000000 -1.000000 0.000000
Option B (otherwise identical matrix but with a non-zero m44) gives me the visual result I expect:
3.351560 0.000000 0.000000 0.000000
0.000000 5.027339 0.000000 0.000000
0.000000 0.000000 -1.000020 25.117201
0.000000 0.000000 -1.000000 25.136698
So I've found a solution to my black screen, but the result using GLKMatrixStackTranslate() surprises me. Am I missing something here? Is it not considered a good practice to put the projection + world matrix at the bottom of the stack (to be followed by model transforms)?
UPDATE
Corrected mistake in my cameraOffset math and updated matrix output to match, although it has no effect on the problem as described. Also for clarity I transposed the matrix values (copy/pasted from xcode, which presents them in row-major order), to column-major (as OpenGL interprets them).
Also filed a bug with Apple to inquire if this is the expected behavior.
For the record, Apple determined my bug report on this issue to be a duplicate of bug 15419952. I expect people don't run into this much as they might typically keep the projection matrix separate from the model transform stack. In any case the workaround presented in the question works fine.

Xna draw cube supplied from OBJ

I got generated Cube to .obj file from Blender. Like this:
o Cube_Cube.001
v -1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 1.000000 1.000000
v -1.000000 1.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 0.000000
vn 0.000000 0.000000 1.000000
vn -1.000000 0.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
usemtl None
s off
f 5//1 6//1 1//1
f 6//2 7//2 2//2
f 7//3 8//3 3//3
f 8//4 5//4 4//4
f 1//5 2//5 4//5
f 8//6 7//6 5//6
f 6//1 2//1 1//1
f 7//2 3//2 2//2
f 8//3 4//3 3//3
f 5//4 1//4 4//4
f 2//5 3//5 4//5
f 7//6 6//6 5//6
I load vertices as they are line by line and store them in array. Then indicies (lines starting with 'f' as I suppose) stored to Indicies array so something like {4, 5, 0, 6, 7 ...} (decreased by 1 because of position of vertex in array) and then create array VertexPositionColors.
But when I draw primitives the cube is wierdly deformed.
Draw method:
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
effect.World = world;
effect.View = view;
effect.Projection = projection;
effect.VertexColorEnabled = true;
RasterizerState rs = new RasterizerState();
rs.CullMode = CullMode.None;
rs.FillMode = FillMode.WireFrame;
GraphicsDevice.RasterizerState = rs;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, model.VertexPositionColors, 0, model.VertexPositionColors.Length, model.Indicies, 0, model.Indicies.Length / 3, model.VertexDeclarations);
}
output
http://i.imgur.com/3pLj0U9.png
[SOLUTION]
Check what your import algorithm generates. My indices were messed up because of bad iteration. My bad :)
I would recommend using FBX and the standard XNA content importer system. This streamlines the process so you don't even have to write an importer; you just point XNA at your content and can draw it in very few, very simple commands. Writing an importer yourself is a good exercise, but on the XNA platform it is not necessary.

Resources