background image

42

Programowanie

grafiki

www.sdjournal.org

 Software Developer’s Journal   6/2006

Java 3D i Python

J

ava  3D  powstała  przy  udziale  konsorcjum 

złożonego z firm: Intel, SGI, Apple oraz Sun 

w  połowie  lat  90-tych.  W  obecnym  czasie 

każdy z tych producentów posiadał już własne śro-

dowisko  graficzne  3D,  lecz  były  one  ograniczone 

a  sposób  obsługi  dosyć  złożony.  Zadaniem  kon-

sorcjum  było  stworzenie  wersji  dla  powszechne-

go odbiorcy takiego środowiska na platformę Java 

i tym samym rozszerzenie jej możliwości. W wyni-

ku  wspólnych  prac  konsorcjum  powstała  Java  3D. 

Od  2004  Java  3D  stała  się  oprogramowaniem  ty-

pu open source rozwijanym w dalszym ciągu przez 

Sun-a i rzesze developerów ochotników. W równym 

stopniu  wspierany  jest  zarówno  OpenGL  jak  i  Di-

rectX.

Nie należy mylić Javy 3D z mobilną platformą 

Sun’a o skrótowej nazwie M3G dla J2ME do roz-

wijania  trójwymiarowych  aplikacji  graficznych  na 

urządzenia w rodzaju: telefony komórkowe, PDA, 

itp.

Java  3D  w  obecnej  postaci  oferuje  przenośną 

platformę  działająca  w  oparciu  o  maszynę  wirtu-

alną  Java.  Jak  prawie  każde  środowisko  graficz-

ne  Java  3D  pozwala  na  tworzenie  grafiki  trójwy-

miarowej w oparciu o hierarchiczną strukturę grafu, 

w  którego  węzłach  znajdować  się  będą  wszystkie 

elementy  wirtualnego  świata  (tj.  geometria  obiek-

tów,  przekształcenia,  kamery,  oświetlenia,  anima-

tory, etc.) konieczne do należytej reprezentacji pro-

jektowanej sceny.

Celem artykułu jest prezentacja niektórych możli-

wości, jakie może dać Java 3D programistom tworzą-

cym przestrzenne aplikacje graficzne (niekoniecznie 

przy użyciu tylko i wyłącznie samej Javy !). Zaczyna-

my od krótkiej charakterystyki kluczowych pakietów 

biblioteki, aby następnie przejść do napisania prostej 

aplikacji typu 

Hello3D

. W dalszym ciągu artykułu sku-

pimy się na integracji Javy 3D ze Swing oraz gene-

rowaniu sceny przy użyciu skryptów Pythona, a ści-

ślej mówiąc, jego implementacji na maszynę wirtual-

ną Javy (Jython) i integracji Java 3D z tym środowi-

skiem.

Java 3D – opis

API Java 3D stanowi kilka pakietów, z których naj-

ważniejsze  to  jądro: 

javax.media.j3d.org

.  Klasy 

związane  z  konstrukcją  i  opisem  geometrii  obiek-

tów,  budową  sceny  zawarte  są  w  kilku  pakietach 

użytkowych,  np. 

com.sun.j3d.utils

,  a  w 

javax.vec-

math

 znajdują się klasy wspomagające operacje na 

współrzędnych,  wektorach  i  macierzach.  Ogólnie, 

mamy do dyspozycji klasy służące tworzeniu geo-

metrii (brak krzywych i powierzchni typu NURBS), 

animacji,  detekcji  kolizji,  oświetleniu  i  teksturowa-

niu.  Java  3D,  w  przeciwieństwie  do  innych  środo-

wisk  typu  OpenSceneGraph  czy  Open  Inventor, 

nie posiada natywnego formatu do zapisu/odczytu 

sceny, w związku z czym, chcąc nie chcąc, musimy 

w sytuacji, gdy np. konieczny jest import sceny do 

naszej  aplikacji  polegać  na  odpowiednich  konwer-

terach dla 3ds Max, VRML, itp. Najczęściej spoty-

ka się konwertery formatów 

.obj

 i 

.vrml

, ale wspie-

rany  jest  także  w  pewnym  ograniczonym  zakresie 

.vtk

 (Virtual Toolkit), 

.lwo

 (LightWave) czy też po-

pularny 

.dxf

.

Interaktywność,  w  ogólnym  tego  słowa  zna-

czeniu,  sprowadza  się  do  możliwości  zapro-

gramowania  reakcji  określonego  obiektu  na  ze-

wnętrzne bodźce. W Java 3D przyjęto zasadę, iż 

reakcje te wykonywane są zawsze wtedy, gdy bo-

dziec ma miejsce w określonych granicach wokół 

obiektu. Zaletą takiego rozwiązania jest to, iż apli-

kacje jest w stanie zignorować zdarzenie mające 

miejsce  poza  zdefiniowanym  dla  danego  obiek-

tu zakresem. Definiowanie zakresów zdarzeń jest 

konieczne  do  określenie  przez  aplikacje,  na  któ-

re  bodźce  powinna  zareagować.  Reakcje  można 

powiązać z dowolną liczbą zdarzeń, np. zbliżenie 

do określonego obiektu może powodować zmianę 

w  kolorze,  obrót,  etc.  Co  więcej,  wiele  reakcji 

można  kontrolować  przy  użyciu  tzw.  interpola-
torów
  (np.  przemieszczanie  się  obiektu  wzdłuż 

określonej trajektorii).

Janusz Gołdasz

Autor  jest  pracownikiem  Centrum  Technicznego  firmy 
Delphi Poland w Krakowie.
Kontakt z autorem: janusz.goldasz@delphi.com

Rysunek 1. 

Pierwsza aplikacja: Hello3D

background image

Java 3D i Python

43

www.sdjournal.org

Software Developer’s Journal   6/2006

Listing 1. 

Pierwsze kroki – Hello3D

import

 

java

.

applet

.

Applet

;

import

 

java

.

awt

.

BorderLayout

;

import

 

java

.

awt

.

event

.

*;

import

 

java

.

awt

.

GraphicsConfiguration

;

import

 

com

.

sun

.

j3d

.

utils

.

applet

.

MainFrame

;

import

 

com

.

sun

.

j3d

.

utils

.

geometry

.

*;

import

 

com

.

sun

.

j3d

.

utils

.

universe

.

*;

import

 

javax

.

media

.

j3d

.

*;

import

 

javax

.

vecmath

.

*;

import

 

com

.

sun

.

j3d

.

utils

.

behaviors

.

mouse

.

*;

import

 

com

.

sun

.

j3d

.

utils

.

behaviors

.

vp

.

*;

import

 

com

.

sun

.

j3d

.

loaders

.

objectfile

.

ObjectFile

;

import

 

com

.

sun

.

j3d

.

loaders

.

ParsingErrorException

;

import

 

com

.

sun

.

j3d

.

loaders

.

IncorrectFormatException

;

import

 

com

.

sun

.

j3d

.

loaders

.

Scene

;

import

 

java

.

io

.

*;

public

 

class

 

Hello3D

 

extends

 

Applet

 

{

   

private

 

SimpleUniverse

 

universe

 

=

 

null

;

 

      

public

 

BranchGroup

 

createSceneGraph

()

 

{

         

BranchGroup

 

objRoot

 

=

 

new

 

BranchGroup

();

         

BoundingSphere

 

bounds

 

=

 

new

 

BoundingSphere

(

            

new

 

Point3d

(

0

,

0

,

0

)

,

100

);

         

Background

 

background

 

=

 

new

 

Background

();

         

background

.

setColor

(

new

 

Color3f

(

0.0f

,

0.0f

,

1.0f

));

 

         

background

.

setApplicationBounds

(

bounds

);

         

objRoot

.

addChild

(

background

);

         

Color3f

 

lightColor1

 

=

 

              

new

 

Color3f

(

0.7f

0.7f

0.7f

);

         

Vector3f

 

lightDirection1

  

=

 

              

new

 

Vector3f

(

1.0f

, .

0f

, .

0f

);

         

Color3f

 

ambientLightColor

 

=

 

              

new

 

Color3f

(

0.2f

0.2f

0.2f

);

         

AmbientLight

 

ambientLight

 

=

 

              

new

 

AmbientLight

(

ambientLightColor

);

         

ambientLight

.

setInfluencingBounds

(

bounds

);

         

DirectionalLight

 

dirLight

 

=

 

              

new

 

DirectionalLight

(

              

lightColor1

lightDirection1

);

         

dirLight

.

setInfluencingBounds

(

bounds

);

         

objRoot

.

addChild

(

ambientLight

);

         

objRoot

.

addChild

(

dirLight

);

         

Transform3D

 

sphereTransform

 

=

 

new

 

Transform3D

();

         

sphereTransform

.

set

(

              

new

 

Vector3f

(

0.5f

,

0.0f

,

-

0.25f

));

         

sphereTransform

.

setScale

(

0.5f

);

         

TransformGroup

 

objTransform1

 

=

 

              

new

 

TransformGroup

();

         

objTrans1

.

setTransform

(

sphereTransform

);

         

Transform3D

 

cubeTransform

 

=

 

new

 

Transform3D

();

         

cubeTransform

.

set

(

new

 

Vector3f

(-

0.5f

,

0f

,

0.25f

));

         

cubeTransform

.

setScale

(

0.5f

);

         

TransformGroup

 

objTransform2

 

=

 

              

new

 

TransformGroup

();

         

objTransform2

.

setTransform

(

cubeTransform

);

         

objTransform2

.

setCapability

(

              

TransformGroup

.

ALLOW_TRANSFORM_WRITE

);

         

objTransform2

.

setCapability

(

              

TransformGroup

.

ALLOW_TRANSFORM_READ

);

         

objRoot

.

addChild

(

objTransform1

);

         

objRoot

.

addChild

(

objTransform2

);

         

Sphere

 

sphere

 

=

 

new

 

Sphere

(

0.5f

);

         

ColorCube

 

cube

 

=

 

new

 

ColorCube

(

0.5f

);

         

Appearance

 

appearance

 

=

 

new

 

Appearance

();

         

Color3f

 

sphereColor

 

=

 

              

new

 

Color3f

(

1.0f

0.2f

0.4f

);

         

app

.

setMaterial

(

new

 

Material

(

sphereColor

              

new

 

Color3f

(

0f

,

0f

,

0f

)

sphereColor

              

new

 

Color3f

(

1f

,

1f

,

1f

)

10

));

         

sphere

.

setAppearance

(

appearance

);

         

objTransform1

.

addChild

(

sphere

);

         

objTransform2

.

addChild

(

cube

);

         

MouseRotate

 

rotate

 

=

 

              

new

 

MouseRotate

(

objTransform2

);

         

objRoot

.

addChild

(

rotate

);

         

rotate

.

setSchedulingBounds

(

bounds

);

         

MouseZoom

 

zoom

 

=

 

new

 

MouseZoom

(

objTransform2

);

         

objRoot

.

addChild

(

zoom

);

         

zoom

.

setSchedulingBounds

(

bounds

);

         

int

 

flags

 

=

 

ObjectFile

.

RESIZE

;

         

ObjectFile

 

file

 

=

 

new

 

ObjectFile

(

flags

);

         

Scene

 

model

 

=

 

null

;

         

model

 

=

 

file

.

load

(

"model.obj"

);

         

Transform3D

 

mdlTransform

 

=

 

new

 

Transform3D

();

         

mdlTransform

.

set

(

new

 

Vector3f

(

0f

,

0f

,

0f

));

         

mdlTransform

.

setScale

(

0.5f

);

         

TransformGroup

 

objTransform3

 

=

 

              

new

 

TransformGroup

();

         

objTransform3

.

setTransform

(

mdlTransform

);

         

objTransform3

.

addChild

(

model

.

getSceneGroup

());

         

objRoot

.

addChild

(

objTransform3

);

         

objRoot

.

compile

();

         

return

 

objRoot

;

      

}

   

public

 

Hello3D

()

 

{

   

}

      

public

 

void

 

init

()

 

{

         

setLayout

(

new

 

BorderLayout

());

         

GraphicsConfiguration

 

config

 

=

 

            

SimpleUniverse

.

getPreferredConfiguration

();

         

Canvas3D

 

c

 

=

 

new

 

Canvas3D

(

config

);

         

add

(

"Center"

c

);

         

BranchGroup

 

scene

 

=

 

createSceneGraph

();

         

universe

 

=

 

new

 

SimpleUniverse

(

c

);

         

universe

.

getViewingPlatform

()

.

              

setNominalViewingTransform

();

         

universe

.

addBranchGraph

(

scene

);

   

}

   

public

 

void

 

destroy

()

 

{

         

u

.

cleanup

();

   

}

   

public

 

static

 

void

 

main

(

String

[]

 

args

)

 

{

       

new

 

MainFrame

(

new

 

Hello3D

()

256

256

);

   

}

}

background image

44

Programowanie

grafiki

www.sdjournal.org

 Software Developer’s Journal   6/2006

Pierwsze kroki

Tworzenie aplikacji w oparciu o Java 3D pokażemy na ra-

czej  typowym  dla  tej  biblioteki  przykładzie  zawierającym 

kilka najczęściej spotykanych elementów (prymitywy, kom-

ponowanie  sceny,  wczytanie  modelu  z  zewnętrznego  pli-

ku, reakcje) – na ekranie (Rysunek 1) pojawią się kula, sze-

ścian i (wczytany z pliku 

.obj

) obiekt 3D. Kula i obiekt pozo-

staną  nieruchome,  a  użytkownik  może  manipulować  jedy-

nie sześcianem.

Na  początek  przyjrzyjmy  się  implementacji  klasy 

Hello-

3D

 w Listingu 1. Nasza aplikacja to typowy applet, w związ-

ku  z  czym  konieczne  jest  dziedziczenie  po  klasie 

Applet

 

i implementacja metody 

init()

. Do rysowania wykorzystuje-

my odpowiednio skonfigurowane okno klasy 

Canvas3D

 – kla-

sa 

Canvas3D

  dziedziczy  po 

awt.Canvas

.  Kluczowa  dla  zawar-

tości trójwymiarowej sceny jest stworzona przez nas metoda 

createScene()

, zwracająca korzeń sceny (

scene

) klasy 

Branch-

Group

 (

javax.media.j3d.BranchGroup

). Chcąc stworzoną scenę 

wyświetlić, w dalszej części metody musimy dołączyć korzeń 

do naszego wszechświata (

universe

). W metodzie 

createSce-

ne()

, na początek musimy zainicjować korzeń drzewa (

root

), 

do którego będziemy dokładać kolejne elementy sceny. Ko-

lejno ustawiamy: tło (

background

), światło otoczenia (

ambien-

tLight

) i kierunkowe (

dirLight

). Granice sceny ustalamy two-

rząc instancję klasy 

BoundingSphere

 (

bounds

). Chcąc manipu-

lować jedynie sześcianem, musimy stworzyć dla niego osob-

ną gałąź (grupę) i w rezultacie do korzenia dołączyć trzy od-

dzielne instancje klasy 

TransformGroup

 (

objTransform1

 dla ku-

li, 

objTransform2

 dla sześcianu i 

objTransform3

 dla sceny, któ-

rą wczytywać będziemy z pliku) – tylko 

objTransform2

 w koń-

cowym  efekcie  będzie  posiadać  mechanizm  obsługi  zda-

rzeń myszki. Tak kulę jak i sześcian przesuwamy i skaluje-

my  używając  metod 

set(Vector3f  x)

  i 

setScale(float  scale)

 

klasy 

Transform3D

. W przypadku kuli i modelu działamy od-

powiednio na instancjach 

sphereTransform

 oraz 

mdlTransform

dla sześcianu natomiast na instancji 

cubeTransform

 (w gałęzi 

objTransform2

). Dodatkowo, kula jest kolorowana (

sphere.se-

tAppearance(appearance)

).  Do  manipulowania  sześcianem 

myszką potrzebne są nam jeszcze instancje klasy 

MouseZoom 

(zoom)

 i 

MouseRotate  (rotate)

. Obydwie przyjmują 

objTrans-

form2

. Dla wczytania zewnętrznego modelu (sceny) kluczo-

wy jest następujący fragment

   ObjectFile file = new ObjectFile(flags);
   Scene model = null;
   model = file.load("model.obj");

Tak wczytany model wystarczy tylko umieścić w przeznaczo-

nej dla niego gałęzi i dołączeniu jej do korzenia:

   objTrans3.addChild(model.getSceneGroup());
   objRoot.addChild(objTrans3);

Musimy  jeszcze  tylko  pamiętać  o  bieżącym  modyfikowaniu 

treści drugiej grupy

objTransform2.setCapability(
   TransformGroup.ALLOW_TRANSFORM_WRITE)
objTransform2.setCapability(
   TransformGroup.ALLOW_TRANSFORM_READ);

dołączeniu  wszystkich  potrzebnych  elementów  do  korzenia 

i pierwsza aplikacja jest gotowa.

Rysunek 2. 

Przykład importu do Java 3D powierzchni 

wygenerowanej w Blenderze

Rysunek 3. 

Przykład połączenia GUI z Java 3D – symulator 

cząsteczek

Listing 2. 

Przykład użycia obiektu Canvas3D ze Swing

class

 

Panel3D

 

extends

 

JPanel

{

   

public

 

Panel3D

()

{

      

this

.

add

(

new

 

Canvas3D

(

config

));

   

}

}

Listing 3. 

Rysunkowa para JTabbedPane – Canvas3D

class

 

Tabbed

 

extends

 

JTabbedPane

{

   

public

 

Tabbed

()

{

     

 // …

     

 // Gotowe: Java 3D wewnątrz Swing

      

this

.

addPage

(

new

 

Panel3D

());

 

     

 //

   

}

}

background image

Java 3D i Python

45

www.sdjournal.org

Software Developer’s Journal   6/2006

Zwróćmy  uwagę,  że  informacja  o  tworzywie  wczyta-

nego  modelu  może  znajdować  się  w  zewnętrznej  biblio-

tece  materiałowej.  W  przypadku  plików  .obj  jest  to  zwykle 

tekstowy  plik  z  rozszerzeniem 

.mtl

  generowany  przy  oka-

zji tworzenia samego modelu. Aby móc z niego skorzystać, 

wystarczy  w  naszym  modelu  (w  formacie 

.obj

)  umieścić 

dwie instrukcje

   mtllib model.mtl
   usemtl 20

Pierwsza z nich określa zewnętrzną bibliotekę, z której mo-

żemy  pobrać  odpowiednie  tworzywa,  a  druga  indeks  two-

rzywa,  którym  kolorujemy  albo  cały  model  albo  wybra-

ną grupę. W tym wypadku spada z nas np. cała odpowie-

dzialność za kolor obiektu, przezroczystość, itp. Trzeba pa-

miętać, że większość istniejących w konwerter plików .obj

a także .lwo zwykle nie obsługuje całości specyfikacji okre-

ślonego  formatu.  W  ekstremalnych  sytuacjach  konieczna 

jest edycja pliku i usunięcie kłopotliwych elementów. Z ist-

niejących modelerów open source dobre wyniki dają Blen-

der oraz Wings – zob. Rysunek 2.

Okienka

Integracja Javy 3D ze Swing nie jest bezproblemowa. Po-

prawnie  Java  3D  pracuje  jedynie  ze  starszymi  kontenera-

Listing 4. 

Java 3D w Jythonie

import

 

java

.

awt

.

GraphicsConfiguration

from

 

java

.

applet

 

import

 

Applet

from

 

java

.

awt

 

import

 

BorderLayout

from

 

java

.

awt

.

event

 

import

 

*

from

 

com

.

sun

.

j3d

.

utils

.

geometry

 

import

 

*

from

 

com

.

sun

.

j3d

.

utils

.

universe

 

import

 

*

from

 

javax

.

media

.

j3d

 

import

 

*

from

 

javax

.

vecmath

 

import

 

*

from

 

com

.

sun

.

j3d

.

utils

.

behaviors

.

mouse

 

import

 

*

from

 

com

.

sun

.

j3d

.

utils

.

behaviors

.

vp

 

import

 

*

# Tworzymy applet…

class

 

Hello3D

(

Applet

):

   

def

 

init

(

self

):

      

self

.

setLayout

(

BorderLayout

())

      

self

.

_bounds

 

=

 

           

BoundingSphere

(

Point3d

(

0.0

,

0.0

,

0.0

)

100.0

)

      

config

 

=

 

SimpleUniverse

.

getPreferredConfiguration

()

      

c

 

=

 

Canvas3D

(

config

)

      

self

.

add

(

"Center"

c

)

      

self

.

_scene

 

=

 

self

.

createScene

()

      

self

.

_universe

 

=

 

SimpleUniverse

(

c

)

      

self

.

_viewingPlatform

 

=

 

           

self

.

_universe

.

getViewingPlatform

();

      

self

.

_viewingPlatform

.

setNominalViewingTransform

();

      

self

.

_universe

.

addBranchGraph

(

self

.

_

 

scene

)

   # Kolej na tworzenie sceny…

   

def

 

createScene

(

self

):

      

# korzeń

      

objRoot

 

=

 

BranchGroup

()

 

      

background

 

=

 

Background

()

      

background

.

setColor

(

Color3f

(

0

,

0

,

1

))

      

background

.

setApplicationBounds

(

self

.

_bounds

)

      

objRoot

.

addChild

(

background

)

      

lightColor1

 

=

 

Color3f

(

0.7

0.7

0.7

)

      

lightDirection1

 

=

 

Vector3f

(

1.0

, .

0

, .

0

)

      

ambientColor

 

=

 

Color3f

(

0.2

0.2

0.2

)

      

ambientLight

 

=

 

AmbientLight

(

alColor

)

      

ambientLight

.

setInfluencingBounds

(

self

.

_bounds

)

      

dirLight

 

=

 

DirectionalLight

(

lightColor1

lightDir1

)

      

dirLight

.

setInfluencingBounds

(

self

.

_bounds

)

      

objRoot

.

addChild

(

ambientLight

)

      

objRoot

.

addChild

(

dirLight

)

      

sphereTransform

 

=

 

Transform3D

()

      

sphereTransform

.

set

(

Vector3f

(

0

.,

0.5

,

-

0.25

))

      

sphereTransform

.

setScale

(

0.5

)

      

objTransform1

 

=

 

TransformGroup

()

 

      

objTransform1

.

setTransform

(

sphereTransform

)

      

cubeTransform

 

=

 

Transform3D

()

      

cubeTransform

.

set

(

Vector3f

(

0

.,

-

0.5

,

0.25

))

 

      

cubeTransform

.

setScale

(

0.5

)

      

objTransform2

 

=

 

TransformGroup

()

      

objTransform2

.

setCapability

(

            

TransformGroup

.

ALLOW_TRANSFORM_WRITE

)

      

objTransform2

.

setCapability

(

            

TransformGroup

.

ALLOW_TRANSFORM_READ

)

      

objTransform2

.

setTransform

(

cubeTransform

)

      

objRoot

.

addChild

(

objTransform1

)

      

objRoot

.

addChild

(

objTransform2

)

 

      

sphere

 

=

 

Sphere

(

0.5

)

      

cube

   

=

 

ColorCube

(

0.5

)

      

appearance

 

=

 

Appearance

()

      

objColor

 

=

 

Color3f

(

1.0

0.2

0.4

)

      

appearance

.

setMaterial

(

Material

(

            

objColor

Color3f

(

0

,

0

,

0

)

objColor

,

            

Color3f

(

1

,

1

,

1

)

10

))

      

sphere

.

setAppearance

(

appearance

)

 

      

objTransform1

.

addChild

(

sphere

)

      

objTransform2

.

addChild

(

cube

)

      

rotate

 

=

 

MouseRotate

(

objTransform2

)

      

objRoot

.

addChild

(

rotate

)

      

rotate

.

setSchedulingBounds

(

self

.

_bounds

)

      

zoom

 

=

 

MouseZoom

(

objTransform2

)

      

objRoot

.

addChild

(

zoom

)

      

zoom

.

setSchedulingBounds

(

self

.

_bounds

)

      

objRoot

.

compile

()

      

return

 

objRoot

      # koniec metody createScene

# Testujemy applet…

if

 

__name__

 

==

 '

__main__

'

:

   

import

 

pawt

   

pawt

.

test

(

Hello3D

())

# Koniec

background image

46

Programowanie

grafiki

www.sdjournal.org

 Software Developer’s Journal   6/2006

mi AWT. Powszechnym problem jest przerysowywanie i za-

krywanie kontrolek Swingowych przez Javę 3D (

Canvas3D

), 

co  powoduje,  że  użycie  większości  kontrolek  Swingowych 

jest praktycznie niemożliwe. Najlepsze wyniki, jak pokaże-

my na przykładzie, daje para 

JTabbedPane

 – 

Canvas3D

 i dzie-

dziczenie po klasie 

JPanel

, np. w sposób pokazany w Listin-

gu 2 i 3.

Teraz wystarczy już tylko Rysunkowa para 

JTabbedPane

 – 

Canvas3D

, przedstawiona na Listigu 3.

Dzięki takiemu rozwiązaniu można w łatwy sposób two-

rzyć  rozbudowane  aplikacje  z  typowym  dla  Swingowych 

aplikacji interfejsem użytkownika, jak np. ta zilustrowana na 

Rysunku 3 przedstawiając symulator cząsteczek.

Java 3D a Jython

Ciekawą  alternatywą  dla  programistów  używających  obiek-

towe języki skryptowe wysokiego poziomu (np. Python) jest 

możliwość integracji Javy 3D z Pythonem, a konkretnie z im-

plementacją Pythona o nazwie Jython na maszynę wirtual-

ną Javy. Zalet tworzenia aplikacji przy użyciu języków skryp-

towych w rodzaju Pythona jest wiele – np. obecność typów 

danych  wysokiego  poziomu  (listy,  słowniki)  przyspiesza-

jąca  tworzenie  aplikacji,  dynamiczne  typowanie,  oszczęd-

ność  kodu,  interpretowanie  zamiast  kompilacji.  Jython 

w  wersji  2.1  jest  zgodny  z  J2SE,  dzięki  czemu  korzystanie 

w  nim  ze  środowiska  Java  3D  w  obecnej  wersji  (1.3.1)  jest 

bezproblemowe – Jython jest w 100% napisany w Javie, co 

daje możliwość importowania klas Javy do tworzonych przez 

nas  skryptów.  Dla  porównania  z  poprzednim  kodem,  poni-

żej przykład skryptu/appletu stworzonego w Pythonie. Apli-

kacja jest identyczna z opisaną na wstępie aplikacją 

Hello-

3D

 poza fragmentem dotyczącym wczytania elementu sceny 

z zewnątrz. Odpowiednie moduły Javy i Javy 3D importuje-

my, używając typowej konstrukcji Pythona

from com.sun.j3d.utils.behaviors.mouse import *

Listing 5. 

Wykorzystanie klasy PythonInterpreter

// Import klas Java 3D…

import

 

org

.

python

.

core

.

PyObject

;

 // Jython

import

 

org

.

python

.

util

.

PythonInterpreter

;

 // interpreter

// Klasa dziedziczy po JFrame - Swing

public

 

class

 

embDemo

 

extends

 

JFrame

 

{

  

 // pola prywatne, etc.

   

public

 

embDemo

(

String

 

source

)

 

{

     

 // …

     

 // Dodajemy do ramki nasz panel 3D

      

this

.

getContentPane

()

.

add

(

new

 

Tabbed

(

source

));

     

 // …

   

}

  

 // Panel dziedziczy po klasie JPanel

   

class

 

Panel3D

 

extends

 

JPanel

 

{

 

      

public

 

Panel3D

(

String

 

source

)

 

{

        

 // …

        

 // Scena i wirtualny wszechświat

         

BranchGroup

 

scene

;

 

         

SimpleUniverse

 

sUniverse

 

=

 

new

 

SimpleUniverse

(

can

vas3D

);

         

scene

 

=

 

createSceneGraph

(

source

);

         

sUniverse

.

addBranchGraph

(

scene

);

        

 //…

         

this

.

setLayout

(

new

 

BorderLayout

());

         

this

.

setOpaque

(

false

);

         

this

.

add

(

"Center"

canvas3D

);

      

}

     

 // Metoda createSceneGraph() jest inna niż zwykle..

      

private

 

BranchGroup

 

createSceneGraph

(

String

 

source

)

 

{

         

BranchGroup

 

objRoot

 

=

 

new

 

BranchGroup

();

         

TransformGroup

 

mainGroup

 

=

 

new

 

TransformGroup

();

        

 // …

        

 // Python: uruchamiamy interpreter

        

 // Tutaj embeddedWorld zwraca wczytaną 

         // z zewnątrz scenę

         

BranchGroup

 

embedded

 

=

 

embeddedWorld

(

source

);

         

mainGroup

.

addChild

(

embedded

);

        

 // Python: KONIEC          

         

objRoot

.

addChild

(

mainGroup

);

         

return

 

objRoot

;

      

}

      

private

 

BranchGroup

 

embeddedWorld

(

String

 

source

)

 

{

         

Object

 

input

 

=

 

null

;

         

BranchGroup

 

root

 

=

 

new

 

BranchGroup

();

        

 // Inicjujemy interpreter…

         

PythonInterpreter

 

interp

 

=

 

new

 

PythonInterpreter

();

        

 // ...i wczytujemy do niego treść skryptu

         

try

 

{

            

DataInputStream

 

in

 

=

 

new

 

DataInputStream

(

               

new

 

FileInputStream

(

 

source

 

));

            

interp

.

execfile

(

in

);

           

 // Pobieramy określony obiekt - scene

            

input

 

=

 

interp

.

get

(

"scene"

,

BranchGroup

.

class

);

         

}

 

catch

 

(

FileNotFoundException

 

E

)

 

{

              

E

.

printStackTrace

(

System

.

err

);

         

}

        

 // Rzutujemy - input jest instancją klasy Object

         

return

 

(

BranchGroup

)

 

input

;

      

}

   

}

  

 // Dziedziczymy po klasie JTabbedPane

   

class

 

Tabbed

 

extends

 

JTabbedPane

 

{

  

 // source to nazwa skryptu Pythona

      

public

 

Tabbed

(

String

 

source

)

 

{

        

 // Zakładka z panelem 3D

         

this

.

addTab

(

"View"

new

 

Panel3D

(

source

));

      

}

   

}

   

public

 

static

 

void

 

main

(

String

[]

 

args

)

 

{

      

new

 

embDemo

(

args

[

0

]);

   

}

}

background image

47

Java 3D i Python

www.sdjournal.org

Software Developer’s Journal   6/2006

Struktura appletu (w Pythonie !) jest w zasadzie identycz-

na z pierwszym przykładem (Java). Do naszego przykła-

du  stosują  się,  oczywiście  wszystkie  charakterystycz-

ne cechy Pythona, a więc oszczędność kodu, typowanie, 

etc. 

Wskazówka  dla  osób  nie  mających  styczności  z  Pytho-

nem: słowo 

self

 jest odpowiednikiem 

this

 Javy, ale w Pytho-

nie 

self

 musi być użyte jawnie w treści klasy.

W aspekcie korzystania z Pythona, pojawia się też moż-

liwość rozszerzania funkcjonalności naszej aplikacji skryp-

tami  Pythona  (Jythona),  co  w  wielu  przypadkach  jest  rze-

czą  nie  do  pogardzenia.  Ogólnie  rzecz  ujmując,  para  Ja-

va-Jython daje dwie możliwości: kompilacja kodu Pythona 

do postaci klasy Javy i wywołanie go w tworzonej aplikacji 

w postaci klasy lub wykorzystanie interpretera poleceń Py-

thona (klasa 

PythonInterpreter

) do wywołania zewnętrzne-

go skryptu generującego np. geometrię. Jak sprawdziliśmy 

w praktyce, pierwsze rozwiązanie nie zawsze działa, nato-

miast korzystanie z interpretera jest o wiele bardziej nieza-

wodne i czytelne.

Integrację Java (i Java 3D) z Python’em (Jython’em) po-

każemy  na  przykładzie  nieskomplikowanej  aplikacji  okien-

kowej  (zob.  Rysunek  4),  skonstruowanej  w  opisany  w  Li-

stingach 2 i 3 sposób. Tym razem jednak zamiast skazywać 

się  tylko  i  wyłącznie  na  towarzystwo  Javy  w  treści  meto-

dy 

createSceneGraph()

 pojawi się wywołanie skryptu Jytho-

na o określonej zawartości (Listingi 5 i 6). W Listingu 5 brak 

części  powtarzających  się  fragmentów  kodu.  Najważniej-

sza  zmiana  w  treści  klasy  to  nowa  metoda  o  nazwie 

eme-

beddedWorld(String  source)

, której parametrem jest nazwa 

zewnętrznego skryptu Jythona 

source

 – aplikację wywołu-

jemy z parametrem w postaci nazwy skryptu. Do zinterpre-

towania treści skryptu w sposób zrozumiały dla naszej apli-

kacji konieczne jest utworzenie obiektu klasy 

PythonInter-

preter

 (Klasa ta posiada metodę 

execfile()

, przyjmującą in-

stancję klasy 

FileInputStream

 (skrypt), której wykonanie po-

woduje przetworzenie treści skryptu oraz metodę 

get()

, któ-

rej pierwszym parametrem jest nazwa pobieranego obiek-

tu  Pythona  (

scene

),  a  drugim  odpowiednia  klasa  Java  3D. 

W naszym przypadku jest to klasa 

BranchGroup

 – konieczne 

jest rzutowanie do klasy 

BranchGroup

.

Oprogramowanie

Wszystkie  omówione  w  niniejszym  artykule  przykłady  po-

wstały przy użyciu Java 3D w wersji 1.3.1, Java 2 SE, Jy-

thon 2.1 oraz środowiska Eclipse w wersji 3.1 z wtyczką Py-

Dev (Python/Jython). Wykorzystany w pierwszym przykła-

dzie model jest częścią dystrybucji Java 3D. Minimalne za-

soby  potrzebne  do  utworzenia  opisanych  aplikacji  są  jed-

nak  znacznie  mniejsze  i  mogą  ograniczać  się  do  koniecz-

ności zainstalowania Javy (J2SE) wraz z Java 3D oraz Jy-

thona  i  tworzeniu  opisanych  aplikacji  przy  pomocy  dowol-

nego edytora tekstowego.

Podsumowanie

Java 3D jest, pomimo pewnych swoich niedociągnięć (kiepska 

współpraca ze Swing, brak bezpośredniego dostępu do funk-

cji  OpenGL,  prosty  model  oświetlenia)  i  wieku,  stosunkowo 

atrakcyjnym rozwiązaniem dla programistów WWW i gier Ja-

va. W dalszym ciągu posiada duży potencjał i możliwości po-

zwalające  programistom  na  umieszczanie  niewielkim  nakła-

dem pracy elementów grafiki 3D w appletach i aplikacjach Ja-

va, choć obecnie popularniejszym rozwiązaniem, np. w sferze 

gier Java, wydają się być dedykowane rozwiązania, np. oparta 

na JOGL biblioteka Xijth3D. Java 3D chyba jednak najbardziej 

wpasowuje się w model przeznaczony dla średnio-wymagają-

cego użytkownika. n

W Sieci

•   niezależne forum developerów Java 3D, JOGL
 

http://www.j3d.org

•   strona domowa Javy
 

http://www.javasoft.com

•   strona domowa Eclipse IDE
 

http://www.eclipse.org

•   strona domowa programu Blender
 

http://www.blender.org

Rysunek 4. 

Jython zanurzony

Listing 6. 

Fragment importowanego skryptu Jython’a 

(brak listy modułów Java 3D)

# Lista modułów Java 3D…
# funkcja createScene()

def

 

createScene

():

   # Tradycyjnie, korzen

   

objRoot

 

=

 

BranchGroup

()

   

bounds

 

=

 

BoundingSphere

(

Point3d

(

0.0

,

0.0

,

0.0

)

100.0

)

   

background

 

=

 

Background

()

 

   

background

.

setColor

(

Color3f

(

0

,

0

,

1

))

 

   

background

.

setApplicationBounds

(

bounds

)

   

objRoot

.

addChild

(

background

)

   

objTransform

 

=

 

TransformGroup

()

   

objTransform

.

setCapability

(

TransformGroup

.

ALLOW_

TRANSFORM_READ

)

   

objRoot

.

addChild

(

objTransform

)

   

objTransform

.

addChild

(

ColorCube

(

0.4

))

   

return

 

objRoot

   # koniec funkcji
# Wywołujemy createScene() i wynik przypisujemy 
# do zwracanej zmiennej scene

scene

 

=

 

createScene

()

# Koniec