モデルのモーフィング

3種類のモデルをブレンドしながらモーフィングします。

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

プログラムの作成

  1. ピラミッドと立方体と逆ピラミッドの頂点座標をブレンドしながらモーフィングします。
    元は Sun Java3D のサンプルソース j3d\examples\morphing\ です。
    オリジナルのソースコードを尊重して作成してみました。
    //★ ピラミッドと立方体をブレンドしながらモーフィングします。
    import com.sun.j3d.utils.universe.*;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    import java.awt.GraphicsConfiguration;
    
    public class Pyramid2Cube extends javax.swing.JFrame
    {
        private SimpleUniverse univ = null;
        private BranchGroup scene = null;
        private javax.swing.JPanel drawingPanel;
    
        // Main Method です
        public static void main(String args[])
        {   java.awt.EventQueue.invokeLater(new Runnable()
            {   public void run()
                {   new Pyramid2Cube().setVisible(true);  }
            });
        }
    
        // Pyramid2Cube Class の Constructor です
        public Pyramid2Cube()
        {   initComponents();       // Components の初期化
    
            // Java3D関係の設定
            Canvas3D c = createUniverse();
            drawingPanel.add(c, java.awt.BorderLayout.CENTER);
    
            // シーンを生成します
            scene = createSceneGraph();
            univ.addBranchGraph(scene);
        }
    
        // Components を初期化します
        private void initComponents()
        {   drawingPanel = new javax.swing.JPanel();
    
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            setTitle("Pyramid2Cube");
            drawingPanel.setLayout(new java.awt.BorderLayout());
    
            drawingPanel.setPreferredSize(new java.awt.Dimension(700, 700));
            getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER);
    
            pack();
        }
    
        // Java3D関係の設定です
        private Canvas3D createUniverse()
        {   GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
            Canvas3D c = new Canvas3D(config);
    
            univ = new SimpleUniverse(c);
            univ.getViewingPlatform().setNominalViewingTransform();
    
            // 毎秒5フレーム(200Hz)で描画
            univ.getViewer().getView().setMinimumFrameCycleTime(5);
            return c;
        }
    
        // 3種類のモデルを作成して、シーンを生成します
        private BranchGroup createSceneGraph()
        {   BranchGroup objRoot = new BranchGroup();
    
            // Transformgroup でモデルのサイズを調整
            TransformGroup objScale = new TransformGroup();
            Transform3D t3d = new Transform3D();
            t3d.setScale(0.4);
            objScale.setTransform(t3d);
            objRoot.addChild(objScale);
    
            // 背景色の設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
    
            Color3f bgColor = new Color3f(0.05f, 0.05f, 0.2f);
            Background bg = new Background(bgColor);
            bg.setApplicationBounds(bounds);
            objScale.addChild(bg);
    
            // 3種類のモデルを作成
            TransformGroup objTrans[] = new TransformGroup[4];
    
            // 3種類のモデル+ブレンドしたモデルの Transform
            for(int i=0; i<4; i++)
            {   objTrans[i] = new TransformGroup();
                objScale.addChild(objTrans[i]);
            }
    
            Transform3D tr = new Transform3D();
            Transform3D rotY15 = new Transform3D();
            rotY15.rotY(15.0 * Math.PI / 180.0);
    
            // 1番目のモデルの座標
            objTrans[0].getTransform(tr);
            tr.setTranslation(new Vector3d(-3.0, 1.5, -6.5));
            tr.mul(rotY15);
            objTrans[0].setTransform(tr);
    
            // 2番目のモデルの座標
            objTrans[1].getTransform(tr);
            tr.setTranslation(new Vector3d(0.0, 1.5, -6.5));
            tr.mul(rotY15);
            objTrans[1].setTransform(tr);
    
            // 3番目のモデルの座標
            objTrans[2].getTransform(tr);
            tr.setTranslation(new Vector3d(3.0, 1.5, -6.5));
            tr.mul(rotY15);
            objTrans[2].setTransform(tr);
    
            // ブレンドしたモデルの座標
            objTrans[3].getTransform(tr);
            tr.setTranslation(new Vector3d(0.0, -2.0, -5.0));
            tr.mul(rotY15);
            objTrans[3].setTransform(tr);
    
            // 3種類のモデルを作成
            QuadArray g[] = new QuadArray[3];
            Shape3D shape[] = new Shape3D[3];
            for(int i=0; i<3; i++)
            {   g[i] = null;
                shape[i] = null;
            }
            g[0] = new ColorPyramidUp();
            g[1] = new ColorCube();
            g[2] = new ColorPyramidDown();
    
            // モデルのマテリアルを設定
            Appearance a = new Appearance();
            for(int i=0; i<3;i++)
            {   shape[i] = new Shape3D(g[i],a); 
                objTrans[i].addChild(shape[i]);
            }
    
            // モーフィングの設定
            Morph morph = new Morph((GeometryArray[]) g, a);
            morph.setCapability(Morph.ALLOW_WEIGHTS_READ);
            morph.setCapability(Morph.ALLOW_WEIGHTS_WRITE);
    
            objTrans[3].addChild(morph);
    
            // モーフィングの Alpha値を設定
            Alpha morphAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE |
                         Alpha.DECREASING_ENABLE,
                         0, 0,
                         4000, 1000, 500,
                         4000, 1000, 500);
    
            // モーフィングを objScale に登録
            MorphingBehavior mBeh = new MorphingBehavior(morphAlpha, morph);
            mBeh.setSchedulingBounds(bounds);
            objScale.addChild(mBeh);
    
            return objRoot;
        }
    }
    
  2. モデルをブレンドする MorphingBehavior.java です。
    Pyramid2Cube.java と同じフォルダーに格納して下さい。
    import java.util.Enumeration;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    
    // User-defined morphing behavior class
    public class MorphingBehavior extends Behavior
    {
        Alpha alpha;
        Morph morph;
        double weights[];
    
        WakeupOnElapsedFrames w = new WakeupOnElapsedFrames(0);
    
        // Override Behavior's initialize method to setup wakeup criteria
        public void initialize()
        {   alpha.setStartTime(System.currentTimeMillis());
    
            // Establish initial wakeup criteria
            wakeupOn(w);
        }
    
        // Override Behavior's stimulus method to handle the event
        public void processStimulus(Enumeration criteria)
        {
            // NOTE: This assumes 3 objects.  It should be generalized to "n" objects.
            double val = alpha.value();
            if (val < 0.5)
            {   double a = val * 2.0;
                weights[0] = 1.0 - a;
                weights[1] = a;
                weights[2] = 0.0;
            }
            else
            {   double a = (val - 0.5) * 2.0;
                weights[0] = 0.0;
                weights[1] = 1.0f - a;
                weights[2] = a;
            }
    
            morph.setWeights(weights);
    
            // Set wakeup criteria for next time
            wakeupOn(w);
        }
    
        public MorphingBehavior(Alpha a, Morph m)
        {   alpha = a;
            morph = m;
            weights = morph.getWeights();
        }
    }
    
  3. 3種類のモデルを生成するソースコードは 3種類のモデルを切り替える に掲載しています。
    ファイル名 説明
    ColorPyramidUp.java 正面が水色の4面体(ピラミッド)です
    ColorCube.java 正面が赤色の立方体です
    ColorPyramidDown.java正面が緑色の4面体(逆ピラミッド)です
  4. プログラムを実行すると3種類のモデルの頂点座標がブレンドされながら切り替わります。

プログラムの説明

  1. 元のソースコードを尊重して作成したので、見かけが今までとは少し違いますが、やっていることは同じです。
    3種類のモデルは、頂点の数や順番が一致するように作成されていなければなりません。
    各モデルの対応する頂点座標や色をブレンドしながら切り替えます。
    ColorPyramidUp.java を見れば解るように、4面体(ピラミッド)はもっと簡単に作成出来るのですが、立方体に合わせています。
  2. Main Method では Runnable でスレッドを起動します。
    Runnable の説明は Runnable で FPS を制御 を参照して下さい。
        // Main Method です
        public static void main(String args[])
        {   java.awt.EventQueue.invokeLater(new Runnable()
            {   public void run()
                {   new Pyramid2Cube().setVisible(true);  }
            });
        }
        
  3. createSceneGraph() メソッドで3種類のモデルを作成してシーンを生成します。
    Transformgroup でモデルのサイズを調整します。
    Background で背景色を設定します。
    3種類のモデルとブレンドしたモデルが重ならないように座標を設定します。
  4. 3種類のモデルを作成するコードです。
            // 3種類のモデルを作成
            QuadArray g[] = new QuadArray[3];
            Shape3D shape[] = new Shape3D[3];
            for(int i=0; i<3; i++)
            {   g[i] = null;
                shape[i] = null;
            }
            g[0] = new ColorPyramidUp();
            g[1] = new ColorCube();
            g[2] = new ColorPyramidDown();
        
  5. モーフィングを設定するコードです。
            // モーフィングの設定
            Morph morph = new Morph((GeometryArray[]) g, a);
            morph.setCapability(Morph.ALLOW_WEIGHTS_READ);
            morph.setCapability(Morph.ALLOW_WEIGHTS_WRITE);
    
            objTrans[3].addChild(morph);
    
            // モーフィングの Alpha値を設定
            Alpha morphAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE |
                         Alpha.DECREASING_ENABLE,
                         0, 0,
                         4000, 1000, 500,
                         4000, 1000, 500);
    
            // モーフィングを objScale に登録
            MorphingBehavior mBeh = new MorphingBehavior(morphAlpha, morph);
            mBeh.setSchedulingBounds(bounds);
            objScale.addChild(mBeh);
        
  6. あとは今までのプログラムを参照して下さい。

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