全コード
以上、各処理についてコードの一部を紹介してきました。以下に、今回作成したアプリケーションの全コードを紹介します。
SwingとJava3DのAPIを活用することで、360行程度の短いコードでちょっとしたツールが作れてしまうことが確認できます。
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モデルの使用など、拡張の余地はいろいろあると思います。
興味がありましたら、今回のコードをいろいろ改良してみてください。
