XFILE Frame Animation

XFILE Frame Animation のプログラムです。

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

プログラムの作成

  1. XFILE Loader α版では Frame 構造とアニメーションの設定は無視していました。
    しかしながら XFILE で最も魅力があるのは、ファイルに組み込まれているアニメーション機能でしょう。
    XFILE Animation の最初のステップとして、Frame 階層構造に従って Animation するプログラムを作成してみましょう。
    最初にお断りしておきますが、DirectX のモデルは左手座標系で、Java のモデルは右手座標系です。
    また DirectX では、一体のモデルの頂点座標を操作してアニメーションするのに対して、このプログラムはパーツ毎に インタープローラを設定してアニメーションしています。
    モデルの動きの違いにこだわらないで、Frame 階層構造に従ってアニメーションする方法に注目して下さい。
    座標系の説明は Windows Guid を参照して下さい。
  2. XFILE をロードして Frame 階層構造と Animation を解析して、アニメーションを行う x_loader.java です。
    メインプログラムと同じフォルダーに置いてコンパイルして下さい。
    //★ Frame 階層に従ってアニメーションをする    前田 稔
    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
        int             FLAG;           // Loader Flag
        boolean         Anime = false;  // AnimationSet を検出
    
        // 入力関係
        URL             PATH= null;     // ファイルの PATH
        BufferedReader  BR;             // 入力 Reader
        String          BUF;            // 1行分の入力バッファ
        StringTokenizer token;          // トークン Object
        String          STR;            // トークンの領域
    
        // 頂点座標領域
        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<mats>         mtbl = new ArrayList<mats>();       // Material Table
        ArrayList<Integer>      fidx = new ArrayList<Integer>();    // face Index
        ArrayList<mats>         fmat = new ArrayList<mats>();       // face Material
    
        // Frame 構造体(Class 名は小文字)
        class  frame
        {   String              Name;       // Frame Name
            TransformGroup      F_TG;       // Frame TransformGroup
            ArrayList<frame>    Link;       // 子・兄弟のリンク
            boolean             MeshFlg;    // Mesh を格納
    
            // Constructor
            public frame(String name)
            {   Name= name;
                F_TG= new TransformGroup();
                F_TG.setTransform(new Transform3D());
                Link= new ArrayList<frame>();
                MeshFlg= false;
            }
    
            // frame(TransformGroup) を連鎖する(frm を return)
            public frame SetLink(frame frm)
            {   Link.add(frm);
                F_TG.addChild(frm.F_TG);
                return frm;
            }
        }
        frame       TOP;        // Frame 構造体の先頭
        frame       FRM;        // Frame 構造体の末尾
        frame       FWK;        // 作業用 Frame 構造体
    
        int         PNUM;       // アニメーションのパス数
        float[]     KNOTS;      // パスのウエイト
        Quat4f[]    QUATS;      // 回転のパス
        Point3f[]   PVERTS;     // 座標のパス
    
        // Material 構造体(Class 名は小文字)
        class  mats
        {   Color4f         faceColor;
            float           power;
            Color3f         specularColor;
            Color3f         emissiveColor;
            BufferedImage   image;
    
            // Constructor
            public mats()
            {   image= null;    }
        }
    
        // Constructor
        public x_loader()
        {   FLAG= 0;
        }
        public x_loader(int flag)
        {   FLAG= flag;
        }
    
        @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();
            if (PATH == null)   PATH= aURL;
            try
            {   InputStream istream = aURL.openStream();
                InputStreamReader ireader = new InputStreamReader(istream);
                BR = new BufferedReader(ireader);
            }
            catch(IOException e)
            {   System.out.println("URL Open Error" + aURL);
                return Base;
            }
            Create();
            return Base;
        }
    
        @Override
        public Scene load(Reader reader)
        {   Base = new SceneBase();
            BR = new BufferedReader(reader);
            Create();
            return Base;
        }
    
        // Scene を生成
        public void Create()
        {   Base.setSceneGroup(new BranchGroup());
    
            // サイズ Frame
            TOP=FRM= new frame("Size");
    
            // 移動 Frame
            FWK= new frame("Trans");
            FRM= FRM.SetLink(FWK);
    
            // 回転 Frame
            FWK= new frame("Rot");
            FRM= FRM.SetLink(FWK);
            Transform3D rotate = new Transform3D();
            rotate.rotY(Math.PI);   // Y 軸について180度回転
            FRM.F_TG.setTransform(rotate);
    
            Base.getSceneGroup().addChild(TOP.F_TG);
    
            Read_Xfile();
            if ((FLAG&1) == 1)      // RESIZE フラグ ON
            {   Resize();  }
        }
    
        // X-File を入力してモデルを解析
        public void Read_Xfile()
        {
            // 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;
            }
    
            MeshPath(FRM);                  // Frame, Mesh の設定
            if (Anime)  AnimePath();        // Animation の設定
            Close();
        }
    
        // Frame(及び含まれる Mesh)を解析する
        public void MeshPath(frame frm)
        {   int     i,j,num,pn;
    
            if (Anime)  return;             // AnimationSet を検出
            while(NextRead())
            {
                STR = token.nextToken();
                if ("Frame".equals(STR))    // Frame を設定
                {   Set_Frame(frm); }
    
                if ("Material".equals(STR)) // マテリアルの登録
                {   STR = token.nextToken();// STR= ID
                    if ("{".equals(STR))    // ID が無い(普通は起こらない)
                    {   mtbl.add(matset());  }
                    else
                    {   mid.add(STR);       // mtbl の ID
                        Skip("{");
                        mtbl.add(matset()); // mtbl に登録
                    }
                }
    
                if ("Mesh".equals(STR))     // 頂点座標
                {   Set_Mesh(frm);          // Frame に登録
                }
    
                if ("AnimationSet".equals(STR)) // AnimationSet
                {   Anime = true;
                    return;
                }
            }
        }
    
        // Frame を設定
        public void Set_Frame(frame frm)
        {   frame   fw= frm;
            if (Anime)  return;     // AnimationSet を検出
            STR = token.nextToken();
            FWK= new frame(STR);
            fw= frm.SetLink(FWK);
            STR = Word();
            if ("FrameTransformMatrix".equals(STR))
            {   Skip("{");
                Matrix4f mat4 = Set_Mat4();
                Transform3D trans4 = new Transform3D(mat4);
                fw.F_TG.setTransform(trans4);
                Skip("}");
                NextRead();
                STR = Word();
            }
    
            // STR == 次のキーワード
            while(true)
            {
                if ("}".equals(STR))
                {   return; }
                if ("Frame".equals(STR))
                {   Set_Frame(fw);          //★ 再帰的に呼び出す
                }
                if ("Mesh".equals(STR))     // 頂点座標
                {   Set_Mesh(fw);           // Frame に登録
                }
                if ("AnimationSet".equals(STR)) // AnimationSet
                {   Anime = true;
                    return;
                }
                NextRead();
                STR = token.nextToken();
            }
        }
    
        // Mesh 情報の収集と生成(frame に登録)
        public void Set_Mesh(frame frm)
        {   int     i,j,num,pn;
    
            frm.MeshFlg= true;
            // Mesh 情報の初期化
            vp.clear();
            vn.clear();
            vt.clear();
            idx.clear();
            xvp.clear();
            xvn.clear();
    
            // Mesh の情報
            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());
            }
    
            // 法線/テクスチャ/マテリアル
            while(NextRead())
            {
                STR = token.nextToken();
                if ("MeshNormals".equals(STR))  // 法線ベクトル
                {   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());
                    }
                    Skip("}");
                }
                if ("MeshTextureCoords".equals(STR))  // テクスチャ座標
                {   num= Val();             // 座標の数
                    for(i=0; i<num; i++)    vt.add(new TexCoord2f(FVal(), FVal()));
                    Skip("}");
                }
                if ("MeshMaterialList".equals(STR)) // マテリアルリスト
                {   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("}");
                        }
                    }
                    Skip("}");
                }
                if ("}".equals(STR))            // Mesh の終り
                {   break;  }
                if ("AnimationSet".equals(STR)) // AnimationSet
                {   Anime = true;
                    break;
                }
                if ("Form".equals(STR))         // Form
                {   break;  }
            }
            create_mesh(frm);                   // Frame に登録
        }
    
        // Set Matrix4f
        public Matrix4f Set_Mat4()
        {   Matrix4f    mat4;
            float[]     fw= new float[16];
            for(int i=0; i<16; i++) fw[i]= FVal();
            mat4 = new Matrix4f(fw[0],fw[4],fw[8],fw[12], fw[1],fw[5],fw[9],fw[13],
                                fw[2],fw[6],fw[10],fw[14], fw[3],fw[7],fw[11],fw[15]);
            return mat4;
        }
    
        // AnimationSet の解析
        public void AnimePath()
        {   TransformGroup  tgrp;
            int         num, i;
            Alpha       alpha;
            Transform3D rt3d;
            while(NextRead())
            {   STR= token.nextToken();
                if ("Animation".equals(STR))
                {   if (SetAnime()==false) return;
                    // アニメーションの設定(FWK= Animation Frame)
                    tgrp= FWK.F_TG;
                    tgrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    
                    float wf= KNOTS[PNUM-1];
                    for(i=0; i<PNUM; i++)   KNOTS[i]= KNOTS[i]/wf;
                    alpha= new Alpha(-1,(int)(wf/5));
                    rt3d= new Transform3D();
                    BoundingSphere bounds = new BoundingSphere(new Point3d(),100.0);
                    RotPosPathInterpolator interpolator=
                      new RotPosPathInterpolator(alpha,tgrp,rt3d,KNOTS,QUATS,PVERTS);
                    interpolator.setSchedulingBounds(bounds);
                    tgrp.addChild(interpolator);
                }
            }
        }
    
        // "Animation" を検出
        public boolean SetAnime()
        {   int     type,siz;
            int     i,j,k;
            float[] q4= new float[8];
    
            PNUM= -1;                   // Frame に設定されているパスの数
            Skip("{");
            NextRead();
            STR = token.nextToken();    // Frame Name
            if ("{".equals(STR))    STR= token.nextToken();
            if (STR.charAt(0)=='{') STR= STR.substring(1);
            if (STR.charAt(STR.length()-1)=='}')    STR= STR.substring(0,STR.length()-1);
    
            FWK= SrhName(TOP,STR);      // Frame 階層を STR で検索
            if (FWK==null)
            {   System.out.println("Frame が見つかりません= [" + STR + "]");
                return false;
            }
    
            // "AnimationKey" の設定
            while(NextRead())
            {   STR= token.nextToken();
                if ("AnimationKey".equals(STR))
                {   type= Val();
                    NextRead();
                    siz= Val();
                    if (AnimeInit(siz)==-1)     return false;
    
                    if (type==0)    // 回転 Animation
                    {   for(i=0; i<PNUM; i++)
                        {   KNOTS[i]= FVal();
                            k= Val();
                            for(j=0; j<4; j++)
                            {   q4[j]= FVal();  }
                            QUATS[i]= new Quat4f(q4[0],q4[1],q4[2],q4[3]);
                        }
                    }
                    if (type==2)    // 移動 Animation
                    {   for(i=0; i<PNUM; i++)
                        {   KNOTS[i]= FVal();
                            k= Val();
                            for(j=0; j<3; j++)
                            {   q4[j]= FVal();  }
                            PVERTS[i]= new Point3f(q4[0],q4[1],q4[2]);
                        }
                    }
                    Skip("}");
                }
                if ("}".equals(STR))    break;
            }
            return true;
        }
    
        // frame Name で検索
        public frame SrhName(frame frm, String nam)
        {   if (frm==null)  return null;
            if (nam.compareToIgnoreCase(frm.Name)==0)   return frm;
            for(int i=0; i<frm.Link.size(); i++)
            {   FWK= SrhName(frm.Link.get(i),nam);
                if (FWK!=null)  return FWK;
            }
            return null;
        }
    
        // Interpolator 領域の初期化
        public int AnimeInit(int siz)
        {   int     i;
            if (PNUM!=-1)       // Interpolator 設定済み
            {   if (PNUM>=siz)  return 0;
                System.out.println("PositionPath と RotationPathtype のサイズを合わせて下さい");
                return  -1;
            }
            // 初期化
            PNUM= siz;
            // Interpolator の領域を確保
            KNOTS= new float[PNUM];
            QUATS= new Quat4f[PNUM];
            PVERTS= new Point3f[PNUM];
            for(i=0; i<PNUM; i++)
            {   KNOTS[i]= 0.0f;
                QUATS[i]= new Quat4f(0.0f,0.0f,0.0f,0.0f);
                PVERTS[i]= new Point3f(0.0f,0.0f,0.0f);
            }
            return PNUM;
        }
    
        // 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;
        }
    
        // Mesh を生成して num の Frame に登録
        public void create_mesh(frame frm)
        {   int i,j,cnt,pt,n;
    
            Point3f[] vw;                   // 頂点座標
            TexCoord2f[] tex;               // テクスチャ
            Vector3f[] nw= new Vector3f[0]; // 法線ベクトル
            mats    matw= null;             // マテリアル
            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.println("頂点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;
                    }
                }
                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;
    
                if (fidx.size()>i)
                {   n= fidx.get(i);         // face Index
                    if (fmat.size()>n)  matw= fmat.get(n);
                }
                face(frm.F_TG,vw,nw,matw,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];
            Color4f     col4f;
    
            stripCount[0] = vertices.length;
            if (mat!=null)  col4f= mat.faceColor;
            else    col4f= new Color4f(1.0f,1.0f,1.0f,1.0f);
            for(int i=0; i<vertices.length; i++)    colors[i]= col4f;
            // Face を作成
            GeometryInfo ginfo = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
            ginfo.setCoordinates(vertices);
            ginfo.setStripCounts(stripCount);
            ginfo.setColors(colors);
    
            // Texture の設定
            if (texture!=null && mat!=null && mat.image!=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(TransformGroup) に追加
            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(1.0f,1.0f,1.0f);
    
            if (para!=null)
            {   cw= new Color3f(para.faceColor.x,para.faceColor.y,para.faceColor.z);
                mat.setShininess(para.power);               // 輝度
                mat.setSpecularColor(para.specularColor);   // 鏡面反射
                mat.setEmissiveColor(para.emissiveColor);   // 発光
            }
            mat.setDiffuseColor(cw);                        // 拡散反射による色
            Color3f     acw = new Color3f(cw.x*0.2f,cw.y*0.2f,cw.z*0.2f);
            mat.setAmbientColor(acw);                       // 環境光
            if (para!=null && 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);
            return ap;
        }
    
        // Model の Resize
        private void Resize()
        {   float   xmn,xmx,ymn,ymx,xrate,yrate;
            if (vp.size()==0)   return;
            xmn=xmx= vp.get(0).x;
            ymn=ymx= vp.get(0).y;
            for(int i=1; i<vp.size(); i++)
            {   if (xmn>vp.get(i).x)    xmn= vp.get(i).x;
                if (xmx<vp.get(i).x)    xmx= vp.get(i).x;
                if (ymn>vp.get(i).y)    ymn= vp.get(i).y;
                if (ymx<vp.get(i).y)    ymx= vp.get(i).y;
            }
            // 移動
            xrate= 0.0f - (xmx+xmn)/2.0f;
            yrate= 0.0f - (ymx+ymn)/2.0f;
            Transform3D t3d = new Transform3D();
            t3d.setTranslation(new Vector3d(xrate, yrate, 0.0));
            TOP.Link.get(0).F_TG.setTransform(t3d);
            // 縮小
            xrate= 1.0f/(xmx-xmn);
            yrate= 1.0f/(ymx-ymn);
            Transform3D scl = new Transform3D();
            if (xrate<yrate)    scl.setScale(xrate);
            else                scl.setScale(yrate);
            TOP.F_TG.setTransform(scl);
        }
    
        // Line Read(BUF に次の行を入力)
        private boolean LineRead()
        {
            try
            {   BUF = BR.readLine();  }
            catch(IOException e)
            {   System.out.println(e);  }
            if (BUF==null)  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;
        }
    
        // BUF にトークンが無い時は、次の行を入力('{' だけの行をスキップ)
        private String Word()
        {   String  wk;
            while(true)
            {   if (token.hasMoreTokens()==true)
                {   wk = token.nextToken();
                    if ("{".equals(wk)) continue;
                    if (wk.charAt(0)!='/')  return wk;
                }
                NextRead();
            }
        }
    
        // Next Float(BUF から次の値を取得)
        private float FVal()
        {   String  wk;
            wk = Word();
            if ("}".equals(wk))
            {   System.out.println("★Float Data の処理中に } を検出");
                return 0.0f;
            }
            return Float.parseFloat(wk);
        }
    
        // Next int(BUF から次の値を取得)
        private int Val()
        {   String  wk;
            wk = Word();
            if ("}".equals(wk))
            {   System.out.println("★Int Data の処理中に } を検出");
                return 0;
            }
            return Integer.parseInt(wk);
        }
    
        // 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. XFILE を入力してモデルを描画する main() プログラム View_Auto.java です。
    x_loader.java と同じフォルダーに置いてコンパイルして下さい。
    テストで使用する XFILE は Animation Model に掲載しています。
    //★ XFILE Frame を解析    前田 稔
    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 View_Auto extends JFrame
    {   static String  Model_name= "C:\\DATA\\XFrame\\Men4_NoHead.x";
        //static String  Model_name= "C:\\DATA\\XFrame\\Cube_rot.x";
        //static String  Model_name= "C:\\DATA\\XFrame\\BoxMen4.x";
    
        // main Method
        public static void main(String[] args)
        {   java.awt.EventQueue.invokeLater(new Runnable()
            {   public void run()
                {   new View_Auto().setVisible(true);  }
            });
        }
    
        // Constructor
        public View_Auto()
        {   // JFrame の初期化
            super("OBJ 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(x_loader.RESIZE);
            Scene s = null;
            s = f.load(Model_name);
            trans.addChild(s.getSceneGroup());
    
            // 背景色の設定
            Color3f bgColor = new Color3f(0.25f, 0.3f, 0.25f);
            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);
       }
    }
    

プログラムの説明

  1. x_loader.java の基本的な説明は X_Loader α版 を参照して下さい。
    frame がアニメーションの階層構造を定義する Frame 構造体です。
        // Frame 構造体(Class 名は小文字)
        class  frame
        {   String              Name;       // Frame Name
            TransformGroup      F_TG;       // Frame TransformGroup
            ArrayList<frame>    Link;       // 子のリンク
            boolean             MeshFlg;    // Mesh を格納
    
    Java にはポリゴンメッシュを階層構造で制御する TransformGroup の機能が備わっています。
    TransformGroup 階層を使ったモデルの描画は 親子孫関係で回転する を参照して下さい。
    F_TG で TransformGroup を定義します。
    Link は、この Frame を親とする子のリンクです。
    MeshFlg は、Frame 内で Mesh が定義されたときに true に設定します。
  2. Create() が Scene を生成するメインのメソッドです。
    モデルをサイズ調整して中央に配置し、正面から描画するために、サイズと移動と回転の Frame を生成します。
  3. Read_Xfile() メソッドが XFILE を入力してモデルを解析するメソッドです。
    ヘッダーをチェックして MeshPath(FRM) で Mesh を解析します。
    アニメーションが設定されているとき、AnimePath() で Animation を解析して SetInterpolator() で Interpolator を設定します。
  4. MeshPath(FRM) メソッドで Frame 階層と、それに含まれる Mesh を解析します。
    1. "Frame" が検出されると Set_Frame(frm) メソッドで Frame を解析して frame 構造体を生成します。
      frame 構造体は、親子孫の階層構造になっています。
    2. "Material" が検出されると mid.add(STR); で名前を登録して、matset() で Material を解析します。
      解析した Material 構造体を mtbl.add(matset()) で Material List に登録します。
      Material は Frame や Mesh とは独立した形で定義されています。
    3. "Mesh" が検出されると Set_Mesh(frm) メソッドでメッシュの情報を解析して frm に生成します。
      Mesh はアニメーションの対象となる Frame の中で定義して下さい。
    4. "AnimationSet" が検出されると MeshPath は終了します。
      これ移行アニメーションの定義が始まるのですが、モデルの記述が終わった後に追加されるような形で定義されています。
  5. AnimePath() メソッドで AnimationSet の解析を行います。
    アニメーションの定義は、モデルの記述が終わった後に定義されています。
    1. "Animation" からアニメーションの定義が始まります。
      SetAnime() メソッドでアニメーション情報を解析して Interpolator を設定します。
    2. このプログラムでは RotPosPathInterpolator を使ってアニメーションを行います。
    3. XFILE Frame Animation では、Frame に直接 Mesh を定義するので、XSkinMeshHeader や SkinWeights は関係しません。
      また Interpolator を設定するボーンの名前には、普通は Mesh を定義した Frame の名前を指定して下さい。

モデルファイル

  1. テストで使用する XFILE は Animation Model に掲載しています。
  2. このプログラム独自の制限です。
  3. アニメーションが設定されていないモデルファイルも描画することが出来ます。
    XFILE の構成は多種多様で、このプログラムでは描画出来ないものもあると思います。
  4. 座標系とアニメーションの方法にも違いがあり DirectX のビューワとは多少動きが異なります。

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