
ArcBall
Posted Saturday, 24 October, 2009 - 19:35 by rakkarage infound this arcball code online
using System; using System.Drawing; namespace arcball { /* * This code is a part of NeHe tutorials and was converted from C++ into C#. * Matrix/Vector manipulations have been updated. */ public class arcball { private const float Epsilon = 1.0e-5f; private Vector3f StVec; //Saved click vector private Vector3f EnVec; //Saved drag vector private float adjustWidth; //Mouse bounds width private float adjustHeight; //Mouse bounds height public arcball(float NewWidth, float NewHeight) { StVec = new Vector3f(); EnVec = new Vector3f(); setBounds(NewWidth, NewHeight); } private void mapToSphere(Point point, Vector3f vector) { Point2f tempPoint = new Point2f(point.X, point.Y); //Adjust point coords and scale down to range of [-1 ... 1] tempPoint.x = (tempPoint.x * this.adjustWidth) - 1.0f; tempPoint.y = 1.0f - (tempPoint.y * this.adjustHeight); //Compute square of the length of the vector from this point to the center float length = (tempPoint.x * tempPoint.x) + (tempPoint.y * tempPoint.y); //If the point is mapped outside the sphere... (length > radius squared) if (length > 1.0f) { //Compute a normalizing factor (radius / sqrt(length)) float norm = (float)(1.0 / Math.Sqrt(length)); //Return the "normalized" vector, a point on the sphere vector.x = tempPoint.x * norm; vector.y = tempPoint.y * norm; vector.z = 0.0f; } //Else it's inside else { //Return a vector to a point mapped inside the sphere sqrt(radius squared - length) vector.x = tempPoint.x; vector.y = tempPoint.y; vector.z = (float)System.Math.Sqrt(1.0f - length); } } public void setBounds(float NewWidth, float NewHeight) { //Set adjustment factor for width/height adjustWidth = 1.0f / ((NewWidth - 1.0f) * 0.5f); adjustHeight = 1.0f / ((NewHeight - 1.0f) * 0.5f); } //Mouse down public virtual void click(Point NewPt) { mapToSphere(NewPt, this.StVec); } //Mouse drag, calculate rotation public void drag(Point NewPt, Quat4f NewRot) { //Map the point to the sphere this.mapToSphere(NewPt, EnVec); //Return the quaternion equivalent to the rotation if (NewRot != null) { Vector3f Perp = new Vector3f(); //Compute the vector perpendicular to the begin and end vectors Vector3f.cross(Perp, StVec, EnVec); //Compute the length of the perpendicular vector if (Perp.length() > Epsilon) //if its non-zero { //We're ok, so return the perpendicular vector as the transform after all NewRot.x = Perp.x; NewRot.y = Perp.y; NewRot.z = Perp.z; //In the quaternion values, w is cosine (theta / 2), where theta is the rotation angle NewRot.w = Vector3f.dot(StVec, EnVec); } //if it is zero else { //The begin and end vectors coincide, so return an identity transform NewRot.x = NewRot.y = NewRot.z = NewRot.w = 0.0f; } } } } public class Matrix4f { private float[,] M; public Quat4f Rotation { set { float n, s; float xs, ys, zs; float wx, wy, wz; float xx, xy, xz; float yy, yz, zz; M = new float[4, 4]; n = (value.x * value.x) + (value.y * value.y) + (value.z * value.z) + (value.w * value.w); s = (n > 0.0f) ? 2.0f / n : 0.0f; xs = value.x * s; ys = value.y * s; zs = value.z * s; wx = value.w * xs; wy = value.w * ys; wz = value.w * zs; xx = value.x * xs; xy = value.x * ys; xz = value.x * zs; yy = value.y * ys; yz = value.y * zs; zz = value.z * zs; M[0, 0] = 1.0f - (yy + zz); M[0, 1] = xy - wz; M[0, 2] = xz + wy; M[1, 0] = xy + wz; M[1, 1] = 1.0f - (xx + zz); M[1, 2] = yz - wx; M[2, 0] = xz - wy; M[2, 1] = yz + wx; M[2, 2] = 1.0f - (xx + yy); M[3, 3] = 1.0f; } } public Matrix4f() { setIdentity(); } public void get_Renamed(float[] dest) { int k = 0; for (int i = 0; i <= 3; i++) for (int j = 0; j <= 3; j++) { dest[k] = this.M[j, i]; k++; } } public void setZero() { this.M = new float[4, 4]; // set to zero } public void setIdentity() { this.M = new float[4, 4]; // set to zero for (int i = 0; i <= 3; i++) this.M[i, i] = 1.0f; } public void set_Renamed(Matrix4f m1) { this.M = m1.M; } public void mul(Matrix4f m1, Matrix4f m2) { float[] MulMat = new float[16]; float elMat = 0.0f; int k = 0; for (int i = 0; i <= 3; i++) for (int j = 0; j <= 3; j++) { for (int l = 0; l <= 3; l++) elMat += m1.M[i, l] * m2.M[l, j]; MulMat[k] = elMat; elMat = 0.0f; k++; } k = 0; for (int i = 0; i <= 3; i++) for (int j = 0; j <= 3; j++) { m1.M[i, j] = MulMat[k]; k++; } } } public class Point2f { public float x, y; public Point2f(float x, float y) { this.x = x; this.y = y; } } public class Quat4f { public float x, y, z, w; } public class Vector3f { public float x, y, z; public static void cross(Vector3f Result, Vector3f v1, Vector3f v2) { Result.x = (v1.y * v2.z) - (v1.z * v2.y); Result.y = (v1.z * v2.x) - (v1.x * v2.z); Result.z = (v1.x * v2.y) - (v1.y * v2.x); } public static float dot(Vector3f v1, Vector3f v2) { return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z + v2.z); } public virtual float length() { return (float)System.Math.Sqrt(x * x + y * y + z * z); } } }
converted it to opentk
using System; using System.Drawing; using OpenTK; using OpenTK.Graphics; namespace Test { public class ArcBall { private Vector3 _start = Vector3.Zero; private Vector3 _end = Vector3.Zero; private float _adjustWidth = 0.0f; private float _adjustHeight = 0.0f; public void Resize(float width, float height) { _adjustWidth = 1.0f / ((width - 1.0f) * 0.5f); _adjustHeight = 1.0f / ((height - 1.0f) * 0.5f); } public void Click(Vector2 point) { MapToSphere(point, ref _start); } public Quaternion Drag(Vector2 point) { MapToSphere(point, ref _end); Quaternion rotate = Quaternion.Identity; Vector3 perpendicular = Vector3.Cross(_start, _end); if (perpendicular.Length > float.Epsilon) { rotate.X = perpendicular.X; rotate.Y = perpendicular.Y; rotate.Z = perpendicular.Z; rotate.W = Vector3.Dot(_start, _end); } return rotate; } private void MapToSphere(Vector2 point, ref Vector3 vector) { point.X = (point.X * _adjustWidth) - 1.0f; point.Y = 1.0f - (point.Y * _adjustHeight); float length = (point.X * point.X) + (point.Y * point.Y); if (length > 1.0f) { float norm = (float)(1.0 / Math.Sqrt(length)); vector.X = point.X * norm; vector.Y = point.Y * norm; vector.Z = 0.0f; } else { vector.X = point.X; vector.Y = point.Y; vector.Z = (float)Math.Sqrt(1.0f - point.Length); } } } }
it does not work... but i guess i am using it wrong? or maybe i translated it wrong... arcball usually used to rotate around an object? it can be used to adjust the camera with the mouse too right? i guess i have to invert something? ya i dont know what i am doing
_m *= Matrix4.Rotate(_arcBall.Drag(new Vector2(e.X, e.Y)));


Comments
Re: ArcBall
o i fixed it... kinda point.Length in last line should be length
Re: ArcBall
but its still like i am outside the sphere instead of inside?
Re: ArcBall - Are _start/_end Vector3's the rotation's center?
Hi rakkarage,
I know it's over a year and a half since you wrote here, but I think I've got a small idea about the usage of your code.
[I've not verified this, but I will do in my own code but,] I'm wondering should the Vector3, _start, be assignable, as this seems to signify the center of the ArcBall?
Basically, in your code, _start ALWAYS represent the point 0, 0, 0. When I use this code, as is, in my current project, it appears to rotate my OpenGL (OpenTK) object around one corner. I know that my object, a lattice of cubes, is drawn beginning at point 0, 0, 0 as the bottom left-hand corner, and expands positively along the X and Y axis (to the right and up) and negatively along the Z axis (away from the camera). So, my thinking is, that if I set _start and _end to the actual centre of that cube, i.e. half-way along the maxium value in each axis direction I mention, I should be able to have my cube spin around its center, using a simple modification to your code.
Bearing in mind that the above is simply a theory (I'm writing this at work, don't have access to my code base), I think this might help.
I'll try to verify this, this week, and I re-post my findings.
Cheers
Rich
P.S. I'm putting the word 'Trackball' into this post for search purposes (this solution appears to also be called that, if I understand correctly).
Re: ArcBall
Hi rakkarage. You could use my code of trackball rotation. I dont acclimatize this code to OpenTK yet.
Re: ArcBall - Solovey's Trackball code converted to OpenTK
Hi Solovey,
I've quickly converted your Trackball code to OpenTK, I think it works fine, I'm still having issues rotating my cube around its center, I've read elsewhere that I might need to do a further translation on my final matrix.
Anyway, I though I'd post the OpenTK-based code here. I've fiddled with the precision slightly (now uses less precise floats instead of doubles - means I didn't have to write so many casts (lazy)). There is an unused variable, z2, in projectTBToSphere() method , was it needed for anything?
Thanks
Big Rich
Re: ArcBall
Hi, rakkarage. To get a final matrix you need to make some multiplies. I mean translate to center and translate to distance where you camera stand.
Where _center is the vector loocking at the point arround wich you are rotating.
_distance - I think it's clear)
If I understood correctly, you want to rotate camera arround the point. Did I?
Re: ArcBall
Sorry for the thread-o-mancy but has anyone ever got the original code to work properly? I've got it implemented at the moment but when I click and drag I keep getting reset to the same point on my object (I.E. I can rotate it 30 degrees left or right but as soon as I click and drag on a new location I start dragging from the original point again). I'm wondering if it's just how I've implemented it that' is causing the problem. Currently I have a global Matrix4 called arcRotationMatrix which is acted upon when I mouseDown and when I mouseMove (with a button depressed). I then GL.MultMatrix with the arcRotationMatrix in the main rendering loop... have I just got this backwards or is it a problem with the code itself?
Re: ArcBall
So, could you please post the final code?
It will be very usufull.