PageRenderTime 100ms CodeModel.GetById 16ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 1ms

/src/SunflowGUI.java

https://github.com/lancelet/sunflow
Java | 1252 lines | 1187 code | 39 blank | 26 comment | 207 complexity | 05548873f683fcd968a86211799e7a82 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1import java.awt.BorderLayout;
   2import java.awt.Dimension;
   3import java.awt.FlowLayout;
   4import java.awt.Toolkit;
   5import java.awt.datatransfer.DataFlavor;
   6import java.awt.datatransfer.Transferable;
   7import java.awt.event.ActionEvent;
   8import java.awt.event.ActionListener;
   9import java.beans.PropertyVetoException;
  10import java.io.BufferedReader;
  11import java.io.File;
  12import java.io.FileNotFoundException;
  13import java.io.FileReader;
  14import java.io.FileWriter;
  15import java.io.IOException;
  16import java.util.List;
  17
  18import javax.swing.BorderFactory;
  19import javax.swing.JButton;
  20import javax.swing.JCheckBoxMenuItem;
  21import javax.swing.JComponent;
  22import javax.swing.JDesktopPane;
  23import javax.swing.JFileChooser;
  24import javax.swing.JInternalFrame;
  25import javax.swing.JMenu;
  26import javax.swing.JMenuBar;
  27import javax.swing.JMenuItem;
  28import javax.swing.JOptionPane;
  29import javax.swing.JPanel;
  30import javax.swing.JProgressBar;
  31import javax.swing.JScrollPane;
  32import javax.swing.JSeparator;
  33import javax.swing.JTextArea;
  34import javax.swing.KeyStroke;
  35import javax.swing.ScrollPaneConstants;
  36import javax.swing.SwingUtilities;
  37import javax.swing.TransferHandler;
  38import javax.swing.filechooser.FileFilter;
  39import javax.swing.plaf.metal.DefaultMetalTheme;
  40import javax.swing.plaf.metal.MetalLookAndFeel;
  41
  42import org.sunflow.Benchmark;
  43import org.sunflow.RealtimeBenchmark;
  44import org.sunflow.SunflowAPI;
  45import org.sunflow.core.Display;
  46import org.sunflow.core.TextureCache;
  47import org.sunflow.core.accel.KDTree;
  48import org.sunflow.core.display.FileDisplay;
  49import org.sunflow.core.display.FrameDisplay;
  50import org.sunflow.core.display.ImgPipeDisplay;
  51import org.sunflow.core.primitive.TriangleMesh;
  52import org.sunflow.system.ImagePanel;
  53import org.sunflow.system.Timer;
  54import org.sunflow.system.UI;
  55import org.sunflow.system.UserInterface;
  56import org.sunflow.system.UI.Module;
  57import org.sunflow.system.UI.PrintLevel;
  58
  59@SuppressWarnings("serial")
  60public class SunflowGUI extends javax.swing.JFrame implements UserInterface {
  61    private static final int DEFAULT_WIDTH = 1024;
  62    private static final int DEFAULT_HEIGHT = 768;
  63    private JPanel jPanel3;
  64    private JScrollPane jScrollPane1;
  65    private JMenuItem exitMenuItem;
  66    private JSeparator jSeparator2;
  67    private JPanel jPanel1;
  68    private JButton renderButton;
  69    private JMenuItem jMenuItem4;
  70    private JSeparator jSeparator1;
  71    private JMenuItem fitWindowMenuItem;
  72    private JMenuItem tileWindowMenuItem;
  73    private JSeparator jSeparator5;
  74    private JMenuItem consoleWindowMenuItem;
  75    private JMenuItem editorWindowMenuItem;
  76    private JMenuItem imageWindowMenuItem;
  77    private JMenu windowMenu;
  78    private JInternalFrame consoleFrame;
  79    private JInternalFrame editorFrame;
  80    private JInternalFrame imagePanelFrame;
  81    private JDesktopPane desktop;
  82    private JCheckBoxMenuItem smallTrianglesMenuItem;
  83    private JMenuItem textureCacheClearMenuItem;
  84    private JSeparator jSeparator4;
  85    private JMenuItem resetZoomMenuItem;
  86    private JMenu imageMenu;
  87    private ImagePanel imagePanel;
  88    private JPanel jPanel6;
  89    private JCheckBoxMenuItem clearLogMenuItem;
  90    private JPanel jPanel5;
  91    private JButton taskCancelButton;
  92    private JProgressBar taskProgressBar;
  93    private JSeparator jSeparator3;
  94    private JCheckBoxMenuItem autoBuildMenuItem;
  95    private JMenuItem iprMenuItem;
  96    private JMenuItem renderMenuItem;
  97    private JMenuItem buildMenuItem;
  98    private JMenu sceneMenu;
  99    private JTextArea editorTextArea;
 100    private JTextArea consoleTextArea;
 101    private JButton clearConsoleButton;
 102    private JPanel jPanel4;
 103    private JScrollPane jScrollPane2;
 104    private JButton iprButton;
 105    private JButton buildButton;
 106    private JMenuItem saveAsMenuItem;
 107    private JMenuItem saveMenuItem;
 108    private JMenuItem openFileMenuItem;
 109    private JMenuItem newFileMenuItem;
 110    private JMenu fileMenu;
 111    private JMenuBar jMenuBar1;
 112
 113    // non-swing items
 114    private String currentFile;
 115    private String currentTask;
 116    private int currentTaskLastP;
 117    private SunflowAPI api;
 118    private File lastSaveDirectory;
 119
 120    public static void usage(boolean verbose) {
 121        System.out.println("Usage: SunflowGUI [options] scenefile");
 122        if (verbose) {
 123            System.out.println("Sunflow v" + SunflowAPI.VERSION + " textmode");
 124            System.out.println("Renders the specified scene file");
 125            System.out.println("Options:");
 126            System.out.println("  -o filename      Saves the output as the specified filename (png, hdr, tga)");
 127            System.out.println("                   #'s get expanded to the current frame number");
 128            System.out.println("  -nogui           Don't open the frame showing rendering progress");
 129            System.out.println("  -ipr             Render using progressive algorithm");
 130            System.out.println("  -sampler type    Render using the specified algorithm");
 131            System.out.println("  -threads n       Render using n threads");
 132            System.out.println("  -lopri           Set thread priority to low (default)");
 133            System.out.println("  -hipri           Set thread priority to high");
 134            System.out.println("  -smallmesh       Load triangle meshes using triangles optimized for memory use");
 135            System.out.println("  -dumpkd          Dump KDTree to an obj file for visualization");
 136            System.out.println("  -buildonly       Do not call render method after loading the scene");
 137            System.out.println("  -showaa          Display sampling levels per pixel for bucket renderer");
 138            System.out.println("  -nogi            Disable any global illumination engines in the scene");
 139            System.out.println("  -nocaustics      Disable any caustic engine in the scene");
 140            System.out.println("  -pathgi n        Use path tracing with n samples to render global illumination");
 141            System.out.println("  -quick_ambocc d  Applies ambient occlusion to the scene with specified maximum distance");
 142            System.out.println("  -quick_uvs       Applies a surface uv visualization shader to the scene");
 143            System.out.println("  -quick_normals   Applies a surface normal visualization shader to the scene");
 144            System.out.println("  -quick_id        Renders using a unique color for each instance");
 145            System.out.println("  -quick_prims     Renders using a unique color for each primitive");
 146            System.out.println("  -quick_gray      Renders using a plain gray diffuse shader");
 147            System.out.println("  -quick_wire      Renders using a wireframe shader");
 148            System.out.println("  -resolution w h  Changes the render resolution to the specified width and height (in pixels)");
 149            System.out.println("  -aa min max      Overrides the image anti-aliasing depths");
 150            System.out.println("  -samples n       Overrides the image sample count (affects bucket and multipass samplers)");
 151            System.out.println("  -bucket n order  Changes the default bucket size to n pixels and the default order");
 152            System.out.println("  -bake name       Bakes a lightmap for the specified instance");
 153            System.out.println("  -bakedir dir     Selects the type of lightmap baking: dir=view or ortho");
 154            System.out.println("  -filter type     Selects the image filter to use");
 155            System.out.println("  -bench           Run several built-in scenes for benchmark purposes");
 156            System.out.println("  -rtbench         Run realtime ray-tracing benchmark");
 157            System.out.println("  -frame n         Set frame number to the specified value");
 158            System.out.println("  -anim n1 n2      Render all frames between the two specified values (inclusive)");
 159            System.out.println("  -translate file  Translate input scene to the specified filename");
 160            System.out.println("  -v verbosity     Set the verbosity level: 0=none,1=errors,2=warnings,3=info,4=detailed");
 161            System.out.println("  -h               Prints this message");
 162        }
 163        System.exit(1);
 164    }
 165
 166    public static void main(String[] args) {
 167        if (args.length > 0) {
 168            boolean showFrame = true;
 169            String sampler = null;
 170            boolean noRender = false;
 171            String filename = null;
 172            String input = null;
 173            int i = 0;
 174            int threads = 0;
 175            boolean lowPriority = true;
 176            boolean showAA = false;
 177            boolean noGI = false;
 178            boolean noCaustics = false;
 179            int pathGI = 0;
 180            float maxDist = 0;
 181            String shaderOverride = null;
 182            int resolutionW = 0, resolutionH = 0; 
 183            int aaMin = -5, aaMax = -5;
 184            int samples = -1;
 185            int bucketSize = 0;
 186            String bucketOrder = null;
 187            String bakingName = null;
 188            boolean bakeViewdep = false;
 189            String filterType = null;
 190            boolean runBenchmark = false;
 191            boolean runRTBenchmark = false;
 192            String translateFilename = null;
 193            int frameStart = 1, frameStop = 1;
 194            while (i < args.length) {
 195                if (args[i].equals("-o")) {
 196                    if (i > args.length - 2)
 197                        usage(false);
 198                    filename = args[i + 1];
 199                    i += 2;
 200                } else if (args[i].equals("-nogui")) {
 201                    showFrame = false;
 202                    i++;
 203                } else if (args[i].equals("-ipr")) {
 204                    sampler = "ipr";
 205                    i++;
 206                } else if (args[i].equals("-threads")) {
 207                    if (i > args.length - 2)
 208                        usage(false);
 209                    threads = Integer.parseInt(args[i + 1]);
 210                    i += 2;
 211                } else if (args[i].equals("-lopri")) {
 212                    lowPriority = true;
 213                    i++;
 214                } else if (args[i].equals("-hipri")) {
 215                    lowPriority = false;
 216                    i++;
 217                } else if (args[i].equals("-sampler")) {
 218                    if (i > args.length - 2)
 219                        usage(false);
 220                    sampler = args[i + 1];
 221                    i += 2;
 222                } else if (args[i].equals("-smallmesh")) {
 223                    TriangleMesh.setSmallTriangles(true);
 224                    i++;
 225                } else if (args[i].equals("-dumpkd")) {
 226                    KDTree.setDumpMode(true, "kdtree");
 227                    i++;
 228                } else if (args[i].equals("-buildonly")) {
 229                    noRender = true;
 230                    i++;
 231                } else if (args[i].equals("-showaa")) {
 232                    showAA = true;
 233                    i++;
 234                } else if (args[i].equals("-nogi")) {
 235                    noGI = true;
 236                    i++;
 237                } else if (args[i].equals("-nocaustics")) {
 238                    noCaustics = true;
 239                    i++;
 240                } else if (args[i].equals("-pathgi")) {
 241                    if (i > args.length - 2)
 242                        usage(false);
 243                    pathGI = Integer.parseInt(args[i + 1]);
 244                    i += 2;
 245                } else if (args[i].equals("-quick_ambocc")) {
 246                    if (i > args.length - 2)
 247                        usage(false);
 248                    maxDist = Float.parseFloat(args[i + 1]);
 249                    shaderOverride = "ambient_occlusion"; // new
 250                    // AmbientOcclusionShader(Color.WHITE,
 251                    // d);
 252                    i += 2;
 253                } else if (args[i].equals("-quick_uvs")) {
 254                    if (i > args.length - 1)
 255                        usage(false);
 256                    shaderOverride = "show_uvs";
 257                    i++;
 258                } else if (args[i].equals("-quick_normals")) {
 259                    if (i > args.length - 1)
 260                        usage(false);
 261                    shaderOverride = "show_normals";
 262                    i++;
 263                } else if (args[i].equals("-quick_id")) {
 264                    if (i > args.length - 1)
 265                        usage(false);
 266                    shaderOverride = "show_instance_id";
 267                    i++;
 268                } else if (args[i].equals("-quick_prims")) {
 269                    if (i > args.length - 1)
 270                        usage(false);
 271                    shaderOverride = "show_primitive_id";
 272                    i++;
 273                } else if (args[i].equals("-quick_gray")) {
 274                    if (i > args.length - 1)
 275                        usage(false);
 276                    shaderOverride = "quick_gray";
 277                    i++;
 278                } else if (args[i].equals("-quick_wire")) {
 279                    if (i > args.length - 1)
 280                        usage(false);
 281                    shaderOverride = "wireframe";
 282                    i++;
 283                } else if (args[i].equals("-resolution")) {
 284                    if (i > args.length - 3)
 285                        usage(false);
 286                    resolutionW = Integer.parseInt(args[i + 1]);
 287                    resolutionH = Integer.parseInt(args[i + 2]);
 288                    i += 3;
 289                } else if (args[i].equals("-aa")) {
 290                    if (i > args.length - 3)
 291                        usage(false);
 292                    aaMin = Integer.parseInt(args[i + 1]);
 293                    aaMax = Integer.parseInt(args[i + 2]);
 294                    i += 3;
 295                } else if (args[i].equals("-samples")) {
 296                    if (i > args.length - 2)
 297                        usage(false);
 298                    samples = Integer.parseInt(args[i+1]);
 299                    i += 2;
 300                } else if (args[i].equals("-bucket")) {
 301                    if (i > args.length - 3)
 302                        usage(false);
 303                    bucketSize = Integer.parseInt(args[i + 1]);
 304                    bucketOrder = args[i + 2];
 305                    i += 3;
 306                } else if (args[i].equals("-bake")) {
 307                    if (i > args.length - 2)
 308                        usage(false);
 309                    bakingName = args[i + 1];
 310                    i += 2;
 311                } else if (args[i].equals("-bakedir")) {
 312                    if (i > args.length - 2)
 313                        usage(false);
 314                    String baketype = args[i + 1];
 315                    if (baketype.equals("view"))
 316                        bakeViewdep = true;
 317                    else if (baketype.equals("ortho"))
 318                        bakeViewdep = false;
 319                    else
 320                        usage(false);
 321                    i += 2;
 322                } else if (args[i].equals("-filter")) {
 323                    if (i > args.length - 2)
 324                        usage(false);
 325                    filterType = args[i + 1];
 326                    i += 2;
 327                } else if (args[i].equals("-bench")) {
 328                    runBenchmark = true;
 329                    i++;
 330                } else if (args[i].equals("-rtbench")) {
 331                    runRTBenchmark = true;
 332                    i++;
 333                } else if (args[i].equals("-frame")) {
 334                    if (i > args.length - 2)
 335                        usage(false);
 336                    frameStart = frameStop = Integer.parseInt(args[i + 1]);
 337                    i += 2;
 338                } else if (args[i].equals("-anim")) {
 339                    if (i > args.length - 3)
 340                        usage(false);
 341                    frameStart = Integer.parseInt(args[i + 1]);
 342                    frameStop = Integer.parseInt(args[i + 2]);
 343                    i += 3;
 344                } else if (args[i].equals("-v")) {
 345                    if (i > args.length - 2)
 346                        usage(false);
 347                    UI.verbosity(Integer.parseInt(args[i + 1]));
 348                    i += 2;
 349                } else if (args[i].equals("-translate")) {
 350                    if (i > args.length - 2)
 351                        usage(false);
 352                    translateFilename = args[i + 1];
 353                    i += 2;
 354                } else if (args[i].equals("-h") || args[i].equals("-help")) {
 355                    usage(true);
 356                } else {
 357                    if (input != null)
 358                        usage(false);
 359                    input = args[i];
 360                    i++;
 361                }
 362            }
 363            if (runBenchmark) {
 364                SunflowAPI.runSystemCheck();
 365                new Benchmark().execute();
 366                return;
 367            }
 368            if (runRTBenchmark) {
 369                SunflowAPI.runSystemCheck();
 370                new RealtimeBenchmark(showFrame, threads);
 371                return;
 372            }
 373            if (input == null)
 374                usage(false);
 375            SunflowAPI.runSystemCheck();
 376            if (translateFilename != null) {
 377                SunflowAPI.translate(input, translateFilename);
 378                return;
 379            }
 380            if (frameStart < frameStop && showFrame) {
 381                UI.printWarning(Module.GUI, "Animations should not be rendered without -nogui - forcing GUI off anyway");
 382                showFrame = false;
 383            }
 384            if (frameStart < frameStop && filename == null) {
 385                filename = "output.#.png";
 386                UI.printWarning(Module.GUI, "Animation output was not specified - defaulting to: \"%s\"", filename);
 387            }
 388            for (int frameNumber = frameStart; frameNumber <= frameStop; frameNumber++) {
 389                SunflowAPI api = SunflowAPI.create(input, frameNumber);
 390                if (api == null)
 391                    continue;
 392                if (noRender)
 393                    continue;
 394                if (resolutionW > 0 && resolutionH > 0) {
 395                    api.parameter("resolutionX", resolutionW);
 396                    api.parameter("resolutionY", resolutionH);
 397                }
 398                if (aaMin != -5 || aaMax != -5) {
 399                    api.parameter("aa.min", aaMin);
 400                    api.parameter("aa.max", aaMax);
 401                }
 402                if (samples >= 0)
 403                    api.parameter("aa.samples", samples);
 404                if (bucketSize > 0)
 405                    api.parameter("bucket.size", bucketSize);
 406                if (bucketOrder != null)
 407                    api.parameter("bucket.order", bucketOrder);
 408                api.parameter("aa.display", showAA);
 409                api.parameter("threads", threads);
 410                api.parameter("threads.lowPriority", lowPriority);
 411                if (bakingName != null) {
 412                    api.parameter("baking.instance", bakingName);
 413                    api.parameter("baking.viewdep", bakeViewdep);
 414                }
 415                if (filterType != null)
 416                    api.parameter("filter", filterType);
 417                if (noGI)
 418                    api.parameter("gi.engine", "none");
 419                else if (pathGI > 0) {
 420                    api.parameter("gi.engine", "path");
 421                    api.parameter("gi.path.samples", pathGI);
 422                }
 423                if (noCaustics)
 424                    api.parameter("caustics", "none");
 425                if (sampler != null)
 426                    api.parameter("sampler", sampler);
 427                api.options(SunflowAPI.DEFAULT_OPTIONS);
 428                if (shaderOverride != null) {
 429                    if (shaderOverride.equals("ambient_occlusion"))
 430                        api.parameter("maxdist", maxDist);
 431                    api.shader("cmdline_override", shaderOverride);
 432                    api.parameter("override.shader", "cmdline_override");
 433                    api.parameter("override.photons", true);
 434                    api.options(SunflowAPI.DEFAULT_OPTIONS);
 435                }
 436                // create display
 437                Display display;
 438                String currentFilename = (filename != null) ? filename.replace("#", String.format("%04d", frameNumber)) : null;
 439                if (showFrame) {
 440                    display = new FrameDisplay(currentFilename);
 441                } else {
 442                    if (currentFilename != null && currentFilename.equals("imgpipe")) {
 443                        display = new ImgPipeDisplay();
 444                    } else
 445                        display = new FileDisplay(currentFilename);
 446                }
 447                api.render(SunflowAPI.DEFAULT_OPTIONS, display);
 448            }
 449        } else {
 450            MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
 451            SunflowGUI gui = new SunflowGUI();
 452            gui.setVisible(true);
 453            Dimension screenRes = Toolkit.getDefaultToolkit().getScreenSize();
 454            if (screenRes.getWidth() <= DEFAULT_WIDTH || screenRes.getHeight() <= DEFAULT_HEIGHT)
 455                gui.setExtendedState(MAXIMIZED_BOTH);
 456            gui.tileWindowMenuItem.doClick();
 457            SunflowAPI.runSystemCheck();
 458        }
 459    }
 460
 461    public SunflowGUI() {
 462        super();
 463        currentFile = null;
 464        lastSaveDirectory = null;
 465        api = null;
 466        initGUI();
 467        pack();
 468        setLocationRelativeTo(null);
 469        newFileMenuItemActionPerformed(null);
 470        UI.set(this);
 471    }
 472
 473    private void initGUI() {
 474        setTitle("Sunflow v" + SunflowAPI.VERSION);
 475        setDefaultCloseOperation(EXIT_ON_CLOSE);
 476        {
 477            desktop = new JDesktopPane();
 478            getContentPane().add(desktop, BorderLayout.CENTER);
 479            Dimension screenRes = Toolkit.getDefaultToolkit().getScreenSize();
 480            if (screenRes.getWidth() <= DEFAULT_WIDTH || screenRes.getHeight() <= DEFAULT_HEIGHT)
 481                desktop.setPreferredSize(new java.awt.Dimension(640, 480));
 482            else
 483                desktop.setPreferredSize(new java.awt.Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
 484            {
 485                imagePanelFrame = new JInternalFrame();
 486                desktop.add(imagePanelFrame);
 487                {
 488                    jPanel1 = new JPanel();
 489                    FlowLayout jPanel1Layout = new FlowLayout();
 490                    jPanel1Layout.setAlignment(FlowLayout.LEFT);
 491                    jPanel1.setLayout(jPanel1Layout);
 492                    imagePanelFrame.getContentPane().add(jPanel1, BorderLayout.NORTH);
 493                    {
 494                        renderButton = new JButton();
 495                        jPanel1.add(renderButton);
 496                        renderButton.setText("Render");
 497                        renderButton.addActionListener(new ActionListener() {
 498                            public void actionPerformed(ActionEvent evt) {
 499                                renderMenuItemActionPerformed(evt);
 500                            }
 501                        });
 502                    }
 503                    {
 504                        iprButton = new JButton();
 505                        jPanel1.add(iprButton);
 506                        iprButton.setText("IPR");
 507                        iprButton.addActionListener(new ActionListener() {
 508                            public void actionPerformed(ActionEvent evt) {
 509                                iprMenuItemActionPerformed(evt);
 510                            }
 511                        });
 512                    }
 513                }
 514                {
 515                    imagePanel = new ImagePanel();
 516                    imagePanelFrame.getContentPane().add(imagePanel, BorderLayout.CENTER);
 517                }
 518                imagePanelFrame.pack();
 519                imagePanelFrame.setResizable(true);
 520                imagePanelFrame.setMaximizable(true);
 521                imagePanelFrame.setVisible(true);
 522                imagePanelFrame.setTitle("Image");
 523                imagePanelFrame.setIconifiable(true);
 524            }
 525            {
 526                editorFrame = new JInternalFrame();
 527                desktop.add(editorFrame);
 528                editorFrame.setTitle("Script Editor");
 529                editorFrame.setMaximizable(true);
 530                editorFrame.setResizable(true);
 531                editorFrame.setIconifiable(true);
 532                {
 533                    jScrollPane1 = new JScrollPane();
 534                    editorFrame.getContentPane().add(jScrollPane1, BorderLayout.CENTER);
 535                    jScrollPane1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 536                    jScrollPane1.setPreferredSize(new java.awt.Dimension(360, 280));
 537                    {
 538                        editorTextArea = new JTextArea();
 539                        jScrollPane1.setViewportView(editorTextArea);
 540                        editorTextArea.setFont(new java.awt.Font("Monospaced", 0, 12));
 541                        // drag and drop
 542                        editorTextArea.setTransferHandler(new SceneTransferHandler());
 543                    }
 544                }
 545                {
 546                    jPanel3 = new JPanel();
 547                    editorFrame.getContentPane().add(jPanel3, BorderLayout.SOUTH);
 548                    FlowLayout jPanel3Layout = new FlowLayout();
 549                    jPanel3Layout.setAlignment(FlowLayout.RIGHT);
 550                    jPanel3.setLayout(jPanel3Layout);
 551                    {
 552                        buildButton = new JButton();
 553                        jPanel3.add(buildButton);
 554                        buildButton.setText("Build Scene");
 555                        buildButton.addActionListener(new ActionListener() {
 556                            public void actionPerformed(ActionEvent evt) {
 557                                buildMenuItemActionPerformed(evt);
 558                            }
 559                        });
 560                    }
 561                }
 562                editorFrame.pack();
 563                editorFrame.setVisible(true);
 564            }
 565            {
 566                consoleFrame = new JInternalFrame();
 567                desktop.add(consoleFrame);
 568                consoleFrame.setIconifiable(true);
 569                consoleFrame.setMaximizable(true);
 570                consoleFrame.setResizable(true);
 571                consoleFrame.setTitle("Console");
 572                {
 573                    jScrollPane2 = new JScrollPane();
 574                    consoleFrame.getContentPane().add(jScrollPane2, BorderLayout.CENTER);
 575                    jScrollPane2.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 576                    jScrollPane2.setPreferredSize(new java.awt.Dimension(360, 100));
 577                    {
 578                        consoleTextArea = new JTextArea();
 579                        jScrollPane2.setViewportView(consoleTextArea);
 580                        consoleTextArea.setFont(new java.awt.Font("Monospaced", 0, 12));
 581                        consoleTextArea.setEditable(false);
 582                    }
 583                }
 584                {
 585                    jPanel4 = new JPanel();
 586                    consoleFrame.getContentPane().add(jPanel4, BorderLayout.SOUTH);
 587                    BorderLayout jPanel4Layout = new BorderLayout();
 588                    jPanel4.setLayout(jPanel4Layout);
 589                    {
 590                        jPanel6 = new JPanel();
 591                        BorderLayout jPanel6Layout = new BorderLayout();
 592                        jPanel6.setLayout(jPanel6Layout);
 593                        jPanel4.add(jPanel6, BorderLayout.CENTER);
 594                        jPanel6.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
 595                        {
 596                            taskProgressBar = new JProgressBar();
 597                            jPanel6.add(taskProgressBar);
 598                            taskProgressBar.setEnabled(false);
 599                            taskProgressBar.setString("");
 600                            taskProgressBar.setStringPainted(true);
 601                            taskProgressBar.setOpaque(false);
 602                        }
 603                    }
 604                    {
 605                        jPanel5 = new JPanel();
 606                        FlowLayout jPanel5Layout = new FlowLayout();
 607                        jPanel5Layout.setAlignment(FlowLayout.RIGHT);
 608                        jPanel5.setLayout(jPanel5Layout);
 609                        jPanel4.add(jPanel5, BorderLayout.EAST);
 610                        {
 611                            taskCancelButton = new JButton();
 612                            jPanel5.add(taskCancelButton);
 613                            taskCancelButton.setText("Cancel");
 614                            taskCancelButton.setEnabled(false);
 615                            taskCancelButton.addActionListener(new ActionListener() {
 616                                public void actionPerformed(ActionEvent evt) {
 617                                    UI.taskCancel();
 618                                }
 619                            });
 620                        }
 621                        {
 622                            clearConsoleButton = new JButton();
 623                            jPanel5.add(clearConsoleButton);
 624                            clearConsoleButton.setText("Clear");
 625                            clearConsoleButton.addActionListener(new ActionListener() {
 626                                public void actionPerformed(ActionEvent evt) {
 627                                    clearConsole();
 628                                }
 629                            });
 630                        }
 631                    }
 632                }
 633                consoleFrame.pack();
 634                consoleFrame.setVisible(true);
 635            }
 636        }
 637        {
 638            jMenuBar1 = new JMenuBar();
 639            setJMenuBar(jMenuBar1);
 640            {
 641                fileMenu = new JMenu();
 642                jMenuBar1.add(fileMenu);
 643                fileMenu.setText("File");
 644                {
 645                    newFileMenuItem = new JMenuItem();
 646                    fileMenu.add(newFileMenuItem);
 647                    newFileMenuItem.setText("New");
 648                    newFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl N"));
 649                    newFileMenuItem.addActionListener(new ActionListener() {
 650                        public void actionPerformed(ActionEvent evt) {
 651                            newFileMenuItemActionPerformed(evt);
 652                        }
 653                    });
 654                }
 655                {
 656                    openFileMenuItem = new JMenuItem();
 657                    fileMenu.add(openFileMenuItem);
 658                    openFileMenuItem.setText("Open ...");
 659                    openFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));
 660                    openFileMenuItem.addActionListener(new ActionListener() {
 661                        public void actionPerformed(ActionEvent evt) {
 662                            openFileMenuItemActionPerformed(evt);
 663                        }
 664                    });
 665                }
 666                {
 667                    saveMenuItem = new JMenuItem();
 668                    fileMenu.add(saveMenuItem);
 669                    saveMenuItem.setText("Save");
 670                    saveMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl S"));
 671                    saveMenuItem.addActionListener(new ActionListener() {
 672                        public void actionPerformed(ActionEvent evt) {
 673                            saveCurrentFile(currentFile);
 674                        }
 675                    });
 676                }
 677                {
 678                    saveAsMenuItem = new JMenuItem();
 679                    fileMenu.add(saveAsMenuItem);
 680                    saveAsMenuItem.setText("Save As ...");
 681                    saveAsMenuItem.addActionListener(new ActionListener() {
 682                        public void actionPerformed(ActionEvent evt) {
 683                            saveAsMenuItemActionPerformed(evt);
 684                        }
 685                    });
 686                }
 687                {
 688                    jSeparator2 = new JSeparator();
 689                    fileMenu.add(jSeparator2);
 690                }
 691                {
 692                    exitMenuItem = new JMenuItem();
 693                    fileMenu.add(exitMenuItem);
 694                    exitMenuItem.setText("Exit");
 695                    exitMenuItem.addActionListener(new ActionListener() {
 696                        public void actionPerformed(ActionEvent evt) {
 697                            System.exit(0);
 698                        }
 699                    });
 700                }
 701            }
 702            {
 703                sceneMenu = new JMenu();
 704                jMenuBar1.add(sceneMenu);
 705                sceneMenu.setText("Scene");
 706                {
 707                    buildMenuItem = new JMenuItem();
 708                    sceneMenu.add(buildMenuItem);
 709                    buildMenuItem.setText("Build");
 710                    buildMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl B"));
 711                    buildMenuItem.addActionListener(new ActionListener() {
 712                        public void actionPerformed(ActionEvent evt) {
 713                            if (sceneMenu.isEnabled())
 714                                buildMenuItemActionPerformed(evt);
 715                        }
 716                    });
 717                }
 718                {
 719                    autoBuildMenuItem = new JCheckBoxMenuItem();
 720                    sceneMenu.add(autoBuildMenuItem);
 721                    autoBuildMenuItem.setText("Build on open");
 722                    autoBuildMenuItem.setSelected(true);
 723                }
 724                {
 725                    jSeparator3 = new JSeparator();
 726                    sceneMenu.add(jSeparator3);
 727                }
 728                {
 729                    renderMenuItem = new JMenuItem();
 730                    sceneMenu.add(renderMenuItem);
 731                    renderMenuItem.setText("Render");
 732                    renderMenuItem.addActionListener(new ActionListener() {
 733                        public void actionPerformed(ActionEvent evt) {
 734                            renderMenuItemActionPerformed(evt);
 735                        }
 736                    });
 737                }
 738                {
 739                    iprMenuItem = new JMenuItem();
 740                    sceneMenu.add(iprMenuItem);
 741                    iprMenuItem.setText("IPR");
 742                    iprMenuItem.addActionListener(new ActionListener() {
 743                        public void actionPerformed(ActionEvent evt) {
 744                            iprMenuItemActionPerformed(evt);
 745                        }
 746                    });
 747                }
 748                {
 749                    clearLogMenuItem = new JCheckBoxMenuItem();
 750                    sceneMenu.add(clearLogMenuItem);
 751                    clearLogMenuItem.setText("Auto Clear Log");
 752                    clearLogMenuItem.setToolTipText("Clears the console before building or rendering");
 753                    clearLogMenuItem.setSelected(true);
 754                }
 755                {
 756                    jSeparator4 = new JSeparator();
 757                    sceneMenu.add(jSeparator4);
 758                }
 759                {
 760                    textureCacheClearMenuItem = new JMenuItem();
 761                    sceneMenu.add(textureCacheClearMenuItem);
 762                    textureCacheClearMenuItem.setText("Clear Texture Cache");
 763                    textureCacheClearMenuItem.addActionListener(new ActionListener() {
 764                        public void actionPerformed(ActionEvent evt) {
 765                            textureCacheClearMenuItemActionPerformed(evt);
 766                        }
 767                    });
 768                }
 769                {
 770                    smallTrianglesMenuItem = new JCheckBoxMenuItem();
 771                    sceneMenu.add(smallTrianglesMenuItem);
 772                    smallTrianglesMenuItem.setText("Low Mem Triangles");
 773                    smallTrianglesMenuItem.setToolTipText("Load future meshes using a low memory footprint triangle representation");
 774                    smallTrianglesMenuItem.addActionListener(new ActionListener() {
 775                        public void actionPerformed(ActionEvent evt) {
 776                            smallTrianglesMenuItemActionPerformed(evt);
 777                        }
 778                    });
 779                }
 780            }
 781            {
 782                imageMenu = new JMenu();
 783                jMenuBar1.add(imageMenu);
 784                imageMenu.setText("Image");
 785                {
 786                    resetZoomMenuItem = new JMenuItem();
 787                    imageMenu.add(resetZoomMenuItem);
 788                    resetZoomMenuItem.setText("Reset Zoom");
 789                    resetZoomMenuItem.addActionListener(new ActionListener() {
 790                        public void actionPerformed(ActionEvent evt) {
 791                            imagePanel.reset();
 792                        }
 793                    });
 794                }
 795                {
 796                    fitWindowMenuItem = new JMenuItem();
 797                    imageMenu.add(fitWindowMenuItem);
 798                    fitWindowMenuItem.setText("Fit to Window");
 799                    fitWindowMenuItem.addActionListener(new ActionListener() {
 800                        public void actionPerformed(ActionEvent evt) {
 801                            imagePanel.fit();
 802                        }
 803                    });
 804                }
 805                {
 806                    jSeparator1 = new JSeparator();
 807                    imageMenu.add(jSeparator1);
 808                }
 809                {
 810                    jMenuItem4 = new JMenuItem();
 811                    imageMenu.add(jMenuItem4);
 812                    jMenuItem4.setText("Save Image ...");
 813                    jMenuItem4.addActionListener(new ActionListener() {
 814                        public void actionPerformed(ActionEvent evt) {
 815                            // imagePanel.image;
 816                            JFileChooser fc = new JFileChooser(".");
 817                            fc.setFileFilter(new FileFilter() {
 818                                @Override
 819                                public String getDescription() {
 820                                    return "Image File";
 821                                }
 822
 823                                @Override
 824                                public boolean accept(File f) {
 825                                    return (f.isDirectory() || f.getName().endsWith(".png") || f.getName().endsWith(".tga"));
 826                                }
 827                            });
 828                            if (fc.showSaveDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) {
 829                                String filename = fc.getSelectedFile().getAbsolutePath();
 830                                imagePanel.save(filename);
 831                            }
 832                        }
 833                    });
 834                }
 835            }
 836            {
 837                windowMenu = new JMenu();
 838                jMenuBar1.add(windowMenu);
 839                windowMenu.setText("Window");
 840            }
 841            {
 842                imageWindowMenuItem = new JMenuItem();
 843                windowMenu.add(imageWindowMenuItem);
 844                imageWindowMenuItem.setText("Image");
 845                imageWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 1"));
 846                imageWindowMenuItem.addActionListener(new ActionListener() {
 847                    public void actionPerformed(ActionEvent evt) {
 848                        selectFrame(imagePanelFrame);
 849                    }
 850                });
 851            }
 852            {
 853                editorWindowMenuItem = new JMenuItem();
 854                windowMenu.add(editorWindowMenuItem);
 855                editorWindowMenuItem.setText("Script Editor");
 856                editorWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 2"));
 857                editorWindowMenuItem.addActionListener(new ActionListener() {
 858                    public void actionPerformed(ActionEvent evt) {
 859                        selectFrame(editorFrame);
 860                    }
 861                });
 862            }
 863            {
 864                consoleWindowMenuItem = new JMenuItem();
 865                windowMenu.add(consoleWindowMenuItem);
 866                consoleWindowMenuItem.setText("Console");
 867                consoleWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 3"));
 868                consoleWindowMenuItem.addActionListener(new ActionListener() {
 869                    public void actionPerformed(ActionEvent evt) {
 870                        selectFrame(consoleFrame);
 871                    }
 872                });
 873            }
 874            {
 875                jSeparator5 = new JSeparator();
 876                windowMenu.add(jSeparator5);
 877            }
 878            {
 879                tileWindowMenuItem = new JMenuItem();
 880                windowMenu.add(tileWindowMenuItem);
 881                tileWindowMenuItem.setText("Tile");
 882                tileWindowMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl T"));
 883                tileWindowMenuItem.addActionListener(new ActionListener() {
 884                    public void actionPerformed(ActionEvent evt) {
 885                        tileWindowMenuItemActionPerformed(evt);
 886                    }
 887                });
 888            }
 889        }
 890    }
 891
 892    private void newFileMenuItemActionPerformed(ActionEvent evt) {
 893        if (evt != null) {
 894            // check save?
 895        }
 896        // put some template code into the editor
 897        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";
 898        editorTextArea.setText(template);
 899    }
 900
 901    private void openFileMenuItemActionPerformed(ActionEvent evt) {
 902        JFileChooser fc = new JFileChooser(".");
 903        if (lastSaveDirectory != null)
 904            fc.setCurrentDirectory(lastSaveDirectory);
 905        fc.setFileFilter(new FileFilter() {
 906            @Override
 907            public String getDescription() {
 908                return "Scene File";
 909            }
 910
 911            @Override
 912            public boolean accept(File f) {
 913                return (f.isDirectory() || f.getName().endsWith(".sc") || f.getName().endsWith(".java"));
 914            }
 915        });
 916
 917        if (fc.showOpenDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) {
 918            final String f = fc.getSelectedFile().getAbsolutePath();
 919            openFile(f);
 920            lastSaveDirectory = fc.getSelectedFile().getParentFile();
 921        }
 922    }
 923
 924    private void buildMenuItemActionPerformed(ActionEvent evt) {
 925        new Thread() {
 926            @Override
 927            public void run() {
 928                setEnableInterface(false);
 929                if (clearLogMenuItem.isSelected())
 930                    clearConsole();
 931                Timer t = new Timer();
 932                t.start();
 933                try {
 934                    api = SunflowAPI.compile(editorTextArea.getText());
 935                } catch (NoClassDefFoundError e) {
 936                    UI.printError(Module.GUI, "Janino library not found. Please check command line.");
 937                    api = null;
 938                }
 939                if (api != null) {
 940                    try {
 941                        if (currentFile != null) {
 942                            String dir = new File(currentFile).getAbsoluteFile().getParent();
 943                            api.searchpath("texture", dir);
 944                            api.searchpath("include", dir);
 945                        }
 946                        api.build();
 947                    } catch (Exception e) {
 948                        UI.printError(Module.GUI, "Build terminated abnormally: %s", e.getMessage());
 949                        for (StackTraceElement elt : e.getStackTrace()) {
 950                            UI.printInfo(Module.GUI, "       at %s", elt.toString());
 951                        }
 952                        e.printStackTrace();
 953                    }
 954                    t.end();
 955                    UI.printInfo(Module.GUI, "Build time: %s", t.toString());
 956                }
 957                setEnableInterface(true);
 958            }
 959        }.start();
 960    }
 961
 962    private void clearConsole() {
 963        consoleTextArea.setText(null);
 964    }
 965
 966    private void println(final String s) {
 967        SwingUtilities.invokeLater(new Runnable() {
 968            public void run() {
 969                consoleTextArea.append(s + "\n");
 970            }
 971        });
 972    }
 973
 974    private void setEnableInterface(boolean enabled) {
 975        // lock or unlock options which are unsafe during builds or renders
 976        newFileMenuItem.setEnabled(enabled);
 977        openFileMenuItem.setEnabled(enabled);
 978        saveMenuItem.setEnabled(enabled);
 979        saveAsMenuItem.setEnabled(enabled);
 980        sceneMenu.setEnabled(enabled);
 981        buildButton.setEnabled(enabled);
 982        renderButton.setEnabled(enabled);
 983        iprButton.setEnabled(enabled);
 984    }
 985
 986    public void print(Module m, PrintLevel level, String s) {
 987        if (level == PrintLevel.ERROR)
 988            JOptionPane.showMessageDialog(SunflowGUI.this, s, String.format("Error - %s", m.name()), JOptionPane.ERROR_MESSAGE);
 989        println(UI.formatOutput(m, level, s));
 990    }
 991
 992    public void taskStart(String s, int min, int max) {
 993        currentTask = s;
 994        currentTaskLastP = -1;
 995        final int taskMin = min;
 996        final int taskMax = max;
 997        SwingUtilities.invokeLater(new Runnable() {
 998            public void run() {
 999                taskProgressBar.setEnabled(true);
1000                taskCancelButton.setEnabled(true);
1001                taskProgressBar.setMinimum(taskMin);
1002                taskProgressBar.setMaximum(taskMax);
1003                taskProgressBar.setValue(taskMin);
1004                taskProgressBar.setString(currentTask);
1005            }
1006        });
1007    }
1008
1009    public void taskUpdate(int current) {
1010        final int taskCurrent = current;
1011        final String taskString = currentTask;
1012        SwingUtilities.invokeLater(new Runnable() {
1013            public void run() {
1014                taskProgressBar.setValue(taskCurrent);
1015                int p = (int) (100.0 * taskProgressBar.getPercentComplete());
1016                if (p > currentTaskLastP) {
1017                    taskProgressBar.setString(taskString + " [" + p + "%]");
1018                    currentTaskLastP = p;
1019                }
1020            }
1021        });
1022    }
1023
1024    public void taskStop() {
1025        SwingUtilities.invokeLater(new Runnable() {
1026            public void run() {
1027                taskProgressBar.setValue(taskProgressBar.getMinimum());
1028                taskProgressBar.setString("");
1029                taskProgressBar.setEnabled(false);
1030                taskCancelButton.setEnabled(false);
1031            }
1032        });
1033    }
1034
1035    private void renderMenuItemActionPerformed(ActionEvent evt) {
1036        new Thread() {
1037            @Override
1038            public void run() {
1039                setEnableInterface(false);
1040                if (clearLogMenuItem.isSelected())
1041                    clearConsole();
1042                if (api != null) {
1043                    api.parameter("sampler", "bucket");
1044                    api.options(SunflowAPI.DEFAULT_OPTIONS);
1045                    api.render(SunflowAPI.DEFAULT_OPTIONS, imagePanel);
1046                } else
1047                    UI.printError(Module.GUI, "Nothing to render!");
1048                setEnableInterface(true);
1049            }
1050        }.start();
1051    }
1052
1053    private void iprMenuItemActionPerformed(ActionEvent evt) {
1054        new Thread() {
1055            @Override
1056            public void run() {
1057                setEnableInterface(false);
1058                if (clearLogMenuItem.isSelected())
1059                    clearConsole();
1060                if (api != null) {
1061                    api.parameter("sampler", "ipr");
1062                    api.options(SunflowAPI.DEFAULT_OPTIONS);
1063                    api.render(SunflowAPI.DEFAULT_OPTIONS, imagePanel);
1064                } else
1065                    UI.printError(Module.GUI, "Nothing to IPR!");
1066                setEnableInterface(true);
1067            }
1068        }.start();
1069    }
1070
1071    private void textureCacheClearMenuItemActionPerformed(ActionEvent evt) {
1072        TextureCache.flush();
1073    }
1074
1075    private void smallTrianglesMenuItemActionPerformed(ActionEvent evt) {
1076        TriangleMesh.setSmallTriangles(smallTrianglesMenuItem.isSelected());
1077    }
1078
1079    private void saveAsMenuItemActionPerformed(ActionEvent evt) {
1080        JFileChooser fc = new JFileChooser(".");
1081        if (lastSaveDirectory != null)
1082            fc.setCurrentDirectory(lastSaveDirectory);
1083        fc.setFileFilter(new FileFilter() {
1084            @Override
1085            public String getDescription() {
1086                return "Scene File";
1087            }
1088
1089            @Override
1090            public boolean accept(File f) {
1091                return (f.isDirectory() || f.getName().endsWith(".java"));
1092            }
1093        });
1094
1095        if (fc.showSaveDialog(SunflowGUI.this) == JFileChooser.APPROVE_OPTION) {
1096            String f = fc.getSelectedFile().getAbsolutePath();
1097            if (!f.endsWith(".java"))
1098                f += ".java";
1099            File file = new File(f);
1100            if (!file.exists() || JOptionPane.showConfirmDialog(SunflowGUI.this, "This file already exists.\nOverwrite?", "Warning", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
1101                // save file
1102                saveCurrentFile(f);
1103                lastSaveDirectory = file.getParentFile();
1104            }
1105        }
1106    }
1107
1108    private void saveCurrentFile(String filename) {
1109        if (filename == null) {
1110            // no filename was picked, go to save as dialog
1111            saveAsMenuItemActionPerformed(null);
1112            return;
1113        }
1114        FileWriter file;
1115        try {
1116            file = new FileWriter(filename);
1117            // get text from editor pane
1118            file.write(editorTextArea.getText());
1119            file.close();
1120            // update current filename
1121            currentFile

Large files files are truncated, but you can click here to view the full file