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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
042bddf195
commit
c7bcb9bbbe
@@ -0,0 +1,131 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user