Java a 3D grafika – graf scény

19. listopadu 2003

V předchozím článku jsme si ukázali, jak se s pomocí Java 3D API vytváří jednoduchá scéna. V tomto článku se podíváme podrobněji na problematiku grafu scény. Ukážeme si, jak jej zakreslovat a seznámíme se s bázovými třídami většiny objektů objevujících se ve scéně.

Jak zakreslovat graf scény

Jak známo, dokáže občas jeden obrázek osvětlit určitou problematiku daleko lépe než několik desítek řádků textu a v případě grafu scény to platí dvojnásob. Jen si zkuste představit, jak slovy popisujete následující jednoduchý graf:

jednoduchý graf scény

A to obsahuje pouze tři barevné krychle (ColorCube), z nichž dvě jsou transformovány jedním společným objektem TransformGroup, z čehož jedna je transformována ještě dalším objektem TransformGroup, a třetí je transformována jinými dvěma objekty TransformGroup.

A teď popravdě. Čemu jste rozuměli lépe? Obrázku, přestože jste v něm neznali nejméně polovinu použitých symbolů, nebo předcházejícímu zmatenému textu? A co teprve, kdybyste v obrázku význam všech symbolů znali? Myslím si, že obrázek vyhrává.

Nyní si pojďme vysvětlit význam použitých symbolů. Následující obrázek je všechny shrnuje. (Tento způsob zápisu je převzat z oficiálního tutoriálu.)

používané symboly

Se třídami VirtualUniverse a Locale jsme se seznámili již minule. Teď je na čase vysvětlit si, k čemu slouží ty zbývající. Instance tříd odvozených od první z nich, třídy Group, mají v grafu, řečeno slovy klasické terminologie pro popis stromů, roli uzlů, tzn. mohou mít pouze jednoho rodiče a neomezeně potomků.

Třída Leaf je nadtřídou pro třídy, jejichž instance jsou v daném stromu listy, což znamená, že už nemají žádné další potomky. Příkladem může být třída ColorCube, se kterou jsme se seznámili v minulém článku a se kterou pracujeme i dnes.

Možná vás nyní napadá otázka, proč jsou v ukázkovém grafu objekty ColorCube zobrazeny jako obdélníky a ne jako trojúhelníky, což by podle výše uvedeného bylo jistě správnější. Inu, tady zase jednou zvítězil pragmatismus nad přesným vyjadřováním. On totiž trojúhelník není právě nejvhodnější pro zápis delšího textu. Z toho důvodu, pokud nás bude v budoucnu zajímat u nějakého objektu v grafu jeho přesný název, zobrazíme jej jako obdélník.

Předposlední ze symbolů v tabulce, ovál, označuje instance tříd, které jsou odvozeny od třídy NodeComponent. Podtřídami třídy NodeComponent jsou například třídy Appearance a Geometry, se kterými se blíže seznámíme v některém z dalších článků této série. Prozatím snad postačí, když si řekneme, že třídy odvozené od této třídy nejsou formálně přímo součástí stromu, protože na ně odkazují objekty listů (Leaf), což by jinak nebylo možné (list přece nemůže mít potomky), nejedná se tedy formálně o vztah potomek-rodič.

Žijící a zkompilované objekty

Vložíme-li objekt BranchGroup do objektu Locale, stanou se tento objekt a objekty v něm obsažené takzvaně živými. To znamená, že kvůli výkonu bude graf scény převeden do výhodnější podoby, což s sebou nevýhodu v tom, že již nebudeme moci nadále přidávat objekty do objektů Group nebo měnit transformační matici v objektech TransformGroup.

Pochopitelně jsou však situace, kdy scénu chceme měnit i nadále – ať už potřebujeme animovat existující objekty nebo přidávat nové. Pak musíme nějak označit ty části scény, které se budou měnit. K tomu slouží metody void setCapability(int bit), void clearCapability(int bit) a boolean getCapability(int bit). Parametr bit typu int, předávaný všem těmto metodám, nese bitový příznak určující, co je s daným objektem (respektive jeho potomky), možné dělat. Metoda setCapability(int bit) „zapíná“ tento příznak, metoda clearCapability(int bit) tento příznak vypíná a konečně metoda getCapability(int bit) vrací true, pokud je daný příznak nastavený.

Například ve třídě Group jsou definovány mimo jiné následující příznaky: ALLOW_CHILDREN_EXTEND dovolující přidávat další objekty do daného objektu Group, ALLOW_CHILDREN_READ dovolující přistupovat k potomkům daného objektu a ALLOW_CHILDREN_WRITE umožňující upravovat odkazy na potomky tohoto objektu. Ve třídě TransformGroup jsou pak definovány další příznaky: ALLOW_TRANSFORM_READ dovolující přistupovat k transformační matici a potažmo i jejím jednotlivým složkám (rotace, posunutí) a ALLOW_TRANSFORM_WRITE umožňující měnit transformaci, což je nezbytné například pro animování. Kromě již zmíněných příznaků však existují i další, s nimiž se setkáme v některém z dalších článků.

Sice je možné renderovat i scénu, která je pouze žijící, ale pokud chcete větší rychlost, je potřeba objekty BranchGroup ve scéně ještě navíc „zkompilovat“. K tomu slouží metoda compile(), jejíž volání způsobí další změny v interní reprezentaci scény. Její používání je navýsost užitečné a neexistuje prakticky žádný důvod, proč ji ignorovat.

Malý příklad

Nyní si na krátkém příkladu ukážeme použití toho, co jsme se dnes naučili. Výsledek nebude nijak oslňující, půjde o další variaci na téma barevné krychle. Nejprve vytvoříme scénu s barevnou krychlí, jejímž rodičem bude objekt TransformGroup, pak ji zkompilujeme a teprve až potom ji budeme transformovat úpravou objektu TransformGroup, což znamená, že se nevyhneme použití metody setCapability(int bit), což je v podstatě hlavním účelem tohoto příkladu. Celou scénu si pro procvičení rovněž zakreslíme.

graf scény příkladu

Zde následuje zdrojový kód příkladu:

package interval.j3d;
import java.awt.*;
import javax.swing.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class Capabilities extends JFrame{
   TransformGroup tg;
   public Capabilities() {
      super(„Ukázka použití setCapability()“);
      //Na následujících řádcích vytvoříme Canvas3D
      Container pane = getContentPane();
      pane.setLayout(new BorderLayout());
      GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
      Canvas3D canvas = new Canvas3D(config, false);
      pane.add(„Center“, canvas);
      //vytvoříme objekt SimpleUniverse
      SimpleUniverse universe = new SimpleUniverse(canvas);
      //nastavíme vzdálenost pozorovatele od monitoru na 2m
       universe.getViewingPlatform().setNominalViewingTransform();
      //přidáme BranchGroup vytvořený v metodě vytvorBranchGroup()
      //do objektu SimpleUniverse
      universe.addBranchGraph(vytvorBranchGroup());
      //upravíme transformaci v objektu TransformGroup tg,
      //který je již součástí grafu scény, což by nebylo možné,
      //kdybychom v metodě vytvorBranchGroup nevolali
      //metodu setCapability s parametrem ALLOW_TRANSFORM_GROUP
      tg.setTransform(vytvorTransform());
      addWindowListener(new java.awt.event.WindowAdapter() {
      &nbsppublic void windowClosing(java.awt.event.WindowEvent evt) {
        System.exit(0);
        }
      });
   }
   protected BranchGroup vytvorBranchGroup(){
      //kořen této části scény
      BranchGroup bg = new BranchGroup();
      //tento objekt upravujeme až dále v konstruktoru
      tg = new TransformGroup();
      //nastavíme, že chceme nadále měnit transformační matici
      //tohoto objektu
      tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
      //přidáme objekt TransformGroup tg jako potomka
      //do objektu bg
      bg.addChild(tg);
      //vložíme ještě barevnou krychli jako potomka zmíněného
      //objektu TransformGroup tg
      tg.addChild(new ColorCube(0.3));
      //zkompilujeme scénu
      bg.compile();
      return bg;
   }
   protected Transform3D vytvorTransform(){
      Transform3D tr1 = new Transform3D();
      //nastavíme úhel otočení na 30°
      tr1.rotX(Math.PI/6);
      //vytvoříme druhou transformační matici
      Transform3D tr2 = new Transform3D();
      //nastavíme úhel otočení na 60°
      tr2.rotY(-Math.PI/3);
      //vynásobíme spolu matice
      tr2.mul(tr1);
      return tr2;
   }
   public static void main(String[] args){
      new Capabilities().show();
  &nbsp}
}

Na tomto příkladu sice není užitečnost popsaných technik příliš zřetelná, až se ale budeme zabývat animováním objektů, neobejdeme se bez nich. Následující obrázek je výsledkem použití předcházejícího kódu:

výsledek příkladu

Příště se seznámíme s třídami pro tvorbu geometrických primitiv a ukážeme si, jak upravovat jejich vzhled (barvu a další vlastnosti) pomocí objektů Appearance.

Předchozí článek Tvorba RSS v PHP
Další článek fotbal
Š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 *