/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.create.eden.swing; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import org.jwildfire.base.Prefs; import org.jwildfire.base.Tools; import org.jwildfire.base.Unchecker; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.create.eden.export.SunflowExporter; import org.jwildfire.create.eden.scene.Scene; import org.jwildfire.create.eden.scene.SceneElementGroup; import org.jwildfire.create.eden.scene.VisibleSceneElement; import org.jwildfire.create.eden.scene.material.Material; import org.jwildfire.create.eden.scene.primitive.Box; import org.jwildfire.create.eden.scene.primitive.Cylinder; import org.jwildfire.create.eden.scene.primitive.Sphere; import org.jwildfire.create.tina.swing.SunflowSceneFileChooser; import org.jwildfire.swing.ErrorHandler; import org.jwildfire.swing.MainController; import org.sunflow.SunflowAPI; import org.sunflow.system.ImagePanel; import org.sunflow.system.Timer; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; import org.sunflow.system.UI.PrintLevel; import org.sunflow.system.UserInterface; public class EDENController implements UserInterface { private enum SceneType { JAVA, SC }; private enum Status { IDLE, RENDERING }; private enum Sampler { BUCKET, IPR } private String currentFile; private SunflowAPI api; private final Prefs prefs; private final MainController mainController; private final ErrorHandler errorHandler; private final JTextArea editorTextArea; private final JTextArea consoleTextArea; private final ImagePanel imagePanel; private final JButton renderButton; private final JButton iprButton; private final JButton loadSceneButton; private final JButton cancelRenderButton; private final JButton buildSceneButton; private final JButton saveSceneButton; private final JButton clearConsoleButton; private final JButton newSceneButton; private SceneType sceneType = null; private Status status = Status.IDLE; public EDENController(MainController pMainController, ErrorHandler pErrorHandler, Prefs pPrefs, JTextArea pEditorTextArea, JTextArea pConsoleTextArea, ImagePanel pImagePanel, JButton pRenderButton, JButton pIprButton, JButton pLoadSceneButton, JButton pCancelRenderButton, JButton pBuildSceneButton, JButton pSaveSceneButton, JButton pClearConsoleButton, JButton pNewSceneButton) { mainController = pMainController; errorHandler = pErrorHandler; prefs = pPrefs; editorTextArea = pEditorTextArea; consoleTextArea = pConsoleTextArea; renderButton = pRenderButton; iprButton = pIprButton; loadSceneButton = pLoadSceneButton; cancelRenderButton = pCancelRenderButton; buildSceneButton = pBuildSceneButton; saveSceneButton = pSaveSceneButton; clearConsoleButton = pClearConsoleButton; newSceneButton = pNewSceneButton; imagePanel = pImagePanel; UI.set(this); } public void newEmptyScene() { currentFile = null; api = null; sceneType = SceneType.SC; currentFile = "new" + genNewFileId() + ".sc"; editorTextArea.setText(""); enableControls(); } public void newScene() { currentFile = null; api = null; sceneType = SceneType.SC; currentFile = "new" + genNewFileId() + ".sc"; String template = edenCreateSurfaceScene(); editorTextArea.setText(template); enableControls(); } private String genNewFileId() { return new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date()); } private JFileChooser getSceneJFileChooser() { JFileChooser fileChooser = new SunflowSceneFileChooser(Prefs.getPrefs()); fileChooser.setAcceptAllFileFilterUsed(false); return fileChooser; } public void loadScene() { try { JFileChooser chooser = getSceneJFileChooser(); if (prefs.getInputSunflowScenePath() != null) { chooser.setCurrentDirectory(new File(prefs.getInputSunflowScenePath())); } if (chooser.showOpenDialog(editorTextArea) == JFileChooser.APPROVE_OPTION) { api = null; File file = chooser.getSelectedFile(); String filename = file.getAbsolutePath(); if (filename.endsWith(".java")) { sceneType = SceneType.JAVA; } else { sceneType = SceneType.SC; } editorTextArea.setText(Tools.readUTF8Textfile(filename)); prefs.setLastInputSunflowSceneFile(file); currentFile = filename; enableControls(); } } catch (Throwable ex) { errorHandler.handleError(ex); } } public void buildScene() { try { clearConsole(); Timer t = new Timer(); t.start(); switch (sceneType) { case JAVA: api = SunflowAPI.compile(editorTextArea.getText()); break; case SC: { File tmpFile = File.createTempFile("jwf", ".sc"); try { String filename = tmpFile.getAbsolutePath(); Tools.writeUTF8Textfile(filename, editorTextArea.getText()); 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 include(\"" + filename.replace("\\", "\\\\") + "\");\n}\n"; api = SunflowAPI.compile(template); } finally { tmpFile.deleteOnExit(); } } break; } if (currentFile != null) { String dir = new File(currentFile).getAbsoluteFile().getParent(); api.searchpath("texture", dir); api.searchpath("include", dir); } api.build(); t.end(); UI.printInfo(Module.GUI, "Build time: %s", t.toString()); enableControls(); } catch (Throwable ex) { api = null; errorHandler.handleError(ex); } } void clearConsole() { consoleTextArea.setText(""); } @Override public void print(Module m, PrintLevel level, String s) { if (level == PrintLevel.ERROR) JOptionPane.showMessageDialog(editorTextArea, s, String.format("Error - %s", m.name()), JOptionPane.ERROR_MESSAGE); println(UI.formatOutput(m, level, s)); } private void println(final String s) { SwingUtilities.invokeLater(new Runnable() { public void run() { consoleTextArea.append(s + "\n"); } }); } @Override public void taskStart(String arg0, int arg1, int arg2) { // TODO Auto-generated method stub } @Override public void taskStop() { // TODO Auto-generated method stub } @Override public void taskUpdate(int arg0) { // TODO Auto-generated method stub } public void renderScene() { doRender(Sampler.BUCKET); } private boolean renderCancelled; public void doRender(final Sampler pSampler) { if (status == Status.RENDERING) { return; } buildScene(); renderCancelled = false; new Thread() { @Override public void run() { status = Status.RENDERING; try { enableControls(); clearConsole(); api.parameter("sampler", pSampler == Sampler.BUCKET ? "bucket" : "ipr"); api.parameter("accel", "kdtree"); api.options(SunflowAPI.DEFAULT_OPTIONS); api.render(SunflowAPI.DEFAULT_OPTIONS, imagePanel); } finally { if (!renderCancelled) { File file; try { file = File.createTempFile("JWF", "png"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } try { imagePanel.save(file.getAbsolutePath()); try { mainController.loadImage(file.getAbsolutePath(), false); } catch (Exception e) { e.printStackTrace(); } } finally { file.delete(); } } status = Status.IDLE; enableControls(); } } }.start(); } private void enableControls() { boolean hasScene = editorTextArea.getText().length() > 0; boolean idle = status == Status.IDLE; renderButton.setEnabled(idle && hasScene); iprButton.setEnabled(renderButton.isEnabled()); loadSceneButton.setEnabled(idle); cancelRenderButton.setEnabled(!idle); buildSceneButton.setEnabled(renderButton.isEnabled()); saveSceneButton.setEnabled(renderButton.isEnabled()); newSceneButton.setEnabled(loadSceneButton.isEnabled()); clearConsoleButton.setEnabled(idle); } public void cancelRendering() { UI.taskCancel(); renderCancelled = true; } public void iprScene() { doRender(Sampler.IPR); } public void saveScene() { try { JFileChooser chooser = getSceneJFileChooser(); chooser.setCurrentDirectory(new File(prefs.getOutputSunflowScenePath())); if (currentFile != null) { chooser.setSelectedFile(new File(currentFile)); } if (chooser.showSaveDialog(editorTextArea) == JFileChooser.APPROVE_OPTION) { File file = chooser.getSelectedFile(); String filename = file.getAbsolutePath(); switch (sceneType) { case JAVA: if (!filename.endsWith(".java")) { filename += ".java"; } break; case SC: if (!filename.endsWith(".sc")) { filename += ".sc"; } break; default: throw new IllegalStateException(); } Tools.writeUTF8Textfile(filename, editorTextArea.getText()); prefs.setLastOutputSunflowSceneFile(file); } } catch (Throwable ex) { errorHandler.handleError(ex); } } public abstract class Creator { protected final Scene scene; public abstract void create(); public Creator(Scene pScene) { scene = pScene; } } public abstract class Transformer { protected final Scene scene; public abstract void transform(VisibleSceneElement pElement); public Transformer(Scene pScene) { scene = pScene; } } public class SphereCreator extends Creator { public SphereCreator(Scene pScene) { super(pScene); } @Override public void create() { Sphere sphere = scene.addSphere(-10, -10, -1, 0.4); sphere.setMaterial(Material.MATERIAL_MIRROR); } } public class BoxCreator extends Creator { public BoxCreator(Scene pScene) { super(pScene); } @Override public void create() { Box box = scene.addBox(-10, -10, 0, 3); box.setMaterial(Material.MATERIAL_SHINY_RED); } } public class Transformer1 extends Transformer { public Transformer1(Scene pScene) { super(pScene); } @Override public void transform(VisibleSceneElement pElement) { pElement.getPosition().setX(pElement.getPosition().getX() + 2.0 * (1.0 + Math.random())); pElement.getOrientation().setGamma(pElement.getOrientation().getGamma() + 3.0 * (1.0 + Math.random())); if (Math.random() < 0.5) { pElement.setMaterial(Material.MATERIAL_SHINY_GREEN); } else { pElement.setMaterial(Material.MATERIAL_SHINY_BLUE); } } } public class Transformer2 extends Transformer { public Transformer2(Scene pScene) { super(pScene); } @Override public void transform(VisibleSceneElement pElement) { pElement.getPosition().setY(pElement.getPosition().getY() + 1); pElement.getOrientation().setBeta(pElement.getOrientation().getBeta() + 6 * (1.0 + Math.random())); pElement.getOrientation().setAlpha(pElement.getOrientation().getAlpha() + 3 * (1.0 + Math.random())); if (Math.random() < 0.5) { pElement.setMaterial(Material.MATERIAL_SHINY_GREEN); } else { pElement.setMaterial(Material.MATERIAL_SHINY_BLUE); } } } private String edenCreateSurfaceScene() { try { Scene scene = new Scene(); scene.addPointLight(0, 0, -200, 0.6); scene.addPointLight(-100, 20, -160, 0.8); scene.addPointLight(-10, 200, -60, 0.5); int width = 800; int height = 600; double surfWidth = width / 25.0; double surfHeight = height / 25.0; double surfDepth = MathLib.sqrt(surfWidth * surfWidth + surfHeight * surfHeight) / 100.0; List<Creator> creators = new ArrayList<Creator>(); // creators.add(new SphereCreator(scene)); creators.add(new BoxCreator(scene)); List<Transformer> transformers = new ArrayList<Transformer>(); transformers.add(new Transformer1(scene)); transformers.add(new Transformer2(scene)); for (Creator creator : creators) { creator.create(); } Map<Transformer, Set<VisibleSceneElement>> expandedMap = new HashMap<Transformer, Set<VisibleSceneElement>>(); for (int i = 0; i < 7; i++) { for (Transformer transformer : transformers) { Set<VisibleSceneElement> expandedSet = expandedMap.get(transformer); if (expandedSet == null) { expandedSet = new HashSet<VisibleSceneElement>(); expandedMap.put(transformer, expandedSet); } List<VisibleSceneElement> elements = scene.getAllVisibleElements(); for (VisibleSceneElement element : elements) { if (!expandedSet.contains(element)) { VisibleSceneElement copy = element.clone(); transformer.transform(copy); expandedSet.add(element); } } } } System.out.println("ELEMENTS: " + scene.getAllVisibleElements().size()); return new SunflowExporter().exportScene(scene); } catch (Exception ex) { Unchecker.rethrow(ex); return null; } } private SceneElementGroup createGroup(org.jwildfire.create.eden.scene.Scene pScene, double size) { SceneElementGroup group = pScene.addGroup(); { Sphere sphere = group.addSphere(-2 * size, 0, 1, size); sphere.setMaterial(Material.MATERIAL_MIRROR); } { Sphere sphere = group.addSphere(2 * size, 0, 1, size); sphere.setMaterial(Material.MATERIAL_GLASS); } { Cylinder cylinder = group.addCylinder(0, 0, 1, size / 4.0, size * 2.0); cylinder.setMaterial(Material.MATERIAL_MIRROR); cylinder.setOrientation(90.0, 0.0, 90.0); } { Box box = group.addBox(0, 3 * size, 0, size); box.setMaterial(Material.MATERIAL_SHINY_RED); box.setOrientation(45.0, 45.0, 45.0); } return group; } }