diff --git a/src/namedef.h b/src/namedef.h index cca872706..17fa9e291 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -296,6 +296,8 @@ xx(Tan) xx(CosH) xx(SinH) xx(TanH) +xx(ATan2) +xx(VectorAngle) xx(Alpha) xx(Angle) xx(Args) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index a422a92c0..ca812e2e5 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -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(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 list; diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index e56215b0f..9a0f2dc78 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -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 choices; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index d2ee090e6..3c0eaa584 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -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(yval)->GetValue().GetFloat(); + double x = static_cast(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(val)->GetValue().GetFloat())); + return reg; + } + return val->Emit(build); +} + //========================================================================== // // diff --git a/src/zscript/vmexec.h b/src/zscript/vmexec.h index 1804d4c36..8232340a0 100644 --- a/src/zscript/vmexec.h +++ b/src/zscript/vmexec.h @@ -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]; diff --git a/src/zscript/vmops.h b/src/zscript/vmops.h index bb4c034c7..decb0b93d 100644 --- a/src/zscript/vmops.h +++ b/src/zscript/vmops.h @@ -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),