Help me please with ray picking
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(35.0f), aspect, 0.1f, 1000.0f);
GLKMatrix4 modelViewMatrix = _mainmodelViewMatrix;
// some transformations
_mainmodelViewMatrix = modelViewMatrix;
_modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
_normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
_modelViewProjectionMatrix and _normalMatrix put to shader
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, _normalMatrix.m);
and in touch end
GLKVector4 normalisedVector = GLKVector4Make((2 * position.x / self.view.bounds.size.width - 1),
(2 * (self.view.bounds.size.height-position.y) / self.view.bounds.size.height - 1) , //1 - 2 * position.y / self.view.bounds.size.height,
-1,
1);
GLKMatrix4 inversedMatrix = GLKMatrix4Invert(_modelViewProjectionMatrix, nil);
GLKVector4 near_point = GLKMatrix4MultiplyVector4(inversedMatrix, normalisedVector);
How I can get far point? And my near_point is correct or not?
Thanks!
it looks like you have
GLKVector4 normalisedVector = GLKVector4Make((2 * position.x / self.view.bounds.size.width - 1),
(2 * (self.view.bounds.size.height-position.y) / self.view.bounds.size.height - 1) ,
-1, 1);
(phew) to calculate the normalized device coordinates of the near point.
To get the far point, just swap the -1 z coordinate for a 1:
GLKVector4 normalisedFarVector = GLKVector4Make((2 * position.x / self.view.bounds.size.width - 1),
(2 * (self.view.bounds.size.height-position.y) / self.view.bounds.size.height - 1) ,
1, 1);
And apply the same inverse transform to that. That should do the trick.
Background: Under normal circumstances, the final coordinates received by the GL for turning a fragment into a pixel are what are called normalised device coordinates. These lie within a cube whose corners are at (-1,-1,-1_ and (1,1,1). So the center of the screen is (0,0,z), the top left corner is (-1,1,z) and so on. The coordinates are transformed so that a point lying on the near plane will have a z coordinate of 1, and one lying just on the far plane will have a z coordinate of -1. These are the numbers that are used for depth testing, if you have it turned on.
So, as you might guess, when you want to convert a screen location back to a point in 3D space, you actually have a number of points to choose from - a line, in fact, stretching from the near plane to the far plane. In normalised device coordinates, this is the line stretching from z=-1 to z=1. So the process goes like this:
convert the x and y coordinates into normalised device coordinates x' and y'
For each of z' = 1 and z' = -1:
convert the coordinates to normalised device coordinates (see here for the formula)
apply the inverse of the projection matrix
apply the inverse of the model/view matrix (as it is before any per-object transformations)
The results are the two coordinates of your line in 3D space.
We can draw line from near_point to far_point.
GLKVector4 normalisedVector = GLKVector4Make((2 * position.x / self.view.bounds.size.width - 1),
(2 * (self.view.bounds.size.height-position.y) / self.view.bounds.size.height - 1),
-1,
1);
GLKMatrix4 inversedMatrix = GLKMatrix4Invert(_modelViewProjectionMatrix, nil);
GLKVector4 near_point = GLKMatrix4MultiplyVector4(inversedMatrix, normalisedVector);
near_point.v[3] = 1.0/near_point.v[3];
near_point = GLKVector4Make(near_point.v[0]*near_point.v[3], near_point.v[1]*near_point.v[3], near_point.v[2]*near_point.v[3], 1);
normalisedVector.z = 1.0;
GLKVector4 far_point = GLKMatrix4MultiplyVector4(inversedMatrix, normalisedVector);
far_point.v[3] = 1.0/far_point.v[3];
far_point = GLKVector4Make(far_point.v[0]*far_point.v[3], far_point.v[1]*far_point.v[3], far_point.v[2]*far_point.v[3], 1);
Related
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 was set up a 2D opengl view in iOS with top left as origin and bottom right as (768, 1366)
My projection matrix is setup like this:
projectionMtx = GLKMatrix4MakeOrtho( 0, 768, 1366, 0, 10, -10);
When I got the touch event, the coordinates are in physical coordinates, and i need to convert them into my own logical coordinates, so I thought like this:
Since V_physical = M_projection * V_logical
So V_logical = M_projection_invert * V_phsical
and I implemented the code like this:
(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self.view];
GLKVector4 locationVector = {
(float)location.x,
(float)location.y,
0,
0,
};
GLKVector4 result = GLKMatrix4MultiplyVector4(GLKMatrix4Invert(projectionMtx, nullptr),locationVector);
NSLog(#"touch %.2f %.2f", location.x, location.y);
NSLog(#"vector %.2f %.2f", result.v[0], result.v[1]);
}
However, this is what I got from testing:
touch 367.00 662.00
vector 140928.00 -452151.28
Is my math wrong or my code wrong?
You have some mix up with your coordinate systems. The projection matrix maps its input coordinates (which in a full 3D pipeline are typically called "eye coordinates") to clip coordinates. For a parallel projection, clip coordinates are the same as normalized device coordinates (NDC), which have a range of [-1, 1] in x and y direction.
This means that your ortho projection:
projectionMtx = GLKMatrix4MakeOrtho( 0, 768, 1366, 0, 10, -10);
maps an x range of [0, 768] to [-1, 1], and a y range of [1366, 0] to [-1, 1]. The resulting mapping done by the matrix is:
xNdc = (2.0 / 768.0) * xEye - 1.0
yNdc = (2.0 / -1366.0) * yEye + 1.0
The inverse of this is:
xEye = (768.0 / 2.0) * (xNdc + 1.0)
yEye = (-1366.0 / 2.0) * (yNdc - 1.0)
Applying this inverse transformation gives:
(768.0 / 2.0) * (367.0 + 1.0) = 141312.0
(-1366.0 / 2.0) * (662.0 - 1.0) = -451463
For reasons I can't explain at the moment, this is slightly off what you got (looks like a one-off difference), but it's very similar.
This is obviously not meaningful. To use the inverse projection transformation, your input coordinates should be in the range [-1, 1].
In your use case, since you set up the projection transformations to transform coordinates in pixels, and you receive touch input that is also in pixels, you really have to do nothing at all to get the touch input in your OpenGL coordinate system. They are already in the same coordinate system (pixels).
If you use any other projection. You would first map your touch coordinates to a [-1, 1] range, and then apply the inverse projection transformation. The coordinate mapping would use the same equations as the ones I had above for mapping eye coordinates to NDC.
I want to draw a filled arc like this:
CCDrawNode only contain methods to draw a circle, a polygon or a line – but no arc.
To be clear, I'm wanting to be able to generate the arc at runtime, with an arbitrary radius and arc angle.
My Question:
Is there a way to have Cocos2d draw an arc, or would I have to do it myself with OpenGL?
I guess you can use the CCProgressNode, and by setting the sprite to an orange circle, you can draw an arc by setting the progress property.
Now, whether you can set the sprite as a vectorized circle that you can just scale, I am not really sure.
Personally, I'd suggest you add the drawing arc code to CCDrawNode, and submit a PR.
tested using cocos2dx v3.8, codes from DrawNode::drawSolidCircle
DrawNode* Pie::drawPie(const Vec2& center, float radius, float startAngle, float endAngle, unsigned int segments, float scaleX, float scaleY, const Color4F &color){
segments++;
auto draw = DrawNode::create();
const float coef = (endAngle - startAngle) / segments;
Vec2 *vertices = new (std::nothrow) Vec2[segments];
if (!vertices)
return nullptr;
for (unsigned int i = 0; i < segments - 1; i++)
{
float rads = i*coef;
GLfloat j = radius * cosf(rads + startAngle) * scaleX + center.x;
GLfloat k = radius * sinf(rads + startAngle) * scaleY + center.y;
vertices[i].x = j;
vertices[i].y = k;
}
vertices[segments - 1].x = center.x;
vertices[segments - 1].y = center.y;
draw->drawSolidPoly(vertices, segments, color);
CC_SAFE_DELETE_ARRAY(vertices);
return draw;
}
call it like
auto pie = Pie::drawPie(_visibleSize / 2, _visibleSize.width / 4, 0, 1, 999, 1, 1, Color4F::BLUE);
I have a triangle created in DirectX11. I now want to play around with viewport and world matrices to help my understanding of them, so Id like to simply rotate the triangle around the Z axis. My code for attempting to do that is below.
void Render(void)
{
if (d3dContext_ == 0)
return;
XMMATRIX view = XMMatrixIdentity();
XMMATRIX projection = XMMatrixOrthographicOffCenterLH(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f); .
XMMATRIX vpMatrix_ = XMMatrixMultiply(view, projection);
XMMATRIX translation = XMMatrixTranslation(0.0f, 0.0f, 0.0f);
XMMATRIX rotationZ = XMMatrixRotationZ(30.0f);
XMMATRIX TriangleWorld = translation * rotationZ;
XMMATRIX mvp = TriangleWorld*vpMatrix_;
mvp = XMMatrixTranspose(mvp);
float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };
d3dContext_->ClearRenderTargetView(backBufferTarget_, clearColor);
unsigned int stride = sizeof(VertexPos);
unsigned int offset = 0;
d3dContext_->IASetInputLayout(inputLayout_);
d3dContext_->IASetVertexBuffers(0, 1, &vertexBuffer_, &stride, &offset);
d3dContext_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
d3dContext_->VSSetShader(solidColorVS_, 0, 0);
d3dContext_->PSSetShader(solidColorPS_, 0, 0);
d3dContext_->UpdateSubresource(mvpCB_, 0, 0, &mvp, 0, 0);
d3dContext_->VSSetConstantBuffers(0, 1, &mvpCB_);
d3dContext_->Draw(3, 0);
swapChain_->Present(0, 0);
}
It just displays the standard triangle, its as if it does not take notice of the mvp.
My desired effect is the rotation as controlled by XMMATRIX rotationZ = XMMatrixRotationZ(30);.
Thanks
XMMatrixRotationZ takes a radian as parameter, not degrees (see MSDN Description ).
To get degrees from radians, you have to multiply by M_PI / 180.0f
XMMATRIX rotationZ = XMMatrixRotationZ(30 * M_PI / 180.0);
As far as i know from OpenGl you must increase the XMMatrixRotationZ-value for an animated rotation a little bit per tick, because otherwise you only draw it once in the specific angle.
So (if you haven't) create a loop for your render function and increase the angle-value per round
Hope i could help
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;
}