summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorlepelog <25211966+lepelog@users.noreply.github.com>2025-12-13 01:23:37 +0100
committerlepelog <25211966+lepelog@users.noreply.github.com>2026-02-01 18:14:38 +0100
commitb017949c4b37acbfea90fdd5641dbdceaee688a7 (patch)
tree1648b752fd8a55070ac3555ea9615e09dd95a7b1 /include
parent62fc066b605680af84cc921aba070b62e7df8ff7 (diff)
d_t_mass_obj 99%
Diffstat (limited to 'include')
-rw-r--r--include/JSystem/JMath/JMath.h2
-rw-r--r--include/c/c_counter.h15
-rw-r--r--include/d/a/d_a_base.h12
-rw-r--r--include/d/col/c/c_cc_d.h16
-rw-r--r--include/d/col/cc/d_cc_mass_s.h3
-rw-r--r--include/d/d_light_env.h30
-rw-r--r--include/d/d_player_act.h10
-rw-r--r--include/d/t/d_t_mass_obj.h301
-rw-r--r--include/egg/math/eggQuat.h22
-rw-r--r--include/egg/math/eggVector.h7
-rw-r--r--include/m/m_mtx.h10
-rw-r--r--include/nw4r/ut/ut_Color.h11
12 files changed, 434 insertions, 5 deletions
diff --git a/include/JSystem/JMath/JMath.h b/include/JSystem/JMath/JMath.h
index 343571b8..08e15fb9 100644
--- a/include/JSystem/JMath/JMath.h
+++ b/include/JSystem/JMath/JMath.h
@@ -214,8 +214,8 @@ namespace JMathInlineVEC {
}
inline f32 C_VECSquareMag(register const Vec* v) {
- register f32 x_y;
register f32 z;
+ register f32 x_y;
register f32 res;
#ifdef __MWERKS__
asm {
diff --git a/include/c/c_counter.h b/include/c/c_counter.h
new file mode 100644
index 00000000..5f3590e4
--- /dev/null
+++ b/include/c/c_counter.h
@@ -0,0 +1,15 @@
+#ifndef C_COUNTER_H
+#define C_COUNTER_H
+
+#include "common.h"
+
+class cCounter_c {
+public:
+ static s32 GetGameFrame() {
+ return m_gameFrame;
+ }
+private:
+ static s32 m_gameFrame;
+};
+
+#endif
diff --git a/include/d/a/d_a_base.h b/include/d/a/d_a_base.h
index ee2350c6..f6823dae 100644
--- a/include/d/a/d_a_base.h
+++ b/include/d/a/d_a_base.h
@@ -158,6 +158,9 @@ public:
mVec3_c &getPosition() {
return mPosition;
}
+ mVec3_c const &getPosition() const {
+ return mPosition;
+ }
mAng3_c &getRotation() {
return mRotation;
}
@@ -166,6 +169,11 @@ public:
return mPosition - other.mPosition;
}
+ void getPostionDifferenceOut(const mVec3_c &other, mVec3_c& result) const {
+ mVec3_c diff = (other - mPosition);
+ result = diff;
+ }
+
f32 getHeightDifference(const dAcBase_c &b) const {
return mPosition.y - b.mPosition.y;
}
@@ -194,6 +202,10 @@ public:
return mRoomID == room;
}
+ void setRoomId(u32 room) {
+ mRoomID = room;
+ }
+
void unsetActorProperty(u32 property) {
mActorProperties &= ~property;
}
diff --git a/include/d/col/c/c_cc_d.h b/include/d/col/c/c_cc_d.h
index b4f7f4f2..58bf05e9 100644
--- a/include/d/col/c/c_cc_d.h
+++ b/include/d/col/c/c_cc_d.h
@@ -751,6 +751,13 @@ public:
const mVec3_c &GetAtHitPos() const;
mVec3_c &GetAtHitPos();
+ const mVec3_c &GetAtHitPosInline() const {
+ return mAt.mHitPos;
+ }
+
+ mVec3_c &GetAtHitPosInline() {
+ return mAt.mHitPos;
+ }
bool GetAtFlag0x2() const;
bool GetAtFlag0x4() const;
bool GetAtFlag0x8() const;
@@ -773,6 +780,9 @@ public:
bool ChkTgAtHitType(u32) const;
u32 GetTgAtHitType() const;
+ bool ChkAtType(u32 mask) const {
+ return mAt.MskType(mask);
+ }
bool ChkTgBit14() const;
u8 GetTgDamage() const;
u16 GetTgDamageFlags() const;
@@ -1051,6 +1061,12 @@ public:
u32 ChkCo_0x40000000() {
return mCo.MskSPrm(0x40000000);
}
+ u32 ChkCo_0x10() {
+ return mCo.MskSPrm(0x10);
+ }
+ u32 ChkCo_0x2() {
+ return mCo.MskSPrm(0x2);
+ }
/**
* SET HIT
diff --git a/include/d/col/cc/d_cc_mass_s.h b/include/d/col/cc/d_cc_mass_s.h
index 056ec3cf..581120ea 100644
--- a/include/d/col/cc/d_cc_mass_s.h
+++ b/include/d/col/cc/d_cc_mass_s.h
@@ -63,6 +63,9 @@ public:
cCcD_Obj *GetAtHitObj() const {
return mpAtObj;
}
+ cCcD_Obj *GetCoHitObj() const {
+ return mpCoObj;
+ }
};
class dCcMassS_Mng {
diff --git a/include/d/d_light_env.h b/include/d/d_light_env.h
index 5e48e39e..a2267a02 100644
--- a/include/d/d_light_env.h
+++ b/include/d/d_light_env.h
@@ -434,6 +434,30 @@ public:
return field_0x2F1C;
}
+ bool getfield_0x38B0() const {
+ return field_0x38B0;
+ }
+
+ void setfield_0x38B0(bool val) {
+ field_0x38B0 = val;
+ }
+
+ f32 getfield_0x38C0() const {
+ return field_0x38C0;
+ }
+
+ const mVec3_c& getfield_0x38B4() const {
+ return field_0x38B4;
+ }
+
+ mVec3_c& getfield_0x38C8() {
+ return field_0x38C8;
+ }
+
+ void setfield_0x38C4(s32 val) {
+ field_0x38C4 = val;
+ }
+
s32 getField_0x38DC() const {
return field_0x38DC;
}
@@ -442,6 +466,10 @@ public:
field_0x38DC = v;
}
+ f32 getfield_0x5D44() const {
+ return field_0x5D44;
+ }
+
void setField_0x5D48(f32 val) {
field_0x5D48 = val;
}
@@ -503,7 +531,7 @@ private:
/* 0x48E4 */ Bpm4 field_0x48E4;
/* 0x5CE4 */ PaletteDefaultMCF field_0x5CE4;
/* 0x5D04 */ TwoPaletteDefaultMCF field_0x5D04;
- /* 0x5D44 */ u8 field_0x5D44[0x5D48 - 0x5D44];
+ /* 0x5D44 */ f32 field_0x5D44;
/* 0x5D48 */ f32 field_0x5D48;
/* 0x5D4C */ u8 field_0x5D4C[0x5D59 - 0x5D4C];
/* 0x5D59 */ struct { // Anonymous until more is known
diff --git a/include/d/d_player_act.h b/include/d/d_player_act.h
index 4d3b9dc2..b8c465dd 100644
--- a/include/d/d_player_act.h
+++ b/include/d/d_player_act.h
@@ -61,8 +61,8 @@ public:
/* vt 0x0C0 */ virtual bool isUsingWhip() const {
return false;
}
- /* vt 0x0C4 */ virtual bool anotherThingWithWhip() {
- return false;
+ /* vt 0x0C4 */ virtual const mVec3_c* anotherThingWithWhip() {
+ return nullptr;
}
/* vt 0x0C8 */ virtual bool somethingWithWHip() {
return false;
@@ -642,7 +642,7 @@ public:
return mAttackDirection == ATTACK_JUMP_SLASH;
}
- u16 getSpecificAttackDirection() const {
+ s32 getSpecificAttackDirection() const {
return mSpecificAttackDirection;
}
@@ -650,6 +650,10 @@ public:
return vt_0x1C0() != nullptr;
}
+ static s32 getCurrentSwordTypeInline() {
+ return sCurrentSword;
+ }
+
static bool isInEvent();
bool isAttackingLeft() const;
bool isAttackingRight() const;
diff --git a/include/d/t/d_t_mass_obj.h b/include/d/t/d_t_mass_obj.h
index bfb7166a..98a34f69 100644
--- a/include/d/t/d_t_mass_obj.h
+++ b/include/d/t/d_t_mass_obj.h
@@ -1,7 +1,255 @@
#ifndef D_T_MASS_OBJ_H
#define D_T_MASS_OBJ_H
+#include "c/c_math.h"
+#include "common.h"
+#include "d/d_heap.h"
#include "d/t/d_tg.h"
+#include "egg/core/eggHeap.h"
+#include "egg/math/eggMatrix.h"
+#include "egg/math/eggQuat.h"
+#include "m/m3d/m_proc.h"
+#include "m/m3d/m_scnleaf.h"
+#include "m/m_angle.h"
+#include "m/m_color.h"
+#include "m/m_heap.h"
+#include "m/m_mtx.h"
+#include "m/m_quat.h"
+#include "m/m_vec.h"
+#include "nw4r/g3d/res/g3d_resmat.h"
+#include "nw4r/g3d/res/g3d_resshp.h"
+#include "nw4r/math/math_types.h"
+#include "rvl/GX/GXTypes.h"
+#include "s/s_Math.h"
+#include "toBeSorted/tlist.h"
+
+class dTgMassObjInstance;
+class dTgMassObjTransform;
+class GrassModel;
+class GrassModelData;
+class dCcMassS_HitInf;
+class dAcObjBase_c;
+
+typedef TList<dTgMassObjInstance, 0> dTgMassObjInstanceList;
+typedef TList<dTgMassObjTransform, 0> dTgMassObjTransformList;
+
+// a single piece of grass
+// dTgMassObjInstance
+// size: 0x34
+class dTgMassObjInstance {
+public:
+ enum Flags {
+ TG_MASS_UNK2_IS_HIDDEN = 1,
+ TG_MASS_UNK2_IS_CUT = 2,
+ TG_MASS_UNK2_TIMESHIFT_RELATED = 4,
+ };
+ dTgMassObjInstance(){
+ reset();
+ }
+ ~dTgMassObjInstance() {}
+ void reset();
+ void releaseDynamicTransform(GrassModel *param_2);
+ s32 checkForHit(GrassModel *param2,GrassModelData *param3,u16 roomid);
+ undefined4 checkForHit(u32 param_2,dCcMassS_HitInf& param_3,dAcObjBase_c* param_4,
+ GrassModel *param_5,GrassModelData* param_6,undefined4 roomid);
+ undefined4 FUN_80278c70(u32 param_2,dCcMassS_HitInf & param_3,dAcBase_c *param_4,
+ GrassModel *param_5);
+ undefined4 handleLinkSpinAttack(GrassModel *param_2);
+ s32 handleTimeshiftZone();
+ void getDrawMatrix(mMtx_c *pOut);
+ bool isHidden(f32 param2, f32 param3);
+ bool isItemDrop10() const {
+ return mSpecialItemDropId == 10;
+ }
+
+// private:
+ /* 0x00 */ TListNode<dTgMassObjInstance> mLink;
+ /* 0x08 */ GXColorS10 mTevColor;
+ /* 0x10 */ mVec3_c mGroundHeight;
+ /* 0x1C */ dTgMassObjTransform *mInitPosTransform;
+ /* 0x20 */ dTgMassObjTransform *mDynamicTransform;
+ /* 0x24 */ f32 field_0x24; // unused?
+ /* 0x28 */ f32 mScale;
+ /* 0x2C */ u16 mGrassFlags;
+ /* 0x2E */ u16 yRotation;
+ /* 0x30 */ u8 mSpecialItemDropId;
+ /* 0x31 */ u8 mLightingCode;
+ /* 0x32 */ u8 mMassObjSubtype;
+ /* 0x33 */ bool mActiveInPresent;
+};
+
+// a transform for a single piece of grass
+// size: 0x50
+class dTgMassObjTransform {
+public:
+
+ dTgMassObjTransform();
+ ~dTgMassObjTransform() {}
+ void update();
+ const mMtx_c& getMtx() const {
+ return mMtx;
+ }
+
+ void setQuat(const mQuat_c& quat) {
+ mQuat.set(quat);
+ }
+
+ void setMtxFromQuat(const mQuat_c& quat) {
+ mMtx.fromQuat(quat);
+ }
+
+ /* 0x00 */ s32 field_0x00;
+ /* 0x04 */ s32 field_0x04;
+ /* 0x08 */ mMtx_c mMtx;
+ /* 0x38 */ mAng mRotY;
+ /* 0x3A */ mAng mRotX;
+ /* 0x3C */ s16 mRotXSpeed;
+ /* 0x40 */ EGG::Quatf mQuat;
+};
+
+class GrassModelData {
+public:
+ GrassModelData() : mLinkedLists(nullptr) {}
+ virtual ~GrassModelData() {
+ destroyLinkedLists();
+ }
+ void initRes(nw4r::g3d::ResMat pResMat, nw4r::g3d::ResShp pResShp);
+ void addToRoom(s32 room, dTgMassObjInstance* p3);
+ void removeFromRoom(s32 room, dTgMassObjInstance* p3);
+ void update(GrassModel*);
+ bool tryCreateLinkedLists(s32 entrycount, EGG::Heap* heap);
+ void destroyLinkedLists();
+ void unloadRoom(GrassModel *param_2,int roomid);
+ void draw(f32 param_1,f32 param_2,
+ nw4r::math::MTX34 *param_4);
+private:
+ /* 0x04 */ nw4r::g3d::ResMat mResMat;
+ /* 0x08 */ nw4r::g3d::ResShp mResShp;
+ /* 0x0C */ dTgMassObjInstanceList* mLinkedLists;
+ /* 0x10 */ u16 mLinkedListsCount;
+};
+
+struct GrassModelInfo {
+ /* 0x00 */ char* mArcName;
+ /* 0x04 */ char* mModelName;
+ /* 0x08 */ char* mCutModelName;
+ /* 0x0C */ u32 field_0x0C;
+ /* 0x10 */ u16 mRadius;
+ /* 0x12 */ u16 field_0x12;
+ /* 0x14 */ u16 mInstanceCount;
+ /* 0x16 */ u16 mStaticTransformCount;
+ /* 0x18 */ u16 mDynamicTransformCount;
+ /* 0x1A */ u8 field_0x1A;
+ /* 0x1B */ u8 field_0x1B; // might be padding
+};
+
+
+struct GrassModelNames {
+ /* 0x00 */ char* mArcName;
+ /* 0x04 */ char* mModelName;
+ /* 0x08 */ char* mCutModelName;
+};
+
+class GrassModel : public m3d::proc_c {
+public:
+ GrassModel() : mpModelData(nullptr), mInstanceList(nullptr), mStaticTransformationList(nullptr), mDynamicTransformationList(nullptr), mCutCounter(0) {}
+ /* vt 0x08 */ virtual ~GrassModel() {
+ remove();
+ }
+ undefined4
+ setModelInfo(f32 radius,f32 param_2,int param_4,
+ s32 roomCount,u16 instanceListLength,u16 staticTransformationListLength,int dynamicTransformationListLength,
+ undefined1 param_9,s32 opaDrawPrio,u32 xluDrawPrio,mHeapAllocator_c *allocator);
+ undefined4
+ spawnSingleGrass(int modelSubtype,u16 roomid,mVec3_c *groundHeight,
+ u16 yRotation,s32 specialItemDropId,int affectedByTimeshift,int activeInPresent,
+ s32 massObjSubtype,u8 lightingCode);
+ void addToFreeInstances(dTgMassObjInstance *param_2);
+ s32 addToRoom(u32 param2, s32 roomid, dTgMassObjInstance* param4);
+ void unloadRoom(u16 roomid);
+ void initResForModel(s32 param_2,nw4r::g3d::ResMat pResMat,nw4r::g3d::ResShp pResShp);
+ dTgMassObjTransform* aquireTransform();
+ void update();
+ void releaseTransform(dTgMassObjTransform*);
+ void setModelInfo(const GrassModelInfo* modelInfo, mHeapAllocator_c * heap) {
+ setModelInfo(
+ modelInfo->mRadius, modelInfo->field_0x0C,
+ modelInfo->field_0x12, 0x3F,
+ modelInfo->mInstanceCount, modelInfo->mStaticTransformCount,
+ modelInfo->mDynamicTransformCount, (f32)modelInfo->field_0x1A,
+ 0x7F, 0xFFFFFFFF, heap
+ );
+ }
+
+ void setRadius(f32 r) {
+ mRadius = r;
+ mRadiusSquared = r * r;
+ }
+
+ void setInstanceLength(s32 len) {
+ mInstanceListLength = len;
+ }
+
+ s32 getInstanceListLength() const {
+ return mInstanceListLength;
+ }
+
+ s32 getDynamicTransformListLength() const {
+ return mDynamicTransformationListLength;
+ }
+
+ dTgMassObjInstanceList& getFree() {
+ return mFreeInstances;
+ }
+
+ bool testSpecialItemtype(s32 itemtype) {
+ return itemtype == 10;
+ }
+
+ /* vt 0x10 */ virtual void remove() override;
+ // /* vt 0x14 */ virtual int entry();
+ /* vt 0x18 */ virtual void drawOpa() override {
+ draw();
+ }
+ /* vt 0x1C */ virtual void drawXlu() override {
+ draw();
+ }
+ void draw();
+
+ void calcCutCounter() {
+ mCutCounter = (mCutCounter != 0) ? mCutCounter-1 : mCutCounter;
+ }
+
+ dTgMassObjTransformList& GetUnk3Too() {
+ return mAquiredTransforms;
+ }
+
+ dTgMassObjTransformList& GetUnk3() {
+ return mAvailableTransforms;
+ }
+
+// private:
+ /* 0x18 */ GrassModelData* mpModelData;
+ /* 0x1C */ dTgMassObjInstance* mInstanceList;
+ /* 0x20 */ dTgMassObjTransform* mStaticTransformationList;
+ /* 0x24 */ dTgMassObjTransform* mDynamicTransformationList;
+ /* 0x28 */ dTgMassObjInstanceList mFreeInstances;
+ /* 0x34 */ dTgMassObjTransformList mAquiredTransforms;
+ /* 0x40 */ dTgMassObjTransformList mAvailableTransforms;
+ /* 0x4C */ f32 mRadius;
+ /* 0x50 */ f32 mRadiusSquared;
+ /* 0x54 */ f32 field_0x54;
+ /* 0x58 */ u16 field_0x58;
+ /* 0x5A */ u16 mRoomCount;
+ /* 0x5C */ u16 mInstanceListLength;
+ /* 0x5E */ u16 mStaticTransformationListLength;
+ /* 0x60 */ u16 mDynamicTransformationListLength;
+ /* 0x62 */ u8 field_0x62;
+ /* 0x63 */ u8 field_0x63;
+ /* 0x64 */ s16 mCutCounter;
+ /* 0x66 */ u8 field_0x66;
+ /* 0x67 */ u8 field_0x67;
+};
class dTgMassObj_c : public dTg_c {
public:
@@ -12,10 +260,63 @@ public:
return sInstance;
}
+ static u8 getCurrentStageGrassSubtype();
+
void unloadRoom(u16 roomid);
+ int initializeCircle(GrassModel *grassModel, mVec3_c *bbStart, mVec3_c *bbEnd,s32 xzDisplacement,mMtx_c *param_7,s32 specialItemDropId,
+ undefined4 affectedByTimeshift,u8 activeInPresent,int randInt,s32 massObjSubtype,
+ u8 lightingCode, f32 fParam);
+
+ int initializeBox(GrassModel *grassModel,mVec3_c *minVec,
+ mVec3_c *maxVec,int xzDisplacement,mMtx_c *boundsMtx,int specialItemDropId,undefined4 affectedByTimeshift,
+ u8 activeInPresent,int randInt,s32 massObjSubtype,u8 lightingCode, f32 fParam);
+
+ virtual int actorExecute() override;
+ virtual int actorCreate() override;
+ virtual int actorPostCreate() override;
+ virtual int draw() override;
+ virtual int doDelete() override;
+
+ mMtx_c getPreparedMtx() const {
+ mMtx_c mtx;
+ mtx.transS(mPosition);
+ mtx.YrotM(mRotation.y);
+ mtx.scaleM(mScale);
+ mMtx_c mtx2 = mtx;
+ mtx2.inverse();
+ return mtx2;
+ }
+
+ GrassModel* getGrassModel(s32 i) {
+ return sGrassModels[i];
+ }
+
+ void toGlobalRoom() {
+ s8 roomid = getRoomId();
+ addActorToRoom(-1);
+ mRoomID = roomid;
+ }
+
+ s32 getGrassTypeFromParams() {
+ return getFromParams(0, 7);
+ }
+
+ mMtx_c makeBoundsMtx() {
+ mMtx_c mtx;
+ mtx.transS(mPosition);
+ mtx.YrotM(mRotation.y);
+ mtx.scaleM(mScale);
+ return mtx;
+ }
+
private:
static dTgMassObj_c *sInstance;
+ static mHeapAllocator_c *sAllocator;
+ static GrassModel *sGrassModels[5];
+
+ /* 0x0FC */ s32 mMassSubtype;
+ /* 0x100 */ u8 field_0xFC[0x104 - 0x100];
};
#endif
diff --git a/include/egg/math/eggQuat.h b/include/egg/math/eggQuat.h
index a752b229..96808c56 100644
--- a/include/egg/math/eggQuat.h
+++ b/include/egg/math/eggQuat.h
@@ -44,10 +44,26 @@ struct Quatf {
Vector3f rotateVector(const Vector3f &);
Vector3f rotateVectorInv(const Vector3f &); // not in SS
void slerpTo(const Quatf &, f32, Quatf &out) const;
+ void slerpTo2(f32 fparam,const Quatf & param1, Quatf &out ) const {
+ const Quatf & tmp = param1;
+ slerpTo(tmp, fparam, out);
+ }
void limitSlerpTo(const Quatf &, f32, f32, Quatf &out) const;
void makeVectorRotationLimit(Vector3f &, Vector3f &, f32); // not in SS
void makeVectorRotation(const Vector3f &, const Vector3f &);
+ // not sure about the name
+ f32 dot(const Quatf& other) const {
+ f32 ret = v.dot(other.v);
+ ret += w * other.w;
+ return ret;
+ }
+
+ void set(const Quatf& other) {
+ v.set(other.v);
+ w = other.w;
+ }
+
void multScalar(f32 s) {
w *= s;
v.x *= s;
@@ -58,6 +74,12 @@ struct Quatf {
set(1.0f, 0.0f, 0.0f, 0.0f);
}
+ void makeWPositive() {
+ if (w < 0.f) {
+ multScalar(-1.f);
+ }
+ }
+
Vector3f v;
f32 w;
};
diff --git a/include/egg/math/eggVector.h b/include/egg/math/eggVector.h
index abcf1176..8886a45a 100644
--- a/include/egg/math/eggVector.h
+++ b/include/egg/math/eggVector.h
@@ -1,11 +1,14 @@
#ifndef EGG_VECTOR_H
#define EGG_VECTOR_H
+#include "JSystem/JMath/JMath.h"
#include "common.h"
#include "egg/math/eggMath.h"
+#include "nw4r/math/math_arithmetic.h"
#include "nw4r/math/math_triangular.h"
#include "nw4r/math.h"
+#include <cmath>
namespace EGG {
@@ -132,6 +135,10 @@ struct Vector3f : public nw4r::math::VEC3 {
return squaredLength() <= Math<f32>::epsilon();
}
+ bool isZero2() const {
+ return fabsf(JMathInlineVEC::C_VECSquareMag(*this)) <= Math<f32>::epsilon();
+ }
+
void setZero() {
x = y = z = 0.0f;
}
diff --git a/include/m/m_mtx.h b/include/m/m_mtx.h
index ac131169..3cdb56bb 100644
--- a/include/m/m_mtx.h
+++ b/include/m/m_mtx.h
@@ -87,6 +87,11 @@ public:
void inverse() {
MTXInverse(*this, *this);
}
+ mMtx_c copyInverse() {
+ mMtx_c ret = *this;
+ ret.inverse();
+ return ret;
+ }
void multVecZero(nw4r::math::VEC3 &out) const; ///< Converts the matrix to a vector.
void zero(); ///< Zeroes out the matrix.
@@ -142,6 +147,11 @@ public:
MTXMultVec(*this, ret, ret);
return ret;
}
+ mVec3_c multVec2(const mVec3_c &v) const {
+ mVec3_c ret;
+ MTXMultVec(*this, v, ret);
+ return ret;
+ }
void multVecSR(const mVec3_c &in, mVec3_c &out) const {
MTXMultVecSR(*this, in, out);
diff --git a/include/nw4r/ut/ut_Color.h b/include/nw4r/ut/ut_Color.h
index e94ba43a..95ecc926 100644
--- a/include/nw4r/ut/ut_Color.h
+++ b/include/nw4r/ut/ut_Color.h
@@ -2,6 +2,7 @@
#define NW4R_UT_COLOR_H
#include "nw4r/types_nw4r.h"
+#include "rvl/GX/GXTypes.h"
#include "rvl/GX.h" // IWYU pragma: export
namespace nw4r {
@@ -61,6 +62,16 @@ public:
operator u32() const {
return ToU32ref();
}
+
+ operator GXColorS10() const {
+ GXColorS10 c;
+ c.r = r;
+ c.g = g;
+ c.b = b;
+ c.a = a;
+ return c;
+ }
+
// clang-format off
static const u32 RED = 0xFF0000FF;
static const u32 GREEN = 0x00FF00FF;