I have plus sign which i drawn using lines. Now i want to rotate these lines, so that it will look like a X sign. I tried rotating the lines but no way.
self.shapeLayer1.transform = CATransform3DMakeRotation( (CGFloat) (GLKMathDegreesToRadians(-45)),0, 0, 0)
self.shapeLayer2.transform = CATransform3DMakeRotation( (CGFloat) (GLKMathDegreesToRadians(-45)), 0, 0, 0)
You guys can see that, i put zeros in the x, y, z places.!! i tries different values.But colud not get the actual rotation. If somebody got any idea, Please share with me. Sometimes the lines move to another point and rotates.
It looks like the x,y,z parameters define the axis of rotation. Since we want to rotate around xy, your axis of rotation should be the z axis, or 0,0,1.
self.shapeLayer1.transform = CATransform3DMakeRotation( (CGFloat) (GLKMathDegreesToRadians(45)),0, 0, 1)
self.shapeLayer2.transform = CATransform3DMakeRotation( (CGFloat) (GLKMathDegreesToRadians(-45)), 0, 0, 1)
Regarding the issue you're having with rotation around a non-centerpoint of the line, if you're unable to redraw the line centred around 0,0,0, you can also use the following code to transform it to 0,0,0, rotate, then transform it back to where you need it:
CGFloat tx = 1.0,ty = 2.0,tz = 0; // Modify these to the values you need
CATransform3D t = CATransform3DMakeTranslation (tx, ty, tz);
t = CATransform3DRotate(t,(CGFloat) (GLKMathDegreesToRadians(45)),0, 0, 1);
self.shapeLayer1.transform = CATransform3DTranslate(t,-tx,-ty,-tz);
CATransform3D t = CATransform3DMakeTranslation (tx, ty, tz);
t = CATransform3DRotate(t,(CGFloat) (GLKMathDegreesToRadians(-45)),0, 0, 1);
self.shapeLayer2.transform = CATransform3DTranslate(t,-tx,-ty,-tz);
Related
I'm using OpenCV.js to rotate image to the left and right, but it was cropped when I rotate.
This is my code:
let src = cv.imread('img');
let dst = new cv.Mat();
let dsize = new cv.Size(src.rows, src.cols);
let center = new cv.Point(src.cols/2, src.rows/2);
let M = cv.getRotationMatrix2D(center, 90, 1);
cv.warpAffine(src, dst, M, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar());
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); M.delete();
Here is an example:
This is my source image:
This is what I want:
But it returned like this:
What should I do to fix this problem?
P/s: I don't know how to use different languages except javascript.
A bit late but given the scarcity of opencv.js material I'll post the answer:
The function cv.warpAffine crops the image because it only does a mathematical transformation as documented on OpenCV and other sources, if you wish to do rotations to any angle you'll need to calculate the padding in order to compensate that.
If you wish to only rotate in multiples of 90 degrees you could use cv.rotate as follows:
cv.rotate(src, dst, cv.ROTATE_90_CLOCKWISE);
Where src is the matrix with your source image, dst is the destination matrix which could be defined empty as follows let dst = new cv.Mat(); and cv.ROTATE_90_CLOCKWISE is the rotate flag indicating the angle of rotation, there are three different options:
cv.ROTATE_90_CLOCKWISE
cv.ROTATE_180
cv.ROTATE_90_COUNTERCLOCKWISE
You can find which OpenCV functions are implemented on OpenCV.js on the repository's opencv_js.congif.py file if the function is indicated as whitelisted then is working on opencv.js even if it is not included in the opencv.js tutorial.
The info about how to use each function can be found in the general documentation, the order of the parameters is generally the indicated on the C++ indications (don't be distracted by the oscure C++ vector types sintax) and the name of the flags (like rotate flag) is usually indicated on the python indications.
I was also experiencing this issue so had a look into #fernando-garcia's answer, however I couldn't see that rotate had been implemented in opencv.js so it seems that the fix in the post #dan-mašek's links is the best solution for this, however the functions required are slightly different.
This is the solution I came up with (note, I haven't tested this exact code and there is probably a more elegant/efficient way of writing this, but it gives the general idea. Also this will only work with images rotated by multiples of 90°):
const canvas = document.getElementById('canvas');
const image = cv.imread(canvas);
let output = new cv.Mat();
const size = new cv.Size();
size.width = image.cols;
size.height = image.rows;
// To add transparent borders
const scalar = new cv.Scalar(0, 0, 0, 0);
let center;
let padding;
let height = size.height;
let width = size.width;
if (height > width) {
center = new cv.Point(height / 2, height / 2);
padding = (height - width) / 2;
// Pad out the left and right before rotating to make the width the same as the height
cv.copyMakeBorder(image, output, 0, 0, padding, padding, cv.BORDER_CONSTANT, scalar);
size.width = height;
} else {
center = new cv.Point(width / 2, width / 2);
padding = (width - height) / 2;
// Pad out the top and bottom before rotating to make the height the same as the width
cv.copyMakeBorder(image, output, padding, padding, 0, 0, cv.BORDER_CONSTANT, scalar);
size.height = width;
}
// Do the rotation
const rotationMatrix = cv.getRotationMatrix2D(center, 90, 1);
cv.warpAffine(
output,
output,
rotationMatrix,
size,
cv.INTER_LINEAR,
cv.BORDER_CONSTANT,
new cv.Scalar()
);
let rectangle;
if (height > width) {
rectangle = new cv.Rect(0, padding, height, width);
} else {
/* These arguments might not be in the right order as my solution only needed height
* > width so I've just assumed this is the order they'll need to be for width >=
* height.
*/
rectangle = new cv.Rect(padding, 0, height, width);
}
// Crop the image back to its original dimensions
output = output.roi(rectangle);
cv.imshow(canvas, output);
Is there a way of converting UIKit coordinates (0,0 top left) to Quartz/CoreImage (0,0 bottom left)? Can't find anything swift related like this on here.
You can use affine transformation matrix, this snipped is taken from a code of mine to convert from Core Image / Core graphics to UIKit:
CGAffineTransform t = CGAffineTransformMakeScale(1, -1);
t = CGAffineTransformTranslate(t,0, -imageView.bounds.size.height);
Basically you need to:
Negate the y axis
translate origin by the view height
After the you can use those geometric functions to calculate your rect or points
CGPoint pointUIKit = CGPointApplyAffineTransform(pointCI, t);
CGRect rectUIKit = CGRectApplyAffineTransform(rectCI, t);
In Swift 3.x:
var t = CGAffineTransform(scaleX: 1, y: -1)
t = t.translatedBy(x: 0, y: -imageView.bounds.size.height)
let pointUIKit = pointCI.applying(t)
let rectUIKIT = rectCI.applying(t)
With the origin in the bottom left versus the top left, you need to do nothing with the X axis, but you need to flip the right axis. UIKit (or in this case, Core Graphics) uses CGPoints. Core Image typically uses a CIVectors, which can have 2, 3, or 4 axis angles.
Here's a simple function that will turn a CGPoint(X,Y) into a CIVector(X,Y):
func createVector(_ point:CGPoint, image:CIImage) -> CIVector {
return CIVector(x: point.x, y: image.extent.height - point.y)
}
I have an object displayed using OpenGL ES on an iPad. The model is defined by vertices, normals and indexes to vertices. The origin of the model is 0,0,0. Using UIGestureRecognizer I can detect various gestures - two-fingered swipe horizontally for rotation about y, vertically for rotation about x. Two-fingered rotate gesture for rotation about y. Pan to move the model around. Pinch/zoom gesture to scale. I want the viewer to be able to manipulate the model to see (for example) the reverse of the model or the whole thing at once.
The basic strategy comes from Ray Wenderlich's tutorial but I have rewritten this in Swift.
I understand quaternions to be a vector and an angle. The vectors up, right and front represent the three axes:
front = GLKVector3Make(0.0, 0.0, 1.0)
right = GLKVector3Make(1.0, 0.0, 0.0)
up = GLKVector3Make(0.0, 1.0, 0.0)
so the quaternion apples a rotation around each of the three axes (though only one of dx, dy, dz has a value, decided by the gesture recognizer.)
func rotate(rotation : GLKVector3, multiplier : Float) {
let dx = rotation.x - rotationStart.x
let dy = rotation.y - rotationStart.y
let dz = rotation.z - rotationStart.z
rotationStart = GLKVector3Make(rotation.x, rotation.y, rotation.z)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd)
rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd)
state = .Rotation
}
Drawing uses the modelViewMatrix, calculated by the following function:
func modelViewMatrix() -> GLKMatrix4 {
var modelViewMatrix = GLKMatrix4Identity
// translation and zoom
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, translationEnd.x, translationEnd.y, -initialDepth);
// rotation
let quaternionMatrix = GLKMatrix4MakeWithQuaternion(rotationEnd)
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, quaternionMatrix)
// scale
modelViewMatrix = GLKMatrix4Scale(modelViewMatrix, scaleEnd, scaleEnd, scaleEnd);
// rotation
return modelViewMatrix
}
And mostly this works. However everything is relative to the origin.
If the model is rotated then the pivot is always an axis passing through the origin - if zoomed in looking at the end of the model away from the origin and then rotating, the model can rapidly swing out of view. If the model is scaled then the origin is always the fixed point with the model growing larger or smaller - if the origin is off-screen and scale is reduced the model can disappear from view as it collapses toward the origin...
What should happen is that whatever the current view, the model rotates or scales relative to the current view. For a rotation around the y axis that would mean defining the y axis around which the rotation occurs as passing vertically through the middle of the current view. For a scale operation the fixed point of the model would be in the centre of the screen with the model shrinking toward or growing outward from that point.
I know that in 2D the solution is always to translate to the origin, apply rotation and then apply the inverse of the first translation. I don't see why this should be different in 3D, but I cannot find any example doing this with quaternions only matrices. I have tried to apply a translation and its inverse around the rotation but nothing has an effect.
So I tried to do this in the rotate function:
let xTranslation : Float = 300.0
let yTranslation : Float = 300.0
let translation = GLKMatrix4Translate(GLKMatrix4Identity, xTranslation, yTranslation, -initialDepth);
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(translation) , rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd)
rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd)
// inverse translation
let inverseTranslation = GLKMatrix4Translate(GLKMatrix4Identity, -xTranslation, -yTranslation, -initialDepth);
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(inverseTranslation) , rotationEnd)
The translation is 300,300 but there is no effect at all, it still pivots around where I know the origin to be. I've searched a long time for sample code and not found any.
The modelViewMatrix is applied in update() with:
effect?.transform.modelviewMatrix = modelViewMatrix
I could also cheat by adjusting all of the values in the model so that 0,0,0 falls at a central point - but that would still be a fixed origin and would be only marginally better.
The problem is in the last operation you made, you should swap the inverseTranslation with rotationEnd :
rotationEnd = GLKQuaternionMultiply(rotationEnd, GLKQuaternionMakeWithMatrix4(inverseTranslation))
And I think the partial rotation(dx, dy, dz) should follow the same rule.
In fact, if you want to change the pivot, this is how your matrix multiplication should be done:
modelMatrix = translationMatrix * rotationMatrix * inverse(translationMatrix)
and the result in homogeneous coordinates will be calculated as follows:
newPoint = translationMatrix * rotationMatrix * inverse(translationMatrix) * v4(x,y,z,1)
Example
This is a 2D test example that you can run in a playground.
let v4 = GLKVector4Make(1, 0, 0, 1) // Point A
let T = GLKMatrix4Translate(GLKMatrix4Identity, 1, 2, 0);
let rot = GLKMatrix4MakeWithQuaternion(GLKQuaternionMakeWithAngleAndVector3Axis(Float(M_PI)*0.5, GLKVector3Make(0, 0, 1))) //rotate by PI/2 around the z axis.
let invT = GLKMatrix4Translate(GLKMatrix4Identity, -1, -2, 0);
let partModelMat = GLKMatrix4Multiply(T, rot)
let modelMat = GLKMatrix4Multiply(partModelMat, invT) //The parameters were swapped in your code
//and the result would the rot matrix, since T*invT will be identity
var v4r = GLKMatrix4MultiplyVector4(modelMat, v4) //ModelMatrix multiplication with pointA
print(v4r.v) //(3,2,0,1)
//Step by step multiplication using the relation described above
v4r = GLKMatrix4MultiplyVector4(invT, v4)
v4r = GLKMatrix4MultiplyVector4(rot, v4r)
v4r = GLKMatrix4MultiplyVector4(T, v4r)
print(v4r.v) //(3,2,0,1)
As for the scale, if I understand correctly what you want, I would recommend to do it like it's done here: https://gamedev.stackexchange.com/questions/61473/combining-rotation-scaling-around-a-pivot-with-translation-into-a-matrix
I'm using Vuforia on Android for AR development. We can obtain the modelViewMatrix using
Matrix44F modelViewMatrix_Vuforia = Tool.convertPose2GLMatrix(trackableResult.getPose());
This works great. Any geometry multiplied by this matrix and then by the projection matrix shows up on the screen as expected, with (0,0,0) at the centre of the tracked target.
But what I also want to do is to simultaneously draw geometry relative to the user's device, so to achieve this we can work out the inverse modelViewMatrix using:
Matrix44F inverseMV = SampleMath.Matrix44FInverse(invTranspMV);
Matrix44F invTranspMV = SampleMath.Matrix44FTranspose(modelViewMatrix_Vuforia);
modelViewMatrixInverse = invTranspMV.getData();
This works pretty well, e.g. if I draw a cube using this matrix, then when I tilt my phone up and down, the cube is also tilted up and down correctly, but when I turn left and right there's a problem. Left turning causes the cube to turn the wrong way as if I'm looking to the right hand side of it. Similarly with right turning. What should be happening is that the cube should appear "stuck" to the screen, i.e. which ever way I turn I should be able to see the same face "stuck" to the screen always.
I think the problem might be do with the Vuforia projection matrix, and I am going to create my own projection matrix (using guidance here) to experiment with different settings. As this post says, it could be to do with the intrinsic camera calibration of a specific device.
Am I on the right track? Any ideas what might be wrong and how I might solve this?
UPDATE
I don't think it's the projection matrix anymore (due to experimentation and peedee's answer comment below)
Having looked at this post I think I've made some progress. I am now using the following code:
Matrix44F modelViewMatrix_Vuforia = Tool.convertPose2GLMatrix(trackableResult.getPose());
Matrix44F inverseMV = SampleMath.Matrix44FInverse(modelViewMatrix_Vuforia);
Matrix44F invTranspMV = SampleMath.Matrix44FTranspose(inverseMV);
modelViewMatrixInverse = invTranspMV.getData();
float [] position = {0, 0, 0, 1};
float [] lookAt = {0, 0, 1, 0};
float [] cam_position = new float[16];
float [] cam_lookat = new float[16];
Matrix.multiplyMV(cam_position, 0, modelViewMatrixInverse, 0, position, 0);
Matrix.multiplyMV(cam_lookat, 0, modelViewMatrixInverse, 0, lookAt, 0);
Log.v("QCV", "posx = " + cam_position[0] + ", posy = " + cam_position[1] + ", posz = " + cam_position[2]);
Log.v("QCV", "latx = " + cam_lookat[0] + ", laty = " + cam_lookat[1] + ", latz = " + cam_lookat[2]);
This successfully returns the camera position, and the normal to the camera as you move the camera about the target. I think I should be able to use this to project geometry in the way I want. Will update later if it works.
UPDATE2
Ok, some progress made. I'm now using the following code. It does the same thing as the previous code block but uses Matrix class instead of the SampleMath class.
float [] temp = new float[16];
temp = modelViewMatrix_Vuforia.getData();
Matrix.invertM(modelViewMatrixInverse, 0, temp, 0);
float [] position = {0, 0, 0, 1};
float [] lookAt = {0, 0, 1, 0};
float [] cam_position = new float[16];
float [] cam_lookat = new float[16];
Matrix.multiplyMV(cam_position, 0, modelViewMatrixInverse, 0, position, 0);
Matrix.multiplyMV(cam_lookat, 0, modelViewMatrixInverse, 0, lookAt, 0);
Log.v("QCV", "posx = " + cam_position[0] / kObjectScale + ", posy = " + cam_position[1] / kObjectScale + ", posz = " + cam_position[2] / kObjectScale);
Log.v("QCV", "latx = " + cam_lookat[0] + ", laty = " + cam_lookat[1] + ", latz = " + cam_lookat[2]);
The next bit of code gives (almost) the desired result:
modelViewMatrix = modelViewMatrix_Vuforia.getData();
Matrix.translateM(modelViewMatrix, 0, 0, 0, kObjectScale);
Matrix.scaleM(modelViewMatrix, 0, kObjectScale, kObjectScale, kObjectScale);
line.setVerts(cam_position[0] / kObjectScale,
cam_position[1] / kObjectScale,
cam_position[2] / kObjectScale,
cam_position[0] / kObjectScale + 0.5f,
cam_position[1] / kObjectScale + 0.5f,
cam_position[2] / kObjectScale - 30);
This defines a line along the negative z-axis from position vector equal to the camera position (which is calculated from the position of the actual physical device). Since the vector is normal, I have offsetted the X/Y so the normal can actually be visualised.
As you reposition your physical device, the normal moves with you. Great!
However, keeping the phone in the same position, but tilting the phone forwards/backwards or turning left/right, the line does not maintain it's central position within the camera's display. The effect I want is for the line to be rotated in world space as I tilt/turn so that in camera/screen space the line appears normal and is central to the physical display.
Note - you may wonder why I don't use something like:
line.setVerts(cam_position[0] / kObjectScale,
cam_position[1] / kObjectScale,
cam_position[2] / kObjectScale,
cam_position[0] / kObjectScale + cam_lookat[0] * 30,
cam_position[1] / kObjectScale + cam_lookat[1] * 30,
cam_position[2] / kObjectScale + cam_lookat[2] * 30);
The simple answer is I did try and it doesn't work ! All this achieves is that one end of the line stays where it is, whilst the other end points in the direction of the screen device normal. What we need is to rotate the line in world space based on angles obtained from cam_lookat so that the line actually appears in front of the camera in the centre and normal to the camera.
The next stage is to adjust the position of the line in world space based on angles calculated from the cam_lookat unit vector. These can be used to update the vertices of the line so that the normal always appears in the centre of the camera whichever way you orient the phone.
I think this is the right way to go. I will update again if this works!
Ok, this was a tough nut to crack but success is sooo sweet!
One crucial part is that it uses a function from SampleMath to compute the start of an intersection line from the centre of the physical device to the target. We combine this with the camera normal vector to get the line we want !
If you want to dig deeper I'm sure you can unearth/workout the matrix math behind the getPointToPlaneLineStart function.
This is the code that works. It's not optimal so you can probably tidy it up a bit/lot!
modelViewMatrix44F = Tool.convertPose2GLMatrix(trackableResult.getPose());
modelViewMatrixInverse44F = SampleMath.Matrix44FInverse(modelViewMatrix44F);
modelViewMatrixInverseTranspose44F = SampleMath.Matrix44FTranspose(modelViewMatrix44F);
modelViewMatrix = modelViewMatrix44F.getData();
Matrix.translateM(modelViewMatrix, 0, 0, 0, kObjectScale);
Matrix.scaleM(modelViewMatrix, 0, kObjectScale, kObjectScale, kObjectScale);
modelViewMatrix44F.setData(modelViewMatrix);
projectionMatrix44F = vuforiaAppSession.getProjectionMatrix();
projectionMatrixInverse44F = SampleMath.Matrix44FInverse(projectionMatrix44F);
projectionMatrixInverseTranspose44F = SampleMath.Matrix44FTranspose(projectionMatrixInverse44F);
// work out camera position and direction
modelViewMatrixInverse = modelViewMatrixInverseTranspose44F.getData();
position = new float [] {0, 0, 0, 1}; // camera position
lookAt = new float [] {0, 0, 1, 0}; // camera direction
float [] rotate = new float [] {(float) Math.cos(angle_degrees * 0.017453292f), (float) Math.sin(angle_degrees * 0.017453292f), 0, 0};
angle_degrees += 10;
if(angle_degrees > 359)
angle_degrees = 0;
float [] cam_position = new float[16];
float [] cam_lookat = new float[16];
float [] cam_rotate = new float[16];
Matrix.multiplyMV(cam_position, 0, modelViewMatrixInverse, 0, position, 0);
Matrix.multiplyMV(cam_lookat, 0, modelViewMatrixInverse, 0, lookAt, 0);
Matrix.multiplyMV(cam_rotate, 0, modelViewMatrixInverse, 0, rotate, 0);
Vec3F line_start = SampleMath.getPointToPlaneLineStart(projectionMatrixInverse44F, modelViewMatrix44F, 2*kObjectScale, 2*kObjectScale, new Vec2F(0, 0), new Vec3F(0, 0, 0), new Vec3F(0, 0, 1));
float x1 = line_start.getData()[0];
float y1 = line_start.getData()[1];
float z1 = line_start.getData()[2];
float x2 = x1 + cam_lookat[0] * 3 + cam_rotate[0] * 0.1f;
float y2 = y1 + cam_lookat[1] * 3 + cam_rotate[1] * 0.1f;
float z2 = z1 + cam_lookat[2] * 3 + cam_rotate[2] * 0.1f;
line.setVerts(x1, y1, z1, x2, y2, z2);
Note - I added the cam_rotate vector so that you could see the line, otherwise you can't see it - or at least you only see a speck on the screen - because it is defined to be perpendicular to the screen !
And it's Friday so I might go to the pub later to celebrate :-)
UPDATE
In fact the getPointToPlaneLineStart Java SampleMath method calls the following code (C++), so you can probably decipher the matrix math from it if you don't want to use the SampleMath class (c.f. this post)
SampleMath::projectScreenPointToPlane(QCAR::Matrix44F inverseProjMatrix, QCAR::Matrix44F modelViewMatrix,
float contentScalingFactor, float screenWidth, float screenHeight,
QCAR::Vec2F point, QCAR::Vec3F planeCenter, QCAR::Vec3F planeNormal,
QCAR::Vec3F &intersection, QCAR::Vec3F &lineStart, QCAR::Vec3F &lineEnd)
{
// Window Coordinates to Normalized Device Coordinates
QCAR::VideoBackgroundConfig config = QCAR::Renderer::getInstance().getVideoBackgroundConfig();
float halfScreenWidth = screenHeight / 2.0f;
float halfScreenHeight = screenWidth / 2.0f;
float halfViewportWidth = config.mSize.data[0] / 2.0f;
float halfViewportHeight = config.mSize.data[1] / 2.0f;
float x = (contentScalingFactor * point.data[0] - halfScreenWidth) / halfViewportWidth;
float y = (contentScalingFactor * point.data[1] - halfScreenHeight) / halfViewportHeight * -1;
QCAR::Vec4F ndcNear(x, y, -1, 1);
QCAR::Vec4F ndcFar(x, y, 1, 1);
// Normalized Device Coordinates to Eye Coordinates
QCAR::Vec4F pointOnNearPlane = Vec4FTransform(ndcNear, inverseProjMatrix);
QCAR::Vec4F pointOnFarPlane = Vec4FTransform(ndcFar, inverseProjMatrix);
pointOnNearPlane = Vec4FDiv(pointOnNearPlane, pointOnNearPlane.data[3]);
pointOnFarPlane = Vec4FDiv(pointOnFarPlane, pointOnFarPlane.data[3]);
// Eye Coordinates to Object Coordinates
QCAR::Matrix44F inverseModelViewMatrix = Matrix44FInverse(modelViewMatrix);
QCAR::Vec4F nearWorld = Vec4FTransform(pointOnNearPlane, inverseModelViewMatrix);
QCAR::Vec4F farWorld = Vec4FTransform(pointOnFarPlane, inverseModelViewMatrix);
lineStart = QCAR::Vec3F(nearWorld.data[0], nearWorld.data[1], nearWorld.data[2]);
lineEnd = QCAR::Vec3F(farWorld.data[0], farWorld.data[1], farWorld.data[2]);
linePlaneIntersection(lineStart, lineEnd, planeCenter, planeNormal, intersection);
}
I'm by no means an expert, but it sounds to me like this left/right inversion should be expected. In my mind, the object in world space is looking in the direction of the positive z-axis towards the camera, while the camera space is looking in the direction of the negative z-axis facing the camera. Such a transformation of the coordinate system is bound to invert one of the x/y-axes to keep the coordinate system consistent.
ELI5: When you're standing in front of someone and tell them "on the count of 3 we both step to the left", you won't be standing in front of each other anymore afterwards.
I think it's unlikely to be a problem with the projection matrix as you said. The projection matrix merely transforms the 3d objects onto your 2d screen. Also the camera intrinsics doesn't sound like the right place to me. That matrix will correct for small distortions caused by the camera lens shape and placement, nothing as drastic as a left/right inversion.
Unfortunately I also don't know how to solve it right now, but what I had to say was too long for a comment. Sorry :-(
I have a texture that follows a user's finger in GLKit. I calculate the radian to draw the angle at using arctan between the two points.
Part of the trick here is to keep the object centered underfed the finger. So i have introduced the idea of an anchor point so that things can be drawn relative to their origin or center. My goal is to move the sprite into place and then rotate. I have the following code in my renderer.
// lets adjust for our location based on our anchor point.
GLKVector2 adjustment = GLKVector2Make(self.spriteSize.width * self.anchorPoint.x,
self.spriteSize.height * self.anchorPoint.y);
GLKVector2 adjustedPosition = GLKVector2Subtract(self.position, adjustment);
GLKMatrix4 modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(adjustedPosition.x, adjustedPosition.y, 1.0), GLKMatrix4MakeScale(adjustedScale.x, adjustedScale.y, 1));
modelMatrix = GLKMatrix4Rotate(modelMatrix, self.rotation, 0, 0, 1);
effect.transform.modelviewMatrix = modelMatrix;
effect.transform.projectionMatrix = scene.projection;
One other note is that my sprite is on a texture alias. If i take out my rotation my sprite draws correctly centered under my finger. My project matrix is GLKMatrix4MakeOrtho(0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame), 0, 1, -1); so it matches the UIkit and the view its embedded in.
I ended up having to add a little more math to calculate additional offsets before i rotate.
// lets adjust for our location based on our anchor point.
GLKVector2 adjustment = GLKVector2Make(self.spriteSize.width * self.anchorPoint.x,
self.spriteSize.height * self.anchorPoint.y);
// we need to further adjust based on our so we can calucate the adjust based on our anchor point in our image.
GLKVector2 angleAdjustment;
angleAdjustment.x = adjustment.x * cos(self.rotation) - adjustment.y * sin(self.rotation);
angleAdjustment.y = adjustment.x * sin(self.rotation) + adjustment.y * cos(self.rotation);
// now create our real position.
GLKVector2 adjustedPosition = GLKVector2Subtract(self.position, angleAdjustment);
GLKMatrix4 modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(adjustedPosition.x, adjustedPosition.y, 1.0), GLKMatrix4MakeScale(adjustedScale.x, adjustedScale.y, 1));
modelMatrix = GLKMatrix4Rotate(modelMatrix, self.rotation, 0, 0, 1);
This will create an additional adjustment based on where in the image we want to rotate and then transform based on that. This works like a charm..
There is a similar code I used to rotate a sprite around its center
First you move it to the position, then you rotate it, then you move it back halfsprite
- (GLKMatrix4) modelMatrix {
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
float radians = GLKMathDegreesToRadians(self.rotation);
modelMatrix = GLKMatrix4Multiply(
GLKMatrix4Translate(modelMatrix, self.position.x , self.position.y , 0),
GLKMatrix4MakeRotation(radians, 0, 0, 1));
modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.height/2, -self.contentSize.width/2 , 0);
return modelMatrix;
}