Below is a simple* example.
*I ended up converting the quaternion matrix to euler-angles ( instead of manipulating the modelView matrix directly ) in an attempt to make things easy to understand .. just keep in mind that ZGE converts the euler-angles back to a matrix internally again, so this is a bit wasteful performance-wise.
Code: Select all
<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" LightPosition="0 1 0" FileVersion="2">
<OnLoaded>
<ZLibrary Comment="Quaternion">
<Source>
<![CDATA[//
vec3 applyQuaternion(vec3 v, vec4 q)
{
float x, y, z, w;
x = q.w*v.x+q.y*v.z-q.z*v.y;
y = q.w*v.y+q.z*v.x-q.x*v.z;
z = q.w*v.z+q.x*v.y-q.y*v.x;
w = -q.x*v.x-q.y*v.y-q.z*v.z;
return vector3
(
x*q.w-w*q.x-y*q.z+z*q.y,
y*q.w-w*q.y-z*q.x+x*q.z,
z*q.w-w*q.z-x*q.y+y*q.x
);
}
vec4 invertQuaternion(vec4 q)
{
return vector4(-q.x, -q.y, -q.z, q.w);
}
vec4 multiplyQuaternion(vec4 a, vec4 b)
{
return vector4
(
a.x*b.w+a.w*b.x+a.y*b.z-a.z*b.y,
a.y*b.w+a.w*b.y+a.z*b.x-a.x*b.z,
a.z*b.w+a.w*b.z+a.x*b.y-a.y*b.x,
a.w*b.w-a.x*b.x-a.y*b.y-a.z*b.z
);
}
//
vec4 axisAngleToQuaternion(vec3 axis, float angle)
{
float a, s;
a = angle*0.5;
s = sin(a);
return vector4(axis.x*s, axis.y*s, axis.z*s, cos(a));
}
//
mat4 quaternionToMatrix(vec4 q)
{
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;
mat4 m;
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;
}
//
vec3 matrixToEuler(mat4 m, int order)
{
vec3 v;
switch(order)
{
case 0x7: // XYZ
v.x = atan2(m[1,2], m[2,2])/PI*0.5;
v.y = asin(-clamp(m[0,2], -1, 1))/PI*0.5;
v.z = atan2(m[0,1], m[0,0])/PI*0.5;
break;
case 0xF: // ZYX
v.x = atan2(-m[2,1], m[2,2])/PI*0.5;
v.y = asin(clamp(m[2,0], -1, 1))/PI*0.5;
v.z = atan2(-m[1,0], m[0,0])/PI*0.5;
break;
}
return v;
}]]>
</Source>
</ZLibrary>
<ZExpression>
<Expression>
<![CDATA[//
CameraOffset = vector3(0, 0.5, 5);]]>
</Expression>
</ZExpression>
<SpawnModel Model="Ship" Position="0 8 0"/>
</OnLoaded>
<OnUpdate>
<Repeat Name="AxisRepeat" Count="3">
<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>
<KeyPress Name="Key" Keys="12">
<OnKeyDown>
<ZExpression>
<Expression>
<![CDATA[//
if(Key.KeyIndex)
{
CameraOffset = vector3(0, 0.5, 5);
}
else
{
CameraOffset = vector3(0, 0, 0);
}]]>
</Expression>
</ZExpression>
</OnKeyDown>
</KeyPress>
</OnUpdate>
<OnRender>
<UseMaterial Material="TerrainMaterial"/>
<RenderMesh Mesh="TerrainMesh"/>
</OnRender>
<Content>
<Array Name="Axis" SizeDim1="4"/>
<Array Name="AxisMap" Type="4" Dimensions="1" SizeDim1="3" SizeDim2="2" Persistent="255">
<Values>
<![CDATA[789C535755D37071040003820120]]>
</Values>
</Array>
<Variable Name="CameraOffset" Type="7"/>
<Model Name="Ship">
<Definitions>
<Variable Name="ShipQuaternion" Type="8"/>
</Definitions>
<OnSpawn>
<ZExpression>
<Expression>
<![CDATA[//
ShipQuaternion = vector4(0, 0, 0, 1);]]>
</Expression>
</ZExpression>
</OnSpawn>
<OnUpdate>
<ZExpression>
<Expression>
<![CDATA[//
float dt, x, y, z;
//
dt = App.DeltaTime;
x = -Axis[1]*dt;
y = -Axis[2]*dt;
z = -Axis[0]*dt;
// Steering
vec4 qx, qy, qz, q;
qx = axisAngleToQuaternion(vector3(1, 0, 0), x);
qy = axisAngleToQuaternion(vector3(0, 1, 0), y);
qz = axisAngleToQuaternion(vector3(0, 0, 1), z);
q = multiplyQuaternion(qz, qy);
q = multiplyQuaternion(qx, q);
ShipQuaternion = multiplyQuaternion(ShipQuaternion, q);
Ship.Rotation = matrixToEuler(quaternionToMatrix(ShipQuaternion), 0x7);
// Movement
vec3 v = applyQuaternion(vector3(0, 0, -8), ShipQuaternion);
Ship.Position.X += v.x*dt;
Ship.Position.Y += v.y*dt;
Ship.Position.Z += v.z*dt;
// Camera
App.CameraRotation = matrixToEuler(quaternionToMatrix(invertQuaternion(ShipQuaternion)), 0xF);
vec3 o = applyQuaternion(CameraOffset, ShipQuaternion);
App.CameraPosition.X = Ship.Position.X+o.x;
App.CameraPosition.Y = Ship.Position.Y+o.y;
App.CameraPosition.Z = Ship.Position.Z+o.z;]]>
</Expression>
</ZExpression>
</OnUpdate>
<OnRender>
<Condition>
<Expression>
<![CDATA[//
return CameraOffset.Z;]]>
</Expression>
<OnTrue>
<UseMaterial Material="ShipMaterial"/>
<RenderMesh Mesh="ShipMesh"/>
</OnTrue>
</Condition>
</OnRender>
</Model>
<Mesh Name="ShipMesh">
<Producers>
<MeshImport HasVertexColors="1">
<MeshData>
<![CDATA[789CD5923F68535118C5AFFF103A084E2EC649A4253A640BA5605B22018B105A692D165248846296486A33682A151751120DA8F4CF835774B0201D149B0E62105F9A621E423010084587D2D1423A8884CAB587C3C7ED5AEA13E5F11D0EBF73EEF7EE83F74E297571675CF755B1ADFEFCFD6278BEE8BA67BB7FB48F9CEF5F3ED6FDF9493A74C1826EAC4E4F4723C532097DDC4A878418BFB14A2561B36F2C1A49954E27D2216AC19E8AA743971CC30B36537212A686A44AFE5C47E25BAC96A43AD670D69FA382407B5B48A9A649E258D4E1ACD1DE163650CD36129CDABD995CF680945E4623E5CDE38F95827624943AF5C8B1CA9BC24198929330158214DB94AA25E1CF5D414A221E4DE9D0F3141584CD5BF7279C4005FAE6973FF76926B805FDB27C6F02A4961412DC62073C50614AC20D68F214B7052A668FA4DCC6B30F674E8C37F327CB5ACF7F87AFD6E93F3CD577AA75127640A4693AE4D5FA541CCDF520387C334FD23786949C0AC2B499674AC2341BBB11F6359482C27FBC4E928D0D0C0A9774609029142909D3C95970ADC5FB1AE2D1246107449AA643EE6BB45E647ADE7EEDB25F9FD17A7236D3A335097D971D1E11425F583084FEE6A239455F58C036E9C073FFCFAB209D43F0997E3449E83B87B04D3AF04BA386D0A7AE9953F44BA366333DF7F36EBCB37C0B3994F7E44EBE4BEE80947BB85FA903EAA03AB4E787278FA8C3FBF47FF3BDFFCBC33B63448FEEFA16C36DA5B497B37DD9DBB97D77CED37157D6FEE8ACADB89ECE83B9679ECEF6B8FAA766E727DED3FC060DFA76D3]]>
</MeshData>
</MeshImport>
</Producers>
</Mesh>
<Material Name="ShipMaterial" Shading="1"/>
<Mesh Name="TerrainMesh">
<Producers>
<MeshBox XCount="15" YCount="15" Grid2DOnly="255"/>
<MeshExpression Scale="64 4 64" VertexColors="255">
<Expression>
<![CDATA[//
V.Z = -V.Y;
V.Y = rnd();
C.R = 0;
C.G = V.Y;
C.B = 1;]]>
</Expression>
</MeshExpression>
</Producers>
</Mesh>
<Material Name="TerrainMaterial"/>
</Content>
</ZApplication>
You can control the ship using the arrow and A/D keys, and switch between 1st and 3rd-person camera using the 1/2 keys.