Java a 3D grafika – Geometry (PointArray, LineArray a TriangleArray)

7. května 2004

V tomto článku se začneme zabývat vytvářením objektů prostřednictvím instancí třídy Shape3D. Ukážeme si, jak pomocí úseček vytvořit drátový model krychle, a pak si povíme, jak se dají pomocí trojúhelníků vytvářet tělesa se stěnami.

Než se pustíte do čtení tohoto článku, doporučuji vám oživit si povědomí o tom, jaké objekty se objevují v grafu scény, jaké podmínky musí graf scény splňovat a k čemu jsou dobré objekty Appearance, prostřednictvím článku Java a 3D grafika – graf scény.

Pro vytváření trojrozměrných objektů se používají objekty Shape3D, které mohou odkazovat na instance podtříd abstraktní třídy Geometry a na objekty Appearance. (Jeden objekt Shape3D může odkazovat i na několik objektů Geometry, ale ty musí být stejného typu.) Tyto objekty se jim přiřazují buď přímo v konstruktoru Shape3D(Geometry geometry, Appearance appearance), nebo pomocí metod setGeometry(Geometry geometry) a setAppearance(Appearance appearance).

Objekty Geometry obsahují vlastní data objektů, tedy souřadnice vrcholů, normálové vektory, barvy vrcholů a souřadnice sloužící k otexturování. V objektech Appearance pak jsou uloženy informace o materiálu, šířce čar, tloušťce bodů, o tom, zda se má nebo nemá použít antialiasing a tak dále. Podrobněji jsme se ale třídě Appearance věnovali v článku o grafických primitivech, takže na tomto místě snad postačí jen tato krátká zmínka.

Třída GeometryArray a její potomci

Vzhledem k tomu, že tříd odvozených od třídy Geometry je poměrně dost, rozložíme si povídání o nich do několika článků. V tomto článku začneme několika potomky třídy GeometryArray, konkrétně třídami PointArray, LineArray a TriangleArray.

Jednodušší verze konstruktorů všech čtyř zmíněných tříd přebírají pouze dva parametry – počet vrcholů a jejich „formát“, tedy bitové příznaky říkající, co přesně bude v daném objektu Geometry uloženo. Příznaky definované ve třídě GeometryArray jsou COORDINATES pro souřadnice, NORMALS pro normálové vektory, COLOR_3 pro barvu bez alfa-transparence, COLOR_4 pro barvu s alfa-transparencí, TEXTURE_COORDINATES_2, TEXTURE_COORDINATES_3 a TEXTURE_COORDINATES_4 pro souřadnice textur. Tak například budeme-li chtít vytvořit instanci třídy PointArray obsahující čtyři barevné body, bude volání konstruktoru vypadat takto: PointArray(4, GeometryArray.COORDINATES | GeometryArray.COLOR_3);.

Máme-li už objekt GeometryArray vytvořený, musíme do něho vložit potřebná data. Následují metody sloužící pro nastavení souřadnic vrcholů:

  • public void setCoordinates(int index, float[] coordinates)
  • public void setCoordinates(int index, double[] coordinates)
  • public void setCoordinates(int index, Point3f[] coordinates)
  • public void setCoordinates(int index, Point3d[] coordinates)

V poli „coordinates“ jsou uloženy souřadnice vrcholů. Parametr „index“ označuje pozici v poli „coordinates“, kde se souřadnice nacházejí. Metody pro nastavení barev, normálových vektorů a souřadnic textur jsou velice podobné a postupně se k nim dostaneme alespoň v příkladech.

Třída LineArray

Nyní už toho víme dost na to, abychom mohli s využitím třídy LineArray, která je určena k vytváření těles z úseček, vytvořit jednoduchou třídu odvozenou od třídy Shape3D sloužící k vytváření drátových krychlí. Její kód následuje:

package interval.j3d;
import javax.media.j3d.*;
public class DratovaKrychle extends Shape3D {
 float souradnice[] =
//souřadnice bodů, vždy dva body pro úsečku
   {-1,-1,1, -1,1,1, -1,1,1, 1,1,1,
   1,1,1, 1,-1,1, 1,-1,1, -1,-1,1,
   -1,-1,-1, -1,1,-1, -1,1,-1, 1,1,-1,
   1,1,-1, 1,-1,-1, 1,-1,-1, -1,-1,-1,
   -1,-1,1, -1,-1,-1, -1,1,1, -1,1,-1,
   1,1,1, 1,1,-1, 1,-1,1, 1,-1,-1};
 public DratovaKrychle(float hrana) {
//nastavíme velikost hran
  for (int i = 0; i < 72; i++)
   souradnice[i] *= hrana/2f;
  setGeometry(vytvorGeometry());
  setAppearance(vytvorAppearance());
 }
 Geometry vytvorGeometry(){
//vytvoříme instanci třídy LineArray
  LineArray lineArray = new LineArray(24, GeometryArray.COORDINATES);
//nastavíme souřadnice
  lineArray.setCoordinates(0, souradnice);
  return lineArray;
 }
 Appearance vytvorAppearance(){
  Appearance app = new Appearance();
//nastavíme tloušťku čáry na 3 pixely, povolíme antialiasing
  LineAttributes la = new LineAttributes(3, LineAttributes.PATTERN_SOLID, true);
  app.setLineAttributes(la);
  return app;
 }
}

Jak můžete sami vidět, je kód této třídy velice jednoduchý. Pouze v konstruktoru vytváří objekt Geometry, do kterého vloží souřadnice zadané v poli „souradnice“, které ještě předtím vynásobí parametrem „hrana/2“, abychom mohli vytvářet krychle různých velikostí. Toto je výsledek:

Jednoduchá drátová krychle

Použití této třídy a také dalších tříd v tomto článku, kromě třídy PointArray, je mnohdy dost neúsporné z hlediska nároků na paměť – například souřadnice každého z vrcholů krychle v příkladu ukládáme třikrát. Řešením mohou být podtřídy, buď třídy GeometryStripArray nebo třídy IndexedGeometryArray. O tom ale až jindy.

Třída PointArray

Třída PointArray nám umožňuje zobrazovat jednotlivé vrcholy. Ve spojení s ní se hodí využít možnosti nastavení tloušťky bodů prostřednictvím třídy PointAttributes, jejíž instance se připojují k příslušným objektům Appearance. Konstruktor PointAttributes(float pointSize, boolean antialiasing) přebírá dva parametry. Parametr „pointSize“ určuje tloušťku bodů a parametr „antialiasing“ určuje, jestli se má použít antialiasing.

Třídu PointArray využijeme k vylepšení předchozího příkladu tím, že do každého vrcholu krychle nakreslíme bod zadané tloušťky. Naše nová třída však nebude rozšiřovat třídu Shape3D, ale bude odvozena od třídy Group (kvůli nemožnosti vkládat do objektů Shape3D objekty Geometry různého typu, v tomto případě tedy instance tříd LineArray a PointArray).

package interval.j3d;
import javax.media.j3d.*;
public class DratovaKrychle2 extends Group {
 float souradnice[] =
   {1,1,1, -1,1,1, 1,-1,1, 1,1,-1,
   -1,-1,1, -1,1,-1, 1,-1,-1, -1,-1,-1};
 public DratovaKrychle2(float hrana) {
  for (int i = 0; i < 24; i++)
   souradnice[i] *= hrana/2f;
  Shape3D shape2 = new Shape3D();
//objektu shape2 nastavíme objekt Geometry
  shape2.setGeometry(vytvorGeometryBodu());
//objektu shape2 nastavíme objekt Appearance
  shape2.setAppearance(vytvorAppearanceBodu());
//přidáme objekt DratovaKrychle
  addChild(new DratovaKrychle(hrana));
  addChild(shape2);
 }
 Geometry vytvorGeometryBodu(){
  PointArray pointArray = new PointArray(8, GeometryArray.COORDINATES);
  pointArray.setCoordinates(0, souradnice);
  return pointArray;
  }
 Appearance vytvorAppearanceBodu(){
  Appearance app = new Appearance();
  PointAttributes pa = new PointAttributes(10, true);
  app.setPointAttributes(pa);
  return app;
 }
}

A takto vypadá výsledek:

Drátová krychle se zvýrazněnými vrcholy

Pro lepší představu si ještě ukážeme, jak by mohla vypadat instance této třídy zakreslená v grafu scény, jak to můžete vidět na následujícím obrázku:

Drátová krychle - graf scény

Třída TriangleArray

Třída TriangleArray slouží, jak už její název napovídá, k vytváření těles z trojúhelníků. Její využití si ukážeme na vytvoření čtyřstěnu. Abychom si také ukázali, jak pracovat s barvami přiřazenými jednotlivým vrcholům, oživíme stěny našeho čtyřstěnu náhodně vybranými barvami.

package interval.j3d;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.util.Random;
public class Ctyrsten extends Shape3D {
//vypočítáme si dvě odmocniny, které budeme potřebovat
 final static float s6 = (float)Math.sqrt(6);
 final static float s3 = (float)Math.sqrt(3);
//každá řádka odpovídá jednomu trojúhelníku
 float souradnice[] = {
  -0.5f,0,-s3/6f, 0.5f,0,-s3/6f, 0,0,s3/3f,
  0,0,s3/3f, 0,s6/3f,0, -0.5f,0,-s3/6f,
  0.5f,0,-s3/6f, 0,s6/3f,0, 0,0,s3/3f,
  -0.5f,0,-s3/6f, 0,s6/3f,0, 0.5f,0,-s3/6f};
 public Ctyrsten(float hrana) {
  for (int i = 0; i < 36; i++)
   souradnice[i] *= hrana;
  setGeometry(vytvorGeometry());
 }
 Geometry vytvorGeometry(){
  TriangleArray ta = new TriangleArray(12, GeometryArray.COORDINATES | GeometryArray.COLOR_3);
  ta.setCoordinates(0,souradnice);
//obarvíme stěny náhodně vybranými barvami
  Random rand = new Random();
  for (int i = 0; i < 4; i++){
   Color3f color = new Color3f(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
   for (int j = 0; j < 3; j++)
    ta.setColor(3*i+j, color);
   }
  return ta;
 }
}

Obarvení tohoto tělesa provádíme v metodě vytvorGeometry(), kde voláním metody setColor(int i, Color3f color) nastavujeme každým třem bodům určujícím stěnu jednu náhodně vybranou barvu. Barvy jsme ale klidně mohli nastavit odlišné pro každý vrchol – pak by se v jednotlivých stěnách postupně měnily.

Čtyřstěn

Objekty, které jsme v tomto článku vytvořili, mají jeden velký nedostatek – světla na ně nemají žádný účinek. Jak se s tímto nedostatkem vypořádat, o tom si povíme příště.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *