/* 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.variation.mesh; import org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.GfxMathLib; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.base.mathlib.VecMathLib.VectorD; import org.jwildfire.create.tina.base.Layer; import org.jwildfire.create.tina.base.XForm; import org.jwildfire.create.tina.base.XYZPoint; import org.jwildfire.create.tina.variation.ColorMapHolder; import org.jwildfire.create.tina.variation.DisplacementMapHolder; import org.jwildfire.create.tina.variation.FlameTransformationContext; import org.jwildfire.create.tina.variation.VariationFunc; public abstract class AbstractOBJMeshWFFunc extends VariationFunc { private static final long serialVersionUID = 1L; public static final String PARAM_SCALEX = "scale_x"; public static final String PARAM_SCALEY = "scale_y"; protected static final String PARAM_SCALEZ = "scale_z"; protected static final String PARAM_OFFSETX = "offset_x"; protected static final String PARAM_OFFSETY = "offset_y"; protected static final String PARAM_OFFSETZ = "offset_z"; protected static final String PARAM_SUBDIV_LEVEL = "subdiv_level"; protected static final String PARAM_SUBDIV_SMOOTH_PASSES = "subdiv_smooth_passes"; protected static final String PARAM_SUBDIV_SMOOTH_LAMBDA = "subdiv_smooth_lambda"; protected static final String PARAM_SUBDIV_SMOOTH_MU = "subdiv_smooth_mu"; protected static final String PARAM_BLEND_COLORMAP = "blend_colormap"; protected static final String PARAM_DISPL_AMOUNT = "displ_amount"; protected static final String PARAM_BLEND_DISPLMAP = "blend_displ_map"; protected static final String PARAM_RECEIVE_ONLY_SHADOWS = "receive_only_shadows"; private static final String[] paramNames = { PARAM_SCALEX, PARAM_SCALEY, PARAM_SCALEZ, PARAM_OFFSETX, PARAM_OFFSETY, PARAM_OFFSETZ, PARAM_SUBDIV_LEVEL, PARAM_SUBDIV_SMOOTH_PASSES, PARAM_SUBDIV_SMOOTH_LAMBDA, PARAM_SUBDIV_SMOOTH_MU, PARAM_BLEND_COLORMAP, PARAM_DISPL_AMOUNT, PARAM_BLEND_DISPLMAP, PARAM_RECEIVE_ONLY_SHADOWS }; protected static final String RESSOURCE_COLORMAP_FILENAME = "colormap_filename"; protected static final String RESSOURCE_DISPL_MAP_FILENAME = "displ_map_filename"; protected double scaleX = 1.0; protected double scaleY = 1.0; protected double scaleZ = 1.0; protected double offsetX = 0.0; protected double offsetY = 0.0; protected double offsetZ = 0.0; protected int subdiv_level = 0; protected int subdiv_smooth_passes = 12; protected double subdiv_smooth_lambda = 0.42; protected double subdiv_smooth_mu = -0.45; protected int receive_only_shadows = 0; protected SimpleMesh mesh; protected ColorMapHolder colorMapHolder = new ColorMapHolder(); protected DisplacementMapHolder displacementMapHolder = new DisplacementMapHolder(); protected UVColorMapper uvColorMapper = new UVColorMapper(); @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { if (mesh == null || mesh.getFaceCount() == 0) { return; } Face f = mesh.getFace(pContext.random(mesh.getFaceCount())); Vertex rawP1 = mesh.getVertex(f.v1); Vertex rawP2 = mesh.getVertex(f.v2); Vertex rawP3 = mesh.getVertex(f.v3); if ((colorMapHolder.isActive() || displacementMapHolder.isActive()) && rawP1 instanceof VertexWithUV) { VertexWithUV p1 = transform((VertexWithUV) rawP1); VertexWithUV p2 = transform((VertexWithUV) rawP2); VertexWithUV p3 = transform((VertexWithUV) rawP3); // uniform sampling: http://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle double sqrt_r1 = MathLib.sqrt(pContext.random()); double r2 = pContext.random(); double a = 1.0 - sqrt_r1; double b = sqrt_r1 * (1.0 - r2); double c = r2 * sqrt_r1; double dx = a * p1.x + b * p2.x + c * p3.x; double dy = a * p1.y + b * p2.y + c * p3.y; double dz = a * p1.z + b * p2.z + c * p3.z; pVarTP.x += pAmount * dx; pVarTP.y += pAmount * dy; pVarTP.z += pAmount * dz; double u = a * p1.u + b * p2.u + c * p3.u; double v = a * p1.v + b * p2.v + c * p3.v; if (colorMapHolder.isActive()) { double iu = GfxMathLib.clamp(u * (colorMapHolder.getColorMapWidth() - 1.0), 0.0, colorMapHolder.getColorMapWidth() - 1.0); double iv = GfxMathLib.clamp(colorMapHolder.getColorMapHeight() - 1.0 - v * (colorMapHolder.getColorMapHeight() - 1.0), 0, colorMapHolder.getColorMapHeight() - 1.0); int ix = (int) MathLib.trunc(iu); int iy = (int) MathLib.trunc(iv); colorMapHolder.applyImageColor(pVarTP, ix, iy, iu, iv); pVarTP.color = uvColorMapper.getUVColorIdx(Tools.FTOI(pVarTP.redColor), Tools.FTOI(pVarTP.greenColor), Tools.FTOI(pVarTP.blueColor)); } if (displacementMapHolder.isActive()) { VectorD av = new VectorD(p2.x - p1.x, p2.y - p1.y, p2.y - p1.y); VectorD bv = new VectorD(p3.x - p1.x, p3.y - p1.y, p3.y - p1.y); VectorD n = VectorD.cross(av, bv); n.normalize(); double iu = GfxMathLib.clamp(u * (displacementMapHolder.getDisplacementMapWidth() - 1.0), 0.0, displacementMapHolder.getDisplacementMapWidth() - 1.0); double iv = GfxMathLib.clamp(displacementMapHolder.getDisplacementMapHeight() - 1.0 - v * (displacementMapHolder.getDisplacementMapHeight() - 1.0), 0, displacementMapHolder.getDisplacementMapHeight() - 1.0); int ix = (int) MathLib.trunc(iu); int iy = (int) MathLib.trunc(iv); double d = displacementMapHolder.calculateImageDisplacement(ix, iy, iu, iv) * _displ_amount; pVarTP.x += pAmount * n.x * d; pVarTP.y += pAmount * n.y * d; pVarTP.z += pAmount * n.z * d; } } else { Vertex p1 = transform(rawP1); Vertex p2 = transform(rawP2); Vertex p3 = transform(rawP3); // uniform sampling: http://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle double sqrt_r1 = MathLib.sqrt(pContext.random()); double r2 = pContext.random(); double a = 1.0 - sqrt_r1; double b = sqrt_r1 * (1.0 - r2); double c = r2 * sqrt_r1; double dx = a * p1.x + b * p2.x + c * p3.x; double dy = a * p1.y + b * p2.y + c * p3.y; double dz = a * p1.z + b * p2.z + c * p3.z; pVarTP.x += pAmount * dx; pVarTP.y += pAmount * dy; pVarTP.z += pAmount * dz; } if (receive_only_shadows == 1) { pVarTP.receiveOnlyShadows = true; } } private Vertex transform(Vertex p) { Vertex res = new Vertex(); res.x = (float) (p.x * scaleX + offsetX); res.y = (float) (p.y * scaleY + offsetY); res.z = (float) (p.z * scaleZ + offsetZ); return res; } private VertexWithUV transform(VertexWithUV p) { VertexWithUV res = new VertexWithUV(); res.x = (float) (p.x * scaleX + offsetX); res.y = (float) (p.y * scaleY + offsetY); res.z = (float) (p.z * scaleZ + offsetZ); res.u = p.u; res.v = p.v; return res; } @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { scaleX, scaleY, scaleZ, offsetX, offsetY, offsetZ, subdiv_level, subdiv_smooth_passes, subdiv_smooth_lambda, subdiv_smooth_mu, colorMapHolder.getBlend_colormap(), displacementMapHolder.getDispl_amount(), displacementMapHolder.getBlend_displ_map(), receive_only_shadows }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_SCALEX.equalsIgnoreCase(pName)) scaleX = pValue; else if (PARAM_SCALEY.equalsIgnoreCase(pName)) scaleY = pValue; else if (PARAM_SCALEZ.equalsIgnoreCase(pName)) scaleZ = pValue; else if (PARAM_OFFSETX.equalsIgnoreCase(pName)) offsetX = pValue; else if (PARAM_OFFSETY.equalsIgnoreCase(pName)) offsetY = pValue; else if (PARAM_OFFSETZ.equalsIgnoreCase(pName)) offsetZ = pValue; else if (PARAM_SUBDIV_LEVEL.equalsIgnoreCase(pName)) subdiv_level = limitIntVal(Tools.FTOI(pValue), 0, 6); else if (PARAM_SUBDIV_SMOOTH_PASSES.equalsIgnoreCase(pName)) subdiv_smooth_passes = limitIntVal(Tools.FTOI(pValue), 0, 24); else if (PARAM_SUBDIV_SMOOTH_LAMBDA.equalsIgnoreCase(pName)) subdiv_smooth_lambda = pValue; else if (PARAM_SUBDIV_SMOOTH_MU.equalsIgnoreCase(pName)) subdiv_smooth_mu = pValue; else if (PARAM_BLEND_COLORMAP.equalsIgnoreCase(pName)) colorMapHolder.setBlend_colormap(limitIntVal(Tools.FTOI(pValue), 0, 1)); else if (PARAM_DISPL_AMOUNT.equalsIgnoreCase(pName)) displacementMapHolder.setDispl_amount(pValue); else if (PARAM_BLEND_DISPLMAP.equalsIgnoreCase(pName)) displacementMapHolder.setBlend_displ_map(limitIntVal(Tools.FTOI(pValue), 0, 1)); else if (PARAM_RECEIVE_ONLY_SHADOWS.equalsIgnoreCase(pName)) receive_only_shadows = limitIntVal(Tools.FTOI(pValue), 0, 1); else throw new IllegalArgumentException(pName); } private double _displ_amount; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { colorMapHolder.init(); uvColorMapper.initFromLayer(pContext, pLayer); displacementMapHolder.init(); _displ_amount = displacementMapHolder.getDispl_amount() / 255.0; } }