/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2016 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.create.tina.base.solidrender; import static org.jwildfire.base.mathlib.MathLib.EPSILON; import static org.jwildfire.base.mathlib.MathLib.fabs; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.create.tina.animate.FlameMorphService; import org.jwildfire.create.tina.edit.Assignable; import org.jwildfire.create.tina.render.filter.FilterKernelType; @SuppressWarnings("serial") public class SolidRenderSettings implements Assignable<SolidRenderSettings>, Serializable { private boolean solidRenderingEnabled = false; private boolean aoEnabled; private double aoIntensity; private double aoSearchRadius; private double aoBlurRadius; private int aoRadiusSamples; private int aoAzimuthSamples; private double aoFalloff; private double aoAffectDiffuse; private ShadowType shadowType; private double shadowmapBias; private int shadowmapSize; private double shadowSmoothRadius; private FilterKernelType postBokehFilterKernel; private double postBokehIntensity; private double postBokehBrightness; private double postBokehSize; private double postBokehActivation; private final List<MaterialSettings> materials = new ArrayList<>(); private final List<DistantLight> lights = new ArrayList<>(); public SolidRenderSettings() { setupDefaultPostBokehOptions(); setupDefaultAmbientShadowOptions(); setupDefaultHardShadowOptions(); } public void setupDefaults() { setupDefaultPostBokehOptions(); setupDefaultAmbientShadowOptions(); setupDefaultHardShadowOptions(); setupDefaultMaterials(); setupDefaultLights(); } public void setupDefaultLights() { lights.clear(); { DistantLight light = new DistantLight(); lights.add(light); light.setAltitude(60); light.setAzimuth(-30.0); light.setIntensity(0.8); light.setShadowIntensity(0.8); light.setRed(1.0); light.setGreen(1.0); light.setBlue(1.0); light.setCastShadows(true); } { DistantLight light = new DistantLight(); lights.add(light); light.setAltitude(55); light.setAzimuth(-15.0); light.setIntensity(0.5); light.setShadowIntensity(0.7); light.setRed(1.0); light.setGreen(1.0); light.setBlue(1.0); light.setCastShadows(false); } } public void setupDefaultMaterials() { materials.clear(); { MaterialSettings material = new MaterialSettings(); material.setAmbient(0.6); material.setDiffuse(0.4); material.setPhong(0.8); material.setPhongSize(12.0); material.setPhongRed(1.0); material.setPhongGreen(1.0); material.setPhongBlue(1.0); material.setReflMapIntensity(0.5); material.setReflMapFilename(null); materials.add(material); } { MaterialSettings material = new MaterialSettings(); material.setAmbient(0.8); material.setDiffuse(0.1); material.setPhong(0.6); material.setPhongSize(15.0); material.setPhongRed(1.0); material.setPhongGreen(1.0); material.setPhongBlue(1.0); material.setReflMapIntensity(0.5); material.setReflMapFilename(null); materials.add(material); } } private boolean isValidMaterialIdx(int idx) { return idx >= 0 && idx < materials.size(); } public MaterialSettings getInterpolatedMaterial(double materialIdx) { if (materialIdx < 0 || materials.isEmpty()) { return null; } int fromIdx = (int) materialIdx; if (fromIdx >= materials.size()) { return materials.get(materials.size() - 1); } int toIdx = fromIdx + 1; if (toIdx >= materials.size()) { toIdx = 0; } double scl = MathLib.frac(materialIdx); double EPS = 0.01; if (scl < EPS) { return isValidMaterialIdx(fromIdx) ? materials.get(fromIdx) : null; } else if (scl > 1.0 - EPS) { return isValidMaterialIdx(toIdx) ? materials.get(toIdx) : null; } else { return isValidMaterialIdx(fromIdx) && isValidMaterialIdx(toIdx) ? morphMaterial(materials.get(fromIdx), materials.get(toIdx), scl) : null; //return scl <= 0.5 ? isValidMaterialIdx(fromIdx) ? materials.get(fromIdx) : null : isValidMaterialIdx(toIdx) ? materials.get(toIdx) : null; } } private MaterialSettings morphMaterial(final MaterialSettings from, final MaterialSettings to, final double scl) { MaterialSettings morph = new MaterialSettings(); morph.setDiffuse(FlameMorphService.morphValue(from.getDiffuse(), to.getDiffuse(), scl)); morph.setAmbient(FlameMorphService.morphValue(from.getAmbient(), to.getAmbient(), scl)); morph.setPhong(FlameMorphService.morphValue(from.getPhong(), to.getPhong(), scl)); morph.setPhongSize(FlameMorphService.morphValue(from.getPhongSize(), to.getPhongSize(), scl)); morph.setPhongRed(FlameMorphService.morphValue(from.getPhongRed(), to.getPhongRed(), scl)); morph.setPhongGreen(FlameMorphService.morphValue(from.getPhongGreen(), to.getPhongGreen(), scl)); morph.setPhongBlue(FlameMorphService.morphValue(from.getPhongBlue(), to.getPhongBlue(), scl)); morph.setDiffuse(FlameMorphService.morphValue(from.getReflMapIntensity(), to.getReflMapIntensity(), scl)); morph.setReflMapIntensity(FlameMorphService.morphValue(from.getReflMapIntensity(), to.getReflMapIntensity(), scl)); morph.setLightDiffFunc(new LightDiffFunc() { @Override public double evaluate(double pCosa) { double d1 = from.getLightDiffFunc().evaluate(pCosa); double d2 = to.getLightDiffFunc().evaluate(pCosa); return FlameMorphService.morphValue(d1, d2, scl); } }); return morph; } public List<DistantLight> getLights() { return lights; } public boolean isAoEnabled() { return aoEnabled; } public void setAoEnabled(boolean aoEnabled) { this.aoEnabled = aoEnabled; } public double getAoIntensity() { return aoIntensity; } public void setAoIntensity(double aoIntensity) { this.aoIntensity = aoIntensity; } public boolean isSolidRenderingEnabled() { return solidRenderingEnabled; } public void setSolidRenderingEnabled(boolean solidRenderingEnabled) { this.solidRenderingEnabled = solidRenderingEnabled; } @Override public void assign(SolidRenderSettings pSrc) { solidRenderingEnabled = pSrc.solidRenderingEnabled; aoEnabled = pSrc.aoEnabled; aoIntensity = pSrc.aoIntensity; aoSearchRadius = pSrc.aoSearchRadius; aoBlurRadius = pSrc.aoBlurRadius; aoRadiusSamples = pSrc.aoRadiusSamples; aoAzimuthSamples = pSrc.aoAzimuthSamples; aoFalloff = pSrc.aoFalloff; aoAffectDiffuse = pSrc.aoAffectDiffuse; shadowType = pSrc.shadowType; shadowmapBias = pSrc.shadowmapBias; shadowmapSize = pSrc.shadowmapSize; shadowSmoothRadius = pSrc.shadowSmoothRadius; postBokehFilterKernel = pSrc.postBokehFilterKernel; postBokehIntensity = pSrc.postBokehIntensity; postBokehBrightness = pSrc.postBokehBrightness; postBokehSize = pSrc.postBokehSize; postBokehActivation = pSrc.postBokehActivation; materials.clear(); for (MaterialSettings src : pSrc.getMaterials()) { MaterialSettings dst = new MaterialSettings(); dst.assign(src); materials.add(dst); } lights.clear(); for (DistantLight src : pSrc.getLights()) { DistantLight dst = new DistantLight(); dst.assign(src); lights.add(dst); } } @Override public SolidRenderSettings makeCopy() { SolidRenderSettings res = new SolidRenderSettings(); res.assign(this); return res; } @Override public boolean isEqual(SolidRenderSettings pSrc) { // do not care when solid rendering is disabled if (!solidRenderingEnabled && !pSrc.solidRenderingEnabled) { return true; } if (solidRenderingEnabled != pSrc.solidRenderingEnabled || aoEnabled != pSrc.aoEnabled || !shadowType.equals(pSrc.shadowType) || fabs(shadowSmoothRadius - pSrc.shadowSmoothRadius) > EPSILON || shadowmapSize != pSrc.shadowmapSize || fabs(shadowmapBias - pSrc.shadowmapBias) > EPSILON || fabs(aoIntensity - pSrc.aoIntensity) > EPSILON || fabs(aoSearchRadius - pSrc.aoSearchRadius) > EPSILON || fabs(aoBlurRadius - pSrc.aoBlurRadius) > EPSILON || aoRadiusSamples != pSrc.aoRadiusSamples || aoAzimuthSamples != pSrc.aoAzimuthSamples || fabs(aoFalloff - pSrc.aoFalloff) > EPSILON || !postBokehFilterKernel.equals(pSrc.postBokehFilterKernel) || fabs(postBokehIntensity - pSrc.postBokehIntensity) > EPSILON || fabs(postBokehBrightness - pSrc.postBokehBrightness) > EPSILON || fabs(postBokehSize - pSrc.postBokehSize) > EPSILON || fabs(postBokehActivation - pSrc.postBokehActivation) > EPSILON || fabs(aoAffectDiffuse - pSrc.aoAffectDiffuse) > EPSILON) { return false; } if (materials.size() != pSrc.materials.size()) { return false; } if (lights.size() != pSrc.lights.size()) { return false; } for (int i = 0; i < materials.size(); i++) { if (!materials.get(i).isEqual(pSrc.getMaterials().get(i))) { return false; } } for (int i = 0; i < lights.size(); i++) { if (!lights.get(i).isEqual(pSrc.getLights().get(i))) { return false; } } return true; } public List<MaterialSettings> getMaterials() { return materials; } public DistantLight addLight() { DistantLight light = new DistantLight(); lights.add(light); light.setAltitude(60.0); light.setAzimuth(-30.0); light.setIntensity(0.7); light.setShadowIntensity(0.6); light.setRed(1.0); light.setGreen(1.0); light.setBlue(1.0); light.setCastShadows(false); return light; } public MaterialSettings addMaterial() { MaterialSettings material = new MaterialSettings(); material.setAmbient(0.6); material.setDiffuse(0.5); material.setPhong(0.7); material.setPhongSize(36.0); material.setPhongRed(1.0); material.setPhongGreen(0.9); material.setPhongBlue(0.3); material.setReflMapIntensity(0.5); materials.add(material); return material; } public double getAoSearchRadius() { return aoSearchRadius; } public void setAoSearchRadius(double aoSearchRadius) { this.aoSearchRadius = aoSearchRadius; } public double getAoBlurRadius() { return aoBlurRadius; } public void setAoBlurRadius(double aoBlurRadius) { this.aoBlurRadius = aoBlurRadius; } public int getAoRadiusSamples() { return aoRadiusSamples; } public void setAoRadiusSamples(int aoRadiusSamples) { this.aoRadiusSamples = aoRadiusSamples; } public int getAoAzimuthSamples() { return aoAzimuthSamples; } public void setAoAzimuthSamples(int aoAzimuthSamples) { this.aoAzimuthSamples = aoAzimuthSamples; } public double getAoFalloff() { return aoFalloff; } public void setAoFalloff(double aoFalloff) { this.aoFalloff = aoFalloff; } public double getAoAffectDiffuse() { return aoAffectDiffuse; } public void setAoAffectDiffuse(double aoAffectDiffuse) { this.aoAffectDiffuse = aoAffectDiffuse; } public void setupDefaultPostBokehOptions() { postBokehFilterKernel = FilterKernelType.SINEPOW15; postBokehIntensity = 0.005; postBokehBrightness = 1.0; postBokehSize = 2.0; postBokehActivation = 0.2; } public void setupDefaultAmbientShadowOptions() { aoEnabled = true; aoIntensity = 0.6; aoSearchRadius = 4.0; aoBlurRadius = 1.5; aoRadiusSamples = 6; aoAzimuthSamples = 7; aoFalloff = 0.5; aoAffectDiffuse = 0.1; } public void setupDefaultHardShadowOptions() { shadowType = ShadowType.OFF; shadowmapBias = 0.01; shadowmapSize = 2048; shadowSmoothRadius = 1.0; } public ShadowType getShadowType() { return shadowType; } public void setShadowType(ShadowType shadowType) { this.shadowType = shadowType; } public double getShadowmapBias() { return shadowmapBias; } public void setShadowmapBias(double shadowmapBias) { this.shadowmapBias = shadowmapBias; } public int getShadowmapSize() { return shadowmapSize; } public void setShadowmapSize(int shadowmapSize) { this.shadowmapSize = shadowmapSize; } public double getShadowSmoothRadius() { return shadowSmoothRadius; } public void setShadowSmoothRadius(double shadowSmoothRadius) { this.shadowSmoothRadius = shadowSmoothRadius; } public FilterKernelType getPostBokehFilterKernel() { return postBokehFilterKernel; } public void setPostBokehFilterKernel(FilterKernelType postBokehFilterKernel) { this.postBokehFilterKernel = postBokehFilterKernel; } public double getPostBokehIntensity() { return postBokehIntensity; } public void setPostBokehIntensity(double postBokehIntensity) { this.postBokehIntensity = postBokehIntensity; } public double getPostBokehBrightness() { return postBokehBrightness; } public void setPostBokehBrightness(double postBokehBrightness) { this.postBokehBrightness = postBokehBrightness; } public double getPostBokehSize() { return postBokehSize; } public void setPostBokehSize(double postBokehSize) { this.postBokehSize = postBokehSize; } public double getPostBokehActivation() { return postBokehActivation; } public void setPostBokehActivation(double postBokehActivation) { this.postBokehActivation = postBokehActivation; } }