Implemented "six degrees of freedom" movement for guided redeemer missiles (reload/zoom for manual tilt control).

Made part of the guided redeemer reticle rotate according to the missile's roll just to show off Shape2D.
This commit is contained in:
Marisa the Magician 2018-08-14 02:57:44 +02:00
commit b24ee842ba
5 changed files with 144 additions and 21 deletions

BIN
graphics/GuidedX1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

BIN
graphics/GuidedX2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -2,6 +2,7 @@ version "3.4"
#include "zscript/mk_matrix.zsc" #include "zscript/mk_matrix.zsc"
#include "zscript/mk_coordutil.zsc" #include "zscript/mk_coordutil.zsc"
#include "zscript/mk_quaternion.zsc"
#include "zscript/utcommon.zsc" #include "zscript/utcommon.zsc"
#include "zscript/impacthammer.zsc" #include "zscript/impacthammer.zsc"
#include "zscript/chainsaw.zsc" #include "zscript/chainsaw.zsc"

97
zscript/mk_quaternion.zsc Normal file
View file

@ -0,0 +1,97 @@
/*
Quaternion math helper class.
(C)2018 Marisa Kirisame, UnSX Team.
Released under the GNU Lesser General Public License version 3 (or later).
See https://www.gnu.org/licenses/lgpl-3.0.txt for its terms.
*/
Class Quat
{
protected double W, X, Y, Z;
Quat init( double w, double x, double y, double z )
{
self.W = w;
self.X = x;
self.Y = y;
self.Z = z;
return self;
}
void copy( Quat q )
{
W = q.W;
X = q.X;
Y = q.Y;
Z = q.Z;
}
static Quat create( double w, double x, double y, double z )
{
return new("Quat").init(w,x,y,z);
}
static Quat create_axis( Vector3 axis, double theta )
{
double scale = axis dot axis;
if ( scale < double.epsilon ) return Quat.create(1,0,0,0);
theta *= 0.5;
double f = sin(theta)/sqrt(scale);
return Quat.create(cos(theta),axis.x*f,axis.y*f,axis.z*f);
}
static Quat create_euler( double pitch, double yaw, double roll )
{
Quat zrot = Quat.create_axis((0,0,1),yaw);
Quat yrot = Quat.create_axis((0,1,0),pitch);
Quat xrot = Quat.create_axis((1,0,0),roll);
Quat sum = zrot.qmul(yrot);
sum = sum.qmul(xrot);
return sum;
}
// copied here since Actor.Normalize180 is not (yet) clearscope
static double Normalize180( double ang )
{
ang = ang%360;
ang = (ang+360)%360;
if ( ang > 180 ) ang -= 360;
return ang;
}
double, double, double to_euler()
{
double stest = z*x-w*y;
double yawY = 2*(w*z+x*y);
double yawX = 1-2*(y*y+z*z);
double st = 0.4999995;
double pitch = 0;
double yaw = 0;
double roll = 0;
if ( stest <= -st )
{
pitch = 90;
yaw = atan2(yawY,yawX);
roll = Normalize180(yaw+(2*atan2(x,w)));
}
else if ( stest > st )
{
pitch = -90;
yaw = atan2(yawY,yawX);
roll = Normalize180(yaw+(2*atan2(x,w)));
}
else
{
pitch = -asin(2*stest);
yaw = atan2(yawY,yawX);
roll = atan2(2*(w*x+y*z),(1-2*(x*x+y*y)));
}
return pitch, yaw, roll;
}
Quat qmul( Quat q )
{
return Quat.create(w*q.w-x*q.x-y*q.y-z*q.z,w*q.x+x*q.w+y*q.z-z
*q.y,w*q.y+y*q.w+z*q.x-x*q.z,w*q.z+z*q.w+x*q.y-y*q.x);
}
}

View file

@ -355,15 +355,13 @@ Class WarShell : Actor
Class GuidedWarShell : WarShell Class GuidedWarShell : WarShell
{ {
double lagangle, lagpitch, lagangle2, lagpitch2; double lagangle, lagpitch, lagroll, lagangle2, lagpitch2, lagroll2,
double guideangle, guidepitch, lastguideroll; guideangle, guidepitch, guideroll;
override void PostBeginPlay() override void PostBeginPlay()
{ {
Super.PostBeginPlay(); Super.PostBeginPlay();
if ( target && target.player ) target.player.camera = self; if ( target && target.player ) target.player.camera = self;
guideangle = angle;
guidepitch = pitch;
} }
override void Tick() override void Tick()
{ {
@ -385,22 +383,25 @@ Class GuidedWarShell : WarShell
{ {
lagangle = target.player.cmd.yaw/128.; lagangle = target.player.cmd.yaw/128.;
lagpitch = -target.player.cmd.pitch/128.; lagpitch = -target.player.cmd.pitch/128.;
guideangle += lagangle2*0.95+lagangle*0.05; if ( target.player.cmd.buttons&BT_RELOAD ) lagroll = 5;
guidepitch += lagpitch2*0.95+lagpitch*0.05; else if ( target.player.cmd.buttons&BT_ZOOM ) lagroll = -5;
guidepitch = Clamp(guidepitch,-89,89); else lagroll = 0;
double guideroll = -lagangle2*15; guideangle = lagangle2*0.95+lagangle*0.05;
Vector3 dir = (cos(guideangle)*cos(guidepitch),sin(guideangle)*cos(guidepitch),-sin(guidepitch)); guidepitch = lagpitch2*0.95+lagpitch*0.05;
destangle = atan2(dir.y,dir.x); guideroll = lagroll2*0.95+lagroll*0.05;
destpitch = asin(-dir.z); Quat orient = Quat.create_euler(pitch,angle,roll);
double destroll = lastguideroll*0.9+guideroll*0.1; Quat angles = Quat.create_euler(guidepitch,guideangle,guideroll);
A_SetAngle(destangle,SPF_INTERPOLATE); orient = orient.qmul(angles);
A_SetPitch(destpitch,SPF_INTERPOLATE); double npitch, nangle, nroll;
A_SetRoll(destroll,SPF_INTERPOLATE); [npitch, nangle, nroll] = orient.to_euler();
A_SetAngle(nangle,SPF_INTERPOLATE);
A_SetPitch(npitch,SPF_INTERPOLATE);
A_SetRoll(nroll,SPF_INTERPOLATE);
vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11; vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11;
} }
lagangle2 = lagangle2*0.95+lagangle*0.05; lagangle2 = lagangle2*0.95+lagangle*0.05;
lagpitch2 = lagpitch2*0.95+lagpitch*0.05; lagpitch2 = lagpitch2*0.95+lagpitch*0.05;
lastguideroll = roll*0.98; lagroll2 = lagroll2*0.95+lagroll*0.05;
} }
States States
{ {
@ -439,25 +440,36 @@ Class RedeemerHUD : HUDMessageBase
{ {
Actor Camera; Actor Camera;
Vector3 ViewPos; Vector3 ViewPos;
double ViewAngle, ViewPitch, ViewRoll; double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2;
TextureID reticle, mark, readout; TextureID reticle1, reticle2, mark, readout;
Font whfont; Font whfont;
ThinkerIterator t; ThinkerIterator t;
MidTracer tr; MidTracer tr;
Array<TargetActor> ta; Array<TargetActor> ta;
Shape2D rreticle;
RedeemerHUD Init() RedeemerHUD Init()
{ {
reticle = TexMan.CheckForTexture("GuidedX",TexMan.Type_Any); reticle1 = TexMan.CheckForTexture("GuidedX1",TexMan.Type_Any);
reticle2 = TexMan.CheckForTexture("GuidedX2",TexMan.Type_Any);
mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any); mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any); readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any);
whfont = Font.GetFont('WHFONT'); whfont = Font.GetFont('WHFONT');
t = ThinkerIterator.Create("Actor"); t = ThinkerIterator.Create("Actor");
tr = new("MidTracer"); tr = new("MidTracer");
rreticle = new("Shape2D");
rreticle.PushCoord((0,0));
rreticle.PushCoord((1,0));
rreticle.PushCoord((0,1));
rreticle.PushCoord((1,1));
rreticle.PushTriangle(0,3,1);
rreticle.PushTriangle(0,2,3);
return self; return self;
} }
override bool Tick() override bool Tick()
{ {
LagRoll = Quat.Normalize180(ViewRoll-LagRoll2);
LagRoll2 += Quat.Normalize180(LagRoll-LagRoll2)*0.1;
// shootable targetting // shootable targetting
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() ) if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() )
{ {
@ -494,8 +506,21 @@ Class RedeemerHUD : HUDMessageBase
Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(ta[i].vpos.x-whfont.StringWidth(ta[i].diststr)/2)-12,ta[i].vpos.y+8,ta[i].diststr,DTA_RenderStyle,(1|2<<8|1<<16)); Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(ta[i].vpos.x-whfont.StringWidth(ta[i].diststr)/2)-12,ta[i].vpos.y+8,ta[i].diststr,DTA_RenderStyle,(1|2<<8|1<<16));
} }
} }
// other stuff // reticle
Screen.DrawTexture(reticle,false,320,240,DTA_VirtualWidth,640,DTA_VirtualHeight,480,DTA_RenderStyle,(1|2<<8|1<<16)); Vector2 vs = (640,640/Screen.GetAspectRatio());
Vector2 mid, siz;
[mid, siz] = Screen.VirtualToRealCoords(vs*0.5,(128,128),vs,false,false);
Vector2 verts[4];
verts[0] = (-siz.x,-siz.y);
verts[1] = (siz.x,-siz.y);
verts[2] = (-siz.x,siz.y);
verts[3] = (siz.x,siz.y);
rreticle.Clear(Shape2D.C_Verts);
for ( int i=0; i<4; i++ )
rreticle.PushVertex(mid+(verts[i].x*cos(LagRoll2)-verts[i].y*sin(LagRoll2),verts[i].x*sin(LagRoll2)+verts[i].y*cos(LagRoll2)));
Screen.DrawShape(reticle1,false,rreticle,DTA_RenderStyle,(1|2<<8|1<<16));
Screen.DrawTexture(reticle2,false,vs.x*0.5,vs.y*0.5,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_RenderStyle,(1|2<<8|1<<16));
// faux assembly readout
int numreadouts = Screen.GetHeight()/128+2; int numreadouts = Screen.GetHeight()/128+2;
for ( int i=0; i<numreadouts; i++ ) for ( int i=0; i<numreadouts; i++ )
{ {