Quaternions are pretty unintuitive yes. However, once you get familiar with them they're not that difficult to use & don't require a ton of code either. Below is a very basic example ( use arrow keys + A/D to yaw ).
Code: Select all
<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Quaternion" CameraPosition="0 0 0" FOV="60" RenderOrder="1" FileVersion="2">
<OnLoaded>
<ZLibrary Comment="Quaternion">
<Source>
<![CDATA[//
vec4 quaternionMultiply(vec4 a, vec4 b)
{
vec4 q;
q.x = a.x*b.w+a.w*b.x+a.y*b.z-a.z*b.y;
q.y = a.y*b.w+a.w*b.y+a.z*b.x-a.x*b.z;
q.z = a.z*b.w+a.w*b.z+a.x*b.y-a.y*b.x;
q.w = a.w*b.w-a.x*b.x-a.y*b.y-a.z*b.z;
return q;
}
//
vec4 axisAngleToQuaternion(vec3 axis, float angle)
{
float h, s, x, y, z, w;
h = angle*0.5;
s = sin(h);
x = axis.x*s;
y = axis.y*s;
z = axis.z*s;
w = cos(h);
return vector4(x, y, z, w);
}
//
mat4 rotationMatrix(vec4 q)
{
mat4 m;
float x2, y2, z2, xx, xy, xz, yy, yz, zz, wx, wy, wz;
x2 = q.x*2;
y2 = q.y*2;
z2 = q.z*2;
xx = q.x*x2; xy = q.x*y2; xz = q.x*z2;
yy = q.y*y2; yz = q.y*z2; zz = q.z*z2;
wx = q.w*x2; wy = q.w*y2; wz = q.w*z2;
m[0,0] = 1-(yy+zz);
m[0,1] = xy+wz;
m[0,2] = xz-wy;
m[0,3] = 0;
m[1,0] = xy-wz;
m[1,1] = 1-(xx+zz);
m[1,2] = yz+wx;
m[1,3] = 0;
m[2,0] = xz+wy;
m[2,1] = yz-wx;
m[2,2] = 1-(xx+yy);
m[2,3] = 0;
m[3,0] = 0;
m[3,1] = 0;
m[3,2] = 0;
m[3,3] = 1;
return m;
}]]>
</Source>
</ZLibrary>
<ZExpression>
<Expression>
<![CDATA[//
CameraQuaternion = vector4(0, 0, 0, 1);]]>
</Expression>
</ZExpression>
</OnLoaded>
<OnUpdate>
<Repeat Name="AxisRepeat" Count="3" WhileExp="//">
<OnIteration>
<ZExpression>
<Expression>
<![CDATA[//
int i = AxisRepeat.Iteration;
//
Axis[i] = 0;
//
AxisPositive.CharCode = AxisMap[i,0];
AxisNegative.CharCode = AxisMap[i,1];]]>
</Expression>
</ZExpression>
<KeyPress Name="AxisPositive" CharCode="68">
<OnPressed>
<ZExpression>
<Expression>
<![CDATA[//
Axis[AxisRepeat.Iteration]++;]]>
</Expression>
</ZExpression>
</OnPressed>
</KeyPress>
<KeyPress Name="AxisNegative" CharCode="65">
<OnPressed>
<ZExpression>
<Expression>
<![CDATA[//
Axis[AxisRepeat.Iteration]--;]]>
</Expression>
</ZExpression>
</OnPressed>
</KeyPress>
</OnIteration>
</Repeat>
<ZExpression>
<Expression>
<![CDATA[//
float dt, rx, ry, rz;
dt = App.DeltaTime;
rx = Axis[2]*2*dt;
ry = Axis[1]*2*dt;
rz = Axis[0]*2*dt;
//
vec4 qx, qy, qz, q;
qx = axisAngleToQuaternion(vector3(0, 1, 0), rx);
qy = axisAngleToQuaternion(vector3(1, 0, 0), ry);
qz = axisAngleToQuaternion(vector3(0, 0, 1), rz);
q = quaternionMultiply(qz, qy);
q = quaternionMultiply(qx, q);
CameraQuaternion = quaternionMultiply(q, CameraQuaternion);]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<ZExpression>
<Expression>
<![CDATA[//
mat4 m;
getMatrix(0, m);
setMatrix(0, m * rotationMatrix(CameraQuaternion));]]>
</Expression>
</ZExpression>
<UseMaterial Material="TowerMaterial"/>
<RenderMesh Mesh="TowerMesh"/>
</OnRender>
<Content>
<Array Name="Axis" SizeDim1="3"/>
<Array Name="AxisMap" Type="4" Dimensions="1" SizeDim1="3" SizeDim2="2" Persistent="255">
<Values>
<![CDATA[78DA535755D37071040003820120]]>
</Values>
</Array>
<Variable Name="CameraQuaternion" Type="8"/>
<Mesh Name="TowerMesh">
<Producers>
<MeshImport HasVertexColors="1">
<MeshData>
<![CDATA[78DAED985F68D45716C76F7E93D49AA86DE322047C1404591F76B72C298A0F7DD9971542C087CD124D0275184B273A355BDC9FB003BFEC0886914CD8C858AAA9422C04EC834F0B2D851463405CD02E8140DA74A130E082B2E94EAC8D33FEF67EE733D7DF643369E26E17F661907B723CF7DCF3E77BCE3DBFDC8CC58CF97B93311FFFEACFD3FE9F3A3ED7CFBFFCBAF3CDCF87126FEAE740614FE74061A0F0D56703857D65A824A2739E24973BC48B7EF5D99C27FDCB1DD2871795CEE58E3D9DFBCA973BBEFA6C5F79CE133FE789977CA020B9687D1DF10305F1A2115F7B56F2394FF2394F1251BCBB38455D3CF0FBCA92EF2B4B228A77C52F3BA2E265CDE5482EE448EE78711E9DC49D7576B44B840E3D798C6210C2E25D244E224D63C4431DF250C5600CB1C9B231CACE51172708383C8D518E50972F92084FED3A9C25E71416D07172A70F95172898B87CA3D8C0DFF1CA54961D0ECE8B1053FC2029CB512E2E5F973BBB7877B570F8D7C6E36CD6FA7248D6222CB98B4DBB375BB67F79E8C1F42E5163BAE7B2DFA4768B1E7AB0FDCBC32BB797C2F0F692F8430FB2DF74CF85E1E115C9D90D4349A49FDA2D6A8CECDC6C79634718BEB1035EF6A1D3BBE0E5C518D1D46EE9209FDEC5297438CB2E9A78A9AF3FBD0B3931D45AC05794C5E115E257E4D96F9411991E7A20F9A10768C2232747F4391BED8203C8600DCDD46E5163A439BD4BF4668B34A77789DE6C91A631A2A9DDF2F8C68E48E7660B12F4230BB5B11183D344A7D6BE1038BC4265A929F54522DC6E2F092B6AC7EEE195A807C8C218ACE18B98D1C102A76A7DD54AB08F3524EC7296DCC1AA36FEDA53C446FC204C15A80868D3155499EAD32D58266672C1A377D44B0F5D2A1EF1D2F15CF1482615CF7947037FE8523E9949B54D9D3F1EF8E5ABE78F7BE9F2D57CD24BB74DFD2697493D3C62B5D2C5234397BCB47774E852E07B47CB5703FFFCF1B6A94C2A9F4C5E0CFCADFDF1512FFDB4273E1AF84F7BDAA6BC743E59BEEAA5ADC51356BFCF8FDBB3BD7EDCF2BDC109CBF795CE047EF148E98C2C8B1EE891E4404FF95AE027F38F266C9CE38F266C6CE3C98B5E7A6B7FF364E0E7079B27ADE5C1C757BC742EF1F84AE0E712B7AC3C96B965E5B18CFC8A4A52BEE6A593F9A73D811F1FDDDA6FF98B5BFBADE58B67C76D8E13C3799BE3B5E1BCE5AF9D1DB7FC442E11F88FAFE407BD74F3647E30F09B2773092FFDF88AA2BA35A908A16D5392B44DB566F774FA7DADD96CC2EF1B9EC82696534F7B2CAAA3363F3F17F7FA6C8427BC3ECB9FF07A2D1F5F4EEDE91C9E584E6513C3137E5F36D19AF5FBF674B6668727F6742EA7C00D0C830B5E7ABFA581BFBFE7808D7FEC0296C9E580E5C72E100955231EEAF55A26F0DBA7867292BF96F1D2ED53AA5DDB1498A85EF11C5593A474468841F3493CC632AA452CA35A08D5D219218C0E08C773548AAABD730A2A89A22D9D5177898A2717C96319222723F19204FE3BA7C4434B6784EAF084507558811B280967101315E68A2D38A5388353F4067D420515DBAD49C5766B92CABAD894AFFA5F9498858CF85846BBD078EEBC4558FDA95E1526C523DC08FA569817ABB743B8A173A08A21B8390C15A18B5608480255F7CAAF6AF7C5A4AAF6C5E4D805453E7641915353AA492EAE1BC5932337978EA593B9C5F498FA6D39255E54180A55D7BD74B2D0F6FB845E7CD4C5A638856A7C9448864685E1D0A8B025426E283A9C05556E01F71D0CB905200F6ECC046E31379A89C16C89A25D4E111B3301EFDC59EE3251D5D62EEAAE58864CE95B7AD861AE8AD4F63F95A28B8884A8A25D3A6DEC02916093DC9936EC72E39830C4498420467D6BEF26726E01FDC95D002B2609BCE680EDE30A92D1AD59DDB1DC32260633C14D2D7961FE330FAB3A957B41E7D0337414727A9BB9C41CE646F32D703DAF6E474E6599096EAA4B727E50166AEBCE570079756656BE4474325F01A616539A5BC0AC6376B99B2B1DEE26F7942F14BDE77A493DCF1DA7A39824CC49A60DB3119EB9CAFC41073EB2D09A45871BC44C6022D1FF7881A7FFE96128124E6181998C05379FE5AB76E2111B13B21A7F2553EAC25718ACE83D2277F35FD640955EA57BCF0FEABB4315A808DF6BBE5CF49BBE3BF9412ACE37942EA25EF4125DCA5780EA6313FB58701DAB998FE5E59431FA1A1AA3EC8CD197D498E594784D7E5124A29A638E6A26489F53B51234B1E05EB5DD73EE6D3B5010EF5E79DD73EEB528DEBD70A5EF5E7FE279F7E965D43D17BD58EBF17A31C9BE7BED3ABEF66C3D1DF77EC497F3581B1B5145EF5C5E91B2C98B52BBBCACF50E52FCBC4C65CDE5428EE44E8EEEF50A2FFBABDFD792BB37AFC30ABFEE2F06C4E0FE6EE078E21928B4BF3F53EC1B3F989B293ECA1E1C992996C69A3F9C29DE7977F8E24C71E80FEDD744BBC7678A1FBF07BFD7F27BCF49A763041D782C600D9DD6DFCF14DBAEB69E9D297E347EE3B42CC7AC4EF903E87C20F9DFFE281DFCC2A3CF59740A49E9DFEB95AF4F4ECC143F7DEFEB638A10F9F5A4FCDE7F4B7EF1022FCDBDE7A4D931820E3CF2630959C3A6EC14FAE5F7F584FCCEF7B28B179D7D7852368304DEE1E5ABF03667D141220482048881243695CBEB09E538DF8B2F78E55E785B9814FAD181472E541F9EC466F3875D8B1D237BC7BB16F79E3B98EB5AB4F5AAEC4AAE7A752DAA5EA2DD5647F5128F3E67AB3AEF772DF65984BB16DBAEDE38DDB5581A2B24BB16CB1F203F3822492C27095EE0E783AE45D54BA7D08147DE7AB6422B36BF3E265F9F9C90DF63097964172FD793B279FF2DF9C23B3CFA9C45E75EAFCEEA94AAD3B5A8EA742DCEF72297BE3A41B9E3055E6755AFAE45D54B3AF0C8E55155934DE5259C2551A66E172FCA4B35128F7778F4398B8E700BECEEC2AC761766B56B693F726A418DF0D2FEFEC2ACE40BB3922FCC4ABE30FBF024A7B08004CDFB6F895E4F4A72E3B476E7838559A1218FF0786F3D2BBE90941D3491A0895CD684F6C2ACD05E98ED1811156EB28F2FED7EFADEBDDE8559E12C1E9D63094938850524682A06F5C3C2ACFA4154361F65E55775178F77C5561A5354EA40F1E8700A79FB35D9DC3B2E2F42A96344F8A80FA5832F61551A134A9A60B28F044D4E214727B276E75DF01FBE282F503C1AD3643C23DA649A4DCCFE33F6FF61D86C69CB2A1EBD98A5CD9BE2D7B7B9B1AFD53A1BDB8919FD85A2C94A57C7502B7FD178D6B3B9197E3367D78F796D24D13FA25D0957C226D3665A9FEBA2B71A93FA76D6EAAC57B5F5E4F5FE4531C4D658FE3EFC3EF4AAD1AE8E273A550F818D2359AF7FC016EB4DCF918EFA3CB213494AA1B45F36CFECCF7245BFD96CA96AD6EF967F5ACD6FC3D7ACBCBD4EC73EB1BB8FC3ED7677479D5AAC6773C99EFA47B8D3CA7F5247E749F8C4D67DBBD9F683F7427CD3F3ECBCEA7E2D86CAF45925C7D82A4CCA6139D429DDF672050FED2E57B3A8F5B86C778BE12B56FEEA9A381FDB28F5B3CD4AB6FD87F7EE876B47FCCF429763ACA21B56337AC9CA4AA1A958F3369827F061A88A730F3D5BF5F52BF5623C75DF28DA7FEFEACDF7C07A7D0562DF87DF594BAE5BA2AEA8C5B91CC69E47FAC31DA51CB4D3F23C23E28F553BC77B5EA3581545AF72F357C2AD56BEA5D20F9165FD0D3856ED474EBD5C1383A9D857F55E5A1395B72A47A7FF346CAA76B9A9545DF2EFAA5DB8DA63A9A2B9C5DA7F75D554DCB89ADFDA1C97C276CBEFACD3459A71DF85DB8C105FBFB7A369A6BFE833ED4AD5EFC0DAE9B45E3F288792C5D5B333B59EAFBF3E9B7F266C7FDAE4D99B3A67F96D66BFE5F79B5828795841AAB972479ED9F573F3CB0A7FB7826F6785BF635787F96DA8B31F59FE77E67E45FE33BB7EB149C4FEBBDF1936F37BC28BFE5EB1F66BB8B1E652F8ADC57BA79DF2F5F49FD85B16DBE03EBEC89CC7A6EEF98FF7EDF8DFC5F9FF6F73B333F3C78EB37E3762F315FBFF7673F7EEDDB0B11AABB11AABB11AABB11AABB11AABB11AABB11A6BB3EB5F359D640C]]>
</MeshData>
</MeshImport>
</Producers>
</Mesh>
<Material Name="TowerMaterial" Shading="1" DrawBackFace="255"/>
</Content>
</ZApplication>