import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyVetoException; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JFileChooser; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.filechooser.FileFilter; import javax.swing.plaf.metal.DefaultMetalTheme; import javax.swing.plaf.metal.MetalLookAndFeel; import org.sunflow.Benchmark; import org.sunflow.RealtimeBenchmark; import org.sunflow.SunflowAPI; import org.sunflow.core.Display; import org.sunflow.core.Shader; import org.sunflow.core.TextureCache; import org.sunflow.core.accel.KDTree; import org.sunflow.core.display.FileDisplay; import org.sunflow.core.display.FrameDisplay; import org.sunflow.core.display.ImgPipeDisplay; import org.sunflow.core.display.OpenExrDisplay; import org.sunflow.core.primitive.TriangleMesh; import org.sunflow.core.shader.AmbientOcclusionShader; import org.sunflow.core.shader.IDShader; import org.sunflow.core.shader.NormalShader; import org.sunflow.core.shader.PrimIDShader; import org.sunflow.core.shader.QuickGrayShader; import org.sunflow.core.shader.UVShader; import org.sunflow.core.shader.WireframeShader; import org.sunflow.image.Color; import org.sunflow.system.ImagePanel; import org.sunflow.system.Timer; import org.sunflow.system.UI; import org.sunflow.system.UserInterface; import org.sunflow.system.UI.Module; import org.sunflow.system.UI.PrintLevel; @SuppressWarnings("serial") public class SunflowGUI extends javax.swing.JFrame implements UserInterface { private static final int DEFAULT_WIDTH = 1024; private static final int DEFAULT_HEIGHT = 768; private JPanel jPanel3; private JScrollPane jScrollPane1; private JMenuItem exitMenuItem; private JSeparator jSeparator2; private JPanel jPanel1; private JButton renderButton; private JMenuItem jMenuItem4; private JSeparator jSeparator1; private JMenuItem fitWindowMenuItem; private JMenuItem tileWindowMenuItem; private JSeparator jSeparator5; private JMenuItem consoleWindowMenuItem; private JMenuItem editorWindowMenuItem; private JMenuItem imageWindowMenuItem; private JMenu windowMenu; private JInternalFrame consoleFrame; private JInternalFrame editorFrame; private JInternalFrame imagePanelFrame; private JDesktopPane desktop; private JCheckBoxMenuItem smallTrianglesMenuItem; private JMenuItem textureCacheClearMenuItem; private JSeparator jSeparator4; private JMenuItem resetZoomMenuItem; private JMenu imageMenu; private ImagePanel imagePanel; private JPanel jPanel6; private JCheckBoxMenuItem clearLogMenuItem; private JPanel jPanel5; private JButton taskCancelButton; private JProgressBar taskProgressBar; private JSeparator jSeparator3; private JCheckBoxMenuItem autoBuildMenuItem; private JMenuItem iprMenuItem; private JMenuItem renderMenuItem; private JMenuItem buildMenuItem; private JMenu sceneMenu; private JTextArea editorTextArea; private JTextArea consoleTextArea; private JButton clearConsoleButton; private JPanel jPanel4; private JScrollPane jScrollPane2; private JButton iprButton; private JButton buildButton; private JMenuItem saveAsMenuItem; private JMenuItem saveMenuItem; private JMenuItem openFileMenuItem; private JMenuItem newFileMenuItem; private JMenu fileMenu; private JMenuBar jMenuBar1; // non-swing items private String currentFile; private String currentTask; private int currentTaskLastP; private SunflowAPI api; public static void usage(boolean verbose) { System.out.println("Usage: SunflowGUI [options] scenefile"); if (verbose) { System.out.println("Sunflow v" + SunflowAPI.VERSION + " textmode"); System.out.println("Renders the specified scene file"); System.out.println("Options:"); System.out.println(" -o filename Saves the output as the specified filename (png, hdr, tga)"); System.out.println(" #'s get expanded to the current frame number"); System.out.println(" -nogui Don't open the frame showing rendering progress"); System.out.println(" -ipr Render using progressive algorithm"); System.out.println(" -sampler type Render using the specified algorithm"); System.out.println(" -threads n Render using n threads"); System.out.println(" -lopri Set thread priority to low (default)"); System.out.println(" -hipri Set thread priority to high"); System.out.println(" -smallmesh Load triangle meshes using triangles optimized for memory use"); System.out.println(" -dumpkd Dump KDTree to an obj file for visualization"); System.out.println(" -buildonly Do not call render method after loading the scene"); System.out.println(" -showaa Display sampling levels per pixel for bucket renderer"); System.out.println(" -nogi Disable any global illumination engines in the scene"); System.out.println(" -nocaustics Disable any caustic engine in the scene"); System.out.println(" -pathgi n Use path tracing with n samples to render global illumination"); System.out.println(" -quick_ambocc d Applies ambient occlusion to the scene with specified maximum distance"); System.out.println(" -quick_uvs Applies a surface uv visualization shader to the scene"); System.out.println(" -quick_normals Applies a surface normal visualization shader to the scene"); System.out.println(" -quick_id Renders using a unique color for each instance"); System.out.println(" -quick_prims Renders using a unique color for each primitive"); System.out.println(" -quick_gray Renders using a plain gray diffuse shader"); System.out.println(" -quick_wire Renders using a wireframe shader"); System.out.println(" -resolution w h Changes the render resolution to the specified width and height (in pixels)"); System.out.println(" -aa min max Overrides the image anti-aliasing depths"); System.out.println(" -bucket n order Changes the default bucket size to n pixels and the default order"); System.out.println(" -bake name Bakes a lightmap for the specified instance"); System.out.println(" -bakedir dir Selects the type of lightmap baking: dir=view or ortho"); System.out.println(" -filter type Selects the image filter to use"); System.out.println(" -bench Run several built-in scenes for benchmark purposes"); System.out.println(" -rtbench Run realtime ray-tracing benchmark"); System.out.println(" -frame n Set frame number to the specified value"); System.out.println(" -anim n1 n2 Render all frames between the two specified values (inclusive)"); System.out.println(" -v verbosity Set the verbosity level: 0=none,1=errors,2=warnings,3=info,4=detailed"); System.out.println(" -h Prints this message"); } System.exit(1); } public static void main(String[] args) { if (args.length > 0) { boolean showFrame = true; String sampler = "bucket"; boolean noRender = false; String filename = null; String input = null; int i = 0; int threads = 0; boolean lowPriority = true; boolean showAA = false; boolean noGI = false; boolean noCaustics = false; int pathGI = 0; Shader shaderOverride = null; int resolutionW = 0, resolutionH = 0; int aaMin = -5, aaMax = -5; int bucketSize = 0; String bucketOrder = null; String bakingName = null; boolean bakeViewdep = false; String filterType = null; boolean runBenchmark = false; boolean runRTBenchmark = false; int frameStart = 1, frameStop = 1; while (i < args.length) { if (args[i].equals("-o")) { if (i > args.length - 2) usage(false); filename = args[i + 1]; i += 2; } else if (args[i].equals("-nogui")) { showFrame = false; i++; } else if (args[i].equals("-ipr")) { sampler = "ipr"; i++; } else if (args[i].equals("-threads")) { if (i > args.length - 2) usage(false); threads = Integer.parseInt(args[i + 1]); i += 2; } else if (args[i].equals("-lopri")) { lowPriority = true; i++; } else if (args[i].equals("-hipri")) { lowPriority = false; i++; } else if (args[i].equals("-sampler")) { if (i > args.length - 2) usage(false); sampler = args[i + 1]; i += 2; } else if (args[i].equals("-smallmesh")) { TriangleMesh.setSmallTriangles(true); i++; } else if (args[i].equals("-dumpkd")) { KDTree.setDumpMode(true, "kdtree"); i++; } else if (args[i].equals("-buildonly")) { noRender = true; i++; } else if (args[i].equals("-showaa")) { showAA = true; i++; } else if (args[i].equals("-nogi")) { noGI = true; i++; } else if (args[i].equals("-nocaustics")) { noCaustics = true; i++; } else if (args[i].equals("-pathgi")) { if (i > args.length - 2) usage(false); pathGI = Integer.parseInt(args[i + 1]); i += 2; } else if (args[i].equals("-quick_ambocc")) { if (i > args.length - 2) usage(false); float d = Float.parseFloat(args[i + 1]); shaderOverride = new AmbientOcclusionShader(Color.WHITE, d); i += 2; } else if (args[i].equals("-quick_uvs")) { if (i > args.length - 1) usage(false); shaderOverride = new UVShader(); i++; } else if (args[i].equals("-quick_normals")) { if (i > args.length - 1) usage(false); shaderOverride = new NormalShader(); i++; } else if (args[i].equals("-quick_id")) { if (i > args.length - 1) usage(false); shaderOverride = new IDShader(); i++; } else if (args[i].equals("-quick_prims")) { if (i > args.length - 1) usage(false); shaderOverride = new PrimIDShader(); i++; } else if (args[i].equals("-quick_gray")) { if (i > args.length - 1) usage(false); shaderOverride = new QuickGrayShader(); i++; } else if (args[i].equals("-quick_wire")) { if (i > args.length - 1) usage(false); shaderOverride = new WireframeShader(); i++; } else if (args[i].equals("-resolution")) { if (i > args.length - 3) usage(false); resolutionW = Integer.parseInt(args[i + 1]); resolutionH = Integer.parseInt(args[i + 2]); i += 3; } else if (args[i].equals("-aa")) { if (i > args.length - 3) usage(false); aaMin = Integer.parseInt(args[i + 1]); aaMax = Integer.parseInt(args[i + 2]); i += 3; } else if (args[i].equals("-bucket")) { if (i > args.length - 3) usage(false); bucketSize = Integer.parseInt(args[i + 1]); bucketOrder = args[i + 2]; i += 3; } else if (args[i].equals("-bake")) { if (i > args.length - 2) usage(false); bakingName = args[i + 1]; i += 2; } else if (args[i].equals("-bakedir")) { if (i > args.length - 2) usage(false); String baketype = args[i + 1]; if (baketype.equals("view")) bakeViewdep = true; else if (baketype.equals("ortho")) bakeViewdep = false; else usage(false); i += 2; } else if (args[i].equals("-filter")) { if (i > args.length - 2) usage(false); filterType = args[i + 1]; i += 2; } else if (args[i].equals("-bench")) { runBenchmark = true; i++; } else if (args[i].equals("-rtbench")) { runRTBenchmark = true; i++; } else if (args[i].equals("-frame")) { if (i > args.length - 2) usage(false); frameStart = frameStop = Integer.parseInt(args[i + 1]); i += 2; } else if (args[i].equals("-anim")) { if (i > args.length - 3) usage(false); frameStart = Integer.parseInt(args[i + 1]); frameStop = Integer.parseInt(args[i + 2]); i += 3; } else if (args[i].equals("-v")) { if (i > args.length - 2) usage(false); UI.verbosity(Integer.parseInt(args[i + 1])); i += 2; } else if (args[i].equals("-h") || args[i].equals("-help")) { usage(true); } else { if (input != null) usage(false); input = args[i]; i++; } } if (runBenchmark) { SunflowAPI.runSystemCheck(); new Benchmark().execute(); return; } if (runRTBenchmark) { SunflowAPI.runSystemCheck(); new RealtimeBenchmark(showFrame, threads); return; } if (input == null) usage(false); SunflowAPI.runSystemCheck(); if (frameStart < frameStop && showFrame) { UI.printWarning(Module.GUI, "Animations should not be rendered without -nogui - forcing GUI off anyway"); showFrame = false; } if (frameStart < frameStop && filename == null) { filename = "output.#.png"; UI.printWarning(Module.GUI, "Animation output was not specified - defaulting to: \"%s\"", filename); } for (int frameNumber = frameStart; frameNumber <= frameStop; frameNumber++) { SunflowAPI api = SunflowAPI.create(input, frameNumber); if (api == null) continue; if (noRender) continue; if (resolutionW > 0 && resolutionH > 0) { api.parameter("resolutionX", resolutionW); api.parameter("resolutionY", resolutionH); } if (aaMin != -5 || aaMax != -5) { api.parameter("aa.min", aaMin); api.parameter("aa.max", aaMax); } if (bucketSize > 0) api.parameter("bucket.size", bucketSize); if (bucketOrder != null) api.parameter("bucket.order", bucketOrder); api.parameter("aa.display", showAA); api.parameter("threads", threads); api.parameter("threads.lowPriority", lowPriority); if (bakingName != null) { api.parameter("baking.instance", bakingName); api.parameter("baking.viewdep", bakeViewdep); } if (filterType != null) api.parameter("filter", filterType); if (noGI) api.parameter("gi.engine", "none"); else if (pathGI > 0) { api.parameter("gi.engine", "path"); api.parameter("gi.path.samples", pathGI); } if (noCaustics) api.parameter("caustics", "none"); api.parameter("sampler", sampler); api.options(SunflowAPI.DEFAULT_OPTIONS); if (shaderOverride != null) { api.shader("ambocc", shaderOverride); api.shaderOverride("ambocc", true); } // create display Display display; String currentFilename = (filename != null) ? filename.replace("#", String.format("%04d", frameNumber)) : null; if (showFrame) { display = new FrameDisplay(currentFilename); } else { if (currentFilename != null && currentFilename.endsWith(".exr")) { try { display = new OpenExrDisplay(currentFilename, "zip", "float"); } catch (Exception e) { e.printStackTrace(); return; } } else if (currentFilename != null && currentFilename.equals("imgpipe")) { display = new ImgPipeDisplay(); } else display = new FileDisplay(currentFilename); } api.render(SunflowAPI.DEFAULT_OPTIONS, display); } } else { MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme()); SunflowGUI gui = new SunflowGUI(); gui.setVisible(true); Dimension screenRes = Toolkit.getDefaultToolkit().getScreenSize(); if (screenRes.getWidth() <= DEFAULT_WIDTH || screenRes.getHeight() <= DEFAULT_HEIGHT) gui.setExtendedState(MAXIMIZED_BOTH); gui.tileWindowMenuItem.doClick(); SunflowAPI.runSystemCheck(); } } public SunflowGUI() { super(); currentFile = null; api = null; initGUI(); pack(); setLocationRelativeTo(null); newFileMenuItemActionPerformed(null); UI.set(this); } private void initGUI() { setTitle("Sunflow v" + SunflowAPI.VERSION); setDefaultCloseOperation(EXIT_ON_CLOSE); { desktop = new JDesktopPane(); getContentPane().add(desktop, BorderLayout.CENTER); Dimension screenRes = Toolkit.getDefaultToolkit().getScreenSize(); if (screenRes.getWidth() <= DEFAULT_WIDTH || screenRes.getHeight() <= DEFAULT_HEIGHT) desktop.setPreferredSize(new java.awt.Dimension(640, 480)); else desktop.setPreferredSize(new java.awt.Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); { imagePanelFrame = new JInternalFrame(); desktop.add(imagePanelFrame); { jPanel1 = new JPanel(); FlowLayout jPanel1Layout = new FlowLayout(); jPanel1Layout.setAlignment(FlowLayout.LEFT); jPanel1.setLayout(jPanel1Layout); imagePanelFrame.getContentPane().add(jPanel1, BorderLayout.NORTH); { renderButton = new JButton(); jPanel1.add(renderButton); renderButton.setText("Render"); renderButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { renderMenuItemActionPerformed(evt); } }); } { iprButton = new JButton(); jPanel1.add(iprButton); iprButton.setText("IPR"); iprButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { iprMenuItemActionPerformed(evt); } }); } } { imagePanel = new ImagePanel(); imagePanelFrame.getContentPane().add(imagePanel, BorderLayout.CENTER); } imagePanelFrame.pack(); imagePanelFrame.setResizable(true); imagePanelFrame.setMaximizable(true); imagePanelFrame.setVisible(true); imagePanelFrame.setTitle("Image"); imagePanelFrame.setIconifiable(true); } { editorFrame = new JInternalFrame(); desktop.add(editorFrame); editorFrame.setTitle("Script Editor"); editorFrame.setMaximizable(true); editorFrame.setResizable(true); editorFrame.setIconifiable(true); { jScrollPane1 = new JScrollPane(); editorFrame.getContentPane().add(jScrollPane1, BorderLayout.CENTER); jScrollPane1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); jScrollPane1.setPreferredSize(new java.awt.Dimension(360, 280)); { editorTextArea = new JTextArea(); jScrollPane1.setViewportView(editorTextArea); editorTextArea.setFont(new java.awt.Font("Monospaced", 0, 12)); // drag and drop editorTextArea.setTransferHandler(new SceneTransferHandler()); } } { jPanel3 = new JPanel(); editorFrame.getContentPane().add(jPanel3, BorderLayout.SOUTH); FlowLayout jPanel3Layout = new FlowLayout(); jPanel3Layout.setAlignment(FlowLayout.RIGHT); jPanel3.setLayout(jPanel3Layout); { buildButton = new JButton(); jPanel3.add(buildButton); buildButton.setText("Build Scene"); buildButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { buildMenuItemActionPerformed(evt); } }); } } editorFrame.pack(); editorFrame.setVisible(true); } { consoleFrame = new JInternalFrame(); desktop.add(consoleFrame); consoleFrame.setIconifiable(true); consoleFrame.setMaximizable(true); consoleFrame.setResizable(true); consoleFrame.setTitle("Console"); { jScrollPane2 = new JScrollPane(); consoleFrame.getContentPane().add(jScrollPane2, BorderLayout.CENTER); jScrollPane2.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); jScrollPane2.setPreferredSize(new java.awt.Dimension(360, 100)); { consoleTextArea = new JTextArea(); jScrollPane2.setViewportView(consoleTextArea); consoleTextArea.setFont(new java.awt.Font("Monospaced", 0, 12)); consoleTextArea.setEditable(false); } } { jPanel4 = new JPanel(); consoleFrame.getContentPane().add(jPanel4, BorderLayout.SOUTH); BorderLayout jPanel4Layout = new BorderLayout(); jPanel4.setLayout(jPanel4Layout); { jPanel6 = new JPanel(); BorderLayout jPanel6Layout = new BorderLayout(); jPanel6.setLayout(jPanel6Layout); jPanel4.add(jPanel6, BorderLayout.CENTER); jPanel6.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0)); { taskProgressBar = new JProgressBar(); jPanel6.add(taskProgressBar); taskProgressBar.setEnabled(false); taskProgressBar.setString(""); taskProgressBar.setStringPainted(true); taskProgressBar.setOpaque(false); } } { jPanel5 = new JPanel(); FlowLayout jPanel5Layout = new FlowLayout(); jPanel5Layout.setAlignment(FlowLayout.RIGHT); jPanel5.setLayout(jPanel5Layout); jPanel4.add(jPanel5, BorderLayout.EAST); { taskCancelButton = new JButton(); jPanel5.add(taskCancelButton); taskCancelButton.setText("Cancel"); taskCancelButton.setEnabled(false); taskCancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { UI.taskCancel(); } }); } { clearConsoleButton = new JButton(); jPanel5.add(clearConsoleButton); clearConsoleButton.setText("Clear"); clearConsoleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { clearConsole(); } }); } } } consoleFrame.pack(); consoleFrame.setVisible(true); } } { jMenuBar1 = new JMenuBar(); setJMenuBar(jMenuBar1); { fileMenu = new JMenu(); jMenuBar1.add(fileMenu); fileMenu.setText("File"); { newFileMenuItem = new JMenuItem(); fileMenu.add(newFileMenuItem); newFileMenuItem.setText("New"); newFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl N")); newFileMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { newFileMenuItemActionPerformed(evt); } }); } { openFileMenuItem = new JMenuItem(); fileMenu.add(openFileMenuItem); openFileMenuItem.setText("Open ..."); openFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O")); openFileMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { openFileMenuItemActionPerformed(evt); } }); } { saveMenuItem = new JMenuItem(); fileMenu.add(saveMenuItem); saveMenuItem.setText("Save"); saveMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl S")); saveMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveCurrentFile(currentFile); } }); } { saveAsMenuItem = new JMenuItem(); fileMenu.add(saveAsMenuItem); saveAsMenuItem.setText("Save As ..."); saveAsMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveAsMenuItemActionPerformed(evt); } }); } { jSeparator2 = new JSeparator(); fileMenu.add(jSeparator2); } { exitMenuItem = new JMenuItem(); fileMenu.add(exitMenuItem); exitMenuItem.setText("Exit"); exitMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { System.exit(0); } }); } } { sceneMenu = new JMenu(); jMenuBar1.add(sceneMenu); sceneMenu.setText("Scene"); { buildMenuItem = new JMenuItem(); sceneMenu.add(buildMenuItem); buildMenuItem.setText("Build"); buildMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl B")); buildMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if (sceneMenu.isEnabled()) buildMenuItemActionPerformed(evt); } }); } { autoBuildMenuItem = new JCheckBoxMenuItem(); sceneMenu.add(autoBuildMenuItem); autoBuildMenuItem.setText("Build on open"); autoBuildMenuItem.setSelected(true); } { jSeparator3 = new JSeparator(); sceneMenu.add(jSeparator3); } { renderMenuItem = new JMenuItem(); sceneMenu.add(renderMenuItem); renderMenuItem.setText("Render"); renderMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { renderMenuItemActionPerformed(evt); } }); } { iprMenuItem = new JMenuItem(); sceneMenu.add(iprMenuItem); iprMenuItem.setText("IPR"); iprMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { iprMenuItemActionPerformed(evt); } }); } { clearLogMenuItem = new JCheckBoxMenuItem(); sceneMenu.add(clearLogMenuItem); clearLogMenuItem.setText("Auto Clear Log"); clearLogMenuItem.setToolTipText("Clears the console before building or rendering"); clearLogMenuItem.setSelected(true); } { jSeparator4 = new JSeparator(); sceneMenu.add(jSeparator4); } { textureCacheClearMenuItem = new JMenuItem(); sceneMenu.add(textureCacheClearMenuItem); textureCacheClearMenuItem.setText("Clear Texture Cache"); textureCacheClearMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { textureCacheClearMenuItemActionPerformed(evt); } }); } { smallTrianglesMenuItem = new JCheckBoxMenuItem(); sceneMenu.add(smallTrianglesMenuItem); smallTrianglesMenuItem.setText("Low Mem Triangles"); smallTrianglesMenuItem.setToolTipText("Load future meshes using a low memory footprint triangle representation"); smallTrianglesMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { smallTrianglesMenuItemActionPerformed(evt); } }); } } { imageMenu = new JMenu(); jMenuBar1.add(imageMenu); imageMenu.setText("Image"); { resetZoomMenuItem = new JMenuItem(); imageMenu.add(resetZoomMenuItem); resetZoomMenuItem.setText("Reset Zoom"); resetZoomMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { imagePanel.reset(); } }); } { fitWindowMenuItem = new JMenuItem(); imageMenu.add(fitWindowMenuItem); fitWindowMenuItem.setText("Fit to Window"); fitWindowMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { imagePanel.fit(); } }); } { jSeparator1 = new JSeparator(); imageMenu.add(jSeparator1); } { jMenuItem4 = new JMenuItem(); imageMenu.add(jMenuItem4); jMenuItem4.setText("Save Image ..."); jMenuItem4.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // imagePanel.image; JFileChooser fc = new JFileChooser("."); fc.setFileFilter(new FileFilter() { @Override public String getDescription() { return "Image File"; } @Override public boolean accept(File f) { return (f.isDirectory() || f.getName().endsWith(".png") || f.getName().endsWith(".tga")); } }); if (fc.showSaveDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) { String filename = fc.getSelectedFile().getAbsolutePath(); imagePanel.save(filename); } } }); } } { windowMenu = new JMenu(); jMenuBar1.add(windowMenu); windowMenu.setText("Window"); } { imageWindowMenuItem = new JMenuItem(); windowMenu.add(imageWindowMenuItem); imageWindowMenuItem.setText("Image"); imageWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 1")); imageWindowMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { selectFrame(imagePanelFrame); } }); } { editorWindowMenuItem = new JMenuItem(); windowMenu.add(editorWindowMenuItem); editorWindowMenuItem.setText("Script Editor"); editorWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 2")); editorWindowMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { selectFrame(editorFrame); } }); } { consoleWindowMenuItem = new JMenuItem(); windowMenu.add(consoleWindowMenuItem); consoleWindowMenuItem.setText("Console"); consoleWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 3")); consoleWindowMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { selectFrame(consoleFrame); } }); } { jSeparator5 = new JSeparator(); windowMenu.add(jSeparator5); } { tileWindowMenuItem = new JMenuItem(); windowMenu.add(tileWindowMenuItem); tileWindowMenuItem.setText("Tile"); tileWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl T")); tileWindowMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { tileWindowMenuItemActionPerformed(evt); } }); } } } private void newFileMenuItemActionPerformed(ActionEvent evt) { if (evt != null) { // check save? } // put some template code into the editor String template = "import org.sunflow.core.*;\nimport org.sunflow.core.accel.*;\nimport org.sunflow.core.camera.*;\nimport org.sunflow.core.primitive.*;\nimport org.sunflow.core.shader.*;\nimport org.sunflow.image.Color;\nimport org.sunflow.math.*;\n\npublic void build() {\n // your code goes here\n\n}\n"; editorTextArea.setText(template); } private void openFileMenuItemActionPerformed(ActionEvent evt) { JFileChooser fc = new JFileChooser("."); fc.setFileFilter(new FileFilter() { @Override public String getDescription() { return "Scene File"; } @Override public boolean accept(File f) { return (f.isDirectory() || f.getName().endsWith(".sc") || f.getName().endsWith(".java")); } }); if (fc.showOpenDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) { final String f = fc.getSelectedFile().getAbsolutePath(); openFile(f); } } private void buildMenuItemActionPerformed(ActionEvent evt) { new Thread() { public void run() { setEnableInterface(false); if (clearLogMenuItem.isSelected()) clearConsole(); Timer t = new Timer(); t.start(); try { api = SunflowAPI.compile(editorTextArea.getText()); } catch (NoClassDefFoundError e) { UI.printError(Module.GUI, "Janino library not found. Please check command line."); api = null; } if (api != null) { try { if (currentFile != null) { String dir = new File(currentFile).getAbsoluteFile().getParent(); api.addIncludeSearchPath(dir); api.addIncludeSearchPath(dir); } api.build(); } catch (Exception e) { UI.printError(Module.GUI, "Build terminated abnormally: %s", e.getMessage()); for (StackTraceElement elt : e.getStackTrace()) { UI.printInfo(Module.GUI, " at %s", elt.toString()); } e.printStackTrace(); } t.end(); UI.printInfo(Module.GUI, "Build time: %s", t.toString()); } setEnableInterface(true); } }.start(); } private void clearConsole() { consoleTextArea.setText(null); } private void println(final String s) { SwingUtilities.invokeLater(new Runnable() { public void run() { consoleTextArea.append(s + "\n"); } }); } private void setEnableInterface(boolean enabled) { // lock or unlock options which are unsafe during builds or renders newFileMenuItem.setEnabled(enabled); openFileMenuItem.setEnabled(enabled); saveMenuItem.setEnabled(enabled); saveAsMenuItem.setEnabled(enabled); sceneMenu.setEnabled(enabled); buildButton.setEnabled(enabled); renderButton.setEnabled(enabled); iprButton.setEnabled(enabled); } public void print(Module m, PrintLevel level, String s) { if (level == PrintLevel.ERROR) JOptionPane.showMessageDialog(SunflowGUI.this, s, String.format("Error - %s", m.name()), JOptionPane.ERROR_MESSAGE); println(UI.formatOutput(m, level, s)); } public void taskStart(String s, int min, int max) { currentTask = s; currentTaskLastP = -1; final int taskMin = min; final int taskMax = max; SwingUtilities.invokeLater(new Runnable() { public void run() { taskProgressBar.setEnabled(true); taskCancelButton.setEnabled(true); taskProgressBar.setMinimum(taskMin); taskProgressBar.setMaximum(taskMax); taskProgressBar.setValue(taskMin); taskProgressBar.setString(currentTask); } }); } public void taskUpdate(int current) { final int taskCurrent = current; final String taskString = currentTask; SwingUtilities.invokeLater(new Runnable() { public void run() { taskProgressBar.setValue(taskCurrent); int p = (int) (100.0 * taskProgressBar.getPercentComplete()); if (p > currentTaskLastP) { taskProgressBar.setString(taskString + " [" + p + "%]"); currentTaskLastP = p; } } }); } public void taskStop() { SwingUtilities.invokeLater(new Runnable() { public void run() { taskProgressBar.setValue(taskProgressBar.getMinimum()); taskProgressBar.setString(""); taskProgressBar.setEnabled(false); taskCancelButton.setEnabled(false); } }); } private void renderMenuItemActionPerformed(ActionEvent evt) { new Thread() { public void run() { setEnableInterface(false); if (clearLogMenuItem.isSelected()) clearConsole(); if (api != null) { api.parameter("sampler", "bucket"); api.options(SunflowAPI.DEFAULT_OPTIONS); api.render(SunflowAPI.DEFAULT_OPTIONS, imagePanel); } else UI.printError(Module.GUI, "Nothing to render!"); setEnableInterface(true); } }.start(); } private void iprMenuItemActionPerformed(ActionEvent evt) { new Thread() { public void run() { setEnableInterface(false); if (clearLogMenuItem.isSelected()) clearConsole(); if (api != null) { api.parameter("sampler", "ipr"); api.options(SunflowAPI.DEFAULT_OPTIONS); api.render(SunflowAPI.DEFAULT_OPTIONS, imagePanel); } else UI.printError(Module.GUI, "Nothing to IPR!"); setEnableInterface(true); } }.start(); } private void textureCacheClearMenuItemActionPerformed(ActionEvent evt) { TextureCache.flush(); } private void smallTrianglesMenuItemActionPerformed(ActionEvent evt) { TriangleMesh.setSmallTriangles(smallTrianglesMenuItem.isSelected()); } private void saveAsMenuItemActionPerformed(ActionEvent evt) { JFileChooser fc = new JFileChooser("."); fc.setFileFilter(new FileFilter() { @Override public String getDescription() { return "Scene File"; } @Override public boolean accept(File f) { return (f.isDirectory() || f.getName().endsWith(".java")); } }); if (fc.showSaveDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) { String f = fc.getSelectedFile().getAbsolutePath(); if (!f.endsWith(".java")) f += ".java"; File file = new File(f); if (!file.exists() || JOptionPane.showConfirmDialog(SunflowGUI.this, "This file already exists.\nOverwrite?", "Warning", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { // save file saveCurrentFile(f); } } } private void saveCurrentFile(String filename) { if (filename == null) { // no filename was picked, go to save as dialog saveAsMenuItemActionPerformed(null); return; } FileWriter file; try { file = new FileWriter(filename); // get text from editor pane file.write(editorTextArea.getText()); file.close(); // update current filename currentFile = filename; UI.printInfo(Module.GUI, "Saved current script to \"%s\"", filename); } catch (IOException e) { UI.printError(Module.GUI, "Unable to save: \"%s\"", filename); e.printStackTrace(); } } private void selectFrame(JInternalFrame frame) { try { frame.setSelected(true); frame.setIcon(false); } catch (PropertyVetoException e) { // this should never happen e.printStackTrace(); } } private void tileWindowMenuItemActionPerformed(ActionEvent evt) { try { if (imagePanelFrame.isIcon()) imagePanelFrame.setIcon(false); if (editorFrame.isIcon()) editorFrame.setIcon(false); if (consoleFrame.isIcon()) consoleFrame.setIcon(false); int width = desktop.getWidth(); int height = desktop.getHeight(); int widthLeft = width * 7 / 12; int widthRight = width - widthLeft; int pad = 2; int pad2 = pad + pad; imagePanelFrame.reshape(pad, pad, widthLeft - pad2, height - pad2); editorFrame.reshape(pad + widthLeft, pad, widthRight - pad2, height / 2 - pad2); consoleFrame.reshape(pad + widthLeft, pad + height / 2, widthRight - pad2, height / 2 - pad2); } catch (PropertyVetoException e) { e.printStackTrace(); } } private void openFile(String filename) { if (filename.endsWith(".java")) { // read the file line by line String code = ""; FileReader file; try { file = new FileReader(filename); BufferedReader bf = new BufferedReader(file); while (true) { String line; line = bf.readLine(); if (line == null) break; code += line; code += "\n"; } file.close(); editorTextArea.setText(code); } catch (FileNotFoundException e) { UI.printError(Module.GUI, "Unable to load: \"%s\"", filename); return; } catch (IOException e) { UI.printError(Module.GUI, "Unable to load: \"%s\"", filename); return; } // loade went ok, use filename as current currentFile = filename; UI.printInfo(Module.GUI, "Loaded script: \"%s\"", filename); } else if (filename.endsWith(".sc")) { String template = "import org.sunflow.core.*;\nimport org.sunflow.core.accel.*;\nimport org.sunflow.core.camera.*;\nimport org.sunflow.core.primitive.*;\nimport org.sunflow.core.shader.*;\nimport org.sunflow.image.Color;\nimport org.sunflow.math.*;\n\npublic void build() {\n parse(\"" + filename.replace("\\", "\\\\") + "\");\n}\n"; editorTextArea.setText(template); // no java file associated currentFile = null; UI.printInfo(Module.GUI, "Created template for \"%s\"", filename); } else { UI.printError(Module.GUI, "Unknown file format selected"); return; } editorTextArea.setCaretPosition(0); if (autoBuildMenuItem.isSelected()) { // try to compile the code we just loaded buildMenuItemActionPerformed(null); } } private class SceneTransferHandler extends TransferHandler { public boolean importData(JComponent c, Transferable t) { if (!sceneMenu.isEnabled()) return false; // can I import it? if (!canImport(c, t.getTransferDataFlavors())) { return false; } try { // get a List of Files List files = (java.util.List) t.getTransferData(DataFlavor.javaFileListFlavor); for (int i = 0; i < files.size(); i++) { final File file = (File) files.get(i); String filename = file.getAbsolutePath(); // check extension if (filename.endsWith(".sc") || filename.endsWith(".java")) { openFile(filename); // load only one file at a time, stop here break; } } } catch (Exception exp) { // debug exp.printStackTrace(); } return false; } public boolean canImport(JComponent c, DataFlavor[] flavors) { // Just a quick check to see if a file can be accepted at this time // Are there any files around? for (int i = 0; i < flavors.length; i++) { if (flavors[i].isFlavorJavaFileListType()) return true; } // guess not return false; } } }