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:
parent
d763744234
commit
b24ee842ba
5 changed files with 144 additions and 21 deletions
BIN
graphics/GuidedX1.png
Normal file
BIN
graphics/GuidedX1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5 KiB |
BIN
graphics/GuidedX2.png
Normal file
BIN
graphics/GuidedX2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -2,6 +2,7 @@ version "3.4"
|
|||
|
||||
#include "zscript/mk_matrix.zsc"
|
||||
#include "zscript/mk_coordutil.zsc"
|
||||
#include "zscript/mk_quaternion.zsc"
|
||||
#include "zscript/utcommon.zsc"
|
||||
#include "zscript/impacthammer.zsc"
|
||||
#include "zscript/chainsaw.zsc"
|
||||
|
|
|
|||
97
zscript/mk_quaternion.zsc
Normal file
97
zscript/mk_quaternion.zsc
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -355,15 +355,13 @@ Class WarShell : Actor
|
|||
|
||||
Class GuidedWarShell : WarShell
|
||||
{
|
||||
double lagangle, lagpitch, lagangle2, lagpitch2;
|
||||
double guideangle, guidepitch, lastguideroll;
|
||||
double lagangle, lagpitch, lagroll, lagangle2, lagpitch2, lagroll2,
|
||||
guideangle, guidepitch, guideroll;
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if ( target && target.player ) target.player.camera = self;
|
||||
guideangle = angle;
|
||||
guidepitch = pitch;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
|
|
@ -385,22 +383,25 @@ Class GuidedWarShell : WarShell
|
|||
{
|
||||
lagangle = target.player.cmd.yaw/128.;
|
||||
lagpitch = -target.player.cmd.pitch/128.;
|
||||
guideangle += lagangle2*0.95+lagangle*0.05;
|
||||
guidepitch += lagpitch2*0.95+lagpitch*0.05;
|
||||
guidepitch = Clamp(guidepitch,-89,89);
|
||||
double guideroll = -lagangle2*15;
|
||||
Vector3 dir = (cos(guideangle)*cos(guidepitch),sin(guideangle)*cos(guidepitch),-sin(guidepitch));
|
||||
destangle = atan2(dir.y,dir.x);
|
||||
destpitch = asin(-dir.z);
|
||||
double destroll = lastguideroll*0.9+guideroll*0.1;
|
||||
A_SetAngle(destangle,SPF_INTERPOLATE);
|
||||
A_SetPitch(destpitch,SPF_INTERPOLATE);
|
||||
A_SetRoll(destroll,SPF_INTERPOLATE);
|
||||
if ( target.player.cmd.buttons&BT_RELOAD ) lagroll = 5;
|
||||
else if ( target.player.cmd.buttons&BT_ZOOM ) lagroll = -5;
|
||||
else lagroll = 0;
|
||||
guideangle = lagangle2*0.95+lagangle*0.05;
|
||||
guidepitch = lagpitch2*0.95+lagpitch*0.05;
|
||||
guideroll = lagroll2*0.95+lagroll*0.05;
|
||||
Quat orient = Quat.create_euler(pitch,angle,roll);
|
||||
Quat angles = Quat.create_euler(guidepitch,guideangle,guideroll);
|
||||
orient = orient.qmul(angles);
|
||||
double npitch, nangle, nroll;
|
||||
[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;
|
||||
}
|
||||
lagangle2 = lagangle2*0.95+lagangle*0.05;
|
||||
lagpitch2 = lagpitch2*0.95+lagpitch*0.05;
|
||||
lastguideroll = roll*0.98;
|
||||
lagroll2 = lagroll2*0.95+lagroll*0.05;
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
@ -439,25 +440,36 @@ Class RedeemerHUD : HUDMessageBase
|
|||
{
|
||||
Actor Camera;
|
||||
Vector3 ViewPos;
|
||||
double ViewAngle, ViewPitch, ViewRoll;
|
||||
TextureID reticle, mark, readout;
|
||||
double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2;
|
||||
TextureID reticle1, reticle2, mark, readout;
|
||||
Font whfont;
|
||||
ThinkerIterator t;
|
||||
MidTracer tr;
|
||||
Array<TargetActor> ta;
|
||||
Shape2D rreticle;
|
||||
|
||||
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);
|
||||
readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any);
|
||||
whfont = Font.GetFont('WHFONT');
|
||||
t = ThinkerIterator.Create("Actor");
|
||||
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;
|
||||
}
|
||||
override bool Tick()
|
||||
{
|
||||
LagRoll = Quat.Normalize180(ViewRoll-LagRoll2);
|
||||
LagRoll2 += Quat.Normalize180(LagRoll-LagRoll2)*0.1;
|
||||
// shootable targetting
|
||||
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));
|
||||
}
|
||||
}
|
||||
// other stuff
|
||||
Screen.DrawTexture(reticle,false,320,240,DTA_VirtualWidth,640,DTA_VirtualHeight,480,DTA_RenderStyle,(1|2<<8|1<<16));
|
||||
// reticle
|
||||
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;
|
||||
for ( int i=0; i<numreadouts; i++ )
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue