Add atan2, which was conspicuously absent, to the VM

- DECORATE now has atan2(y,x) and VectorAngle(x,y) functions. They are
  identical except for the order of their parameters. The returned angle
  is in degrees (not radians).
This commit is contained in:
Randy Heit 2016-04-18 23:06:17 -05:00
parent 30a530b178
commit e83bc53678
6 changed files with 135 additions and 0 deletions

View file

@ -296,6 +296,8 @@ xx(Tan)
xx(CosH)
xx(SinH)
xx(TanH)
xx(ATan2)
xx(VectorAngle)
xx(Alpha)
xx(Angle)
xx(Args)

View file

@ -56,6 +56,7 @@ static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cl
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls);
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls);
static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls);
static FxExpression *ParseAtan2(FScanner &sc, FName identifier, PClassActor *cls);
static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls);
static FxExpression *ParseClamp(FScanner &sc, PClassActor *cls);
@ -386,6 +387,9 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
return ParseClamp(sc, cls);
case NAME_Abs:
return ParseAbs(sc, cls);
case NAME_ATan2:
case NAME_VectorAngle:
return ParseAtan2(sc, identifier, cls);
default:
args = new FArgumentList;
func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(identifier, true));
@ -511,6 +515,15 @@ static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls)
return new FxAbs(x);
}
static FxExpression *ParseAtan2(FScanner &sc, FName identifier, PClassActor *cls)
{
FxExpression *a = ParseExpressionM(sc, cls);
sc.MustGetToken(',');
FxExpression *b = ParseExpressionM(sc, cls);
sc.MustGetToken(')');
return identifier == NAME_ATan2 ? new FxATan2(a, b, sc) : new FxATan2(b, a, sc);
}
static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls)
{
TArray<FxExpression*> list;

View file

@ -623,6 +623,27 @@ public:
//
//==========================================================================
class FxATan2 : public FxExpression
{
FxExpression *yval, *xval;
public:
FxATan2(FxExpression *y, FxExpression *x, const FScriptPosition &pos);
~FxATan2();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
private:
ExpEmit ToReg(VMFunctionBuilder *build, FxExpression *val);
};
//==========================================================================
//
//
//
//==========================================================================
class FxMinMax : public FxExpression
{
TDeletingArray<FxExpression *> choices;

View file

@ -1991,6 +1991,99 @@ ExpEmit FxAbs::Emit(VMFunctionBuilder *build)
return out;
}
//==========================================================================
//
//
//
//==========================================================================
FxATan2::FxATan2(FxExpression *y, FxExpression *x, const FScriptPosition &pos)
: FxExpression(pos)
{
yval = y;
xval = x;
}
//==========================================================================
//
//
//
//==========================================================================
FxATan2::~FxATan2()
{
SAFE_DELETE(yval);
SAFE_DELETE(xval);
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxATan2::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(yval, ctx);
SAFE_RESOLVE(xval, ctx);
if (!yval->IsNumeric() || !xval->IsNumeric())
{
ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter");
delete this;
return NULL;
}
if (yval->isConstant() && xval->isConstant())
{
double y = static_cast<FxConstant *>(yval)->GetValue().GetFloat();
double x = static_cast<FxConstant *>(xval)->GetValue().GetFloat();
FxExpression *z = new FxConstant(g_atan2(y, x) * (180 / M_PI), ScriptPosition);
delete this;
return z;
}
if (yval->ValueType->GetRegType() != REGT_FLOAT && !yval->isConstant())
{
yval = new FxFloatCast(yval);
}
if (xval->ValueType->GetRegType() != REGT_FLOAT && !xval->isConstant())
{
xval = new FxFloatCast(xval);
}
ValueType = TypeFloat64;
return this;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxATan2::Emit(VMFunctionBuilder *build)
{
ExpEmit yreg = ToReg(build, yval);
ExpEmit xreg = ToReg(build, xval);
yreg.Free(build);
xreg.Free(build);
ExpEmit out(build, REGT_FLOAT);
build->Emit(OP_ATAN2, out.RegNum, yreg.RegNum, xreg.RegNum);
return out;
}
//==========================================================================
//
// The atan2 opcode only takes registers as parameters, so any constants
// must be loaded into registers first.
//
//==========================================================================
ExpEmit FxATan2::ToReg(VMFunctionBuilder *build, FxExpression *val)
{
if (val->isConstant())
{
ExpEmit reg(build, REGT_FLOAT);
build->Emit(OP_LKF, reg.RegNum, build->GetConstantFloat(static_cast<FxConstant*>(val)->GetValue().GetFloat()));
return reg;
}
return val->Emit(build);
}
//==========================================================================
//
//

View file

@ -1039,6 +1039,11 @@ begin:
reg.f[a] = reg.f[B] > konstf[C] ? reg.f[B] : konstf[C];
NEXTOP;
OP(ATAN2):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = g_atan2(reg.f[B], reg.f[C]) * (180 / M_PI);
NEXTOP;
OP(FLOP):
ASSERTF(a); ASSERTF(B);
fb = reg.f[B];

View file

@ -174,6 +174,7 @@ xx(MINF_RR, min, RFRFRF), // fA = min(fB),fkC)
xx(MINF_RK, min, RFRFKF),
xx(MAXF_RR, max, RFRFRF), // fA = max(fB),fkC)
xx(MAXF_RK, max, RFRFKF),
xx(ATAN2, atan2, RFRFRF), // fA = atan2(fB,fC), result is in degrees
xx(FLOP, flop, RFRFI8), // fA = f(fB), where function is selected by C
xx(EQF_R, beq, CFRR), // if ((fB == fkC) != (A & 1)) then pc++
xx(EQF_K, beq, CFRK),