Files
mtgodot-poc/tests/port/port_grp_math_test.cpp
T
shenleiandClaude Opus 5.5 c7bcb9bbbe port(EterLib): GrpMath/GrpColor/GrpColorInstance verbatim
port.grp_math pins the 40250 conventions: D3DXVec3Rotation applies the
inverse rotation, GetRotationFromMatrix returns negated Z*X*Y angles, and
CGraphicColor defaults to opaque white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:59:44 +09:00

132 lines
4.9 KiB
C++

// EterLib GrpMath / GrpColor / GrpColorInstance (40250 bodies, verbatim).
#include "EterLib/StdAfx.h"
#include "EterLib/GrpMath.h"
#include "EterLib/GrpColor.h"
#include "EterLib/GrpColorInstance.h"
#include "EterBase/Timer.h"
#include <cmath>
#include <cstdio>
namespace
{
int failures = 0;
void check(bool ok, const char * name)
{
if (!ok)
{
std::printf("FAIL %s\n", name);
++failures;
}
}
bool near(float a, float b, float eps = 1e-4f)
{
return std::fabs(a - b) <= eps;
}
}
int main()
{
// CrossProduct2D / IsInTriangle2D (terrain + collision picking): interior, outside, and on an edge,
// where the zero cross product falls back to the bounding-box test.
check(CrossProduct2D(1, 0, 0, 1) == 1.0f && CrossProduct2D(0, 1, 1, 0) == -1.0f, "CrossProduct2D");
check(IsInTriangle2D(0, 0, 4, 0, 0, 4, 1, 1), "IsInTriangle2D inside");
check(IsInTriangle2D(0, 0, 0, 4, 4, 0, 1, 1), "IsInTriangle2D inside, other winding");
check(!IsInTriangle2D(0, 0, 4, 0, 0, 4, 3, 3), "IsInTriangle2D outside");
check(IsInTriangle2D(0, 0, 4, 0, 0, 4, 2, 0), "IsInTriangle2D on edge");
check(!IsInTriangle2D(0, 0, 4, 0, 0, 4, 5, 0), "IsInTriangle2D on edge line, past the vertex");
// D3DXVec3Rotation: D3DXQuaternionMultiply(a, b) is b * a, so the two calls give conj(q) * v * q, the
// inverse rotation of the one D3DXMatrixRotationQuaternion(q) applies.
{
D3DXQUATERNION q;
D3DXVECTOR3 axis(0.3f, -0.5f, 0.8f);
D3DXVec3Normalize(&axis, &axis);
D3DXQuaternionRotationAxis(&q, &axis, 1.2f);
D3DXVECTOR3 v(1, 2, 3), out;
check(D3DXVec3Rotation(&out, &v, &q) == &out, "D3DXVec3Rotation returns out");
D3DXQUATERNION inverse;
D3DXQuaternionConjugate(&inverse, &q);
D3DXMATRIX m;
D3DXMatrixRotationQuaternion(&m, &inverse);
D3DXVECTOR3 expect;
D3DXVec3TransformNormal(&expect, &v, &m);
check(near(out.x, expect.x) && near(out.y, expect.y) && near(out.z, expect.z), "D3DXVec3Rotation = inverse rotation");
}
// GetRotationFromMatrix / GetPivotAndRotationFromMatrix read sin(x) from _32: on Z*X*Y they return the
// negated angles. ExtractMovement rebuilds X*Y*Z*T from those angles, not the source rotation (40250 has
// no caller of these helpers; the bodies are kept verbatim).
{
const float rx = 0.3f, ry = -0.7f, rz = 1.1f;
D3DXMATRIX mx, my, mz, mt;
D3DXMatrixRotationX(&mx, rx);
D3DXMatrixRotationY(&my, ry);
D3DXMatrixRotationZ(&mz, rz);
D3DXMatrixTranslation(&mt, 10, 20, 30);
D3DXMATRIX rot3 = mz * mx * my;
D3DXMATRIX m = rot3 * mt;
D3DXVECTOR3 rot;
GetRotationFromMatrix(&rot, &m);
check(near(rot.x, -rx) && near(rot.y, -ry) && near(rot.z, -rz), "GetRotationFromMatrix");
D3DXVECTOR3 pivot;
GetPivotAndRotationFromMatrix(&m, &pivot, &rot);
check(near(rot.x, -rx) && near(rot.y, -ry) && near(rot.z, -rz) && pivot == D3DXVECTOR3(10, 20, 30), "GetPivotAndRotationFromMatrix");
D3DXMATRIX extracted;
ExtractMovement(&extracted, &m);
D3DXMATRIX ix, iy, iz;
D3DXMatrixRotationX(&ix, -rx);
D3DXMatrixRotationY(&iy, -ry);
D3DXMatrixRotationZ(&iz, -rz);
D3DXMATRIX expect = ix * iy * iz * mt;
bool same = true;
for (int i = 0; i < 16; ++i)
same = same && near(((const float *) extracted)[i], ((const float *) expect)[i]);
check(same, "ExtractMovement = X*Y*Z*T of the negated angles");
D3DXMATRIX gimbal;
D3DXMatrixRotationX(&gimbal, D3DX_PI / 2);
GetRotationFromMatrix(&rot, &gimbal);
check(near(rot.x, -D3DX_PI / 2) && rot.z == 0.0f, "GetRotationFromMatrix gimbal lock");
}
// CGraphicColor: ARGB pack round trip (truncating), Blend.
{
CGraphicColor c(0x80FF4000);
check(c.GetPackValue() == 0x80FF4000, "CGraphicColor pack round trip");
CGraphicColor d;
check(d.GetPackValue() == 0xFFFFFFFF, "CGraphicColor default is opaque white");
CGraphicColor half;
half.Blend(0.5f, CGraphicColor(0.0f, 0.0f, 0.0f, 0.0f), CGraphicColor(1.0f, 1.0f, 1.0f, 1.0f));
check(half.GetPackValue() == 0x7F7F7F7F, "CGraphicColor Blend truncates");
CGraphicColor copy(c);
check(copy.GetPackValue() == c.GetPackValue(), "CGraphicColor copy");
}
// CGraphicColorInstance: SetColorReference sets all three; a finished blend lands on the target;
// a fresh long blend starts from the current colour.
{
CTimer timer;
CGraphicColorInstance inst;
inst.SetColorReference(CGraphicColor(0xFF000000));
inst.Update();
check(inst.GetCurrentColorReference().GetPackValue() == 0xFF000000, "SetColorReference");
inst.BlendColorReference(0, CGraphicColor(0xFFFFFFFF));
inst.Update();
check(inst.GetCurrentColorReference().GetPackValue() == 0xFFFFFFFF, "BlendColorReference zero time");
inst.BlendColorReference(1000000, CGraphicColor(0xFF000000));
inst.Update();
check(inst.GetCurrentColorReference().GetPackValue() == 0xFFFFFFFF, "long blend starts at current colour");
inst.Clear();
check(inst.GetCurrentColorReference().GetPackValue() == 0xFFFFFFFF, "CGraphicColorInstance Clear");
}
if (!failures)
std::printf("ok\n");
return failures ? 1 : 0;
}