Skip to content

Commit

Permalink
Quaternion structure and math ops
Browse files Browse the repository at this point in the history
  • Loading branch information
MrRaveYard authored and coelckers committed Nov 21, 2022
1 parent 802f77a commit 540f778
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 24 deletions.
33 changes: 28 additions & 5 deletions src/common/scripting/backend/codegen.cpp
Expand Up @@ -3093,7 +3093,14 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
[[fallthrough]];

case '*':
if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric())
if (Operator == '*' && left->IsQuaternion() && (right->IsVector3() || right->IsQuaternion()))
{
// quat * vec3
// quat * quat
ValueType = right->ValueType;
break;
}
else if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric())
{
if (right->IsInteger())
{
Expand Down Expand Up @@ -3208,10 +3215,26 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build)
ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build);

if (IsVector() || IsQuaternion())
if (Operator == '*' && left->IsQuaternion() && right->IsQuaternion())
{
op1.Free(build);
op2.Free(build);
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
build->Emit(OP_MULQQ_RR, to.RegNum, op1.RegNum, op2.RegNum);
return to;
}
else if (Operator == '*' && left->IsQuaternion() && right->IsVector3())
{
op1.Free(build);
op2.Free(build);
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
build->Emit(OP_MULQV3_RR, to.RegNum, op1.RegNum, op2.RegNum);
return to;
}
else if (IsVector() || IsQuaternion())
{
assert(Operator != '%');
if (right->IsVector())
if (left->IsFloat())
{
std::swap(op1, op2);
}
Expand Down Expand Up @@ -8385,9 +8408,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
delete this;
return x->Resolve(ctx);
}
}


Self->ValueType = TypeQuaternionStruct;
}
else if (Self->ValueType == TypeString)
{
if (MethodName == NAME_Length) // This is an intrinsic because a dedicated opcode exists for it.
Expand Down
2 changes: 1 addition & 1 deletion src/common/scripting/backend/codegen.h
Expand Up @@ -344,7 +344,7 @@ class FxExpression
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 IsQuaternion() const { return ValueType == TypeQuaternion || ValueType == TypeFQuaternion || ValueType == TypeQuaternionStruct; };
bool IsBoolCompat() const { return ValueType->isScalar(); }
bool IsObject() const { return ValueType->isObjectPointer(); }
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); }
Expand Down
2 changes: 2 additions & 0 deletions src/common/scripting/core/types.cpp
Expand Up @@ -69,6 +69,7 @@ PStruct* TypeFVector4;
PStruct* TypeFQuaternion;
PStruct *TypeColorStruct;
PStruct *TypeStringStruct;
PStruct* TypeQuaternionStruct;
PPointer *TypeNullPtr;
PPointer *TypeVoidPtr;

Expand Down Expand Up @@ -316,6 +317,7 @@ void PType::StaticInit()
TypeVoidPtr = NewPointer(TypeVoid, false);
TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeStringStruct = NewStruct("Stringstruct", nullptr, true);
TypeQuaternionStruct = NewStruct("QuatStruct", nullptr, true);
TypeFont = NewPointer(NewStruct("Font", nullptr, true));
#ifdef __BIG_ENDIAN__
TypeColorStruct->AddField(NAME_a, TypeUInt8);
Expand Down
1 change: 1 addition & 0 deletions src/common/scripting/core/types.h
Expand Up @@ -623,6 +623,7 @@ extern PStruct* TypeQuaternion;
extern PStruct* TypeFQuaternion;
extern PStruct *TypeColorStruct;
extern PStruct *TypeStringStruct;
extern PStruct* TypeQuaternionStruct;
extern PStatePointer *TypeState;
extern PPointer *TypeFont;
extern PStateLabel *TypeStateLabel;
Expand Down
63 changes: 63 additions & 0 deletions src/common/scripting/interface/vmnatives.cpp
Expand Up @@ -1129,3 +1129,66 @@ DEFINE_FIELD(DStatusBarCore, drawClip);
DEFINE_FIELD(DStatusBarCore, fullscreenOffsets);
DEFINE_FIELD(DStatusBarCore, defaultScale);
DEFINE_FIELD(DHUDFont, mFont);

//
// Quaternion
DEFINE_ACTION_FUNCTION(_QuatStruct, FromEuler)
{
PARAM_PROLOGUE;
PARAM_FLOAT(yaw);
PARAM_FLOAT(pitch);
PARAM_FLOAT(roll);

I_Error("Quat.FromEuler not implemented");
ret->SetVector4({0, 1, 2, 3}); // X Y Z W
return 1;
}

DEFINE_ACTION_FUNCTION(_QuatStruct, AxisAngle)
{
PARAM_PROLOGUE;
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
PARAM_FLOAT(angle);

I_Error("Quat.AxisAngle not implemented");
ret->SetVector4({ 0, 1, 2, 3 }); // X Y Z W
return 1;
}

DEFINE_ACTION_FUNCTION(_QuatStruct, Nlerp)
{
PARAM_PROLOGUE;
PARAM_FLOAT(ax);
PARAM_FLOAT(ay);
PARAM_FLOAT(az);
PARAM_FLOAT(aw);
PARAM_FLOAT(bx);
PARAM_FLOAT(by);
PARAM_FLOAT(bz);
PARAM_FLOAT(bw);
PARAM_FLOAT(f);

I_Error("Quat.NLerp not implemented");
ret->SetVector4({ 0, 1, 2, 3 }); // X Y Z W
return 1;
}

DEFINE_ACTION_FUNCTION(_QuatStruct, Slerp)
{
PARAM_PROLOGUE;
PARAM_FLOAT(ax);
PARAM_FLOAT(ay);
PARAM_FLOAT(az);
PARAM_FLOAT(aw);
PARAM_FLOAT(bx);
PARAM_FLOAT(by);
PARAM_FLOAT(bz);
PARAM_FLOAT(bw);
PARAM_FLOAT(f);

I_Error("Quat.SLerp not implemented");
ret->SetVector4({ 0, 1, 2, 3 }); // X Y Z W
return 1;
}
56 changes: 56 additions & 0 deletions src/common/scripting/jit/jit_math.cpp
Expand Up @@ -1606,6 +1606,62 @@ void JitCompiler::EmitEQV4_K()
I_Error("EQV4_K is not used.");
}

// Quaternion ops
void FuncMULQQ(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz, double bw)
{
*reinterpret_cast<DQuaternion*>(result) = DQuaternion(ax, ay, az, aw) * DQuaternion(bx, by, bz, bw);
}

void FuncMULQV3(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz)
{
*reinterpret_cast<DVector3*>(result) = DQuaternion(ax, ay, az, aw) * DVector3(bx, by, bz);
}

void JitCompiler::EmitMULQQ_RR()
{
auto stack = GetTemporaryVectorStackStorage();
auto tmp = newTempIntPtr();
cc.lea(tmp, stack);

auto call = CreateCall<void, void*, double, double, double, double, double, double, double, double>(FuncMULQQ);
call->setArg(0, tmp);
call->setArg(1, regF[B + 0]);
call->setArg(2, regF[B + 1]);
call->setArg(3, regF[B + 2]);
call->setArg(4, regF[B + 3]);
call->setArg(5, regF[C + 0]);
call->setArg(6, regF[C + 1]);
call->setArg(7, regF[C + 2]);
call->setArg(8, regF[C + 3]);

cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0));
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
cc.movsd(regF[A + 3], asmjit::x86::qword_ptr(tmp, 24));
}

void JitCompiler::EmitMULQV3_RR()
{
auto stack = GetTemporaryVectorStackStorage();
auto tmp = newTempIntPtr();
cc.lea(tmp, stack);

auto call = CreateCall<void, void*, double, double, double, double, double, double, double>(FuncMULQV3);
call->setArg(0, tmp);
call->setArg(1, regF[B + 0]);
call->setArg(2, regF[B + 1]);
call->setArg(3, regF[B + 2]);
call->setArg(4, regF[B + 3]);
call->setArg(5, regF[C + 0]);
call->setArg(6, regF[C + 1]);
call->setArg(7, regF[C + 2]);

cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0));
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
}


/////////////////////////////////////////////////////////////////////////////
// Pointer math.

Expand Down
21 changes: 20 additions & 1 deletion src/common/scripting/jit/jitintern.h
Expand Up @@ -186,7 +186,13 @@ class JitCompiler
asmjit::CCFuncCall *CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)) { return cc.call(asmjit::imm_ptr(reinterpret_cast<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6)>(func))), asmjit::FuncSignature6<RetType, P1, P2, P3, P4, P5, P6>()); }

template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
asmjit::CCFuncCall *CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7)) { return cc.call(asmjit::imm_ptr(reinterpret_cast<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7)>(func))), asmjit::FuncSignature7<RetType, P1, P2, P3, P4, P5, P6, P7>()); }
asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7)) { return cc.call(asmjit::imm_ptr(reinterpret_cast<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7)>(func))), asmjit::FuncSignature7<RetType, P1, P2, P3, P4, P5, P6, P7>()); }

template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8)) { return cc.call(asmjit::imm_ptr(reinterpret_cast<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7, P8)>(func))), asmjit::FuncSignature8<RetType, P1, P2, P3, P4, P5, P6, P7, P8>()); }

template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9)) { return cc.call(asmjit::imm_ptr(reinterpret_cast<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7, P8, P9)>(func))), asmjit::FuncSignature9<RetType, P1, P2, P3, P4, P5, P6, P7, P8, P9>()); }

FString regname;
size_t tmpPosInt32, tmpPosInt64, tmpPosIntPtr, tmpPosXmmSd, tmpPosXmmSs, tmpPosXmmPd, resultPosInt32, resultPosIntPtr, resultPosXmmSd;
Expand Down Expand Up @@ -305,6 +311,19 @@ class JitCompiler

TArray<OpcodeLabel> labels;

// Get temporary storage enough for DVector4 which is required by operation such as MULQQ and MULQV3
bool vectorStackAllocated = false;
asmjit::X86Mem vectorStack;
asmjit::X86Mem GetTemporaryVectorStackStorage()
{
if (!vectorStackAllocated)
{
vectorStack = cc.newStack(sizeof(DVector4), alignof(DVector4), "tmpDVector4");
vectorStackAllocated = true;
}
return vectorStack;
}

const VMOP *pc;
VM_UBYTE op;
};
Expand Down
17 changes: 16 additions & 1 deletion src/common/scripting/vm/vmexec.h
Expand Up @@ -1887,7 +1887,22 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
ASSERTF(B+3); ASSERTKF(C+3);
fcp = &konstf[C];
goto Do_EQV4;

OP(MULQV3_RR):
ASSERTF(a + 2); ASSERTF(B + 3); ASSERTF(C + 2);
{
const DQuaternion& q = reinterpret_cast<DQuaternion&>(reg.f[B]);
const DVector3& v = reinterpret_cast<DVector3&>(reg.f[C]);
reinterpret_cast<DVector3&>(reg.f[A]) = q * v;
}
NEXTOP;
OP(MULQQ_RR):
ASSERTF(a + 3); ASSERTF(B + 3); ASSERTF(C + 3);
{
const DQuaternion& q1 = reinterpret_cast<DQuaternion&>(reg.f[B]);
const DQuaternion& q2 = reinterpret_cast<DQuaternion&>(reg.f[C]);
reinterpret_cast<DQuaternion&>(reg.f[A]) = q1 * q2;
}
NEXTOP;
OP(ADDA_RR):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
c = reg.d[C];
Expand Down
4 changes: 4 additions & 0 deletions src/common/scripting/vm/vmops.h
Expand Up @@ -278,6 +278,10 @@ xx(LENV4, lenv4, RFRV, NOP, 0, 0) // fA = vB.Length
xx(EQV4_R, beqv4, CVRR, NOP, 0, 0) // if ((vB == vkC) != A) then pc++ (inexact if A & 33)
xx(EQV4_K, beqv4, CVRK, NOP, 0, 0) // this will never be used.

// Quaternion math
xx(MULQQ_RR, mulqq, RVRVRV, NOP, 0, 0) // qA = qB * qC
xx(MULQV3_RR, mulqv3, RVRVRV, NOP, 0, 0) // qA = qB * vC

// Pointer math.
xx(ADDA_RR, add, RPRPRI, NOP, 0, 0) // pA = pB + dkC
xx(ADDA_RK, add, RPRPKI, ADDA_RR,4, REGT_INT)
Expand Down
58 changes: 42 additions & 16 deletions src/common/utility/vectors.h
Expand Up @@ -724,6 +724,11 @@ struct TVector4
{
}

TVector4(const vec_t v[4])
: TVector4(v[0], v[1], v[2], v[3])
{
}

void Zero()
{
Z = Y = X = W = 0;
Expand Down Expand Up @@ -846,22 +851,6 @@ struct TVector4
return TVector4(v.X * scalar, v.Y * scalar, v.Z * scalar, v.W * scalar);
}

// Multiply as Quaternion
TVector4& operator*= (const TVector4& v)
{
*this = *this * v;
return *this;
}

friend TVector4 operator* (const TVector4& v1, const TVector4& v2)
{
return TVector4(v2.W * v1.X + v2.X * v1.W + v2.Y * v1.Z - v1.Z * v1.Y,
v2.W * v1.Y + v2.Y * v1.W + v2.Z * v1.X - v2.X * v1.Z,
v2.W * v1.Z + v2.Z * v1.W + v2.X * v1.Y - v2.Y * v1.X,
v2.W * v1.W - v2.X * v1.X - v2.Y * v1.Y - v2.Z * v1.Z
);
}

// Scalar division
TVector4 &operator/= (vec_t scalar)
{
Expand Down Expand Up @@ -1727,19 +1716,56 @@ inline TMatrix3x3<T>::TMatrix3x3(const TVector3<T> &axis, TAngle<T> degrees)
}


template<typename vec_t>
class TQuaternion : public TVector4<vec_t>
{
public:
TQuaternion() = default;
TQuaternion(vec_t a, vec_t b, vec_t c, vec_t d) : TVector4<vec_t>(a, b, c, d) {}
TQuaternion(const vec_t* o) : TVector4<vec_t>(o[0], o[1], o[2], o[3]) {}
TQuaternion(const TQuaternion& other) = default;

TQuaternion& operator*= (const TQuaternion& q)
{
*this = *this * q;
return *this;
}

friend TQuaternion<vec_t> operator* (const TQuaternion<vec_t>& q1, const TQuaternion<vec_t>& q2)
{
return TQuaternion(
q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y,
q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X,
q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W,
q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z
);
}

// Rotate Vector3 by Quaternion q
friend TVector3<vec_t> operator* (const TQuaternion<vec_t>& q, const TVector3<vec_t>& v)
{
auto r = TQuaternion({ v.X, v.Y, v.Z, 0 }) * TQuaternion({ -q.X, -q.Y, -q.Z, q.W });
r = q * r;
return TVector3(r.X, r.Y, r.Z);
}
};


typedef TVector2<float> FVector2;
typedef TVector3<float> FVector3;
typedef TVector4<float> FVector4;
typedef TRotator<float> FRotator;
typedef TMatrix3x3<float> FMatrix3x3;
typedef TAngle<float> FAngle;
typedef TQuaternion<float> FQuaternion;

typedef TVector2<double> DVector2;
typedef TVector3<double> DVector3;
typedef TVector4<double> DVector4;
typedef TRotator<double> DRotator;
typedef TMatrix3x3<double> DMatrix3x3;
typedef TAngle<double> DAngle;
typedef TQuaternion<double> DQuaternion;

constexpr DAngle nullAngle = DAngle::fromDeg(0.);
constexpr DAngle minAngle = DAngle::fromDeg(1. / 65536.);
Expand Down
10 changes: 10 additions & 0 deletions wadsrc/static/zscript/engine/base.zs
Expand Up @@ -892,3 +892,13 @@ struct Translation version("2.4")
}
}

// Convenient way to attach functions to Quat
struct QuatStruct native
{
native static Quat SLerp(Quat from, Quat to, double f);
native static Quat NLerp(Quat from, Quat to, double f);
native static Quat FromEuler(double yaw, double pitch, double roll);
native static Quat AxisAngle(Vector3 xyz, double angle);
// native double Length();
// native Quat Unit();
}

0 comments on commit 540f778

Please sign in to comment.