Jeżeli szukasz odpowiedzi na pytanie jak rzutować współrzędne 3D na 2D to trafiłeś w dobre miejsce. W tym wpisie rozwijam ten problem w oparciu o użycie metody calculateScreenCoords dostępnej we wszystkich obiektach dziedziczących po DisplayObject3D.
FLEX: 3.0, Papervision3D: 2.0.883
Zaczynamy od utworzenia klasy dziedziczącej po BasicView. Jeżeli ktoś nie ma doświadczenia z Papervision3D to polecam najpierw zapoznać się z wpisem: Pierwsze kroki z Papervision3D. Do klasy dodajemy trzy pola:
private var vertexPlane:Plane;
private var vertices2dSprite:Sprite;
Zmienna plane posłuży jako obiekt, którego współrzędne wierzchołków 3D będziemy rzutować na 2D. Zmienna vertexPlane jest niezbędna do rzutowania wspomnianych wierzchołków (ale o tym zaraz), natomiast vertices2dSprite to zwykły sprite, na którym będziemy wizualizować rzutowane wierzchołki.
Dodajmy teraz do naszej klasy metodę init3D(), w której wstawiamy na scenę wszystkie niezbędne w tym przykładzie obiekty 3D:
var wireframeMaterial: WireframeMaterial = new WireframeMaterial(0x000000);
var colorMaterial:ColorMaterial = new ColorMaterial(0xffffff);
var planeMaterial:CompositeMaterial = new CompositeMaterial;
planeMaterial.addMaterial(colorMaterial);
planeMaterial.addMaterial(wireframeMaterial);
planeMaterial.doubleSided = true;
plane = new Plane(planeMaterial,250,250);
scene.addChild(plane);
vertexPlane = new Plane(null,1,1);
scene.addChild(vertexPlane);
}
Oraz metodę init2D dodającą do aplikacji sprite, na którym odrysujemy rzutowane wierzchołki:
vertices2dSprite = new Sprite;
addChild(vertices2dSprite);
}
Przejdźmy teraz do sedna sprawy a mianowicie do obliczania współrzędnych 2D i ich odrysowywania.
Pierwszą kwestią zajmie się nasza metoda o nazwie getPoint2D.
singleRender();
do3d.calculateScreenCoords(camera);
return new Number2D(do3d.screen.x + viewport.viewportWidth*0.5, do3d.screen.y + viewport.viewportHeight*0.5);
}
Metoda getPoint2D przyjmuje jako parametr dowolny obiekt typu DisplayObject3D i zwraca jego zrzutowane na 2D współrzędne. W tym celu na rzutowanym obiekcie wywołuje funkcję calculateScreenCoords, która jako parametr przyjmuje obiekt kamery względem, którego ma odbyć się rzutowanie. Wywołanie funkcji calculateScreenCoords powoduje ustawienie wartości dla pola screen rzutowanego obiektu. Pole screen zaś to nic innego jak struktura, która przechowuje pożądane współrzędne ekranowe (2D). Wiążą się z tym dwa szczegóły, na które trzeba zwrócić uwagę.
Pierwszy to taki, że żeby pole screen mogło zostać ustawione to obiekt dla którego wywoływana jest funkcja calculateScreenCoords musi być wyrenderowany (czyli też dodany do sceny przyp.). Dlatego też na samym początku omawianej metody wywołujemy funkcję singleRender klasy BasicView.
Druga kwestia to konieczność zwiększenia składowych pola screen odpowiednio o wartość połowy wysokości i szerokości viewportu. Dzieje się tak ponieważ współrzędne w polu screen liczone są od środka viewportu.
Teraz pozostało nam tylko odrysować to co umiemy już liczyć. Posłuży do tego nasza kolejna metoda:
var vertex2D:Number2D;
vertices2dSprite.graphics.beginFill(0xffff00);
vertices2dSprite.graphics.lineStyle(1,0x000000);
plane.geometry.transformVertices(plane.transform);
for (var i:Number=0; i<plane.geometry.vertices.length; i++) {
vertexPlane.x = plane.geometry.vertices[i].x;
vertexPlane.y = plane.geometry.vertices[i].y;
vertexPlane.z = plane.geometry.vertices[i].z;
vertex2D = getPoint2D(vertexPlane);
vertices2dSprite.graphics.drawCircle(vertex2D.x,vertex2D.y,7);
}
plane.geometry.transformVertices(Matrix3D.inverse(plane.transform));
vertices2dSprite.graphics.beginFill(0xff0000);
vertex2D = getPoint2D(plane);
vertices2dSprite.graphics.drawCircle(vertex2D.x,vertex2D.y,7);
vertices2dSprite.graphics.endFill();
}
Warto tu zwrócić uwagę na to co się dzieje w miejscu wystąpienia pętli for. Ponieważ wierzchołki płaszczyzny nie dziedziczą po klasie DisplayObject3D dlatego nie ma możliwości ich bezpośredniego zrzutowania na 2D. Żeby to obejść korzystamy z dodatkowej, stworzonej w metodzie init3D płaszczyzny vertexPlane o wymiarach 1×1. W pętli for iterujemy po wszystkich wierzchołkach ustawiając położenie vertexPlane zgodnie z aktualnie przetwarzanym a następnie rzutujemy współrzędne vertexPlane na 2D.
Żeby uzyskać poprawne odwzorowanie położenia rzutowanych wierzchołków przed pętlą musimy zastosować na nich macierz przekształceń obiektu do którego należą. W przeciwnym razie nie uwzględnione zostaną zmiany współrzędnych wierzchołków wynikające ze skalowania, obracania i przesunięcia plane’a. Ponieważ macierz transformacji odnosi się do bazowych wartości współrzędnych wierzchołków dlatego po wyjściu z pętli przywracamy je do stanu sprzed przekształcenia. W tym celu stosujemy na nich odwrotność macierzy transformacji.
Oprócz wierzchołków odrysowujemy też to co zwraca metoda getPoint2D dla plane’a (czerwona kropka). Dzięki temu widzimy, że po obliczeniu wartości pola screen zawiera ono współrzędne środka rzutowanego obiektu.