/* * Copyright (c) 2009-2015 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.environment.generation; import com.jme3.environment.util.CubeMapWrapper; import com.jme3.environment.util.EnvMapUtils; import com.jme3.app.Application; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.texture.TextureCubeMap; import static com.jme3.environment.util.EnvMapUtils.shBandFactor; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.util.concurrent.Callable; /** * * Generates the Irrafiance map for PBR. This job can be lauched from a separate * thread. * * TODO there is a lot of duplicate code here with the EnvMapUtils. * * @author Nehon */ //TODO there is a lot of duplicate code here with the EnvMapUtils. We should, //either leverage the code from the util class either remove it and only allow //parallel generation using this runnable. public class IrradianceMapGenerator extends RunnableWithProgress { private int targetMapSize; private EnvMapUtils.FixSeamsMethod fixSeamsMethod; private TextureCubeMap sourceMap; private TextureCubeMap store; private final Application app; /** * Creates an Irradiance map generator. The app is needed to enqueue the * call to the EnvironmentCamera when the generation is done, so that this * process is thread safe. * * @param app the Application * @param listener */ public IrradianceMapGenerator(Application app, JobProgressListener<Integer> listener) { super(listener); this.app = app; } /** * Fills all the genration parameters * * @param sourceMap the source cube map * @param targetMapSize the size of the generated map (width or height in * pixel) * @param fixSeamsMethod the method used to fix seams as described here * {@link EnvMapUtils.FixSeamsMethod} * * @param store The cube map to store the result in. */ public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { this.sourceMap = sourceMap; this.targetMapSize = targetMapSize; this.fixSeamsMethod = fixSeamsMethod; this.store = store; reset(); } @Override public void run() { app.enqueue(new Callable<Void>() { @Override public Void call() throws Exception { listener.start(); return null; } }); try { Vector3f[] shCoeffs = EnvMapUtils.getSphericalHarmonicsCoefficents(sourceMap); store = generateIrradianceMap(shCoeffs, targetMapSize, fixSeamsMethod, store); } catch (Exception e) { e.printStackTrace(); } app.enqueue(new Callable<Void>() { @Override public Void call() throws Exception { listener.done(6); return null; } }); } /** * Generates the Irradiance map (used for image based difuse lighting) from * Spherical Harmonics coefficients previously computed with * {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)} * * @param shCoeffs the SH coeffs * @param targetMapSize the size of the irradiance map to generate * @param fixSeamsMethod the method to fix seams * @param store * @return The irradiance cube map for the given coefficients */ public TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { TextureCubeMap irrCubeMap = store; setEnd(6 + 6); for (int i = 0; i < 6; i++) { ByteBuffer buf = BufferUtils.createByteBuffer(targetMapSize * targetMapSize * store.getImage().getFormat().getBitsPerPixel() / 8); irrCubeMap.getImage().setData(i, buf); progress(); } Vector3f texelVect = new Vector3f(); ColorRGBA color = new ColorRGBA(ColorRGBA.Black); float[] shDir = new float[9]; CubeMapWrapper envMapWriter = new CubeMapWrapper(irrCubeMap); for (int face = 0; face < 6; face++) { for (int y = 0; y < targetMapSize; y++) { for (int x = 0; x < targetMapSize; x++) { EnvMapUtils.getVectorFromCubemapFaceTexCoord(x, y, targetMapSize, face, texelVect, fixSeamsMethod); EnvMapUtils.evalShBasis(texelVect, shDir); color.set(0, 0, 0, 0); for (int i = 0; i < EnvMapUtils.NUM_SH_COEFFICIENT; i++) { color.set(color.r + shCoeffs[i].x * shDir[i] * shBandFactor[i], color.g + shCoeffs[i].y * shDir[i] * shBandFactor[i], color.b + shCoeffs[i].z * shDir[i] * shBandFactor[i], 1.0f); } //clamping the color because very low value close to zero produce artifacts color.r = Math.max(0.0001f, color.r); color.g = Math.max(0.0001f, color.g); color.b = Math.max(0.0001f, color.b); envMapWriter.setPixel(x, y, face, color); } } progress(); } return irrCubeMap; } }