В играх со сложным пользовательским интерфейсом (большинство стратегий, ряд симуляторов типа Sims и т.д.) предоставляется возможность выделять трехмерные объекты, щелкая на на них курсором мыши. Предлагаю реализацию этого механизма:
Vector3f clickVector(
Camera camera,
int x,
int y,
int scrw,
int scrh,
float yfov)
{
Vector3f camPos = camera.getPosition();
Vector3f camDir = camera.getDirectionVector();
float aspect = cast(float)scrw / cast(float)scrh;
float xfov = fovXfromY(yfov, aspect);
float tfov1 = tan(yfov * PI / 360.0f);
float tfov2 = tan(xfov * PI / 360.0f);
Vector3f camUp = camera.getUpVector() * tfov1;
Vector3f camRight = camera.getRightVector() * tfov2;
float width = 1.0f - 2.0f * cast(float)(x) / cast(float)(scrw);
float height = 1.0f - 2.0f * cast(float)(y) / cast(float)(scrh);
float mx = camDir.x + camUp.x * height + camRight.x * width;
float my = camDir.y + camUp.y * height + camRight.y * width;
float mz = camDir.z + camUp.z * height + camRight.z * width;
return Vector3f(mx, my, mz);
}
T fovXfromY(T) (T yfov, T aspectRatio)
{
yfov = degtorad(yfov);
T xfov = 2.0 * atan(tan(yfov * 0.5) * aspectRatio);
return radtodeg(xfov);
}
При нажатии кнопки мыши делаем следующее:
Vector3f v = -clickVector(
camera, mouseX, mouseY,
windowWidth, windowHeight, 60);
v.normalize();
Vector3f rayStart = camera.getPosition;
Vector3f rayEnd = camera.getPosition + v * 1000.0f;
Ray r = Ray(rayStart, rayEnd);
Vector3f isecPoint;
float nearestDistance = float.max;
int pickIndex = -1;
foreach(i, obj; sceneObjects)
{
auto bs = obj.boundingSphere;
if (r.intersectSphere(bs, isecPoint))
{
float d = distance(isecPoint, rayStart);
if (d < nearestDistance)
{
nearestDistance = d;
pickIndex = i;
}
}
}
if (pickIndex >= 0)
{
writefln("You clicked sphere %s!", pickIndex);
}