Rotation in XNA - c#

I have a question concerning model rotation in XNA. The question is - what should I do (which values should be changed and how) to rotate green model in that way (red arrows):
http://img843.imageshack.us/i/question1.jpg/
Code used to draw :
DrawModel(elementD, new Vector3(-1, 0.5f, 0.55f), 0,-90,0);
private void DrawModel(Model model, Vector3 position, float rotXInDeg, float rotYInDeg, float rotZInDeg)
{
float rotX = (float)(rotXInDeg * Math.PI / 180);
float rotY = (float)(rotYInDeg * Math.PI / 180);
float rotZ = (float)(rotZInDeg * Math.PI / 180);
Matrix worldMatrix = Matrix.CreateScale(0.5f, 0.5f, 0.5f) * Matrix.CreateRotationY(rotY) *Matrix.CreateRotationX(rotX)*Matrix.CreateRotationZ(rotZ)* Matrix.CreateTranslation(position);
Matrix[] xwingTransforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(xwingTransforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.View = cam.viewMatrix;
effect.Projection = cam.projectionMatrix;
effect.World = (xwingTransforms[mesh.ParentBone.Index] * worldMatrix);
}
mesh.Draw();
}
}
So I tried to apply your solution by changing my code slightly:
Matrix worldMatrix = Matrix.CreateScale(0.5f, 0.5f, 0.5f) * Matrix.CreateRotationY(rotY) * Matrix.CreateRotationX(rotX) * Matrix.CreateTranslation(position)
* Matrix.CreateRotationX((float)(45 * Math.PI / 180)) * Matrix.CreateRotationZ(rotZ) * Matrix.CreateRotationX((float)(-45 * Math.PI / 180));
By changing rotZ parameter indeed I was able to rotate the model. However the effect is not what I wanted to achieve http://img225.imageshack.us/i/questionau.jpg/, it changed its position. Is it because of a faulty model or some other mistake? I want the "cylinder" to remain in its position. Do you know how can I do this?

Rotation with rotation matrices are cummulative. So you can probably calculate your rotation matrix by rotating the model 45 degrees "down", then apply your rotation around your wanted axis, then rotating the model 45 degrees "up" again. The product of these three matrices should give you your desired matrix.

Another option is to use a Quaternion to generation the rotation matrix. A Quaternion is basicly an axis and a rotaion around it. Which may be easier to manipulate in this case?

Related

Random Rotation on a 3D sphere given an angle

This question is in between computer graphic, probability, and programming, but since I am coding it for an Unity project in C# I decided to post it here. Sorry if not appropriate.
I need to solve this problem: given a object on a 3d sphere at a certain position, and given a range of degrees, sample points on the sphere uniformly within the given range.
For example:
Left picture: the cube represents the center of the sphere, the green sphere is the starting position. I want to uniformly cover all surface of the circle within a certain degree, for example from -90 to 90 degrees around the green sphere. My approach (right picture) doesn't work as it over-samples points that are close to the starting position.
My sampler:
Vector3 getRandomEulerAngles(float min, float max)
{
float degree = Random.Range(min, max);
return degree * Vector3.Normalize(new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max)));
}
and for covering the top half of the sphere I would call getRandomEulerAngles(-90, 90).
Any idea?
Try this:
public class Sphere : MonoBehaviour
{
public float Radius = 10f;
public float Angle = 90f;
private void Start()
{
for (int i = 0; i < 10000; i++)
{
var randomPosition = GetRandomPosition(Angle, Radius);
Debug.DrawLine(transform.position, randomPosition, Color.green, 100f);
}
}
private Vector3 GetRandomPosition(float angle, float radius)
{
var rotationX = Quaternion.AngleAxis(Random.Range(-angle, angle), transform.right);
var rotationZ = Quaternion.AngleAxis(Random.Range(-angle, angle), transform.forward);
var position = rotationZ * rotationX * transform.up * radius + transform.position;
return position;
}
}
We can use a uniform sphere sampling for that. Given two random variables u and v (uniformly distributed), we can calculate a random point (p, q, r) on the sphere (also uniformly distributed) with:
float azimuth = v * 2.0 * PI;
float cosDistFromZenith = 1.0 - u;
float sinDistFromZenith = sqrt(1.0 - cosDistFromZenith * cosDistFromZenith);
(p, q, r) = (cos(azimuth) * sinDistFromZenith, sin(azimuth) * sinDistFromZenith, cosDistFromZenith);
If we put our reference direction (your object location) into zenithal position, we need to sample v from [0, 1] to get all directions around the object and u in [cos(minDistance), cos(maxDistance)], where minDistance and maxDistance are the angle distances from the object you want to allow. A distance of 90° or Pi/2 will give you a hemisphere. A distance of 180° or Pi will give you the full sphere.
Now that we can sample the region around the object in zenithal position, we need to account for other object locations as well. Let the object be positioned at (ox, oy, oz), which is a unit vector describing the direction from the sphere center.
We then build a local coordinate system:
rAxis = (ox, oy, oz)
pAxis = if |ox| < 0.9 : (1, 0, 0)
else : (0, 1, 0)
qAxis = normalize(cross(rAxis, pAxis))
pAxis = cross(qAxis, rAxis)
And finally, we can get our random point (x, y, z) on the sphere surface:
(x, y, z) = p * pAxis + q * qAxis + r * rAxis
Adapted from Nice Schertler, this is the code I am using
Vector3 GetRandomAroundSphere(float angleA, float angleB, Vector3 aroundPosition)
{
Assert.IsTrue(angleA >= 0 && angleB >= 0 && angleA <= 180 && angleB <= 180, "Both angles should be[0, 180]");
var v = Random.Range(0F, 1F);
var a = Mathf.Cos(Mathf.Deg2Rad * angleA);
var b = Mathf.Cos(Mathf.Deg2Rad * angleB);
float azimuth = v * 2.0F * UnityEngine.Mathf.PI;
float cosDistFromZenith = Random.Range(Mathf.Min(a, b), Mathf.Max(a, b));
float sinDistFromZenith = UnityEngine.Mathf.Sqrt(1.0F - cosDistFromZenith * cosDistFromZenith);
Vector3 pqr = new Vector3(UnityEngine.Mathf.Cos(azimuth) * sinDistFromZenith, UnityEngine.Mathf.Sin(azimuth) * sinDistFromZenith, cosDistFromZenith);
Vector3 rAxis = aroundPosition; // Vector3.up when around zenith
Vector3 pAxis = UnityEngine.Mathf.Abs(rAxis[0]) < 0.9 ? new Vector3(1F, 0F, 0F) : new Vector3(0F, 1F, 0F);
Vector3 qAxis = Vector3.Normalize(Vector3.Cross(rAxis, pAxis));
pAxis = Vector3.Cross(qAxis, rAxis);
Vector3 position = pqr[0] * pAxis + pqr[1] * qAxis + pqr[2] * rAxis;
return position;
}

Unity - Determining UVs for a circular plane mesh generated by code

I'm trying to generate a circular mesh made up of triangles with a common center at the center of the circle.
The mesh is generated properly, but the UVs are not and I am having some trouble understanding how to add them.
I assumed I would just copy the vertexes' pattern, but it didn't work out.
Here is the function:
private void _MakeMesh(int sides, float radius = 0.5f)
{
m_LiquidMesh.Clear();
float angleStep = 360.0f / (float) sides;
List<Vector3> vertexes = new List<Vector3>();
List<int> triangles = new List<int>();
List<Vector2> uvs = new List<Vector2>();
Quaternion rotation = Quaternion.Euler(0.0f, angleStep, 0.0f);
// Make first triangle.
vertexes.Add(new Vector3(0.0f, 0.0f, 0.0f));
vertexes.Add(new Vector3(radius, 0.0f, 0.0f));
vertexes.Add(rotation * vertexes[1]);
// First UV ??
uvs.Add(new Vector2(0, 0));
uvs.Add(new Vector2(1, 0));
uvs.Add(rotation * uvs[1]);
// Add triangle indices.
triangles.Add(0);
triangles.Add(1);
triangles.Add(2);
for (int i = 0; i < sides - 1; i++)
{
triangles.Add(0);
triangles.Add(vertexes.Count - 1);
triangles.Add(vertexes.Count);
// UV ??
vertexes.Add(rotation * vertexes[vertexes.Count - 1]);
}
m_LiquidMesh.vertices = vertexes.ToArray();
m_LiquidMesh.triangles = triangles.ToArray();
m_LiquidMesh.uv = uvs.ToArray();
m_LiquidMesh.RecalculateNormals();
m_LiquidMesh.RecalculateBounds();
Debug.Log("<color=yellow>Liquid mesh created</color>");
}
How does mapping UV work in a case like this?
Edit: I'm trying to use this circle as an effect of something flowing outwards from the center (think: liquid mesh for a brewing pot)
This is an old post, but maybe someone else will benefit from my solution.
So basically I gave my center point the center of the uv (0.5, 0.5) and then used the used circle formula to give every other point the uv coordinate. But of course I had to remap the cos and sin results from -1..1 to 0..1 and everything is working great.
Vector2[] uv = new Vector2[vertices.Length];
uv[uv.Length - 1] = new Vector2(0.5f, 0.5f);
for (int i = 0; i < uv.Length - 1; i++)
{
float radians = (float) i / (uv.Length - 1) * 2 * Mathf.PI;
uv[i] = new Vector2(Mathf.Cos(radians).Remap(-1f, 1f, 0f, 1f), Mathf.Sin(radians).Remap(-1f, 1f, 0f, 1f));
}
mesh.uv = uv;
Where the remap is an extension like this and it basically take a value in a range and remaps it to another range (in this case from -1..1 to 0..1):
public static float Remap(this float value, float from1, float to1, float from2, float to2) {
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}

Unity Moving Objects using Input.mousePosition

I have a simple paddle that I am trying to move along the X axis using the mouse. Currently it moves but is off by a large margin, relative to where the mouse is.
I think it is due the fact that half my screen is -7.5 and the other half is 7.5
I was wondering if there is any way to correct this problem. As you can see from my code I am multiplying by 16, which would be the width if the other half was not negative.
I can move the whole screenplay to make it not negative, so I was hoping there was a function
Vector3 paddlePos = new Vector3 (0f, this.transform.position.y , -0.25f);
float mousePosInBlocks = Input.mousePosition.x / Screen.width * 16;
paddlePos.x = Mathf.Clamp(mousePosInBlocks, -7.5f, 7.5f);
this.transform.position = paddlePos;
Use this code -
Vector3 paddlePos = new Vector3 (0f, this.transform.position.y , -0.25f);
float mousePosInBlocks = Input.mousePosition.x / Screen.width * 16;
paddlePos.x = Mathf.Clamp((mousePosInBlocks - 7.5f), -7.5f, 7.5f);
this.transform.position = paddlePos;
Mathf.clamp function only returns the value inside the minimum and maximum value range. While you need the mousePosInBlocks value to consider the negative and positive screen space.

C# / OpenTK, why does my sphere not look smooth?

This should hopefully be a simple question. So I finally figured out how to render stuff in 3D in OpenTK. Great! Only problem is, it doesn't quite look how I expect. I'm drawing a sphere using the Polar method, and drawing using PrimitiveType.Polygon.
Here's the algorithm for calculating the coordinates. What I'm doing is stepping through each phi then theta in the sphere, incrementally adding more adjacent quads to my final point list:
Point 1: Theta1, Phi1
Point 2: Theta1, Phi2
Point 3: Theta2, Phi2
Point 4: Theta2: Phi1
protected static RegularPolygon3D _create_unit(int n)
{
List<Vector3> pts = new List<Vector3>();
float theta = 0.0f;
float theta2 = 0.0f;
float phi = 0.0f;
float phi2 = 0.0f;
float segments = n;
float cosT = 0.0f;
float cosT2 = 0.0f;
float cosP = 0.0f;
float cosP2 = 0.0f;
float sinT = 0.0f;
float sinT2 = 0.0f;
float sinP = 0.0f;
float sinP2 = 0.0f;
List<Vector3> current = new List<Vector3>(4);
for (float lat = 0; lat < segments; lat++)
{
phi = (float)Math.PI * (lat / segments);
phi2 = (float)Math.PI * ((lat + 1.0f) / segments);
cosP = (float)Math.Cos(phi);
cosP2 = (float)Math.Cos(phi2);
sinP = (float)Math.Sin(phi);
sinP2 = (float)Math.Sin(phi2);
for (float lon = 0; lon < segments; lon++)
{
current = new List<Vector3>(4);
theta = TWO_PI * (lon / segments);
theta2 = TWO_PI * ((lon + 1.0f) / segments);
cosT = (float)Math.Cos(theta);
cosT2 = (float)Math.Cos(theta2);
sinT = (float)Math.Sin(theta);
sinT2 = (float)Math.Sin(theta2);
current.Add(new Vector3(
cosT * sinP,
sinT * sinP,
cosP
));
current.Add(new Vector3(
cosT * sinP2,
sinT * sinP2,
cosP2
));
current.Add(new Vector3(
cosT2 * sinP2,
sinT2 * sinP2,
cosP2
));
current.Add(new Vector3(
cosT2 * sinP,
sinT2 * sinP,
cosP
));
pts.AddRange(current);
}
}
var rtn = new RegularPolygon3D(pts);
rtn.Translation = Vector3.ZERO;
rtn.Scale = Vector3.ONE;
return rtn;
}
And so my Sphere class looks like this:
public class Sphere : RegularPolygon3D
{
public static Sphere Create(Vector3 center, float radius)
{
var rp = RegularPolygon3D.Create(30, center, radius);
return new Sphere(rp);
}
private Sphere(RegularPolygon3D polygon) : base(polygon)
{
}
}
I should also mention, that the color of this sphere is not constant. I 2 dimensions, I have this code that works great for gradients. In 3D...not so much. That's why my sphere has multiple colors. The way the 2d gradient code works, is there is a list of colors coming from a class I created called GeometryColor. When the polygon is rendered, every vertex gets colored based off the list of colors within GeometryColor. So if there are 3 colors the user wished to gradient between, and there were 6 vertices (hexagon), then the code would assign the first 2 vertices color 1, the 2nd two color 2, then the last 2 color 3. The following code shows how the color for the vertex is calculated.
public ColorLibrary.sRGB GetVertexFillColor(int index)
{
var pct = ((float)index + 1.0f) / (float)Vertices.Count;
var colorIdx = (int)Math.Round((FillColor.Colors.Count - 1.0f) * pct);
return FillColor.Colors[colorIdx];
}
Anyway, here's the output I'm getting...hope somebody can see my error...
Thanks.
Edit: If I only use ONE Vertex color (i,e instead of my array of 4 diff colors), then I get a completely smooth sphere...although without lighting and stuff its hard to tell its anything but a circle lol)
Edit....so somehow my sphere is slightly see through...even though all my alphas are set to 1.0f and I'm doing depth testing..
GL.DepthMask(true);
GL.Enable(EnableCap.DepthTest);
GL.ClearDepth(1.0f);
GL.DepthFunc(DepthFunction.Lequal);
Final edit: OK, it has SOMETHING to do with my vertices I'm guessing, because when I use PrimitiveType.Quads it works perfectly....

Getting Mouse Coordinates from 2D Camera and Independent Resolution

I've been trying to combine these two samples from David Amador:
http://www.david-amador.com/2010/03/xna-2d-independent-resolution-rendering/
http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/
Everything is working fine except I'm having some difficulty getting the mouse coordinates. I was able to get them for each individual sample, but my math for taking both into account seems to be wrong.
The mouse coordinates ARE correct if my virtual resolution and normal resolution are the same. It's when I do something like Resolution.SetVirtualResolution(1920, 1080)
and Resolution.SetResolution(1280, 720, false) when the coordinates slowly get out of sync as I move the camera.
Here is the code:
public static Vector2 MousePositionCamera(Camera camera)
{
float MouseWorldX = (Mouse.GetState().X - Resolution.VirtualWidth * 0.5f + (camera.position.X) * (float)Math.Pow(camera.Zoom, 1)) /
(float)Math.Pow(camera.Zoom, 1);
float MouseWorldY = ((Mouse.GetState().Y - Resolution.VirtualHeight * 0.5f + (camera.position.Y) * (float)Math.Pow(camera.Zoom, 1))) /
(float)Math.Pow(camera.Zoom, 1);
Vector2 mousePosition = new Vector2(MouseWorldX, MouseWorldY);
Vector2 virtualViewport = new Vector2(Resolution.VirtualViewportX, Resolution.VirtualViewportY);
mousePosition = Vector2.Transform(mousePosition - virtualViewport, Matrix.Invert(Resolution.getTransformationMatrix()));
return mousePosition;
}
In resolution I added this:
virtualViewportX = (_Device.PreferredBackBufferWidth / 2) - (width / 2);
virtualViewportY = (_Device.PreferredBackBufferHeight / 2) - (height / 2);
Everything else is the same as the sample code. Thanks in advance!
Thanks to David Gouveia I was able to identify the problem... My camera matrix was using the wrong math.
I'm going to post all of my code with the hopes of helping someone who is trying to do something similar.
Camera transformation matrix:
public Matrix GetTransformMatrix()
{
transform = Matrix.CreateTranslation(new Vector3(-position.X, -position.Y, 0)) * Matrix.CreateRotationZ(rotation) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) * Matrix.CreateTranslation(new Vector3(Resolution.VirtualWidth
* 0.5f, Resolution.VirtualHeight * 0.5f, 0));
return transform;
}
That will also center the camera. Here's how you get the mouse coordinates combining both the Resolution class and camera class:
public static Vector2 MousePositionCamera(Camera camera)
{
Vector2 mousePosition;
mousePosition.X = Mouse.GetState().X;
mousePosition.Y = Mouse.GetState().Y;
//Adjust for resolutions like 800 x 600 that are letter boxed on the Y:
mousePosition.Y -= Resolution.VirtualViewportY;
Vector2 screenPosition = Vector2.Transform(mousePosition, Matrix.Invert(Resolution.getTransformationMatrix()));
Vector2 worldPosition = Vector2.Transform(screenPosition, Matrix.Invert(camera.GetTransformMatrix()));
return worldPosition;
}
Combined with all of the other code I posted/mentioned this should be all you need to achieve resolution independence and an awesome camera!

Categories

Resources