From 31ac1bd414158e2c6ffda5c4f8a7651f11b0e424 Mon Sep 17 00:00:00 2001 From: RaveYard Date: Sat, 12 Nov 2022 20:05:36 +0100 Subject: [PATCH] Initial implementation of Quaternion type in ZScript --- src/common/engine/namedef.h | 2 + src/common/scripting/backend/codegen.cpp | 121 +++++++++++++++--- src/common/scripting/backend/codegen.h | 15 +++ src/common/scripting/core/types.cpp | 38 ++++++ src/common/scripting/core/types.h | 2 + src/common/scripting/frontend/zcc_compile.cpp | 20 ++- src/common/scripting/jit/jit.cpp | 4 +- 7 files changed, 181 insertions(+), 21 deletions(-) diff --git a/src/common/engine/namedef.h b/src/common/engine/namedef.h index 9d5cb27ff..37593e692 100644 --- a/src/common/engine/namedef.h +++ b/src/common/engine/namedef.h @@ -125,9 +125,11 @@ xx(Fixed) xx(Vector2) xx(Vector3) xx(Vector4) +xx(Quat) xx(FVector2) xx(FVector3) xx(FVector4) +xx(FQuat) xx(let) xx(Min) diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 6f6a8e025..fcba6e257 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -809,6 +809,34 @@ ExpEmit FxVectorValue::Emit(VMFunctionBuilder *build) return out; } +//========================================================================== +// +// +// +//========================================================================== + +FxQuaternionValue::FxQuaternionValue(FxExpression* x, FxExpression* y, FxExpression* z, FxExpression* w, const FScriptPosition& sc) : FxVectorValue(x, y, z, w, sc) +{ +} + +FxExpression* FxQuaternionValue::Resolve(FCompileContext& ctx) +{ + auto base = FxVectorValue::Resolve(ctx); + if (base) + { + if (base->ValueType->GetRegCount() != 4) + { + ScriptPosition.Message(MSG_ERROR, "Quat expression requires 4 arguments, got %d instead", base->ValueType->GetRegCount()); + delete base; + return nullptr; + } + + base->ValueType = TypeQuaternion; + } + return base; +} + + //========================================================================== // // @@ -1727,7 +1755,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) delete this; return x; } - else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()) || (basex->IsVector4() && IsVector4())) + else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()) || (basex->IsVector4() && IsVector4()) || (basex->IsQuaternion() && IsQuaternion())) { auto x = basex; basex = nullptr; @@ -1799,7 +1827,7 @@ FxExpression *FxPlusSign::Resolve(FCompileContext& ctx) CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); - if (Operand->IsNumeric() || Operand->IsVector()) + if (Operand->IsNumeric() || Operand->IsVector() || Operand->IsQuaternion()) { FxExpression *e = Operand; Operand = nullptr; @@ -1853,7 +1881,7 @@ FxExpression *FxMinusSign::Resolve(FCompileContext& ctx) CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); - if (Operand->IsNumeric() || Operand->IsVector()) + if (Operand->IsNumeric() || Operand->IsVector() || Operand->IsQuaternion()) { if (Operand->isConstant()) { @@ -2441,7 +2469,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) delete this; return nullptr; } - if (!Base->IsVector() && Base->ValueType->isStruct()) + if (!Base->IsVector() && !Base->IsQuaternion() && Base->ValueType->isStruct()) { ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet"); delete this; @@ -2839,6 +2867,10 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx) { ValueType = TypeTextureID; } + else if (left->IsQuaternion() && right->IsQuaternion()) + { + ValueType = left->ValueType; + } else if (left->IsVector() && right->IsVector()) { // a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand. @@ -2944,6 +2976,13 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) } return to; } + else if (IsQuaternion()) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + assert(op1.RegCount == 4 && op2.RegCount == 4); + build->Emit(OP_ADDV4_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } else if (ValueType->GetRegType() == REGT_FLOAT) { assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); @@ -2972,6 +3011,13 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) build->Emit(right->IsVector4() ? OP_SUBV4_RR : right->IsVector3() ? OP_SUBV3_RR : OP_SUBV2_RR, to.RegNum, op1.RegNum, op2.RegNum); return to; } + else if (IsQuaternion()) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + assert(op1.RegCount == 4 && op2.RegCount == 4); + build->Emit(OP_SUBV4_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } else if (ValueType->GetRegType() == REGT_FLOAT) { assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); @@ -3037,7 +3083,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) return nullptr; } - if (left->IsVector() || right->IsVector()) + if (left->IsVector() || right->IsVector() || left->IsQuaternion() || right->IsQuaternion()) { switch (Operator) { @@ -3047,7 +3093,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) [[fallthrough]]; case '*': - if (left->IsVector() && right->IsNumeric()) + if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric()) { if (right->IsInteger()) { @@ -3062,7 +3108,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) ValueType = left->ValueType; break; } - else if (right->IsVector() && left->IsNumeric()) + else if ((right->IsVector() || right->IsQuaternion()) && left->IsNumeric()) { if (left->IsInteger()) { @@ -3162,7 +3208,7 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build) ExpEmit op1 = left->Emit(build); ExpEmit op2 = right->Emit(build); - if (IsVector()) + if (IsVector() || IsQuaternion()) { assert(Operator != '%'); if (right->IsVector()) @@ -3641,7 +3687,7 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) } // identical types are always comparable, if they can be placed in a register, so we can save most checks if this is the case. - if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()) && !(left->IsVector4() && right->IsVector4())) + if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()) && !(left->IsVector4() && right->IsVector4()) && !(left->IsQuaternion() && right->IsQuaternion())) { FxExpression *x; if (left->IsNumeric() && right->ValueType == TypeString && (x = StringConstToChar(right))) @@ -7139,7 +7185,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) classx = nullptr; return x; } - else if (classx->ExprType == EFX_LocalVariable && classx->IsVector()) // vectors are a special case because they are held in registers + else if (classx->ExprType == EFX_LocalVariable && (classx->IsVector() || classx->IsQuaternion())) // vectors are a special case because they are held in registers { // since this is a vector, all potential things that may get here are single float or an xy-vector. auto locvar = static_cast(classx); @@ -8058,6 +8104,25 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) } break; + case NAME_FQuat: + case NAME_Quat: + if (CheckArgSize(MethodName, ArgList, 1, 4, ScriptPosition)) + { + // Reuse vector expression + func = new FxQuaternionValue( + ArgList[0], + ArgList.Size() >= 2 ? ArgList[1] : nullptr, + ArgList.Size() >= 3 ? ArgList[2] : nullptr, + ArgList.Size() >= 4 ? ArgList[3] : nullptr, + ScriptPosition + ); + ArgList.Clear(); + + delete this; + auto vector = func->Resolve(ctx); + return vector; + } + break; default: ScriptPosition.Message(MSG_ERROR, "Call to unknown function '%s'", MethodName.GetChars()); @@ -8301,6 +8366,24 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) return x->Resolve(ctx); } } + else if (Self->IsQuaternion()) + { + // Reuse vector built-ins for quaternion + if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + auto x = new FxVectorBuiltin(Self, MethodName); + Self = nullptr; + delete this; + return x->Resolve(ctx); + } + } + else if (Self->ValueType == TypeString) { @@ -9275,12 +9358,13 @@ FxVectorBuiltin::~FxVectorBuiltin() FxExpression *FxVectorBuiltin::Resolve(FCompileContext &ctx) { SAFE_RESOLVE(Self, ctx); - assert(Self->IsVector()); // should never be created for anything else. + assert(Self->IsVector() || Self->IsQuaternion()); // should never be created for anything else. switch (Function.GetIndex()) { + case NAME_Angle: + assert(Self->IsVector()); case NAME_Length: case NAME_LengthSquared: - case NAME_Angle: ValueType = TypeFloat64; break; @@ -10673,7 +10757,7 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) else if (retCount == 1) { // If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.) - if (hasProto && protoRetCount > 0 && ctx.Function->SymbolName != NAME_None) + if (hasProto && protoRetCount > 0 && ctx.Function->SymbolName != NAME_None && Args[0]->ValueType != ctx.ReturnProto->ReturnTypes[0]) { Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false); Args[0] = Args[0]->Resolve(ctx); @@ -10683,11 +10767,15 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) } else { + assert(ctx.ReturnProto != nullptr); for (unsigned i = 0; i < retCount; i++) { - Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false); - Args[i] = Args[i]->Resolve(ctx); - if (Args[i] == nullptr) fail = true; + if (Args[i]->ValueType != ctx.ReturnProto->ReturnTypes[i]) + { + Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false); + Args[i] = Args[i]->Resolve(ctx); + if (Args[i] == nullptr) fail = true; + } } if (fail) { @@ -11065,6 +11153,7 @@ FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name, if (type == TypeFVector2) type = TypeVector2; else if (type == TypeFVector3) type = TypeVector3; else if (type == TypeFVector4) type = TypeVector4; + else if (type == TypeFQuaternion) type = TypeQuaternion; ValueType = type; VarFlags = varflags; diff --git a/src/common/scripting/backend/codegen.h b/src/common/scripting/backend/codegen.h index e2f167420..a86d27cce 100644 --- a/src/common/scripting/backend/codegen.h +++ b/src/common/scripting/backend/codegen.h @@ -343,6 +343,7 @@ public: bool IsVector2() const { return ValueType == TypeVector2 || ValueType == TypeFVector2; }; bool IsVector3() const { return ValueType == TypeVector3 || ValueType == TypeFVector3; }; bool IsVector4() const { return ValueType == TypeVector4 || ValueType == TypeFVector4; }; + bool IsQuaternion() const { return ValueType == TypeQuaternion || ValueType == TypeFQuaternion; }; bool IsBoolCompat() const { return ValueType->isScalar(); } bool IsObject() const { return ValueType->isObjectPointer(); } bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); } @@ -577,6 +578,20 @@ public: }; +//========================================================================== +// +// +// +//========================================================================== + +class FxQuaternionValue : public FxVectorValue +{ +public: + FxQuaternionValue(FxExpression* x, FxExpression* y, FxExpression* z, FxExpression* w, const FScriptPosition& sc); + FxExpression* Resolve(FCompileContext&); +}; + + //========================================================================== // // diff --git a/src/common/scripting/core/types.cpp b/src/common/scripting/core/types.cpp index f9b367da8..046c8358e 100644 --- a/src/common/scripting/core/types.cpp +++ b/src/common/scripting/core/types.cpp @@ -62,9 +62,11 @@ PStateLabel *TypeStateLabel; PStruct *TypeVector2; PStruct *TypeVector3; PStruct* TypeVector4; +PStruct* TypeQuaternion; PStruct* TypeFVector2; PStruct* TypeFVector3; PStruct* TypeFVector4; +PStruct* TypeFQuaternion; PStruct *TypeColorStruct; PStruct *TypeStringStruct; PPointer *TypeNullPtr; @@ -410,6 +412,40 @@ void PType::StaticInit() TypeFVector4->RegCount = 4; TypeFVector4->isOrdered = true; + + TypeQuaternion = new PStruct(NAME_Quat, nullptr); + TypeQuaternion->AddField(NAME_X, TypeFloat64); + TypeQuaternion->AddField(NAME_Y, TypeFloat64); + TypeQuaternion->AddField(NAME_Z, TypeFloat64); + TypeQuaternion->AddField(NAME_W, TypeFloat64); + // allow vector access. + TypeQuaternion->Symbols.AddSymbol(Create(NAME_XYZ, TypeVector3, VARF_Transient, 0)); + TypeQuaternion->Symbols.AddSymbol(Create(NAME_XY, TypeVector2, VARF_Transient, 0)); + TypeTable.AddType(TypeQuaternion, NAME_Struct); + TypeQuaternion->loadOp = OP_LV4; + TypeQuaternion->storeOp = OP_SV4; + TypeQuaternion->moveOp = OP_MOVEV4; + TypeQuaternion->RegType = REGT_FLOAT; + TypeQuaternion->RegCount = 4; + TypeQuaternion->isOrdered = true; + + TypeFQuaternion = new PStruct(NAME_FQuat, nullptr); + TypeFQuaternion->AddField(NAME_X, TypeFloat32); + TypeFQuaternion->AddField(NAME_Y, TypeFloat32); + TypeFQuaternion->AddField(NAME_Z, TypeFloat32); + TypeFQuaternion->AddField(NAME_W, TypeFloat32); + // allow accessing xyz as a vector3 + TypeFQuaternion->Symbols.AddSymbol(Create(NAME_XYZ, TypeFVector3, VARF_Transient, 0)); + TypeFQuaternion->Symbols.AddSymbol(Create(NAME_XY, TypeFVector2, VARF_Transient, 0)); + TypeTable.AddType(TypeFQuaternion, NAME_Struct); + TypeFQuaternion->loadOp = OP_LFV4; + TypeFQuaternion->storeOp = OP_SFV4; + TypeFQuaternion->moveOp = OP_MOVEV4; + TypeFQuaternion->RegType = REGT_FLOAT; + TypeFQuaternion->RegCount = 4; + TypeFQuaternion->isOrdered = true; + + Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_sByte, TypeSInt8)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Byte, TypeUInt8)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Short, TypeSInt16)); @@ -429,9 +465,11 @@ void PType::StaticInit() Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Vector2, TypeVector2)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Vector3, TypeVector3)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Vector4, TypeVector4)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_Quat, TypeQuaternion)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_FVector2, TypeFVector2)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_FVector3, TypeFVector3)); Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_FVector4, TypeFVector4)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(Create(NAME_FQuat, TypeFQuaternion)); } diff --git a/src/common/scripting/core/types.h b/src/common/scripting/core/types.h index 25fe895fd..862138c1d 100644 --- a/src/common/scripting/core/types.h +++ b/src/common/scripting/core/types.h @@ -619,6 +619,8 @@ extern PStruct* TypeVector4; extern PStruct* TypeFVector2; extern PStruct* TypeFVector3; extern PStruct* TypeFVector4; +extern PStruct* TypeQuaternion; +extern PStruct* TypeFQuaternion; extern PStruct *TypeColorStruct; extern PStruct *TypeStringStruct; extern PStatePointer *TypeState; diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index ed4848897..52bbd55f2 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -2154,7 +2154,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool do { auto type = DetermineType(c->Type(), f, f->Name, t, false, false); - if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4) + if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) { // structs and classes only get passed by pointer. type = NewPointer(type); @@ -2176,6 +2176,10 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { type = TypeVector4; } + else if (type == TypeFQuaternion) + { + type = TypeQuaternion; + } // TBD: disallow certain types? For now, let everything pass that isn't an array. rets.Push(type); t = static_cast(t->SiblingNext); @@ -2353,7 +2357,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); int flags = 0; - if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4) || type->isDynArray()) + if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray()) { // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); @@ -2370,12 +2374,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { elementcount = 3; } - else if (type == TypeVector4 || type == TypeFVector4) + else if (type == TypeVector4 || type == TypeFVector4 || type == TypeQuaternion || type == TypeFQuaternion) { elementcount = 4; } } - if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4) + if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) { // If it's TypeError, then an error was already given if (type != TypeError) @@ -2437,6 +2441,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool vmval[2] = static_cast(vx->xyzw[2])->GetValue().GetFloat(); vmval[3] = static_cast(vx->xyzw[3])->GetValue().GetFloat(); } + else if ((type == TypeQuaternion || type == TypeFQuaternion) && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(4)) + { + auto vx = static_cast(x); + vmval[0] = static_cast(vx->xyzw[0])->GetValue().GetFloat(); + vmval[1] = static_cast(vx->xyzw[1])->GetValue().GetFloat(); + vmval[2] = static_cast(vx->xyzw[2])->GetValue().GetFloat(); + vmval[3] = static_cast(vx->xyzw[3])->GetValue().GetFloat(); + } else if (!x->isConstant()) { Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars()); diff --git a/src/common/scripting/jit/jit.cpp b/src/common/scripting/jit/jit.cpp index 6cd78bd05..13679d59a 100644 --- a/src/common/scripting/jit/jit.cpp +++ b/src/common/scripting/jit/jit.cpp @@ -7,6 +7,8 @@ extern PString *TypeString; extern PStruct *TypeVector2; extern PStruct *TypeVector3; extern PStruct* TypeVector4; +extern PStruct* TypeQuaternion; +extern PStruct* TypeFQuaternion; static void OutputJitLog(const asmjit::StringLogger &logger); @@ -316,7 +318,7 @@ void JitCompiler::SetupSimpleFrame() cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f))); cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f))); } - else if (type == TypeVector4 || type == TypeFVector4) + else if (type == TypeVector4 || type == TypeFVector4 || type == TypeQuaternion || type == TypeFQuaternion) { cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f))); cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));