How to rotate a 3D model based on the projection coordinates - c#

I'm trying to implement something using OpenTK in C# where when you render a 3d plot, you can rotate it around the z axis (pointing up) as I move the mouse.
I already have it working but as shown below, I had to come up with an arbitrary constant 500;
The equation is:
arc_length = radius * theta
theta = arc_length / radius
where radius = eye_pos.Length
arc_length is the delta_x in model coordinate, but
(e.X - mouse_down_pos.X) is in screen coordinate.
so that 500 was used to map model coordinate to screen projected coordinate.
QUESTION: is there a way to eliminate this constant and properly obtain the conversion factor?
My educated guess is that I should use somehow multiply [delta_x, 0, 0] by inverse of the projection matrix...but do not have a clue how.
private static Vector3 eye_pos = new Vector3(30.0f, 0.0f, 0.0f);
GL.MatrixMode(MatrixMode.Modelview);
Matrix4 lookat = Matrix4.LookAt(eye_pos.X, eye_pos.Y, eye_pos.Z, 0, 0, 0, 0, 0, 1);
...
private void glControl1_MouseMove(object sender, MouseEventArgs e)
{
if (mouse_op == Mouse_Operation.Rotate)
{
decimal yaw = (decimal)MathHelper.DegreesToRadians((e.X - mouse_down_pos.X) * 500 / eye_pos.Length) + ref_yaw;
SetYawAndRedraw(yaw);
}
}

Related

Math: Mercator Projection Convert 360 video pixel coordinate to sphere surface coordinate

I was trying to map the 360 video pixel coordinate to sphere surface coordinate but I couldn't get right result... It just mapped to the wrong position I already know the points of the XY data for 360 video pixels.
how map 2d grid points (x,y) onto sphere as 3d points (x,y,z)
I checked this link and I copied method from this but what I'm getting is not mapped to the desired position.
How can I get radius from the pixels?
I am not sure if I'm passing right radius for imageRadius but I thought it will be circumference/PI to get radius and the video ratio is 4096x2048. I also tried to pass the number 1 because UV is 0-1 but it was not right...
Is Method wrong?
Maybe the method is wrong. I passed random numbers into the imageRadius but couldn't get the right position... If I make X to negative number the seems like little bit closer to result....?
Current Result
https://youtu.be/t0I7Hlb-tbk
It mapped to up right position with the method that I found online...
Project File
https://drive.google.com/a/swordfish-sf.com/file/d/0B45RYzVs0t0_VVdaaHdmNHRWTk0/view?usp=sharing
If somebody can check the Unity project file that will be great...
Current Code
public class mapScript : MonoBehaviour {
public int input = 4098;
float imageRadius = 4098f / Mathf.PI; //2098? 3072? 4098?
float radius;
public GameObject testSphere;
void Start () {
radius = this.transform.localScale.x;
}
void Update () {
imageRadius = input / Mathf.PI;
int currentFrame = (int)this.GetComponent<VideoPlayer>().frame;
testSphere.transform.position = MercatorProjection(mapVals[currentFrame,0],mapVals[currentFrame,1]);
}
Vector3 MercatorProjection(float xVal, float yVal)
{
float lon = (xVal / imageRadius);
float lat = (2 * Mathf.Atan(Mathf.Exp(yVal / imageRadius)) - Mathf.PI / 2);
float calcX = radius * Mathf.Cos(lat) * Mathf.Cos(lon);
float calcY = radius * Mathf.Cos(lat) * Mathf.Sin(lon);
float calcZ = radius * Mathf.Sin(lat);
Vector3 result = new Vector3(calcX,calcY,calcZ);
Debug.Log(result);
return result;
}
float[,] mapVals = new float[,] {
{1969.21f, 928.625f},
{1969.6f, 928.533f},
{1968.92f, 928.825f},
{1968.68f, 929f},
{1968.47f, 929.067f},
{1968.41f, 929.025f},
{1968.48f, 928.992f},
....
};
}
Thank you.
As a side note, the radius is arbitrary. The pixel coordinates only map to the directional coordinates (polar [θ] and azimuthal [ϕ] angles).
We can do this by mapping each pixel to equal θ and ϕ intervals. The diagram below illustrates a low-resolution setup:
Let us adopt the convention that, for an image of with W, ϕ = 0 corresponds to:
Even W: half way between X = floor((W - 1) / 2) and X = ceil((W - 1) / 2)
Odd W: in the middle of the pixel column at X = floor((W - 1) / 2)
The pixel row at Y maps to the equilatitudinal line at θ = (Y + 0.5) / H * π.
To map all pixels in their entirety, let X start at -0.5 instead of 0, and end at W - 0.5; likewise for Y. Since integer coordinates map to the centers of the pixel regions shown above, this allows the whole area of any particular pixel to be addressed. You may need this later on if you plan on doing multi-sampling filtering for e.g. anti-aliasing.
Code:
Vector3 Mercator(float x, float y, int w, int h)
{
// outside of valid pixel region
if (x < -0.5f || x >= w - 0.5f || y < -0.5f || y >= h - 0.5f)
return new Vector3();
float theta = (y + 0.5f) / h * Math.PI;
float phi = ((x + 0.5f) / w - 0.5f) * 2.0 * Math.PI;
float c_t = Math.Cos(theta);
return new Vector3(c_t * Math.Cos(phi), c_t * Math.Sin(phi), Math.Sin(theta));
}
... and multiply the resulting direction vector by any "radius" you like, since it has (basically) nothing to do with the mapping anyway.

rotation matrix to facing direction vector

I have a position of a character as xyz coordinates x= 102, y= 0.75, z= -105.7 for example. And i have the rotation matrix for the character as
M11 = -0.14
M12 = 0
M13 = -0.99
M21 = 0
M22 = 1
M23 = 0
M31 = 0.99
M32 =0
M33 = 0.14
I don't have much understanding about graphics and how these data can correlate to the facing direction of the character. I want to find a vector such that i can use that vector to aim at a direction that the character is facing. How do i do that?
The direction your character is facing is the 3rd row of the rotation matrix. So that would be:
Vector3 facingDirection = new Vector3(0.99f, 0f, 0.14f);//(m31,m32,m33)
this vector appears to be normalized, but if it weren't, you would do:
Vector3 facingDirection = Vector3.Normalize(new Vector3(0.99f, 0f, 0.14f));
If the matrix is an XNA Matrix, then you would simply use the Matrix.Forward property of the Matrix structure and get the same result.
I am finally able to resolve it. Actually it's pretty simple vector mathematics. The rotation matrix was already giving me the direction vector as Steve suggests above, but I had to fire along that line to some particular point to make my animation working...So I actually needed a point along the line denoted by the direction vector. So, i just calculated a point some 1000 units away from the character's current position along the direction vector and fired at the line, it worked!
Vector3 facingDirection = new Vector3(RotMatrix[2, 0], RotMatrix[2, 1], RotMatrix[2, 2]); // direction vector
Vector3 currentPos = new Vector3(character.PosX, character.PosY, character.PosZ); // I also have position of the character
// Now calculate a point 10000 units away along the vector line
var px = currentPos.X + facingDirection.X * 10000;
var py = currentPos.Y + facingDirection.Y * 10000;
var pz = currentPos.Z + facingDirection.Z * 10000;
return new Vector3(px, py, pz);

Spin Control in Windows Form

I have a picturebox with an image in it. The image contains two ellipses face to face (black & blue).
What I want is to rotate the picturebox in a timer (for the effect) so the image to be "upside down" would look much more like they've changed place, which basically it's just rotating the picturebox like how the erath is moving around it's axis.
There are various kinds of rotations from a globe, depending on how you look at it.
If you look at it from above the poles it spins like a disk or a gear and you can find code for it here. This has the advantage that you can use any image and rotate it.
If you look at it from the side, facing the equator you can't easily use bitmaps, but using just two colors it will still look nice..
Here is an example of a 'globe-like' spinning rotation:
float angle = 0f;
float aSpeed = 4f; // <-- set your speed
Brush brush1 = Brushes.CadetBlue; // and your..
Brush brush2 = Brushes.DarkSlateBlue; // ..colors
private void timer1_Tick(object sender, EventArgs e)
{
angle += aSpeed;
if (angle + aSpeed > 360)
{
angle -= 360f;
Brush temp = brush1;
brush1 = brush2;
brush2 = temp;
}
pictureBox1.Invalidate();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Rectangle r = pictureBox1.ClientRectangle;
Rectangle r2 = r; // see below..
r.Inflate(-20, -20); // a little smaller than the panel or pBox
if (angle < 180)
{
e.Graphics.FillEllipse(brush1, r);
e.Graphics.FillPie(brush2, r, 270, 180);
r.Inflate(-(int)(r.Width * angle / 360f), 0);
e.Graphics.FillEllipse(brush2, r);
}
else
{
e.Graphics.FillEllipse(brush2, r);
e.Graphics.FillPie(brush1, r, 90, 180);
r.Inflate(-(int)(r.Width * angle / 360f), 0);
e.Graphics.FillEllipse(brush1, r);
}
}
}
This is created by three DrawXXX calls: a circle of one color and an ellipse and an arc, set to display a half circle of the same, second color.
Note: To make the angular speed uniform you may want to play with a little Math.Sin and/or an angle table..
If you look at it from any other angle and if you need to show rotating bitmaps in 3D you can't easily draw it but will need to resort to displaying frames..
But you can combine the disk rotation from the link with the code above and will get rather complex rotations, that look a lot like a 3D sphere.. Simply add the code before the drawing..
float bw2 = r2.Width / 2f;
float bh2 = r2.Height / 2f;
e.Graphics.TranslateTransform(bw2, bh2);
e.Graphics.RotateTransform(angle / 3);
e.Graphics.TranslateTransform(-bw2, -bh2);
..use the drawing from above instead of the DrawImage line and move the ResetTransform to the end. You will want to use a different or scaled angle!

XNA: Orthographic Projection that matches Screen Coordinates

I am using XNA with SpriteBatch and custom drawn verticies in parallel. The goal is to have the same coordinate system for both techniques.
That means I need a projection matrix that maps to screen coordinates: (0, 0) is in the top left screen corner, while width and height are determined by the screen resolution.
Matrix.CreateOrthographicOffCenter(0, width, 0, height, -1, 1);
Works well but has the center in the bottom-left corner.
Matrix.CreateOrthographicOffCenter(0, width, height, 0, -1, 1);
Does not display anything at all.
Trying to combine the first projection matrix with a translation and scaling y by -1 does not display anything at all either. Scaling by positive values works well, translation too. But as soon as I scale by a negative value I do not get any output at all.
Any ideas?
PS: For testing purpose I am drawing vertices far beyond the screen coordinates, so I would at least see something if there is some error in translation.
I use this code to initialize my 2D camera for drawing lines, and use a basic custom effect to draw.
Vector2 center;
center.X = Game.GraphicsDevice.Viewport.Width * 0.5f;
center.Y = Game.GraphicsDevice.Viewport.Height * 0.5f;
Matrix View = Matrix.CreateLookAt( new Vector3( center, 0 ), new Vector3( center, 1 ), new Vector3( 0, -1, 0 ) );
Matrix Projection = Matrix.CreateOrthographic( center.X * 2, center.Y * 2, -0.5f, 1 );
Effect
uniform float4x4 xWorld;
uniform float4x4 xViewProjection;
void VS_Basico(in float4 inPos : POSITION, in float4 inColor: COLOR0, out float4 outPos: POSITION, out float4 outColor:COLOR0 )
{
float4 tmp = mul (inPos, xWorld);
outPos = mul (tmp, xViewProjection);
outColor = inColor;
}
technique Lines
{
pass Pass0
{
VertexShader = compile vs_2_0 VS_Basico();
FILLMODE = SOLID;
CULLMODE = NONE;
}
}

Rotating a Microsoft.XNA.Framework.Rectangle and creating a rectangle based on that rotation?

I've been trying to do this for a while but haven't had much success. All I want to do is rotate the rectangle and then create a new rectangle which encompasses the rotated points.
Anyone have any ideas how it should be done properly?
The code I have doesn't work, but I'm not sure where it's going wrong exactly (the numbers make me think it actually works), for example if I have a rectangle with the following values:
{X:865 Y:76 Width:22 Height:164}
The result is:
{X:1863 Y:1740 Width:164 Height:22}
Where it is rotated -1.57094443
What I do is grab all four points of the original rectangle and rotate them with this function:
static public Vector2 RotateVectorAround(Vector2 anchor, Vector2 vector, float rotation)
{
Matrix mat = new Matrix();
mat.Translation = new Vector3(vector.X - anchor.X, vector.Y - anchor.Y, 0);
Matrix rot = new Matrix();
rot = Matrix.CreateRotationZ(rotation);
mat *= rot;
mat.Translation += new Vector3(anchor.X, anchor.Y, 0);
return new Vector2(mat.Translation.X, mat.Translation.Y);
}
Where 'anchor' is the pivot point (I'm not sure if this function is mathematically sound), then I determine the corners of the rotated rectangle with this:
Vector2 newTopLeft = new Vector2( Math.Min(Math.Min(topleft.X, bottomRight.X), Math.Min(bottomleft.X, topright.X)),
Math.Min(Math.Min(topleft.Y, bottomRight.Y), Math.Min(bottomleft.Y, topright.Y)));
Vector2 newBottomRight = new Vector2(
Math.Max(Math.Max(topleft.X, bottomRight.X), Math.Max(bottomleft.X, topright.X)),
Math.Max(Math.Max(topleft.Y, bottomRight.Y), Math.Max(bottomleft.Y, topright.Y) ));
You can multiply the points of the rectangle with a rotation matrix.
so given point P in a rotation will result in point R
where a is the rotation
a = degrees * (PI/180)
Rx = Px * cos(a) + Py * -sin(a)
Ry = Px * sin(a) + Py * cos(a)
to rotate about a point you can substract the pivot point before rotating it and add them after the rotation again (so the rotation is virtually around (0,0)
Px = Px - PivotX
Py = Py - PivotY
Rx = Px * cos(a) + Py * -sin(a)
Ry = Px * sin(a) + Py * cos(a)
Px = Rx + PivotX
Py = Ry + PivotY
I would not use the 3'th dimension here for a 2d rotation
in XNA that is something like (sorry no VStudio here):
point -= pivot
point = Vector2.Transform(point, Matrix.CreateRotationZ(angle));
point += pivot

Categories