# Stuck on rotation problem for days and need help

This topic is 1252 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

This is an example of TrackBall rotation using Quaternions. It is written in C# using managed directX 9 in VS 2013.

With this you can figure out how to rotate things just like in 3DS Max or any other CAD software using C# and .NET.


using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace DX9Sample
{
public delegate void RotationChangedHandler(Object sender, Matrix newRotation);
public class DX9Form : System.Windows.Forms.Form
{
private Device d3dDevice = null;
private VertexBuffer vertexBuffer = null;
Texture texture = null;

public static Quaternion WorldRotation;
public static Matrix WorldMatrix;

static private float elapsedTime;
static private DateTime currentTime;
static private DateTime lastTime;
private int clicked = 0;

const float TRACKBALLSIZE = 0.8f;
const int RENORMCOUNT = 97;

public Control m_Target;
private float m_MouseDownX;
private float m_MouseDownY;
private float m_HalfWidth;  // half the width of m_Target.ClientRectangle
private float m_HalfHeight; // half the height of m_Target.ClientRectangle
private Quaternion m_CurrentRotQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);

public event RotationChangedHandler RotationChanged;

private float xrot = 0.0f;
private float yrot = 0.0f;
private float zrot = 0.0f;

struct Vertex
{
float x, y, z;
float tu, tv;

public Vertex( float _x, float _y, float _z, float _tu, float _tv )
{
x = _x; y = _y; z = _z;
tu = _tu; tv = _tv;
}

public static readonly VertexFormats FVF_Flags = VertexFormats.Position | VertexFormats.Texture1;
};

Vertex[] cubeVertices =
{
new Vertex(-1.0f, 1.0f,-1.0f,  0.0f,0.0f ),
new Vertex( 1.0f, 1.0f,-1.0f,  1.0f,0.0f ),
new Vertex(-1.0f,-1.0f,-1.0f,  0.0f,1.0f ),
new Vertex( 1.0f,-1.0f,-1.0f,  1.0f,1.0f ),

new Vertex(-1.0f, 1.0f, 1.0f,  1.0f,0.0f ),
new Vertex(-1.0f,-1.0f, 1.0f,  1.0f,1.0f ),
new Vertex( 1.0f, 1.0f, 1.0f,  0.0f,0.0f ),
new Vertex( 1.0f,-1.0f, 1.0f,  0.0f,1.0f ),

new Vertex(-1.0f, 1.0f, 1.0f,  0.0f,0.0f ),
new Vertex( 1.0f, 1.0f, 1.0f,  1.0f,0.0f ),
new Vertex(-1.0f, 1.0f,-1.0f,  0.0f,1.0f ),
new Vertex( 1.0f, 1.0f,-1.0f,  1.0f,1.0f ),

new Vertex(-1.0f,-1.0f, 1.0f,  0.0f,0.0f ),
new Vertex(-1.0f,-1.0f,-1.0f,  1.0f,0.0f ),
new Vertex( 1.0f,-1.0f, 1.0f,  0.0f,1.0f ),
new Vertex( 1.0f,-1.0f,-1.0f,  1.0f,1.0f ),

new Vertex( 1.0f, 1.0f,-1.0f,  0.0f,0.0f ),
new Vertex( 1.0f, 1.0f, 1.0f,  1.0f,0.0f ),
new Vertex( 1.0f,-1.0f,-1.0f,  0.0f,1.0f ),
new Vertex( 1.0f,-1.0f, 1.0f,  1.0f,1.0f ),

new Vertex(-1.0f, 1.0f,-1.0f,  1.0f,0.0f ),
new Vertex(-1.0f,-1.0f,-1.0f,  1.0f,1.0f ),
new Vertex(-1.0f, 1.0f, 1.0f,  0.0f,0.0f ),
new Vertex(-1.0f,-1.0f, 1.0f,  0.0f,1.0f )
};

public DX9Form()
{
m_Target = DX9Form.ActiveForm;
this.ClientSize = new Size( 640, 480 );
this.Text = "Direct3D (DX9/C#) - Vertex Data";
this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true );
}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
this.Render();
this.Invalidate();
}

protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
{
switch( e.KeyCode )
{
case System.Windows.Forms.Keys.Escape:
this.Dispose();
break;
}
}

protected override void Dispose(bool disposing)
{
base.Dispose( disposing );
}

static void Main()
{
using( DX9Form frm = new DX9Form() )
{
frm.InitializeComponent();
frm.Show();
frm.Init();
Application.Run( frm );
}
}

{
try
{
Bitmap testImage = (Bitmap)Bitmap.FromFile( "test.bmp" );
texture = Texture.FromBitmap( d3dDevice, testImage, 0, Pool.Managed );
}
catch
{
// We must be running from within Visual Studio. Relocate the
// current directory and try again.
System.IO.Directory.SetCurrentDirectory(
System.Windows.Forms.Application.StartupPath +  @"\..\..\");

Bitmap testImage = (Bitmap)Bitmap.FromFile( "test.bmp" );
texture = Texture.FromBitmap( d3dDevice, testImage, 0, Pool.Managed );
}

d3dDevice.SamplerState[0].MinFilter = TextureFilter.Linear;
d3dDevice.SamplerState[0].MagFilter = TextureFilter.Linear;
}

/// <summary>
/// This method basically creates and initialize the Direct3D device and
/// anything else that doens't need to be recreated after a device
/// reset.
/// </summary>
private void Init()
{
//
// Do we support hardware vertex processing? If so, use it.
// If not, downgrade to software.
//

Caps caps = Manager.GetDeviceCaps( Manager.Adapters.Default.Adapter, DeviceType.Hardware );
CreateFlags flags;

if( caps.DeviceCaps.SupportsHardwareTransformAndLight )
flags = CreateFlags.HardwareVertexProcessing;
else
flags = CreateFlags.SoftwareVertexProcessing;

//
// Everything checks out - create a simple, windowed device.
//

PresentParameters d3dpp = new PresentParameters();

d3dpp.BackBufferFormat       = Format.Unknown;
d3dpp.Windowed               = true;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = DepthFormat.D16;
d3dpp.PresentationInterval   = PresentInterval.Immediate;

d3dDevice = new Device( 0, DeviceType.Hardware, this, flags, d3dpp );

// Register an event-handler for DeviceReset and call it to continue
// our setup.
d3dDevice.DeviceReset += new System.EventHandler( this.OnResetDevice );
OnResetDevice( d3dDevice, null );
}

/// <summary>
/// This event-handler is a good place to create and initialize any
/// Direct3D related objects, which may become invalid during a
/// device reset.
/// </summary>
public void OnResetDevice(object sender, EventArgs e)
{
Device device = (Device)sender;

device.RenderState.ZBufferEnable = true;
device.RenderState.Lighting = false;

vertexBuffer = new VertexBuffer( typeof(Vertex),
cubeVertices.Length, device,
Usage.Dynamic | Usage.WriteOnly,
Vertex.FVF_Flags,
Pool.Default );

GraphicsStream gStream = vertexBuffer.Lock( 0, 0, LockFlags.None );

// Now, copy the vertex data into the vertex buffer
gStream.Write( cubeVertices );

vertexBuffer.Unlock();

}

/// <summary>
/// This method is dedicated completely to rendering our 3D scene and is
/// is called by the OnPaint() event-handler.
/// </summary>
private void Render()
{
d3dDevice.Clear( ClearFlags.Target | ClearFlags.ZBuffer,
Color.FromArgb(255, 0, 0, 0), 1.0f, 0 );

d3dDevice.BeginScene();

if (clicked == 0)
{
d3dDevice.Transform.World = Matrix.Translation(0.0f, 0.0f, 5.0f);
}
else
{
d3dDevice.Transform.World = WorldMatrix * Matrix.Translation(0.0f, 0.0f, 5.0f);
}
d3dDevice.Transform.Projection = Matrix.PerspectiveFovLH(Geometry.DegreeToRadian(45.0f), (float)this.ClientSize.Width / this.ClientSize.Height, 0.1f, 100.0f);

d3dDevice.VertexFormat = Vertex.FVF_Flags;
d3dDevice.SetStreamSource( 0, vertexBuffer, 0 );

d3dDevice.SetTexture( 0, texture );

d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip,  0, 2 );
d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip,  4, 2 );
d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip,  8, 2 );
d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 12, 2 );
d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 16, 2 );
d3dDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 20, 2 );

d3dDevice.EndScene();

d3dDevice.Present();
}

private void InitializeComponent()
{
//  this.SuspendLayout();
//
// DX9Form
//
this.ClientSize = new System.Drawing.Size(284, 261);
this.Name = "DX9Form";
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DX9Form_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DX9Form_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DX9Form_MouseUp);
this.ResumeLayout(false);

}

private void DX9Form_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.m_Target.Capture = true;

//    System.Diagnostics.Debug.Assert( m_Target.ClientRectangle.Left == 0 );
//    System.Diagnostics.Debug.Assert( m_Target.ClientRectangle.Top == 0 );

m_HalfWidth = m_Target.ClientRectangle.Width / 2.0f;
m_HalfHeight = m_Target.ClientRectangle.Height / 2.0f;

m_MouseDownX = (e.X - m_HalfWidth) / m_HalfWidth;
m_MouseDownY = (m_HalfHeight - e.Y) / m_HalfHeight;
}
}

private void DX9Form_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_Target.Capture = false;

float curX = (e.X - m_HalfWidth) / m_HalfWidth;
float curY = (m_HalfHeight - e.Y) / m_HalfHeight;

Quaternion curRot = ComputeRotation(m_MouseDownX, m_MouseDownY, curX, curY);
m_CurrentRotQuat = AddQuats(m_CurrentRotQuat, curRot);

if (m_QuatAddCount++ > RENORMCOUNT)
{
m_CurrentRotQuat.Normalize();
}

Matrix m = Matrix.RotationQuaternion(m_CurrentRotQuat);

WorldMatrix = m;
}
}

private void DX9Form_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
float curX = (e.X - m_HalfWidth) / m_HalfWidth;
float curY = (m_HalfHeight - e.Y) / m_HalfHeight;

Quaternion curRot = ComputeRotation(m_MouseDownX, m_MouseDownY, curX, curY);

Matrix m = Matrix.RotationQuaternion(AddQuats(m_CurrentRotQuat, curRot));

WorldMatrix = m; clicked = 1;
}
}

public Control Target
{
get { return m_Target; }
set
{
m_Target = value;
}
}

/// <summary>
/// This function computes a rotation quat for two points representing the
/// beginning and end points of a drag on the virtual trackball.
/// </summary>
/// <param name="p1x">Beginning point's x value (specified in units where -1.0 to 1.0 represents client width).</param>
/// <param name="p1y">Beginning point's y value (specified in units where -1.0 to 1.0 represents client height).</param>
/// <param name="p2x">Ending point's x value (specified in units where -1.0 to 1.0 represents client width).</param>
/// <param name="p2y">Ending point's y value (specified in units where -1.0 to 1.0 represents client height).</param>
/// <returns>a quarternion representing the rotation.</returns>
public static Quaternion ComputeRotation(float p1x, float p1y, float p2x, float p2y)
{
Vector3 axis;  // axis of rotation
Vector3 p1;
Vector3 p2;
Vector3 d;
float phi;     // how much to rotate about the axis
float t;

if ((p1x == p2x) && (p1y == p2y))
{
// Zero rotation
return new Quaternion(0, 0, 0, 1.0f);
}

//
// First, figure out z-coordinates for projection of P1 and P2 to
// deformed sphere
//
p1 = new Vector3(p1x, p1y, p_ProjectToSphere(TRACKBALLSIZE, p1x, p1y));
p2 = new Vector3(p2x, p2y, p_ProjectToSphere(TRACKBALLSIZE, p2x, p2y));

//
// Now, we want the cross product of P1 and P2
//
axis = Vector3.Cross(p1, p2);

//
// Figure out how much to rotate around that axis.
//
d = p1 - p2;
t = d.Length() / (2.0f * TRACKBALLSIZE);

//
// Avoid problems with out-of-control values...
//
if (t > 1.0f) t = 1.0f;
if (t < -1.0f) t = -1.0f;
phi = 2.0f * (float)System.Math.Asin(t);

return Quaternion.RotationAxis(axis, phi);
}

public Quaternion AddQuats(Quaternion q1, Quaternion q2)
{
Vector3 q1v3 = new Vector3(q1.X, q1.Y, q1.Z);
Vector3 q2v3 = new Vector3(q2.X, q2.Y, q2.Z);

Vector3 t1 = new Vector3(q1.X, q1.Y, q1.Z);
t1.Scale(q2.W);

Vector3 t2 = new Vector3(q2.X, q2.Y, q2.Z);
t2.Scale(q1.W);

Vector3 t3 = Vector3.Cross(new Vector3(q2.X, q2.Y, q2.Z), new Vector3(q1.X, q1.Y, q1.Z));
Vector3 tf = t1 + t2;
tf = tf + t3;
float tfW = q1.W * q2.W - Vector3.Dot(q1v3, q2v3);

return new Quaternion(tf.X, tf.Y, tf.Z, tfW);
}

private static float p_ProjectToSphere(float r, float x, float y)
{
float d, t, z;

d = (float)System.Math.Sqrt(x * x + y * y);

if (d < r * 0.70710678118654752440)
{
// Inside sphere
z = (float)System.Math.Sqrt(r * r - d * d);
}
else
{
// On hyperbola
t = (float)(r / 1.41421356237309504880);
z = t * t / d;
}

return -z;
}
}
}


Edited by bsharp

##### Share on other sites

Figured it out!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Yay me!

I updated the code so if there is some other poor smuck that runs into this hopefully they wont have to spend a few days like I did trying to figure out how to do this.

It just does the rotation but adding translation "pan" and scaling "zoom" will be a walk in the park after fighting with the rotation.

Thanks to Kevin Harris at codesampler.com for providing the simple Vertex data demo and also Jonathan Leonard for the TrackBall code I found on SourceForge.

PS Screw You macosxnerd101 on Dreamincode.net! I didnt need your crappy site or flaming help anyway.

1. 1
2. 2
3. 3
4. 4
Rutin
16
5. 5

• 12
• 9
• 12
• 37
• 12
• ### Forum Statistics

• Total Topics
631419
• Total Posts
2999981
×