SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Swingを用いたJava3D用材質エディタ

Java3Dで使用する材質編集用アプリケーションをSwingを使って作成


全コード

 以上、各処理についてコードの一部を紹介してきました。以下に、今回作成したアプリケーションの全コードを紹介します。

 SwingとJava3DのAPIを活用することで、360行程度の短いコードでちょっとしたツールが作れてしまうことが確認できます。

MaterialEditor.java
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.text.*;
import javax.vecmath.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.image.*;

public class MaterialEditor extends JFrame 
    implements ActionListener, ChangeListener, AdjustmentListener {

    // レンダリングキャンバス
    private final static int RENDER_CANVAS_SIZE = 200;
    private final static int RENDER_CANVAS_BG_GRID_NUM = 8;
    
    // パラメータ識別ID
    private final static int ID_AMBIENT      = 0;
    private final static int ID_DIFFUSE      = 1;
    private final static int ID_SPECULAR     = 2;
    private final static int ID_EMISSION     = 3;
    private final static int ID_SHINIESS     = 4;
    private final static int ID_TRANSPARENCY = 5;
    
    // UI部品
    private JLabel paramNameLabels[] = new JLabel[6];
    private JButton paramButtons[] = new JButton[4];
    private JScrollBar paramScrollBars[] = new JScrollBar[2];
    private JColorChooser colorChooser;
    private JButton outputButton;
    private Color paramColors[] = { new Color(87, 61, 54),
        new Color(161, 50, 47), new Color(59, 60, 21),
        new Color(20, 17, 15)};
    private int selectedParamID = 0;
    String labelNames[] 
      = {"Ambient(環境光)", "Diffuse(拡散光)","Specular(鏡面光)",
      "Emission(放射光)", "Shiniess(輝度)", "Transparency(透明度)"};
    
    private Appearance m_appearance;
    
    // レンダリングキャンパスの作成
    private Canvas3D createCanvas() {
        GraphicsConfiguration config
        = SimpleUniverse.getPreferredConfiguration();
        Canvas3D canvas = new Canvas3D(config);
        canvas.setSize(RENDER_CANVAS_SIZE, RENDER_CANVAS_SIZE);

        SimpleUniverse universe = new SimpleUniverse(canvas);
        universe.getViewingPlatform().setNominalViewingTransform();
        universe.addBranchGraph(createBranchGraph());
        return canvas;
    }

    // BranchGroup の作成
    private BranchGroup createBranchGraph() {
        BranchGroup branchGroup = new BranchGroup();

        // 平行光源の追加
        branchGroup.addChild(createDirectionalLight());

        // 環境光の追加
        branchGroup.addChild(createAmbientLight());

        //背景イメージの追加
        branchGroup.addChild(createBackgroundImage());

        // レンダリング対象の球体を追加
        branchGroup.addChild(createSphere());
        
        branchGroup.compile();
        
        return branchGroup;
    }

    // 平行光源の作成 
    private Light createDirectionalLight(){
        DirectionalLight light = 
            new DirectionalLight( 
                true,
                new Color3f(1.0f, 1.0f, 1.0f),
                new Vector3f(-1.0f, -1.0f, -1.0f)
            );
        light.setInfluencingBounds(
            new BoundingSphere(new Point3d(), 10.0));
        return light;
    }

    // 環境光の作成
    private Light createAmbientLight(){
        AmbientLight light = new AmbientLight();
        light.setInfluencingBounds(
            new BoundingSphere(new Point3d(), 10.0));
        return light;
    }

    // 背景画像の作成
    private Background createBackgroundImage() {
        BufferedImage bufferedImage = 
            new BufferedImage(RENDER_CANVAS_SIZE, 
                              RENDER_CANVAS_SIZE, 
                              BufferedImage.TYPE_3BYTE_BGR
                             );
        Graphics2D g = bufferedImage.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, RENDER_CANVAS_SIZE, RENDER_CANVAS_SIZE);
        g.setColor(Color.black);
        int gridSize
            = RENDER_CANVAS_SIZE / RENDER_CANVAS_BG_GRID_NUM;
        for(int y = 0; y < RENDER_CANVAS_BG_GRID_NUM; y++) {
            for(int x = 0; x < RENDER_CANVAS_BG_GRID_NUM; x++) {
                if( (x + y) % 2 == 0) {
                    g.fillRect(x * gridSize, y * gridSize,
                        gridSize, gridSize);
                }
            }
        }
        Background background = new Background(
            new ImageComponent2D(
            ImageComponent2D.FORMAT_RGB, bufferedImage)
            );
        background.setApplicationBounds(
            new BoundingSphere(new Point3d(), 10.0));
        return background;
    }

    // Sphere の作成
    private Sphere createSphere() {
        // Material の作成
        Material material = new Material();
        material.setCapability(Material.ALLOW_COMPONENT_READ);
        material.setCapability(Material.ALLOW_COMPONENT_WRITE);
        material.setLightingEnable(true);
        
        // TransparencyAttributes の作成
        TransparencyAttributes tansparency = 
            new TransparencyAttributes(
                TransparencyAttributes.BLENDED, 0.0f);
        tansparency.setCapability(
            TransparencyAttributes.ALLOW_VALUE_READ );
        tansparency.setCapability(
            TransparencyAttributes.ALLOW_VALUE_WRITE );

        // Appearance の作成
        m_appearance = new Appearance();
        m_appearance.setCapability(Appearance.ALLOW_MATERIAL_READ);
        m_appearance.setCapability(Appearance.ALLOW_MATERIAL_WRITE);
        m_appearance.setCapability(
            Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ);    
        m_appearance.setCapability(
            Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);

        // Appearance に Material を設定
        m_appearance.setMaterial(material);

        // Appearance に TransparencyAttributes を設定
        m_appearance.setTransparencyAttributes(tansparency);
        
        // Sphere の作成
        Sphere sphere = new Sphere(
            0.7f, Sphere.GENERATE_NORMALS, 128, m_appearance );
        sphere.getShape()
            .setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        sphere.getShape()
            .setCapability(Shape3D.ALLOW_APPEARANCE_READ);
        
        return sphere;
    }
    
    // パラメータの選択
    private void setSelectedParamID(int id) {
        paramNameLabels[selectedParamID].setBorder(null);
        selectedParamID = id;
        paramNameLabels[selectedParamID].setBorder(
            BorderFactory.createLineBorder(Color.blue));

        colorChooser.setBorder(BorderFactory
            .createTitledBorder(labelNames[selectedParamID]));
        colorChooser.setColor(paramColors[selectedParamID]);
    }
        
    // パラメータ編集パネルの作成
    private JPanel createParamEditPanel() {
        JPanel panel = new JPanel();
        panel.setPreferredSize(
            new Dimension(300, RENDER_CANVAS_SIZE));
        panel.setLayout(new GridLayout(6, 2, 10, 5));
        
        for(int i = 0; i < 6; i++) {
            // ラベルの追加
            panel.add(paramNameLabels[i]
                = new JLabel(labelNames[i], JLabel.RIGHT));
            
            if(i < 4) {
                // ボタンの追加
                paramButtons[i] = new JButton();
                panel.add(paramButtons[i]);
                paramButtons[i].addActionListener(this);
            } else {
                // スクロールバーの追加
                int barIndex = i - 4;
                paramScrollBars[barIndex] = new JScrollBar(
                    JScrollBar.HORIZONTAL, 0, 1, 0, 100);
                paramScrollBars[barIndex]
                    .addAdjustmentListener(this);
                panel.add(paramScrollBars[barIndex]);
            }
        }

        return panel;
    }

    // GUI部品の構築
    private void buildUI() {
        // 上部パネル
        // (レンダリングキャンパス+パラメータ編集パネル)の作成
        JPanel panelNorth = new JPanel();
        panelNorth.setLayout(new FlowLayout());
        panelNorth.add(createCanvas());
        panelNorth.add(createParamEditPanel());
        
        // カラー選択パネルの作成
        JPanel panelCenter = new JPanel();
        colorChooser = new JColorChooser();
        colorChooser.getSelectionModel().addChangeListener(this);
        panelCenter.add(colorChooser); 

        // 下部パネル(コード出力ボタン)の作成
        JPanel panelSouth = new JPanel();
        panelSouth.add(outputButton = new JButton("コード出力"));
        outputButton.addActionListener(this);
        
        // 上部パネルと下部パネルをフレームに追加
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(panelNorth, BorderLayout.NORTH);
        getContentPane().add(panelCenter, BorderLayout.CENTER);
        getContentPane().add(panelSouth, BorderLayout.SOUTH);
    }    
    
    // 新しい色が選択された時の処理
    public void stateChanged(ChangeEvent e) {
        setMaterialParam(selectedParamID, colorChooser.getColor());
    }
    
    // 環境光、拡散光、鏡面光、放射光のパラメータを設定
    private void setMaterialParam(int paramID, Color c) {
        paramColors[paramID] = c;
        paramButtons[paramID].setBackground(c);

        Material material = m_appearance.getMaterial();
        switch(paramID) {
            case ID_AMBIENT:
                material.setAmbientColor(new Color3f(c));
                break;
            case ID_DIFFUSE:
                material.setDiffuseColor(new Color3f(c));
                break;
            case ID_SPECULAR:
                material.setSpecularColor(new Color3f(c));
                break;
            case ID_EMISSION:
                material.setEmissiveColor(new Color3f(c));
                break;
        }
    }

    // 輝度、透明度のパラメータを設定
    private void setMaterialParam(int paramID, float value) {
        switch(paramID) {
            case ID_SHINIESS:
                m_appearance.getMaterial().setShininess(value);
                break;
            case ID_TRANSPARENCY:
                m_appearance.getTransparencyAttributes()
                    .setTransparency(value);
                break;
        }
    }
    
    // スクロールバーが操作された時の処理
    public void adjustmentValueChanged(AdjustmentEvent e) {
        if(e.getAdjustable().equals(paramScrollBars[0])) {
            setMaterialParam(
                ID_SHINIESS, paramScrollBars[0].getValue() * 1.28f);
        } else {
            setMaterialParam(ID_TRANSPARENCY,
                paramScrollBars[1].getValue() / 100.0f);
        }
    }

    // ボタンが押された時の処理
    public void actionPerformed(ActionEvent e) {
        if(e.getSource().equals(outputButton)) {
            outputCode();
        } else {    
            for(int i = 0; i < 4; i++) {
                if(e.getSource().equals(paramButtons[i])) {
                    setSelectedParamID(i);
                    break;
                }
            }
        }
    }
    
    // Appearance 設定用コードの出力
    private void outputCode() {
        Material material = m_appearance.getMaterial();
        Color3f color = new Color3f();
        
        JFrame frame = new JFrame("Code");
        frame.setBounds( 100, 100, 500, 250);
        JTextArea textArea = new JTextArea();
        textArea.setLineWrap(true);
        DecimalFormat form = new DecimalFormat("0.0000");
        
        textArea.append("Material material = new Material();\n");
    
        material.getAmbientColor(color);
        textArea.append("material.setAmbientColor("
                         + form.format(color.x) + "f, " 
                         + form.format(color.y) + "f, " 
                         + form.format(color.z) + "f);\n");

        material.getDiffuseColor(color);
        textArea.append("material.setDiffuseColor("
                         + form.format(color.x) + "f, " 
                         + form.format(color.y) + "f, " 
                         + form.format(color.z) + "f);\n");

        material.getSpecularColor(color);
        textArea.append("material.setSpecularColor("
                         + form.format(color.x) + "f, " 
                         + form.format(color.y) + "f, " 
                         + form.format(color.z) + "f);\n");
        
        material.getEmissiveColor(color);
        textArea.append("material.setEmissiveColor("
                         + form.format(color.x) + "f, " 
                         + form.format(color.y) + "f, " 
                         + form.format(color.z) + "f);\n");
        
        float shininess = material.getShininess();
        textArea.append("material.setShininess("
                         + form.format(shininess) + "f);\n"); 

        textArea.append("TransparencyAttributes tansparency = "
             + "new TransparencyAttributes"
             + "(TransparencyAttributes.BLENDED, " 
             + form.format(m_appearance
               .getTransparencyAttributes().getTransparency()) 
             + "f);\n");
            
        textArea.append(
            "Appearance appearance = new Appearance();\n");
        textArea.append(
            "appearance.setMaterial(material);\n");
        textArea.append(
            "appearance.setTransparencyAttributes(tansparency);\n");
        
        frame.add(textArea);
        frame.setVisible(true);
    }

    // MaterialEditor のコンストラクタ
    public MaterialEditor() {
        super("MaterialEditor");
        // UI部品の構築
        buildUI();
       
        // パラメータの初期化
        setSelectedParamID(0);
        for(int i = 0; i < 4; i++) {
            setMaterialParam(i, paramColors[i]);
        }
        
        paramScrollBars[0].setValue(30);
        paramScrollBars[1].setValue(3);
        setMaterialParam(ID_SHINIESS, 128.0f * 0.3f);
        setMaterialParam(ID_TRANSPARENCY, 0.03f);
    }

    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        MaterialEditor materialEditor = new MaterialEditor();
        materialEditor
             .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        materialEditor.setBounds( 10, 10, 600, 650);
        materialEditor.setVisible(true);        
    }
}

おわりに

 今回はSwingを使用してJava3D用の材質を編集するアプリケーションを作成しました。

 3Dモデルの見え方は、物体の材質だけでなく光源の設定にも大きく依存するので、意図した結果を得るのはまだまだ難しい問題です。光源の種類、位置、色の設定やテクスチャの指定、球体以外の3Dモデルの使用など、拡張の余地はいろいろあると思います。

 興味がありましたら、今回のコードをいろいろ改良してみてください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

三谷 純(ミタニ ジュン)

Javaとの出会いは1996年にJDK1.0が登場した時までさかのぼります。それ以降、アプレットやスタンドアロンのアプリケーション、JSPを用いたサーバサイドのサービスや携帯電話で動くJavaアプリの開発など、広い範囲でJavaに関するプログラミングを行っています。拙著『独りで習うJava』は初めてJava...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/74 2006/10/10 14:53

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー