XFILE Loader の基礎

DirectX のモデルファイル(XFILE) を入力します。

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

Xモデルファイル

  1. Scene を生成する基本的なプログラムは Scene XTest を参照して下さい。
    まず最初に、立方体の簡単なモデル CubeBASE.x を作成してみましょう。
    XFILE は template と呼ばれる構造体のような記述に従って作成されています。
    詳細は 前田稔の超初心者のプログラム入門 から 「DirectX9/VertexBuffer のプログラム/メッシュを生成して X-FILE に保存する」や
    「DirectX10/矩形ポリゴンを X-FILE に保存する」などを参照して下さい。
    XFILE には TEXT 形式と BINARY 形式のものがありますが、ここでは非圧縮の TEXT 形式を扱います。
    XFILE の変換は 前田稔の超初心者のプログラム入門 から 「Game Program & Tool のダウンロード/Xファイルのバイナリ⇔テキスト変換」を参照して下さい。
    xof 0302txt 0064
    
    Header {
     1;
     0;
     1;
    }
    
    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,3,1,2;,
     4;4,0,2,6;,
     4;6,2,1,5;,
     4;5,1,3,7;,
     4;7,3,0,4;,
     4;4,6,5,7;;
    
     MeshNormals {
      6;
      -1.000000;0.000000;0.000000;,
      0.000000;0.000000;-1.000000;,
      0.000000;1.000000;0.000000;,
      0.000000;0.000000;1.000000;,
      0.000000;-1.000000;0.000000;,
      1.000000;0.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;;
     }
    }
    
  2. 先頭の行が XFILE のヘッダーです。
    xof がファイルの識別子で 0302 がバージョンです。
    次の txt が TEXT 形式の印で、BINARY 形式のときは bin です。
    0064 が64ビットモードで 0032 が32ビットモードのモデルです。
  3. Header はモデルのヘッダーで、1;0;1; はモデルのバージョンです。
  4. Mesh は頂点座標の定義で、8; はその数です。
    X,Y,Z の座標定義が続きます。
    6; は Face(多角形)の数です。
    頂点座標の index が続きます。
  5. MeshNormals は法線ベクトルの定義です。
    6; は法線ベクトルの数で、X成分,Y成分,Z成分が続きます。
    次の 6; は Face の法線ベクトルの index の数です。
    各頂点ごとの index が続きます。

main Program

  1. XFILE を入力してモデルを描画する main() プログラムです。
    //★ X-Model の描画テスト    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    import com.sun.j3d.utils.universe.*;
    import com.sun.j3d.loaders.*;
    import com.sun.j3d.utils.behaviors.mouse.*;
    
    public class X_Model extends JFrame
    {
        // main Method
        public static void main(String[] args)
        {   java.awt.EventQueue.invokeLater(new Runnable()
            {   public void run()
                {   new X_Model().setVisible(true);  }
            });
        }
    
        // Constructor
        public X_Model()
        {   // JFrame の初期化
            super("X Model");
            setSize(512,512);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            // Java3D 関係の設定
            GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
            Canvas3D canvas = new Canvas3D(config);
            add(canvas);
    
            // SimpleUniverseを生成
            SimpleUniverse universe = new SimpleUniverse(canvas);
            universe.getViewingPlatform().setNominalViewingTransform();
    
            // Scene を生成
            universe.addBranchGraph(CreateScene());
        }
    
        // Scene の生成
        public BranchGroup CreateScene()
        {   BranchGroup objRoot = new BranchGroup();
    
            // Light の設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(),100.0);
            DirectionalLight dlight =
              new DirectionalLight(true, new Color3f(1.0f,1.0f,1.0f), new Vector3f(0.3f,-0.3f,-0.3f));
            dlight.setInfluencingBounds(bounds);
            objRoot.addChild(dlight);
            AmbientLight alight = new AmbientLight();
            alight.setInfluencingBounds(bounds);
            objRoot.addChild(alight);
    
            // Mouse 操作の設定
            TransformGroup trans = new TransformGroup();
            SetMouse(objRoot, trans);
            objRoot.addChild(trans);
    
            // モデルの入力
            x_loader f = new x_loader();
            Scene s = null;
            s = f.load("CubeBASE.x");
            trans.addChild(s.getSceneGroup());
    
            // 背景色の設定
            Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f);
            Background bgNode = new Background(bgColor);
            bgNode.setApplicationBounds(bounds);
            objRoot.addChild(bgNode);
            return objRoot;
        }
    
        // Mouse 操作の設定
        public void SetMouse(BranchGroup objRoot, TransformGroup trans)
        {
            // Model の修正を許可
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    
            // 回転を設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
            MouseRotate rotator = new MouseRotate(trans);
            rotator.setSchedulingBounds(bounds);
            objRoot.addChild(rotator);
    
            // 移動を設定
            MouseTranslate translator = new MouseTranslate(trans);
            translator.setSchedulingBounds(bounds);
            objRoot.addChild(translator);
    
            // ズームを設定
            MouseZoom zoomer = new MouseZoom(trans);
            zoomer.setSchedulingBounds(bounds);
            objRoot.addChild(zoomer);
       }
    }
    
  2. s = f.load("CubeBASE.x"); でモデルを入力して Scene を設定します。
    load メソッドでは、モデルファイルの名前が渡されるものとして、load(String fname) だけを処理しています。
    その他の説明は、これまでのページを参照して下さい。
            // モデルの入力
            x_loader f = new x_loader();
            Scene s = null;
            s = f.load("CubeBASE.x");
            trans.addChild(s.getSceneGroup());
        

XFILE Loader

  1. XFILE のローダーです。
    //★ x_loader object class    前田 稔
    //   頂点データ+法線ベクトルを設定
    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
    {
        SceneBase       Base;      // SceneBase
        BufferedReader  BR;        // 入力 Reader
        String          BUF;       // 1行分の入力バッファ
        StringTokenizer token;     // トークン Object
    
        ArrayList<Point3f>    vp = new ArrayList<Point3f>();    // 頂点座標
        ArrayList<Vector3f>   vn = new ArrayList<Vector3f>();   // 法線ベクトル
        ArrayList<Integer>    idx = new ArrayList<Integer>();   // Index(ポリゴン)の区切り
        ArrayList<Integer>    xvp = new ArrayList<Integer>();   // 頂点 Index の並び
        ArrayList<Integer>    xvn = new ArrayList<Integer>();   // 法線 Index の並び
    
        @Override
        public Scene load(String fname)
        {
            File    file = new File(fname);
            try
            {   BR = new BufferedReader(new FileReader(file));  }
            catch(IOException e)
            {   System.out.println("File Open Error=" + file);  }
    
            set_data();
            creatscene();
            return Base;
        }
    
        @Override
        public Scene load(URL aURL)
        {   Base = new SceneBase();
            return Base;
        }
    
        @Override
        public Scene load(Reader reader)
        {   Base = new SceneBase();
            return Base;
        }
    
        // 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());
                    }
                }
            }
            Close();
        }
    
        // Scene を生成
        public void creatscene()
        {   int i,j,cnt,pt,n;
            Base = new SceneBase();
            Base.setSceneGroup(new BranchGroup());
    
            Point3f[] vw;
            Vector3f[] nw = new Vector3f[0];
            pt= 0;                          // 多角形の先頭Index
            for(i=0; i<idx.size(); i++)     // Frame(ポリゴン)の数
            {
                cnt= idx.get(i);            // 多角形の角数
                vw = new Point3f[cnt];
                for(j=0; j<cnt; j++)
                {   n= xvp.get(pt+j);       // 頂点Index
                    vw[j]= vp.get(n);
                }
                if (0<xvn.size())
                {   nw = new Vector3f[cnt];
                    for(j=0; j<cnt; j++)
                    {   n= xvn.get(pt+j);   // 法線Index
                        nw[j]= vn.get(n);
                    }
                }
                if (0<xvn.size())   face(Base,vw,nw);
                else                face(Base,vw,null);
                pt+= cnt;
            }
        }
    
        // Face(Polygon) を生成
        public void face(SceneBase base, Point3f[] vertices, Vector3f[] normal)
        {
            int[] stripCount = new int[1];
    
            stripCount[0] = vertices.length;
            // Face を作成
            GeometryInfo ginfo = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
            ginfo.setCoordinates(vertices);
            ginfo.setStripCounts(stripCount);
    
            // 法線ベクトルの設定
            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());
            base.getSceneGroup().addChild(shape);
        }
    
        // Material の設定
        private Appearance createAppearance()
        {   Appearance ap = new Appearance();
            Material mat = new Material();
            mat.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.0f));
            ap.setMaterial(mat);
            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");  }
        }
    }
    
  2. XFILE を入力して ArrayList に格納する set_data() メソッドです。
    基本的な説明は OBJ Loader の基礎 を参照して下さい。
    XFILE のヘッダーをチェックして "Mesh" と "MeshNormals" を処理して ArrayList に登録します。
        // 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());
                    }
                }
            }
            Close();
        }
        
  3. Scene を生成して立方体を格納する creatscene() メソッドです。
    XFILE のモデルを Face と呼ばれる多角形ごとに生成します。
    face(base,vw,nw); メソッドで Face を SceneBase に追加します。
    nw は法線ベクトルで、法線が定義されていないときは null を渡してプログラムで計算しています。
    法線が定義されていない有名なモデルに「Tiger.x(Tiger.bmp)」があります。
    モデルが真っ黒になり、随分悩まされたものです。 (^_^;)
    詳細は 前田稔の超初心者のプログラム入門 から 「DirectX9/マテリアルを設定して、虎のモデルを描画する」を参照して下さい。
        // Scene を生成
        public void creatscene()
        {   int i,j,cnt,pt,n;
            Base = new SceneBase();
            Base.setSceneGroup(new BranchGroup());
    
            Point3f[] vw;
            Vector3f[] nw = new Vector3f[0];
            pt= 0;                          // 多角形の先頭Index
            for(i=0; i<idx.size(); i++)     // Frame(ポリゴン)の数
            {
                cnt= idx.get(i);            // 多角形の角数
                vw = new Point3f[cnt];
                for(j=0; j<cnt; j++)
                {   n= xvp.get(pt+j);       // 頂点Index
                    vw[j]= vp.get(n);
                }
                if (0<xvn.size())
                {   nw = new Vector3f[cnt];
                    for(j=0; j<cnt; j++)
                    {   n= xvn.get(pt+j);   // 法線Index
                        nw[j]= vn.get(n);
                    }
                }
                if (0<xvn.size())   face(Base,vw,nw);
                else                face(Base,vw,null);
                pt+= cnt;
            }
        }
        

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