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>
132 lines
4.9 KiB
C++
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;
|
|
}
|