WPF AxisAngleRotation3D vs. XNA CreateFromAxisAngle - c#

I am pretty dumb to 3D, just like my question may be:
I made a "viewer" program in WPF that renders stuff on screen, and what may be rotated as well. I use this for rotation what works for my taste:
Code: WPF
Transform3DGroup tg = new Transform3DGroup();
tg.Children.Add(new ScaleTransform3D(sx, sy, 1));
tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), rx)));
tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), ry)));
tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), rz)));
tg.Children.Add(new TranslateTransform3D(x, y, z));
darabka.Transform = tg;
TeljesModell.Children.Add(darabka);
I decided to make this program in XNA, because that seems to be a bit faster, however I could not make it to work.
Tried this way (Code, XNA):
Pozició = Matrix.CreateScale(sx, sy, 1f); //méretezés
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitX, rx); //forgatás
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitY, ry);
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitZ, rz);
Pozició *= Matrix.CreateTranslation(x, y, z); //középpont
I tried even CreateFromYawPitchRoll, CreateRotationX(YZ) too, but without luck, the stuff drawed on screen was always differently rotated. So I guessed I ask the brains here if they know what math I am to put into between the two technologies.
Thanks in advance.
edit: I tried on other forums where I was asked for details. I copy / paste them here too
The XNA code is like:
main
...
protected override void LoadContent()
{
t3 = new Airplane(); //this is a "zone" object, having zone regions and zone objects in it. all of them together give the zone itself as a static object, where player walks in
Kamera.Példány.Pozició = new Vector3(1362, 627, -757); //starting pozition in the zone - this is camera position so the player's starting pozition. (camera is FPS)
...
}
...
protected override void Update(GameTime gameTime)
{
...
Kameramozgatása(); //kamera mozgatását vezérlő billentyűk
//this moves the FPS camera around
}
...
protected override void Draw(GameTime gameTime)
{
...
ÁltalánosGrafikaiObjektumok.Effect.View = Kamera.Példány.Idenézünk; //camera views this
ÁltalánosGrafikaiObjektumok.Effect.Projection = projectionMatrix; //... and camera views FROM is set in Kameramozgatása()
...
t3.Rajzolj(); //draw zone object
...
}
zone object
constructor: set the effect and device to the same as main, set the Pozició (a matrix containing the current position and rotations) to origo
...
public virtual Alapmodel Inicializálás(float x, float y, float z, float rx, float ry, float rz, float sx, float sy)
{
//this initializer starts when the coordinates are from a file and not set with Pozició = Matrix....., so we are to convert. this runs only one time. overrides set the vertex positions and textures
VertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
Pozició = Matrix.CreateScale(sx, sy, 1f); //méretezés
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitX, rx); //forgatás
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitY, ry);
Pozició *= Matrix.CreateFromAxisAngle(Vector3.UnitZ, rz);
Pozició *= Matrix.CreateTranslation(x, y, z); //középpont
//we set the starting position - nowhere else we do it, from here only the main loop can modify this,... could.. but does not. this is zone, and has only static objects - never moving
}
public void Rajzolj()
{
//this draws the object (what can be a zone or a static object in it)
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
foreach (Elem elem in Elemek)
{
//végigmegyünk az elemeken
effect.Texture = elem.Textúra;
//pozicionálás
effect.World = Pozició; //we take the initialized position
effect.CommitChanges(); //this is needed. if you do not use this, the textures will be screwed up
device.VertexDeclaration = VertexDeclaration;
device.DrawUserIndexedPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, Poziciók, 0, Poziciók.Length, elem.Indexek, 0, elem.Indexek.Length / 3);
}
pass.End();
}
}
That's it. I still guess it is some conversion and not the drawing code. Nowhere else is the position altered. I guessed that matrices are just like WPFs stacked transforms - my problem was and is that I do not know the math to convert between the two. The WPF code works perfectly, the models in the zone show up good. The XNA code is bad somehow, because the Inicializálás() has wrong conversion from x,y,z,etc. in it. In this I do need help.
Thanks in advance.

I advise against storing your orientation in an angular fashion that way...
But there are times when your issue can be solved in the xna version simply by applying the Z rotation first, Then X, then Y.

You can use CreateWorld too, but that has no scaling in it, so I decided to solve this step-by-step. First is putting to origo, then scaling, then rotating the scaled, then pozitioning the scaled and rotated.
You are to use radian, instead of degree, if you have degree, use the MathHelper.ToRadians(), like I did.
If you come from WPF and your rotation angles are working there, but here not, try to multiply your angle with 360/512 before you calc it to radians. You see example for this in comment, you are to use it to all rx,ry and rz.
Pozició = Matrix.CreateTranslation(Vector3.Zero); //alappozíció az origó
if (sx != 1 || sy != 1) Pozició *= Matrix.CreateScale(sx, sy, 1f); //méretezés
if (rx != 0 || ry != 0 || rz != 0) Pozició *= Matrix.CreateFromYawPitchRoll(
MathHelper.ToRadians(rx) //WPFből: MathHelper.ToRadians(rx * 360f / 512f)
, MathHelper.ToRadians(ry)
, MathHelper.ToRadians(rz)
);
if (x != 0 || y != 0 || z != 0) Pozició *= Matrix.CreateTranslation(x, y, z); //középpont
If you can give faster formula then *360/512, or something that looks better, please comment.

Related

How to do vector addition in Unity

This is my first from scratch game project. I'm trying to make a pinball game but I don't want to just "watch a video on how to make a pinball game". I want to run into the problems and learn how to tackle them as they come.
So far, attaching script to a sprite was issue #1 but I've kinda worked that out. Issue #2 was creating variables and having them translate to real object values. After multiple hours of trial and error I eventually just copied someone elses script that had the most basic setup possible, then broke it and rebuilt it to what I have below with the addition of void Update.
My question is mostly to gather a better understanding but also about a new problem of mine.
Issue #3 is currently when I click play, it moves the object only once. I thought void update is supposed to call every frame?
I would also like to know why when I do transform.position, why can't I do transform.position += (value 1, value 2)? From what I've come up with from experimenting, the only way to alter transform.position is to do = new Vector everytime which I don't fully understand... Another way of wording this part of the question would be: Is there a shorter way of writing a vector transformation or is this the only way the change can be written?
Below is the code. I appreciate any answers even if it's simply directing on the right path to find the information I want.
public float width, height, xSpeed, ySpeed, xPosition, yPosition;
public Vector2 position, scale;
void Start() {
// Initialise the variables
width = 0.5f;
height = 0.5f;
xSpeed = 0;
ySpeed = -1f;
xPosition = 0;
yPosition = 3.5f;
// set the scaling
Vector2 scale = new Vector2(width, height);
transform.localScale = scale;
// set the position
transform.position = new Vector2(xPosition, yPosition);
}
void Update() {
transform.position = new Vector2(xPosition + xSpeed,
yPosition + ySpeed);
}
First I would recommend you changing the title of the question to something that is a bit more on point, so that people have an idea what programming concept your question is about! :) I would recommend something like "How to do vector addition in Unity".
I will go through your questions one-by-one:
Yes, the Update-Function is called every frame! With each call of Update() you set your position to exactly the same value again and again. That is why it is not moving. Neither the xPosition/yPosition nor the xSpeed/ySpeed variables are changing after they have been defined in Start(), so your Update Function will always set your position to (0, 2.5, 0).
You can do Vector addition! But in order to do that, you need to properly write it in your code, by which I mean you need to make a vector out of the values you want to add and only then you can add them to the position vector! So if you wanted to add the xSpeed/ySpeed values ontop of your position, it would look like that:
transform.position += new Vector2(xSpeed, ySpeed);
I hope that helps!
With the help of Jayne, this is what the code ended up turning into, hope this helps anyone else who just wants a straightforward way to alter a vector:
public float width, height, xSpeed, ySpeed, xPosition, yPosition;
public Vector2 position, scale;
void Start() {
// Initialise the variables
width = 0.5f;
height = 0.5f;
xSpeed = 0;
ySpeed = -0.01f;
xPosition = 0;
yPosition = 3.5f;
// set the scaling
Vector2 scale = new Vector2(width, height);
transform.localScale = scale;
// set the position
transform.position = new Vector2(xPosition, yPosition);
}
void Update() {
// Move the pinball
xPosition += xSpeed;
yPosition += ySpeed;
transform.position = new Vector2(xPosition, yPosition);
}

Unity 3D RotateTowards not working need a threshold I think

I have the following code that rotates an object towards a target point at a smooth rate.
public bool RotatedTowards(Vector3 lookAtPoint, float deltaTime)
{
flatDirPath = Vector3.Scale((lookAtPoint - transform.position), new Vector3(1, 0, 1));
targetRotation = Quaternion.LookRotation(flatDirPath, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, deltaTime * basicProperties.turnRate);
if (Vector3.Dot(flatDirPath.normalized, transform.forward) >= 0.9f)
{
return true;
}
else
{
return false;
}
}
It works well enough, the problem is if I right click around a point to move to really fast (telling this object to rotate toward a very similar but different lookAtPoint at a very high frequency), the object shakes very slightly as its constantly making small rotational changes (I believe that's whats happening).
I believe the best solution may be to only do the transform.rotation if the target point is over a threshold from the front of the object, say at least 1 degree to the left or the right of the direction the object is already facing. How could I test this? If it's under 1 degree, no point rotating toward it, and that should remove the shaking from clicking around the same point really fast. I hope.
Any help is appreciated. Thanks
you can use this: https://docs.unity3d.com/ScriptReference/Quaternion.Angle.html
in your code:
public bool RotatedTowards(Vector3 lookAtPoint, float deltaTime)
{
flatDirPath = Vector3.Scale((lookAtPoint - transform.position), new Vector3(1, 0, 1));
targetRotation = Quaternion.LookRotation(flatDirPath, Vector3.up);
float angle = Quaternion.Angle(transform.rotation, targetRotation);
float threshold = 1;
if(Mathf.Abs(angle) > threshold)
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, deltaTime * basicProperties.turnRate);
return (Vector3.Dot(flatDirPath.normalized, transform.forward) >= 0.9f);
}
if you don't like how this threshold behaves, you can try another approach:
use Queue<Vector3> to store the last few flatDirPath vectors (last 10, for example), then add a new vector each time and remove the oldest vector each time - then compute the average vector from all vectors in the Queue and use that in Quaternion.LookRotation - you should get a more smooth behavior. the rotation will lag behind a bit with more vectors you store in the Queue, so use 4-6 vectors for a faster response or 10-20 vectors for a more smooth response.

XNA View Matrix Issues

I've been working on something that loads the geometry of a game and follows the players position and view but I've ran into a problem: I can't rotate the view without all the axis being messed up.
Here's a screenshot of the program with a pitch and yaw of 0 (just like the game's client):
http://i.stack.imgur.com/DXhIr.jpg
Here's my view matrix code:
public void UpdateViewMatrix()
{
Vector3 pos = Position;
pos.X *= -1;
pos.Y *= -1;
pos.Z *= -1;
this.ViewMatrix = Matrix.CreateScale(1) * Matrix.CreateLookAt(Vector3.Zero, new Vector3(1, 0, 0), new Vector3(0, 0, 1)) * Matrix.CreateTranslation(pos) * Matrix.CreateFromYawPitchRoll(-MathHelper.PiOver2, -MathHelper.PiOver2, MathHelper.Pi);
this.Frustum.Matrix = (this.ViewMatrix * this.ProjectionMatrix);
if (this.CameraUpdated != null) this.CameraUpdated(this, new EventArgs());
}
I'm unsure why I have to flip all the coordinates as well before I update the view matrix.
Thanks in advance!
I'm unsure why I have to flip all the coordinates as well before I
update the view matrix
You probably don't.
In an Xna first person shooter, an easy way to handle the camera's view matrix to follow a character is to create a copy of that character's world matrix, offset it behind and slightly above, then invert it. That way, all the rotation and position manipulation that is done for the player does not have to be duplicated for the camera. This is a simple example of this:
//player moved and rotated and player's world matrix is determined
Matrix cameraWorld = playerWorld;
cameraWorld.Translation += (cameraWorld.Backward * followingDistance) +
(cameraWorld.Up * verticalOffset);
ViewMatrix = Matrix.Invert(cameraWorld);
Now the camera view matrix is orientated identically as the player and will stick to him like glue.

Moving a 3D object after rotation

I'm having a problem with moving a 3D object after I apply a rotation. Both the move and rotation functions work perfectly on their own. But the problem is when I move an object after a rotation, the object doesn't follow the mouse and goes in weird directions. If anyone can see my flaw, I'd appreciate it. Thanks! Here's my code:
private void Rotate()
{
double angle;
bool willangle = Double.TryParse(AngleRot.Text.ToString(), out angle);
RectangleVisual3D rect = (RectangleVisual3D)LastSelectedObject;
AxisAngleRotation3D r = new AxisAngleRotation3D(new Vector3D(0, 0, 1), angle);
RotateTransform3D rot = new RotateTransform3D(r, rect.Origin);
rect.Transform = Transform3DHelper.CombineTransform(rect.Transform, rot);
LastSelectedObject = rect as ModelVisual3D;
}
private void MoveObject(MouseEventArgs e)
{
if (LastSelectedObject is RectangleVisual3D)
{
RectangleVisual3D rect = (RectangleVisual3D)LastSelectedObject;
Point3D? origin = GetPoints(e);
if (origin == null)
return;
rect.Origin = (Point3D)origin;
LastSelectedObject = rect as ModelVisual3D;
}
}
I hope this help: The order of rotation and move is very important. If you move, then rotate, then it move according to the x,y,z co-ordinates. If you rotate, then move, then it will move according to the rotations co-ordinates.
Moving your object by setting its origin is generally a bad move. If your helper library ( I don't think Transform3DHelper is .Net? ) is doing matrix math in the basic way, then you're messing it up by setting rect.Origin.
Instead, try finding the distance vector moved and apply that translation matrix.
I'm assuming
Vector2D dist=new Vector2D((oldPosition - newPosition).x, (oldPosition - newPosition).y);
TranslateTransform3D trans = new TranslateTransform3D(dist.x,dist.y,0);
rect.Transform = Transform3DHelper.CombineTransform(rect.Transform, trans);
The other possible error is that CombineTransform should reverse rect.Transform and rot, but I'm not sure if the API is handling that for you. See if an overloaded version of the method allows you to reverse those two.

How to map texture to cylinder in unmanaged DirectX?

In C#.net I have a mesh cylinder with a dynamic diameter and length and am trying to map a texture to it. I have spent the better part of a day trying to find out how to do so but have had no success finding any information on Google.
The cylinders texture has a top area of the jpg and the side has the rest of the jpg.
I need to position the jpgs image edge along the top edge of the cylinder. eg. Red on top and green on side using one image.
Can anyone help me to map the VertexBuffer points to the texture?
C#.Net 2008
DirectX 9 (unmanaged)
I Have Posted My Working Solution Below
Although this tutorial is in VB it clearly explains the process.
Calculating the texture coordinates can be quite some work; that is why normally this is done by 3D modeling software so you can easily and, more importantly, visually adjust the mapping.
Let me know if you have any questions.
EDIT
For adding texture coordinates to the DirecxtX generated cylinder see this
Ok, I've finally figured it out. I had some code previously that was working but not exactly what I was wanting from
http://channel9.msdn.com/coding4fun/articles/Ask-the-ZMan-Applying-Textures-Part-3
Anyway, I just did some mods to it.
For reference and for those arriving from Google, here you go.
public static float ComputeBoundingSphere(Mesh mesh, out Microsoft.DirectX.Vector3 center)
{
// Lock the vertex buffer
Microsoft.DirectX.GraphicsStream data = null;
try
{
data = mesh.LockVertexBuffer(LockFlags.ReadOnly);
// Now compute the bounding sphere
return Geometry.ComputeBoundingSphere(data, mesh.NumberVertices,
mesh.VertexFormat, out center);
}
finally
{
// Make sure to unlock the vertex buffer
if (data != null)
mesh.UnlockVertexBuffer();
}
}
private static Mesh SetSphericalTexture(Mesh mesh)
{
Microsoft.DirectX.Vector3 vertexRay;
Microsoft.DirectX.Vector3 meshCenter;
double phi;
float u;
Microsoft.DirectX.Vector3 north = new Microsoft.DirectX.Vector3(0f, 0f, 1f);
Microsoft.DirectX.Vector3 equator = new Microsoft.DirectX.Vector3(0f, 1f, 0f);
Microsoft.DirectX.Vector3 northEquatorCross = Microsoft.DirectX.Vector3.Cross(north, equator);
ComputeBoundingSphere(mesh, out meshCenter);
using (VertexBuffer vb = mesh.VertexBuffer)
{
CustomVertex.PositionNormalTextured[] verts = (CustomVertex.PositionNormalTextured[])vb.Lock(0, typeof(CustomVertex.PositionNormalTextured), LockFlags.None, mesh.NumberVertices);
try
{
for (int i = 0; i < verts.Length; i++)
{
//For each vertex take a ray from the centre of the mesh to the vertex and normalize so the dot products work.
vertexRay = Microsoft.DirectX.Vector3.Normalize(verts[i].Position - meshCenter);
phi = Math.Acos((double)vertexRay.Z);
if (vertexRay.Z > -0.9)
{
verts[i].Tv = 0.121f; //percentage of the image being the top side
}
else
verts[i].Tv = (float)(phi / Math.PI);
if (vertexRay.Z == 1.0f || vertexRay.Z == -1.0f)
{
verts[i].Tu = 0.5f;
}
else
{
u = (float)(Math.Acos(Math.Max(Math.Min((double)vertexRay.Y / Math.Sin(phi), 1.0), -1.0)) / (2.0 * Math.PI));
//Since the cross product is just giving us (1,0,0) i.e. the xaxis
//and the dot product was giving us a +ve or -ve angle, we can just compare the x value with 0
verts[i].Tu = (vertexRay.X > 0f) ? u : 1 - u;
}
}
}
finally
{
vb.Unlock();
}
}
return mesh;
}

Categories

Resources