テクスチャを張り付けたXモデル(XFILE)を描画します。

前田稔の超初心者のプログラム入門

XFILE テクスチャモデル

  1. 立方体にテクスチャを張り付けたモデル cube_texture2.x です。
    TEXT 形式のXモデル(XFILE)です。
    エディッタなどでタイプして保存して下さい。
    xof 0302txt 0064
    
    Header {
     1;
     0;
     1;
    }
    
    Material Matred {
     1.000000;1.000000;1.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
     TextureFilename {
      "test.jpg";
     }
    }
    Material Matgr {
     1.000000;1.000000;1.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
     TextureFilename {
      "kishi.gif";
     }
    }
    Material Matwh {
     1.000000;1.000000;1.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
    }
    Material Matye {
     1.000000;1.000000;0.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
    }
    Material Matpi {
     1.000000;0.501961;1.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
    }
    Material Matbr {
     0.000000;0.000000;1.000000;1.000000;;
     21.333333;
     0.000000;0.000000;0.000000;;
     0.000000;0.000000;0.000000;;
    }
    Mesh {
     8;
     1.0;-1.0;-1.0;,
     -1.0;-1.0;-1.0;,
     -1.0;1.0;-1.0;,
     1.0;1.0;-1.0;,
     -1.0;-1.0;1.0;,
     1.0;-1.0;1.0;,
     1.0;1.0;1.0;,
     -1.0;1.0;1.0;;
    
     6;
     4;0,1,2,3;,
     4;4,5,6,7;,
     4;5,0,3,6;,
     4;1,4,7,2;,
     4;1,0,5,4;,
     4;2,7,6,3;;
    
     MeshMaterialList {
      6;
      6;
      0,
      1,
      2,
      3,
      4,
      5;;
      {Matred}
      {Matred}
      {Matgr}
      {Matgr}
      {Matbr}
      {Matbr}
     }
     MeshNormals {
      6;
      0.000000;0.000000;-1.000000;,
      0.000000;0.000000;1.000000;,
      1.000000;0.000000;0.000000;,
      -1.000000;0.000000;0.000000;,
      0.000000;-1.000000;0.000000;,
      0.000000;1.000000;0.000000;;
    
      6;
      4;0,0,0,0;,
      4;1,1,1,1;,
      4;2,2,2,2;,
      4;3,3,3,3;,
      4;4,4,4,4;,
      4;5,5,5,5;;
     }
    
     MeshTextureCoords {
      8;
      0.0;0.0;
      1.0;0.0;
      1.0;1.0;
      0.0;1.0;
      0.0;0.0;
      1.0;0.0;
      1.0;1.0;
      0.0;1.0;;
     }
    }
    
  2. テクスチャファイルは Material の中の TextureFilename で指定します。
    "test.jpg", "kishi.gif" がテクスチャ画像ファイルの名前です。
    OBJ モデルと違い XFILE(DirectX) では GIF 形式は使えません。
    テストするときは適当な画像を調達してきてプロジェクトのフォルダーに格納して下さい。
    MeshTextureCoords がテクスチャ座標の定義で、頂点座標と1対1に対応しています。
    Xモデルファイルの基本的な説明は XFILE Loader の基礎 を参照して下さい。

プログラムの作成

  1. XFILE を入力してモデルを描画する main() プログラムは XFILE Loader の基礎 を参照して下さい。
  2. テクスチャをサポートしたXモデルのローダー x_loader.java です。
    //★ x_loader object class    前田 稔
    //   Texture を設定
    import java.awt.*;
    import javax.swing.*;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    import com.sun.j3d.utils.universe.*;
    import com.sun.j3d.utils.geometry.*;
    import java.io.*;
    import java.net.URL;
    import com.sun.j3d.loaders.*;
    import com.sun.j3d.utils.behaviors.mouse.*;
    import java.util.*;
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import com.sun.j3d.utils.image.TextureLoader;
    
    //★ X-File Loader Object Class
    public class x_loader extends LoaderBase
    {
        //public static final int RESIZE = 1;
        SceneBase       Base;      // SceneBase
        BufferedReader  BR;        // 入力 Reader
        String          BUF;       // 1行分の入力バッファ
        StringTokenizer token;     // トークン Object
        URL             PATH= null;// ファイルの PATH
        TransformGroup  Rot;       // 回転
    
        // 頂点座標領域
        ArrayList<Point3f>    vp = new ArrayList<Point3f>();    // 頂点座標
        ArrayList<Vector3f>   vn = new ArrayList<Vector3f>();   // 法線ベクトル
        ArrayList<TexCoord2f> vt = new ArrayList<TexCoord2f>(); // テクスチャ座標
        ArrayList<Integer>    idx = new ArrayList<Integer>();   // Index(ポリゴン)の区切り
        ArrayList<Integer>    xvp = new ArrayList<Integer>();   // 頂点 Index の並び
        ArrayList<Integer>    xvn = new ArrayList<Integer>();   // 法線 Index の並び
    
        // Material 領域
        ArrayList<String>     mid = new ArrayList<String>();    // mtbl の ID
        ArrayList<Mat>        mtbl = new ArrayList<Mat>();      // Material Table
        // MaterialList 領域
        ArrayList<Integer>    fidx = new ArrayList<Integer>();  // face Index
        ArrayList<Mat>        fmat = new ArrayList<Mat>();      // face Material
    
        // 構造体
        class  mats
        {   Color4f         faceColor;
            float           power;
            Color3f         specularColor;
            Color3f         emissiveColor;
            BufferedImage   image;
        }
    
        @Override
        public Scene load(String fname)
        {   Base = new SceneBase();
            try
            {   File    file = new File(fname);
                java.net.URI  uri = file.toURI();
                PATH = uri.toURL();
                BR = new BufferedReader(new FileReader(file));
            }
            catch(IOException e)
            {   System.out.println("File Open Error=" + fname);
                return Base;
            }
            Create();
            return Base;
        }
    
        @Override
        public Scene load(URL aURL)
        {   Base = new SceneBase();
            return Base;
        }
    
        @Override
        public Scene load(Reader reader)
        {   Base = new SceneBase();
            return Base;
        }
    
        // Scene を生成
        public void Create()
        {   Base.setSceneGroup(new BranchGroup());
    
            // 回転の初期化
            Rot = new TransformGroup();
            Transform3D rotate = new Transform3D();
            rotate.rotY(Math.PI);       // Y 軸について180度回転
            Rot.setTransform(rotate);
            Base.getSceneGroup().addChild(Rot);
    
            set_data();
            creatscene();
        }
    
        // X-File を入力して ArrayList に格納
        public void set_data()
        {   int     i,j,num,pn;
            String  str;
    
            // Header Check
            NextRead();
            str = token.nextToken();
            if ("xof".equals(str)==false)
            {   System.out.println("X-File error?" + str);
                return;
            }
            str = token.nextToken();
            if ("txt".equals(str.substring(4,7))==false)
            {   System.out.println("X-File error?" + str);
                return;
            }
    
            while(NextRead())
            {
                str = token.nextToken();
                if ("Mesh".equals(str))     // 頂点座標
                {   Skip("{");
                    num= Val();             // 頂点の数
                    for(i=0; i<num; i++)    vp.add(new Point3f(FVal(), FVal(), FVal()));
                    num= Val();             // ポリゴンの数
                    for(i=0; i<num; i++)
                    {   pn= Val();          // ポリゴンの頂点数
                        idx.add(pn);
                        for(j=0; j<pn; j++) xvp.add(Val());
                    }
                }
                if ("MeshNormals".equals(str))  // 法線ベクトル
                {   Skip("{");
                    num= Val();             // 法線の数
                    for(i=0; i<num; i++)    vn.add(new Vector3f(FVal(), FVal(), FVal()));
                    num= Val();             // 面の数
                    for(i=0; i<num; i++)
                    {   pn= Val();          // ポリゴンの頂点数
                        for(j=0; j<pn; j++) xvn.add(Val());
                    }
                }
                if ("MeshTextureCoords".equals(str))  // テクスチャ座標
                {   Skip("{");
                    num= Val();             // 座標の数
                    for(i=0; i<num; i++)    vt.add(new TexCoord2f(FVal(), FVal()));
                }
                if ("Material".equals(str)) // マテリアル
                {   str = token.nextToken();
                    if ("{".equals(str))    // ID が無い(普通は起こらない)
                    {   mtbl.add(matset());  }
                    else
                    {   mid.add(str);       // mtbl の ID
                        Skip("{");
                        mtbl.add(matset()); // mtbl に登録
                    }
                }
                if ("MeshMaterialList".equals(str)) // マテリアルリスト
                {   Skip("{");
                    num= Val();             // マテリアルの数
                    pn= Val();              // フェース(ポリゴン)の数
                    for(i=0; i<pn; i++)     fidx.add(Val());
                    for(i=0; i<num; i++)
                    {   NextRead();
                        str = token.nextToken();
                        if ("Material".equals(str))
                        {   Skip("{");          // MeshMaterialList で直接定義
                            fmat.add(matset());
                        }
                        if (str.charAt(0)=='{') // {Matgr} { Matgr }
                        {   String  wk;
                            if ("{".equals(str))    wk= token.nextToken();
                            else                    wk= str.substring(1,str.length()-1);
                            for(j=0; j<mid.size() && wk.equals(mid.get(j))==false; j++);
                            if (j<mid.size())   fmat.add(mtbl.get(j));
                            else
                            {   System.out.println("not found MaterialID=" + wk);
                                fmat.add(mtbl.get(0));  // 仮に設定
                            }
                            if ("{".equals(str))    Skip("}");
                        }
                    }
                }
            }
            Close();
        }
    
        // Material のセット
        public mats matset()
        {   String  wstr;
            mats mat = new mats();
    
            mat.faceColor = new Color4f(FVal(), FVal(), FVal(), FVal());
            mat.power = FVal();
            mat.specularColor = new Color3f(FVal(), FVal(), FVal());
            mat.emissiveColor = new Color3f(FVal(), FVal(), FVal());
            NextRead();
            wstr = token.nextToken();
            if ("TextureFilename".equals(wstr))
            {   // Texture の設定
                NextRead();
                wstr = token.nextToken();
                mat.image = loadImage(wstr);
                Skip("}");      // TextureFilename の終わり
                Skip("}");      // Material の終わり
            }
            return mat;
        }
    
        // Scene を生成
        public void creatscene()
        {   int i,j,cnt,pt,n;
    
            Point3f[] vw;                   // 頂点座標
            TexCoord2f[] tex;               // テクスチャ
            Vector3f[] nw = new Vector3f[0]; // 法線ベクトル
            pt= 0;                          // 多角形の先頭Index
            for(i=0; i<idx.size(); i++)     // i=Frame(ポリゴン)の番号
            {
                cnt= idx.get(i);            // 多角形の角数
                vw = new Point3f[cnt];      // Frame の頂点座標
                tex = new TexCoord2f[cnt];  // Frame のテクスチャ座標
                for(j=0; j<cnt; j++)
                {   n= xvp.get(pt+j);       // 頂点Index
                    if (n<vp.size())    vw[j]= vp.get(n);
                    else    System.out.print("頂点Index Error=" + n);
                    if (n<vt.size())
                    {   tex[j]= new TexCoord2f();
                        tex[j].x= vt.get(n).x;
                        tex[j].y= 1.0f-vt.get(n).y;
                    }
                    else    System.out.print("Texture Index Error=" + n);
                }
                if (0<xvn.size())
                {   nw = new Vector3f[cnt];
                    for(j=0; j<cnt; j++)
                    {   n= xvn.get(pt+j);   // 法線Index
                        if (n<vn.size())    nw[j]= vn.get(n);
                    }
                }
                if (vn.size()<1)      nw= null;
                if (vt.size()<1)      tex= null;
                n= fidx.get(i);             // マテリアルの Index
                if (n>=fmat.size())     n= 0;
                face(Rot,vw,nw,fmat.get(n),tex);
                pt+= cnt;
            }
        }
    
        // Face(Polygon) を生成
        public void face(TransformGroup trans, Point3f[] vertices, Vector3f[] normal, mats mat, TexCoord2f[] texture)
        {
            int[] stripCount = new int[1];
            Color4f[]   colors = new Color4f[vertices.length];
    
            stripCount[0] = vertices.length;
            for(int i=0; i<vertices.length; i++)    colors[i]= mat.faceColor;
            // Face を作成
            GeometryInfo ginfo = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
            ginfo.setCoordinates(vertices);
            ginfo.setStripCounts(stripCount);
            ginfo.setColors(colors);
    
            // Texture の設定
            if (mat.image!=null && texture!=null)
            {   ginfo.setTextureCoordinateParams(1,2);  // 二次元のテクスチャ座標を一枚使う
                ginfo.setTextureCoordinates(0,texture); // 参照する次元(二次元のテクスチャ=0)
            }
    
            // 法線ベクトルの設定
            if (normal!=null)   ginfo.setNormals(normal);
            else
            {   NormalGenerator gen = new NormalGenerator();
                gen.generateNormals(ginfo);
            }
    
            // Material を設定して SceneBase に追加
            Shape3D shape = new Shape3D(ginfo.getGeometryArray());
            shape.setAppearance(createAppearance(mat));
            trans.addChild(shape);
        }
    
        // Material の設定
        private Appearance createAppearance(mats para)
        {   Appearance ap = new Appearance();
            Material mat = new Material();
            Color3f     cw= new Color3f(para.faceColor.x,para.faceColor.y,para.faceColor.z);
            mat.setDiffuseColor(cw);                    // 拡散反射による色
            Color3f     acw = new Color3f(cw.x*0.2f,cw.y*0.2f,cw.z*0.2f);
            mat.setAmbientColor(acw);                   // 環境光
            mat.setShininess(para.power);               // 輝度
            mat.setSpecularColor(para.specularColor);   // 鏡面反射
            mat.setEmissiveColor(para.emissiveColor);   // 発光
            if (para.image!=null)                       // Texture の設定
            {   TextureLoader texload = new TextureLoader(para.image);
                Texture2D texture2d = (Texture2D)texload.getTexture();
                ap.setTexture(texture2d);
    
                TextureAttributes txattr = new TextureAttributes();
                txattr.setTextureMode(TextureAttributes.MODULATE);
                txattr.setTextureBlendColor(para.faceColor);
                ap.setTextureAttributes(txattr);
            }
            ap.setMaterial(mat);
            //※ 透明度の設定
            //ap.setTransparencyAttributes(new TransparencyAttributes(TransparencyAttributes.BLENDED, 0.0f));
            return ap;
        }
    
        // Line Read(BUF に入力)
        private boolean LineRead()
        {
            try
            {   BUF = BR.readLine();  }
            catch(IOException e)
            {   System.out.println(e);  }
            if (BUF == null)
            {   System.out.println("End of file");
                return false;
            }
            return true;
        }
    
        // Next Read Line(/をスキップ, Token を設定)
        private boolean NextRead()
        {
            while(LineRead())
            {   if (BUF.length()>0 && BUF.charAt(0)!='/')
                {   token = new StringTokenizer(BUF, " ,;\t", false);
                    if (token.hasMoreTokens())  return true;
                }
            }
            return false;
        }
    
        // Next Float(BUF から次の値を取得)
        private float FVal()
        {   if (token.hasMoreTokens()==false)
                if (NextRead()==false)  return -1.0f;
            return Float.parseFloat(token.nextToken());
        }
    
        // Next int(BUF から次の値を取得)
        private int Val()
        {   if (token.hasMoreTokens()==false)
                if (NextRead()==false)  return -1;
            return Integer.parseInt(token.nextToken());
        }
    
        // key まで読み飛ばす
        private void Skip(String key)
        {   String  str;
            while(true)
            {   if (token.hasMoreTokens()==false)
                    if (NextRead()==false)  return;
                str = token.nextToken();
                if (str.equals(key))    return;
            }
        }
    
        // BUFferedReader Close
        public void Close()
        {   try
            {   BR.close();  }
            catch(IOException e)
            {   System.out.println("File Close Error");  }
        }
    
        // BufferedImage の入力
        public BufferedImage loadImage(String fileName)
        {   BufferedImage img= null;
            String  wk = fileName;
            if (wk.charAt(0)=='"')    wk= fileName.substring(1,fileName.length()-1);
            try
            {   URL u = new URL(PATH, wk);
                img = ImageIO.read(u);
            }
            catch (IOException e)
            {   System.out.println("Image Open Error=" + fileName);  }
            return img;
        }
    }
    
  3. Java3D は右手座標系ですが、DirectX の既定値は左手座標系です。
    そこでY軸を中心に180度回転して、向きを合わせています。
        TransformGroup  Rot;       // 回転
            ・・・
    
            // 回転の初期化
            Rot = new TransformGroup();
            Transform3D rotate = new Transform3D();
            rotate.rotY(Math.PI);       // Y 軸について180度回転
            Rot.setTransform(rotate);
            Base.getSceneGroup().addChild(Rot);
        
  4. class mats が material を設定する構造体です。
    faceColor は face(多角形)の色です。
    power は material の輝度です。
    specularColor は material の鏡面反射です。
    emissiveColor は material の発光です。
    image はテクスチャ画像です。
        // 構造体
        class  mats
        {   Color4f         faceColor;
            float           power;
            Color3f         specularColor;
            Color3f         emissiveColor;
            BufferedImage   image;
        }
        
  5. テクスチャ関係の ArrayList を定義します。
        // 頂点座標領域
        ArrayList<TexCoord2f> vt = new ArrayList<TexCoord2f>(); // テクスチャ座標
    
        // Material 領域
        ArrayList<String>     mid = new ArrayList<String>();    // mtbl の ID
        ArrayList<Mat>        mtbl = new ArrayList<Mat>();      // Material Table
        // MaterialList 領域
        ArrayList<Integer>    fidx = new ArrayList<Integer>();  // face Index
        ArrayList<Mat>        fmat = new ArrayList<Mat>();      // face Material
        
  6. "MeshTextureCoords" でテクスチャ座標を ArrayList に登録します。
    "Material" でマテリアルを ArrayList に登録します。
    "MeshMaterialList" でマテリアルリストを ArrayList に登録します。
        if ("MeshTextureCoords".equals(str))  // テクスチャ座標
        {
             ・・・
        }
    
        if ("Material".equals(str))         // マテリアル
        {
             ・・・
        }
    
        if ("MeshMaterialList".equals(str)) // マテリアルリスト
        {
             ・・・
        }
        
  7. Scene を生成する creatscene() メソッドです。
    face() メソッドにマテリアル関係のパラメータを設定して渡します。
    Rot は Face を追加する TransformGroup です。
    vw は Face の頂点座標です。
    nw は Face の法線です。
    fmat.get(n) は Face のマテリアルです。
    tex は Face のテクスチャ座標です。
        face(Rot,vw,nw,fmat.get(n),tex);
        
    Java3D と XFILE ではテクスチャのY座標が上下逆になっていました。
        tex[j]= new TexCoord2f();
        tex[j].x= vt.get(n).x;
        tex[j].y= 1.0f-vt.get(n).y;
        
  8. face() メソッドでマテリアルとテクスチャを設定して多角形を生成します。
        // Face(Polygon) を生成
        public void face(TransformGroup trans, Point3f[] vertices, Vector3f[] normal, mats mat, TexCoord2f[] texture)
        {
             ・・・
        }
        
  9. createAppearance(() メソッドでマテリアルを設定します。
    テクスチャ画像のロードもここで行っています。
        // Material の設定
        private Appearance createAppearance(mats para)
        {
             ・・・
        }
        
    その他プログラムの説明は、これまで説明した各ページを参照して下さい。

超初心者のプログラム入門(Java2)