SAI Öğrencesi

Bu öğrence SAI (“Scene Access Interface” Sahne Erişim Arayüzü)’nün nasıl kullanılacağını gösterir. Bu arayüz programcının X3D dünyalarını değiştirmesi veya yenilerini oluşturmasına olanak sağlar.

 

Dışsal Erişim: Aşağıdaki örnekler X3D dünyasının dışarıdan değiştirilmesini göstermektedir. VRML97’deki EAI’ye benzemektedirler.

 

 

İçsel Erişim:  Aşağıdakiler X3D dünyasının içeriden değiştirilmesini göstermektedir. VRML97’deki JSAI’ye benzemektedirler.

 

 

Bütün bu örnekler sıkıştırılmış dosya şeklinde erişilebilir.  Bu dosya Xj3D’yi otomatik olarak çalıştıran Ant betiği içerir. Bu dosyayı Xj3D kurulumunuzun olduğu dizine açın. Orada examples/sai adlı bir dizin oluşturacaktır. Ant’ı buradan indirebilirsiniz: http://ant.apache.org/

 

Sıkıştırılmış Dosya : SAI Örnekleri

 

Örnekleri çalıştırmak için:

 

cd examples/sai

 

Dünyayı Yüklemek

Bu örnekte basit bir dosya yükleyicisinin nasıl yazılacağını öğreneceksiniz. X3D tarayıcısının yaratılma temellerinden başlayıp dosyanın nasıl yükleneceği anlatılacaktır.

 

3B Penceresini Eklemek

3B Penceresini uygulamanıza eklemek kolaydır. SAI BrowserFactory sınıfını kullanarak X3D bileşenini yaratırsınız. Daha sonra bu bileşeni uygulamanıza eklersiniz. Aşağıdaki kod bu adımları göstermektedir:

 

import org.web3d.x3d.sai.*;
 
public class SimpleSAIDemo extends JFrame {
 
    public SimpleSAIDemo() {
        Container contentPane = getContentPane();
 
        // SAI bilesenini yaratalim
        X3DComponent x3dComp = BrowserFactory.createX3DComponent(null);
 
        // UI’ye bu bileseni ekleyelim
        JComponent x3dPanel = (JComponent)x3dComp.getImplementation();
        contentPane.add(x3dPanel, BorderLayout.CENTER);
 

SAI erişimi için gereken tüm sınıflar org.web3d.x3d.sai paketinde bulunmaktadır. Xj3D javadoc’u SAI kodu yazarken kullanışlı bir kaynaktır. 3B penceresini uygulamanıza eklemek için gerekenler bunlardır. Bir sonraki kısım dosya yüklemeyi gösterecektir.

 

Dosya Yükleme

Dosya yüklemeden önce tarayıcı kavramını anlatmak gerekiyor. SAI’de tarayıcı sistemle etkileşim için gereken basit bir arayüzdür. Dosya yükleme, düğüm yaratma ve çerçeve oranını getirme gibi işler yapmanıza olanak sağlar. Browser sınıfı dışsal ve içsel SAI programları için mevcuttur.

 

ExternalBrowser arayüzü dışsal SAI programlarının imgelemeyi duraklatma ve tarayıcı dinleyicisi ekleme gibi daha fazla işlevselliğe erişmelerini sağlar. Bu işlemleri sadece dışsal kodtan yapabilirsiniz. Dosya yükleyebilmek için yarattığımız X3DComponent bileşeninden tarayıcı (Browser) referansını alırız.

 
// Dissal tarayici alalim
ExternalBrowser x3dBrowser = x3dComp.getBrowser();
 

Tarayıcı referansını elde ettikten sonra bu referansı dosya yüklemek için kullanabiliriz. Bu işi yapmak için bir kaç yöntem vardır. Bu ilk örnek için en kolayını kullanacağız. Bu dosya indirilip gösterilmeye hazırlanana kadar bloklanmış createX3DFromURL çağrısıdır. Bu metod X3D sahnesini kapsayan X3DScene nesnesini döndürür. Daha sonra biz şu an için boş olan güncel sahneyi bu yeni sahne ile değiştiririz.

 

        // Dosya yukleyerek bir X3D sahnesi olustur
        X3DScene mainScene = x3dBrowser.createX3DFromURL(new String[] { "moving_box.x3dv" });
 
        // Varolan dunyayi yenisiyle degistir
        x3dBrowser.replaceWorld(mainScene);
 

Bu örneğin tam kodunu şurada bulabilirsiniz : SimleSAIDemo

 

URL İşleme Tarzı

Bu noktada anlaşılması gereken kavram URL’lerin nasıl ele alındığıdır. createX3DFromURL girdi olarak URL katarı almaktadır. Bu katardaki URL’lerden birine ulaşıp başarıyla indirene kadar arama gerçekleşir. URL internet üzerindeki veya kendi yerel makinemizdeki nesnenin konumunu belirtme yöntemi sağlar. Eğer sadece dosya adı veya kısmi dizin ve dosya yolu (örneğin “examples/foo.xd3v”) verirsek göreceli URL sağlanmıştır. X3D belirtimi bu URL’lerin o an çalışılan dizinden başlaması gerektiğini söylemektedir. Daha ayrıntılı bilgi için X3D Java binding specification(ISO/IEC 19777-2:2005) 3.3.4.2 maddesini inceleyin.

 

Bir Sahanın Değerini Değiştirmek

Bu bölümde X3D düğümlerinin saha değerlerini nasıl değiştirileceğini öğreneceksiniz. Bunun için kutuların rengini kırmızıdan maviye çeviren bir örnek inceleyeceğiz. Dünya yüklenince dinamik olarak birşeyleri değiştirmek olağandır. Eğer düğüm DEF yardımıyla adlandırılmış ise SAI tarafından düğüme erişmek kolaydır. Aşağıdaki örnek MAT olarak adlandırılmış Material düğümünün rengini (diffuseColor) maviye çevireceğiz. Değiştirilecek olan dosya aşağıdaki basit hareket eden kırmızı bir kutu içeren sahnedir:

 

#X3D V3.0 utf8
 
PROFILE Interactive
 
DEF TS TimeSensor {
  cycleInterval 10
  loop TRUE
}
 
DEF TG Transform {
  rotation 0 1 0 0.78
  children Shape {
    geometry Box {}
    appearance Appearance {
      material DEF MAT Material {
        diffuseColor 1 0 0
      }
    }
  }
}
 
DEF PI PositionInterpolator {
  key [ 0 0.25 0.5 0.75 1 ]
  keyValue [
     0 0 0
    -1 0 0
    -1 1 0
     0 1 0
     0 0 0
  ]
}
 
ROUTE TS.fraction_changed TO PI.set_fraction
ROUTE PI.value_changed TO TG.translation

 

Bu dosyada farkettiğimiz üzere MAT olarak tanımlanmış Material düğümü kırmızıdır. Takip eden kodları kullanarak bu düğümün diffuseColor (renk) saha değerini mavi olarak değiştireceğiz.

 

İlk olarak MAT olarak tanımlanmış düğümü bulalım. Bu örnekte moving_box.xv3d dosyasından yüklenen ana sahneyi kullanacağız. getNamedNode metodu verilen sahne içerisinde istenen isimli düğümü arayıp getirir. Eğer bulamazsa null döndürür.

 

                // MAT isimli dugumu bul
        X3DNode mat = mainScene.getNamedNode("MAT");
        if (mat == null) {
            System.out.println("Materyal Bulunamiyor: MAT");
            return;
        }
 

Düğümü bulduktan sonra düğümün diffuseColor sahasını bulup değiştirmemiz gerekiyor. Bir düğümün sahasını almak için getField metodunu kullanırız. Bu sahanın X3D tipini, bizim örneğimizde SFColor olarak alırız. Tipini ayarladıktan sonra değerini sorgular veya ayarlayabiliriz. Bu örnekte blue (mavi) isimli bir yerel değişken tanımlayıp bu değişkeni sahanın değeri olarak ayarlıyoruz.

 

X3D Soyut belirtimi her düğüm için bir saha ismi tanımlar. X3D erişim modelinde tanımlanan şekliyle kullanmamız gerekir. Bunun anlamı örneğin initializeOnly sahalar sadece düğüm gerçeklenmeden (realize) önce değişebilir. Bir düğüm sahne çizgesine eklendiği veya realize metodu çağrıldığı zaman gerçeklenir. inputOnly ve inputOutput sahalar her zaman değiştirilebilir. outputOnly sahalar SAI ile değiştirilemez.

 

        // diffuseColor sahasini alalim
        SFColor color = (SFColor) mat.getField("diffuseColor");
 
        // Degerini maviye cevirelim
        float[] blue = {0,0,1};
        color.setValue(blue);
 

Bu örneğin tam kodunu şurada bulabilirsiniz : FieldAccessDemo.java

 

Yeni Düğümler Yaratmak

Bu bölümde yeni düğümlerin nasıl yaratıldığını göreceksiniz. Bir dosyadan yüklemek yerine tüm sahneyi dinamik olarak düğüm düğüm yaratabilirsiniz. Örnekte bir kutu (“Box” düğümü) içeren çok basit bir dünyayı oluşturacağız.

            İlk görevimiz yeni bir X3DScene nesnesi yaratmaktır. Her X3DScene nesnesi hangi profil ve bileşenleri kullanacağını önceden tanımlamalıdır. Bir profili talep ederken bu profilin o tarayıcı tarafından desteklenmeyebileceğini unutmamalısınız. Eğer profil tarayıcı tarafından desteklenmiyorsa NotSupportedException istisnası oluşturulup fırlatılır.  Örneğimizde Immersive profili desteklenmiyorsa programdan çıkacaktır.  

 

ProfileInfo profile = null;
 
        try {
            profile = x3dBrowser.getProfile("Immersive");
        } catch(NotSupportedException nse) {
System.out.println("Immersive profili 
             +“desteklenmemektedir ");
            System.exit(-1);
        }
 

ProfileInfo ve ComponentInfo örneklerini elde ettikten sonra sahnenin yaratılması için hazırlığımızı tamamlamış oluruz. createScene metodu boş bir sahne yaratır ve X3DScene nesnesi dödürür.

 
        X3DScene mainScene = x3dBrowser.createScene(profile, null);
 

Şimdi düğümleri yaratıp dünyamıza eklemeye başlayabiliriz. createNode metodu yeni düğümler yaratmak için kullanılmaktadır. Eğer düğüm bilinmiyorsa InvalidNodeException istisnası oluşturulup fırlatılır. Aşağıdaki kod örneği Shape düğümünün yaratılışını göstermektedir. Daha sonra Box düğümü yaratılıp bu Shape nesnesinin geometri sahası olarak ayarlanır. Son olarak Shape nesnesi yeni yarattığımız sahnenin kök düğümüne eklenir.

 
        X3DNode shape = mainScene.createNode("Shape");
        SFNode shape_geometry = (SFNode) (shape.getField("geometry"));
        X3DNode box = mainScene.createNode("Box");
 
        shape_geometry.setValue(box);
 
        mainScene.addRootNode(shape);

Bu örneğin tam koduna şurada ulaşabilirsiniz : CreateNodeSAIDemo.java

Saha Değişimlerini Dinlemek

Bir SAI programı herhangi bir sahanın değişimlerini dinleyebilir. Bu uygulamanın, 3B simülasyonda devam eden olaylara karşı tepkiler verebilmesine olanak sağlar. Örnek olarak dokunma algılayıcısı olan bir kutuya (“Box”) tıklandığı zaman bunu öğrenmek istiyoruz. Bu örneğin X3D sahnesi çok kolaydır.

 
#X3D V3.0 utf8
PROFILE Interactive
DEF TG Transform {
  rotation 0 1 0 0.78
  children [
     Shape {
    geometry Box {}
    appearance Appearance {
      material Material {
        diffuseColor 1 0 0
      }
    }
  }
  DEF TOUCH_SENSOR TouchSensor {}
  ]
}
 

SAI kodumuz, TouchSensor düğümünün touchTime sahasını dinlemek için addX3DEventListener metodunu kullanacaktır. Ayrıca X3DFieldEventListener arayüzünü gerçekleştirmemiz gerekmektedir.

 
        // Bir dosya yukleyerek X3D sahnesini olusturalim
        X3DScene mainScene = x3dBrowser.createX3DFromURL(new String[] { "touchy_box.x3dv" });
 
        // Var olan dunyayı yukledigimizle degistirelim
        x3dBrowser.replaceWorld(mainScene);
 
  // TOUCH_SENSOR olarak adlandirilmis dokunma algilayiciyi 
  // bulalim
        X3DNode touch = mainScene.getNamedNode("TOUCH_SENSOR");
        if (touch == null) {
            System.out.println("TOUCH_SENSOR adli dokunma algilayicisi
            +”bulunamiyor");
            return;
        }
 
        SFTime ttime = (SFTime) touch.getField("touchTime");
 
        // touchTime sahasindaki degisimleri dinleyelim
        ttime.addX3DEventListener(this);
    }
 
    //----------------------------------------------------------
    // X3DFieldEventListener tarafindan tanimlanmis metod
    //----------------------------------------------------------
 
    public void readableFieldChanged(X3DFieldEvent evt) {
        System.out.println("Bana Dokunmayi Birak!");
    }
 

Bu örneğin tam koduna şuradan ulaşabilirsiniz : FieldEventDemo.java

 

Rota Eklenmesi

Rotalar X3D’de düğümler arası mesaj göndermek için kullanılmaktadır. Örnek olarak interpolator’un bazı geometrilerin üzerindeki dönüşüm düğümlerine gönderdiği yönlendirme çıktısı verilebilir. Bu örnek bir TouchSensor düğümüne tıklanmasıyla başlar. Bu düğüm tıklandığı zaman SAI kodu TimeSensor düğümünü interpolatora, interpolatorda bir kutu (“Box” düğümü) barındıran Transform düğümüne ateşleme yollayacaktır. Ve kutu canlanacaktır.

Aşağıdaki kod örneği TOUCH_SENSOR isimli TouchSensor düğümünü bulmaktadır. Daha sonra touchTime sahasını alıp bu sahanın X3DFieldEventListener yardımıyla dinlenmesini sağlamaktadır. Bu dokunma algılayıcı (aslında tıklama algılayıcı) tıklandığı zaman sahanın dinleyicisini ateşleyecektir.

        X3DNode ts = mainScene.getNamedNode("TOUCH_SENSOR");
        if (ts == null) {

System.out.println("TOUCH_SENSOR adli dokunma algilayicisi bulunamiyor ");

 
            return;
        }
 
        // TOUCH_SENSOR.touchTime sahasini alalim
        SFTime touchTimeField = (SFTime) ts.getField("touchTime");
 
        touchTimeField.addX3DEventListener(this);
 

Burada dinamik bir rota ekleyeceğiz. Rota eklemek için bağlanacak düğümlere ve kullanılacak saha isimlerine ihtiyacımız var. Rotada kullanacağımız TimeSensor, PositionInterpolator ve Transform düğümlerini alabilmek için getNamedNode metodunu kullanacağız. İki rota eklenecektir. Biri TimeSensor düğümünden Interpolator düğümüne, diğer Interpolator düğümünden Transform düğümüne. Eğer yanlış bir rota oluşursa aşağıdaki istisnalardan biriyle karşılaşırız: InvalidReadableFieldException, InvalidWriteableFieldException veya InvalidNodeException.

 
    /**
     * Bir X3D sahasi degisirse bu metod calisacaktir
     *
     * @param evt Olay.
     */
    public void readableFieldChanged(X3DFieldEvent evt) {
        // TIME_SENSOR isimli dugumu bulalim (TimeSensor)
        X3DNode TS = mainScene.getNamedNode("TIME_SENSOR");
        //  PI isimli dugumu bulalim (PositionInterpolator)
        X3DNode PI = mainScene.getNamedNode("PI");
        //  TG isimli dugumu bulalim (Transform)
        X3DNode TG = mainScene.getNamedNode("TG");
 
        //  TS->PI rotasi olusturulsun
        mainScene.addRoute(TS,"fraction_changed",PI,"set_fraction");
        //  PI->TG rotasi olusturulsun
        mainScene.addRoute(PI,"value_changed",TG,"translation");
    }
 

Bu örneğin tam koduna şuradan ulaşabilirsiniz: AddRouteSAIDemo.java

 

X3D Sahne Çizgesinin Dolaşılması

X3D sahne çizgesinin bir başka değiştirilme yöntemi çizgeyi dolaşıp belli tipteki düğümlere bakmaktır. Buradaki örnekte sahne çizgesini dolaşıp Material düğümlerine bakmayı göstereceğiz.

 

İlgilendiğimiz ana kod findMaterial metodu içerisindedir. Bu metod sahne çizgesini özyinelemeli olarak dolaşır ve Material düğümlerini araştırır.

    /**
     * Tum Material dugumlerini bul ve Maviye çevir
     */
    private void findMaterial(X3DNode[] nodes) {
        int len = nodes.length;
        int types[];
 
        X3DFieldDefinition[] decls;
        int dlen;
        X3DNode node;
 
        for(int i=0; i < len; i++) {
            node = nodes[i];
 
            // NOT: getFieldType() daha hizli, henuz gerceklestirilmedi
            if (node.getNodeName().equals("Material")) {
 
                // diffuseColor sahasini alalim
                SFColor color = (SFColor) 
                               node.getField("diffuseColor");
 
                // degerini maviye cevirelim
                float[] blue = {0,0,1};
                color.setValue(blue);
            }
 

Yukarıdaki kod her düğümü inceler ve Material düğümü olup olmadığını tespit eder. Eğer Material düğümü ise rengini maviye çevirir.

 
            decls = node.getFieldDefinitions();
            dlen = decls.length;
            int ftype;
            int atype;
            MFNode mfnode;
            SFNode sfnode;
 
            X3DNode[] snodes;
 
            for(int j=0; j < dlen; j++) {
                ftype = decls[j].getFieldType();
                atype = decls[j].getAccessType();
 
                if (atype == X3DFieldTypes.INPUT_OUTPUT || atype == X3DFieldTypes.INITIALIZE_ONLY) {
                    if (ftype == X3DFieldTypes.MFNODE) {
                        mfnode = (MFNode) node.getField(decls[j].getName());
                        snodes = new X3DNode[mfnode.size()];
 
                        mfnode.getValue(snodes);
                        findMaterial(snodes);
                    } else if (ftype == X3DFieldTypes.SFNODE) {
                        sfnode = (SFNode) node.getField(decls[j].getName());
                        snodes = new X3DNode[1];
 
                        snodes[0] = sfnode.getValue();
 
                        if (snodes[0] != null) {
                            findMaterial(snodes);
                        }
                    }
                }
            }
        }
    }
 

Yukarıdaki kod bir düğümün saha tanımlamalarını alır. Bu sahanın okunabilir olup olmadığını (inputOutput veya initializeOnly) getFieldAccess metoduyla tespit eder. MFNode veya SFNode sahaları daha fazla işlem için özyinelemeli olarak dolaşılır. getFieldType metodu X3DFieldTypes arayüzü içerisinde tanımlanmış sabit döndürür.

Bu örneğin tam koduna şuradan erişebilirsiniz: SAISGWalkDemo.java