├── janino.jar ├── sunflow.sh ├── examples ├── sky_small.hdr ├── textures │ ├── brick_color.jpg │ ├── brick_normal.jpg │ ├── dirty_bump.jpg │ ├── slime_bump.jpg │ ├── shiphull_bump.png │ └── reptileskin_bump.png ├── shader_examples │ ├── AO002.sc │ ├── Mirror001.sc │ ├── Phong003.sc │ ├── Glass001.sc │ ├── Phong004.sc │ ├── Shiny001.sc │ ├── Glass002.sc │ ├── AO001.sc │ ├── Phong002.sc │ ├── Glass003.sc │ ├── Ward001.sc │ ├── Phong001.sc │ ├── Ward002.sc │ └── include │ │ └── example_array.geo.sc ├── sphereflake.sc ├── julia.sc ├── wireframe_demo.java └── cornell_box_jensen.sc ├── resources ├── golden_0020.png ├── golden_0040.png ├── golden_0060.png ├── golden_0080.png ├── golden_0100.png ├── golden_0180.png └── golden_0200.png ├── exporters ├── sketchup │ └── Components │ │ └── su2sf │ │ ├── su2sf_pointlight.skp │ │ ├── su2sf_sphericallight.skp │ │ └── su2sf_directionnallight.skp └── maya │ └── sunflowExporter │ ├── mel │ ├── sunflowDeleteMenu.mel │ ├── sunflowShutdown.mel │ ├── unregisterSunflowRenderer.mel │ ├── sunflowStartup.mel │ ├── sunflowRender.mel │ ├── AEsunflowSkyNodeTemplate.mel │ ├── updateSunflowGlobalsTab.mel │ ├── sunflowCreateMenu.mel │ ├── AEsunflowHelperNodeTemplate.mel │ ├── sunflowUtils.mel │ └── registerSunflowRenderer.mel │ ├── src │ ├── sunflowConstants.h │ ├── sunflowConstants.cpp │ ├── sunflowBucketToRenderView.h │ ├── sunflowHelperNode.h │ ├── sunflowGlobalsNode.h │ ├── sunflowExportCmd.h │ ├── sunflowShaderNode.h │ ├── sunflowBucketToRenderView.cpp │ ├── pluginMain.cpp │ ├── sunflowSkyNode.h │ └── skylight.h │ ├── sunflowExport.sln │ └── SConstruct ├── sunflow.bat ├── .settings ├── org.eclipse.ltk.core.refactoring.prefs └── org.eclipse.jdt.ui.prefs ├── .classpath ├── src └── org │ └── sunflow │ ├── core │ ├── filter │ │ ├── BoxFilter.java │ │ ├── TriangleFilter.java │ │ ├── SincFilter.java │ │ ├── GaussianFilter.java │ │ ├── CatmullRomFilter.java │ │ ├── CubicBSpline.java │ │ ├── LanczosFilter.java │ │ ├── BlackmanHarrisFilter.java │ │ └── MitchellFilter.java │ ├── CausticPhotonMapInterface.java │ ├── Modifier.java │ ├── bucket │ │ ├── RowBucketOrder.java │ │ ├── ColumnBucketOrder.java │ │ ├── DiagonalBucketOrder.java │ │ ├── InvertedBucketOrder.java │ │ ├── BucketOrderFactory.java │ │ ├── SpiralBucketOrder.java │ │ └── RandomBucketOrder.java │ ├── Options.java │ ├── AccelerationStructure.java │ ├── shader │ │ ├── SimpleShader.java │ │ ├── ViewGlobalPhotonsShader.java │ │ ├── UVShader.java │ │ ├── ViewIrradianceShader.java │ │ ├── ConstantShader.java │ │ ├── IDShader.java │ │ ├── NormalShader.java │ │ ├── PrimIDShader.java │ │ ├── ViewCausticsShader.java │ │ ├── TexturedPhongShader.java │ │ ├── TexturedDiffuseShader.java │ │ ├── TexturedWardShader.java │ │ ├── TexturedShinyDiffuseShader.java │ │ ├── TexturedAmbientOcclusionShader.java │ │ ├── AmbientOcclusionShader.java │ │ ├── DiffuseShader.java │ │ ├── MirrorShader.java │ │ └── QuickGrayShader.java │ ├── SceneParser.java │ ├── GlobalPhotonMapInterface.java │ ├── BucketOrder.java │ ├── camera │ │ ├── FisheyeLens.java │ │ ├── SphericalLens.java │ │ └── PinholeLens.java │ ├── accel │ │ └── NullAccelerator.java │ ├── Filter.java │ ├── ImageSampler.java │ ├── RenderObject.java │ ├── Shader.java │ ├── modifiers │ │ ├── NormalMapModifier.java │ │ ├── BumpMappingModifier.java │ │ └── PerlinModifier.java │ ├── AccelerationStructureFactory.java │ ├── primitive │ │ └── Background.java │ ├── CameraLens.java │ ├── Tesselatable.java │ ├── gi │ │ ├── FakeGIEngine.java │ │ ├── AmbientOcclusionGIEngine.java │ │ └── PathTracingGIEngine.java │ ├── GIEngine.java │ ├── TextureCache.java │ ├── PhotonStore.java │ ├── ShadingCache.java │ ├── InstanceList.java │ ├── light │ │ └── PointLight.java │ ├── display │ │ └── FileDisplay.java │ ├── parser │ │ └── RA3Parser.java │ ├── PrimitiveList.java │ └── LightSource.java │ ├── image │ ├── Bitmap.java │ ├── BlackbodySpectrum.java │ ├── formats │ │ ├── BitmapBlack.java │ │ ├── BitmapGA8.java │ │ ├── BitmapG8.java │ │ ├── BitmapXYZ.java │ │ ├── BitmapRGB8.java │ │ ├── BitmapRGBA8.java │ │ ├── BitmapRGBE.java │ │ └── GenericBitmap.java │ ├── ConstantSpectralCurve.java │ ├── XYZColor.java │ ├── RegularSpectralCurve.java │ ├── writers │ │ ├── PNGBitmapWriter.java │ │ ├── HDRBitmapWriter.java │ │ ├── TGABitmapWriter.java │ │ └── IGIBitmapWriter.java │ ├── BitmapReader.java │ ├── readers │ │ ├── BMPBitmapReader.java │ │ ├── JPGBitmapReader.java │ │ └── PNGBitmapReader.java │ └── IrregularSpectralCurve.java │ ├── system │ ├── Memory.java │ ├── BenchmarkTest.java │ ├── ui │ │ ├── SilentInterface.java │ │ └── ConsoleInterface.java │ ├── FileUtils.java │ ├── Timer.java │ ├── UserInterface.java │ ├── SearchPath.java │ └── BenchmarkFramework.java │ ├── math │ └── Point2.java │ └── util │ ├── IntArray.java │ └── FloatArray.java ├── .project ├── LICENSE └── .externalToolBuilders └── Ant Build.launch /janino.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/janino.jar -------------------------------------------------------------------------------- /sunflow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mem=1G 3 | java -Xmx$mem -server -jar sunflow.jar $* 4 | -------------------------------------------------------------------------------- /examples/sky_small.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/sky_small.hdr -------------------------------------------------------------------------------- /resources/golden_0020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0020.png -------------------------------------------------------------------------------- /resources/golden_0040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0040.png -------------------------------------------------------------------------------- /resources/golden_0060.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0060.png -------------------------------------------------------------------------------- /resources/golden_0080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0080.png -------------------------------------------------------------------------------- /resources/golden_0100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0100.png -------------------------------------------------------------------------------- /resources/golden_0180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0180.png -------------------------------------------------------------------------------- /resources/golden_0200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/resources/golden_0200.png -------------------------------------------------------------------------------- /examples/textures/brick_color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/brick_color.jpg -------------------------------------------------------------------------------- /examples/textures/brick_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/brick_normal.jpg -------------------------------------------------------------------------------- /examples/textures/dirty_bump.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/dirty_bump.jpg -------------------------------------------------------------------------------- /examples/textures/slime_bump.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/slime_bump.jpg -------------------------------------------------------------------------------- /examples/textures/shiphull_bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/shiphull_bump.png -------------------------------------------------------------------------------- /examples/textures/reptileskin_bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/examples/textures/reptileskin_bump.png -------------------------------------------------------------------------------- /exporters/sketchup/Components/su2sf/su2sf_pointlight.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/exporters/sketchup/Components/su2sf/su2sf_pointlight.skp -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowDeleteMenu.mel: -------------------------------------------------------------------------------- 1 | global proc sunflowDeleteMenu(){ 2 | if ( `menu -exists sunflowMainWindowMenu` ) deleteUI sunflowMainWindowMenu; 3 | } -------------------------------------------------------------------------------- /exporters/sketchup/Components/su2sf/su2sf_sphericallight.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/exporters/sketchup/Components/su2sf/su2sf_sphericallight.skp -------------------------------------------------------------------------------- /sunflow.bat: -------------------------------------------------------------------------------- 1 | @set javadir="c:\program files\java\jdk1.6.0" 2 | @set mem=1G 3 | @%javadir%\bin\java -Xmx%mem% -server -jar sunflow.jar %* 4 | @if %errorlevel% neq 0 pause 5 | -------------------------------------------------------------------------------- /exporters/sketchup/Components/su2sf/su2sf_directionnallight.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fpsunflower/sunflow/HEAD/exporters/sketchup/Components/su2sf/su2sf_directionnallight.skp -------------------------------------------------------------------------------- /.settings/org.eclipse.ltk.core.refactoring.prefs: -------------------------------------------------------------------------------- 1 | #Sat Sep 23 11:47:05 CDT 2006 2 | eclipse.preferences.version=1 3 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false 4 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowShutdown.mel: -------------------------------------------------------------------------------- 1 | global proc sunflowShutdown(){ 2 | sunflowDeleteMenu(); 3 | unregisterSunflowRenderer(); 4 | print("Sunflow is shutting down!\n"); 5 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/unregisterSunflowRenderer.mel: -------------------------------------------------------------------------------- 1 | global proc unregisterSunflowRenderer(){ 2 | if ( `renderer -q -ex sunflow` ) renderer -unregisterRenderer sunflow; 3 | updateRendererUI(); 4 | } -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | #Sat Sep 23 11:47:05 CDT 2006 2 | eclipse.preferences.version=1 3 | formatter_profile=_Sunflow Conventions 4 | formatter_settings_version=10 5 | internal.default.compliance=default 6 | org.eclipse.jdt.ui.text.custom_code_templates= 7 | -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/BoxFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class BoxFilter implements Filter { 6 | public float getSize() { 7 | return 1.0f; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return 1.0f; 12 | } 13 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/TriangleFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class TriangleFilter implements Filter { 6 | public float getSize() { 7 | return 2; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return (1.0f - Math.abs(x)) * (1.0f - Math.abs(y)); 12 | } 13 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowConstants.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_CONSTANTS_H 2 | #define SUNFLOW_CONSTANTS_H 3 | 4 | extern const char* FILTER_NAMES[]; 5 | extern const unsigned int NUM_FILTER_NAMES; 6 | 7 | extern const char* BUCKET_ORDERS[]; 8 | extern const unsigned int NUM_BUCKET_ORDERS; 9 | 10 | extern const unsigned int DIR_LIGHT_RADIUS; 11 | #endif /* SUNFLOW_CONSTANTS_H */ 12 | -------------------------------------------------------------------------------- /src/org/sunflow/image/Bitmap.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | public abstract class Bitmap { 4 | protected static final float INV255 = 1.0f / 255; 5 | protected static final float INV65535 = 1.0f / 65535; 6 | 7 | public abstract int getWidth(); 8 | 9 | public abstract int getHeight(); 10 | 11 | public abstract Color readColor(int x, int y); 12 | 13 | public abstract float readAlpha(int x, int y); 14 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/CausticPhotonMapInterface.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * This class is a generic interface to caustic photon mapping capabilities. 5 | */ 6 | public interface CausticPhotonMapInterface extends PhotonStore { 7 | /** 8 | * Retrieve caustic photons at the specified shading location and add them 9 | * as diffuse light samples. 10 | * 11 | * @param state 12 | */ 13 | void getSamples(ShadingState state); 14 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/BlackbodySpectrum.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | public class BlackbodySpectrum extends SpectralCurve { 4 | private float temp; 5 | 6 | public BlackbodySpectrum(float temp) { 7 | this.temp = temp; 8 | } 9 | 10 | @Override 11 | public float sample(float lambda) { 12 | double wavelength = lambda * 1e-9; 13 | return (float) ((3.74183e-16 * Math.pow(wavelength, -5.0)) / (Math.exp(1.4388e-2 / (wavelength * temp)) - 1.0)); 14 | } 15 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/Modifier.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * This represents a surface modifier. This is run on each instance prior to 5 | * shading and can modify the shading state in arbitrary ways to provide effects 6 | * such as bump mapping. 7 | */ 8 | public interface Modifier extends RenderObject { 9 | 10 | /** 11 | * Modify the shading state for the point to be shaded. 12 | * 13 | * @param state shading state to modify 14 | */ 15 | public void modify(ShadingState state); 16 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/SincFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class SincFilter implements Filter { 6 | public float getSize() { 7 | return 4; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return sinc1d(x) * sinc1d(y); 12 | } 13 | 14 | private float sinc1d(float x) { 15 | x = Math.abs(x); 16 | if (x < 0.0001f) 17 | return 1.0f; 18 | x *= Math.PI; 19 | return (float) Math.sin(x) / x; 20 | } 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/Memory.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | public final class Memory { 4 | public static final String sizeof(int[] array) { 5 | return bytesToString(array == null ? 0 : 4 * array.length); 6 | } 7 | 8 | public static final String bytesToString(long bytes) { 9 | if (bytes < 1024) 10 | return String.format("%db", bytes); 11 | if (bytes < 1024 * 1024) 12 | return String.format("%dKb", (bytes + 512) >>> 10); 13 | return String.format("%dMb", (bytes + 512 * 1024) >>> 20); 14 | } 15 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/GaussianFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class GaussianFilter implements Filter { 6 | private float es2; 7 | 8 | public GaussianFilter() { 9 | es2 = (float) -Math.exp(-getSize() * getSize()); 10 | } 11 | 12 | public float getSize() { 13 | return 3.0f; 14 | } 15 | 16 | public float get(float x, float y) { 17 | float gx = (float) Math.exp(-x * x) + es2; 18 | float gy = (float) Math.exp(-y * y) + es2; 19 | return gx * gy; 20 | } 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/BenchmarkTest.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | /** 4 | * This interface is used to represent a piece of code which is to be 5 | * benchmarked by repeatedly running and timing the kernel code. The begin/end 6 | * routines are called per-iteration to do any local initialization which is not 7 | * meant to be taken into acount in the timing (like preparing or destroying 8 | * data structures). 9 | */ 10 | public interface BenchmarkTest { 11 | 12 | public void kernelBegin(); 13 | 14 | public void kernelMain(); 15 | 16 | public void kernelEnd(); 17 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowStartup.mel: -------------------------------------------------------------------------------- 1 | global proc sunflowStartup(){ 2 | string $mel_files[] = { 3 | "sunflowUtils.mel", 4 | "createSunflowGlobalsTab.mel", 5 | "updateSunflowGlobalsTab.mel", 6 | "registerSunflowRenderer.mel", 7 | "unregisterSunflowRenderer.mel", 8 | "sunflowRender.mel", 9 | "sunflowCreateMenu.mel", 10 | "sunflowDeleteMenu.mel", 11 | "sunflowShutdown.mel" 12 | }; 13 | 14 | int $i; 15 | for ( $i = 0; $i < size($mel_files); $i++) { 16 | eval( "source \""+$mel_files[$i]+"\"" ); 17 | } 18 | registerSunflowRenderer(); 19 | sunflowCreateMenu(); 20 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapBlack.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapBlack extends Bitmap { 7 | @Override 8 | public int getWidth() { 9 | return 1; 10 | } 11 | 12 | @Override 13 | public int getHeight() { 14 | return 1; 15 | } 16 | 17 | @Override 18 | public Color readColor(int x, int y) { 19 | return Color.BLACK; 20 | } 21 | 22 | @Override 23 | public float readAlpha(int x, int y) { 24 | return 0; 25 | } 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/RowBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class RowBucketOrder implements BucketOrder { 6 | public int[] getBucketSequence(int nbw, int nbh) { 7 | int[] coords = new int[2 * nbw * nbh]; 8 | for (int i = 0; i < nbw * nbh; i++) { 9 | int by = i / nbw; 10 | int bx = i % nbw; 11 | if ((by & 1) == 1) 12 | bx = nbw - 1 - bx; 13 | coords[2 * i + 0] = bx; 14 | coords[2 * i + 1] = by; 15 | } 16 | return coords; 17 | } 18 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/ColumnBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class ColumnBucketOrder implements BucketOrder { 6 | public int[] getBucketSequence(int nbw, int nbh) { 7 | int[] coords = new int[2 * nbw * nbh]; 8 | for (int i = 0; i < nbw * nbh; i++) { 9 | int bx = i / nbh; 10 | int by = i % nbh; 11 | if ((bx & 1) == 1) 12 | by = nbh - 1 - by; 13 | coords[2 * i + 0] = bx; 14 | coords[2 * i + 1] = by; 15 | } 16 | return coords; 17 | } 18 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowRender.mel: -------------------------------------------------------------------------------- 1 | global proc sunflowRender(int $width, int $height, int $doShadows, int $doGlowPass, string $camera, string $option){ 2 | // sunflowCheckGlobals; 3 | sunflowExportCmd($width, $height, $doShadows, $doGlowPass, $camera, $option); 4 | } 5 | 6 | global proc sunflowIPRRender(int $width, int $height, int $doShadows, int $doGlowPass, string $camera, string $option){ 7 | print($width+"\n"); 8 | print($height+"\n"); 9 | print($doShadows+"\n"); 10 | print($doGlowPass+"\n"); 11 | print($camera+"\n"); 12 | print($option+"\n"); 13 | sunflowExportCmd($width, $height, $doShadows, $doGlowPass, $camera, $option); 14 | } 15 | -------------------------------------------------------------------------------- /src/org/sunflow/core/Options.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.util.FastHashMap; 5 | 6 | /** 7 | * This holds rendering objects as key, value pairs. 8 | */ 9 | public final class Options extends ParameterList implements RenderObject { 10 | public boolean update(ParameterList pl, SunflowAPI api) { 11 | // take all attributes, and update them into the current set 12 | for (FastHashMap.Entry e : pl.list) { 13 | list.put(e.getKey(), e.getValue()); 14 | e.getValue().check(); 15 | } 16 | return true; 17 | } 18 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/AccelerationStructure.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | public interface AccelerationStructure { 4 | /** 5 | * Construct an acceleration structure for the specified primitive list. 6 | * 7 | * @param primitives 8 | */ 9 | public void build(PrimitiveList primitives); 10 | 11 | /** 12 | * Intersect the specified ray with the geometry in local space. The ray 13 | * will be provided in local space. 14 | * 15 | * @param r ray in local space 16 | * @param istate state to store the intersection into 17 | */ 18 | public void intersect(Ray r, IntersectionState istate); 19 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/ui/SilentInterface.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system.ui; 2 | 3 | import org.sunflow.system.UserInterface; 4 | import org.sunflow.system.UI.Module; 5 | import org.sunflow.system.UI.PrintLevel; 6 | 7 | /** 8 | * Null implementation of a user interface. This is usefull to silence the 9 | * output. 10 | */ 11 | public class SilentInterface implements UserInterface { 12 | public void print(Module m, PrintLevel level, String s) { 13 | } 14 | 15 | public void taskStart(String s, int min, int max) { 16 | } 17 | 18 | public void taskUpdate(int current) { 19 | } 20 | 21 | public void taskStop() { 22 | } 23 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/SimpleShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class SimpleShader implements Shader { 10 | public boolean update(ParameterList pl, SunflowAPI api) { 11 | return true; 12 | } 13 | 14 | public Color getRadiance(ShadingState state) { 15 | return new Color(Math.abs(state.getRay().dot(state.getNormal()))); 16 | } 17 | 18 | public void scatterPhoton(ShadingState state, Color power) { 19 | } 20 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/AEsunflowSkyNodeTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEsunflowSkyNodeTemplate( string $nodeName ) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | editorTemplate -beginLayout "Sky Attributes" -collapse 0; 5 | editorTemplate -l "Turbidity" -addControl "Turbidity"; 6 | editorTemplate -endLayout; 7 | editorTemplate -beginLayout "UI Attributes" -collapse 0; 8 | editorTemplate -l "Size" -addControl "size"; 9 | editorTemplate -l "Quality" -addControl "resolution"; 10 | editorTemplate -l "Exposure" -addControl "Exposure"; 11 | editorTemplate -endLayout; 12 | editorTemplate -addExtraControls; 13 | editorTemplate -endScrollLayout; 14 | } 15 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowConstants.cpp: -------------------------------------------------------------------------------- 1 | #include "sunflowConstants.h" 2 | 3 | const char* FILTER_NAMES[] = { 4 | "box", 5 | "triangle", 6 | "catmull-rom", 7 | "mitchell", 8 | "lanczos", 9 | "blackman-harris", 10 | "sinc", 11 | "gaussian" 12 | }; 13 | 14 | const unsigned int NUM_FILTER_NAMES = sizeof(FILTER_NAMES) / sizeof(const char*); 15 | 16 | const char* BUCKET_ORDERS[] = { 17 | "hilbert", 18 | "spiral", 19 | "column", 20 | "row", 21 | "diagonal", 22 | "random" 23 | }; 24 | const unsigned int NUM_BUCKET_ORDERS = sizeof(BUCKET_ORDERS) / sizeof(const char*); 25 | 26 | const unsigned int DIR_LIGHT_RADIUS = 1000; 27 | -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/CatmullRomFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class CatmullRomFilter implements Filter { 6 | public float getSize() { 7 | return 4.0f; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return catrom1d(x) * catrom1d(y); 12 | } 13 | 14 | private float catrom1d(float x) { 15 | x = Math.abs(x); 16 | float x2 = x * x; 17 | float x3 = x * x2; 18 | if (x >= 2) 19 | return 0; 20 | if (x < 1) 21 | return 3 * x3 - 5 * x2 + 2; 22 | return -x3 + 5 * x2 - 8 * x + 4; 23 | } 24 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/ViewGlobalPhotonsShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class ViewGlobalPhotonsShader implements Shader { 10 | public boolean update(ParameterList pl, SunflowAPI api) { 11 | return true; 12 | } 13 | 14 | public Color getRadiance(ShadingState state) { 15 | state.faceforward(); 16 | return state.getGlobalRadiance(); 17 | } 18 | 19 | public void scatterPhoton(ShadingState state, Color power) { 20 | } 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/ConstantSpectralCurve.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | /** 4 | * Very simple class equivalent to a constant spectral curve. Note that this is 5 | * most likely physically impossible for amplitudes > 0, however this class can 6 | * be handy since in practice spectral curves end up being integrated against 7 | * the finite width color matching functions. 8 | */ 9 | public class ConstantSpectralCurve extends SpectralCurve { 10 | private final float amp; 11 | 12 | public ConstantSpectralCurve(float amp) { 13 | this.amp = amp; 14 | } 15 | 16 | @Override 17 | public float sample(float lambda) { 18 | return amp; 19 | } 20 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/UVShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class UVShader implements Shader { 10 | public boolean update(ParameterList pl, SunflowAPI api) { 11 | return true; 12 | } 13 | 14 | public Color getRadiance(ShadingState state) { 15 | if (state.getUV() == null) 16 | return Color.BLACK; 17 | return new Color(state.getUV().x, state.getUV().y, 0); 18 | } 19 | 20 | public void scatterPhoton(ShadingState state, Color power) { 21 | } 22 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/SceneParser.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.SunflowAPIInterface; 5 | 6 | /** 7 | * Simple interface to allow for scene creation from arbitrary file formats. 8 | */ 9 | public interface SceneParser { 10 | /** 11 | * Parse the specified file to create a scene description into the provided 12 | * {@link SunflowAPI} object. 13 | * 14 | * @param filename filename to parse 15 | * @param api scene to parse the file into 16 | * @return true upon sucess, or false if 17 | * errors have occured. 18 | */ 19 | public boolean parse(String filename, SunflowAPIInterface api); 20 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/ViewIrradianceShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class ViewIrradianceShader implements Shader { 10 | public boolean update(ParameterList pl, SunflowAPI api) { 11 | return true; 12 | } 13 | 14 | public Color getRadiance(ShadingState state) { 15 | state.faceforward(); 16 | return new Color().set(state.getIrradiance(Color.WHITE)).mul(1.0f / (float) Math.PI); 17 | } 18 | 19 | public void scatterPhoton(ShadingState state, Color power) { 20 | } 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/CubicBSpline.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class CubicBSpline implements Filter { 6 | public float get(float x, float y) { 7 | return B3(x) * B3(y); 8 | } 9 | 10 | public float getSize() { 11 | return 4; 12 | } 13 | 14 | private float B3(float t) { 15 | t = Math.abs(t); 16 | if (t <= 1) 17 | return b1(1 - t); 18 | return b0(2 - t); 19 | } 20 | 21 | private float b0(float t) { 22 | return t * t * t * (1.0f / 6); 23 | } 24 | 25 | private float b1(float t) { 26 | return (1.0f / 6) * (-3 * t * t * t + 3 * t * t + 3 * t + 1); 27 | } 28 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/updateSunflowGlobalsTab.mel: -------------------------------------------------------------------------------- 1 | //================================================================== 2 | // Sunflow Renderer Tab 3 | //================================================================== 4 | 5 | // Description: 6 | // Update procedure that is called whenever renderer is changed or 7 | // different layer is selected. 8 | // Must update any controls whose state may have changed 9 | // through another instance of the tab. 10 | // 11 | global proc updateSunflowGlobalsTab() 12 | { 13 | //updateSunflowSamplingFrameUI(); 14 | updateSunflowPhotonFrameUI(); 15 | updateSunflowGIFrameUI(); 16 | updateSunflowEnvironmentFrameUI(); 17 | updateSunflowOverrideFrameUI(); 18 | updateSunflowSystemFrameUI(); 19 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/LanczosFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class LanczosFilter implements Filter { 6 | public float getSize() { 7 | return 4.0f; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return sinc1d(x * 0.5f) * sinc1d(y * 0.5f); 12 | } 13 | 14 | private float sinc1d(float x) { 15 | x = Math.abs(x); 16 | if (x < 1e-5f) 17 | return 1; 18 | if (x > 1.0f) 19 | return 0; 20 | x *= Math.PI; 21 | float sinc = (float) Math.sin(3 * x) / (3 * x); 22 | float lanczos = (float) Math.sin(x) / x; 23 | return sinc * lanczos; 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapGA8.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapGA8 extends Bitmap { 7 | private int w, h; 8 | private byte[] data; 9 | 10 | @Override 11 | public int getWidth() { 12 | return w; 13 | } 14 | 15 | @Override 16 | public int getHeight() { 17 | return h; 18 | } 19 | 20 | @Override 21 | public Color readColor(int x, int y) { 22 | return new Color((data[2 * (x + y * w) + 0] & 0xFF) * INV255); 23 | } 24 | 25 | @Override 26 | public float readAlpha(int x, int y) { 27 | return (data[2 * (x + y * w) + 1] & 0xFF) * INV255; 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/GlobalPhotonMapInterface.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | import org.sunflow.math.Point3; 5 | import org.sunflow.math.Vector3; 6 | 7 | /** 8 | * Represents a global photon map. This is a structure which can return a rough 9 | * approximation of the diffuse radiance at a given surface point. 10 | */ 11 | public interface GlobalPhotonMapInterface extends PhotonStore { 12 | 13 | /** 14 | * Lookup the global diffuse radiance at the specified surface point. 15 | * 16 | * @param p surface position 17 | * @param n surface normal 18 | * @return an approximation of global diffuse radiance at this point 19 | */ 20 | public Color getRadiance(Point3 p, Vector3 n); 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/ConstantShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class ConstantShader implements Shader { 10 | private Color c; 11 | 12 | public ConstantShader() { 13 | c = Color.WHITE; 14 | } 15 | 16 | public boolean update(ParameterList pl, SunflowAPI api) { 17 | c = pl.getColor("color", c); 18 | return true; 19 | } 20 | 21 | public Color getRadiance(ShadingState state) { 22 | return c; 23 | } 24 | 25 | public void scatterPhoton(ShadingState state, Color power) { 26 | } 27 | } -------------------------------------------------------------------------------- /src/org/sunflow/math/Point2.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.math; 2 | 3 | public final class Point2 { 4 | public float x, y; 5 | 6 | public Point2() { 7 | } 8 | 9 | public Point2(float x, float y) { 10 | this.x = x; 11 | this.y = y; 12 | } 13 | 14 | public Point2(Point2 p) { 15 | x = p.x; 16 | y = p.y; 17 | } 18 | 19 | public final Point2 set(float x, float y) { 20 | this.x = x; 21 | this.y = y; 22 | return this; 23 | } 24 | 25 | public final Point2 set(Point2 p) { 26 | x = p.x; 27 | y = p.y; 28 | return this; 29 | } 30 | 31 | @Override 32 | public final String toString() { 33 | return String.format("(%.2f, %.2f)", x, y); 34 | } 35 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/IDShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | import org.sunflow.math.Vector3; 9 | 10 | public class IDShader implements Shader { 11 | public boolean update(ParameterList pl, SunflowAPI api) { 12 | return true; 13 | } 14 | 15 | public Color getRadiance(ShadingState state) { 16 | Vector3 n = state.getNormal(); 17 | float f = n == null ? 1.0f : Math.abs(state.getRay().dot(n)); 18 | return new Color(state.getInstance().hashCode()).mul(f); 19 | } 20 | 21 | public void scatterPhoton(ShadingState state, Color power) { 22 | } 23 | } -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | sunflow 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.ui.externaltools.ExternalToolBuilder 15 | full,incremental, 16 | 17 | 18 | LaunchConfigHandle 19 | <project>/.externalToolBuilders/Ant Build.launch 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/org/sunflow/core/BucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * Creates an array of coordinates that iterate over the tiled screen. Classes 5 | * which implement this interface are responsible for guarenteeing the entire 6 | * screen is tiled. No attempt is made to check for duplicates or incomplete 7 | * coverage. 8 | */ 9 | public interface BucketOrder { 10 | /** 11 | * Computes the order in which each coordinate on the screen should be 12 | * visited. 13 | * 14 | * @param nbw number of buckets in the X direction 15 | * @param nbh number of buckets in the Y direction 16 | * @return array of coordinates with interleaved X, Y of the positions of 17 | * buckets to be rendered. 18 | */ 19 | int[] getBucketSequence(int nbw, int nbh); 20 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/BlackmanHarrisFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class BlackmanHarrisFilter implements Filter { 6 | public float getSize() { 7 | return 4; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return bh1d(x * 0.5f) * bh1d(y * 0.5f); 12 | } 13 | 14 | private float bh1d(float x) { 15 | if (x < -1.0f || x > 1.0f) 16 | return 0.0f; 17 | x = (x + 1) * 0.5f; 18 | final double A0 = 0.35875; 19 | final double A1 = -0.48829; 20 | final double A2 = 0.14128; 21 | final double A3 = -0.01168; 22 | return (float) (A0 + A1 * Math.cos(2 * Math.PI * x) + A2 * Math.cos(4 * Math.PI * x) + A3 * Math.cos(6 * Math.PI * x)); 23 | } 24 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/camera/FisheyeLens.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.camera; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.CameraLens; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.Ray; 7 | 8 | public class FisheyeLens implements CameraLens { 9 | public boolean update(ParameterList pl, SunflowAPI api) { 10 | return true; 11 | } 12 | 13 | public Ray getRay(float x, float y, int imageWidth, int imageHeight, double lensX, double lensY, double time) { 14 | float cx = 2.0f * x / imageWidth - 1.0f; 15 | float cy = 2.0f * y / imageHeight - 1.0f; 16 | float r2 = cx * cx + cy * cy; 17 | if (r2 > 1) 18 | return null; // outside the fisheye 19 | return new Ray(0, 0, 0, cx, cy, (float) -Math.sqrt(1 - r2)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/accel/NullAccelerator.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.accel; 2 | 3 | import org.sunflow.core.AccelerationStructure; 4 | import org.sunflow.core.IntersectionState; 5 | import org.sunflow.core.PrimitiveList; 6 | import org.sunflow.core.Ray; 7 | 8 | public class NullAccelerator implements AccelerationStructure { 9 | private PrimitiveList primitives; 10 | private int n; 11 | 12 | public NullAccelerator() { 13 | primitives = null; 14 | n = 0; 15 | } 16 | 17 | public void build(PrimitiveList primitives) { 18 | this.primitives = primitives; 19 | n = primitives.getNumPrimitives(); 20 | } 21 | 22 | public void intersect(Ray r, IntersectionState state) { 23 | for (int i = 0; i < n; i++) 24 | primitives.intersectPrimitive(r, i, state); 25 | } 26 | } -------------------------------------------------------------------------------- /examples/shader_examples/AO002.sc: -------------------------------------------------------------------------------- 1 | shader { 2 | name "boxes" 3 | type amb-occ2 4 | bright 0 0 1 5 | dark 0 0.6 0 6 | samples 32 7 | dist 2 8 | } 9 | 10 | shader { 11 | name "ground" 12 | type amb-occ2 13 | bright 1 0 1 14 | dark 0 0 0 15 | samples 32 16 | dist 2 17 | } 18 | 19 | shader { 20 | name "left_sphere" 21 | type amb-occ2 22 | bright 0 1 1 23 | dark 0 0 0 24 | samples 32 25 | dist 2 26 | } 27 | 28 | shader { 29 | name "top_sphere" 30 | type amb-occ2 31 | bright 0 0 0 32 | dark 1 1 1 33 | samples 32 34 | dist 2 35 | } 36 | 37 | shader { 38 | name "right_sphere" 39 | type amb-occ2 40 | bright 0 0.3 0 41 | dark 0.4 0 0 42 | samples 32 43 | dist 2 44 | } 45 | 46 | include "include/example_scene.geo.sc" 47 | -------------------------------------------------------------------------------- /src/org/sunflow/core/filter/MitchellFilter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.filter; 2 | 3 | import org.sunflow.core.Filter; 4 | 5 | public class MitchellFilter implements Filter { 6 | public float getSize() { 7 | return 4.0f; 8 | } 9 | 10 | public float get(float x, float y) { 11 | return mitchell(x) * mitchell(y); 12 | } 13 | 14 | private float mitchell(float x) { 15 | final float B = 1 / 3.0f; 16 | final float C = 1 / 3.0f; 17 | final float SIXTH = 1 / 6.0f; 18 | x = Math.abs(x); 19 | float x2 = x * x; 20 | if (x > 1.0f) 21 | return ((-B - 6 * C) * x * x2 + (6 * B + 30 * C) * x2 + (-12 * B - 48 * C) * x + (8 * B + 24 * C)) * SIXTH; 22 | return ((12 - 9 * B - 6 * C) * x * x2 + (-18 + 12 * B + 6 * C) * x2 + (6 - 2 * B)) * SIXTH; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapG8.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapG8 extends Bitmap { 7 | private int w, h; 8 | private byte[] data; 9 | 10 | public BitmapG8(int w, int h, byte[] data) { 11 | this.w = w; 12 | this.h = h; 13 | this.data = data; 14 | } 15 | 16 | @Override 17 | public int getWidth() { 18 | return w; 19 | } 20 | 21 | @Override 22 | public int getHeight() { 23 | return h; 24 | } 25 | 26 | @Override 27 | public Color readColor(int x, int y) { 28 | return new Color((data[x + y * w] & 0xFF) * INV255); 29 | } 30 | 31 | @Override 32 | public float readAlpha(int x, int y) { 33 | return 1; 34 | } 35 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/camera/SphericalLens.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.camera; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.CameraLens; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.Ray; 7 | 8 | public class SphericalLens implements CameraLens { 9 | public boolean update(ParameterList pl, SunflowAPI api) { 10 | return true; 11 | } 12 | 13 | public Ray getRay(float x, float y, int imageWidth, int imageHeight, double lensX, double lensY, double time) { 14 | // Generate environment camera ray direction 15 | double theta = 2 * Math.PI * x / imageWidth + Math.PI / 2; 16 | double phi = Math.PI * (imageHeight - 1 - y) / imageHeight; 17 | return new Ray(0, 0, 0, (float) (Math.cos(theta) * Math.sin(phi)), (float) (Math.cos(phi)), (float) (Math.sin(theta) * Math.sin(phi))); 18 | } 19 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowCreateMenu.mel: -------------------------------------------------------------------------------- 1 | global proc sunflowCreateMenu(){ 2 | global string $gMainWindow; 3 | 4 | if ( `menu -exists sunflowMainWindowMenu` ) deleteUI sunflowMainWindowMenu; 5 | 6 | if ( $gMainWindow != "" ){ 7 | setParent $gMainWindow; 8 | menu -label "Sunflow" -tearOff true sunflowMainWindowMenu; 9 | menuItem -label "Helper" -subMenu true; 10 | menuItem -label "File Mesh" -command sunflowCreateFileMeshHelper; 11 | menuItem -label "Infinite Plane" -command sunflowCreateInfinitePlaneHelper; 12 | setParent -menu ..; 13 | } 14 | } 15 | 16 | global proc sunflowCreateFileMeshHelper(){ 17 | createNode sunflowHelperNode -n "sunflowFileMesh#"; 18 | } 19 | 20 | global proc sunflowCreateInfinitePlaneHelper(){ 21 | $infinitePlane = `createNode sunflowHelperNode -n "sunflowInfinitePlane#"`; 22 | setAttr ($infinitePlane+".type") 1; 23 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/Filter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * Represents a multi-pixel image filter kernel. 5 | */ 6 | public interface Filter { 7 | /** 8 | * Width in pixels of the filter extents. The filter will be applied to the 9 | * range of pixels within a box of +/- getSize() / 2 around 10 | * the center of the pixel. 11 | * 12 | * @return width in pixels 13 | */ 14 | public float getSize(); 15 | 16 | /** 17 | * Get value of the filter at offset (x, y). The filter should never be 18 | * called with values beyond its extents but should return 0 in those cases 19 | * anyway. 20 | * 21 | * @param x x offset in pixels 22 | * @param y y offset in pixels 23 | * @return value of the filter at the specified location 24 | */ 25 | public float get(float x, float y); 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/ImageSampler.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * This interface represents an image sampling algorithm capable of rendering 5 | * the entire image. Implementations are responsible for anti-aliasing and 6 | * filtering. 7 | */ 8 | public interface ImageSampler { 9 | /** 10 | * Prepare the sampler for rendering an image of w x h pixels 11 | * 12 | * @param w width of the image 13 | * @param h height of the image 14 | */ 15 | public boolean prepare(Options options, Scene scene, int w, int h); 16 | 17 | /** 18 | * Render the image to the specified display. The sampler can assume the 19 | * display has been opened and that it will be closed after the method 20 | * returns. 21 | * 22 | * @param display Display driver to send image data to 23 | */ 24 | public void render(Display display); 25 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/NormalShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | import org.sunflow.math.Vector3; 9 | 10 | public class NormalShader implements Shader { 11 | public boolean update(ParameterList pl, SunflowAPI api) { 12 | return true; 13 | } 14 | 15 | public Color getRadiance(ShadingState state) { 16 | Vector3 n = state.getNormal(); 17 | if (n == null) 18 | return Color.BLACK; 19 | float r = (n.x + 1) * 0.5f; 20 | float g = (n.y + 1) * 0.5f; 21 | float b = (n.z + 1) * 0.5f; 22 | return new Color(r, g, b); 23 | } 24 | 25 | public void scatterPhoton(ShadingState state, Color power) { 26 | } 27 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/DiagonalBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class DiagonalBucketOrder implements BucketOrder { 6 | public int[] getBucketSequence(int nbw, int nbh) { 7 | int[] coords = new int[2 * nbw * nbh]; 8 | int x = 0, y = 0, nx = 1, ny = 0; 9 | for (int i = 0; i < nbw * nbh; i++) { 10 | coords[2 * i + 0] = x; 11 | coords[2 * i + 1] = y; 12 | do { 13 | if (y == ny) { 14 | y = 0; 15 | x = nx; 16 | ny++; 17 | nx++; 18 | } else { 19 | x--; 20 | y++; 21 | } 22 | } while ((y >= nbh || x >= nbw) && i != (nbw * nbh - 1)); 23 | } 24 | return coords; 25 | } 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/InvertedBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class InvertedBucketOrder implements BucketOrder { 6 | private BucketOrder order; 7 | 8 | public InvertedBucketOrder(BucketOrder order) { 9 | this.order = order; 10 | } 11 | 12 | public int[] getBucketSequence(int nbw, int nbh) { 13 | int[] coords = order.getBucketSequence(nbw, nbh); 14 | for (int i = 0; i < coords.length / 2; i += 2) { 15 | int src = i; 16 | int dst = coords.length - 2 - i; 17 | int tmp = coords[src + 0]; 18 | coords[src + 0] = coords[dst + 0]; 19 | coords[dst + 0] = tmp; 20 | tmp = coords[src + 1]; 21 | coords[src + 1] = coords[dst + 1]; 22 | coords[dst + 1] = tmp; 23 | } 24 | return coords; 25 | } 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/FileUtils.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | import java.io.File; 4 | import java.util.Locale; 5 | 6 | public final class FileUtils { 7 | /** 8 | * Extract the file extension from the specified filename. 9 | * 10 | * @param filename filename to get the extension of 11 | * @return a string representing the file extension, or null 12 | * if the filename doesn't have any extension, or is not a file 13 | */ 14 | public static final String getExtension(String filename) { 15 | if (filename == null) 16 | return null; 17 | File f = new File(filename); 18 | if (f.isDirectory()) 19 | return null; 20 | String name = new File(filename).getName(); 21 | int idx = name.lastIndexOf('.'); 22 | return idx == -1 ? null : name.substring(idx + 1).toLowerCase(Locale.ENGLISH); 23 | } 24 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/PrimIDShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | import org.sunflow.math.Vector3; 9 | 10 | public class PrimIDShader implements Shader { 11 | private static final Color[] BORDERS = { Color.RED, Color.GREEN, 12 | Color.BLUE, Color.YELLOW, Color.CYAN, Color.MAGENTA }; 13 | 14 | public boolean update(ParameterList pl, SunflowAPI api) { 15 | return true; 16 | } 17 | 18 | public Color getRadiance(ShadingState state) { 19 | Vector3 n = state.getNormal(); 20 | float f = n == null ? 1.0f : Math.abs(state.getRay().dot(n)); 21 | return BORDERS[state.getPrimitiveID() % BORDERS.length].copy().mul(f); 22 | } 23 | 24 | public void scatterPhoton(ShadingState state, Color power) { 25 | } 26 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/ViewCausticsShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.LightSample; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.Shader; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | 10 | public class ViewCausticsShader implements Shader { 11 | public boolean update(ParameterList pl, SunflowAPI api) { 12 | return true; 13 | } 14 | 15 | public Color getRadiance(ShadingState state) { 16 | state.faceforward(); 17 | state.initCausticSamples(); 18 | // integrate a diffuse function 19 | Color lr = Color.black(); 20 | for (LightSample sample : state) 21 | lr.madd(sample.dot(state.getNormal()), sample.getDiffuseRadiance()); 22 | return lr.mul(1.0f / (float) Math.PI); 23 | 24 | } 25 | 26 | public void scatterPhoton(ShadingState state, Color power) { 27 | } 28 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapXYZ.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | import org.sunflow.image.XYZColor; 6 | 7 | public class BitmapXYZ extends Bitmap { 8 | private int w, h; 9 | private float[] data; 10 | 11 | public BitmapXYZ(int w, int h, float[] data) { 12 | this.w = w; 13 | this.h = h; 14 | this.data = data; 15 | } 16 | 17 | @Override 18 | public int getWidth() { 19 | return w; 20 | } 21 | 22 | @Override 23 | public int getHeight() { 24 | return h; 25 | } 26 | 27 | @Override 28 | public Color readColor(int x, int y) { 29 | int index = 3 * (x + y * w); 30 | return Color.NATIVE_SPACE.convertXYZtoRGB(new XYZColor(data[index], data[index + 1], data[index + 2])).mul(0.1f); 31 | } 32 | 33 | @Override 34 | public float readAlpha(int x, int y) { 35 | return 1; 36 | } 37 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/TexturedPhongShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.ShadingState; 6 | import org.sunflow.core.Texture; 7 | import org.sunflow.core.TextureCache; 8 | import org.sunflow.image.Color; 9 | 10 | public class TexturedPhongShader extends PhongShader { 11 | private Texture tex; 12 | 13 | public TexturedPhongShader() { 14 | tex = null; 15 | } 16 | 17 | @Override 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); 22 | return tex != null && super.update(pl, api); 23 | } 24 | 25 | @Override 26 | public Color getDiffuse(ShadingState state) { 27 | return tex.getPixel(state.getUV().x, state.getUV().y); 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/TexturedDiffuseShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.ShadingState; 6 | import org.sunflow.core.Texture; 7 | import org.sunflow.core.TextureCache; 8 | import org.sunflow.image.Color; 9 | 10 | public class TexturedDiffuseShader extends DiffuseShader { 11 | private Texture tex; 12 | 13 | public TexturedDiffuseShader() { 14 | tex = null; 15 | } 16 | 17 | @Override 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); 22 | return tex != null && super.update(pl, api); 23 | } 24 | 25 | @Override 26 | public Color getDiffuse(ShadingState state) { 27 | return tex.getPixel(state.getUV().x, state.getUV().y); 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/TexturedWardShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.ShadingState; 6 | import org.sunflow.core.Texture; 7 | import org.sunflow.core.TextureCache; 8 | import org.sunflow.image.Color; 9 | 10 | public class TexturedWardShader extends AnisotropicWardShader { 11 | private Texture tex; 12 | 13 | public TexturedWardShader() { 14 | tex = null; 15 | } 16 | 17 | @Override 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); 22 | return tex != null && super.update(pl, api); 23 | } 24 | 25 | @Override 26 | public Color getDiffuse(ShadingState state) { 27 | return tex.getPixel(state.getUV().x, state.getUV().y); 28 | } 29 | } -------------------------------------------------------------------------------- /examples/shader_examples/Mirror001.sc: -------------------------------------------------------------------------------- 1 | shader { 2 | name "ground" 3 | type diffuse 4 | diff 0.800000011921 0.800000011921 0.800000011921 5 | } 6 | 7 | shader { 8 | name "shader01" 9 | type mirror 10 | refl 0.1 0.1 0.1 11 | } 12 | 13 | shader { 14 | name "shader02" 15 | type mirror 16 | refl 0.5 0.5 0.5 17 | } 18 | shader { 19 | name "shader03" 20 | type mirror 21 | refl 1 1 1 22 | } 23 | 24 | shader { 25 | name "shader04" 26 | type mirror 27 | refl 0 0 0.1 28 | } 29 | 30 | shader { 31 | name "shader05" 32 | type mirror 33 | refl 0 0 0.5 34 | } 35 | 36 | shader { 37 | name "shader06" 38 | type mirror 39 | refl 0 0 1 40 | } 41 | 42 | shader { 43 | name "shader07" 44 | type mirror 45 | refl 0.1 0.0 0.0 46 | } 47 | 48 | shader { 49 | name "shader08" 50 | type mirror 51 | refl 0.5 0 0 52 | } 53 | 54 | shader { 55 | name "shader09" 56 | type mirror 57 | refl 1 0 0 58 | } 59 | 60 | include "include/example_array.geo.sc" 61 | -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/TexturedShinyDiffuseShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.ShadingState; 6 | import org.sunflow.core.Texture; 7 | import org.sunflow.core.TextureCache; 8 | import org.sunflow.image.Color; 9 | 10 | public class TexturedShinyDiffuseShader extends ShinyDiffuseShader { 11 | private Texture tex; 12 | 13 | public TexturedShinyDiffuseShader() { 14 | tex = null; 15 | } 16 | 17 | @Override 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); 22 | return tex != null && super.update(pl, api); 23 | } 24 | 25 | @Override 26 | public Color getDiffuse(ShadingState state) { 27 | return tex.getPixel(state.getUV().x, state.getUV().y); 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/BucketOrderFactory.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.PluginRegistry; 4 | import org.sunflow.core.BucketOrder; 5 | import org.sunflow.system.UI; 6 | import org.sunflow.system.UI.Module; 7 | 8 | public class BucketOrderFactory { 9 | public static BucketOrder create(String order) { 10 | boolean flip = false; 11 | if (order.startsWith("inverse") || order.startsWith("invert") || order.startsWith("reverse")) { 12 | String[] tokens = order.split("\\s+"); 13 | if (tokens.length == 2) { 14 | order = tokens[1]; 15 | flip = true; 16 | } 17 | } 18 | BucketOrder o = PluginRegistry.bucketOrderPlugins.createObject(order); 19 | if (o == null) { 20 | UI.printWarning(Module.BCKT, "Unrecognized bucket ordering: \"%s\" - using hilbert", order); 21 | return create("hilbert"); 22 | } 23 | return flip ? new InvertedBucketOrder(o) : o; 24 | } 25 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowBucketToRenderView.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUNFLOW_BUCKET_TO_RENDER_VIEW_H_ 2 | #define _SUNFLOW_BUCKET_TO_RENDER_VIEW_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef long sint32 ; 9 | typedef unsigned long uint32 ; 10 | typedef short int sint16 ; 11 | typedef unsigned short int uint16 ; 12 | typedef signed char sint8; 13 | typedef unsigned char uint8 ; 14 | 15 | union data32 { 16 | sint32 sbits32; 17 | uint32 ubits32; 18 | sint16 sbits16[2]; 19 | uint16 ubits16[2]; 20 | sint8 sbits8[4]; 21 | uint8 ubits8[4]; 22 | float f; 23 | // TODO: half h[2]; 24 | }; 25 | 26 | struct DisplayPacket { 27 | data32 type; 28 | data32 data[4]; 29 | }; 30 | 31 | class bucketToRenderView 32 | { 33 | public: 34 | DisplayPacket getPacket(); 35 | void checkStream(); 36 | 37 | FILE* renderPipe; 38 | 39 | int renderWidth; 40 | int renderHeight; 41 | MDagPath renderCamera; 42 | }; 43 | 44 | #endif /* _SUNFLOW_BUCKET_TO_RENDER_VIEW_H_ */ 45 | -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.ShadingState; 6 | import org.sunflow.core.Texture; 7 | import org.sunflow.core.TextureCache; 8 | import org.sunflow.image.Color; 9 | 10 | public class TexturedAmbientOcclusionShader extends AmbientOcclusionShader { 11 | private Texture tex; 12 | 13 | public TexturedAmbientOcclusionShader() { 14 | tex = null; 15 | } 16 | 17 | @Override 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | tex = TextureCache.getTexture(api.resolveTextureFilename(filename), false); 22 | return tex != null && super.update(pl, api); 23 | } 24 | 25 | @Override 26 | public Color getBrightColor(ShadingState state) { 27 | return tex.getPixel(state.getUV().x, state.getUV().y); 28 | } 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapRGB8.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapRGB8 extends Bitmap { 7 | private int w, h; 8 | private byte[] data; 9 | 10 | public BitmapRGB8(int w, int h, byte[] data) { 11 | this.w = w; 12 | this.h = h; 13 | this.data = data; 14 | } 15 | 16 | @Override 17 | public int getWidth() { 18 | return w; 19 | } 20 | 21 | @Override 22 | public int getHeight() { 23 | return h; 24 | } 25 | 26 | @Override 27 | public Color readColor(int x, int y) { 28 | int index = 3 * (x + y * w); 29 | float r = (data[index + 0] & 0xFF) * INV255; 30 | float g = (data[index + 1] & 0xFF) * INV255; 31 | float b = (data[index + 2] & 0xFF) * INV255; 32 | return new Color(r, g, b); 33 | } 34 | 35 | @Override 36 | public float readAlpha(int x, int y) { 37 | return 1; 38 | } 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2007 Christopher Kulla 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /src/org/sunflow/core/RenderObject.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.SunflowAPI; 4 | 5 | /** 6 | * This is the base interface for all public rendering object interfaces. It 7 | * handles incremental updates via {@link ParameterList} objects. 8 | */ 9 | public interface RenderObject { 10 | /** 11 | * Update this object given a list of parameters. This method is guarenteed 12 | * to be called at least once on every object, but it should correctly 13 | * handle empty parameter lists. This means that the object should be in a 14 | * valid state from the time it is constructed. This method should also 15 | * return true or false depending on whether the update was succesfull or 16 | * not. 17 | * 18 | * @param pl list of parameters to read from 19 | * @param api reference to the current scene 20 | * @return true if the update is succesfull, 21 | * false otherwise 22 | */ 23 | public boolean update(ParameterList pl, SunflowAPI api); 24 | } -------------------------------------------------------------------------------- /examples/shader_examples/Phong003.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "ground" 9 | type phong 10 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 11 | spec { "sRGB nonlinear" 0.9 0.9 0.9 } 20 12 | samples 4 13 | } 14 | 15 | shader { 16 | name "boxes" 17 | type phong 18 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 19 | spec { "sRGB nonlinear" 0.9 0.9 0.9 } 20 20 | samples 4 21 | } 22 | 23 | shader { 24 | name "top_sphere" 25 | type phong 26 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 27 | spec { "sRGB nonlinear" 0.9 0.9 0.9 } 20 28 | samples 4 29 | } 30 | 31 | shader { 32 | name "left_sphere" 33 | type phong 34 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 35 | spec { "sRGB nonlinear" 0.9 0.9 0.9 } 20 36 | samples 4 37 | } 38 | 39 | shader { 40 | name "right_sphere" 41 | type phong 42 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 43 | spec { "sRGB nonlinear" 0.9 0.9 0.9 } 20 44 | samples 4 45 | } 46 | 47 | include "include/example_scene.geo.sc" 48 | -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapRGBA8.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapRGBA8 extends Bitmap { 7 | private int w, h; 8 | private byte[] data; 9 | 10 | public BitmapRGBA8(int w, int h, byte[] data) { 11 | this.w = w; 12 | this.h = h; 13 | this.data = data; 14 | } 15 | 16 | @Override 17 | public int getWidth() { 18 | return w; 19 | } 20 | 21 | @Override 22 | public int getHeight() { 23 | return h; 24 | } 25 | 26 | @Override 27 | public Color readColor(int x, int y) { 28 | int index = 4 * (x + y * w); 29 | float r = (data[index + 0] & 0xFF) * INV255; 30 | float g = (data[index + 1] & 0xFF) * INV255; 31 | float b = (data[index + 2] & 0xFF) * INV255; 32 | return new Color(r, g, b); 33 | } 34 | 35 | @Override 36 | public float readAlpha(int x, int y) { 37 | return (data[4 * (x + y * w) + 3] & 0xFF) * INV255; 38 | } 39 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/XYZColor.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | public final class XYZColor { 4 | private float X, Y, Z; 5 | 6 | public XYZColor() { 7 | } 8 | 9 | public XYZColor(float X, float Y, float Z) { 10 | this.X = X; 11 | this.Y = Y; 12 | this.Z = Z; 13 | } 14 | 15 | public final float getX() { 16 | return X; 17 | } 18 | 19 | public final float getY() { 20 | return Y; 21 | } 22 | 23 | public final float getZ() { 24 | return Z; 25 | } 26 | 27 | public final XYZColor mul(float s) { 28 | X *= s; 29 | Y *= s; 30 | Z *= s; 31 | return this; 32 | } 33 | 34 | public final void normalize() { 35 | float XYZ = X + Y + Z; 36 | if (XYZ < 1e-6f) 37 | return; 38 | float s = 1 / XYZ; 39 | X *= s; 40 | Y *= s; 41 | Z *= s; 42 | } 43 | 44 | @Override 45 | public final String toString() { 46 | return String.format("(%.3f, %.3f, %.3f)", X, Y, Z); 47 | } 48 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/RegularSpectralCurve.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | public class RegularSpectralCurve extends SpectralCurve { 4 | private final float[] spectrum; 5 | private final float lambdaMin, lambdaMax; 6 | private final float delta, invDelta; 7 | 8 | public RegularSpectralCurve(float[] spectrum, float lambdaMin, float lambdaMax) { 9 | this.lambdaMin = lambdaMin; 10 | this.lambdaMax = lambdaMax; 11 | this.spectrum = spectrum; 12 | delta = (lambdaMax - lambdaMin) / (spectrum.length - 1); 13 | invDelta = 1 / delta; 14 | } 15 | 16 | @Override 17 | public float sample(float lambda) { 18 | // reject wavelengths outside the valid range 19 | if (lambda < lambdaMin || lambda > lambdaMax) 20 | return 0; 21 | // interpolate the two closest samples linearly 22 | float x = (lambda - lambdaMin) * invDelta; 23 | int b0 = (int) x; 24 | int b1 = Math.min(b0 + 1, spectrum.length - 1); 25 | float dx = x - b0; 26 | return (1 - dx) * spectrum[b0] + dx * spectrum[b1]; 27 | } 28 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/Shader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | 5 | /** 6 | * A shader represents a particular light-surface interaction. 7 | */ 8 | public interface Shader extends RenderObject { 9 | /** 10 | * Gets the radiance for a specified rendering state. When this method is 11 | * called, you can assume that a hit has been registered in the state and 12 | * that the hit surface information has been computed. 13 | * 14 | * @param state current render state 15 | * @return color emitted or reflected by the shader 16 | */ 17 | public Color getRadiance(ShadingState state); 18 | 19 | /** 20 | * Scatter a photon with the specied power. Incoming photon direction is 21 | * specified by the ray attached to the current render state. This method 22 | * can safely do nothing if photon scattering is not supported or relevant 23 | * for the shader type. 24 | * 25 | * @param state current state 26 | * @param power power of the incoming photon. 27 | */ 28 | public void scatterPhoton(ShadingState state, Color power); 29 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/modifiers/NormalMapModifier.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.modifiers; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.Modifier; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.core.Texture; 8 | import org.sunflow.core.TextureCache; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | 11 | public class NormalMapModifier implements Modifier { 12 | private Texture normalMap; 13 | 14 | public NormalMapModifier() { 15 | normalMap = null; 16 | } 17 | 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | String filename = pl.getString("texture", null); 20 | if (filename != null) 21 | normalMap = TextureCache.getTexture(api.resolveTextureFilename(filename), true); 22 | return normalMap != null; 23 | } 24 | 25 | public void modify(ShadingState state) { 26 | // apply normal map 27 | state.getNormal().set(normalMap.getNormal(state.getUV().x, state.getUV().y, state.getBasis())); 28 | state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal())); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/shader_examples/Glass001.sc: -------------------------------------------------------------------------------- 1 | % photons { 2 | % caustics 10000000 kd 64 0.5 3 | % } 4 | 5 | shader { 6 | name "ground" 7 | type diffuse 8 | diff 0.800000011921 0.800000011921 0.800000011921 9 | } 10 | 11 | shader { 12 | name "shader01" 13 | type glass 14 | eta 1.1 15 | color 1 1 1 16 | } 17 | 18 | shader { 19 | name "shader02" 20 | type glass 21 | eta 1.3 22 | color 1 1 1 23 | } 24 | 25 | shader { 26 | name "shader03" 27 | type glass 28 | eta 1.5 29 | color 1 1 1 30 | } 31 | 32 | shader { 33 | name "shader04" 34 | type glass 35 | eta 1.7 36 | color 1 1 1 37 | } 38 | 39 | shader { 40 | name "shader05" 41 | type glass 42 | eta 1.9 43 | color 1 1 1 44 | } 45 | 46 | shader { 47 | name "shader06" 48 | type glass 49 | eta 2.1 50 | color 1 1 1 51 | } 52 | 53 | shader { 54 | name "shader07" 55 | type glass 56 | eta 2.3 57 | color 1 1 1 58 | } 59 | 60 | shader { 61 | name "shader08" 62 | type glass 63 | eta 2.5 64 | color 1 1 1 65 | } 66 | 67 | shader { 68 | name "shader09" 69 | type glass 70 | eta 2.7 71 | color 1 1 1 72 | } 73 | 74 | include "include/example_array.geo.sc" 75 | -------------------------------------------------------------------------------- /examples/shader_examples/Phong004.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "boxes" 9 | type phong 10 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 11 | spec { "sRGB nonlinear" 0.90000000298 0.90000000298 0.90000000298 } 20 12 | samples 4 13 | } 14 | 15 | shader { 16 | name "ground" 17 | type phong 18 | diff { "sRGB nonlinear" 0.3 0.3 0.3 } 19 | spec { "sRGB nonlinear" 1.90000000298 1.90000000298 1.90000000298 } 20 20 | samples 8 21 | } 22 | 23 | shader { 24 | name "left_sphere" 25 | type phong 26 | diff { "sRGB nonlinear" 0.9 0.4 0.0 } 27 | spec { "sRGB nonlinear" 1.00000000298 0.50000000298 0.00000000298 } 20 28 | samples 4 29 | } 30 | 31 | shader { 32 | name "top_sphere" 33 | type phong 34 | diff { "sRGB nonlinear" 0.7 0.1 0.0 } 35 | spec { "sRGB nonlinear" 0.90000000298 0.30000000298 0.00000000298 } 3 36 | samples 4 37 | } 38 | 39 | shader { 40 | name "right_sphere" 41 | type phong 42 | diff { "sRGB nonlinear" 0.0 0.7 1.0 } 43 | spec { "sRGB nonlinear" 0.30000000298 0.40000000298 0.50000000298 } 130 44 | samples 4 45 | } 46 | 47 | include "include/example_scene.geo.sc" 48 | -------------------------------------------------------------------------------- /examples/sphereflake.sc: -------------------------------------------------------------------------------- 1 | image { 2 | resolution 1920 1080 3 | aa 2 2 4 | samples 4 5 | filter gaussian 6 | } 7 | 8 | trace-depths { 9 | diff 1 10 | refl 1 11 | refr 0 12 | } 13 | 14 | 15 | % |persp|perspShape 16 | camera { 17 | type thinlens 18 | eye -5 0 -0.9 19 | target 0 0 0.2 20 | up 0 0 1 21 | fov 60 22 | aspect 1.77777777777 23 | fdist 5 24 | lensr 0.01 25 | } 26 | 27 | gi { type path samples 16 } 28 | 29 | shader { 30 | name simple1 31 | type diffuse 32 | diff 0.5 0.5 0.5 33 | } 34 | 35 | shader { 36 | name glass 37 | type glass 38 | eta 1.333 39 | color 0.8 0.8 0.8 40 | absorbtion.distance 15 41 | absorbtion.color { "sRGB nonlinear" 0.2 0.7 0.2 } 42 | } 43 | 44 | light { 45 | type sunsky 46 | up 0 0 1 47 | east 0 1 0 48 | sundir -1 1 0.2 49 | turbidity 2 50 | samples 32 51 | } 52 | 53 | shader { 54 | name metal 55 | type phong 56 | diff 0.1 0.1 0.1 57 | spec 0.2 0.2 0.2 30 58 | samples 4 59 | } 60 | 61 | 62 | object { 63 | shader metal 64 | type sphereflake 65 | name left 66 | level 7 67 | } 68 | 69 | object { 70 | shader simple1 71 | type plane 72 | p 0 0 -1 73 | n 0 0 1 74 | } 75 | 76 | shader { name ao type amb-occ diff 1 1 1 } 77 | % override ao true 78 | -------------------------------------------------------------------------------- /src/org/sunflow/core/AccelerationStructureFactory.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.PluginRegistry; 4 | import org.sunflow.system.UI; 5 | import org.sunflow.system.UI.Module; 6 | 7 | class AccelerationStructureFactory { 8 | static final AccelerationStructure create(String name, int n, boolean primitives) { 9 | if (name == null || name.equals("auto")) { 10 | if (primitives) { 11 | if (n > 20000000) 12 | name = "uniformgrid"; 13 | else if (n > 2000000) 14 | name = "bih"; 15 | else if (n > 2) 16 | name = "kdtree"; 17 | else 18 | name = "null"; 19 | } else { 20 | if (n > 2) 21 | name = "bih"; 22 | else 23 | name = "null"; 24 | } 25 | } 26 | AccelerationStructure accel = PluginRegistry.accelPlugins.createObject(name); 27 | if (accel == null) { 28 | UI.printWarning(Module.ACCEL, "Unrecognized intersection accelerator \"%s\" - using auto", name); 29 | return create(null, n, primitives); 30 | } 31 | return accel; 32 | } 33 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/modifiers/BumpMappingModifier.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.modifiers; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.Modifier; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.core.Texture; 8 | import org.sunflow.core.TextureCache; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | 11 | public class BumpMappingModifier implements Modifier { 12 | private Texture bumpTexture; 13 | private float scale; 14 | 15 | public BumpMappingModifier() { 16 | bumpTexture = null; 17 | scale = 1; 18 | } 19 | 20 | public boolean update(ParameterList pl, SunflowAPI api) { 21 | String filename = pl.getString("texture", null); 22 | if (filename != null) 23 | bumpTexture = TextureCache.getTexture(api.resolveTextureFilename(filename), true); 24 | scale = pl.getFloat("scale", scale); 25 | return bumpTexture != null; 26 | } 27 | 28 | public void modify(ShadingState state) { 29 | // apply bump 30 | state.getNormal().set(bumpTexture.getBump(state.getUV().x, state.getUV().y, state.getBasis(), scale)); 31 | state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal())); 32 | } 33 | } -------------------------------------------------------------------------------- /examples/julia.sc: -------------------------------------------------------------------------------- 1 | image { 2 | resolution 512 512 3 | aa 0 2 4 | filter gaussian 5 | } 6 | 7 | trace-depths { 8 | diff 1 9 | refl 0 10 | refr 0 11 | } 12 | 13 | 14 | % |persp|perspShape 15 | camera { 16 | type pinhole 17 | eye -5 0 0 18 | target 0 0 0 19 | up 0 1 0 20 | fov 58 21 | aspect 1 22 | } 23 | 24 | % background { color { "sRGB nonlinear" 0.5 0.5 0.5 } } 25 | 26 | gi { type path samples 16 } 27 | 28 | shader { 29 | name simple1 30 | type diffuse 31 | diff { "sRGB nonlinear" 0.5 0.5 0.5 } 32 | } 33 | 34 | light { 35 | type spherical 36 | color { "sRGB nonlinear" 1 1 .6 } 37 | radiance 60 38 | center -5 7 5 39 | radius 2 40 | samples 8 41 | } 42 | 43 | light { 44 | type spherical 45 | color { "sRGB nonlinear" .6 .6 1 } 46 | radiance 20 47 | center -15 -17 -15 48 | radius 5 49 | samples 8 50 | } 51 | 52 | object { 53 | shader simple1 54 | transform { scaleu 2 rotatey 45 rotatex -55 } 55 | type julia 56 | name left 57 | 58 | ## q is the main julia set parameter - it defines its shape 59 | % q -0.2 0.4 -0.4 -0.4 60 | % q -1 0.4 0 0 61 | % q -0.08 0.0 -0.83 -0.025 62 | q -0.125 -0.256 0.847 0.0895 63 | 64 | ## iterations/epsilon affect the speed and accuracy of the calculation 65 | ## comment these lines out for very high quality defaults 66 | iterations 8 67 | epsilon 0.001 68 | } 69 | -------------------------------------------------------------------------------- /src/org/sunflow/system/ui/ConsoleInterface.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system.ui; 2 | 3 | import org.sunflow.system.UI; 4 | import org.sunflow.system.UserInterface; 5 | import org.sunflow.system.UI.Module; 6 | import org.sunflow.system.UI.PrintLevel; 7 | 8 | /** 9 | * Basic console implementation of a user interface. 10 | */ 11 | public class ConsoleInterface implements UserInterface { 12 | private int min; 13 | private int max; 14 | private float invP; 15 | private String task; 16 | private int lastP; 17 | 18 | public ConsoleInterface() { 19 | } 20 | 21 | public void print(Module m, PrintLevel level, String s) { 22 | System.err.println(UI.formatOutput(m, level, s)); 23 | } 24 | 25 | public void taskStart(String s, int min, int max) { 26 | task = s; 27 | this.min = min; 28 | this.max = max; 29 | lastP = -1; 30 | invP = 100.0f / (max - min); 31 | } 32 | 33 | public void taskUpdate(int current) { 34 | int p = (min == max) ? 0 : (int) ((current - min) * invP); 35 | if (p != lastP) 36 | System.err.print(task + " [" + (lastP = p) + "%]\r"); 37 | } 38 | 39 | public void taskStop() { 40 | System.err.print(" \r"); 41 | } 42 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/writers/PNGBitmapWriter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.writers; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | import org.sunflow.image.BitmapWriter; 10 | import org.sunflow.image.Color; 11 | 12 | public class PNGBitmapWriter implements BitmapWriter { 13 | private String filename; 14 | private BufferedImage image; 15 | 16 | public void configure(String option, String value) { 17 | } 18 | 19 | public void openFile(String filename) throws IOException { 20 | this.filename = filename; 21 | } 22 | 23 | public void writeHeader(int width, int height, int tileSize) throws IOException, UnsupportedOperationException { 24 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 25 | } 26 | 27 | public void writeTile(int x, int y, int w, int h, Color[] color, float[] alpha) throws IOException { 28 | for (int j = 0, index = 0; j < h; j++) 29 | for (int i = 0; i < w; i++, index++) 30 | image.setRGB(x + i, y + j, color[index].copy().mul(1.0f / alpha[index]).toNonLinear().toRGBA(alpha[index])); 31 | } 32 | 33 | public void closeFile() throws IOException { 34 | ImageIO.write(image, "png", new File(filename)); 35 | } 36 | } -------------------------------------------------------------------------------- /examples/shader_examples/Shiny001.sc: -------------------------------------------------------------------------------- 1 | shader { 2 | name "ground" 3 | type diffuse 4 | diff 0.800000011921 0.800000011921 0.800000011921 5 | } 6 | 7 | shader { 8 | name "shader01" 9 | type shiny 10 | diff { "sRGB nonlinear" 1 .33 .33 } 11 | refl .1 12 | } 13 | 14 | shader { 15 | name "shader02" 16 | type shiny 17 | diff { "sRGB nonlinear" 1 .33 .33 } 18 | refl .2 19 | } 20 | 21 | shader { 22 | name "shader03" 23 | type shiny 24 | diff { "sRGB nonlinear" 1 .33 .33 } 25 | refl .5 26 | } 27 | 28 | shader { 29 | name "shader04" 30 | type shiny 31 | diff { "sRGB nonlinear" 1 .33 .33 } 32 | refl .7 33 | } 34 | 35 | shader { 36 | name "shader05" 37 | type shiny 38 | diff { "sRGB nonlinear" 1 .33 .33 } 39 | refl .9 40 | } 41 | 42 | shader { 43 | name "shader06" 44 | type shiny 45 | diff { "sRGB nonlinear" 1 .33 .33 } 46 | refl 1.1 47 | } 48 | 49 | shader { 50 | name "shader07" 51 | type shiny 52 | diff { "sRGB nonlinear" 1 .33 .33 } 53 | refl 1.3 54 | } 55 | 56 | shader { 57 | name "shader08" 58 | type shiny 59 | diff { "sRGB nonlinear" 1 .33 .33 } 60 | refl 1.5 61 | } 62 | 63 | shader { 64 | name "shader09" 65 | type shiny 66 | diff { "sRGB nonlinear" 1 .33 .33 } 67 | refl 1.6 68 | } 69 | 70 | include "include/example_array.geo.sc" 71 | -------------------------------------------------------------------------------- /src/org/sunflow/core/primitive/Background.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.primitive; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.IntersectionState; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.PrimitiveList; 7 | import org.sunflow.core.Ray; 8 | import org.sunflow.core.ShadingState; 9 | import org.sunflow.math.BoundingBox; 10 | import org.sunflow.math.Matrix4; 11 | 12 | public class Background implements PrimitiveList { 13 | public Background() { 14 | } 15 | 16 | public boolean update(ParameterList pl, SunflowAPI api) { 17 | return true; 18 | } 19 | 20 | public void prepareShadingState(ShadingState state) { 21 | if (state.getDepth() == 0) 22 | state.setShader(state.getInstance().getShader(0)); 23 | } 24 | 25 | public int getNumPrimitives() { 26 | return 1; 27 | } 28 | 29 | public float getPrimitiveBound(int primID, int i) { 30 | return 0; 31 | } 32 | 33 | public BoundingBox getWorldBounds(Matrix4 o2w) { 34 | return null; 35 | } 36 | 37 | public void intersectPrimitive(Ray r, int primID, IntersectionState state) { 38 | if (r.getMax() == Float.POSITIVE_INFINITY) 39 | state.setIntersection(0); 40 | } 41 | 42 | public PrimitiveList getBakingPrimitives() { 43 | return null; 44 | } 45 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/camera/PinholeLens.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.camera; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.CameraLens; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.Ray; 7 | 8 | public class PinholeLens implements CameraLens { 9 | private float au, av; 10 | private float aspect, fov; 11 | private float shiftX, shiftY; 12 | 13 | public PinholeLens() { 14 | fov = 90; 15 | aspect = 1; 16 | shiftX = shiftY = 0; 17 | update(); 18 | } 19 | 20 | public boolean update(ParameterList pl, SunflowAPI api) { 21 | // get parameters 22 | fov = pl.getFloat("fov", fov); 23 | aspect = pl.getFloat("aspect", aspect); 24 | shiftX = pl.getFloat("shift.x", shiftX); 25 | shiftY = pl.getFloat("shift.y", shiftY); 26 | update(); 27 | return true; 28 | } 29 | 30 | private void update() { 31 | au = (float) Math.tan(Math.toRadians(fov * 0.5f)); 32 | av = au / aspect; 33 | } 34 | 35 | public Ray getRay(float x, float y, int imageWidth, int imageHeight, double lensX, double lensY, double time) { 36 | float du = shiftX - au + ((2.0f * au * x) / (imageWidth - 1.0f)); 37 | float dv = shiftY - av + ((2.0f * av * y) / (imageHeight - 1.0f)); 38 | return new Ray(0, 0, 0, du, dv, -1); 39 | } 40 | } -------------------------------------------------------------------------------- /examples/shader_examples/Glass002.sc: -------------------------------------------------------------------------------- 1 | % photons { 2 | % caustics 10000000 kd 64 0.5 3 | % } 4 | 5 | shader { 6 | name "ground" 7 | type diffuse 8 | diff 0.800000011921 0.800000011921 0.800000011921 9 | } 10 | 11 | shader { 12 | name "shader01" 13 | type glass 14 | eta 1.1 15 | color { "sRGB nonlinear" 1 0.5 0.5 } 16 | } 17 | 18 | shader { 19 | name "shader02" 20 | type glass 21 | eta 1.3 22 | color { "sRGB nonlinear" 0.5 1 0.5 } 23 | } 24 | 25 | shader { 26 | name "shader03" 27 | type glass 28 | eta 1.5 29 | color { "sRGB nonlinear" 0.5 0.5 1 } 30 | } 31 | 32 | shader { 33 | name "shader04" 34 | type glass 35 | eta 1.7 36 | color { "sRGB nonlinear" 1 1 0.5 } 37 | } 38 | 39 | shader { 40 | name "shader05" 41 | type glass 42 | eta 1.9 43 | color { "sRGB nonlinear" 1 0.5 1 } 44 | } 45 | 46 | shader { 47 | name "shader06" 48 | type glass 49 | eta 2.1 50 | color { "sRGB nonlinear" 0 0.5 1 } 51 | } 52 | 53 | shader { 54 | name "shader07" 55 | type glass 56 | eta 2.3 57 | color { "sRGB nonlinear" 1 0.66 0.33 } 58 | } 59 | 60 | shader { 61 | name "shader08" 62 | type glass 63 | eta 2.5 64 | color { "sRGB nonlinear" 0.5 1 1 } 65 | } 66 | 67 | shader { 68 | name "shader09" 69 | type glass 70 | eta 2.7 71 | color { "sRGB nonlinear" 1 1 1 } 72 | } 73 | 74 | include "include/example_array.geo.sc" 75 | -------------------------------------------------------------------------------- /.externalToolBuilders/Ant Build.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/org/sunflow/core/CameraLens.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | /** 4 | * Represents a mapping from the 3D scene onto the final image. A camera lens is 5 | * responsible for determining what ray to cast through each pixel. 6 | */ 7 | public interface CameraLens extends RenderObject { 8 | /** 9 | * Create a new {@link Ray ray}to be cast through pixel (x,y) on the image 10 | * plane. Two sampling parameters are provided for lens sampling. They are 11 | * guarenteed to be in the interval [0,1). They can be used to perturb the 12 | * position of the source of the ray on the lens of the camera for DOF 13 | * effects. A third sampling parameter is provided for motion blur effects. 14 | * Note that the {@link Camera} class already handles camera movement motion 15 | * blur. Rays should be generated in camera space - that is, with the eye at 16 | * the origin, looking down the -Z axis, with +Y pointing up. 17 | * 18 | * @param x x coordinate of the (sub)pixel 19 | * @param y y coordinate of the (sub)pixel 20 | * @param imageWidth image width in pixels 21 | * @param imageHeight image height in pixels 22 | * @param lensX x lens sampling parameter 23 | * @param lensY y lens sampling parameter 24 | * @param time time sampling parameter 25 | * @return a new ray passing through the given pixel 26 | */ 27 | public Ray getRay(float x, float y, int imageWidth, int imageHeight, double lensX, double lensY, double time); 28 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/Timer.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | public class Timer { 4 | private long startTime, endTime; 5 | 6 | public Timer() { 7 | startTime = endTime = 0; 8 | } 9 | 10 | public void start() { 11 | startTime = endTime = System.nanoTime(); 12 | } 13 | 14 | public void end() { 15 | endTime = System.nanoTime(); 16 | } 17 | 18 | public long nanos() { 19 | return endTime - startTime; 20 | } 21 | 22 | public double seconds() { 23 | return (endTime - startTime) * 1e-9; 24 | } 25 | 26 | public static String toString(long nanos) { 27 | Timer t = new Timer(); 28 | t.endTime = nanos; 29 | return t.toString(); 30 | } 31 | 32 | public static String toString(double seconds) { 33 | Timer t = new Timer(); 34 | t.endTime = (long) (seconds * 1e9); 35 | return t.toString(); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | long millis = nanos() / (1000 * 1000); 41 | if (millis < 10000) 42 | return String.format("%dms", millis); 43 | long hours = millis / (60 * 60 * 1000); 44 | millis -= hours * 60 * 60 * 1000; 45 | long minutes = millis / (60 * 1000); 46 | millis -= minutes * 60 * 1000; 47 | long seconds = millis / 1000; 48 | millis -= seconds * 1000; 49 | return String.format("%d:%02d:%02d.%1d", hours, minutes, seconds, millis / 100); 50 | } 51 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/SpiralBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class SpiralBucketOrder implements BucketOrder { 6 | public int[] getBucketSequence(int nbw, int nbh) { 7 | int[] coords = new int[2 * nbw * nbh]; 8 | for (int i = 0; i < nbw * nbh; i++) { 9 | int bx, by; 10 | int center = (Math.min(nbw, nbh) - 1) / 2; 11 | int nx = nbw; 12 | int ny = nbh; 13 | while (i < (nx * ny)) { 14 | nx--; 15 | ny--; 16 | } 17 | int nxny = nx * ny; 18 | int minnxny = Math.min(nx, ny); 19 | if ((minnxny & 1) == 1) { 20 | if (i <= (nxny + ny)) { 21 | bx = nx - minnxny / 2; 22 | by = -minnxny / 2 + i - nxny; 23 | } else { 24 | bx = nx - minnxny / 2 - (i - (nxny + ny)); 25 | by = ny - minnxny / 2; 26 | } 27 | } else { 28 | if (i <= (nxny + ny)) { 29 | bx = -minnxny / 2; 30 | by = ny - minnxny / 2 - (i - nxny); 31 | } else { 32 | bx = -minnxny / 2 + (i - (nxny + ny)); 33 | by = -minnxny / 2; 34 | } 35 | } 36 | coords[2 * i + 0] = bx + center; 37 | coords[2 * i + 1] = by + center; 38 | } 39 | return coords; 40 | } 41 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/AmbientOcclusionShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Shader; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | 9 | public class AmbientOcclusionShader implements Shader { 10 | private Color bright; 11 | private Color dark; 12 | private int samples; 13 | private float maxDist; 14 | 15 | public AmbientOcclusionShader() { 16 | bright = Color.WHITE; 17 | dark = Color.BLACK; 18 | samples = 32; 19 | maxDist = Float.POSITIVE_INFINITY; 20 | } 21 | 22 | public AmbientOcclusionShader(Color c, float d) { 23 | this(); 24 | bright = c; 25 | maxDist = d; 26 | } 27 | 28 | public boolean update(ParameterList pl, SunflowAPI api) { 29 | bright = pl.getColor("bright", bright); 30 | dark = pl.getColor("dark", dark); 31 | samples = pl.getInt("samples", samples); 32 | maxDist = pl.getFloat("maxdist", maxDist); 33 | if (maxDist <= 0) 34 | maxDist = Float.POSITIVE_INFINITY; 35 | return true; 36 | } 37 | 38 | public Color getBrightColor(ShadingState state) { 39 | return bright; 40 | } 41 | 42 | public Color getRadiance(ShadingState state) { 43 | return state.occlusion(samples, maxDist, getBrightColor(state), dark); 44 | } 45 | 46 | public void scatterPhoton(ShadingState state, Color power) { 47 | } 48 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/Tesselatable.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.core.primitive.TriangleMesh; 4 | import org.sunflow.math.BoundingBox; 5 | import org.sunflow.math.Matrix4; 6 | 7 | /** 8 | * Represents an object which can be tesselated into a list of primitives such 9 | * as a {@link TriangleMesh}. 10 | */ 11 | public interface Tesselatable extends RenderObject { 12 | /** 13 | * Tesselate this object into a {@link PrimitiveList}. This may return 14 | * null if tesselation fails. 15 | * 16 | * @return a list of primitives generated by the tesselation 17 | */ 18 | public PrimitiveList tesselate(); 19 | 20 | /** 21 | * Compute a bounding box of this object in world space, using the specified 22 | * object-to-world transformation matrix. The bounds should be as exact as 23 | * possible, if they are difficult or expensive to compute exactly, you may 24 | * use {@link Matrix4#transform(BoundingBox)}. If the matrix is 25 | * null no transformation is needed, and object space is 26 | * equivalent to world space. This method may return null if 27 | * these bounds are difficult or impossible to compute, in which case the 28 | * tesselation will be executed right away and the bounds of the resulting 29 | * primitives will be used. 30 | * 31 | * @param o2w object to world transformation matrix 32 | * @return object bounding box in world space 33 | */ 34 | public BoundingBox getWorldBounds(Matrix4 o2w); 35 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/BitmapReader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * This is a very simple interface, designed to handle loading of bitmap data. 7 | */ 8 | public interface BitmapReader { 9 | /** 10 | * Load the specified filename. This method should throw exception if it 11 | * encounters any errors. If the file is valid but its contents are not 12 | * (invalid header for example), a {@link BitmapFormatException} may be 13 | * thrown. It is an error for this method to return null. 14 | * 15 | * @param filename image filename to load 16 | * @param isLinear if this is true, the bitmap is assumed to 17 | * be already in linear space. This can be usefull when reading 18 | * greyscale images for bump mapping for example. HDR formats can 19 | * ignore this flag since they usually always store data in 20 | * linear form. 21 | * @return a new {@link Bitmap} object 22 | */ 23 | public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException; 24 | 25 | /** 26 | * This exception can be used internally by bitmap readers to signal they 27 | * have encountered a valid file but which contains invalid content. 28 | */ 29 | @SuppressWarnings("serial") 30 | public static final class BitmapFormatException extends Exception { 31 | public BitmapFormatException(String message) { 32 | super(message); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/bucket/RandomBucketOrder.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.bucket; 2 | 3 | import org.sunflow.core.BucketOrder; 4 | 5 | public class RandomBucketOrder implements BucketOrder { 6 | public int[] getBucketSequence(int nbw, int nbh) { 7 | int[] coords = new int[2 * nbw * nbh]; 8 | for (int i = 0; i < nbw * nbh; i++) { 9 | int by = i / nbw; 10 | int bx = i % nbw; 11 | if ((by & 1) == 1) 12 | bx = nbw - 1 - bx; 13 | coords[2 * i + 0] = bx; 14 | coords[2 * i + 1] = by; 15 | } 16 | 17 | long seed = 2463534242L; 18 | for (int i = 0; i < coords.length; i++) { 19 | // pick 2 random indices 20 | seed = xorshift(seed); 21 | int src = mod((int) seed, nbw * nbh); 22 | seed = xorshift(seed); 23 | int dst = mod((int) seed, nbw * nbh); 24 | int tmp = coords[2 * src + 0]; 25 | coords[2 * src + 0] = coords[2 * dst + 0]; 26 | coords[2 * dst + 0] = tmp; 27 | tmp = coords[2 * src + 1]; 28 | coords[2 * src + 1] = coords[2 * dst + 1]; 29 | coords[2 * dst + 1] = tmp; 30 | } 31 | 32 | return coords; 33 | } 34 | 35 | private int mod(int a, int b) { 36 | int m = a % b; 37 | return (m < 0) ? m + b : m; 38 | } 39 | 40 | private long xorshift(long y) { 41 | y = y ^ (y << 13); 42 | y = y ^ (y >>> 17); // unsigned 43 | y = y ^ (y << 5); 44 | return y; 45 | } 46 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/BitmapRGBE.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import org.sunflow.image.Bitmap; 4 | import org.sunflow.image.Color; 5 | 6 | public class BitmapRGBE extends Bitmap { 7 | private int w, h; 8 | private int[] data; 9 | private static final float[] EXPONENT = new float[256]; 10 | 11 | static { 12 | EXPONENT[0] = 0; 13 | for (int i = 1; i < 256; i++) { 14 | float f = 1.0f; 15 | int e = i - (128 + 8); 16 | if (e > 0) 17 | for (int j = 0; j < e; j++) 18 | f *= 2.0f; 19 | else 20 | for (int j = 0; j < -e; j++) 21 | f *= 0.5f; 22 | EXPONENT[i] = f; 23 | } 24 | } 25 | 26 | public BitmapRGBE(int w, int h, int[] data) { 27 | this.w = w; 28 | this.h = h; 29 | this.data = data; 30 | } 31 | 32 | @Override 33 | public int getWidth() { 34 | return w; 35 | } 36 | 37 | @Override 38 | public int getHeight() { 39 | return h; 40 | } 41 | 42 | @Override 43 | public Color readColor(int x, int y) { 44 | int rgbe = data[x + y * w]; 45 | float f = EXPONENT[rgbe & 0xFF]; 46 | float r = f * ((rgbe >>> 24) + 0.5f); 47 | float g = f * (((rgbe >> 16) & 0xFF) + 0.5f); 48 | float b = f * (((rgbe >> 8) & 0xFF) + 0.5f); 49 | return new Color(r, g, b); 50 | } 51 | 52 | @Override 53 | public float readAlpha(int x, int y) { 54 | return 1; 55 | } 56 | } -------------------------------------------------------------------------------- /examples/wireframe_demo.java: -------------------------------------------------------------------------------- 1 | import org.sunflow.PluginRegistry; 2 | import org.sunflow.core.ShadingState; 3 | import org.sunflow.core.shader.WireframeShader; 4 | import org.sunflow.image.Color; 5 | 6 | public void build() { 7 | PluginRegistry.shaderPlugins.registerPlugin("custom_wireframe", CustomWireShader.class); 8 | 9 | parameter("width", (float) (Math.PI * 0.5 / 8192)); 10 | shader("ao_wire", "custom_wireframe"); 11 | // you can put the path to your own scene here to use this rendering 12 | // technique just copy this file to the same directory as your main .sc 13 | // file, and swap the fileanme in the line below 14 | include("gumbo_and_teapot.sc"); 15 | 16 | // shader override 17 | parameter("override.shader", "ao_wire"); 18 | parameter("override.photons", true); 19 | 20 | // this may need to be tweaked if you want really fine lines 21 | // this is higher than most scenes need so if you render with ambocc = 22 | // false, make sure you turn down the sampling rates of 23 | // dof/lights/gi/reflections accordingly 24 | parameter("aa.min", 2); 25 | parameter("aa.max", 2); 26 | parameter("filter", "blackman-harris"); 27 | options(DEFAULT_OPTIONS); 28 | } 29 | 30 | 31 | // this class will be registered as a plugin 32 | public static class CustomWireShader extends WireframeShader { 33 | // set to false to overlay wires on regular shaders 34 | private boolean ambocc = true; 35 | 36 | public Color getFillColor(ShadingState state) { 37 | return ambocc ? state.occlusion(16, 6.0f) : state.getShader().getRadiance(state); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/shader_examples/AO001.sc: -------------------------------------------------------------------------------- 1 | shader { 2 | name ground 3 | type amb-occ 4 | bright 1 0 1 5 | dark 0 0 0 6 | samples 128 7 | dist 6 8 | } 9 | 10 | shader { 11 | name "shader01" 12 | type amb-occ 13 | bright 1 0.5 0 14 | dark .3 .1 0 15 | samples 1 16 | dist 6 17 | } 18 | 19 | shader { 20 | name "shader02" 21 | type amb-occ 22 | bright 1 0.5 0 23 | dark .3 .1 0 24 | samples 2 25 | dist 6 26 | } 27 | 28 | shader { 29 | name "shader03" 30 | type amb-occ 31 | bright 1 0.5 0 32 | dark .3 .1 0 33 | samples 4 34 | dist 6 35 | } 36 | 37 | shader { 38 | name "shader04" 39 | type amb-occ 40 | bright 1 0.5 0 41 | dark .3 .1 0 42 | samples 8 43 | dist 10 44 | } 45 | 46 | shader { 47 | name "shader05" 48 | type amb-occ 49 | bright 1 0.5 0 50 | dark .3 .1 0 51 | samples 16 52 | dist 6 53 | } 54 | 55 | shader { 56 | name "shader06" 57 | type amb-occ 58 | bright 1 0.5 0 59 | dark .3 .1 0 60 | samples 32 61 | dist 6 62 | } 63 | 64 | shader { 65 | name "shader07" 66 | type amb-occ 67 | bright 1 0.5 0 68 | dark .3 .1 0 69 | samples 64 70 | dist 6 71 | } 72 | 73 | shader { 74 | name "shader08" 75 | type amb-occ 76 | bright 1 0.5 0 77 | dark .3 .1 0 78 | samples 128 79 | dist 6 80 | } 81 | 82 | shader { 83 | name "shader09" 84 | type amb-occ 85 | bright 1 0.5 0 86 | dark .3 .1 0 87 | samples 256 88 | dist 6 89 | } 90 | 91 | include include/example_array.geo.sc 92 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowHelperNode.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_HELPER_NODE_H 2 | #define SUNFLOW_HELPER_NODE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | 17 | 18 | class sunflowHelperNode : public MPxLocatorNode 19 | { 20 | public: 21 | 22 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 23 | 24 | virtual void draw( M3dView & view, const MDagPath & path, 25 | M3dView::DisplayStyle displaystyle, 26 | M3dView::DisplayStatus displaystatus ); 27 | 28 | virtual bool isBounded() const; 29 | virtual MBoundingBox boundingBox() const; 30 | 31 | virtual MStatus connectionMade ( const MPlug & plug, const MPlug & otherPlug, bool asSrc ); 32 | 33 | static void * creator(); 34 | static MStatus initialize(); 35 | 36 | static MObject aDummy; 37 | static MObject aHelperType; 38 | static MObject aHelperColor; 39 | static MObject aHelperOpacity; 40 | static MObject aMeshPath; 41 | static MObject aShaderNode; 42 | 43 | public: 44 | static MTypeId id; 45 | 46 | int m_helperType; 47 | MColor m_helperColor; 48 | 49 | }; 50 | 51 | #endif /* SUNFLOW_HELPER_NODE_H */ 52 | -------------------------------------------------------------------------------- /src/org/sunflow/core/gi/FakeGIEngine.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.gi; 2 | 3 | import org.sunflow.core.GIEngine; 4 | import org.sunflow.core.Options; 5 | import org.sunflow.core.Scene; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.image.Color; 8 | import org.sunflow.math.Vector3; 9 | 10 | /** 11 | * This is a quick way to get a bit of ambient lighting into your scene with 12 | * hardly any overhead. It's based on the formula found here: 13 | * 14 | * @link http://www.cs.utah.edu/~shirley/papers/rtrt/node7.html#SECTION00031100000000000000 15 | */ 16 | public class FakeGIEngine implements GIEngine { 17 | private Vector3 up; 18 | private Color sky; 19 | private Color ground; 20 | 21 | public Color getIrradiance(ShadingState state, Color diffuseReflectance) { 22 | float cosTheta = Vector3.dot(up, state.getNormal()); 23 | float sin2 = (1 - cosTheta * cosTheta); 24 | float sine = sin2 > 0 ? (float) Math.sqrt(sin2) * 0.5f : 0; 25 | if (cosTheta > 0) 26 | return Color.blend(sky, ground, sine); 27 | else 28 | return Color.blend(ground, sky, sine); 29 | } 30 | 31 | public Color getGlobalRadiance(ShadingState state) { 32 | return Color.BLACK; 33 | } 34 | 35 | public boolean init(Options options, Scene scene) { 36 | up = options.getVector("gi.fake.up", new Vector3(0, 1, 0)).normalize(); 37 | sky = options.getColor("gi.fake.sky", Color.WHITE).copy(); 38 | ground = options.getColor("gi.fake.ground", Color.BLACK).copy(); 39 | sky.mul((float) Math.PI); 40 | ground.mul((float) Math.PI); 41 | return true; 42 | } 43 | } -------------------------------------------------------------------------------- /examples/shader_examples/Phong002.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "ground" 9 | type diffuse 10 | diff 0.800000011921 0.800000011921 0.800000011921 11 | } 12 | 13 | shader { 14 | name "shader01" 15 | type phong 16 | diff { "sRGB nonlinear" 0.33 0.33 1 } 17 | spec 1 1 1 1 18 | samples 4 19 | } 20 | 21 | shader { 22 | name "shader02" 23 | type phong 24 | diff { "sRGB nonlinear" 0.33 0.33 1 } 25 | spec 1 1 1 4 26 | samples 4 27 | } 28 | 29 | shader { 30 | name "shader03" 31 | type phong 32 | diff { "sRGB nonlinear" 0.33 0.33 1 } 33 | spec 1 1 1 8 34 | samples 4 35 | } 36 | 37 | shader { 38 | name "shader04" 39 | type phong 40 | diff { "sRGB nonlinear" 0.33 0.33 1 } 41 | spec 1 1 1 10 42 | samples 4 43 | } 44 | 45 | shader { 46 | name "shader05" 47 | type phong 48 | diff { "sRGB nonlinear" 0.33 0.33 1 } 49 | spec 1 1 1 20 50 | samples 4 51 | } 52 | 53 | shader { 54 | name "shader06" 55 | type phong 56 | diff { "sRGB nonlinear" 0.33 0.33 1 } 57 | spec 1 1 1 30 58 | samples 4 59 | } 60 | 61 | shader { 62 | name "shader07" 63 | type phong 64 | diff { "sRGB nonlinear" 0.33 0.33 1 } 65 | spec 1 1 1 100 66 | samples 4 67 | } 68 | 69 | shader { 70 | name "shader08" 71 | type phong 72 | diff { "sRGB nonlinear" 0.33 0.33 1 } 73 | spec 1 1 1 300 74 | samples 4 75 | } 76 | 77 | shader { 78 | name "shader09" 79 | type phong 80 | diff { "sRGB nonlinear" 0.33 0.33 1 } 81 | spec 1 1 1 600 82 | samples 4 83 | } 84 | 85 | include "include/example_array.geo.sc" 86 | -------------------------------------------------------------------------------- /src/org/sunflow/image/readers/BMPBitmapReader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.readers; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | import org.sunflow.image.Bitmap; 10 | import org.sunflow.image.BitmapReader; 11 | import org.sunflow.image.Color; 12 | import org.sunflow.image.formats.BitmapRGB8; 13 | 14 | public class BMPBitmapReader implements BitmapReader { 15 | public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { 16 | // regular image, load using Java api - ignore alpha channel 17 | BufferedImage bi = ImageIO.read(new File(filename)); 18 | int width = bi.getWidth(); 19 | int height = bi.getHeight(); 20 | byte[] pixels = new byte[3 * width * height]; 21 | for (int y = 0, index = 0; y < height; y++) { 22 | for (int x = 0; x < width; x++, index += 3) { 23 | int argb = bi.getRGB(x, height - 1 - y); 24 | pixels[index + 0] = (byte) (argb >> 16); 25 | pixels[index + 1] = (byte) (argb >> 8); 26 | pixels[index + 2] = (byte) argb; 27 | } 28 | } 29 | if (!isLinear) { 30 | for (int index = 0; index < pixels.length; index += 3) { 31 | pixels[index + 0] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 0]); 32 | pixels[index + 1] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 1]); 33 | pixels[index + 2] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 2]); 34 | } 35 | } 36 | return new BitmapRGB8(width, height, pixels); 37 | } 38 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/readers/JPGBitmapReader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.readers; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | import org.sunflow.image.Bitmap; 10 | import org.sunflow.image.BitmapReader; 11 | import org.sunflow.image.Color; 12 | import org.sunflow.image.formats.BitmapRGB8; 13 | 14 | public class JPGBitmapReader implements BitmapReader { 15 | public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { 16 | // regular image, load using Java api - ignore alpha channel 17 | BufferedImage bi = ImageIO.read(new File(filename)); 18 | int width = bi.getWidth(); 19 | int height = bi.getHeight(); 20 | byte[] pixels = new byte[3 * width * height]; 21 | for (int y = 0, index = 0; y < height; y++) { 22 | for (int x = 0; x < width; x++, index += 3) { 23 | int argb = bi.getRGB(x, height - 1 - y); 24 | pixels[index + 0] = (byte) (argb >> 16); 25 | pixels[index + 1] = (byte) (argb >> 8); 26 | pixels[index + 2] = (byte) argb; 27 | } 28 | } 29 | if (!isLinear) { 30 | for (int index = 0; index < pixels.length; index += 3) { 31 | pixels[index + 0] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 0]); 32 | pixels[index + 1] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 1]); 33 | pixels[index + 2] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 2]); 34 | } 35 | } 36 | return new BitmapRGB8(width, height, pixels); 37 | } 38 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/GIEngine.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | 5 | /** 6 | * This represents a global illumination algorithm. It provides an interface to 7 | * compute indirect diffuse bounces of light and make those results available to 8 | * shaders. 9 | */ 10 | public interface GIEngine { 11 | /** 12 | * This is an optional method for engines that contain a secondary 13 | * illumination engine which can return an approximation of the global 14 | * radiance in the scene (like a photon map). Engines can safely return 15 | * Color.BLACK if they can't or don't wish to support this. 16 | * 17 | * @param state shading state 18 | * @return color approximating global radiance 19 | */ 20 | public Color getGlobalRadiance(ShadingState state); 21 | 22 | /** 23 | * Initialize the engine. This is called before rendering begins. 24 | * 25 | * @return true if the init phase succeeded, 26 | * false otherwise 27 | */ 28 | public boolean init(Options options, Scene scene); 29 | 30 | /** 31 | * Return the incomming irradiance due to indirect diffuse illumination at 32 | * the specified surface point. 33 | * 34 | * @param state current render state describing the point to be computed 35 | * @param diffuseReflectance diffuse albedo of the point being shaded, this 36 | * can be used for importance tracking 37 | * @return irradiance from indirect diffuse illumination at the specified 38 | * point 39 | */ 40 | public Color getIrradiance(ShadingState state, Color diffuseReflectance); 41 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/readers/PNGBitmapReader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.readers; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | import org.sunflow.image.Bitmap; 10 | import org.sunflow.image.BitmapReader; 11 | import org.sunflow.image.Color; 12 | import org.sunflow.image.formats.BitmapRGBA8; 13 | 14 | public class PNGBitmapReader implements BitmapReader { 15 | public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException { 16 | // regular image, load using Java api 17 | BufferedImage bi = ImageIO.read(new File(filename)); 18 | int width = bi.getWidth(); 19 | int height = bi.getHeight(); 20 | byte[] pixels = new byte[4 * width * height]; 21 | for (int y = 0, index = 0; y < height; y++) { 22 | for (int x = 0; x < width; x++, index += 4) { 23 | int argb = bi.getRGB(x, height - 1 - y); 24 | pixels[index + 0] = (byte) (argb >> 16); 25 | pixels[index + 1] = (byte) (argb >> 8); 26 | pixels[index + 2] = (byte) argb; 27 | pixels[index + 3] = (byte) (argb >> 24); 28 | } 29 | } 30 | if (!isLinear) { 31 | for (int index = 0; index < pixels.length; index += 4) { 32 | pixels[index + 0] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 0]); 33 | pixels[index + 1] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 1]); 34 | pixels[index + 2] = Color.NATIVE_SPACE.rgbToLinear(pixels[index + 2]); 35 | } 36 | } 37 | return new BitmapRGBA8(width, height, pixels); 38 | } 39 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/AEsunflowHelperNodeTemplate.mel: -------------------------------------------------------------------------------- 1 | 2 | global proc AEsunflowHelperNewGroup(string $shadingGroupPlug ) 3 | { 4 | createRenderNode -shadersSG ("connectAttr %nodeSG.message "+$shadingGroupPlug) ""; 5 | } 6 | 7 | global proc AEsunflowHelperReplaceGroup( string $shadingGroupPlug, string $dashSource, string $node ) 8 | { 9 | connectAttr -f ($node+".message") $shadingGroupPlug; 10 | } 11 | 12 | global proc AEsunflowHelperNodeShaderNew( string $shadingGroupPlug ) 13 | { 14 | setUITemplate -pst attributeEditorTemplate; 15 | 16 | attrNavigationControlGrp -label "Shader" shadingGroupOverride; 17 | AEsunflowHelperNodeShaderReplace($shadingGroupPlug); 18 | } 19 | 20 | global proc AEsunflowHelperNodeShaderReplace( string $shadingGroupPlug ) 21 | { 22 | attrNavigationControlGrp -edit 23 | -attribute $shadingGroupPlug 24 | -enable true 25 | -createNew ("AEsunflowHelperNewGroup "+$shadingGroupPlug) 26 | -connectToExisting ("AEsunflowHelperReplaceGroup "+$shadingGroupPlug) 27 | shadingGroupOverride; 28 | } 29 | 30 | global proc AEsunflowHelperNodeTemplate( string $nodeName ) 31 | { 32 | editorTemplate -beginScrollLayout; 33 | editorTemplate -l "Type" -addControl "type"; 34 | editorTemplate -beginLayout "File Mesh Attributes" -collapse 0; 35 | editorTemplate -l "File" -addControl "meshPath"; 36 | editorTemplate -endLayout; 37 | editorTemplate -beginLayout "Infinite Plane" -collapse 0; 38 | editorTemplate 39 | -callCustom "AEsunflowHelperNodeShaderNew" 40 | "AEsunflowHelperNodeShaderReplace" 41 | "shaderNode"; 42 | editorTemplate -endLayout; 43 | //editorTemplate -addExtraControls; 44 | editorTemplate -endScrollLayout; 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/org/sunflow/core/TextureCache.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.sunflow.system.UI; 6 | import org.sunflow.system.UI.Module; 7 | 8 | /** 9 | * Maintains a cache of all loaded texture maps. This is usefull if the same 10 | * texture might be used more than once in your scene. 11 | */ 12 | public final class TextureCache { 13 | private static HashMap textures = new HashMap(); 14 | 15 | private TextureCache() { 16 | } 17 | 18 | /** 19 | * Gets a reference to the texture specified by the given filename. If the 20 | * texture has already been loaded the previous reference is returned, 21 | * otherwise, a new texture is created. 22 | * 23 | * @param filename image file to load 24 | * @param isLinear is the texture gamma corrected? 25 | * @return texture object 26 | * @see Texture 27 | */ 28 | public synchronized static Texture getTexture(String filename, boolean isLinear) { 29 | if (textures.containsKey(filename)) { 30 | UI.printInfo(Module.TEX, "Using cached copy for file \"%s\" ...", filename); 31 | return textures.get(filename); 32 | } 33 | UI.printInfo(Module.TEX, "Using file \"%s\" ...", filename); 34 | Texture t = new Texture(filename, isLinear); 35 | textures.put(filename, t); 36 | return t; 37 | } 38 | 39 | /** 40 | * Flush all textures from the cache, this will cause them to be reloaded 41 | * anew the next time they are accessed. 42 | */ 43 | public synchronized static void flush() { 44 | UI.printInfo(Module.TEX, "Flushing texture cache"); 45 | textures.clear(); 46 | } 47 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/UserInterface.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | import org.sunflow.system.UI.Module; 4 | import org.sunflow.system.UI.PrintLevel; 5 | 6 | public interface UserInterface { 7 | /** 8 | * Displays some information to the user from the specified module with the 9 | * specified print level. A user interface is free to show or ignore any 10 | * message. Level filtering is done in the core and shouldn't be 11 | * re-implemented by the user interface. All messages will be short enough 12 | * to fit on one line. 13 | * 14 | * @param m module the message came from 15 | * @param level seriousness of the message 16 | * @param s string to display 17 | */ 18 | void print(Module m, PrintLevel level, String s); 19 | 20 | /** 21 | * Prepare a progress bar representing a lengthy task. The actual progress 22 | * is first shown by the call to update and closed when update is closed 23 | * with the max value. It is currently not possible to nest calls to 24 | * setTask, so only one task needs to be tracked at a time. 25 | * 26 | * @param s desriptive string 27 | * @param min minimum value of the task 28 | * @param max maximum value of the task 29 | */ 30 | void taskStart(String s, int min, int max); 31 | 32 | /** 33 | * Updates the current progress bar to a value between the current min and 34 | * max. When min or max are passed the progressed bar is shown or hidden 35 | * respectively. 36 | * 37 | * @param current current value of the task in progress. 38 | */ 39 | void taskUpdate(int current); 40 | 41 | /** 42 | * Closes the current progress bar to indicate the task is over 43 | */ 44 | void taskStop(); 45 | } -------------------------------------------------------------------------------- /examples/shader_examples/Glass003.sc: -------------------------------------------------------------------------------- 1 | %photons { 2 | % caustics 10000000 kd 64 0.5 3 | %} 4 | 5 | shader { 6 | name "ground" 7 | type diffuse 8 | diff 0.800000011921 0.800000011921 0.800000011921 9 | } 10 | 11 | shader { 12 | name "shader01" 13 | type glass 14 | eta 1.55 15 | color 1 1 1 16 | absorbtion.distance 2 17 | absorbtion.color 0.8 0.2 0.2 18 | } 19 | 20 | shader { 21 | name "shader02" 22 | type glass 23 | eta 1.55 24 | color 1 1 1 25 | absorbtion.distance 1 26 | absorbtion.color 0.8 0.2 0.2 27 | } 28 | 29 | shader { 30 | name "shader03" 31 | type glass 32 | eta 1.55 33 | color 1 1 1 34 | absorbtion.distance 0.5 35 | absorbtion.color 0.8 0.2 0.2 36 | } 37 | 38 | shader { 39 | name "shader04" 40 | type glass 41 | eta 1.55 42 | color 1 1 1 43 | absorbtion.distance 2 44 | absorbtion.color 0.1 0.7 0.1 45 | } 46 | 47 | shader { 48 | name "shader05" 49 | type glass 50 | eta 1.55 51 | color 1 1 1 52 | absorbtion.distance 1 53 | absorbtion.color 0.1 0.7 0.1 54 | } 55 | 56 | shader { 57 | name "shader06" 58 | type glass 59 | eta 1.55 60 | color 1 1 1 61 | absorbtion.distance 0.5 62 | absorbtion.color 0.1 0.7 0.1 63 | } 64 | 65 | shader { 66 | name "shader07" 67 | type glass 68 | eta 1.55 69 | color 1 1 1 70 | absorbtion.distance 2 71 | absorbtion.color 0.3 0.3 0.6 72 | } 73 | 74 | shader { 75 | name "shader08" 76 | type glass 77 | eta 1.55 78 | color 1 1 1 79 | absorbtion.distance 1 80 | absorbtion.color 0.3 0.3 0.6 81 | } 82 | 83 | shader { 84 | name "shader09" 85 | type glass 86 | eta 1.55 87 | color 1 1 1 88 | absorbtion.distance 0.5 89 | absorbtion.color 0.3 0.3 0.6 90 | } 91 | 92 | include "include/example_array.geo.sc" 93 | -------------------------------------------------------------------------------- /src/org/sunflow/core/PhotonStore.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | import org.sunflow.math.BoundingBox; 5 | import org.sunflow.math.Vector3; 6 | 7 | /** 8 | * Describes an object which can store photons. 9 | */ 10 | public interface PhotonStore { 11 | /** 12 | * Number of photons to emit from this surface. 13 | * 14 | * @return number of photons 15 | */ 16 | int numEmit(); 17 | 18 | /** 19 | * Initialize this object for the specified scene size. 20 | * 21 | * @param sceneBounds scene bounding box 22 | */ 23 | void prepare(Options options, BoundingBox sceneBounds); 24 | 25 | /** 26 | * Store the specified photon. 27 | * 28 | * @param state shading state 29 | * @param dir photon direction 30 | * @param power photon power 31 | * @param diffuse diffuse color at the hit point 32 | */ 33 | void store(ShadingState state, Vector3 dir, Color power, Color diffuse); 34 | 35 | /** 36 | * Initialize the map after all photons have been stored. This can be used 37 | * to balance a kd-tree based photon map for example. 38 | */ 39 | void init(); 40 | 41 | /** 42 | * Allow photons reflected diffusely? 43 | * 44 | * @return true if diffuse bounces should be traced 45 | */ 46 | boolean allowDiffuseBounced(); 47 | 48 | /** 49 | * Allow specularly reflected photons? 50 | * 51 | * @return true if specular reflection bounces should be 52 | * traced 53 | */ 54 | boolean allowReflectionBounced(); 55 | 56 | /** 57 | * Allow refracted photons? 58 | * 59 | * @return true if refracted bounces should be traced 60 | */ 61 | boolean allowRefractionBounced(); 62 | } -------------------------------------------------------------------------------- /examples/shader_examples/Ward001.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "ground" 9 | type diffuse 10 | diff 0.800000011921 0.800000011921 0.800000011921 11 | } 12 | 13 | shader { 14 | name "shader01" 15 | type ward 16 | diff { "sRGB nonlinear" .33 1 .33 } 17 | spec { "sRGB nonlinear" 1 1 1 } 18 | rough .4 .01 19 | } 20 | 21 | shader { 22 | name "shader02" 23 | type ward 24 | diff { "sRGB nonlinear" .33 1 .33 } 25 | spec { "sRGB nonlinear" 1 1 1 } 26 | rough .35 .01 27 | } 28 | 29 | shader { 30 | name "shader03" 31 | type ward 32 | diff { "sRGB nonlinear" .33 1 .33 } 33 | spec { "sRGB nonlinear" 1 1 1 } 34 | rough .3 .01 35 | samples 4 36 | } 37 | 38 | shader { 39 | name "shader04" 40 | type ward 41 | diff { "sRGB nonlinear" .33 1 .33 } 42 | spec 1 1 1 43 | rough .25 .01 44 | samples 4 45 | } 46 | 47 | shader { 48 | name "shader05" 49 | type ward 50 | diff { "sRGB nonlinear" .33 1 .33 } 51 | spec 1 1 1 52 | rough .2 .01 53 | samples 4 54 | } 55 | 56 | shader { 57 | name "shader06" 58 | type ward 59 | diff { "sRGB nonlinear" .33 1 .33 } 60 | spec 1 1 1 61 | rough .15 .01 62 | samples 4 63 | } 64 | 65 | shader { 66 | name "shader07" 67 | type ward 68 | diff { "sRGB nonlinear" .33 1 .33 } 69 | spec 1 1 1 70 | rough .1 .01 71 | samples 4 72 | } 73 | 74 | shader { 75 | name "shader08" 76 | type ward 77 | diff { "sRGB nonlinear" .33 1 .33 } 78 | spec { "sRGB nonlinear" 1 1 1 } 79 | rough .05 .01 80 | samples 4 81 | } 82 | 83 | shader { 84 | name "shader09" 85 | type ward 86 | diff { "sRGB nonlinear" .33 1 .33 } 87 | spec 1 1 1 88 | rough .01 .01 89 | samples 4 90 | } 91 | 92 | include "include/example_array.geo.sc" 93 | -------------------------------------------------------------------------------- /examples/shader_examples/Phong001.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "ground" 9 | type diffuse 10 | diff 0.800000011921 0.800000011921 0.800000011921 11 | } 12 | 13 | shader { 14 | name "shader01" 15 | type phong 16 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 17 | spec { "sRGB nonlinear" 0.28 1 1 } 1 18 | samples 4 19 | } 20 | 21 | shader { 22 | name "shader02" 23 | type phong 24 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 25 | spec { "sRGB nonlinear" 0.28 1 1 } 4 26 | samples 4 27 | } 28 | 29 | shader { 30 | name "shader03" 31 | type phong 32 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 33 | spec { "sRGB nonlinear" 0.28 1 1 } 8 34 | samples 4 35 | } 36 | 37 | shader { 38 | name "shader04" 39 | type phong 40 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 41 | spec { "sRGB nonlinear" 0.28 1 1 } 10 42 | samples 4 43 | } 44 | 45 | shader { 46 | name "shader05" 47 | type phong 48 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 49 | spec { "sRGB nonlinear" 0.28 1 1 } 20 50 | samples 4 51 | } 52 | 53 | shader { 54 | name "shader06" 55 | type phong 56 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 57 | spec { "sRGB nonlinear" 0.28 1 1 } 30 58 | samples 4 59 | } 60 | 61 | shader { 62 | name "shader07" 63 | type phong 64 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 65 | spec { "sRGB nonlinear" 0.28 1 1 } 100 66 | samples 4 67 | } 68 | 69 | shader { 70 | name "shader08" 71 | type phong 72 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 73 | spec { "sRGB nonlinear" 0.28 1 1 } 300 74 | samples 4 75 | } 76 | 77 | shader { 78 | name "shader09" 79 | type phong 80 | diff { "sRGB nonlinear" 0.1 0.1 0.7 } 81 | spec { "sRGB nonlinear" 0.28 1 1 } 600 82 | samples 4 83 | } 84 | 85 | include "include/example_array.geo.sc" 86 | -------------------------------------------------------------------------------- /src/org/sunflow/image/writers/HDRBitmapWriter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.writers; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | import org.sunflow.image.BitmapWriter; 9 | import org.sunflow.image.Color; 10 | import org.sunflow.image.ColorEncoder; 11 | 12 | public class HDRBitmapWriter implements BitmapWriter { 13 | private String filename; 14 | private int width, height; 15 | private int[] data; 16 | 17 | public void configure(String option, String value) { 18 | } 19 | 20 | public void openFile(String filename) throws IOException { 21 | this.filename = filename; 22 | } 23 | 24 | public void writeHeader(int width, int height, int tileSize) throws IOException, UnsupportedOperationException { 25 | this.width = width; 26 | this.height = height; 27 | data = new int[width * height]; 28 | } 29 | 30 | public void writeTile(int x, int y, int w, int h, Color[] color, float[] alpha) throws IOException { 31 | int[] tileData = ColorEncoder.encodeRGBE(color); 32 | for (int j = 0, index = 0, pixel = x + y * width; j < h; j++, pixel += width - w) 33 | for (int i = 0; i < w; i++, index++, pixel++) 34 | data[pixel] = tileData[index]; 35 | } 36 | 37 | public void closeFile() throws IOException { 38 | OutputStream f = new BufferedOutputStream(new FileOutputStream(filename)); 39 | f.write("#?RGBE\n".getBytes()); 40 | f.write("FORMAT=32-bit_rle_rgbe\n\n".getBytes()); 41 | f.write(("-Y " + height + " +X " + width + "\n").getBytes()); 42 | for (int i = 0; i < data.length; i++) { 43 | int rgbe = data[i]; 44 | f.write(rgbe >> 24); 45 | f.write(rgbe >> 16); 46 | f.write(rgbe >> 8); 47 | f.write(rgbe); 48 | } 49 | f.close(); 50 | } 51 | } -------------------------------------------------------------------------------- /examples/shader_examples/Ward002.sc: -------------------------------------------------------------------------------- 1 | trace-depths { 2 | diff 0 3 | refl 1 4 | refr 0 5 | } 6 | 7 | shader { 8 | name "ground" 9 | type diffuse 10 | diff 0.800000011921 0.800000011921 0.800000011921 11 | } 12 | 13 | shader { 14 | name "shader01" 15 | type ward 16 | diff { "sRGB nonlinear" .33 1 .33 } 17 | spec { "sRGB nonlinear" 1 1 1 } 18 | rough .07 .1 19 | samples 4 20 | } 21 | 22 | shader { 23 | name "shader02" 24 | type ward 25 | diff { "sRGB nonlinear" .33 1 .33 } 26 | spec { "sRGB nonlinear" 1 1 1 } 27 | rough .07 .2 28 | samples 4 29 | } 30 | 31 | shader { 32 | name "shader03" 33 | type ward 34 | diff { "sRGB nonlinear" .33 1 .33 } 35 | spec { "sRGB nonlinear" 1 1 1 } 36 | rough .07 .3 37 | samples 4 38 | } 39 | 40 | shader { 41 | name "shader04" 42 | type ward 43 | diff { "sRGB nonlinear" .33 1 .33 } 44 | spec { "sRGB nonlinear" 1 1 1 } 45 | rough .07 .4 46 | samples 4 47 | } 48 | 49 | shader { 50 | name "shader05" 51 | type ward 52 | diff { "sRGB nonlinear" .33 1 .33 } 53 | spec { "sRGB nonlinear" 1 1 1 } 54 | rough .07 .5 55 | samples 4 56 | } 57 | 58 | shader { 59 | name "shader06" 60 | type ward 61 | diff { "sRGB nonlinear" .33 1 .33 } 62 | spec { "sRGB nonlinear" 1 1 1 } 63 | rough .07 .6 64 | samples 4 65 | } 66 | 67 | shader { 68 | name "shader07" 69 | type ward 70 | diff { "sRGB nonlinear" .33 1 .33 } 71 | spec { "sRGB nonlinear" 1 1 1 } 72 | rough .07 .7 73 | samples 4 74 | } 75 | 76 | shader { 77 | name "shader08" 78 | type ward 79 | diff { "sRGB nonlinear" .33 1 .33 } 80 | spec { "sRGB nonlinear" 1 1 1 } 81 | rough .07 .8 82 | samples 4 83 | } 84 | 85 | shader { 86 | name "shader09" 87 | type ward 88 | diff { "sRGB nonlinear" .33 1 .33 } 89 | spec { "sRGB nonlinear" 1 1 1 } 90 | rough .07 .9 91 | samples 4 92 | } 93 | 94 | include "include/example_array.geo.sc" 95 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowGlobalsNode.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_GLOBALS_NODE_H 2 | #define SUNFLOW_GLOBALS_NODE_H 3 | 4 | ///////////////////////////////////////////////////////////////////////////////////////// 5 | //Sunflow Render Globals Node 6 | ///////////////////////////////////////////////////////////////////////////////////////// 7 | 8 | #include 9 | 10 | class sunflowGlobalsNode : public MPxNode 11 | { 12 | public: 13 | sunflowGlobalsNode(){}; 14 | virtual ~sunflowGlobalsNode(){}; 15 | 16 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 17 | 18 | static void* creator(); 19 | static MStatus initialize(); 20 | 21 | static MObject renderMode; 22 | static MObject preset; 23 | static MObject pixelFilter; 24 | static MObject minSamples; 25 | static MObject maxSamples; 26 | 27 | static MObject enablePhotons; 28 | static MObject Photons; 29 | static MObject PhotonsKd; 30 | static MObject PhotonsRadius; 31 | 32 | static MObject diffuseDepth; 33 | static MObject reflectionDepth; 34 | static MObject refractionDepth; 35 | 36 | static MObject enableGI; 37 | static MObject GIMode; 38 | 39 | static MObject PTSamples; 40 | 41 | static MObject IGISamples; 42 | static MObject IGISets; 43 | static MObject IGIBias; 44 | static MObject IGIBSamples; 45 | 46 | static MObject ICSamples; 47 | static MObject ICTolerance; 48 | static MObject ICSpacingMin; 49 | static MObject ICSpacingMax; 50 | 51 | static MObject skyNode; 52 | static MObject skySize; 53 | static MObject skyResolution; 54 | static MObject skyTurbidity; 55 | static MObject skySamples; 56 | static MObject skyExposure; 57 | 58 | static MObject materialOverride; 59 | static MObject ambOverrideDist; 60 | 61 | static MObject bucketOrder; 62 | static MObject bucketSize; 63 | static MObject bucketReverse; 64 | 65 | static MObject exportPath; 66 | 67 | static MTypeId id; 68 | }; 69 | 70 | #endif /* SUNFLOW_GLOBALS_NODE_H */ 71 | -------------------------------------------------------------------------------- /src/org/sunflow/util/IntArray.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.util; 2 | 3 | public final class IntArray { 4 | private int[] array; 5 | private int size; 6 | 7 | public IntArray() { 8 | array = new int[10]; 9 | size = 0; 10 | } 11 | 12 | public IntArray(int capacity) { 13 | array = new int[capacity]; 14 | size = 0; 15 | } 16 | 17 | /** 18 | * Append an integer to the end of the array. 19 | * 20 | * @param i 21 | */ 22 | public final void add(int i) { 23 | if (size == array.length) { 24 | int[] oldArray = array; 25 | array = new int[(size * 3) / 2 + 1]; 26 | System.arraycopy(oldArray, 0, array, 0, size); 27 | } 28 | array[size] = i; 29 | size++; 30 | } 31 | 32 | /** 33 | * Write a value to the specified index. Assumes the array is already big 34 | * enough. 35 | * 36 | * @param index 37 | * @param value 38 | */ 39 | public final void set(int index, int value) { 40 | array[index] = value; 41 | } 42 | 43 | /** 44 | * Read value from the array. 45 | * 46 | * @param index index into the array 47 | * @return value at the specified index 48 | */ 49 | public final int get(int index) { 50 | return array[index]; 51 | } 52 | 53 | /** 54 | * Returns the number of elements added to the array. 55 | * 56 | * @return current size of the array 57 | */ 58 | public final int getSize() { 59 | return size; 60 | } 61 | 62 | /** 63 | * Return a copy of the array, trimmed to fit the size of its contents 64 | * exactly. 65 | * 66 | * @return a new array of exactly the right length 67 | */ 68 | public final int[] trim() { 69 | if (size < array.length) { 70 | int[] oldArray = array; 71 | array = new int[size]; 72 | System.arraycopy(oldArray, 0, array, 0, size); 73 | } 74 | return array; 75 | } 76 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/sunflowExport.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sunflowExport", "sunflowExport.vcproj", "{CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Maya_7.0_Debug|Win32 = Maya_7.0_Debug|Win32 9 | Maya_7.0_Release|Win32 = Maya_7.0_Release|Win32 10 | Maya_8.0_Debug|Win32 = Maya_8.0_Debug|Win32 11 | Maya_8.0_Release|Win32 = Maya_8.0_Release|Win32 12 | Maya_8.5_Debug|Win32 = Maya_8.5_Debug|Win32 13 | Maya_8.5_Release|Win32 = Maya_8.5_Release|Win32 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_7.0_Debug|Win32.ActiveCfg = Maya_7.0_Debug|Win32 17 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_7.0_Debug|Win32.Build.0 = Maya_7.0_Debug|Win32 18 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_7.0_Release|Win32.ActiveCfg = Maya_7.0_Release|Win32 19 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_7.0_Release|Win32.Build.0 = Maya_7.0_Release|Win32 20 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.0_Debug|Win32.ActiveCfg = Maya_8.0_Debug|Win32 21 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.0_Debug|Win32.Build.0 = Maya_8.0_Debug|Win32 22 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.0_Release|Win32.ActiveCfg = Maya_8.0_Release|Win32 23 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.0_Release|Win32.Build.0 = Maya_8.0_Release|Win32 24 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.5_Debug|Win32.ActiveCfg = Maya_8.5_Release|Win32 25 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.5_Debug|Win32.Build.0 = Maya_8.5_Release|Win32 26 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.5_Release|Win32.ActiveCfg = Maya_8.5_Release|Win32 27 | {CD4CF0BD-AE53-48D8-9152-B34A2E397FC6}.Maya_8.5_Release|Win32.Build.0 = Maya_8.5_Release|Win32 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/sunflowUtils.mel: -------------------------------------------------------------------------------- 1 | global proc string sunflowGetPrefix() { 2 | return "sunflow"; 3 | } 4 | 5 | global proc string sunflowCheckGlobals(){ 6 | string $sel[]= `ls -sl`; 7 | string $globalsNode; 8 | string $globalsNodes[] = stringArrayRemove({""},`lsType sunflowGlobalsNode`); 9 | if(size($globalsNodes)){ 10 | $globalsNode = $globalsNodes[0]; 11 | string $unusedNodes[] = stringArrayRemove({$globalsNode},$globalsNodes); 12 | if(size($unusedNodes)) 13 | delete $unusedNodes; 14 | }else{ 15 | $globalsNode = `createNode sunflowGlobalsNode`; 16 | } 17 | string $projPath = `workspace -q -rd`; 18 | string $tmpPath = $projPath+"sunflowScenes/"; 19 | string $exportPath = `getAttr ($globalsNode+".exportPath")`; 20 | if(!`filetest -d $tmpPath`) 21 | sysFile -makeDir $tmpPath; 22 | if($exportPath=="") 23 | setAttr -type "string" ($globalsNode+".exportPath") $tmpPath; 24 | select $sel; 25 | return $globalsNode; 26 | } 27 | 28 | global proc string[] sunflowCreateSunSky(){ 29 | string $sunLightShape = `createNode directionalLight`; 30 | addAttr -ln "isSunSkyLight" -at bool $sunLightShape; 31 | setAttr ($sunLightShape+".isSunSkyLight") true; 32 | string $sunLight[] = `pickWalk -d up`; 33 | $sunLight[0] = `rename $sunLight[0] "sunflowSun#"`; 34 | setAttr ($sunLight[0]+".rotateX") -45; 35 | setAttr ($sunLight[0]+".rotateY") 45; 36 | setAttr ($sunLight[0]+".rotateZ") 0; 37 | string $sky = sunflowCreateSky($sunLight[0]); 38 | return {$sunLight[0], $sky}; 39 | } 40 | 41 | global proc string sunflowCreateSky(string $light){ 42 | string $lightShape[] = `listRelatives -s $light`; 43 | string $skyShape = `createNode sunflowSkyNode`; 44 | string $sky[] = `pickWalk -d up`; 45 | $sky[0] = `rename $sky[0] "sunflowSky#"`; 46 | string $tmp[] = `pickWalk -d down`;$skyShape=$tmp[0]; 47 | connectAttr -f ($lightShape[0]+".message") ($skyShape+".sunLight"); 48 | setAttr ($skyShape+".template") 1; 49 | setAttr ($skyShape+".size") 0.5; 50 | pointConstraint -offset 0 0 0 -weight 1 $light $sky[0]; 51 | return $skyShape; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/org/sunflow/util/FloatArray.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.util; 2 | 3 | public final class FloatArray { 4 | private float[] array; 5 | private int size; 6 | 7 | public FloatArray() { 8 | array = new float[10]; 9 | size = 0; 10 | } 11 | 12 | public FloatArray(int capacity) { 13 | array = new float[capacity]; 14 | size = 0; 15 | } 16 | 17 | /** 18 | * Append a float to the end of the array. 19 | * 20 | * @param f 21 | */ 22 | public final void add(float f) { 23 | if (size == array.length) { 24 | float[] oldArray = array; 25 | array = new float[(size * 3) / 2 + 1]; 26 | System.arraycopy(oldArray, 0, array, 0, size); 27 | } 28 | array[size] = f; 29 | size++; 30 | } 31 | 32 | /** 33 | * Write a value to the specified index. Assumes the array is already big 34 | * enough. 35 | * 36 | * @param index 37 | * @param value 38 | */ 39 | public final void set(int index, float value) { 40 | array[index] = value; 41 | } 42 | 43 | /** 44 | * Read value from the array. 45 | * 46 | * @param index index into the array 47 | * @return value at the specified index 48 | */ 49 | public final float get(int index) { 50 | return array[index]; 51 | } 52 | 53 | /** 54 | * Returns the number of elements added to the array. 55 | * 56 | * @return current size of the array 57 | */ 58 | public final int getSize() { 59 | return size; 60 | } 61 | 62 | /** 63 | * Return a copy of the array, trimmed to fit the size of its contents 64 | * exactly. 65 | * 66 | * @return a new array of exactly the right length 67 | */ 68 | public final float[] trim() { 69 | if (size < array.length) { 70 | float[] oldArray = array; 71 | array = new float[size]; 72 | System.arraycopy(oldArray, 0, array, 0, size); 73 | } 74 | return array; 75 | } 76 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/ShadingCache.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | 5 | public class ShadingCache { 6 | private Sample first; 7 | private int depth; 8 | // stats 9 | long hits; 10 | long misses; 11 | long sumDepth; 12 | long numCaches; 13 | 14 | private static class Sample { 15 | Instance i; 16 | Shader s; 17 | float nx, ny, nz; 18 | float dx, dy, dz; 19 | Color c; 20 | Sample next; // linked list 21 | } 22 | 23 | public ShadingCache() { 24 | reset(); 25 | hits = 0; 26 | misses = 0; 27 | } 28 | 29 | public void reset() { 30 | sumDepth += depth; 31 | if (depth > 0) 32 | numCaches++; 33 | first = null; 34 | depth = 0; 35 | } 36 | 37 | public Color lookup(ShadingState state, Shader shader) { 38 | if (state.getNormal() == null) 39 | return null; 40 | // search further 41 | for (Sample s = first; s != null; s = s.next) { 42 | if (s.i != state.getInstance()) 43 | continue; 44 | if (s.s != shader) 45 | continue; 46 | if (state.getRay().dot(s.dx, s.dy, s.dz) < 0.999f) 47 | continue; 48 | if (state.getNormal().dot(s.nx, s.ny, s.nz) < 0.99f) 49 | continue; 50 | // we have a match 51 | hits++; 52 | return s.c; 53 | } 54 | misses++; 55 | return null; 56 | } 57 | 58 | public void add(ShadingState state, Shader shader, Color c) { 59 | if (state.getNormal() == null) 60 | return; 61 | depth++; 62 | Sample s = new Sample(); 63 | s.i = state.getInstance(); 64 | s.s = shader; 65 | s.c = c; 66 | s.dx = state.getRay().dx; 67 | s.dy = state.getRay().dy; 68 | s.dz = state.getRay().dz; 69 | s.nx = state.getNormal().x; 70 | s.ny = state.getNormal().y; 71 | s.nz = state.getNormal().z; 72 | s.next = first; 73 | first = s; 74 | } 75 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/gi/AmbientOcclusionGIEngine.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.gi; 2 | 3 | import org.sunflow.core.GIEngine; 4 | import org.sunflow.core.Options; 5 | import org.sunflow.core.Ray; 6 | import org.sunflow.core.Scene; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | import org.sunflow.math.Vector3; 11 | 12 | public class AmbientOcclusionGIEngine implements GIEngine { 13 | private Color bright; 14 | private Color dark; 15 | private int samples; 16 | private float maxDist; 17 | 18 | public Color getGlobalRadiance(ShadingState state) { 19 | return Color.BLACK; 20 | } 21 | 22 | public boolean init(Options options, Scene scene) { 23 | bright = options.getColor("gi.ambocc.bright", Color.WHITE); 24 | dark = options.getColor("gi.ambocc.dark", Color.BLACK); 25 | samples = options.getInt("gi.ambocc.samples", 32); 26 | maxDist = options.getFloat("gi.ambocc.maxdist", 0); 27 | maxDist = (maxDist <= 0) ? Float.POSITIVE_INFINITY : maxDist; 28 | return true; 29 | } 30 | 31 | public Color getIrradiance(ShadingState state, Color diffuseReflectance) { 32 | OrthoNormalBasis onb = state.getBasis(); 33 | Vector3 w = new Vector3(); 34 | Color result = Color.black(); 35 | for (int i = 0; i < samples; i++) { 36 | float xi = (float) state.getRandom(i, 0, samples); 37 | float xj = (float) state.getRandom(i, 1, samples); 38 | float phi = (float) (2 * Math.PI * xi); 39 | float cosPhi = (float) Math.cos(phi); 40 | float sinPhi = (float) Math.sin(phi); 41 | float sinTheta = (float) Math.sqrt(xj); 42 | float cosTheta = (float) Math.sqrt(1.0f - xj); 43 | w.x = cosPhi * sinTheta; 44 | w.y = sinPhi * sinTheta; 45 | w.z = cosTheta; 46 | onb.transform(w); 47 | Ray r = new Ray(state.getPoint(), w); 48 | r.setMax(maxDist); 49 | result.add(Color.blend(bright, dark, state.traceShadow(r))); 50 | } 51 | return result.mul((float) Math.PI / samples); 52 | } 53 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/mel/registerSunflowRenderer.mel: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Description: This procedure is called to register the 3 | // sunflow renderer with other renderers in Maya. 4 | /////////////////////////////////////////////////////////////////////////////// 5 | 6 | global proc registerSunflowRenderer() 7 | { 8 | renderer 9 | -rendererUIName "Sunflow" 10 | -renderProcedure "sunflowRender" 11 | -commandRenderProcedure "sunflowRender" 12 | -batchRenderProcedure "sunflowRender" 13 | -showRenderLogProcedure "" 14 | -batchRenderOptionsProcedure "" 15 | -cancelBatchRenderProcedure "sunflowCancelBatch" 16 | -showBatchRenderProcedure "" 17 | -showBatchRenderLogProcedure "" 18 | -iprRenderProcedure "" 19 | -startIprRenderProcedure "" 20 | -stopIprRenderProcedure "" 21 | -changeIprRegionProcedure "" 22 | -iprOptionsMenuLabel "" 23 | -iprOptionsSubMenuProcedure "" 24 | -renderDiagnosticsProcedure "" 25 | -renderRegionProcedure "" 26 | -textureBakingProcedure "" 27 | -polyPrelightProcedure "" 28 | -renderingEditorsSubMenuProcedure "" 29 | -addGlobalsNode "sunflowRenderGlobals" 30 | -logoImageName "sunflowRender.xpm" 31 | -logoCallbackProcedure "sunflowLogoCallback" 32 | sunflow; 33 | 34 | // 35 | //The first argument of the "-addGlobalsTab" flag is used in creating layout names 36 | //and should not be localized. 37 | // 38 | 39 | renderer 40 | -edit 41 | -addGlobalsTab "Common" 42 | "createMayaSoftwareCommonGlobalsTab" 43 | "updateMayaSoftwareCommonGlobalsTab" 44 | sunflow; 45 | renderer 46 | -edit 47 | -addGlobalsTab "Sunflow" 48 | "createSunflowGlobalsTab" 49 | "updateSunflowGlobalsTab" 50 | sunflow; 51 | } 52 | 53 | global proc sunflowLogoCallback(){ 54 | string $url = "http://sunflow.sourceforge.net/"; 55 | evalDeferred ("showHelp -absolute \""+$url+"\"" ); 56 | } 57 | 58 | global proc string renderSettingsTabLabel_melToUI (string $mel){ 59 | string $result = $mel; 60 | 61 | if($mel == "Sunflow"){ 62 | $result = "Sunflow"; 63 | } 64 | 65 | return $result; 66 | } 67 | -------------------------------------------------------------------------------- /src/org/sunflow/image/formats/GenericBitmap.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.formats; 2 | 3 | import java.io.IOException; 4 | 5 | import org.sunflow.PluginRegistry; 6 | import org.sunflow.image.Bitmap; 7 | import org.sunflow.image.BitmapWriter; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.system.FileUtils; 10 | import org.sunflow.system.UI; 11 | import org.sunflow.system.UI.Module; 12 | 13 | /** 14 | * This is a generic and inefficient bitmap format which may be used for 15 | * debugging purposes (dumping small images), when memory usage is not a 16 | * concern. 17 | */ 18 | public class GenericBitmap extends Bitmap { 19 | private int w, h; 20 | private Color[] color; 21 | private float[] alpha; 22 | 23 | public GenericBitmap(int w, int h) { 24 | this.w = w; 25 | this.h = h; 26 | color = new Color[w * h]; 27 | alpha = new float[w * h]; 28 | } 29 | 30 | @Override 31 | public int getWidth() { 32 | return w; 33 | } 34 | 35 | @Override 36 | public int getHeight() { 37 | return h; 38 | } 39 | 40 | @Override 41 | public Color readColor(int x, int y) { 42 | return color[x + y * w]; 43 | } 44 | 45 | @Override 46 | public float readAlpha(int x, int y) { 47 | return alpha[x + y * w]; 48 | } 49 | 50 | public void writePixel(int x, int y, Color c, float a) { 51 | color[x + y * w] = c; 52 | alpha[x + y * w] = a; 53 | } 54 | 55 | public void save(String filename) { 56 | String extension = FileUtils.getExtension(filename); 57 | BitmapWriter writer = PluginRegistry.bitmapWriterPlugins.createObject(extension); 58 | if (writer == null) { 59 | UI.printError(Module.IMG, "Unable to save file \"%s\" - unknown file format: %s", filename, extension); 60 | return; 61 | } 62 | try { 63 | writer.openFile(filename); 64 | writer.writeHeader(w, h, Math.max(w, h)); 65 | writer.writeTile(0, 0, w, h, color, alpha); 66 | writer.closeFile(); 67 | } catch (IOException e) { 68 | UI.printError(Module.IMG, "Unable to save file \"%s\" - %s", filename, e.getLocalizedMessage()); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/DiffuseShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Ray; 6 | import org.sunflow.core.Shader; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | import org.sunflow.math.Vector3; 11 | 12 | public class DiffuseShader implements Shader { 13 | private Color diff; 14 | 15 | public DiffuseShader() { 16 | diff = Color.WHITE; 17 | } 18 | 19 | public boolean update(ParameterList pl, SunflowAPI api) { 20 | diff = pl.getColor("diffuse", diff); 21 | return true; 22 | } 23 | 24 | public Color getDiffuse(ShadingState state) { 25 | return diff; 26 | } 27 | 28 | public Color getRadiance(ShadingState state) { 29 | // make sure we are on the right side of the material 30 | state.faceforward(); 31 | // setup lighting 32 | state.initLightSamples(); 33 | state.initCausticSamples(); 34 | return state.diffuse(getDiffuse(state)); 35 | } 36 | 37 | public void scatterPhoton(ShadingState state, Color power) { 38 | Color diffuse; 39 | // make sure we are on the right side of the material 40 | if (Vector3.dot(state.getNormal(), state.getRay().getDirection()) > 0.0) { 41 | state.getNormal().negate(); 42 | state.getGeoNormal().negate(); 43 | } 44 | diffuse = getDiffuse(state); 45 | state.storePhoton(state.getRay().getDirection(), power, diffuse); 46 | float avg = diffuse.getAverage(); 47 | double rnd = state.getRandom(0, 0, 1); 48 | if (rnd < avg) { 49 | // photon is scattered 50 | power.mul(diffuse).mul(1.0f / avg); 51 | OrthoNormalBasis onb = state.getBasis(); 52 | double u = 2 * Math.PI * rnd / avg; 53 | double v = state.getRandom(0, 1, 1); 54 | float s = (float) Math.sqrt(v); 55 | float s1 = (float) Math.sqrt(1.0 - v); 56 | Vector3 w = new Vector3((float) Math.cos(u) * s, (float) Math.sin(u) * s, s1); 57 | w = onb.transform(w, new Vector3()); 58 | state.traceDiffusePhoton(new Ray(state.getPoint(), w), power); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/InstanceList.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.math.BoundingBox; 5 | import org.sunflow.math.Matrix4; 6 | 7 | final class InstanceList implements PrimitiveList { 8 | private Instance[] instances; 9 | private Instance[] lights; 10 | 11 | InstanceList() { 12 | instances = new Instance[0]; 13 | clearLightSources(); 14 | } 15 | 16 | InstanceList(Instance[] instances) { 17 | this.instances = instances; 18 | clearLightSources(); 19 | } 20 | 21 | void addLightSourceInstances(Instance[] lights) { 22 | this.lights = lights; 23 | } 24 | 25 | void clearLightSources() { 26 | lights = new Instance[0]; 27 | } 28 | 29 | public final float getPrimitiveBound(int primID, int i) { 30 | if (primID < instances.length) 31 | return instances[primID].getBounds().getBound(i); 32 | else 33 | return lights[primID - instances.length].getBounds().getBound(i); 34 | } 35 | 36 | public final BoundingBox getWorldBounds(Matrix4 o2w) { 37 | BoundingBox bounds = new BoundingBox(); 38 | for (Instance i : instances) 39 | bounds.include(i.getBounds()); 40 | for (Instance i : lights) 41 | bounds.include(i.getBounds()); 42 | return bounds; 43 | } 44 | 45 | public final void intersectPrimitive(Ray r, int primID, IntersectionState state) { 46 | if (primID < instances.length) 47 | instances[primID].intersect(r, state); 48 | else 49 | lights[primID - instances.length].intersect(r, state); 50 | } 51 | 52 | public final int getNumPrimitives() { 53 | return instances.length + lights.length; 54 | } 55 | 56 | public final int getNumPrimitives(int primID) { 57 | return primID < instances.length ? instances[primID].getNumPrimitives() : lights[primID - instances.length].getNumPrimitives(); 58 | } 59 | 60 | public final void prepareShadingState(ShadingState state) { 61 | state.getInstance().prepareShadingState(state); 62 | } 63 | 64 | public boolean update(ParameterList pl, SunflowAPI api) { 65 | return true; 66 | } 67 | 68 | public PrimitiveList getBakingPrimitives() { 69 | return null; 70 | } 71 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowExportCmd.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_EXPORT_CMD_H 2 | #define SUNFLOW_EXPORT_CMD_H 3 | ///////////////////////////////////////////////////////////////////////////////////////// 4 | //Sunflow Export Command 5 | ///////////////////////////////////////////////////////////////////////////////////////// 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class sunflowExportCmd : public MPxCommand 16 | { 17 | public: 18 | sunflowExportCmd(){}; 19 | virtual ~sunflowExportCmd(){}; 20 | 21 | virtual MStatus doIt ( const MArgList& args ); 22 | 23 | static void* creator(); 24 | 25 | void getCustomAttribute(MFloatVector &colorAttribute, MString attribute, MFnDependencyNode &node); 26 | void getCustomAttribute(MString &texture, MFloatVector &colorAttribute, MString attribute, MFnDependencyNode &node); 27 | void getCustomAttribute(float &floatAttribute, MString attribute, MFnDependencyNode &node); 28 | void getCustomAttribute(int &intAttribute, MString attribute, MFnDependencyNode &node); 29 | void getCustomAttribute(MString &stringAttribute, MString attribute, MFnDependencyNode &node); 30 | void getCustomAttribute(bool &boolAttribute, MString attribute, MFnDependencyNode &node); 31 | 32 | bool isObjectVisible(const MDagPath& path); 33 | bool areObjectAndParentsVisible(const MDagPath& path); 34 | bool isCameraRenderable(const MDagPath& path); 35 | int getAttributeInt(const std::string& node, const std::string& attributeName, int defaultValue); 36 | float getAttributeFloat(const std::string& node, const std::string& attributeName, float defaultValue); 37 | bool getShaderFromEngine(const MObject& obj, MFnDependencyNode& node); 38 | bool getShaderFromGeometry(const MDagPath& path, MFnDependencyNode& node); 39 | void exportMesh(const MDagPath& path, std::ofstream& file); 40 | void exportSurface(const MDagPath& path, std::ofstream& file); 41 | void exportCamera(const MDagPath& path, std::ofstream& file); 42 | 43 | bool getBumpFromShader(MFnDependencyNode& node, MString &texturePath, float &depth, MObject &bumpObject); 44 | MObjectArray shaderList; 45 | bool findShaderInList(MString shader); 46 | 47 | MDagPath renderCamera; 48 | }; 49 | 50 | #endif /* SUNFLOW_EXPORT_CMD_H */ 51 | -------------------------------------------------------------------------------- /src/org/sunflow/core/gi/PathTracingGIEngine.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.gi; 2 | 3 | import org.sunflow.core.GIEngine; 4 | import org.sunflow.core.Options; 5 | import org.sunflow.core.Ray; 6 | import org.sunflow.core.Scene; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | import org.sunflow.math.Vector3; 11 | import org.sunflow.system.UI; 12 | import org.sunflow.system.UI.Module; 13 | 14 | public class PathTracingGIEngine implements GIEngine { 15 | private int samples; 16 | 17 | public boolean init(Options options, Scene scene) { 18 | samples = options.getInt("gi.path.samples", 16); 19 | samples = Math.max(0, samples); 20 | UI.printInfo(Module.LIGHT, "Path tracer settings:"); 21 | UI.printInfo(Module.LIGHT, " * Samples: %d", samples); 22 | return true; 23 | } 24 | 25 | public Color getIrradiance(ShadingState state, Color diffuseReflectance) { 26 | if (samples <= 0) 27 | return Color.BLACK; 28 | // compute new sample 29 | Color irr = Color.black(); 30 | OrthoNormalBasis onb = state.getBasis(); 31 | Vector3 w = new Vector3(); 32 | int n = state.getDiffuseDepth() == 0 ? samples : 1; 33 | for (int i = 0; i < n; i++) { 34 | float xi = (float) state.getRandom(i, 0, n); 35 | float xj = (float) state.getRandom(i, 1, n); 36 | float phi = (float) (xi * 2 * Math.PI); 37 | float cosPhi = (float) Math.cos(phi); 38 | float sinPhi = (float) Math.sin(phi); 39 | float sinTheta = (float) Math.sqrt(xj); 40 | float cosTheta = (float) Math.sqrt(1.0f - xj); 41 | w.x = cosPhi * sinTheta; 42 | w.y = sinPhi * sinTheta; 43 | w.z = cosTheta; 44 | onb.transform(w); 45 | ShadingState temp = state.traceFinalGather(new Ray(state.getPoint(), w), i); 46 | if (temp != null) { 47 | temp.getInstance().prepareShadingState(temp); 48 | if (temp.getShader() != null) 49 | irr.add(temp.getShader().getRadiance(temp)); 50 | } 51 | } 52 | irr.mul((float) Math.PI / n); 53 | return irr; 54 | } 55 | 56 | public Color getGlobalRadiance(ShadingState state) { 57 | return Color.BLACK; 58 | } 59 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/MirrorShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Ray; 6 | import org.sunflow.core.Shader; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.math.Vector3; 10 | 11 | public class MirrorShader implements Shader { 12 | private Color color; 13 | 14 | public MirrorShader() { 15 | color = Color.WHITE; 16 | } 17 | 18 | public boolean update(ParameterList pl, SunflowAPI api) { 19 | color = pl.getColor("color", color); 20 | return true; 21 | } 22 | 23 | public Color getRadiance(ShadingState state) { 24 | if (!state.includeSpecular()) 25 | return Color.BLACK; 26 | state.faceforward(); 27 | float cos = state.getCosND(); 28 | float dn = 2 * cos; 29 | Vector3 refDir = new Vector3(); 30 | refDir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x; 31 | refDir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y; 32 | refDir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; 33 | Ray refRay = new Ray(state.getPoint(), refDir); 34 | 35 | // compute Fresnel term 36 | cos = 1 - cos; 37 | float cos2 = cos * cos; 38 | float cos5 = cos2 * cos2 * cos; 39 | Color ret = Color.white(); 40 | ret.sub(color); 41 | ret.mul(cos5); 42 | ret.add(color); 43 | return ret.mul(state.traceReflection(refRay, 0)); 44 | } 45 | 46 | public void scatterPhoton(ShadingState state, Color power) { 47 | float avg = color.getAverage(); 48 | double rnd = state.getRandom(0, 0, 1); 49 | if (rnd >= avg) 50 | return; 51 | state.faceforward(); 52 | float cos = state.getCosND(); 53 | power.mul(color).mul(1.0f / avg); 54 | // photon is reflected 55 | float dn = 2 * cos; 56 | Vector3 dir = new Vector3(); 57 | dir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x; 58 | dir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y; 59 | dir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z; 60 | state.traceReflectionPhoton(new Ray(state.getPoint(), dir), power); 61 | } 62 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/light/PointLight.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.light; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.Instance; 5 | import org.sunflow.core.LightSample; 6 | import org.sunflow.core.LightSource; 7 | import org.sunflow.core.ParameterList; 8 | import org.sunflow.core.Ray; 9 | import org.sunflow.core.ShadingState; 10 | import org.sunflow.image.Color; 11 | import org.sunflow.math.Point3; 12 | import org.sunflow.math.Vector3; 13 | 14 | public class PointLight implements LightSource { 15 | private Point3 lightPoint; 16 | private Color power; 17 | 18 | public PointLight() { 19 | lightPoint = new Point3(0, 0, 0); 20 | power = Color.WHITE; 21 | } 22 | 23 | public boolean update(ParameterList pl, SunflowAPI api) { 24 | lightPoint = pl.getPoint("center", lightPoint); 25 | power = pl.getColor("power", power); 26 | return true; 27 | } 28 | 29 | public int getNumSamples() { 30 | return 1; 31 | } 32 | 33 | public void getSamples(ShadingState state) { 34 | Vector3 d = Point3.sub(lightPoint, state.getPoint(), new Vector3()); 35 | if (Vector3.dot(d, state.getNormal()) > 0 && Vector3.dot(d, state.getGeoNormal()) > 0) { 36 | LightSample dest = new LightSample(); 37 | // prepare shadow ray 38 | dest.setShadowRay(new Ray(state.getPoint(), lightPoint)); 39 | float scale = 1.0f / (float) (4 * Math.PI * lightPoint.distanceToSquared(state.getPoint())); 40 | dest.setRadiance(power, power); 41 | dest.getDiffuseRadiance().mul(scale); 42 | dest.getSpecularRadiance().mul(scale); 43 | dest.traceShadow(state); 44 | state.addSample(dest); 45 | } 46 | } 47 | 48 | public void getPhoton(double randX1, double randY1, double randX2, double randY2, Point3 p, Vector3 dir, Color power) { 49 | p.set(lightPoint); 50 | float phi = (float) (2 * Math.PI * randX1); 51 | float s = (float) Math.sqrt(randY1 * (1.0f - randY1)); 52 | dir.x = (float) Math.cos(phi) * s; 53 | dir.y = (float) Math.sin(phi) * s; 54 | dir.z = (float) (1 - 2 * randY1); 55 | power.set(this.power); 56 | } 57 | 58 | public float getPower() { 59 | return power.getLuminance(); 60 | } 61 | 62 | public Instance createInstance() { 63 | return null; 64 | } 65 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/SearchPath.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.LinkedList; 6 | 7 | import org.sunflow.system.UI.Module; 8 | 9 | public class SearchPath { 10 | private LinkedList searchPath; 11 | private String type; 12 | 13 | public SearchPath(String type) { 14 | this.type = type; 15 | searchPath = new LinkedList(); 16 | } 17 | 18 | public void resetSearchPath() { 19 | searchPath.clear(); 20 | } 21 | 22 | public void addSearchPath(String path) { 23 | File f = new File(path); 24 | if (f.exists() && f.isDirectory()) { 25 | try { 26 | path = f.getCanonicalPath(); 27 | for (String prefix : searchPath) 28 | if (prefix.equals(path)) 29 | return; 30 | UI.printInfo(Module.SYS, "Adding %s search path: \"%s\"", type, path); 31 | searchPath.add(path); 32 | } catch (IOException e) { 33 | UI.printError(Module.SYS, "Invalid %s search path specification: \"%s\" - %s", type, path, e.getMessage()); 34 | } 35 | } else 36 | UI.printError(Module.SYS, "Invalid %s search path specification: \"%s\" - invalid directory", type, path); 37 | } 38 | 39 | public String resolvePath(String filename) { 40 | // account for relative naming schemes from 3rd party softwares 41 | if (filename.startsWith("//")) 42 | filename = filename.substring(2); 43 | UI.printDetailed(Module.SYS, "Resolving %s path \"%s\" ...", type, filename); 44 | File f = new File(filename); 45 | if (!f.isAbsolute()) { 46 | for (String prefix : searchPath) { 47 | UI.printDetailed(Module.SYS, " * searching: \"%s\" ...", prefix); 48 | if (prefix.endsWith(File.separator) || filename.startsWith(File.separator)) 49 | f = new File(prefix + filename); 50 | else 51 | f = new File(prefix + File.separator + filename); 52 | if (f.exists()) { 53 | // suggested path exists - try it 54 | return f.getAbsolutePath(); 55 | } 56 | } 57 | } 58 | // file was not found in the search paths - return the filename itself 59 | return filename; 60 | } 61 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/shader/QuickGrayShader.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.shader; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.ParameterList; 5 | import org.sunflow.core.Ray; 6 | import org.sunflow.core.Shader; 7 | import org.sunflow.core.ShadingState; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.math.OrthoNormalBasis; 10 | import org.sunflow.math.Vector3; 11 | 12 | public class QuickGrayShader implements Shader { 13 | public QuickGrayShader() { 14 | } 15 | 16 | public boolean update(ParameterList pl, SunflowAPI api) { 17 | return true; 18 | } 19 | 20 | public Color getRadiance(ShadingState state) { 21 | if (state.getNormal() == null) { 22 | // if this shader has been applied to an infinite instance because 23 | // of shader overrides 24 | // run the default shader, otherwise, just shade black 25 | return state.getShader() != this ? state.getShader().getRadiance(state) : Color.BLACK; 26 | } 27 | // make sure we are on the right side of the material 28 | state.faceforward(); 29 | // setup lighting 30 | state.initLightSamples(); 31 | state.initCausticSamples(); 32 | return state.diffuse(Color.GRAY); 33 | } 34 | 35 | public void scatterPhoton(ShadingState state, Color power) { 36 | Color diffuse; 37 | // make sure we are on the right side of the material 38 | if (Vector3.dot(state.getNormal(), state.getRay().getDirection()) > 0.0) { 39 | state.getNormal().negate(); 40 | state.getGeoNormal().negate(); 41 | } 42 | diffuse = Color.GRAY; 43 | state.storePhoton(state.getRay().getDirection(), power, diffuse); 44 | float avg = diffuse.getAverage(); 45 | double rnd = state.getRandom(0, 0, 1); 46 | if (rnd < avg) { 47 | // photon is scattered 48 | power.mul(diffuse).mul(1.0f / avg); 49 | OrthoNormalBasis onb = state.getBasis(); 50 | double u = 2 * Math.PI * rnd / avg; 51 | double v = state.getRandom(0, 1, 1); 52 | float s = (float) Math.sqrt(v); 53 | float s1 = (float) Math.sqrt(1.0 - v); 54 | Vector3 w = new Vector3((float) Math.cos(u) * s, (float) Math.sin(u) * s, s1); 55 | w = onb.transform(w, new Vector3()); 56 | state.traceDiffusePhoton(new Ray(state.getPoint(), w), power); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/org/sunflow/image/writers/TGABitmapWriter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.writers; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | import org.sunflow.image.BitmapWriter; 9 | import org.sunflow.image.Color; 10 | import org.sunflow.image.ColorEncoder; 11 | 12 | public class TGABitmapWriter implements BitmapWriter { 13 | private String filename; 14 | private int width, height; 15 | private byte[] data; 16 | 17 | public void configure(String option, String value) { 18 | } 19 | 20 | public void openFile(String filename) throws IOException { 21 | this.filename = filename; 22 | } 23 | 24 | public void writeHeader(int width, int height, int tileSize) throws IOException, UnsupportedOperationException { 25 | this.width = width; 26 | this.height = height; 27 | data = new byte[width * height * 4]; // RGBA8 28 | } 29 | 30 | public void writeTile(int x, int y, int w, int h, Color[] color, float[] alpha) throws IOException { 31 | color = ColorEncoder.unlinearize(color); // gamma correction 32 | byte[] tileData = ColorEncoder.quantizeRGBA8(color, alpha); 33 | for (int j = 0, index = 0; j < h; j++) { 34 | int imageIndex = 4 * (x + (height - 1 - (y + j)) * width); 35 | for (int i = 0; i < w; i++, index += 4, imageIndex += 4) { 36 | // swap bytes around so buffer is in native BGRA order 37 | data[imageIndex + 0] = tileData[index + 2]; 38 | data[imageIndex + 1] = tileData[index + 1]; 39 | data[imageIndex + 2] = tileData[index + 0]; 40 | data[imageIndex + 3] = tileData[index + 3]; 41 | } 42 | } 43 | } 44 | 45 | public void closeFile() throws IOException { 46 | // actually write the file from here 47 | OutputStream f = new BufferedOutputStream(new FileOutputStream(filename)); 48 | // no id, no colormap, uncompressed 32bpp RGBA 49 | byte[] tgaHeader = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 50 | f.write(tgaHeader); 51 | // then the size info 52 | f.write(width & 0xFF); 53 | f.write((width >> 8) & 0xFF); 54 | f.write(height & 0xFF); 55 | f.write((height >> 8) & 0xFF); 56 | // bitsperpixel and filler 57 | f.write(32); 58 | f.write(0); 59 | f.write(data); // write image data bytes (already in BGRA order) 60 | f.close(); 61 | } 62 | } -------------------------------------------------------------------------------- /examples/cornell_box_jensen.sc: -------------------------------------------------------------------------------- 1 | image { 2 | resolution 800 600 3 | aa 0 2 4 | filter gaussian 5 | } 6 | 7 | trace-depths { 8 | diff 4 9 | refl 3 10 | refr 2 11 | } 12 | 13 | photons { 14 | caustics 1000000 kd 100 0.5 15 | } 16 | 17 | 18 | % uncomment this block and comment the following GI block to switch gi engines 19 | /* 20 | gi { 21 | type irr-cache 22 | samples 512 23 | tolerance 0.01 24 | spacing 0.05 5.0 25 | % comment the following line to use path tracing for secondary bounces 26 | global 1000000 grid 100 0.75 27 | } 28 | */ 29 | 30 | gi { 31 | type igi 32 | samples 64 % number of virtual photons per set 33 | sets 1 % number of sets (increase this to translate shadow boundaries into noise) 34 | b 0.00003 % bias - decrease this values until bright spots dissapear 35 | bias-samples 0 % set this >0 to make the algorithm unbiased 36 | } 37 | 38 | shader { 39 | name debug_caustics 40 | type view-caustics 41 | } 42 | 43 | shader { 44 | name debug_globals 45 | type view-global 46 | } 47 | 48 | shader { 49 | name debug_gi 50 | type view-irradiance 51 | } 52 | 53 | %% use these to view the effect of the individual gi components 54 | % override debug_caustics false 55 | % override debug_globals false 56 | % override debug_gi false 57 | 58 | 59 | camera { 60 | type pinhole 61 | eye 0 -205 50 62 | target 0 0 50 63 | up 0 0 1 64 | fov 45 65 | aspect 1.333333 66 | } 67 | 68 | shader { 69 | name Grey 70 | type diffuse 71 | diff 0.7 0.7 0.7 72 | } 73 | 74 | shader { 75 | name Blue 76 | type diffuse 77 | diff 0.25 0.25 0.8 78 | } 79 | 80 | shader { 81 | name Red 82 | type diffuse 83 | diff 0.8 0.25 0.25 84 | } 85 | 86 | shader { 87 | name Mirror 88 | type mirror 89 | refl 0.7 0.7 0.7 90 | } 91 | 92 | shader { 93 | name Glass 94 | type glass 95 | eta 1.6 96 | color 1 1 1 97 | } 98 | 99 | light { 100 | type cornellbox 101 | corner0 -60 -60 0 102 | corner1 60 60 100 103 | left 0.80 0.25 0.25 104 | right 0.25 0.25 0.80 105 | top 0.70 0.70 0.70 106 | bottom 0.70 0.70 0.70 107 | back 0.70 0.70 0.70 108 | emit 15 15 15 109 | samples 32 110 | } 111 | 112 | object { 113 | shader Mirror 114 | type sphere 115 | c -30 30 20 116 | r 20 117 | } 118 | 119 | object { 120 | shader Glass 121 | type sphere 122 | c 28 2 20 123 | r 20 124 | } 125 | -------------------------------------------------------------------------------- /src/org/sunflow/image/IrregularSpectralCurve.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image; 2 | 3 | /** 4 | * This class allows spectral curves to be defined from irregularly sampled 5 | * data. Note that the wavelength array is assumed to be sorted low to high. Any 6 | * values beyond the defined range will simply be extended to infinity from the 7 | * end points. Points inside the valid range will be linearly interpolated 8 | * between the two nearest samples. No explicit error checking is performed, but 9 | * this class will run into {@link ArrayIndexOutOfBoundsException}s if the 10 | * array lengths don't match. 11 | */ 12 | public class IrregularSpectralCurve extends SpectralCurve { 13 | private final float[] wavelengths; 14 | private final float[] amplitudes; 15 | 16 | /** 17 | * Define an irregular spectral curve from the provided (sorted) wavelengths 18 | * and amplitude data. The wavelength array is assumed to contain values in 19 | * nanometers. Array lengths must match. 20 | * 21 | * @param wavelengths sampled wavelengths in nm 22 | * @param amplitudes amplitude of the curve at the sampled points 23 | */ 24 | public IrregularSpectralCurve(float[] wavelengths, float[] amplitudes) { 25 | this.wavelengths = wavelengths; 26 | this.amplitudes = amplitudes; 27 | if (wavelengths.length != amplitudes.length) 28 | throw new RuntimeException(String.format("Error creating irregular spectral curve: %d wavelengths and %d amplitudes", wavelengths.length, amplitudes.length)); 29 | for (int i = 1; i < wavelengths.length; i++) 30 | if (wavelengths[i - 1] >= wavelengths[i]) 31 | throw new RuntimeException(String.format("Error creating irregular spectral curve: values are not sorted - error at index %d", i)); 32 | } 33 | 34 | @Override 35 | public float sample(float lambda) { 36 | if (wavelengths.length == 0) 37 | return 0; // no data 38 | if (wavelengths.length == 1 || lambda <= wavelengths[0]) 39 | return amplitudes[0]; 40 | if (lambda >= wavelengths[wavelengths.length - 1]) 41 | return amplitudes[wavelengths.length - 1]; 42 | for (int i = 1; i < wavelengths.length; i++) { 43 | if (lambda < wavelengths[i]) { 44 | float dx = (lambda - wavelengths[i - 1]) / (wavelengths[i] - wavelengths[i - 1]); 45 | return (1 - dx) * amplitudes[i - 1] + dx * amplitudes[i]; 46 | } 47 | } 48 | return amplitudes[wavelengths.length - 1]; 49 | } 50 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/display/FileDisplay.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.display; 2 | 3 | import java.io.IOException; 4 | 5 | import org.sunflow.PluginRegistry; 6 | import org.sunflow.core.Display; 7 | import org.sunflow.image.BitmapWriter; 8 | import org.sunflow.image.Color; 9 | import org.sunflow.system.FileUtils; 10 | import org.sunflow.system.UI; 11 | import org.sunflow.system.UI.Module; 12 | 13 | public class FileDisplay implements Display { 14 | private BitmapWriter writer; 15 | private String filename; 16 | 17 | public FileDisplay(boolean saveImage) { 18 | this(saveImage ? "output.png" : ".none"); 19 | } 20 | 21 | public FileDisplay(String filename) { 22 | this.filename = filename == null ? "output.png" : filename; 23 | String extension = FileUtils.getExtension(filename); 24 | writer = PluginRegistry.bitmapWriterPlugins.createObject(extension); 25 | } 26 | 27 | public void imageBegin(int w, int h, int bucketSize) { 28 | if (writer == null) 29 | return; 30 | try { 31 | writer.openFile(filename); 32 | writer.writeHeader(w, h, bucketSize); 33 | } catch (IOException e) { 34 | UI.printError(Module.IMG, "I/O error occured while preparing image for display: %s", e.getMessage()); 35 | } 36 | } 37 | 38 | public void imagePrepare(int x, int y, int w, int h, int id) { 39 | // does nothing for files 40 | } 41 | 42 | public void imageUpdate(int x, int y, int w, int h, Color[] data, float[] alpha) { 43 | if (writer == null) 44 | return; 45 | try { 46 | writer.writeTile(x, y, w, h, data, alpha); 47 | } catch (IOException e) { 48 | UI.printError(Module.IMG, "I/O error occured while writing image tile [(%d,%d) %dx%d] image for display: %s", x, y, w, h, e.getMessage()); 49 | } 50 | } 51 | 52 | public void imageFill(int x, int y, int w, int h, Color c, float alpha) { 53 | if (writer == null) 54 | return; 55 | Color[] colorTile = new Color[w * h]; 56 | float[] alphaTile = new float[w * h]; 57 | for (int i = 0; i < colorTile.length; i++) { 58 | colorTile[i] = c; 59 | alphaTile[i] = alpha; 60 | } 61 | imageUpdate(x, y, w, h, colorTile, alphaTile); 62 | } 63 | 64 | public void imageEnd() { 65 | if (writer == null) 66 | return; 67 | try { 68 | writer.closeFile(); 69 | } catch (IOException e) { 70 | UI.printError(Module.IMG, "I/O error occured while closing the display: %s", e.getMessage()); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/org/sunflow/core/parser/RA3Parser.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.parser; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.nio.ByteOrder; 8 | import java.nio.FloatBuffer; 9 | import java.nio.IntBuffer; 10 | import java.nio.MappedByteBuffer; 11 | import java.nio.channels.FileChannel; 12 | 13 | import org.sunflow.SunflowAPIInterface; 14 | import org.sunflow.core.SceneParser; 15 | import org.sunflow.system.UI; 16 | import org.sunflow.system.UI.Module; 17 | 18 | public class RA3Parser implements SceneParser { 19 | public boolean parse(String filename, SunflowAPIInterface api) { 20 | try { 21 | UI.printInfo(Module.USER, "RA3 - Reading geometry: \"%s\" ...", filename); 22 | File file = new File(filename); 23 | FileInputStream stream = new FileInputStream(filename); 24 | MappedByteBuffer map = stream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()); 25 | map.order(ByteOrder.LITTLE_ENDIAN); 26 | IntBuffer ints = map.asIntBuffer(); 27 | FloatBuffer buffer = map.asFloatBuffer(); 28 | int numVerts = ints.get(0); 29 | int numTris = ints.get(1); 30 | UI.printInfo(Module.USER, "RA3 - * Reading %d vertices ...", numVerts); 31 | float[] verts = new float[3 * numVerts]; 32 | for (int i = 0; i < verts.length; i++) 33 | verts[i] = buffer.get(2 + i); 34 | UI.printInfo(Module.USER, "RA3 - * Reading %d triangles ...", numTris); 35 | int[] tris = new int[3 * numTris]; 36 | for (int i = 0; i < tris.length; i++) 37 | tris[i] = ints.get(2 + verts.length + i); 38 | stream.close(); 39 | UI.printInfo(Module.USER, "RA3 - * Creating mesh ..."); 40 | 41 | // create geometry 42 | api.parameter("triangles", tris); 43 | api.parameter("points", "point", "vertex", verts); 44 | api.geometry(filename, "triangle_mesh"); 45 | 46 | // create default shader (this will simply error out if the shader 47 | // already exists) 48 | api.shader("ra3shader", "simple"); 49 | // create instance 50 | api.parameter("shaders", "ra3shader"); 51 | api.instance(filename + ".instance", filename); 52 | } catch (FileNotFoundException e) { 53 | e.printStackTrace(); 54 | return false; 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | return false; 58 | } 59 | return true; 60 | } 61 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowShaderNode.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_SHADER_NODE_H 2 | #define SUNFLOW_SHADER_NODE_H 3 | 4 | #include 5 | #include 6 | 7 | // 8 | // DESCRIPTION: 9 | /////////////////////////////////////////////////////// 10 | class sunflowShaderNode : public MPxNode 11 | { 12 | public: 13 | sunflowShaderNode(); 14 | virtual ~sunflowShaderNode(); 15 | 16 | virtual MStatus compute( const MPlug&, MDataBlock& ); 17 | virtual void postConstructor(); 18 | 19 | MColor getDiffuseComponent(MDataBlock& block); 20 | MColor getPhongComponent(MFloatVector &specular, float power, MDataBlock& block); 21 | 22 | static void * creator(); 23 | static MStatus initialize(); 24 | static MTypeId id; 25 | 26 | private: 27 | static MObject aShader; 28 | //PHONG SHADER 29 | static MObject phong_specular; 30 | static MObject phong_power; 31 | static MObject phong_samples; 32 | //AMB_OCC SHADER 33 | static MObject amb_dark; 34 | static MObject amb_samples; 35 | static MObject amb_maxdist; 36 | 37 | //GLASS SHADER 38 | static MObject glass_eta; 39 | static MObject glass_absorbtion_distance; 40 | static MObject glass_absorbtion_color; 41 | 42 | //SHINY SHADER 43 | static MObject shiny_shiny; 44 | 45 | //WARD SHADER 46 | static MObject ward_specular; 47 | static MObject ward_upower; 48 | static MObject ward_vpower; 49 | static MObject ward_samples; 50 | 51 | //UBER SHADER 52 | static MObject uber_diffuse_texture; 53 | static MObject uber_diffuse_blend; 54 | static MObject uber_specular; 55 | static MObject uber_specular_texture; 56 | static MObject uber_specular_blend; 57 | static MObject uber_gloss; 58 | static MObject uber_samples; 59 | 60 | static MObject aColor; 61 | static MObject aPointCamera; 62 | static MObject aNormalCamera; 63 | static MObject aLightDirection; 64 | static MObject aLightIntensity; 65 | static MObject aPower; 66 | static MObject aSpecularity; 67 | static MObject aLightAmbient; 68 | static MObject aLightDiffuse; 69 | static MObject aLightSpecular; 70 | static MObject aLightShadowFraction; 71 | static MObject aPreShadowIntensity; 72 | static MObject aLightBlindData; 73 | static MObject aLightData; 74 | 75 | static MObject aRayOrigin; 76 | static MObject aRayDirection; 77 | 78 | static MObject aObjectId; 79 | static MObject aRaySampler; 80 | static MObject aRayDepth; 81 | 82 | static MObject aReflectGain; 83 | 84 | static MObject aTriangleNormalCamera; 85 | 86 | static MObject aOutColor; 87 | 88 | }; 89 | 90 | #endif /* SUNFLOW_SHADER_NODE_H */ 91 | -------------------------------------------------------------------------------- /src/org/sunflow/core/PrimitiveList.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.math.BoundingBox; 4 | import org.sunflow.math.Matrix4; 5 | 6 | /** 7 | * This class represents an object made up of many primitives. 8 | */ 9 | public interface PrimitiveList extends RenderObject { 10 | /** 11 | * Compute a bounding box of this object in world space, using the specified 12 | * object-to-world transformation matrix. The bounds should be as exact as 13 | * possible, if they are difficult or expensive to compute exactly, you may 14 | * use {@link Matrix4#transform(BoundingBox)}. If the matrix is 15 | * null no transformation is needed, and object space is 16 | * equivalent to world space. 17 | * 18 | * @param o2w object to world transformation matrix 19 | * @return object bounding box in world space 20 | */ 21 | public BoundingBox getWorldBounds(Matrix4 o2w); 22 | 23 | /** 24 | * Returns the number of individual primtives in this aggregate object. 25 | * 26 | * @return number of primitives 27 | */ 28 | public int getNumPrimitives(); 29 | 30 | /** 31 | * Retrieve the bounding box component of a particular primitive in object 32 | * space. Even indexes get minimum values, while odd indexes get the maximum 33 | * values for each axis. 34 | * 35 | * @param primID primitive index 36 | * @param i bounding box side index 37 | * @return value of the request bound 38 | */ 39 | public float getPrimitiveBound(int primID, int i); 40 | 41 | /** 42 | * Intersect the specified primitive in local space. 43 | * 44 | * @param r ray in the object's local space 45 | * @param primID primitive index to intersect 46 | * @param state intersection state 47 | * @see Ray#setMax(float) 48 | * @see IntersectionState#setIntersection(int, float, float) 49 | */ 50 | public void intersectPrimitive(Ray r, int primID, IntersectionState state); 51 | 52 | /** 53 | * Prepare the specified {@link ShadingState} by setting all of its internal 54 | * parameters. 55 | * 56 | * @param state shading state to fill in 57 | */ 58 | public void prepareShadingState(ShadingState state); 59 | 60 | /** 61 | * Create a new {@link PrimitiveList} object suitable for baking lightmaps. 62 | * This means a set of primitives laid out in the unit square UV space. This 63 | * method is optional, objects which do not support it should simply return 64 | * null. 65 | * 66 | * @return a list of baking primitives 67 | */ 68 | public PrimitiveList getBakingPrimitives(); 69 | } -------------------------------------------------------------------------------- /src/org/sunflow/system/BenchmarkFramework.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.system; 2 | 3 | import org.sunflow.system.UI.Module; 4 | 5 | /** 6 | * This class provides a very simple framework for running a BenchmarkTest 7 | * kernel several times and time the results. 8 | */ 9 | public class BenchmarkFramework { 10 | private Timer[] timers; 11 | private int timeLimit; // time limit in seconds 12 | 13 | public BenchmarkFramework(int iterations, int timeLimit) { 14 | this.timeLimit = timeLimit; 15 | timers = new Timer[iterations]; 16 | } 17 | 18 | public void execute(BenchmarkTest test) { 19 | // clear previous results 20 | for (int i = 0; i < timers.length; i++) 21 | timers[i] = null; 22 | // loop for the specified number of iterations or until the time limit 23 | long startTime = System.nanoTime(); 24 | for (int i = 0; i < timers.length && ((System.nanoTime() - startTime) / 1000000000) < timeLimit; i++) { 25 | UI.printInfo(Module.BENCH, "Running iteration %d", (i + 1)); 26 | timers[i] = new Timer(); 27 | test.kernelBegin(); 28 | timers[i].start(); 29 | test.kernelMain(); 30 | timers[i].end(); 31 | test.kernelEnd(); 32 | } 33 | // report stats 34 | double avg = 0; 35 | double min = Double.POSITIVE_INFINITY; 36 | double max = Double.NEGATIVE_INFINITY; 37 | int n = 0; 38 | for (Timer t : timers) { 39 | if (t == null) 40 | break; 41 | double s = t.seconds(); 42 | min = Math.min(min, s); 43 | max = Math.max(max, s); 44 | avg += s; 45 | n++; 46 | } 47 | if (n == 0) 48 | return; 49 | avg /= n; 50 | double stdDev = 0; 51 | for (Timer t : timers) { 52 | if (t == null) 53 | break; 54 | double s = t.seconds(); 55 | stdDev += (s - avg) * (s - avg); 56 | } 57 | stdDev = Math.sqrt(stdDev / n); 58 | UI.printInfo(Module.BENCH, "Benchmark results:"); 59 | UI.printInfo(Module.BENCH, " * Iterations: %d", n); 60 | UI.printInfo(Module.BENCH, " * Average: %s", Timer.toString(avg)); 61 | UI.printInfo(Module.BENCH, " * Fastest: %s", Timer.toString(min)); 62 | UI.printInfo(Module.BENCH, " * Longest: %s", Timer.toString(max)); 63 | UI.printInfo(Module.BENCH, " * Deviation: %s", Timer.toString(stdDev)); 64 | for (int i = 0; i < timers.length && timers[i] != null; i++) 65 | UI.printDetailed(Module.BENCH, " * Iteration %d: %s", i + 1, timers[i]); 66 | } 67 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowBucketToRenderView.cpp: -------------------------------------------------------------------------------- 1 | #include "sunflowBucketToRenderView.h" 2 | #include 3 | 4 | DisplayPacket bucketToRenderView::getPacket(){ 5 | DisplayPacket p; 6 | p.type.sbits8[3] = fgetc( renderPipe ); 7 | p.type.sbits8[2] = fgetc( renderPipe ); 8 | p.type.sbits8[1] = fgetc( renderPipe ); 9 | p.type.sbits8[0] = fgetc( renderPipe ); 10 | for (int i = 0; i < 4; i++) { 11 | p.data[i].sbits8[3] = fgetc( renderPipe ); 12 | p.data[i].sbits8[2] = fgetc( renderPipe ); 13 | p.data[i].sbits8[1] = fgetc( renderPipe ); 14 | p.data[i].sbits8[0] = fgetc( renderPipe ); 15 | } 16 | //std::cout << "Recieved packet "< 1: 14 | ## on windows, more than one file is created when shared libraries are built 15 | local_env.InstallAs(os.path.join('plugin', version, 'sunflowExport.mll'), dll[0]) 16 | else: 17 | ## on *nix just one file is created 18 | local_env.InstallAs(os.path.join('plugin', version, 'sunflowExport.so'), dll) 19 | 20 | 21 | ## find source code automatically 22 | source_files = [] 23 | for f in glob.glob('src/*.cpp'): 24 | source_files += [ f ] 25 | 26 | 27 | if sys.platform == 'win32': 28 | ## windows XP x64 29 | sdk_base = "C:\\Program Files\\Microsoft Platform SDK for Windows Server 2003 R2\\" 30 | env64 = Environment( 31 | CCFLAGS = Split('/nologo /W3 /GR /EHsc /MT /O2 /Ox'), 32 | CPPDEFINES = Split('_CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE NDEBUG WIN32 _WINDOWS NT_PLUGIN REQUIRE_IOSTREAM Bits64_'), 33 | LINKFLAGS = Split('/nologo /incremental:no /MACHINE:AMD64 /export:initializePlugin /export:uninitializePlugin'), 34 | LIBS = Split('Foundation OpenMaya OpenMayaUI opengl32 shell32 bufferoverflowU')) 35 | 36 | env64.Append(CPPPATH = [ "%s\\Include\\" % (sdk_base) ]) 37 | env64.Append(CPPPATH = [ "%s\\INCLUDE\\crt\\" % (sdk_base) ]) 38 | env64.Append(LIBPATH = [ "%s\\Lib\\AMD64\\" % (sdk_base) ]) 39 | 40 | env64['ENV']['PATH'] = "%s\\Bin\\win64\\x86\\AMD64;" % (sdk_base) 41 | # env64['ENV']['INCLUDE'] = "%s\\Include;%s\\INCLUDE\\crt" % (sdk_base, sdk_base) 42 | # env64['ENV']['LIB'] = "%s\\Lib\\AMD64;" % (sdk_base) 43 | 44 | compile_maya(env64, "C:\\Program Files\\Autodesk\\Maya8.5\\" , "85_x64") 45 | compile_maya(env64, "C:\\Program Files\\Alias\\Maya8.0\\" , "80_x64") 46 | elif sys.platform == 'linux2': 47 | env = Environment( 48 | CCFLAGS = Split('-g -O2 -Wno-deprecated -Wall'), 49 | CPPDEFINES = Split('LINUX _BOOL REQUIRE_IOSTREAM'), 50 | CPPINCLUDE= Split('/usr/X11R6/include/'), 51 | LIBS = Split('OpenMaya OpenMayaUI GL'), 52 | LIBPATH = Split('/usr/X11R6/lib/')) 53 | compile_maya(env, "/usr/aw/maya7.0/", "70") 54 | compile_maya(env, "/usr/aw/maya8.0/", "80") 55 | compile_maya(env, "/usr/autodesk/maya8.5/", "85") 56 | else: 57 | print 'Unsupported platform: %s' % (sys.platform) 58 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/pluginMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "sunflowGlobalsNode.h" 5 | #include "sunflowExportCmd.h" 6 | #include "sunflowShaderNode.h" 7 | #include "sunflowSkyNode.h" 8 | #include "sunflowHelperNode.h" 9 | 10 | MStatus initializePlugin( MObject obj ) 11 | { 12 | MStatus status; 13 | MFnPlugin plugin( obj, "Alias", "3.0", "Any"); 14 | 15 | status = plugin.registerNode( "sunflowGlobalsNode", sunflowGlobalsNode::id, &sunflowGlobalsNode::creator, &sunflowGlobalsNode::initialize, MPxNode::kDependNode ); 16 | if (!status) { status.perror("registerNode: sunflowGlobalsNode"); return status;} 17 | 18 | status = plugin.registerCommand("sunflowExportCmd", sunflowExportCmd::creator ); 19 | if (!status) status.perror("registerCommand: sunflowExportCmd"); 20 | 21 | const MString UserClassify( "shader/surface" ); 22 | status = plugin.registerNode( "sunflowShader", sunflowShaderNode::id, sunflowShaderNode::creator, sunflowShaderNode::initialize, MPxNode::kDependNode, &UserClassify ); 23 | if (!status) status.perror("registerNode: sunflowShaderNode"); 24 | 25 | status = plugin.registerNode( "sunflowSkyNode", sunflowSkyNode::id, &sunflowSkyNode::creator, &sunflowSkyNode::initialize, MPxNode::kLocatorNode ); 26 | if (!status) status.perror("registerNode: sunflowSkyNode"); 27 | 28 | status = plugin.registerNode( "sunflowHelperNode", sunflowHelperNode::id, sunflowHelperNode::creator, sunflowHelperNode::initialize, MPxNode::kLocatorNode ); 29 | if (!status) status.perror("registerNode: sunflowHelperNode"); 30 | 31 | status = MGlobal::executeCommand("source sunflowStartup.mel"); 32 | status = plugin.registerUI("sunflowStartup", "sunflowShutdown"); 33 | if (!status) { status.perror("Can't register sunflowStartup and sunflowShutdown interface scripts"); return status;} 34 | return status; 35 | } 36 | 37 | MStatus uninitializePlugin( MObject obj) 38 | { 39 | MStatus status; 40 | MFnPlugin plugin( obj ); 41 | 42 | status = plugin.deregisterNode( sunflowGlobalsNode::id ); 43 | if (!status) { status.perror("deregisterNode: sunflowGlobalsNode"); return status;} 44 | 45 | status = plugin.deregisterCommand("sunflowExportCmd"); 46 | if (!status) status.perror("deregisterCommand: sunflowExportCmd"); 47 | 48 | status = plugin.deregisterNode( sunflowShaderNode::id ); 49 | if (!status) status.perror("deregisterNode: sunflowShaderNode"); 50 | 51 | status = plugin.deregisterNode( sunflowSkyNode::id ); 52 | if (!status) status.perror("deregisterNode: sunflowSkyNode"); 53 | 54 | status = plugin.deregisterNode( sunflowHelperNode::id ); 55 | if (!status) status.perror("deregisterNode: sunflowHelperNode"); 56 | 57 | //MGlobal::executeCommandOnIdle("unregisterSunflowRenderer"); 58 | return status; 59 | } 60 | -------------------------------------------------------------------------------- /src/org/sunflow/core/LightSource.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core; 2 | 3 | import org.sunflow.image.Color; 4 | import org.sunflow.math.Point3; 5 | import org.sunflow.math.Vector3; 6 | 7 | /** 8 | * This interface is used to represent any light emitting primitive. It permits 9 | * efficient sampling of direct illumination and photon shooting. 10 | */ 11 | public interface LightSource extends RenderObject { 12 | /** 13 | * Get the maximum number of samples that can be taken from this light 14 | * source. This is currently only used for statistics reporting. 15 | * 16 | * @return maximum number of samples to be taken from this light source 17 | */ 18 | public int getNumSamples(); 19 | 20 | /** 21 | * Samples the light source to compute direct illumination. Light samples 22 | * can be created using the {@link LightSample} class and added to the 23 | * current {@link ShadingState}. This method is responsible for the 24 | * shooting of shadow rays which allows for non-physical lights that don't 25 | * cast shadows. It is recommended that only a single shadow ray be shot if 26 | * {@link ShadingState#getDiffuseDepth()} is greater than 0. This avoids an 27 | * exponential number of shadow rays from being traced. 28 | * 29 | * @param state current state, including point to be shaded 30 | * @see LightSample 31 | */ 32 | public void getSamples(ShadingState state); 33 | 34 | /** 35 | * Gets a photon to emit from this light source by setting each of the 36 | * arguments. The two sampling parameters are points on the unit square that 37 | * can be used to sample a position and/or direction for the emitted photon. 38 | * 39 | * @param randX1 sampling parameter 40 | * @param randY1 sampling parameter 41 | * @param randX2 sampling parameter 42 | * @param randY2 sampling parameter 43 | * @param p position to shoot the photon from 44 | * @param dir direction to shoot the photon in 45 | * @param power power of the photon 46 | */ 47 | public void getPhoton(double randX1, double randY1, double randX2, double randY2, Point3 p, Vector3 dir, Color power); 48 | 49 | /** 50 | * Get the total power emitted by this light source. Lights that have 0 51 | * power will not emit any photons. 52 | * 53 | * @return light source power 54 | */ 55 | public float getPower(); 56 | 57 | /** 58 | * Create an instance which represents the geometry of this light source. 59 | * This instance will be created just before and removed immediately after 60 | * rendering. Non-area light sources can return null to 61 | * indicate that no geometry needs to be created. 62 | * 63 | * @return an instance describing the light source 64 | */ 65 | public Instance createInstance(); 66 | } -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/sunflowSkyNode.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNFLOW_SKY_NODE_H 2 | #define SUNFLOW_SKY_NODE_H 3 | 4 | #include "skylight.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define PI 3.14159265358979323846 24 | #define TWOPI 6.2831853071795864769 25 | 26 | #define DRAWMODE 0 27 | 28 | class sunflowSkyNode : public MPxLocatorNode 29 | { 30 | public: 31 | sunflowSkyNode(); 32 | virtual ~sunflowSkyNode(); 33 | 34 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 35 | 36 | virtual void draw( M3dView & view, const MDagPath & path, 37 | M3dView::DisplayStyle style, 38 | M3dView::DisplayStatus status ); 39 | 40 | virtual bool isBounded() const; 41 | virtual MBoundingBox boundingBox() const; 42 | 43 | static void * creator(); 44 | static MStatus initialize(); 45 | 46 | static MObject size; // The size of the dome 47 | static MObject resolution; 48 | 49 | SunSky skyDome; 50 | void drawTriDome(M3dView & view, M3dView::DisplayStatus status); 51 | MStatus subdivide(M3dView & view, MVector v1, MVector v2, MVector v3, int depth, int drawMode); 52 | void drawTriangle(M3dView & view, MVector v1, MVector v2, MVector v3, int drawMode); 53 | 54 | void drawQuadDome(M3dView & view, M3dView::DisplayStatus status); 55 | void quadDome(M3dView & view, int drawMode); 56 | 57 | public: 58 | static MTypeId id; 59 | protected: 60 | static MObject sunVectorX; 61 | static MObject sunVectorY; 62 | static MObject sunVectorZ; 63 | static MObject sunVector; 64 | static MObject turbidity; 65 | static MObject exposure; 66 | static MObject sunLight; 67 | static MObject domeAlpha; 68 | 69 | MVectorArray vData; 70 | MVectorArray tData; 71 | 72 | float radius; 73 | float domeOpacity; 74 | 75 | float m_radius; 76 | float m_multiplier; 77 | float m_triRes; 78 | float m_quadRes; 79 | float m_exposure; 80 | float m_alpha; 81 | 82 | //static MObject Gamma; 83 | //static MObject enableGround; 84 | //static MObject groundColorR; 85 | //static MObject groundColorG; 86 | //static MObject groundColorB; 87 | //static MObject groundColor; 88 | 89 | }; 90 | 91 | #endif /* SUNFLOW_SKY_NODE_H */ 92 | -------------------------------------------------------------------------------- /src/org/sunflow/image/writers/IGIBitmapWriter.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.image.writers; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | import org.sunflow.image.BitmapWriter; 9 | import org.sunflow.image.Color; 10 | import org.sunflow.image.XYZColor; 11 | 12 | /** 13 | * Writes images in Indigo's native XYZ format. 14 | * http://www2.indigorenderer.com/joomla/forum/viewtopic.php?p=11430 15 | */ 16 | public class IGIBitmapWriter implements BitmapWriter { 17 | private String filename; 18 | private int width, height; 19 | private float[] xyz; 20 | 21 | public void configure(String option, String value) { 22 | } 23 | 24 | public void openFile(String filename) throws IOException { 25 | this.filename = filename; 26 | } 27 | 28 | public void writeHeader(int width, int height, int tileSize) throws IOException, UnsupportedOperationException { 29 | this.width = width; 30 | this.height = height; 31 | xyz = new float[width * height * 3]; 32 | } 33 | 34 | public void writeTile(int x, int y, int w, int h, Color[] color, float[] alpha) throws IOException { 35 | for (int j = 0, index = 0, pixel = 3 * (x + y * width); j < h; j++, pixel += 3 * (width - w)) { 36 | for (int i = 0; i < w; i++, index++, pixel += 3) { 37 | XYZColor c = Color.NATIVE_SPACE.convertRGBtoXYZ(color[index]); 38 | xyz[pixel + 0] = c.getX(); 39 | xyz[pixel + 1] = c.getY(); 40 | xyz[pixel + 2] = c.getZ(); 41 | } 42 | } 43 | } 44 | 45 | public void closeFile() throws IOException { 46 | OutputStream stream = new BufferedOutputStream(new FileOutputStream(filename)); 47 | write32(stream, 66613373); // magic number 48 | write32(stream, 1); // version 49 | write32(stream, 0); // this should be a double - assume it won't be used 50 | write32(stream, 0); 51 | write32(stream, width); 52 | write32(stream, height); 53 | write32(stream, 1); // super sampling factor 54 | write32(stream, 0); // compression 55 | write32(stream, width * height * 12); // data size 56 | write32(stream, 0); // colorspace 57 | stream.write(new byte[5000]); 58 | for (float f : xyz) 59 | write32(stream, f); 60 | stream.close(); 61 | } 62 | 63 | private static final void write32(OutputStream stream, int i) throws IOException { 64 | stream.write(i & 0xFF); 65 | stream.write((i >> 8) & 0xFF); 66 | stream.write((i >> 16) & 0xFF); 67 | stream.write((i >> 24) & 0xFF); 68 | } 69 | 70 | private static final void write32(OutputStream stream, float f) throws IOException { 71 | write32(stream, Float.floatToIntBits(f)); 72 | } 73 | } -------------------------------------------------------------------------------- /examples/shader_examples/include/example_array.geo.sc: -------------------------------------------------------------------------------- 1 | %% common settings 2 | 3 | image { 4 | resolution 600 600 5 | aa 0 2 6 | filter mitchell 7 | } 8 | 9 | %% camera 10 | 11 | camera { 12 | type pinhole 13 | eye 3.27743673325 -9.07978439331 9.93055152893 14 | target 0 0 0 15 | up 0 0 1 16 | fov 40 17 | aspect 1 18 | } 19 | 20 | 21 | %% light sources 22 | 23 | light { 24 | type meshlight 25 | name "meshlight" 26 | emit 1 1 1 27 | radiance 16 28 | samples 32 29 | points 4 30 | -1.79750967026 -6.22097349167 5.70054674149 31 | -2.28231739998 -7.26064729691 4.06224298477 32 | -4.09493303299 -6.41541051865 4.06224298477 33 | -3.61012482643 -5.37573671341 5.70054721832 34 | triangles 2 35 | 0 1 2 36 | 0 2 3 37 | } 38 | 39 | light { 40 | type meshlight 41 | name "meshlight.001" 42 | emit 1 1 1 43 | radiance 15 44 | samples 32 45 | points 4 46 | -4.25819396973 -4.8784570694 5.70054674149 47 | -5.13696432114 -5.61583280563 4.06224298477 48 | -6.422539711 -4.08374404907 4.06224298477 49 | -5.54376888275 -3.34636831284 5.70054721832 50 | triangles 2 51 | 0 1 2 52 | 0 2 3 53 | } 54 | 55 | 56 | %% geometry 57 | 58 | object { 59 | shader ground 60 | type generic-mesh 61 | name "Plane" 62 | points 8 63 | 3.1 3.1 0 64 | 3.1 -3.1 0 65 | -3.1 -3.1 0 66 | -3.1 3.1 0 67 | -3.1 3.1 -0.61 68 | -3.1 -3.1 -0.61 69 | 3.1 -3.1 -0.61 70 | 3.1 3.1 -0.61 71 | triangles 12 72 | 0 3 2 73 | 0 2 1 74 | 2 3 4 75 | 2 4 5 76 | 3 0 7 77 | 3 7 4 78 | 0 1 6 79 | 0 6 7 80 | 1 2 5 81 | 1 5 6 82 | 5 4 7 83 | 5 7 6 84 | normals none 85 | uvs none 86 | } 87 | 88 | object { 89 | shader "shader01" 90 | type sphere 91 | c -2 2 0.7 92 | r 0.7 93 | } 94 | 95 | object { 96 | shader "shader02" 97 | type sphere 98 | c 0 2 0.7 99 | r 0.7 100 | } 101 | 102 | object { 103 | shader "shader03" 104 | type sphere 105 | c 2 2 0.7 106 | r 0.7 107 | } 108 | 109 | object { 110 | shader "shader04" 111 | type sphere 112 | c -2 0 0.7 113 | r 0.7 114 | } 115 | object { 116 | shader "shader05" 117 | type sphere 118 | c 0 0 0.7 119 | r 0.7 120 | } 121 | 122 | object { 123 | shader "shader06" 124 | type sphere 125 | c 2 0 0.7 126 | r 0.7 127 | } 128 | 129 | object { 130 | shader "shader07" 131 | type sphere 132 | c -2 -2 0.7 133 | r 0.7 134 | } 135 | 136 | object { 137 | shader "shader08" 138 | type sphere 139 | c 0 -2 0.7 140 | r 0.7 141 | } 142 | 143 | object { 144 | shader "shader09" 145 | type sphere 146 | c 2 -2 0.7 147 | r 0.7 148 | } 149 | -------------------------------------------------------------------------------- /exporters/maya/sunflowExporter/src/skylight.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////// 2 | // copyright : (C) 2006 by Mad Crew 3 | // Email : fredrik (at) madcrew (dot) se 4 | // Website : http://www.madcrew.se 5 | // Created : 2006-11-03 6 | //////////////////////////////////////////////////////////////////////////// 7 | 8 | //////////////////////////////////////////////////////////////////////////// 9 | // // 10 | // This program is free software; you can redistribute it and/or modify // 11 | // it under the terms of the GNU General Public License as published by // 12 | // the Free Software Foundation; either version 2 of the License, or // 13 | // (at your option) any later version. // 14 | // // 15 | //////////////////////////////////////////////////////////////////////////// 16 | 17 | 18 | #ifndef SUNSKY_H 19 | #define SUNSKY_H 20 | 21 | #include 22 | #include 23 | #include 24 | //============================================================================= 25 | // SunSky 26 | class SunSky 27 | { 28 | public: 29 | SunSky(); 30 | SunSky( MVector position, double size, double turbidity ); 31 | // [in] position - the location of the sun. 32 | // [in] angle - the number of degrees of arc occupied by the sun. 33 | // [in] turbidity- is the ratio of the optical thickness of the haze and 34 | // molecules to haze. Turbidity = (Tm + Th) / Tm. It is the fraction of 35 | // scattering due to haze as opposed to molecules. 2-6 are good for clear 36 | // days 37 | 38 | MColor getSkyColor(const MVector& direction ) const; 39 | // returns the color of the sky in a particular direction 40 | 41 | void setTurbidity( double t ) { turbidity = t; } 42 | void setPosition( MVector pos ) { position = pos; } 43 | void initialize(); 44 | MColor gammaCorrect( MColor& color, float gamma ); 45 | 46 | private: 47 | double perez_x[5], // coefficients for the perez functions 48 | perez_y[5], 49 | perez_Y[5]; 50 | double zenith_x, // chromaticity at the zenith 51 | zenith_y, 52 | zenith_Y; // in cd/m^2 53 | MVector position; // normalized MVector pointing to the sun 54 | double theta, phi; // spherical coordinates of the sun position 55 | double turbidity; // turbitity of the atmosphere 56 | 57 | 58 | SunSky( const SunSky& ); 59 | // copy constructor - unimplemented 60 | 61 | SunSky& operator=( const SunSky& ); 62 | // assigment - unimplemented 63 | 64 | double perezFunction(const double *coeff, double thetaV, double gamma, double Yz ) const; 65 | MColor toRGB(double x, double y, double Ys ) const; 66 | }; 67 | 68 | #endif /* SUNSKY_H */ 69 | -------------------------------------------------------------------------------- /src/org/sunflow/core/modifiers/PerlinModifier.java: -------------------------------------------------------------------------------- 1 | package org.sunflow.core.modifiers; 2 | 3 | import org.sunflow.SunflowAPI; 4 | import org.sunflow.core.Modifier; 5 | import org.sunflow.core.ParameterList; 6 | import org.sunflow.core.ShadingState; 7 | import org.sunflow.math.OrthoNormalBasis; 8 | import org.sunflow.math.PerlinScalar; 9 | import org.sunflow.math.Point3; 10 | import org.sunflow.math.Vector3; 11 | 12 | public class PerlinModifier implements Modifier { 13 | private int function = 0; 14 | private float scale = 50; 15 | private float size = 1; 16 | 17 | public boolean update(ParameterList pl, SunflowAPI api) { 18 | function = pl.getInt("function", function); 19 | size = pl.getFloat("size", size); 20 | scale = pl.getFloat("scale", scale); 21 | return true; 22 | } 23 | 24 | public void modify(ShadingState state) { 25 | Point3 p = state.transformWorldToObject(state.getPoint()); 26 | p.x *= size; 27 | p.y *= size; 28 | p.z *= size; 29 | Vector3 normal = state.transformNormalWorldToObject(state.getNormal()); 30 | double f0 = f(p.x, p.y, p.z); 31 | double fx = f(p.x + .0001, p.y, p.z); 32 | double fy = f(p.x, p.y + .0001, p.z); 33 | double fz = f(p.x, p.y, p.z + .0001); 34 | 35 | normal.x -= scale * (fx - f0) / .0001; 36 | normal.y -= scale * (fy - f0) / .0001; 37 | normal.z -= scale * (fz - f0) / .0001; 38 | normal.normalize(); 39 | 40 | state.getNormal().set(state.transformNormalObjectToWorld(normal)); 41 | state.getNormal().normalize(); 42 | state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal())); 43 | } 44 | 45 | double f(double x, double y, double z) { 46 | switch (function) { 47 | case 0: 48 | return .03 * noise(x, y, z, 8); 49 | case 1: 50 | return .01 * stripes(x + 2 * turbulence(x, y, z, 1), 1.6); 51 | default: 52 | return -.10 * turbulence(x, y, z, 1); 53 | } 54 | } 55 | 56 | private static final double stripes(double x, double f) { 57 | double t = .5 + .5 * Math.sin(f * 2 * Math.PI * x); 58 | return t * t - .5; 59 | } 60 | 61 | private static final double turbulence(double x, double y, double z, double freq) { 62 | double t = -.5; 63 | for (; freq <= 300 / 12; freq *= 2) 64 | t += Math.abs(noise(x, y, z, freq) / freq); 65 | return t; 66 | } 67 | 68 | private static final double noise(double x, double y, double z, double freq) { 69 | double x1, y1, z1; 70 | x1 = .707 * x - .707 * z; 71 | z1 = .707 * x + .707 * z; 72 | y1 = .707 * x1 + .707 * y; 73 | x1 = .707 * x1 - .707 * y; 74 | return PerlinScalar.snoise((float) (freq * x1 + 100), (float) (freq * y1), (float) (freq * z1)); 75 | } 76 | } --------------------------------------------------------------------------------