Jak określić obiekt / powierzchnię wskazaną przez użytkownika za pomocą lwjgl?

9

Tytuł mówi prawie wszystko. Pracuję nad prostym projektem „przyzwyczajajmy się do lwjgl”, polegającym na manipulowaniu kostką rubika, i nie mogę wymyślić, jak powiedzieć, na którą stronę / kwadrat wskazuje użytkownik.

Flynn1179
źródło
Uwaga: Wiele silników AFAIK robi to całkowicie na procesorze, niezależnie od renderowania i bez użycia OpenGL, ponieważ jest szybszy (ale bardziej skomplikowany).
user253751,

Odpowiedzi:

8

Będziesz chciał skorzystać z wyboru 3D. Oto kod, którego używam w mojej grze.

Najpierw rzuciłem promień z aparatu. Używam myszy, ale jeśli używasz tylko tego, na co patrzy użytkownik, możesz po prostu użyć środka okna. Oto kod z mojej klasy kamer:

public Ray GetPickRay() {
    int mouseX = Mouse.getX();
    int mouseY = WORLD.Byte56Game.getHeight() - Mouse.getY();

    float windowWidth = WORLD.Byte56Game.getWidth();
    float windowHeight = WORLD.Byte56Game.getHeight();

    //get the mouse position in screenSpace coords
    double screenSpaceX = ((float) mouseX / (windowWidth / 2) - 1.0f) * aspectRatio;
    double screenSpaceY = (1.0f - (float) mouseY / (windowHeight / 2));

    double viewRatio = Math.tan(((float) Math.PI / (180.f/ViewAngle) / 2.00f)) * zoomFactor;

    screenSpaceX = screenSpaceX * viewRatio;
    screenSpaceY = screenSpaceY * viewRatio;

    //Find the far and near camera spaces
    Vector4f cameraSpaceNear = new Vector4f((float) (screenSpaceX * NearPlane), (float) (screenSpaceY * NearPlane), (float) (-NearPlane), 1);
    Vector4f cameraSpaceFar = new Vector4f((float) (screenSpaceX * FarPlane), (float) (screenSpaceY * FarPlane), (float) (-FarPlane), 1);


    //Unproject the 2D window into 3D to see where in 3D we're actually clicking
    Matrix4f tmpView = Matrix4f(view);
    Matrix4f invView = (Matrix4f) tmpView.invert();
    Vector4f worldSpaceNear = new Vector4f();
    Matrix4f.transform(invView, cameraSpaceNear, worldSpaceNear);

    Vector4f worldSpaceFar = new Vector4f();

    Matrix4f.transform(invView, cameraSpaceFar, worldSpaceFar);

    //calculate the ray position and direction
    Vector3f rayPosition = new Vector3f(worldSpaceNear.x, worldSpaceNear.y, worldSpaceNear.z);
    Vector3f rayDirection = new Vector3f(worldSpaceFar.x - worldSpaceNear.x, worldSpaceFar.y - worldSpaceNear.y, worldSpaceFar.z - worldSpaceNear.z);

    rayDirection.normalise();

    return new Ray(rayPosition, rayDirection);
}

Następnie podążam za promieniem, dopóki nie przecina się on z obiektem, możesz to zrobić za pomocą obwiedni lub czegoś podobnego, ponieważ jest to specyficzne dla twojej gry, pozwolę ci sobie z tym poradzić. Zasadniczo odbywa się to przez podążanie za promieniem (dodawanie kierunku promienia do jego punktu początkowego w kółko, dopóki nie wpadniesz na coś).

Następnie chcesz zobaczyć, która twarz jest wybierana, możesz to zrobić, iterując po trójkątach w sześcianie, aby sprawdzić, czy promień ich przecina. Następująca funkcja to robi i zwraca odległość do wybranej twarzy, a następnie po prostu używam przecinającej się twarzy, która jest najbliżej aparatu (więc nie wybierasz tylnej twarzy).

public static float RayIntersectsTriangle(Ray R, Vector3f vertex1, Vector3f vertex2, Vector3f vertex3) {
    // Compute vectors along two edges of the triangle.
    Vector3f edge1 = null, edge2 = null;

    edge1 = Vector3f.sub(vertex2, vertex1, edge1);
    edge2 = Vector3f.sub(vertex3, vertex1, edge2);

    // Compute the determinant.
    Vector3f directionCrossEdge2 = null;
    directionCrossEdge2 = Vector3f.cross(R.Direction, edge2, directionCrossEdge2);


    float determinant = Vector3f.dot(directionCrossEdge2, edge1);
    // If the ray and triangle are parallel, there is no collision.
    if (determinant > -.0000001f && determinant < .0000001f) {
        return Float.MAX_VALUE;
    }

    float inverseDeterminant = 1.0f / determinant;

    // Calculate the U parameter of the intersection point.
    Vector3f distanceVector = null;
    distanceVector = Vector3f.sub(R.Position, vertex1, distanceVector);


    float triangleU = Vector3f.dot(directionCrossEdge2, distanceVector);
    triangleU *= inverseDeterminant;

    // Make sure the U is inside the triangle.
    if (triangleU < 0 || triangleU > 1) {
        return Float.MAX_VALUE;
    }

    // Calculate the V parameter of the intersection point.
    Vector3f distanceCrossEdge1 = null;
    distanceCrossEdge1 = Vector3f.cross(distanceVector, edge1, distanceCrossEdge1);


    float triangleV = Vector3f.dot(R.Direction, distanceCrossEdge1);
    triangleV *= inverseDeterminant;

    // Make sure the V is inside the triangle.
    if (triangleV < 0 || triangleU + triangleV > 1) {
        return Float.MAX_VALUE;
    }

    // Get the distance to the face from our ray origin
    float rayDistance = Vector3f.dot(distanceCrossEdge1, edge2);
    rayDistance *= inverseDeterminant;


    // Is the triangle behind us?
    if (rayDistance < 0) {
        rayDistance *= -1;
        return Float.MAX_VALUE;
    }
    return rayDistance;
}

Trójkąt o najmniejszej odległości to wybrany trójkąt. Ponadto, bezwstydna wtyczka do mojej gry, powinieneś ją sprawdzić, śledź ją i głosuj w ankietach, które od czasu do czasu wygłaszam. Dzięki! http://byte56.com

MichaelHouse
źródło
3

Technika, której szukasz, nazywa się „kompletowaniem” lub „kompletowaniem 3D”. Można to zrobić na kilka sposobów; jednym z bardziej powszechnych jest przekształcenie punktu 2D na ekranie w przestrzeń oka za pomocą odwrotnej transformacji projekcji. Umożliwi to wygenerowanie promienia w przestrzeni widoku, którego można użyć do przetestowania kolizji z fizyczną reprezentacją różnych bitów geometrii sceny w celu ustalenia, który obiekt „trafił” użytkownik.

Możesz także użyć „bufora pobierania” (lub „buforu wyboru”), który obsługuje GL. Zasadniczo wymaga to zapisania unikalnego identyfikatora obiektu w buforze dla każdego piksela, a następnie po prostu przetestowania tego bufora.

Często zadawane pytania dotyczące OpenGL dotyczą obu tych zagadnień (bardziej skupia się na buforze wyboru, ponieważ jest to całkowicie funkcja GL; wybieranie promieniowe jest niezależne od API, z wyjątkiem być może wyodrębnienia aktywnych macierzy z potoku). Oto bardziej konkretny przykład techniki zbierania promieni (na iOS, ale powinna być wystarczająco łatwa do przetłumaczenia). Ta strona zawiera kod źródłowy niektórych przykładów OpenGL Red Book przeniesionych do LWJGL, w tym demo kompletacji.

Zobacz także to pytanie dotyczące SO.

Społeczność
źródło
Należy również pamiętać, że interfejs API do pobierania OpenGL jest przestarzały w GL3 Core. Nadal jest dostępny w pełnym profilu.
nieważne
Heh, wiedza o tym, który termin wyszukać, robi ogromną różnicę :) Jednak właśnie szukałem w Google hasła „przykład wyboru lwjgl”, a ten wątek był jednym z najlepszych hitów!
Flynn1179