185 lines
5.7 KiB
Text
185 lines
5.7 KiB
Text
// screen projection and other functions from SWWM GZ
|
|
|
|
|
|
Struct dt_ProjectionData
|
|
{
|
|
double tanfovx, tanfovy;
|
|
Vector3 viewpos, z, x, y;
|
|
int viewx, viewy, vieww, viewh;
|
|
}
|
|
|
|
Class dt_Utility
|
|
{
|
|
// cache some data that requires trig and quat math
|
|
static clearscope void PrepareProjData( dt_ProjectionData &d, Vector3 viewpos, double angle, double pitch, double roll, double fov )
|
|
{
|
|
// store for internal use
|
|
d.viewpos = viewpos;
|
|
// precalc vfov/hfov tangents
|
|
// (vfov in gzdoom has a small quirk to it)
|
|
double aspect = Screen.GetAspectRatio();
|
|
double fovratio = (aspect>=1.3)?1.333333:aspect;
|
|
d.tanfovy = tan(clamp(fov,5,170)/2.)/fovratio;
|
|
d.tanfovx = d.tanfovy*aspect;
|
|
// precalc view-space axes
|
|
// (don't forget pixel stretch, very important)
|
|
Quat r = Quat.FromAngles(angle,pitch,roll);
|
|
d.z = r*(1.,0.,0.);
|
|
d.x = r*(0.,1.,0.);
|
|
d.y = r*(0.,0.,level.pixelstretch);
|
|
// precalc view origin and clip
|
|
int sblocks = CVar.FindCVar('screenblocks').GetInt();
|
|
let [viewx, viewy, vieww, viewh] = Screen.GetViewWindow();
|
|
int sh = Screen.GetHeight();
|
|
int h = sh;
|
|
if ( sblocks < 10 ) h = (sblocks*sh/10)&~7;
|
|
int bottom = sh-(h+viewy-((h-viewh)/2));
|
|
d.viewx = viewx;
|
|
d.viewy = sh-bottom-h;
|
|
d.vieww = vieww;
|
|
d.viewh = h;
|
|
}
|
|
|
|
// simple projection without matrices, translated from old UnrealScript work
|
|
// bFast: quit early if point is behind screen, for cases where behind-view coords are not really needed
|
|
static clearscope Vector3 ProjectPoint( dt_ProjectionData d, Vector3 worldpos, bool bFast = true )
|
|
{
|
|
Vector3 tdir = worldpos-d.viewpos;
|
|
// early bail, skip behind-view points
|
|
if ( bFast && (d.z dot tdir <= 0.) )
|
|
return (0.,0.,0.);
|
|
double dist = tdir.length();
|
|
// points are pretty much equal, skip projection
|
|
if ( dist <= double.epsilon )
|
|
return (0.,0.,0.);
|
|
tdir /= dist;
|
|
Vector3 dir = d.z*(tdir dot d.z);
|
|
// I don't understand this math, but it works?
|
|
Vector3 xy = tdir-dir;
|
|
double dx = xy dot d.x;
|
|
double dy = xy dot d.y;
|
|
double dlen = dir.length();
|
|
// guard against division by zero here?
|
|
if ( dlen <= double.epsilon )
|
|
return (0.,0.,0.);
|
|
double tanx = dx/dlen;
|
|
double tany = dy/dlen;
|
|
return (1.-tanx/d.tanfovx,1.-tany/d.tanfovy,dir dot d.z);
|
|
}
|
|
|
|
static clearscope Vector2 NDCToViewport( dt_ProjectionData d, Vector3 cpos )
|
|
{
|
|
return (d.viewx+(cpos.x*.5*d.vieww),d.viewy+(cpos.y*.5*d.viewh));
|
|
}
|
|
|
|
// checks if a point is inside the viewport
|
|
static clearscope bool TestScreenBounds( dt_ProjectionData d, Vector2 vpos )
|
|
{
|
|
return ((vpos.x == clamp(vpos.x,d.viewx,d.viewx+d.vieww))
|
|
&& (vpos.y == clamp(vpos.y,d.viewy,d.viewy+d.viewh)));
|
|
}
|
|
|
|
static clearscope Vector3 Vec3FromAngle( double angle, double pitch )
|
|
{
|
|
return (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
}
|
|
|
|
static clearscope Vector3 CircleOffset( Vector3 y, Vector3 z, double angle, double radius )
|
|
{
|
|
return (y*cos(angle)*radius+z*sin(angle)*radius);
|
|
}
|
|
|
|
static clearscope Vector3 ConeSpread( Vector3 x, Vector3 y, Vector3 z, double angle, double spread )
|
|
{
|
|
return (x+y*cos(angle)*spread+z*sin(angle)*spread).unit();
|
|
}
|
|
|
|
static clearscope Vector3, Vector3, Vector3 GetAxes( double angle, double pitch, double roll )
|
|
{
|
|
Vector3 x = (1,0,0), y = (0,-1,0), z = (0,0,1);
|
|
Quat r = Quat.FromAngles(angle,pitch,roll);
|
|
return r*x, r*y, r*z;
|
|
}
|
|
|
|
static double Normalize180( double angle )
|
|
{
|
|
angle = ((angle%360.)+360.)%360.;
|
|
return (angle>180.)?(angle-360.):(angle);
|
|
}
|
|
|
|
static double, double, double ToAngles( Quat q )
|
|
{
|
|
double angle = 0., pitch = 0., roll = 0.;
|
|
double stest = q.z*q.x-q.w*q.y;
|
|
double angY = 2.*(q.w*q.z+q.x*q.y);
|
|
double angX = 1.-2.*(q.y*q.y+q.z*q.z);
|
|
if ( stest < -.4999995 )
|
|
{
|
|
angle = atan2(angY,angX);
|
|
pitch = 90.;
|
|
roll = Normalize180(angle+(2.*atan2(q.x,q.w)));
|
|
}
|
|
else if ( stest > .4999995 )
|
|
{
|
|
angle = atan2(angY,angX);
|
|
pitch = -90.;
|
|
roll = Normalize180(angle+(2.*atan2(q.x,q.w)));
|
|
}
|
|
else
|
|
{
|
|
angle = atan2(angY,angX);
|
|
pitch = -asin(2.*stest);
|
|
roll = atan2(2.*(q.w*q.x+q.y*q.z),1.-2.*(q.x*q.x+q.y*q.y));
|
|
}
|
|
return angle, pitch, roll;
|
|
}
|
|
|
|
// for aiming and shooting
|
|
static Vector3 GetPlayerViewDir( Actor player )
|
|
{
|
|
Quat r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
return r*(1,0,0);
|
|
}
|
|
static play Vector3 GetPlayerAimDir( Actor player )
|
|
{
|
|
FTranslatedLineTarget t;
|
|
double pitch = player.BulletSlope(t);
|
|
Quat r;
|
|
if ( !t.linetarget ) r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
else r = Quat.FromAngles(player.angle,pitch,player.roll);
|
|
return r*(1,0,0);
|
|
}
|
|
static Vector3, Vector3, Vector3 GetPlayerAxes( Actor player )
|
|
{
|
|
Quat r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
return r*(1,0,0), r*(0,-1,0), r*(0,0,1);
|
|
}
|
|
static play Vector3, Vector3, Vector3 GetPlayerAxesAutoAimed( Actor player )
|
|
{
|
|
FTranslatedLineTarget t;
|
|
double pitch = player.BulletSlope(t);
|
|
Quat r;
|
|
if ( !t.linetarget ) r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
else r = Quat.FromAngles(player.angle,pitch,player.roll);
|
|
return r*(1,0,0), r*(0,-1,0), r*(0,0,1);
|
|
}
|
|
static Vector3 GetPlayerEye( Actor player )
|
|
{
|
|
if ( !player.viewpos )
|
|
return player.Vec2OffsetZ(0,0,player.player.viewz);
|
|
if ( player.viewpos.flags&VPSF_ABSOLUTEPOS )
|
|
return player.viewpos.offset;
|
|
Vector3 origin = player.Vec2OffsetZ(0,0,player.player.viewz);
|
|
if ( player.viewpos.flags&VPSF_ABSOLUTEOFFSET )
|
|
return level.Vec3Offset(origin,player.viewpos.offset);
|
|
Quat r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
return level.Vec3Offset(origin,r*player.viewpos.offset);
|
|
}
|
|
static Vector3 GetFireOffset( Actor player, double x, double y, double z )
|
|
{
|
|
Vector3 origin = GetPlayerEye(player);
|
|
Quat r = Quat.FromAngles(player.angle,player.pitch,player.roll);
|
|
return level.Vec3Offset(origin,r*(x,-y,z));
|
|
}
|
|
|
|
}
|