debian/0000755000000000000000000000000012207104023007155 5ustar debian/compat0000644000000000000000000000000212207104023010353 0ustar 7 debian/control0000644000000000000000000000407012207104023010561 0ustar Source: sunflow Section: graphics Priority: extra Maintainer: Gabriele Giacone <1o5g4r8o@gmail.com> Build-Depends: cdbs (>= 0.4.90~), debhelper (>= 7), ant, imagemagick, default-jdk, libmagickcore-extra, python (>= 2.6.6-3~) Build-Depends-Indep: janino Standards-Version: 3.9.4 Homepage: http://sunflow.sourceforge.net/ Vcs-Git: git://anonscm.debian.org/collab-maint/sunflow.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/sunflow.git Package: sunflow Architecture: all Depends: default-jre, libsunflow-java, ${python:Depends}, ${misc:Depends} Replaces: libsunflow-java (= 0.07.2.svn396+dfsg-2) Description: rendering system for photo-realistic image synthesis (GUI) Sunflow is an open source rendering system for photo-realistic image synthesis. It is written in Java and built around a flexible ray tracing core and an extensible object-oriented design. It was created as a framework for experimenting with global illumination algorithms and new surface shading models. . This package contains the sunflow GUI. Package: libsunflow-java Architecture: all Section: java Depends: janino, ${misc:Depends} Replaces: sunflow (<= 0.07.2.svn396+dfsg-2) Description: rendering system for photo-realistic image synthesis Sunflow is an open source rendering system for photo-realistic image synthesis. It is written in Java and built around a flexible ray tracing core and an extensible object-oriented design. It was created as a framework for experimenting with global illumination algorithms and new surface shading models. Package: libsunflow-java-doc Architecture: all Section: doc Depends: ${misc:Depends} Suggests: libsunflow-java, sunflow Description: rendering system for photo-realistic image synthesis (javadoc) Sunflow is an open source rendering system for photo-realistic image synthesis. It is written in Java and built around a flexible ray tracing core and an extensible object-oriented design. It was created as a framework for experimenting with global illumination algorithms and new surface shading models. . This is the API documentation for sunflow debian/copyright0000644000000000000000000001044612207104023011115 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Sunflow - rendering system for photo-realistic image synthesis Upstream-Contact: Christopher Kulla Source: http://sunflow.sourceforge.net/ Files: * Copyright: 2003-2008, Christopher Kulla License: MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: exporters/blender/sunflow_export.py Copyright: Robert "hayfever" Lindsay, Miguel "MADCello" Dominguez, Olivier "olivS" Saraja, Eugene Reilly, "Heavily Tessellated", "Humfred" License: GPL-2+ On Debian systems the full text of the GNU General Public License can be found in the `/usr/share/common-licenses/GPL-2' file. Files: exporters/maya/sunflowExporter/src/skylight.cpp exporters/maya/sunflowExporter/src/sunskyConstants.h exporters/maya/sunflowExporter/src/skylight.h Copyright: 2006, Mad Crew http://www.madcrew.se License: GPL-2+ On Debian systems the full text of the GNU General Public License can be found in the `/usr/share/common-licenses/GPL-2' file. Files: debian/* Copyright: 2007-2008, Cyril Brulebois 2010, Gabriele Giacone <1o5g4r8o@gmail.com> License: MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: debian/sunflow.svg Copyright: 2008, Philipp Hagemeister License: MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. debian/sunflow.svg0000644000000000000000000002007012207104023011372 0ustar image/svg+xml Sunflow logo debian/source/0000755000000000000000000000000012207104023010455 5ustar debian/source/format0000644000000000000000000000001412207104023011663 0ustar 3.0 (quilt) debian/TODO0000644000000000000000000000227512207104023007653 0ustar TODO for sunflow: ----------------- * Complete the manpage (use the output of -help to get the list of all available options and their descriptions). * Actually the “server JVM” isn't detected with either of the icedtea7 jdk and jre packages, but is OK when using sun-java5-jre without sun-java5-jdk. This needs polishing. * Sync script: add a check to determine when the patch can be dropped (to be done, debugged, and tested along with the next upstream release). * Think about eventually lowering the Depends on ${python:Depends} to a Recommends:, so that: - people wanting to only use sunflow without the python exporter for Blender can do so; - the exporter works by default. To be thought about: - that dependency will be pulled by blender anyway; - it then creates the following problems: if python-support is installed after sunflow, or removed before sunflow, the .pyc will either not be created, or not be deleted. It looks like it is safer to let the Depends: as it is now, and to eventually wait for a bugreport before reconsidering this. -- Cyril Brulebois , Mon, 14 Jul 2008 04:28:23 +0200 debian/sunflow.conf0000644000000000000000000000137012207104023011522 0ustar # Sunflow wrapper configuration file # # Please refer to sunflow(1) for the list of supported options. # Uncomment and adjust the variables at your convenience. # # One can overwrite these settings using a ~/.sunflowrc file, # or using environment variables. # Do not use the default "java" binary, handled via alternatives, but # another virtual machine: # # export SUNFLOW_JAVA=/path/to/some/jvm # Automatically pass these options to the java interpreter. In this example: # - start the VM server # - use a memory upper limit of 500M # # Note that the use of quotation marks if more than one option is passed # is important. # # export SUNFLOW_JAVA_OPTIONS="-server -Xmx500M" # Automatically pass these options to sunflow. # # export SUNFLOW_OPTIONS="" debian/menu0000644000000000000000000000027512207104023010050 0ustar ?package(sunflow):needs="X11" section="Applications/Graphics"\ longtitle="Sunflow Rendering System"\ icon="/usr/share/pixmaps/sunflow.xpm"\ title="Sunflow" command="/usr/bin/sunflow" debian/sunflow.desktop0000644000000000000000000000033312207104023012244 0ustar [Desktop Entry] Type=Application Name=Sunflow Comment=Photorealistic Rendering Engine Exec=sunflow Icon=sunflow.png Categories=Graphics;3DGraphics Keywords=rendering,photo,realistic,image,synthesis,illumination,shading debian/watch0000644000000000000000000000013112207104023010201 0ustar version=3 opts="dversionmangle=s/\+dfsg//" \ http://sf.net/sunflow/sunflow-src-v(.*).zip debian/patches/0000755000000000000000000000000012207104023010604 5ustar debian/patches/30_sweethome3d0000644000000000000000000031616512207104023013274 0ustar Description: Changes for sweethome3d. From sh3d upstream tarball, version 4.1. Author: Emmanuel Puybaret, eTeks --- a/src/SunflowGUI.java +++ b/src/SunflowGUI.java @@ -43,7 +43,6 @@ import org.sunflow.Benchmark; import org.sunflow.RealtimeBenchmark; import org.sunflow.SunflowAPI; import org.sunflow.core.Display; -import org.sunflow.core.TextureCache; import org.sunflow.core.accel.KDTree; import org.sunflow.core.display.FileDisplay; import org.sunflow.core.display.FrameDisplay; @@ -52,9 +51,9 @@ import org.sunflow.core.primitive.Triang 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; +import org.sunflow.system.UserInterface; @SuppressWarnings("serial") public class SunflowGUI extends javax.swing.JFrame implements UserInterface { @@ -1069,7 +1068,8 @@ public class SunflowGUI extends javax.sw } private void textureCacheClearMenuItemActionPerformed(ActionEvent evt) { - TextureCache.flush(); + // EP : Made texture cache local to SunFlow API + api.getTextureCache().flush(); } private void smallTrianglesMenuItemActionPerformed(ActionEvent evt) { --- a/src/org/sunflow/PluginRegistry.java +++ b/src/org/sunflow/PluginRegistry.java @@ -51,12 +51,12 @@ import org.sunflow.core.light.TriangleMe import org.sunflow.core.modifiers.BumpMappingModifier; import org.sunflow.core.modifiers.NormalMapModifier; import org.sunflow.core.modifiers.PerlinModifier; -import org.sunflow.core.parser.RA2Parser; -import org.sunflow.core.parser.RA3Parser; -import org.sunflow.core.parser.SCAsciiParser; -import org.sunflow.core.parser.SCBinaryParser; -import org.sunflow.core.parser.SCParser; -import org.sunflow.core.parser.ShaveRibParser; +//import org.sunflow.core.parser.RA2Parser; +//import org.sunflow.core.parser.RA3Parser; +//import org.sunflow.core.parser.SCAsciiParser; +//import org.sunflow.core.parser.SCBinaryParser; +//import org.sunflow.core.parser.SCParser; +//import org.sunflow.core.parser.ShaveRibParser; import org.sunflow.core.photonmap.CausticPhotonMap; import org.sunflow.core.photonmap.GlobalPhotonMap; import org.sunflow.core.photonmap.GridPhotonMap; @@ -291,15 +291,16 @@ public final class PluginRegistry { imageSamplerPlugins.registerPlugin("multipass", MultipassRenderer.class); } - static { - // parsers - parserPlugins.registerPlugin("sc", SCParser.class); - parserPlugins.registerPlugin("sca", SCAsciiParser.class); - parserPlugins.registerPlugin("scb", SCBinaryParser.class); - parserPlugins.registerPlugin("rib", ShaveRibParser.class); - parserPlugins.registerPlugin("ra2", RA2Parser.class); - parserPlugins.registerPlugin("ra3", RA3Parser.class); - } +// EP : Don't need parsers +// static { +// // parsers +// parserPlugins.registerPlugin("sc", SCParser.class); +// parserPlugins.registerPlugin("sca", SCAsciiParser.class); +// parserPlugins.registerPlugin("scb", SCBinaryParser.class); +// parserPlugins.registerPlugin("rib", ShaveRibParser.class); +// parserPlugins.registerPlugin("ra2", RA2Parser.class); +// parserPlugins.registerPlugin("ra3", RA3Parser.class); +// } static { // bitmap readers @@ -309,6 +310,8 @@ public final class PluginRegistry { bitmapReaderPlugins.registerPlugin("jpg", JPGBitmapReader.class); bitmapReaderPlugins.registerPlugin("bmp", BMPBitmapReader.class); bitmapReaderPlugins.registerPlugin("igi", IGIBitmapReader.class); + // EP : Added extension jpeg + bitmapReaderPlugins.registerPlugin("jpeg", JPGBitmapReader.class); } static { --- a/src/org/sunflow/SunflowAPI.java +++ b/src/org/sunflow/SunflowAPI.java @@ -1,16 +1,8 @@ package org.sunflow; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.StringReader; import java.util.Locale; -import org.codehaus.janino.ClassBodyEvaluator; -import org.codehaus.janino.CompileException; -import org.codehaus.janino.Scanner; -import org.codehaus.janino.Parser.ParseException; -import org.codehaus.janino.Scanner.ScanException; import org.sunflow.core.Camera; import org.sunflow.core.CameraLens; import org.sunflow.core.Display; @@ -26,6 +18,7 @@ import org.sunflow.core.Scene; import org.sunflow.core.SceneParser; import org.sunflow.core.Shader; import org.sunflow.core.Tesselatable; +import org.sunflow.core.TextureCache; import org.sunflow.core.ParameterList.InterpolationType; import org.sunflow.image.ColorFactory; import org.sunflow.image.ColorFactory.ColorSpecificationException; @@ -36,7 +29,6 @@ import org.sunflow.math.Point3; import org.sunflow.math.Vector3; import org.sunflow.system.FileUtils; import org.sunflow.system.SearchPath; -import org.sunflow.system.Timer; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; @@ -565,51 +557,53 @@ public class SunflowAPI implements Sunfl * @return a valid SunflowAPI object or null on failure */ public static SunflowAPI create(String filename, int frameNumber) { - if (filename == null) - return new SunflowAPI(); - SunflowAPI api = null; - if (filename.endsWith(".java")) { - Timer t = new Timer(); - UI.printInfo(Module.API, "Compiling \"" + filename + "\" ..."); - t.start(); - try { - FileInputStream stream = new FileInputStream(filename); - api = (SunflowAPI) ClassBodyEvaluator.createFastClassBodyEvaluator(new Scanner(filename, stream), SunflowAPI.class, ClassLoader.getSystemClassLoader()); - stream.close(); - } catch (CompileException e) { - UI.printError(Module.API, "Could not compile: \"%s\"", filename); - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (ParseException e) { - UI.printError(Module.API, "Could not compile: \"%s\"", filename); - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (ScanException e) { - UI.printError(Module.API, "Could not compile: \"%s\"", filename); - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (IOException e) { - UI.printError(Module.API, "Could not compile: \"%s\"", filename); - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } - t.end(); - UI.printInfo(Module.API, "Compile time: " + t.toString()); - // allow relative paths - String currentFolder = new File(filename).getAbsoluteFile().getParentFile().getAbsolutePath(); - api.includeSearchPath.addSearchPath(currentFolder); - api.textureSearchPath.addSearchPath(currentFolder); - UI.printInfo(Module.API, "Build script running ..."); - t.start(); - api.currentFrame(frameNumber); - api.build(); - t.end(); - UI.printInfo(Module.API, "Build script time: %s", t.toString()); - } else { - api = new SunflowAPI(); - api = api.include(filename) ? api : null; - } - return api; +// EP : Don't need parser +// if (filename == null) +// return new SunflowAPI(); +// SunflowAPI api = null; +// if (filename.endsWith(".java")) { +// Timer t = new Timer(); +// UI.printInfo(Module.API, "Compiling \"" + filename + "\" ..."); +// t.start(); +// try { +// FileInputStream stream = new FileInputStream(filename); +// api = (SunflowAPI) ClassBodyEvaluator.createFastClassBodyEvaluator(new Scanner(filename, stream), SunflowAPI.class, ClassLoader.getSystemClassLoader()); +// stream.close(); +// } catch (CompileException e) { +// UI.printError(Module.API, "Could not compile: \"%s\"", filename); +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (ParseException e) { +// UI.printError(Module.API, "Could not compile: \"%s\"", filename); +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (ScanException e) { +// UI.printError(Module.API, "Could not compile: \"%s\"", filename); +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (IOException e) { +// UI.printError(Module.API, "Could not compile: \"%s\"", filename); +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } +// t.end(); +// UI.printInfo(Module.API, "Compile time: " + t.toString()); +// // allow relative paths +// String currentFolder = new File(filename).getAbsoluteFile().getParentFile().getAbsolutePath(); +// api.includeSearchPath.addSearchPath(currentFolder); +// api.textureSearchPath.addSearchPath(currentFolder); +// UI.printInfo(Module.API, "Build script running ..."); +// t.start(); +// api.currentFrame(frameNumber); +// api.build(); +// t.end(); +// UI.printInfo(Module.API, "Build script time: %s", t.toString()); +// } else { +// api = new SunflowAPI(); +// api = api.include(filename) ? api : null; +// } +// return api; + throw new UnsupportedOperationException("Removed parser support from SunFlow"); } /** @@ -620,35 +614,37 @@ public class SunflowAPI implements Sunfl * @return true upon success, false otherwise */ public static boolean translate(String filename, String outputFilename) { - FileSunflowAPI api = null; - try { - if (outputFilename.endsWith(".sca")) - api = new AsciiFileSunflowAPI(outputFilename); - else if (outputFilename.endsWith(".scb")) - api = new BinaryFileSunflowAPI(outputFilename); - else { - UI.printError(Module.API, "Unable to determine output filetype: \"%s\"", outputFilename); - return false; - } - } catch (IOException e) { - UI.printError(Module.API, "Unable to create output file - %s", e.getMessage()); - return false; - } - String extension = filename.substring(filename.lastIndexOf('.') + 1); - SceneParser parser = PluginRegistry.parserPlugins.createObject(extension); - if (parser == null) { - UI.printError(Module.API, "Unable to find a suitable parser for: \"%s\"", filename); - return false; - } - try { - return parser.parse(filename, api); - } catch (RuntimeException e) { - e.printStackTrace(); - UI.printError(Module.API, "Error occured during translation: %s", e.getMessage()); - return false; - } finally { - api.close(); - } +// EP : Don't need parser +// FileSunflowAPI api = null; +// try { +// if (outputFilename.endsWith(".sca")) +// api = new AsciiFileSunflowAPI(outputFilename); +// else if (outputFilename.endsWith(".scb")) +// api = new BinaryFileSunflowAPI(outputFilename); +// else { +// UI.printError(Module.API, "Unable to determine output filetype: \"%s\"", outputFilename); +// return false; +// } +// } catch (IOException e) { +// UI.printError(Module.API, "Unable to create output file - %s", e.getMessage()); +// return false; +// } +// String extension = filename.substring(filename.lastIndexOf('.') + 1); +// SceneParser parser = PluginRegistry.parserPlugins.createObject(extension); +// if (parser == null) { +// UI.printError(Module.API, "Unable to find a suitable parser for: \"%s\"", filename); +// return false; +// } +// try { +// return parser.parse(filename, api); +// } catch (RuntimeException e) { +// e.printStackTrace(); +// UI.printError(Module.API, "Error occured during translation: %s", e.getMessage()); +// return false; +// } finally { +// api.close(); +// } + throw new UnsupportedOperationException("Removed parser support from SunFlow"); } /** @@ -661,26 +657,28 @@ public class SunflowAPI implements Sunfl * otherwise. */ public static SunflowAPI compile(String code) { - try { - Timer t = new Timer(); - t.start(); - SunflowAPI api = (SunflowAPI) ClassBodyEvaluator.createFastClassBodyEvaluator(new Scanner(null, new StringReader(code)), SunflowAPI.class, (ClassLoader) null); - t.end(); - UI.printInfo(Module.API, "Compile time: %s", t.toString()); - return api; - } catch (CompileException e) { - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (ParseException e) { - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (ScanException e) { - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } catch (IOException e) { - UI.printError(Module.API, "%s", e.getMessage()); - return null; - } +// EP : Don't need parser +// try { +// Timer t = new Timer(); +// t.start(); +// SunflowAPI api = (SunflowAPI) ClassBodyEvaluator.createFastClassBodyEvaluator(new Scanner(null, new StringReader(code)), SunflowAPI.class, (ClassLoader) null); +// t.end(); +// UI.printInfo(Module.API, "Compile time: %s", t.toString()); +// return api; +// } catch (CompileException e) { +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (ParseException e) { +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (ScanException e) { +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } catch (IOException e) { +// UI.printError(Module.API, "%s", e.getMessage()); +// return null; +// } + throw new UnsupportedOperationException("Removed parser support from SunFlow"); } /** @@ -697,4 +695,12 @@ public class SunflowAPI implements Sunfl public void currentFrame(int currentFrame) { this.currentFrame = currentFrame; } + + // EP : Made texture cache local to a SunFlow API instance + private TextureCache textureCache = new TextureCache(); + + public TextureCache getTextureCache() { + return this.textureCache; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/LightServer.java +++ b/src/org/sunflow/core/LightServer.java @@ -24,12 +24,15 @@ class LightServer { private int maxDiffuseDepth; private int maxReflectionDepth; private int maxRefractionDepth; + // EP : Added transparency management + private int maxTransparencyDepth; // indirect illumination private CausticPhotonMapInterface causticPhotonMap; private GIEngine giEngine; private int photonCounter; + LightServer(Scene scene) { this.scene = scene; lights = new LightSource[0]; @@ -41,6 +44,8 @@ class LightServer { maxDiffuseDepth = 1; maxReflectionDepth = 4; maxRefractionDepth = 4; + // EP : Added transparency management + maxTransparencyDepth = 4; causticPhotonMap = null; giEngine = null; @@ -64,6 +69,8 @@ class LightServer { maxDiffuseDepth = options.getInt("depths.diffuse", maxDiffuseDepth); maxReflectionDepth = options.getInt("depths.reflection", maxReflectionDepth); maxRefractionDepth = options.getInt("depths.refraction", maxRefractionDepth); + // EP : Added transparency management + maxTransparencyDepth = options.getInt("depths.transparency", maxTransparencyDepth); String giEngineType = options.getString("gi.engine", null); giEngine = PluginRegistry.giEnginePlugins.createObject(giEngineType); String caustics = options.getString("caustics", null); @@ -143,7 +150,8 @@ class LightServer { synchronized (LightServer.this) { UI.taskUpdate(photonCounter); photonCounter++; - if (UI.taskCanceled()) + // EP : Manage renderer stop with interruptions + if (Thread.currentThread().isInterrupted()) return; } @@ -176,18 +184,19 @@ class LightServer { photonThreads[i].setPriority(scene.getThreadPriority()); photonThreads[i].start(); } - for (int i = 0; i < photonThreads.length; i++) { - try { + // EP : Moved InterruptedException out of loop to be able to stop all rendering threads + try { + for (int i = 0; i < photonThreads.length; i++) { photonThreads[i].join(); - } catch (InterruptedException e) { - UI.printError(Module.LIGHT, "Photon thread %d of %d was interrupted", i + 1, photonThreads.length); - return false; } - } - if (UI.taskCanceled()) { - UI.taskStop(); // shut down task cleanly + } catch (InterruptedException e) { + for (int i = 0; i < photonThreads.length; i++) { + photonThreads[i].interrupt(); + } + UI.printError(Module.BCKT, "Photon thread was interrupted"); return false; } + // EP : End of modification photonTimer.end(); UI.taskStop(); UI.printInfo(Module.LIGHT, "Tracing time for %s photons: %s", type, photonTimer.toString()); @@ -294,7 +303,7 @@ class LightServer { Color shadeHit(ShadingState state) { state.getInstance().prepareShadingState(state); Shader shader = getShader(state); - return (shader != null) ? shader.getRadiance(state) : Color.BLACK; + return (shader != null) ? shader.getRadiance(state) : Color.BLACK; } Color traceGlossy(ShadingState previous, Ray r, int i) { @@ -357,4 +366,28 @@ class LightServer { if (causticPhotonMap != null) causticPhotonMap.getSamples(state); } + + // EP : Added transparency management + Color traceShadow(Ray r, ShadingState previous) { + float maxDist = r.getMax(); + scene.traceShadow(r, previous.getIntersectionState()); + if (previous.getIntersectionState().hit()) { + Shader previousShader = previous.getIntersectionState().instance.getShader(0); + if (previousShader == null || previousShader.isOpaque() || previous.getShadowDepth() >= maxTransparencyDepth) { + return Color.WHITE; // fully opaque hit + } + ShadingState sstate = ShadingState.createShadowState(previous, r); + sstate.getInstance().prepareShadingState(sstate); + Shader shader = getShader(sstate); + if (shader == null || shader.isOpaque()) + return Color.WHITE; + Color opac = shader.getOpacity(sstate); + if (opac.isWhite()) + return opac; + else + return opac.copy().madd(Color.sub(Color.WHITE, opac), sstate.traceTransparentShadow(maxDist)); + } else + return Color.BLACK; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/Ray.java +++ b/src/org/sunflow/core/Ray.java @@ -214,4 +214,11 @@ public final class Ray { public final void setMax(float t) { tMax = t; } + + // EP : Added transparency management + public void setMinMax(float min, float max) { + tMin = min; + tMax = max; + } + // EP : end of modification } \ No newline at end of file --- a/src/org/sunflow/core/Shader.java +++ b/src/org/sunflow/core/Shader.java @@ -26,4 +26,18 @@ public interface Shader extends RenderOb * @param power power of the incoming photon. */ public void scatterPhoton(ShadingState state, Color power); + + // EP : Added transparency management + /** + * Returns true if this shader is fully opaque. + * This gives a quick way to find out if a shader needs further processing + * when hit by a shadow ray. + */ + public boolean isOpaque(); + + /** + * Returns how much light is blocked by this shader. + */ + public Color getOpacity(ShadingState state); + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/ShadingState.java +++ b/src/org/sunflow/core/ShadingState.java @@ -46,9 +46,12 @@ public final class ShadingState implemen private boolean includeSpecular; private LightSample lightSample; private PhotonStore map; + // EP : Added transparency management + private int shadowDepth; static ShadingState createPhotonState(Ray r, IntersectionState istate, int i, PhotonStore map, LightServer server) { - ShadingState s = new ShadingState(null, istate, r, i, 4); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(null, istate, r, i, 4, false); s.server = server; s.map = map; return s; @@ -56,7 +59,8 @@ public final class ShadingState implemen } static ShadingState createState(IntersectionState istate, float rx, float ry, float time, Ray r, int i, int d, LightServer server) { - ShadingState s = new ShadingState(null, istate, r, i, d); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(null, istate, r, i, d, false); s.server = server; s.rx = rx; s.ry = ry; @@ -65,40 +69,48 @@ public final class ShadingState implemen } static ShadingState createDiffuseBounceState(ShadingState previous, Ray r, int i) { - ShadingState s = new ShadingState(previous, previous.istate, r, i, 2); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(previous, previous.istate, r, i, 2, false); s.diffuseDepth++; return s; } static ShadingState createGlossyBounceState(ShadingState previous, Ray r, int i) { - ShadingState s = new ShadingState(previous, previous.istate, r, i, 2); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(previous, previous.istate, r, i, 2, false); s.includeLights = false; - s.includeSpecular = false; - s.reflectionDepth++; + // EP : Set includeSpecular to true to get the reflects + s.includeSpecular = true; + // EP : Very dirty hack to let mirror shader manage more bounces than uber shader + s.reflectionDepth += 4; return s; } static ShadingState createReflectionBounceState(ShadingState previous, Ray r, int i) { - ShadingState s = new ShadingState(previous, previous.istate, r, i, 2); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(previous, previous.istate, r, i, 2, false); s.reflectionDepth++; return s; } static ShadingState createRefractionBounceState(ShadingState previous, Ray r, int i) { - ShadingState s = new ShadingState(previous, previous.istate, r, i, 2); + // EP : Added ignoreHalton parameter + ShadingState s = new ShadingState(previous, previous.istate, r, i, 2, false); s.refractionDepth++; return s; } static ShadingState createFinalGatherState(ShadingState state, Ray r, int i) { - ShadingState finalGatherState = new ShadingState(state, state.istate, r, i, 2); + // EP : Added ignoreHalton parameter + ShadingState finalGatherState = new ShadingState(state, state.istate, r, i, 2, false); finalGatherState.diffuseDepth++; finalGatherState.includeLights = false; finalGatherState.includeSpecular = false; return finalGatherState; } - private ShadingState(ShadingState previous, IntersectionState istate, Ray r, int i, int d) { + // EP : Added ignoreHalton parameter + private ShadingState(ShadingState previous, IntersectionState istate, Ray r, int i, int d, boolean ignoreHalton) { this.r = r; this.istate = istate; this.i = i; @@ -121,6 +133,8 @@ public final class ShadingState implemen diffuseDepth = previous.diffuseDepth; reflectionDepth = previous.reflectionDepth; refractionDepth = previous.refractionDepth; + // EP : copy shadow depth + shadowDepth = previous.shadowDepth; server = previous.server; map = previous.map; rx = previous.rx; @@ -131,8 +145,12 @@ public final class ShadingState implemen behind = false; cosND = Float.NaN; includeLights = includeSpecular = true; - qmcD0I = QMC.halton(this.d, this.i); - qmcD1I = QMC.halton(this.d + 1, this.i); + // EP : Ignore Halton values for transparency computing + if (!ignoreHalton) { + qmcD0I = QMC.halton(this.d, this.i); + qmcD1I = QMC.halton(this.d + 1, this.i); + } + // EP : End of modification result = null; bias = 0.001f; } @@ -691,7 +709,8 @@ public final class ShadingState implemen * @return opacity along the shadow ray */ public final Color traceShadow(Ray r) { - return server.getScene().traceShadow(r, istate); + // EP : Added transparency management + return server.traceShadow(r, this); } /** @@ -926,4 +945,22 @@ public final class ShadingState implemen throw new UnsupportedOperationException(); } } + + // EP : Added transparency management + static ShadingState createShadowState(ShadingState previous, Ray r) { + ShadingState s = new ShadingState(previous, previous.istate, r, previous.i, previous.d, true); + s.shadowDepth++; + return s; + } + + public final int getShadowDepth() { + return shadowDepth; + } + + public Color traceTransparentShadow(float oldMaxT) { + Ray tr = new Ray(r.ox, r.oy, r.oz, r.dx, r.dy, r.dz); + tr.setMinMax(r.getMax(), oldMaxT); + return traceShadow(tr); + } + // EP : end of modification } \ No newline at end of file --- a/src/org/sunflow/core/Texture.java +++ b/src/org/sunflow/core/Texture.java @@ -1,6 +1,12 @@ package org.sunflow.core; +import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import org.sunflow.PluginRegistry; import org.sunflow.image.Bitmap; @@ -23,6 +29,8 @@ public class Texture { private boolean isLinear; private Bitmap bitmap; private int loaded; + // EP : Added bitmap transparency support + private boolean isTransparent; /** * Creates a new texture from the specfied file. @@ -43,11 +51,54 @@ public class Texture { try { UI.printInfo(Module.TEX, "Reading texture bitmap from: \"%s\" ...", filename); BitmapReader reader = PluginRegistry.bitmapReaderPlugins.createObject(extension); + // EP : Tolerate no extension in URLs + if (reader == null) { + try { + // Choose a reader depending on the magic number of the file + URL url = new URL(filename); + URLConnection connection = url.openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + InputStream in = connection.getInputStream(); + int firstByte = in.read(); + int secondByte = in.read(); + in.close(); + reader = firstByte == 0xFF && secondByte == 0xD8 + ? PluginRegistry.bitmapReaderPlugins.createObject("jpg") + : PluginRegistry.bitmapReaderPlugins.createObject("png"); + } catch (IOException ex) { + // Don't try to search an other reader + } + } + // EP : End of modification if (reader != null) { bitmap = reader.load(filename, isLinear); if (bitmap.getWidth() == 0 || bitmap.getHeight() == 0) bitmap = null; } + // EP : Check transparency + for (int x = 0; x < bitmap.getWidth(); x++) { + for (int y = 0; y < bitmap.getHeight(); y++) { + if (bitmap.readAlpha(x, y) < 1) { + this.isTransparent = true; + break; + } + } + } + // EP : End of modification if (bitmap == null) { UI.printError(Module.TEX, "Bitmap reading failed"); bitmap = new BitmapBlack(); @@ -105,7 +156,51 @@ public class Texture { c.madd(k11, c11); return c; } - + + // EP : Added bitmap transparency support + public Color getOpacity(float x, float y) { + Bitmap bitmap = getBitmap(); + x = MathUtils.frac(x); + y = MathUtils.frac(y); + float dx = x * (bitmap.getWidth() - 1); + float dy = y * (bitmap.getHeight() - 1); + int ix0 = (int) dx; + int iy0 = (int) dy; + int ix1 = (ix0 + 1) % bitmap.getWidth(); + int iy1 = (iy0 + 1) % bitmap.getHeight(); + float u = dx - ix0; + float v = dy - iy0; + u = u * u * (3.0f - (2.0f * u)); + v = v * v * (3.0f - (2.0f * v)); + float k00 = (1.0f - u) * (1.0f - v); + float a00 = bitmap.readAlpha(ix0, iy0); + float k01 = (1.0f - u) * v; + float a01 = bitmap.readAlpha(ix0, iy1); + float k10 = u * (1.0f - v); + float a10 = bitmap.readAlpha(ix1, iy0); + float k11 = u * v; + float a11 = bitmap.readAlpha(ix1, iy1); + float transparency = k00 * a00 + k01 * a01 + k10 * a10 + k11 * a11; + if (transparency <= 0.9999) { + Color c00 = bitmap.readColor(ix0, iy0); + Color c01 = bitmap.readColor(ix0, iy1); + Color c10 = bitmap.readColor(ix1, iy0); + Color c11 = bitmap.readColor(ix1, iy1); + Color c = Color.mul(k00, c00); + c.madd(k01, c01); + c.madd(k10, c10); + c.madd(k11, c11); + return c.opposite().mul(transparency); + } else { + return Color.WHITE; + } + } + + public boolean isTransparent() { + return this.isTransparent; + } + // EP : End of modification + public Vector3 getNormal(float x, float y, OrthoNormalBasis basis) { float[] rgb = getPixel(x, y).getRGB(); return basis.transform(new Vector3(2 * rgb[0] - 1, 2 * rgb[1] - 1, 2 * rgb[2] - 1)).normalize(); --- a/src/org/sunflow/core/TextureCache.java +++ b/src/org/sunflow/core/TextureCache.java @@ -10,9 +10,11 @@ import org.sunflow.system.UI.Module; * texture might be used more than once in your scene. */ public final class TextureCache { - private static HashMap textures = new HashMap(); + // EP : Removed static to enable GC to free Texture memory + private HashMap textures = new HashMap(); - private TextureCache() { + // EP : Made texture cache local to SunFlow API + public TextureCache() { } /** @@ -25,7 +27,8 @@ public final class TextureCache { * @return texture object * @see Texture */ - public synchronized static Texture getTexture(String filename, boolean isLinear) { + // EP : Removed static to enable GC to free Texture memory + public synchronized Texture getTexture(String filename, boolean isLinear) { if (textures.containsKey(filename)) { UI.printInfo(Module.TEX, "Using cached copy for file \"%s\" ...", filename); return textures.get(filename); @@ -40,7 +43,8 @@ public final class TextureCache { * Flush all textures from the cache, this will cause them to be reloaded * anew the next time they are accessed. */ - public synchronized static void flush() { + // EP : Removed static to enable GC to free Texture memory + public synchronized void flush() { UI.printInfo(Module.TEX, "Flushing texture cache"); textures.clear(); } --- a/src/org/sunflow/core/accel/BoundingIntervalHierarchy.java +++ b/src/org/sunflow/core/accel/BoundingIntervalHierarchy.java @@ -177,7 +177,13 @@ public class BoundingIntervalHierarchy i float clipL = Float.NaN, clipR = Float.NaN, prevClip = Float.NaN; float split = Float.NaN, prevSplit; boolean wasLeft = true; - while (true) { + // EP : Added a loop counter to avoid endless loop + float[] gridBoxCopy = gridBox.clone(); + float[] nodeBoxCopy = nodeBox.clone(); + int loopCount = 0; + // EP : End of modification + do { + // while (true) { prevAxis = axis; prevSplit = split; // perform quick consistency checks @@ -256,7 +262,8 @@ public class BoundingIntervalHierarchy i // ensure we are making progress in the subdivision if (right == rightOrig) { // all left - if (clipL <= split) { + // EP : Added additional test to avoid endless loop + if (clipL < split || (clipL == split && !(prevAxis == axis && prevSplit == split))) { // keep looping on left half gridBox[2 * axis + 1] = split; prevClip = clipL; @@ -274,7 +281,8 @@ public class BoundingIntervalHierarchy i } else if (left > right) { // all right right = rightOrig; - if (clipR >= split) { + // EP : Added additional test to avoid endless loop + if (clipR > split || (clipR == split && !(prevAxis == axis && prevSplit == split))) { // keep looping on right half gridBox[2 * axis + 0] = split; prevClip = clipR; @@ -322,7 +330,15 @@ public class BoundingIntervalHierarchy i } break; } + // EP : Added test to avoid endless loop + } while (loopCount++ < 100); + if (loopCount > 100) { + System.arraycopy(gridBoxCopy, 0, gridBox, 0, gridBox.length); + System.arraycopy(nodeBoxCopy, 0, nodeBox, 0, nodeBox.length); + return; } + // EP : End of modification + // compute index of child nodes int nextIndex = tempTree.getSize(); // allocate left node @@ -482,8 +498,13 @@ public class BoundingIntervalHierarchy i intervalMax = (tf <= intervalMax) ? tf : intervalMax; continue; } + // EP : Give up if stack is full + if (stackPos == stack.length) { + break pushloop; + } + // EP : End of modification // ray passes through both nodes - // push back node + // push back node stack[stackPos].node = back; stack[stackPos].near = (tb >= intervalMin) ? tb : intervalMin; stack[stackPos].far = intervalMax; @@ -511,6 +532,11 @@ public class BoundingIntervalHierarchy i intervalMax = (tf <= intervalMax) ? tf : intervalMax; continue; } + // EP : Give up if stack is full + if (stackPos == stack.length) { + break pushloop; + } + // EP : End of modification // ray passes through both nodes // push back node stack[stackPos].node = back; @@ -541,6 +567,11 @@ public class BoundingIntervalHierarchy i intervalMax = (tf <= intervalMax) ? tf : intervalMax; continue; } + // EP : Give up if stack is full + if (stackPos == stack.length) { + break pushloop; + } + // EP : End of modification // ray passes through both nodes // push back node stack[stackPos].node = back; @@ -597,7 +628,12 @@ public class BoundingIntervalHierarchy i } // traversal loop do { // stack is empty? - if (stackPos == 0) + if (stackPos == 0 + // EP : Check ray values aren't NaN + || Float.isNaN(r.dx) + || Float.isNaN(r.dy) + || Float.isNaN(r.dz)) + // EP : End of modification return; // move back up the stack stackPos--; --- a/src/org/sunflow/core/light/ImageBasedLight.java +++ b/src/org/sunflow/core/light/ImageBasedLight.java @@ -11,7 +11,6 @@ import org.sunflow.core.Ray; import org.sunflow.core.Shader; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Bitmap; import org.sunflow.image.Color; import org.sunflow.math.BoundingBox; @@ -55,7 +54,8 @@ public class ImageBasedLight implements numLowSamples = pl.getInt("lowsamples", numLowSamples); String filename = pl.getString("texture", null); if (filename != null) - texture = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + texture = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); // no texture provided if (texture == null) @@ -272,4 +272,14 @@ public class ImageBasedLight implements public Instance createInstance() { return Instance.createTemporary(this, null, this); } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/light/SphereLight.java +++ b/src/org/sunflow/core/light/SphereLight.java @@ -150,4 +150,14 @@ public class SphereLight implements Ligh public Instance createInstance() { return Instance.createTemporary(new Sphere(), Matrix4.translation(center.x, center.y, center.z).multiply(Matrix4.scale(radius)), this); } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/light/SunSkyLight.java +++ b/src/org/sunflow/core/light/SunSkyLight.java @@ -315,6 +315,12 @@ public class SunSkyLight implements Ligh return getSkyRGB(basis.untransform(state.getRay().getDirection())).constrainRGB(); } + // EP : Reused sun sky color + public Color getSunColor() { + return getSkyRGB(basis.untransform(sunDirWorld)).constrainRGB(); + } + // EP : End of modification + public void scatterPhoton(ShadingState state, Color power) { // let photon escape } @@ -334,4 +340,14 @@ public class SunSkyLight implements Ligh public Instance createInstance() { return Instance.createTemporary(this, null, this); } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/light/TriangleMeshLight.java +++ b/src/org/sunflow/core/light/TriangleMeshLight.java @@ -269,4 +269,14 @@ public class TriangleMeshLight extends T } } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/modifiers/BumpMappingModifier.java +++ b/src/org/sunflow/core/modifiers/BumpMappingModifier.java @@ -5,7 +5,6 @@ import org.sunflow.core.Modifier; import org.sunflow.core.ParameterList; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.math.OrthoNormalBasis; public class BumpMappingModifier implements Modifier { @@ -20,7 +19,8 @@ public class BumpMappingModifier impleme public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - bumpTexture = TextureCache.getTexture(api.resolveTextureFilename(filename), true); + // EP : Made texture cache local to a SunFlow API instance + bumpTexture = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), true); scale = pl.getFloat("scale", scale); return bumpTexture != null; } --- a/src/org/sunflow/core/modifiers/NormalMapModifier.java +++ b/src/org/sunflow/core/modifiers/NormalMapModifier.java @@ -5,7 +5,6 @@ import org.sunflow.core.Modifier; import org.sunflow.core.ParameterList; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.math.OrthoNormalBasis; public class NormalMapModifier implements Modifier { @@ -18,7 +17,8 @@ public class NormalMapModifier implement public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - normalMap = TextureCache.getTexture(api.resolveTextureFilename(filename), true); + // EP : Made texture cache local to a SunFlow API instance + normalMap = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), true); return normalMap != null; } --- a/src/org/sunflow/core/photonmap/GlobalPhotonMap.java +++ b/src/org/sunflow/core/photonmap/GlobalPhotonMap.java @@ -260,7 +260,8 @@ public final class GlobalPhotonMap imple t.start(); balance(); t.end(); - UI.taskStop(); + // EP : Replaced task management with interruptions + // UI.taskStop(); UI.printInfo(Module.LIGHT, "Global photon map:"); UI.printInfo(Module.LIGHT, " * Photons stored: %d", storedPhotons); UI.printInfo(Module.LIGHT, " * Photons/estimate: %d", numGather); @@ -328,7 +329,8 @@ public final class GlobalPhotonMap imple curr.data = irr.toRGBE(); temp[i] = curr; } - UI.taskStop(); + // EP : Replaced task management with interruptions + // UI.taskStop(); // resize photon map to only include irradiance photons numGather /= 4; --- a/src/org/sunflow/core/primitive/CornellBox.java +++ b/src/org/sunflow/core/primitive/CornellBox.java @@ -443,4 +443,14 @@ public class CornellBox implements Primi public Instance createInstance() { return Instance.createTemporary(this, null, this); } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/primitive/Hair.java +++ b/src/org/sunflow/core/primitive/Hair.java @@ -258,4 +258,14 @@ public class Hair implements PrimitiveLi public PrimitiveList getBakingPrimitives() { return null; } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/renderer/BucketRenderer.java +++ b/src/org/sunflow/core/renderer/BucketRenderer.java @@ -157,15 +157,22 @@ public class BucketRenderer implements I renderThreads[i].setPriority(scene.getThreadPriority()); renderThreads[i].start(); } - for (int i = 0; i < renderThreads.length; i++) { - try { - renderThreads[i].join(); - } catch (InterruptedException e) { - UI.printError(Module.BCKT, "Bucket processing thread %d of %d was interrupted", i + 1, renderThreads.length); - } finally { - renderThreads[i].updateStats(); + // EP : Moved InterruptedException out of loop to be able to stop all rendering threads + try { + for (int i = 0; i < renderThreads.length; i++) { + try { + renderThreads[i].join(); + } finally { + renderThreads[i].updateStats(); + } + } + } catch (InterruptedException e) { + for (int i = 0; i < renderThreads.length; i++) { + renderThreads[i].interrupt(); } + UI.printError(Module.BCKT, "Bucket processing was interrupted"); } + // EP : End of modification UI.taskStop(); timer.end(); UI.printInfo(Module.BCKT, "Render time: %s", timer.toString()); @@ -183,7 +190,8 @@ public class BucketRenderer implements I @Override public void run() { - while (true) { + // EP : Check rendering isn't interrupted + while (!isInterrupted()) { int bx, by; synchronized (BucketRenderer.this) { if (bucketCounter >= bucketCoords.length) @@ -194,8 +202,6 @@ public class BucketRenderer implements I bucketCounter += 2; } renderBucket(display, bx, by, threadID, istate); - if (UI.taskCanceled()) - return; } } @@ -251,8 +257,13 @@ public class BucketRenderer implements I } } for (int x = 0; x < sbw - 1; x += maxStepSize) - for (int y = 0; y < sbh - 1; y += maxStepSize) + for (int y = 0; y < sbh - 1; y += maxStepSize) { + // EP : Check rendering isn't interrupted + if (Thread.currentThread().isInterrupted()) { + return; + } refineSamples(samples, sbw, x, y, maxStepSize, thresh, istate); + } if (dumpBuckets) { UI.printInfo(Module.BCKT, "Dumping bucket [%d, %d] to file ...", bx, by); GenericBitmap bitmap = new GenericBitmap(sbw, sbh); @@ -297,7 +308,9 @@ public class BucketRenderer implements I if (Math.abs(dy) > fhs) continue; float f = filter.get(dx, dy); - c.madd(f, samples[s].c); + // EP : Test if color isn't null + if (samples[s].c != null) + c.madd(f, samples[s].c); a += f * samples[s].alpha; weight += f; --- a/src/org/sunflow/core/renderer/MultipassRenderer.java +++ b/src/org/sunflow/core/renderer/MultipassRenderer.java @@ -86,15 +86,22 @@ public class MultipassRenderer implement renderThreads[i].setPriority(scene.getThreadPriority()); renderThreads[i].start(); } - for (int i = 0; i < renderThreads.length; i++) { - try { - renderThreads[i].join(); - } catch (InterruptedException e) { - UI.printError(Module.BCKT, "Bucket processing thread %d of %d was interrupted", i + 1, renderThreads.length); - } finally { - renderThreads[i].updateStats(); + // EP : Moved InterruptedException out of loop to be able to stop all rendering threads + try { + for (int i = 0; i < renderThreads.length; i++) { + try { + renderThreads[i].join(); + } finally { + renderThreads[i].updateStats(); + } } + } catch (InterruptedException e) { + for (int i = 0; i < renderThreads.length; i++) { + renderThreads[i].interrupt(); + } + UI.printError(Module.BCKT, "Bucket processing was interrupted"); } + // EP : End of modification UI.taskStop(); timer.end(); UI.printInfo(Module.BCKT, "Render time: %s", timer.toString()); @@ -114,7 +121,8 @@ public class MultipassRenderer implement @Override public void run() { - while (true) { + // EP : Check rendering isn't interrupted or canceled + while (!isInterrupted()) { int bx, by; synchronized (MultipassRenderer.this) { if (bucketCounter >= bucketCoords.length) --- a/src/org/sunflow/core/renderer/ProgressiveRenderer.java +++ b/src/org/sunflow/core/renderer/ProgressiveRenderer.java @@ -58,15 +58,22 @@ public class ProgressiveRenderer impleme renderThreads[i] = new SmallBucketThread(); renderThreads[i].start(); } - for (int i = 0; i < renderThreads.length; i++) { - try { - renderThreads[i].join(); - } catch (InterruptedException e) { - UI.printError(Module.IPR, "Thread %d of %d was interrupted", i + 1, renderThreads.length); - } finally { - renderThreads[i].updateStats(); + // EP : Moved InterruptedException out of loop to be able to stop all rendering threads + try { + for (int i = 0; i < renderThreads.length; i++) { + try { + renderThreads[i].join(); + } finally { + renderThreads[i].updateStats(); + } + } + } catch (InterruptedException e) { + for (int i = 0; i < renderThreads.length; i++) { + renderThreads[i].interrupt(); } + UI.printError(Module.IPR, "Thread was interrupted"); } + // EP : End of modification UI.taskStop(); t.end(); UI.printInfo(Module.IPR, "Rendering time: %s", t.toString()); @@ -78,7 +85,8 @@ public class ProgressiveRenderer impleme @Override public void run() { - while (true) { + // EP : Check rendering isn't interrupted + while (!isInterrupted()) { int n = progressiveRenderNext(istate); synchronized (ProgressiveRenderer.this) { if (counter >= counterMax) @@ -86,8 +94,6 @@ public class ProgressiveRenderer impleme counter += n; UI.taskUpdate(counter); } - if (UI.taskCanceled()) - return; } } --- a/src/org/sunflow/core/renderer/SimpleRenderer.java +++ b/src/org/sunflow/core/renderer/SimpleRenderer.java @@ -41,15 +41,23 @@ public class SimpleRenderer implements I renderThreads[i] = new BucketThread(); renderThreads[i].start(); } - for (int i = 0; i < renderThreads.length; i++) { - try { - renderThreads[i].join(); - } catch (InterruptedException e) { - UI.printError(Module.BCKT, "Bucket processing thread %d of %d was interrupted", i + 1, renderThreads.length); - } finally { - renderThreads[i].updateStats(); + // EP : Moved InterruptedException out of loop to be able to stop all rendering threads + try { + for (int i = 0; i < renderThreads.length; i++) { + try { + renderThreads[i].join(); + } finally { + renderThreads[i].updateStats(); + } + } + } catch (InterruptedException e) { + for (int i = 0; i < renderThreads.length; i++) { + renderThreads[i].interrupt(); } + UI.printError(Module.BCKT, "Bucket processing was interrupted"); } + UI.taskStop(); + // EP : End of modification timer.end(); UI.printInfo(Module.BCKT, "Render time: %s", timer.toString()); display.imageEnd(); @@ -60,7 +68,8 @@ public class SimpleRenderer implements I @Override public void run() { - while (true) { + // EP : Check rendering isn't interrupted + while (!isInterrupted()) { int bx, by; synchronized (SimpleRenderer.this) { if (bucketCounter >= numBuckets) @@ -90,6 +99,9 @@ public class SimpleRenderer implements I for (int y = 0, i = 0; y < bh; y++) { for (int x = 0; x < bw; x++, i++) { + // EP : Check rendering isn't interrupted + if (Thread.currentThread().isInterrupted()) + return; ShadingState state = scene.getRadiance(istate, x0 + x, imageHeight - 1 - (y0 + y), 0.0, 0.0, 0.0, 0, 0, null); bucketRGB[i] = (state != null) ? state.getResult() : Color.BLACK; bucketAlpha[i] = (state != null) ? 1 : 0; --- a/src/org/sunflow/core/shader/AmbientOcclusionShader.java +++ b/src/org/sunflow/core/shader/AmbientOcclusionShader.java @@ -45,4 +45,14 @@ public class AmbientOcclusionShader impl public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/AnisotropicWardShader.java +++ b/src/org/sunflow/core/shader/AnisotropicWardShader.java @@ -208,4 +208,14 @@ public class AnisotropicWardShader imple state.traceReflectionPhoton(r, power); } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/ConstantShader.java +++ b/src/org/sunflow/core/shader/ConstantShader.java @@ -24,4 +24,14 @@ public class ConstantShader implements S public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/DiffuseShader.java +++ b/src/org/sunflow/core/shader/DiffuseShader.java @@ -58,4 +58,14 @@ public class DiffuseShader implements Sh state.traceDiffusePhoton(new Ray(state.getPoint(), w), power); } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/GlassShader.java +++ b/src/org/sunflow/core/shader/GlassShader.java @@ -136,4 +136,14 @@ public class GlassShader implements Shad } } } + + // EP : Added transparency management + public boolean isOpaque() { + return absorptionColor.isWhite(); + } + + public Color getOpacity(ShadingState state) { + return absorptionColor; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/IDShader.java +++ b/src/org/sunflow/core/shader/IDShader.java @@ -20,4 +20,14 @@ public class IDShader implements Shader public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/MirrorShader.java +++ b/src/org/sunflow/core/shader/MirrorShader.java @@ -59,4 +59,14 @@ public class MirrorShader implements Sha dir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; state.traceReflectionPhoton(new Ray(state.getPoint(), dir), power); } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/NormalShader.java +++ b/src/org/sunflow/core/shader/NormalShader.java @@ -24,4 +24,14 @@ public class NormalShader implements Sha public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/PhongShader.java +++ b/src/org/sunflow/core/shader/PhongShader.java @@ -82,4 +82,14 @@ public class PhongShader implements Shad state.traceReflectionPhoton(new Ray(state.getPoint(), w), power); } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/PrimIDShader.java +++ b/src/org/sunflow/core/shader/PrimIDShader.java @@ -23,4 +23,14 @@ public class PrimIDShader implements Sha public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/QuickGrayShader.java +++ b/src/org/sunflow/core/shader/QuickGrayShader.java @@ -56,4 +56,14 @@ public class QuickGrayShader implements state.traceDiffusePhoton(new Ray(state.getPoint(), w), power); } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/ShinyDiffuseShader.java +++ b/src/org/sunflow/core/shader/ShinyDiffuseShader.java @@ -24,6 +24,12 @@ public class ShinyDiffuseShader implemen return true; } + // EP : Added getter to read shininess from subclasses + protected float getShininess() { + return this.refl; + } + // EP : End of modification + public Color getDiffuse(ShadingState state) { return diff; } @@ -90,4 +96,14 @@ public class ShinyDiffuseShader implemen state.traceReflectionPhoton(new Ray(state.getPoint(), dir), power); } } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/SimpleShader.java +++ b/src/org/sunflow/core/shader/SimpleShader.java @@ -17,4 +17,14 @@ public class SimpleShader implements Sha public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java +++ b/src/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java @@ -4,7 +4,6 @@ import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; public class TexturedAmbientOcclusionShader extends AmbientOcclusionShader { @@ -18,7 +17,8 @@ public class TexturedAmbientOcclusionSha public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + tex = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); return tex != null && super.update(pl, api); } --- a/src/org/sunflow/core/shader/TexturedDiffuseShader.java +++ b/src/org/sunflow/core/shader/TexturedDiffuseShader.java @@ -2,10 +2,11 @@ package org.sunflow.core.shader; import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; +import org.sunflow.core.Ray; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; +import org.sunflow.math.Vector3; public class TexturedDiffuseShader extends DiffuseShader { private Texture tex; @@ -18,7 +19,8 @@ public class TexturedDiffuseShader exten public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + tex = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); return tex != null && super.update(pl, api); } @@ -26,4 +28,32 @@ public class TexturedDiffuseShader exten public Color getDiffuse(ShadingState state) { return tex.getPixel(state.getUV().x, state.getUV().y); } + + // EP : Added transparency management + @Override + public Color getRadiance(ShadingState state) { + Color opacity; + if (isOpaque() || (opacity = getOpacity(state)).isWhite()) { + // Pixel is fully opaque + return super.getRadiance(state); + } else { + state.faceforward(); + state.initLightSamples(); + state.initCausticSamples(); + Vector3 refrDir = state.getRay().getDirection(); + Color refraction = state.traceRefraction(new Ray(state.getPoint(), refrDir), 0); + return Color.sub(Color.WHITE, opacity).mul(refraction); + } + } + + @Override + public boolean isOpaque() { + return !(tex.isTransparent()); + } + + @Override + public Color getOpacity(ShadingState state) { + return tex.getOpacity(state.getUV().x, state.getUV().y); + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/TexturedPhongShader.java +++ b/src/org/sunflow/core/shader/TexturedPhongShader.java @@ -2,10 +2,11 @@ package org.sunflow.core.shader; import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; +import org.sunflow.core.Ray; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; +import org.sunflow.math.Vector3; public class TexturedPhongShader extends PhongShader { private Texture tex; @@ -18,7 +19,8 @@ public class TexturedPhongShader extends public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + tex = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); return tex != null && super.update(pl, api); } @@ -26,4 +28,31 @@ public class TexturedPhongShader extends public Color getDiffuse(ShadingState state) { return tex.getPixel(state.getUV().x, state.getUV().y); } + + // EP : Added transparency management + @Override + public Color getRadiance(ShadingState state) { + Color opacity; + if (isOpaque() || (opacity = getOpacity(state)).isWhite()) { + return super.getRadiance(state); + } else { + state.faceforward(); + state.initLightSamples(); + state.initCausticSamples(); + Vector3 refrDir = state.getRay().getDirection(); + Color refraction = state.traceRefraction(new Ray(state.getPoint(), refrDir), 0); + return Color.sub(Color.WHITE, opacity).mul(refraction); + } + } + + @Override + public boolean isOpaque() { + return !(tex.isTransparent()); + } + + @Override + public Color getOpacity(ShadingState state) { + return tex.getOpacity(state.getUV().x, state.getUV().y); + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/TexturedShinyDiffuseShader.java +++ b/src/org/sunflow/core/shader/TexturedShinyDiffuseShader.java @@ -2,10 +2,11 @@ package org.sunflow.core.shader; import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; +import org.sunflow.core.Ray; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; +import org.sunflow.math.Vector3; public class TexturedShinyDiffuseShader extends ShinyDiffuseShader { private Texture tex; @@ -18,7 +19,8 @@ public class TexturedShinyDiffuseShader public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + tex = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); return tex != null && super.update(pl, api); } @@ -26,4 +28,56 @@ public class TexturedShinyDiffuseShader public Color getDiffuse(ShadingState state) { return tex.getPixel(state.getUV().x, state.getUV().y); } + + // EP : Added transparency management + @Override + public Color getRadiance(ShadingState state) { + Color opacity; + if (isOpaque() || (opacity = getOpacity(state)).isWhite()) { + // Pixel is fully opaque + return super.getRadiance(state); + } else { + state.faceforward(); + // direct lighting + state.initLightSamples(); + state.initCausticSamples(); + Color d = Color.sub(Color.WHITE, opacity); + Vector3 refrDir = state.getRay().getDirection(); + Color refraction = state.traceRefraction(new Ray(state.getPoint(), refrDir), 0); + d.mul(refraction); + if (!state.includeSpecular() + || opacity.isBlack()) { // No reflection when fully transparent + return d; + } + float cos = state.getCosND(); + float dn = 2 * cos; + Vector3 refDir = new Vector3(); + refDir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x; + refDir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y; + refDir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; + Ray refRay = new Ray(state.getPoint(), refDir); + // compute Fresnel term + cos = 1 - cos; + float cos2 = cos * cos; + float cos5 = cos2 * cos2 * cos; + + Color ret = Color.white(); + Color r = Color.sub(Color.WHITE, opacity).mul(getShininess()); + ret.sub(r); + ret.mul(cos5); + ret.add(r); + return d.add(ret.mul(state.traceReflection(refRay, 0))); + } + } + + @Override + public boolean isOpaque() { + return !(tex.isTransparent()); + } + + @Override + public Color getOpacity(ShadingState state) { + return tex.getOpacity(state.getUV().x, state.getUV().y); + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/TexturedWardShader.java +++ b/src/org/sunflow/core/shader/TexturedWardShader.java @@ -4,7 +4,6 @@ import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; public class TexturedWardShader extends AnisotropicWardShader { @@ -18,7 +17,8 @@ public class TexturedWardShader extends public boolean update(ParameterList pl, SunflowAPI api) { String filename = pl.getString("texture", null); if (filename != null) - tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + tex = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); return tex != null && super.update(pl, api); } @@ -26,4 +26,16 @@ public class TexturedWardShader extends public Color getDiffuse(ShadingState state) { return tex.getPixel(state.getUV().x, state.getUV().y); } + + // EP : Added transparency management + @Override + public boolean isOpaque() { + return !(tex.isTransparent()); + } + + @Override + public Color getOpacity(ShadingState state) { + return tex.getOpacity(state.getUV().x, state.getUV().y); + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/UVShader.java +++ b/src/org/sunflow/core/shader/UVShader.java @@ -19,4 +19,14 @@ public class UVShader implements Shader public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/UberShader.java +++ b/src/org/sunflow/core/shader/UberShader.java @@ -6,7 +6,6 @@ import org.sunflow.core.Ray; import org.sunflow.core.Shader; import org.sunflow.core.ShadingState; import org.sunflow.core.Texture; -import org.sunflow.core.TextureCache; import org.sunflow.image.Color; import org.sunflow.math.MathUtils; import org.sunflow.math.OrthoNormalBasis; @@ -36,10 +35,12 @@ public class UberShader implements Shade String filename; filename = pl.getString("diffuse.texture", null); if (filename != null) - diffmap = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + diffmap = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); filename = pl.getString("specular.texture", null); if (filename != null) - specmap = TextureCache.getTexture(api.resolveTextureFilename(filename), false); + // EP : Made texture cache local to a SunFlow API instance + specmap = api.getTextureCache().getTexture(api.resolveTextureFilename(filename), false); diffBlend = MathUtils.clamp(pl.getFloat("diffuse.blend", diffBlend), 0, 1); specBlend = MathUtils.clamp(pl.getFloat("specular.blend", diffBlend), 0, 1); glossyness = MathUtils.clamp(pl.getFloat("glossyness", glossyness), 0, 1); @@ -61,30 +62,41 @@ public class UberShader implements Shade // direct lighting state.initLightSamples(); state.initCausticSamples(); - Color d = getDiffuse(state); - Color lr = state.diffuse(d); - if (!state.includeSpecular()) - return lr; - if (glossyness == 0) { - float cos = state.getCosND(); - float dn = 2 * cos; - Vector3 refDir = new Vector3(); - refDir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x; - refDir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y; - refDir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; - Ray refRay = new Ray(state.getPoint(), refDir); - // compute Fresnel term - cos = 1 - cos; - float cos2 = cos * cos; - float cos5 = cos2 * cos2 * cos; - Color spec = getSpecular(state); - Color ret = Color.white(); - ret.sub(spec); - ret.mul(cos5); - ret.add(spec); - return lr.add(ret.mul(state.traceReflection(refRay, 0))); - } else - return lr.add(state.specularPhong(getSpecular(state), 2 / glossyness, numSamples)); + // EP : Added transparency management + Color opacity; + if (!isOpaque() && !(opacity = getOpacity(state)).isWhite()) { + Vector3 refrDir = state.getRay().getDirection(); + Color refraction = state.traceRefraction(new Ray(state.getPoint(), refrDir), 0); + return Color.sub(Color.WHITE, opacity).mul(refraction); + } else { + // EP : End of modification + Color d = getDiffuse(state); + Color lr = state.diffuse(d); + if (!state.includeSpecular()) + return lr; + if (glossyness == 0) { + float cos = state.getCosND(); + float dn = 2 * cos; + Vector3 refDir = new Vector3(); + refDir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x; + refDir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y; + refDir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; + Ray refRay = new Ray(state.getPoint(), refDir); + // compute Fresnel term + cos = 1 - cos; + float cos2 = cos * cos; + float cos5 = cos2 * cos2 * cos; + Color spec = getSpecular(state); + Color ret = Color.white(); + ret.sub(spec); + ret.mul(cos5); + ret.add(spec); + return lr.add(ret.mul(state.traceReflection(refRay, 0))); + } else + return lr.add(state.specularPhong(getSpecular(state), 2 / glossyness, numSamples)); + // EP : Added transparency management + } + // EP : End of modification } public void scatterPhoton(ShadingState state, Color power) { @@ -138,4 +150,14 @@ public class UberShader implements Shade } } } + + // EP : Added transparency management + public boolean isOpaque() { + return diffmap == null || !(diffmap.isTransparent()); + } + + public Color getOpacity(ShadingState state) { + return diffmap != null ? diffmap.getOpacity(state.getUV().x, state.getUV().y) : Color.WHITE; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/ViewCausticsShader.java +++ b/src/org/sunflow/core/shader/ViewCausticsShader.java @@ -25,4 +25,14 @@ public class ViewCausticsShader implemen public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/ViewGlobalPhotonsShader.java +++ b/src/org/sunflow/core/shader/ViewGlobalPhotonsShader.java @@ -18,4 +18,14 @@ public class ViewGlobalPhotonsShader imp public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/ViewIrradianceShader.java +++ b/src/org/sunflow/core/shader/ViewIrradianceShader.java @@ -18,4 +18,14 @@ public class ViewIrradianceShader implem public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/core/shader/WireframeShader.java +++ b/src/org/sunflow/core/shader/WireframeShader.java @@ -73,4 +73,14 @@ public class WireframeShader implements public void scatterPhoton(ShadingState state, Color power) { } + + // EP : Added transparency management + public boolean isOpaque() { + return true; + } + + public Color getOpacity(ShadingState state) { + return null; + } + // EP : End of modification } \ No newline at end of file --- a/src/org/sunflow/image/Color.java +++ b/src/org/sunflow/image/Color.java @@ -116,6 +116,12 @@ public final class Color { return r <= 0 && g <= 0 && b <= 0; } + // EP : Added to manage white colors + public boolean isWhite() { + return r >= 1 && g >= 1 && b >= 1; + } + // EP : End of modification + public final float getLuminance() { return (0.2989f * r) + (0.5866f * g) + (0.1145f * b); } --- a/src/org/sunflow/image/readers/BMPBitmapReader.java +++ b/src/org/sunflow/image/readers/BMPBitmapReader.java @@ -2,7 +2,14 @@ package org.sunflow.image.readers; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import javax.imageio.ImageIO; @@ -13,8 +20,41 @@ import org.sunflow.image.formats.BitmapR public class BMPBitmapReader implements BitmapReader { public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - // regular image, load using Java api - ignore alpha channel - BufferedImage bi = ImageIO.read(new File(filename)); + // EP : Try to read filename as an URL or as a file + InputStream f; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + f = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + f = new FileInputStream(filename); + } + + BufferedImage bi; + try { + // regular image, load using Java api - ignore alpha channel + bi = ImageIO.read(f); + } finally { + f.close(); + } + // EP : End of modification + int width = bi.getWidth(); int height = bi.getHeight(); byte[] pixels = new byte[3 * width * height]; --- a/src/org/sunflow/image/readers/HDRBitmapReader.java +++ b/src/org/sunflow/image/readers/HDRBitmapReader.java @@ -1,9 +1,15 @@ package org.sunflow.image.readers; import java.io.BufferedInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import org.sunflow.image.Bitmap; import org.sunflow.image.BitmapReader; @@ -11,8 +17,35 @@ import org.sunflow.image.formats.BitmapR public class HDRBitmapReader implements BitmapReader { public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - // load radiance rgbe file - InputStream f = new BufferedInputStream(new FileInputStream(filename)); + // EP : Try to read filename as an URL or as a file + InputStream f; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + f = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + f = new FileInputStream(filename); + } + + f = new BufferedInputStream(f); + // End of modification + // parse header boolean parseWidth = false, parseHeight = false; int width = 0, height = 0; --- a/src/org/sunflow/image/readers/IGIBitmapReader.java +++ b/src/org/sunflow/image/readers/IGIBitmapReader.java @@ -1,9 +1,15 @@ package org.sunflow.image.readers; import java.io.BufferedInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import org.sunflow.image.Bitmap; import org.sunflow.image.BitmapReader; @@ -15,7 +21,35 @@ import org.sunflow.image.formats.BitmapX */ public class IGIBitmapReader implements BitmapReader { public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - InputStream stream = new BufferedInputStream(new FileInputStream(filename)); + // EP : Try to read filename as an URL or as a file + InputStream stream; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + stream = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + stream = new FileInputStream(filename); + } + + stream = new BufferedInputStream(stream); + // End of modification + // read header int magic = read32i(stream); int version = read32i(stream); --- a/src/org/sunflow/image/readers/JPGBitmapReader.java +++ b/src/org/sunflow/image/readers/JPGBitmapReader.java @@ -1,8 +1,17 @@ package org.sunflow.image.readers; +import java.awt.Graphics2D; +import java.awt.Transparency; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import javax.imageio.ImageIO; @@ -13,14 +22,63 @@ import org.sunflow.image.formats.BitmapR public class JPGBitmapReader implements BitmapReader { public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - // regular image, load using Java api - ignore alpha channel - BufferedImage bi = ImageIO.read(new File(filename)); + // EP : Try to read filename as an URL or as a file + InputStream f; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + f = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + f = new FileInputStream(filename); + } + + BufferedImage bi; + try { + // regular image, load using Java api - ignore alpha channel + bi = ImageIO.read(f); + } finally { + f.close(); + } + + if (bi.getType() != BufferedImage.TYPE_INT_RGB + && bi.getType() != BufferedImage.TYPE_INT_ARGB) { + // Transform as TYPE_INT_ARGB or TYPE_INT_RGB (much faster than calling image.getRGB()) + BufferedImage tmp = new BufferedImage(bi.getWidth(), bi.getHeight(), + bi.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)tmp.getGraphics(); + g.drawImage(bi, null, 0, 0); + g.dispose(); + bi = tmp; + } + // Retrieve image bits + int [] imageBits = (int [])bi.getRaster().getDataElements(0, 0, bi.getWidth(), bi.getHeight(), null); + // EP : End of modification + int width = bi.getWidth(); int height = bi.getHeight(); byte[] pixels = new byte[3 * width * height]; for (int y = 0, index = 0; y < height; y++) { for (int x = 0; x < width; x++, index += 3) { - int argb = bi.getRGB(x, height - 1 - y); + // EP : Retrieved image data with raster data + // int argb = bi.getRGB(x, height - 1 - y); + int argb = imageBits [x + (height - 1 - y) * width]; + // EP : End of modification pixels[index + 0] = (byte) (argb >> 16); pixels[index + 1] = (byte) (argb >> 8); pixels[index + 2] = (byte) argb; --- a/src/org/sunflow/image/readers/PNGBitmapReader.java +++ b/src/org/sunflow/image/readers/PNGBitmapReader.java @@ -1,8 +1,17 @@ package org.sunflow.image.readers; +import java.awt.Graphics2D; +import java.awt.Transparency; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import javax.imageio.ImageIO; @@ -13,18 +22,70 @@ import org.sunflow.image.formats.BitmapR public class PNGBitmapReader implements BitmapReader { public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - // regular image, load using Java api - BufferedImage bi = ImageIO.read(new File(filename)); + // EP : Try to read filename as an URL or as a file + InputStream f; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + f = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + f = new FileInputStream(filename); + } + + BufferedImage bi; + try { + // regular image, load using Java api + bi = ImageIO.read(f); + } finally { + f.close(); + } + + boolean opaque = bi.getTransparency() == Transparency.OPAQUE; + if (bi.getType() != BufferedImage.TYPE_INT_RGB + && bi.getType() != BufferedImage.TYPE_INT_ARGB) { + // Transform as TYPE_INT_ARGB or TYPE_INT_RGB (much faster than calling image.getRGB()) + BufferedImage tmp = new BufferedImage(bi.getWidth(), bi.getHeight(), + opaque ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)tmp.getGraphics(); + g.drawImage(bi, null, 0, 0); + g.dispose(); + bi = tmp; + } + // Retrieve image bits + int [] imageBits = (int [])bi.getRaster().getDataElements(0, 0, bi.getWidth(), bi.getHeight(), null); + // EP : End of modification + int width = bi.getWidth(); int height = bi.getHeight(); byte[] pixels = new byte[4 * width * height]; for (int y = 0, index = 0; y < height; y++) { for (int x = 0; x < width; x++, index += 4) { - int argb = bi.getRGB(x, height - 1 - y); + // EP : Retrieved image data with raster data + // int argb = bi.getRGB(x, height - 1 - y); + int argb = imageBits [x + (height - 1 - y) * width]; + // EP : End of modification pixels[index + 0] = (byte) (argb >> 16); pixels[index + 1] = (byte) (argb >> 8); pixels[index + 2] = (byte) argb; - pixels[index + 3] = (byte) (argb >> 24); + // EP : Added opaque transparency + pixels[index + 3] = opaque ? (byte)0xFF : (byte) (argb >> 24); + // EP : End of modification } } if (!isLinear) { --- a/src/org/sunflow/image/readers/TGABitmapReader.java +++ b/src/org/sunflow/image/readers/TGABitmapReader.java @@ -1,9 +1,15 @@ package org.sunflow.image.readers; import java.io.BufferedInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; import org.sunflow.image.Bitmap; import org.sunflow.image.BitmapReader; @@ -16,7 +22,35 @@ public class TGABitmapReader implements private static final int[] CHANNEL_INDEX = { 2, 1, 0, 3 }; public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { - InputStream f = new BufferedInputStream(new FileInputStream(filename)); + // EP : Try to read filename as an URL or as a file + InputStream f; + try { + // Let's try first to read filename as an URL + URLConnection connection = new URL(filename).openConnection(); + if (connection instanceof JarURLConnection) { + JarURLConnection urlConnection = (JarURLConnection) connection; + URL jarFileUrl = urlConnection.getJarFileURL(); + if (jarFileUrl.getProtocol().equalsIgnoreCase("file")) { + try { + if (new File(jarFileUrl.toURI()).canWrite()) { + // Refuse to use cache to be able to delete the writable files accessed with jar protocol, + // as suggested in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962459 + connection.setUseCaches(false); + } + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } + f = connection.getInputStream(); + } catch (MalformedURLException ex) { + // Let's try to read filename as a file + f = new FileInputStream(filename); + } + + f = new BufferedInputStream(f); + // End of modification + byte[] read = new byte[4]; // read header --- a/src/org/sunflow/system/ImagePanel.java +++ b/src/org/sunflow/system/ImagePanel.java @@ -4,7 +4,6 @@ import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -78,10 +77,11 @@ public class ImagePanel extends JPanel i mouseDragged(e); } - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - zoom(-20 * e.getWheelRotation(), 0); - } +// EP : Support Java 1.5 +// @Override +// public void mouseWheelMoved(MouseWheelEvent e) { +// zoom(-20 * e.getWheelRotation(), 0); +// } } public ImagePanel() { @@ -92,7 +92,8 @@ public class ImagePanel extends JPanel i ScrollZoomListener listener = new ScrollZoomListener(); addMouseListener(listener); addMouseMotionListener(listener); - addMouseWheelListener(listener); +// EP : Support Java 1.5 +// addMouseWheelListener(listener); } public void save(String filename) { --- a/src/org/sunflow/system/Plugins.java +++ b/src/org/sunflow/system/Plugins.java @@ -1,9 +1,5 @@ package org.sunflow.system; -import org.codehaus.janino.ClassBodyEvaluator; -import org.codehaus.janino.CompileException; -import org.codehaus.janino.Parser.ParseException; -import org.codehaus.janino.Scanner.ScanException; import org.sunflow.system.UI.Module; import org.sunflow.util.FastHashMap; @@ -98,25 +94,27 @@ public final class Plugins { */ @SuppressWarnings("unchecked") public boolean registerPlugin(String name, String sourceCode) { - try { - ClassBodyEvaluator cbe = new ClassBodyEvaluator(); - cbe.setClassName(name); - if (baseClass.isInterface()) - cbe.setImplementedTypes(new Class[] { baseClass }); - else - cbe.setExtendedType(baseClass); - cbe.cook(sourceCode); - return registerPlugin(name, cbe.getClazz()); - } catch (CompileException e) { - UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); - return false; - } catch (ParseException e) { - UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); - return false; - } catch (ScanException e) { - UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); - return false; - } +// EP : Don't need parser +// try { +// ClassBodyEvaluator cbe = new ClassBodyEvaluator(); +// cbe.setClassName(name); +// if (baseClass.isInterface()) +// cbe.setImplementedTypes(new Class[] { baseClass }); +// else +// cbe.setExtendedType(baseClass); +// cbe.cook(sourceCode); +// return registerPlugin(name, cbe.getClazz()); +// } catch (CompileException e) { +// UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); +// return false; +// } catch (ParseException e) { +// UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); +// return false; +// } catch (ScanException e) { +// UI.printError(Module.API, "Plugin \"%s\" could not be declared - %s", name, e.getLocalizedMessage()); +// return false; +// } + return false; } /** debian/patches/10_to_use_system_janino0000644000000000000000000000067512207104023015277 0ustar Description: To replace removed janino.jar with system janino Author: Cyril Brulebois --- sunflow-0.07.2.svn396+dfsg.orig/build.xml +++ sunflow-0.07.2.svn396+dfsg/build.xml @@ -49,7 +49,9 @@ + debian/patches/20_split0000644000000000000000000000150112207104023012160 0ustar Description: Split sunflow.jar between sunflow.jar and sunflowGUI.jar Author: Gabriele Giacone <1o5g4r8o@gmail.com> --- a/build.xml +++ b/build.xml @@ -45,8 +45,11 @@ - - + + + + +