Merge branch 'PBRisComing' # Conflicts: # jme3-core/src/main/java/com/jme3/material/Material.java # jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.javadefine_list_fix
@ -0,0 +1,337 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import com.jme3.app.Application; |
||||||
|
import com.jme3.app.state.BaseAppState; |
||||||
|
import com.jme3.environment.generation.JobProgressListener; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* A 360 camera that can capture a cube map of a scene, and then generate the |
||||||
|
* Prefiltered Environment cube Map and the Irradiance cube Map needed for PBR |
||||||
|
* indirect lighting |
||||||
|
* |
||||||
|
* @see LightProbeFactory |
||||||
|
* @see LightProbe |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class EnvironmentCamera extends BaseAppState { |
||||||
|
|
||||||
|
protected static Vector3f[] axisX = new Vector3f[6]; |
||||||
|
protected static Vector3f[] axisY = new Vector3f[6]; |
||||||
|
protected static Vector3f[] axisZ = new Vector3f[6]; |
||||||
|
|
||||||
|
protected Image.Format imageFormat = Image.Format.RGB16F; |
||||||
|
|
||||||
|
//Axis for cameras
|
||||||
|
static { |
||||||
|
//PositiveX axis(left, up, direction)
|
||||||
|
axisX[0] = Vector3f.UNIT_Z.mult(1f); |
||||||
|
axisY[0] = Vector3f.UNIT_Y.mult(-1f); |
||||||
|
axisZ[0] = Vector3f.UNIT_X.mult(1f); |
||||||
|
//NegativeX
|
||||||
|
axisX[1] = Vector3f.UNIT_Z.mult(-1f); |
||||||
|
axisY[1] = Vector3f.UNIT_Y.mult(-1f); |
||||||
|
axisZ[1] = Vector3f.UNIT_X.mult(-1f); |
||||||
|
//PositiveY
|
||||||
|
axisX[2] = Vector3f.UNIT_X.mult(-1f); |
||||||
|
axisY[2] = Vector3f.UNIT_Z.mult(1f); |
||||||
|
axisZ[2] = Vector3f.UNIT_Y.mult(1f); |
||||||
|
//NegativeY
|
||||||
|
axisX[3] = Vector3f.UNIT_X.mult(-1f); |
||||||
|
axisY[3] = Vector3f.UNIT_Z.mult(-1f); |
||||||
|
axisZ[3] = Vector3f.UNIT_Y.mult(-1f); |
||||||
|
//PositiveZ
|
||||||
|
axisX[4] = Vector3f.UNIT_X.mult(-1f); |
||||||
|
axisY[4] = Vector3f.UNIT_Y.mult(-1f); |
||||||
|
axisZ[4] = Vector3f.UNIT_Z; |
||||||
|
//NegativeZ
|
||||||
|
axisX[5] = Vector3f.UNIT_X.mult(1f); |
||||||
|
axisY[5] = Vector3f.UNIT_Y.mult(-1f); |
||||||
|
axisZ[5] = Vector3f.UNIT_Z.mult(-1f); |
||||||
|
|
||||||
|
} |
||||||
|
protected Image images[]; |
||||||
|
protected ViewPort[] viewports; |
||||||
|
protected FrameBuffer[] framebuffers; |
||||||
|
protected ByteBuffer[] buffers; |
||||||
|
|
||||||
|
protected Vector3f position = new Vector3f(); |
||||||
|
protected ColorRGBA backGroundColor; |
||||||
|
|
||||||
|
protected int size = 128; |
||||||
|
|
||||||
|
private final List<SnapshotJob> jobs = new ArrayList<SnapshotJob>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an EnvironmentCamera with a size of 128 |
||||||
|
*/ |
||||||
|
public EnvironmentCamera() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an EnvironmentCamera with the given size. |
||||||
|
* |
||||||
|
* @param size the size of the resulting texture. |
||||||
|
*/ |
||||||
|
public EnvironmentCamera(int size) { |
||||||
|
this.size = size; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an EnvironmentCamera with the given size, and the given position |
||||||
|
* |
||||||
|
* @param size the size of the resulting texture. |
||||||
|
* @param position the position of the camera. |
||||||
|
*/ |
||||||
|
public EnvironmentCamera(int size, Vector3f position) { |
||||||
|
this.size = size; |
||||||
|
this.position.set(position); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an EnvironmentCamera with the given size, and the given position |
||||||
|
* |
||||||
|
* @param size the size of the resulting texture, and the given ImageFormat. |
||||||
|
* @param position the position of the camera. |
||||||
|
* @param imageFormat the ImageFormat to use for the resulting texture. |
||||||
|
*/ |
||||||
|
public EnvironmentCamera(int size, Vector3f position, Image.Format imageFormat) { |
||||||
|
this.size = size; |
||||||
|
this.position.set(position); |
||||||
|
this.imageFormat = imageFormat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Takes a snapshot of the surrounding scene. |
||||||
|
* |
||||||
|
* @param scene the scene to snapshot. |
||||||
|
* @param done a callback to call when the snapshot is done. |
||||||
|
*/ |
||||||
|
public void snapshot(final Spatial scene, final JobProgressListener<TextureCubeMap> done) { |
||||||
|
getApplication().enqueue(new Callable<Void>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
SnapshotJob job = new SnapshotJob(done, scene); |
||||||
|
jobs.add(job); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void render(final RenderManager renderManager) { |
||||||
|
|
||||||
|
if (jobs.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
final SnapshotJob job = jobs.get(0); |
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
viewports[i].clearScenes(); |
||||||
|
viewports[i].attachScene(job.scene); |
||||||
|
renderManager.renderViewPort(viewports[i], 0.16f); |
||||||
|
buffers[i] = BufferUtils.createByteBuffer(size * size * imageFormat.getBitsPerPixel() / 8); |
||||||
|
renderManager.getRenderer().readFrameBufferWithFormat(framebuffers[i], buffers[i], imageFormat); |
||||||
|
images[i] = new Image(imageFormat, size, size, buffers[i], ColorSpace.Linear); |
||||||
|
} |
||||||
|
|
||||||
|
final TextureCubeMap map = EnvMapUtils.makeCubeMap(images[0], images[1], images[2], images[3], images[4], images[5], imageFormat); |
||||||
|
|
||||||
|
job.callback.done(map); |
||||||
|
map.getImage().dispose(); |
||||||
|
jobs.remove(0); |
||||||
|
} |
||||||
|
|
||||||
|
public int getSize() { |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
public Vector3f getPosition() { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the camera position in world space. |
||||||
|
* |
||||||
|
* @param position the position in world space |
||||||
|
*/ |
||||||
|
public void setPosition(final Vector3f position) { |
||||||
|
this.position.set(position); |
||||||
|
|
||||||
|
if (viewports == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (final ViewPort viewPort : viewports) { |
||||||
|
viewPort.getCamera().setLocation(position); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initialize(Application app) { |
||||||
|
this.backGroundColor = app.getViewPort().getBackgroundColor(); |
||||||
|
|
||||||
|
final Camera[] cameras = new Camera[6]; |
||||||
|
|
||||||
|
Texture2D[] textures = new Texture2D[6]; |
||||||
|
|
||||||
|
viewports = new ViewPort[6]; |
||||||
|
framebuffers = new FrameBuffer[6]; |
||||||
|
buffers = new ByteBuffer[6]; |
||||||
|
images = new Image[6]; |
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
cameras[i] = createOffCamera(size, position, axisX[i], axisY[i], axisZ[i]); |
||||||
|
viewports[i] = createOffViewPort("EnvView" + i, cameras[i]); |
||||||
|
framebuffers[i] = createOffScreenFrameBuffer(size, viewports[i]); |
||||||
|
textures[i] = new Texture2D(size, size, imageFormat); |
||||||
|
framebuffers[i].setColorTexture(textures[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void cleanup(Application app) { |
||||||
|
this.backGroundColor = null; |
||||||
|
|
||||||
|
for (final FrameBuffer frameBuffer : framebuffers) { |
||||||
|
frameBuffer.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
for (final Image image : images) { |
||||||
|
if( image != null){ |
||||||
|
image.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the images format used for the generated maps. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Image.Format getImageFormat() { |
||||||
|
return imageFormat; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onEnable() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDisable() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an off camera |
||||||
|
* |
||||||
|
* @param mapSize the size |
||||||
|
* @param worldPos the position |
||||||
|
* @param axisX the x axis |
||||||
|
* @param axisY the y axis |
||||||
|
* @param axisZ tha z axis |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
protected Camera createOffCamera(final int mapSize, final Vector3f worldPos, final Vector3f axisX, final Vector3f axisY, final Vector3f axisZ) { |
||||||
|
final Camera offCamera = new Camera(mapSize, mapSize); |
||||||
|
offCamera.setLocation(worldPos); |
||||||
|
offCamera.setAxes(axisX, axisY, axisZ); |
||||||
|
offCamera.setFrustumPerspective(90f, 1f, 1, 1000); |
||||||
|
offCamera.setLocation(position); |
||||||
|
return offCamera; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* creates an offsceen VP |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* @param offCamera |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
protected ViewPort createOffViewPort(final String name, final Camera offCamera) { |
||||||
|
final ViewPort offView = new ViewPort(name, offCamera); |
||||||
|
offView.setClearFlags(true, true, true); |
||||||
|
offView.setBackgroundColor(backGroundColor); |
||||||
|
return offView; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* create an offscreen frame buffer. |
||||||
|
* |
||||||
|
* @param mapSize |
||||||
|
* @param offView |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
protected FrameBuffer createOffScreenFrameBuffer(int mapSize, ViewPort offView) { |
||||||
|
// create offscreen framebuffer
|
||||||
|
final FrameBuffer offBuffer = new FrameBuffer(mapSize, mapSize, 1); |
||||||
|
offBuffer.setDepthBuffer(Image.Format.Depth); |
||||||
|
offView.setOutputFrameBuffer(offBuffer); |
||||||
|
return offBuffer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An inner class to keep track on a snapshot job. |
||||||
|
*/ |
||||||
|
protected class SnapshotJob { |
||||||
|
|
||||||
|
JobProgressListener<TextureCubeMap> callback; |
||||||
|
Spatial scene; |
||||||
|
|
||||||
|
public SnapshotJob(JobProgressListener callback, Spatial scene) { |
||||||
|
this.callback = callback; |
||||||
|
this.scene = scene; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,291 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.environment.generation.JobProgressListener; |
||||||
|
import com.jme3.environment.generation.PrefilteredEnvMapFaceGenerator; |
||||||
|
import com.jme3.environment.generation.IrradianceMapGenerator; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.app.Application; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor; |
||||||
|
|
||||||
|
/** |
||||||
|
* This Factory allows to create LightProbes within a scene given an EnvironmentCamera. |
||||||
|
* |
||||||
|
* Since the process can be long, you can provide a JobProgressListener that |
||||||
|
* will be notified of the ongoing generation process when calling the makeProbe method. |
||||||
|
* |
||||||
|
* The process is the folowing : |
||||||
|
* 1. Create an EnvironmentCamera |
||||||
|
* 2. give it a position in the scene |
||||||
|
* 3. call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} |
||||||
|
* 4. add the created LightProbe to a node with the {@link Node#addLight(com.jme3.light.Light) } method. |
||||||
|
* |
||||||
|
* Optionally for step 3 call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node, com.jme3.environment.generation.JobProgressListener) } |
||||||
|
* with a {@link JobProgressListener} to be notified of the progress of the generation process. |
||||||
|
* |
||||||
|
* The generation will be split in several threads for faster generation. |
||||||
|
* |
||||||
|
* This class is entirely thread safe and can be called from any thread. |
||||||
|
* |
||||||
|
* Note that in case you are using a {@link JobProgressListener} all the its |
||||||
|
* method will be called inside and app.enqueu callable. |
||||||
|
* This means that it's completely safe to modify the scenegraph within the |
||||||
|
* Listener method, but also means that the even will be delayed until next update loop. |
||||||
|
* |
||||||
|
* @see EnvironmentCamera |
||||||
|
* @author bouquet |
||||||
|
*/ |
||||||
|
public class LightProbeFactory { |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a LightProbe with the giver EnvironmentCamera in the given scene. |
||||||
|
* |
||||||
|
* Note that this is an assynchronous process that will run on multiple threads. |
||||||
|
* The process is thread safe. |
||||||
|
* The created lightProbe will only be marked as ready when the rendering process is done. |
||||||
|
* |
||||||
|
* If you want to monitor the process use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node, com.jme3.environment.generation.JobProgressListener) } |
||||||
|
* |
||||||
|
* |
||||||
|
* |
||||||
|
* @see LightProbe |
||||||
|
* @see EnvironmentCamera |
||||||
|
* @param envCam the EnvironmentCamera |
||||||
|
* @param scene the Scene |
||||||
|
* @return the created LightProbe |
||||||
|
*/ |
||||||
|
public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene) { |
||||||
|
return makeProbe(envCam, scene, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a LightProbe with the giver EnvironmentCamera in the given scene. |
||||||
|
* |
||||||
|
* Note that this is an assynchronous process that will run on multiple threads. |
||||||
|
* The process is thread safe. |
||||||
|
* The created lightProbe will only be marked as ready when the rendering process is done. |
||||||
|
* |
||||||
|
* The JobProgressListener will be notified of the progress of the generation. |
||||||
|
* Note that you can also use a {@link JobProgressAdapter}. |
||||||
|
* |
||||||
|
* @see LightProbe |
||||||
|
* @see EnvironmentCamera |
||||||
|
* @see JobProgressListener |
||||||
|
|
||||||
|
* @param envCam the EnvironmentCamera |
||||||
|
* @param scene the Scene |
||||||
|
* @param listener the listener of the genration progress. |
||||||
|
* @return the created LightProbe |
||||||
|
*/ |
||||||
|
public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) { |
||||||
|
final LightProbe probe = new LightProbe(); |
||||||
|
probe.setPosition(envCam.getPosition()); |
||||||
|
probe.setIrradianceMap(EnvMapUtils.createIrradianceMap(envCam.getSize(), envCam.getImageFormat())); |
||||||
|
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat())); |
||||||
|
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(TextureCubeMap map) { |
||||||
|
generatePbrMaps(map, probe, envCam.getApplication(), listener); |
||||||
|
} |
||||||
|
}); |
||||||
|
return probe; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates a LightProbe with the giver EnvironmentCamera in the given scene. |
||||||
|
* |
||||||
|
* Note that this is an assynchronous process that will run on multiple threads. |
||||||
|
* The process is thread safe. |
||||||
|
* The created lightProbe will only be marked as ready when the rendering process is done. |
||||||
|
* |
||||||
|
* The JobProgressListener will be notified of the progress of the generation. |
||||||
|
* Note that you can also use a {@link JobProgressAdapter}. |
||||||
|
* |
||||||
|
* @see LightProbe |
||||||
|
* @see EnvironmentCamera |
||||||
|
* @see JobProgressListener |
||||||
|
* |
||||||
|
* @param probe the Light probe to update |
||||||
|
* @param envCam the EnvironmentCamera |
||||||
|
* @param scene the Scene |
||||||
|
* @param listener the listener of the genration progress. |
||||||
|
* @return the created LightProbe |
||||||
|
*/ |
||||||
|
public static LightProbe updateProbe(final LightProbe probe, final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) { |
||||||
|
|
||||||
|
envCam.setPosition(probe.getPosition()); |
||||||
|
|
||||||
|
probe.setReady(false); |
||||||
|
|
||||||
|
if(probe.getIrradianceMap() != null) { |
||||||
|
probe.getIrradianceMap().getImage().dispose(); |
||||||
|
probe.getPrefilteredEnvMap().getImage().dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
probe.setIrradianceMap(EnvMapUtils.createIrradianceMap(envCam.getSize(), envCam.getImageFormat())); |
||||||
|
probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat())); |
||||||
|
|
||||||
|
|
||||||
|
envCam.snapshot(scene, new JobProgressAdapter<TextureCubeMap>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(TextureCubeMap map) { |
||||||
|
generatePbrMaps(map, probe, envCam.getApplication(), listener); |
||||||
|
} |
||||||
|
}); |
||||||
|
return probe; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Internally called to generate the maps. |
||||||
|
* This method will spawn 7 thread (one for the IrradianceMap, and one for each face of the prefiltered env map). |
||||||
|
* Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the genration is done. |
||||||
|
* |
||||||
|
* @param envMap the raw env map rendered by the env camera |
||||||
|
* @param probe the LigthProbe to generate maps for |
||||||
|
* @param app the Application |
||||||
|
* @param listener a progress listener. (can be null if no progress reporting is needed) |
||||||
|
*/ |
||||||
|
private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, final JobProgressListener<LightProbe> listener) { |
||||||
|
IrradianceMapGenerator irrMapGenerator; |
||||||
|
PrefilteredEnvMapFaceGenerator[] pemGenerators = new PrefilteredEnvMapFaceGenerator[6]; |
||||||
|
|
||||||
|
final JobState jobState = new JobState(new ScheduledThreadPoolExecutor(7)); |
||||||
|
|
||||||
|
irrMapGenerator = new IrradianceMapGenerator(app, new JobListener(listener, jobState, probe, 6)); |
||||||
|
int size = envMap.getImage().getWidth(); |
||||||
|
irrMapGenerator.setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getIrradianceMap()); |
||||||
|
|
||||||
|
jobState.executor.execute(irrMapGenerator); |
||||||
|
|
||||||
|
for (int i = 0; i < pemGenerators.length; i++) { |
||||||
|
pemGenerators[i] = new PrefilteredEnvMapFaceGenerator(app, i, new JobListener(listener, jobState, probe, i)); |
||||||
|
pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.Wrap, probe.getPrefilteredEnvMap()); |
||||||
|
jobState.executor.execute(pemGenerators[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An inner class to keep the state of a generation process |
||||||
|
*/ |
||||||
|
private static class JobState { |
||||||
|
|
||||||
|
double progress[] = new double[7]; |
||||||
|
boolean done[] = new boolean[7]; |
||||||
|
ScheduledThreadPoolExecutor executor; |
||||||
|
boolean started = false; |
||||||
|
|
||||||
|
public JobState(ScheduledThreadPoolExecutor executor) { |
||||||
|
this.executor = executor; |
||||||
|
} |
||||||
|
|
||||||
|
boolean isDone() { |
||||||
|
for (boolean d : done) { |
||||||
|
if (d == false) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
float getProgress() { |
||||||
|
float mean = 0; |
||||||
|
for (double progres : progress) { |
||||||
|
mean += progres; |
||||||
|
} |
||||||
|
return mean / 7f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An inner JobProgressListener to controll the genration process and properly clean up when it's done |
||||||
|
*/ |
||||||
|
private static class JobListener extends JobProgressAdapter<Integer> { |
||||||
|
|
||||||
|
JobProgressListener<LightProbe> globalListener; |
||||||
|
JobState jobState; |
||||||
|
LightProbe probe; |
||||||
|
|
||||||
|
int index; |
||||||
|
|
||||||
|
public JobListener(JobProgressListener<LightProbe> globalListener, JobState jobState, LightProbe probe, int index) { |
||||||
|
this.globalListener = globalListener; |
||||||
|
this.jobState = jobState; |
||||||
|
this.probe = probe; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
if (globalListener != null && !jobState.started) { |
||||||
|
jobState.started = true; |
||||||
|
globalListener.start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void progress(double value) { |
||||||
|
jobState.progress[index] = value; |
||||||
|
if (globalListener != null) { |
||||||
|
globalListener.progress(jobState.getProgress()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(Integer result) { |
||||||
|
if (globalListener != null) { |
||||||
|
if (result < 6) { |
||||||
|
globalListener.step("Prefiltered env map face " + result + " generated"); |
||||||
|
} else { |
||||||
|
globalListener.step("Irradiance map generated"); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
jobState.done[index] = true; |
||||||
|
if (jobState.isDone()) { |
||||||
|
probe.setReady(true); |
||||||
|
if (globalListener != null) { |
||||||
|
globalListener.done(probe); |
||||||
|
} |
||||||
|
jobState.executor.shutdownNow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,176 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract Adapter class that implements optional methods of JobProgressListener. |
||||||
|
* Extends this class instead of implementing a JobProgressListener if you need |
||||||
|
* only a subset of method implemented. |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
* @param <T> |
||||||
|
*/ |
||||||
|
public abstract class JobProgressAdapter<T> implements JobProgressListener<T>{ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void progress(double value) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void step(String message) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void done(T result); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* An interface listener that will be notified of the progress of an asynchronous |
||||||
|
* generation job. |
||||||
|
* |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
* @param <T> The type of object generated. |
||||||
|
*/ |
||||||
|
public interface JobProgressListener<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the process starts. |
||||||
|
*/ |
||||||
|
public void start(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Can be called when a step of the process has been completed with a relevant message. |
||||||
|
* @param message the message stating of the paricular step completion. |
||||||
|
*/ |
||||||
|
public void step(String message); |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the process has made some progress. |
||||||
|
* @param value a value from 0 to 1 representing the percentage of completion of the process. |
||||||
|
*/ |
||||||
|
public void progress(double value); |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the process is done. |
||||||
|
* @param result the object generated by the process. |
||||||
|
*/ |
||||||
|
public void done(T result); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,265 @@ |
|||||||
|
/* |
||||||
|
* 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 static com.jme3.math.FastMath.abs; |
||||||
|
import static com.jme3.math.FastMath.clamp; |
||||||
|
import static com.jme3.math.FastMath.pow; |
||||||
|
import static com.jme3.math.FastMath.sqrt; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.math.Vector4f; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint; |
||||||
|
import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip; |
||||||
|
import static com.jme3.environment.util.EnvMapUtils.getSampleFromMip; |
||||||
|
import static com.jme3.environment.util.EnvMapUtils.getVectorFromCubemapFaceTexCoord; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Generates one face of the prefiltered environnement 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 PrefilteredEnvMapFaceGenerator extends RunnableWithProgress { |
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger(PrefilteredEnvMapFaceGenerator.class.getName()); |
||||||
|
|
||||||
|
private int targetMapSize; |
||||||
|
private EnvMapUtils.FixSeamsMethod fixSeamsMethod; |
||||||
|
private TextureCubeMap sourceMap; |
||||||
|
private TextureCubeMap store; |
||||||
|
private final Application app; |
||||||
|
private int face = 0; |
||||||
|
Vector4f Xi = new Vector4f(); |
||||||
|
Vector3f H = new Vector3f(); |
||||||
|
Vector3f tmp = new Vector3f(); |
||||||
|
ColorRGBA c = new ColorRGBA(); |
||||||
|
Vector3f tmp1 = new Vector3f(); |
||||||
|
Vector3f tmp2 = new Vector3f(); |
||||||
|
Vector3f tmp3 = new Vector3f(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a pem generator for the given face. 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 face the face to generate |
||||||
|
* @param listener |
||||||
|
*/ |
||||||
|
public PrefilteredEnvMapFaceGenerator(Application app, int face, JobProgressListener<Integer> listener) { |
||||||
|
super(listener); |
||||||
|
this.app = app; |
||||||
|
this.face = face; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init(){ |
||||||
|
Xi.set(0, 0, 0, 0); |
||||||
|
H.set(0, 0, 0); |
||||||
|
tmp.set(0, 0, 0); |
||||||
|
c.set(1, 1, 1, 1); |
||||||
|
tmp1.set(0, 0, 0); |
||||||
|
tmp2.set(0, 0, 0); |
||||||
|
tmp3.set(0, 0, 0); |
||||||
|
reset(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
|
||||||
|
app.enqueue(new Callable<Void>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
listener.start(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
store = generatePrefilteredEnvMap(sourceMap, targetMapSize, fixSeamsMethod, store); |
||||||
|
app.enqueue(new Callable<Void>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
listener.done(face); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates the prefiltered env map (used for image based specular |
||||||
|
* lighting) With the GGX/Shlick brdf |
||||||
|
* {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)} |
||||||
|
* Note that the output cube map is in RGBA8 format. |
||||||
|
* |
||||||
|
* @param sourceEnvMap |
||||||
|
* @param targetMapSize the size of the irradiance map to generate |
||||||
|
* @param store |
||||||
|
* @param fixSeamsMethod the method to fix seams |
||||||
|
* @return The irradiance cube map for the given coefficients |
||||||
|
*/ |
||||||
|
private TextureCubeMap generatePrefilteredEnvMap(TextureCubeMap sourceEnvMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { |
||||||
|
TextureCubeMap pem = store; |
||||||
|
|
||||||
|
int nbMipMap = (int) (Math.log(targetMapSize) / Math.log(2) - 1); |
||||||
|
|
||||||
|
setEnd(nbMipMap); |
||||||
|
|
||||||
|
|
||||||
|
CubeMapWrapper sourceWrapper = new CubeMapWrapper(sourceEnvMap); |
||||||
|
CubeMapWrapper targetWrapper = new CubeMapWrapper(pem); |
||||||
|
|
||||||
|
Vector3f texelVect = new Vector3f(); |
||||||
|
Vector3f color = new Vector3f(); |
||||||
|
ColorRGBA outColor = new ColorRGBA(); |
||||||
|
for (int mipLevel = 0; mipLevel < nbMipMap; mipLevel++) { |
||||||
|
float roughness = getRoughnessFromMip(mipLevel, nbMipMap); |
||||||
|
int nbSamples = getSampleFromMip(mipLevel, nbMipMap); |
||||||
|
int targetMipMapSize = (int) pow(2, nbMipMap + 1 - mipLevel); |
||||||
|
|
||||||
|
for (int y = 0; y < targetMipMapSize; y++) { |
||||||
|
for (int x = 0; x < targetMipMapSize; x++) { |
||||||
|
color.set(0, 0, 0); |
||||||
|
getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, EnvMapUtils.FixSeamsMethod.Wrap); |
||||||
|
prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color); |
||||||
|
|
||||||
|
outColor.set(Math.max(color.x, 0.0001f), Math.max(color.y,0.0001f), Math.max(color.z, 0.0001f), 1); |
||||||
|
log.log(Level.FINE, "coords {0},{1}", new Object[]{x, y}); |
||||||
|
targetWrapper.setPixel(x, y, face, mipLevel, outColor); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
progress(); |
||||||
|
} |
||||||
|
|
||||||
|
return pem; |
||||||
|
} |
||||||
|
|
||||||
|
private Vector3f prefilterEnvMapTexel(CubeMapWrapper envMapReader, float roughness, Vector3f N, int numSamples, Vector3f store) { |
||||||
|
|
||||||
|
Vector3f prefilteredColor = store; |
||||||
|
float totalWeight = 0.0f; |
||||||
|
|
||||||
|
// a = roughness² and a2 = a²
|
||||||
|
float a2 = roughness * roughness; |
||||||
|
a2 *= a2; |
||||||
|
a2 *= 10; |
||||||
|
for (int i = 0; i < numSamples; i++) { |
||||||
|
Xi = getHammersleyPoint(i, numSamples, Xi); |
||||||
|
H = importanceSampleGGX(Xi, a2, N, H); |
||||||
|
|
||||||
|
H.normalizeLocal(); |
||||||
|
tmp.set(H); |
||||||
|
float NoH = N.dot(tmp); |
||||||
|
|
||||||
|
Vector3f L = tmp.multLocal(NoH * 2).subtractLocal(N); |
||||||
|
float NoL = clamp(N.dot(L), 0.0f, 1.0f); |
||||||
|
if (NoL > 0) { |
||||||
|
envMapReader.getPixel(L, c); |
||||||
|
prefilteredColor.setX(prefilteredColor.x + c.r * NoL); |
||||||
|
prefilteredColor.setY(prefilteredColor.y + c.g * NoL); |
||||||
|
prefilteredColor.setZ(prefilteredColor.z + c.b * NoL); |
||||||
|
|
||||||
|
totalWeight += NoL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return prefilteredColor.divideLocal(totalWeight); |
||||||
|
} |
||||||
|
|
||||||
|
public Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f normal, Vector3f store) { |
||||||
|
if (store == null) { |
||||||
|
store = new Vector3f(); |
||||||
|
} |
||||||
|
|
||||||
|
float cosTheta = sqrt((1f - xi.x) / (1f + (a2 - 1f) * xi.x)); |
||||||
|
float sinTheta = sqrt(1f - cosTheta * cosTheta); |
||||||
|
|
||||||
|
float sinThetaCosPhi = sinTheta * xi.z;//xi.z is cos(phi)
|
||||||
|
float sinThetaSinPhi = sinTheta * xi.w;//xi.w is sin(phi)
|
||||||
|
|
||||||
|
Vector3f upVector = Vector3f.UNIT_X; |
||||||
|
|
||||||
|
if (abs(normal.z) < 0.999) { |
||||||
|
upVector = Vector3f.UNIT_Y; |
||||||
|
} |
||||||
|
|
||||||
|
Vector3f tangentX = tmp1.set(upVector).crossLocal(normal).normalizeLocal(); |
||||||
|
Vector3f tangentY = tmp2.set(normal).crossLocal(tangentX); |
||||||
|
|
||||||
|
// Tangent to world space
|
||||||
|
tangentX.multLocal(sinThetaCosPhi); |
||||||
|
tangentY.multLocal(sinThetaSinPhi); |
||||||
|
tmp3.set(normal).multLocal(cosTheta); |
||||||
|
|
||||||
|
// Tangent to world space
|
||||||
|
store.set(tangentX).addLocal(tangentY).addLocal(tmp3); |
||||||
|
|
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Abstract runnable that can report its progress |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public abstract class RunnableWithProgress implements Runnable { |
||||||
|
|
||||||
|
private int progress; |
||||||
|
private int end; |
||||||
|
protected JobProgressListener listener; |
||||||
|
|
||||||
|
public RunnableWithProgress() { |
||||||
|
} |
||||||
|
|
||||||
|
public RunnableWithProgress(JobProgressListener listener) { |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* set the end step value of the process. |
||||||
|
* |
||||||
|
* @param end |
||||||
|
*/ |
||||||
|
protected void setEnd(int end) { |
||||||
|
this.end = end; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return the curent progress of the process. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public double getProgress() { |
||||||
|
return (double) progress / (double) end; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* adds one progression step to the process. |
||||||
|
*/ |
||||||
|
protected void progress() { |
||||||
|
progress++; |
||||||
|
if (listener != null) { |
||||||
|
listener.progress(getProgress()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* resets the progression of the process. |
||||||
|
*/ |
||||||
|
protected void reset() { |
||||||
|
progress = 0; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
/* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Mesh; |
||||||
|
import com.jme3.scene.VertexBuffer.Type; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import java.nio.FloatBuffer; |
||||||
|
import java.nio.ShortBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* A debuging shape for a BoundingSphere |
||||||
|
* Consists of 3 axis aligned circles. |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class BoundingSphereDebug extends Mesh { |
||||||
|
|
||||||
|
protected int vertCount; |
||||||
|
protected int triCount; |
||||||
|
protected int radialSamples = 32; |
||||||
|
protected boolean useEvenSlices; |
||||||
|
protected boolean interior; |
||||||
|
/** |
||||||
|
* the distance from the center point each point falls on |
||||||
|
*/ |
||||||
|
public float radius; |
||||||
|
|
||||||
|
public float getRadius() { |
||||||
|
return radius; |
||||||
|
} |
||||||
|
|
||||||
|
public BoundingSphereDebug() { |
||||||
|
setGeometryData(); |
||||||
|
setIndexData(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* builds the vertices based on the radius |
||||||
|
*/ |
||||||
|
private void setGeometryData() { |
||||||
|
setMode(Mode.Lines); |
||||||
|
|
||||||
|
FloatBuffer posBuf = BufferUtils.createVector3Buffer((radialSamples + 1) * 3); |
||||||
|
FloatBuffer colBuf = BufferUtils.createVector3Buffer((radialSamples + 1) * 4); |
||||||
|
|
||||||
|
setBuffer(Type.Position, 3, posBuf); |
||||||
|
setBuffer(Type.Color, 4, colBuf); |
||||||
|
|
||||||
|
// generate geometry
|
||||||
|
float fInvRS = 1.0f / radialSamples; |
||||||
|
|
||||||
|
// Generate points on the unit circle to be used in computing the mesh
|
||||||
|
// points on a sphere slice.
|
||||||
|
float[] afSin = new float[(radialSamples + 1)]; |
||||||
|
float[] afCos = new float[(radialSamples + 1)]; |
||||||
|
for (int iR = 0; iR < radialSamples; iR++) { |
||||||
|
float fAngle = FastMath.TWO_PI * fInvRS * iR; |
||||||
|
afCos[iR] = FastMath.cos(fAngle); |
||||||
|
afSin[iR] = FastMath.sin(fAngle); |
||||||
|
} |
||||||
|
afSin[radialSamples] = afSin[0]; |
||||||
|
afCos[radialSamples] = afCos[0]; |
||||||
|
|
||||||
|
for (int iR = 0; iR <= radialSamples; iR++) { |
||||||
|
posBuf.put(afCos[iR]) |
||||||
|
.put(afSin[iR]) |
||||||
|
.put(0); |
||||||
|
colBuf.put(ColorRGBA.Blue.r) |
||||||
|
.put(ColorRGBA.Blue.g) |
||||||
|
.put(ColorRGBA.Blue.b) |
||||||
|
.put(ColorRGBA.Blue.a); |
||||||
|
|
||||||
|
} |
||||||
|
for (int iR = 0; iR <= radialSamples; iR++) { |
||||||
|
posBuf.put(afCos[iR]) |
||||||
|
.put(0) |
||||||
|
.put(afSin[iR]); |
||||||
|
colBuf.put(ColorRGBA.Green.r) |
||||||
|
.put(ColorRGBA.Green.g) |
||||||
|
.put(ColorRGBA.Green.b) |
||||||
|
.put(ColorRGBA.Green.a); |
||||||
|
} |
||||||
|
for (int iR = 0; iR <= radialSamples; iR++) { |
||||||
|
posBuf.put(0) |
||||||
|
.put(afCos[iR]) |
||||||
|
.put(afSin[iR]); |
||||||
|
colBuf.put(ColorRGBA.Yellow.r) |
||||||
|
.put(ColorRGBA.Yellow.g) |
||||||
|
.put(ColorRGBA.Yellow.b) |
||||||
|
.put(ColorRGBA.Yellow.a); |
||||||
|
} |
||||||
|
|
||||||
|
updateBound(); |
||||||
|
setStatic(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the indices for rendering the sphere. |
||||||
|
*/ |
||||||
|
private void setIndexData() { |
||||||
|
|
||||||
|
// allocate connectivity
|
||||||
|
int nbSegments = (radialSamples) * 3; |
||||||
|
|
||||||
|
ShortBuffer idxBuf = BufferUtils.createShortBuffer(2 * nbSegments); |
||||||
|
setBuffer(Type.Index, 2, idxBuf); |
||||||
|
|
||||||
|
int idx = 0; |
||||||
|
int segDone = 0; |
||||||
|
while (segDone < nbSegments) { |
||||||
|
idxBuf.put((short) idx); |
||||||
|
idxBuf.put((short) (idx + 1)); |
||||||
|
idx++; |
||||||
|
segDone++; |
||||||
|
if (segDone == radialSamples || segDone == radialSamples * 2) { |
||||||
|
idx++; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience factory method that creates a debuging bounding sphere geometry |
||||||
|
* @param assetManager the assetManager |
||||||
|
* @return the bounding sphere debug geometry. |
||||||
|
*/ |
||||||
|
public static Geometry createDebugSphere(AssetManager assetManager) { |
||||||
|
BoundingSphereDebug b = new BoundingSphereDebug(); |
||||||
|
Geometry geom = new Geometry("BoundingDebug", b); |
||||||
|
|
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.setBoolean("VertexColor", true); |
||||||
|
mat.getAdditionalRenderState().setWireframe(true); |
||||||
|
|
||||||
|
geom.setMaterial(mat); |
||||||
|
return geom; |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,243 @@ |
|||||||
|
/* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import static com.jme3.math.FastMath.pow; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.image.DefaultImageRaster; |
||||||
|
import com.jme3.texture.image.MipMapImageRaster; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Wraps a Cube map and allows to read from or write pixels into it. |
||||||
|
* |
||||||
|
* It uses the ImageRaster class to tailor the read write operations. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class CubeMapWrapper { |
||||||
|
|
||||||
|
private MipMapImageRaster mipMapRaster; |
||||||
|
private final DefaultImageRaster raster; |
||||||
|
private int[] sizes; |
||||||
|
private final Vector2f uvs = new Vector2f(); |
||||||
|
private final Image image; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a CubeMapWrapper for the given cube map |
||||||
|
* Note that the cube map must be initialized, and the mipmaps sizes should |
||||||
|
* be set if relevant for them to be readable/writable |
||||||
|
* @param cubeMap the cubemap to wrap. |
||||||
|
*/ |
||||||
|
public CubeMapWrapper(TextureCubeMap cubeMap) { |
||||||
|
image = cubeMap.getImage(); |
||||||
|
if (image.hasMipmaps()) { |
||||||
|
int nbMipMaps = image.getMipMapSizes().length; |
||||||
|
sizes = new int[nbMipMaps]; |
||||||
|
mipMapRaster = new MipMapImageRaster(image, 0); |
||||||
|
|
||||||
|
for (int i = 0; i < nbMipMaps; i++) { |
||||||
|
sizes[i] = Math.max(1, image.getWidth() >> i); |
||||||
|
} |
||||||
|
} else { |
||||||
|
sizes = new int[1]; |
||||||
|
sizes[0] = image.getWidth(); |
||||||
|
} |
||||||
|
raster = new DefaultImageRaster(image, 0,0 , false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads a pixel from the cube map given the coordinate vector |
||||||
|
* @param vector the direction vector to fetch the texel |
||||||
|
* @param store the color in which to store the pixel color read. |
||||||
|
* @return the color of the pixel read. |
||||||
|
*/ |
||||||
|
public ColorRGBA getPixel(Vector3f vector, ColorRGBA store) { |
||||||
|
|
||||||
|
if (store == null) { |
||||||
|
store = new ColorRGBA(); |
||||||
|
} |
||||||
|
|
||||||
|
int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[0], uvs, EnvMapUtils.FixSeamsMethod.Stretch); |
||||||
|
raster.setSlice(face); |
||||||
|
return raster.getPixel((int) uvs.x, (int) uvs.y, store); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Reads a pixel from the cube map given the coordinate vector |
||||||
|
* @param vector the direction vector to fetch the texel |
||||||
|
* @param mipLevel the mip level to read from |
||||||
|
* @param store the color in which to store the pixel color read. |
||||||
|
* @return the color of the pixel read. |
||||||
|
*/ |
||||||
|
public ColorRGBA getPixel(Vector3f vector, int mipLevel, ColorRGBA store) { |
||||||
|
if (mipMapRaster == null) { |
||||||
|
throw new IllegalArgumentException("This cube map has no mip maps"); |
||||||
|
} |
||||||
|
if (store == null) { |
||||||
|
store = new ColorRGBA(); |
||||||
|
} |
||||||
|
|
||||||
|
int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[mipLevel], uvs, EnvMapUtils.FixSeamsMethod.Stretch); |
||||||
|
mipMapRaster.setSlice(face); |
||||||
|
mipMapRaster.setMipLevel(mipLevel); |
||||||
|
return mipMapRaster.getPixel((int) uvs.x, (int) uvs.y, store); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads a pixel from the cube map given the 2D coordinates and the face to read from |
||||||
|
* @param x the x tex coordinate (from 0 to width) |
||||||
|
* @param y the y tex coordinate (from 0 to height) |
||||||
|
* @param face the face to read from |
||||||
|
* @param store the color where the result is stored. |
||||||
|
* @return the color read. |
||||||
|
*/ |
||||||
|
public ColorRGBA getPixel(int x, int y, int face, ColorRGBA store) { |
||||||
|
if (store == null) { |
||||||
|
store = new ColorRGBA(); |
||||||
|
} |
||||||
|
raster.setSlice(face); |
||||||
|
return raster.getPixel((int) x, (int) y, store); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads a pixel from the cube map given the 2D coordinates and the face and |
||||||
|
* the mip level to read from |
||||||
|
* @param x the x tex coordinate (from 0 to width) |
||||||
|
* @param y the y tex coordinate (from 0 to height) |
||||||
|
* @param face the face to read from |
||||||
|
* @param mipLevel the miplevel to read from |
||||||
|
* @param store the color where the result is stored. |
||||||
|
* @return the color read. |
||||||
|
*/ |
||||||
|
public ColorRGBA getPixel(int x, int y, int face, int mipLevel, ColorRGBA store) { |
||||||
|
if (mipMapRaster == null) { |
||||||
|
throw new IllegalArgumentException("This cube map has no mip maps"); |
||||||
|
} |
||||||
|
if (store == null) { |
||||||
|
store = new ColorRGBA(); |
||||||
|
} |
||||||
|
mipMapRaster.setSlice(face); |
||||||
|
mipMapRaster.setMipLevel(mipLevel); |
||||||
|
return mipMapRaster.getPixel((int) x, (int) y, store); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* writes a pixel given the coordinates vector and the color. |
||||||
|
* @param vector the cooredinates where to write the pixel |
||||||
|
* @param color the color to write |
||||||
|
*/ |
||||||
|
public void setPixel(Vector3f vector, ColorRGBA color) { |
||||||
|
|
||||||
|
int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[0], uvs, EnvMapUtils.FixSeamsMethod.Stretch); |
||||||
|
raster.setSlice(face); |
||||||
|
raster.setPixel((int) uvs.x, (int) uvs.y, color); |
||||||
|
} |
||||||
|
/** |
||||||
|
* writes a pixel given the coordinates vector, the mip level and the color. |
||||||
|
* @param vector the cooredinates where to write the pixel |
||||||
|
* @param mipLevel the miplevel to write to |
||||||
|
* @param color the color to write |
||||||
|
*/ |
||||||
|
public void setPixel(Vector3f vector, int mipLevel, ColorRGBA color) { |
||||||
|
if (mipMapRaster == null) { |
||||||
|
throw new IllegalArgumentException("This cube map has no mip maps"); |
||||||
|
} |
||||||
|
int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[mipLevel], uvs, EnvMapUtils.FixSeamsMethod.Stretch); |
||||||
|
mipMapRaster.setSlice(face); |
||||||
|
mipMapRaster.setMipLevel(mipLevel); |
||||||
|
mipMapRaster.setPixel((int) uvs.x, (int) uvs.y, color); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a pixel given the 2D cordinates and the color |
||||||
|
* @param x the x tex coord (from 0 to width) |
||||||
|
* @param y the y tex coord (from 0 to height) |
||||||
|
* @param face the face to write to |
||||||
|
* @param color the color to write |
||||||
|
*/ |
||||||
|
public void setPixel(int x, int y, int face, ColorRGBA color) { |
||||||
|
raster.setSlice(face); |
||||||
|
raster.setPixel((int) x, (int) y, color); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a pixel given the 2D cordinates, the mip level and the color |
||||||
|
* @param x the x tex coord (from 0 to width) |
||||||
|
* @param y the y tex coord (from 0 to height) |
||||||
|
* @param face the face to write to |
||||||
|
* @param mipLevel the mip level to write to |
||||||
|
* @param color the color to write |
||||||
|
*/ |
||||||
|
public void setPixel(int x, int y, int face, int mipLevel, ColorRGBA color) { |
||||||
|
if (mipMapRaster == null) { |
||||||
|
throw new IllegalArgumentException("This cube map has no mip maps"); |
||||||
|
} |
||||||
|
|
||||||
|
mipMapRaster.setSlice(face); |
||||||
|
mipMapRaster.setMipLevel(mipLevel); |
||||||
|
mipMapRaster.setPixel((int) x, (int) y, color); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Inits the mip maps of a cube map witht he given number of mip maps |
||||||
|
* @param nbMipMaps the number of mip maps to initialize |
||||||
|
*/ |
||||||
|
public void initMipMaps(int nbMipMaps) { |
||||||
|
int maxMipMap = (int) (Math.log(image.getWidth()) / Math.log(2) + 1); |
||||||
|
if (nbMipMaps > maxMipMap) { |
||||||
|
throw new IllegalArgumentException("Max mip map number for a " + image.getWidth() + "x" + image.getHeight() + " cube map is " + maxMipMap); |
||||||
|
} |
||||||
|
|
||||||
|
sizes = new int[nbMipMaps]; |
||||||
|
|
||||||
|
int totalSize = 0; |
||||||
|
for (int i = 0; i < nbMipMaps; i++) { |
||||||
|
int size = (int) pow(2, maxMipMap - 1 - i); |
||||||
|
sizes[i] = size * size * image.getFormat().getBitsPerPixel() / 8; |
||||||
|
totalSize += sizes[i]; |
||||||
|
} |
||||||
|
|
||||||
|
image.setMipMapSizes(sizes); |
||||||
|
image.getData().clear(); |
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
image.addData(BufferUtils.createByteBuffer(totalSize)); |
||||||
|
} |
||||||
|
mipMapRaster = new MipMapImageRaster(image, 0); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,947 @@ |
|||||||
|
/* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.math.Vector4f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.shape.Quad; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.ui.Picture; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import static com.jme3.math.FastMath.*; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* This class holds several utility method unseful for Physically Based |
||||||
|
* Rendering. It alloaws to compute useful pre filtered maps from an env map. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class EnvMapUtils { |
||||||
|
|
||||||
|
public final static int NUM_SH_COEFFICIENT = 9; |
||||||
|
// See Peter-Pike Sloan paper for these coefficients
|
||||||
|
//http://www.ppsloan.org/publications/StupidSH36.pdf
|
||||||
|
public static float[] shBandFactor = {1.0f, |
||||||
|
2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f, |
||||||
|
1.0f / 4.0f, 1.0f / 4.0f, 1.0f / 4.0f, 1.0f / 4.0f, 1.0f / 4.0f}; |
||||||
|
|
||||||
|
public static enum FixSeamsMethod { |
||||||
|
|
||||||
|
/** |
||||||
|
* wrap texture coordinates |
||||||
|
*/ |
||||||
|
Wrap, |
||||||
|
/** |
||||||
|
* stretch texture coordinates |
||||||
|
*/ |
||||||
|
Stretch, |
||||||
|
/** |
||||||
|
* No seams fix |
||||||
|
*/ |
||||||
|
None; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a cube map from 6 images |
||||||
|
* |
||||||
|
* @param leftImg the west side image, also called negative x (negX) or left |
||||||
|
* image |
||||||
|
* @param rightImg the east side image, also called positive x (posX) or |
||||||
|
* right image |
||||||
|
* @param downImg the bottom side image, also called negative y (negY) or |
||||||
|
* down image |
||||||
|
* @param upImg the up side image, also called positive y (posY) or up image |
||||||
|
* @param backImg the south side image, also called positive z (posZ) or |
||||||
|
* back image |
||||||
|
* @param frontImg the north side image, also called negative z (negZ) or |
||||||
|
* front image |
||||||
|
* @param format the format of the image |
||||||
|
* @return a cube map |
||||||
|
*/ |
||||||
|
public static TextureCubeMap makeCubeMap(Image rightImg, Image leftImg, Image upImg, Image downImg, Image backImg, Image frontImg, Image.Format format) { |
||||||
|
Image cubeImage = new Image(format, leftImg.getWidth(), leftImg.getHeight(), null, ColorSpace.Linear); |
||||||
|
|
||||||
|
cubeImage.addData(rightImg.getData(0)); |
||||||
|
cubeImage.addData(leftImg.getData(0)); |
||||||
|
|
||||||
|
cubeImage.addData(upImg.getData(0)); |
||||||
|
cubeImage.addData(downImg.getData(0)); |
||||||
|
|
||||||
|
cubeImage.addData(backImg.getData(0)); |
||||||
|
cubeImage.addData(frontImg.getData(0)); |
||||||
|
|
||||||
|
if (leftImg.getEfficentData() != null) { |
||||||
|
// also consilidate efficient data
|
||||||
|
ArrayList<Object> efficientData = new ArrayList<Object>(6); |
||||||
|
efficientData.add(rightImg.getEfficentData()); |
||||||
|
efficientData.add(leftImg.getEfficentData()); |
||||||
|
efficientData.add(upImg.getEfficentData()); |
||||||
|
efficientData.add(downImg.getEfficentData()); |
||||||
|
efficientData.add(backImg.getEfficentData()); |
||||||
|
efficientData.add(frontImg.getEfficentData()); |
||||||
|
cubeImage.setEfficentData(efficientData); |
||||||
|
} |
||||||
|
|
||||||
|
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); |
||||||
|
cubeMap.setAnisotropicFilter(0); |
||||||
|
cubeMap.setMagFilter(Texture.MagFilter.Bilinear); |
||||||
|
cubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||||
|
cubeMap.setWrap(Texture.WrapMode.EdgeClamp); |
||||||
|
|
||||||
|
return cubeMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Make a duplicate of this cube Map. That means that it's another instant |
||||||
|
* od TextureCubeMap, but the underlying buffers are duplicates of the |
||||||
|
* original ones. see {@link ByteBuffer#duplicate()} |
||||||
|
* |
||||||
|
* Use this if you need to read from the map from multiple threads, it |
||||||
|
* should garanty the thread safety. Note that if you want to write to the |
||||||
|
* cube map you have to make sure that the different thread do not write to |
||||||
|
* the same area of the buffer. The position, limit and mark are not an |
||||||
|
* issue. |
||||||
|
* |
||||||
|
* @param sourceMap |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static TextureCubeMap duplicateCubeMap(TextureCubeMap sourceMap) { |
||||||
|
Image srcImg = sourceMap.getImage(); |
||||||
|
Image cubeImage = new Image(srcImg.getFormat(), srcImg.getWidth(), srcImg.getHeight(), null, srcImg.getColorSpace()); |
||||||
|
|
||||||
|
for (ByteBuffer d : srcImg.getData()) { |
||||||
|
cubeImage.addData(d.duplicate()); |
||||||
|
} |
||||||
|
|
||||||
|
if (srcImg.getEfficentData() != null) { |
||||||
|
// also consilidate efficient data
|
||||||
|
ArrayList<Object> efficientData = new ArrayList<Object>(6); |
||||||
|
efficientData.add(srcImg.getEfficentData()); |
||||||
|
cubeImage.setEfficentData(efficientData); |
||||||
|
} |
||||||
|
|
||||||
|
TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); |
||||||
|
cubeMap.setAnisotropicFilter(sourceMap.getAnisotropicFilter()); |
||||||
|
cubeMap.setMagFilter(sourceMap.getMagFilter()); |
||||||
|
cubeMap.setMinFilter(sourceMap.getMinFilter()); |
||||||
|
cubeMap.setWrap(sourceMap.getWrap(Texture.WrapAxis.S)); |
||||||
|
|
||||||
|
return cubeMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes the vector coordinates, for the given x,y texture coordinates |
||||||
|
* and the given cube map face. |
||||||
|
* |
||||||
|
* Also computes the solid angle for those coordinates and returns it. |
||||||
|
* |
||||||
|
* To know what the solid angle is please read this. |
||||||
|
* http://www.codinglabs.net/article_physically_based_rendering.aspx
|
||||||
|
* |
||||||
|
* |
||||||
|
* Original solid angle calculation code is from Ignacio Castaño. This |
||||||
|
* formula is from Manne Öhrström's thesis. It takes two coordiantes in the |
||||||
|
* range [-1, 1] that define a portion of a cube face and return the area of |
||||||
|
* the projection of that portion on the surface of the sphere. |
||||||
|
* |
||||||
|
* @param x texture coordinate from 0 to 1 in the given cube map face |
||||||
|
* @param y texture coordinate from 0 to 1 in the given cube map face |
||||||
|
* @param mapSize the size of the cube map |
||||||
|
* @param face the face id of the cube map |
||||||
|
* @param store the vector3f where the vector will be stored. don't provide |
||||||
|
* null for this param |
||||||
|
* @return the solid angle for the give parameters |
||||||
|
*/ |
||||||
|
static float getSolidAngleAndVector(int x, int y, int mapSize, int face, Vector3f store, FixSeamsMethod fixSeamsMethod) { |
||||||
|
|
||||||
|
if (store == null) { |
||||||
|
throw new IllegalArgumentException("the store parameter ust not be null"); |
||||||
|
} |
||||||
|
|
||||||
|
/* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)] |
||||||
|
(+ 0.5f is for texel center addressing) */ |
||||||
|
float u = (2.0f * ((float) x + 0.5f) / (float) mapSize) - 1.0f; |
||||||
|
float v = (2.0f * ((float) y + 0.5f) / (float) mapSize) - 1.0f; |
||||||
|
|
||||||
|
getVectorFromCubemapFaceTexCoord(x, y, mapSize, face, store, fixSeamsMethod); |
||||||
|
|
||||||
|
/* Solid angle weight approximation : |
||||||
|
* U and V are the -1..1 texture coordinate on the current face. |
||||||
|
* Get projected area for this texel */ |
||||||
|
float x0, y0, x1, y1; |
||||||
|
float invRes = 1.0f / (float) mapSize; |
||||||
|
x0 = u - invRes; |
||||||
|
y0 = v - invRes; |
||||||
|
x1 = u + invRes; |
||||||
|
y1 = v + invRes; |
||||||
|
|
||||||
|
return areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* used to compute the solid angle |
||||||
|
* |
||||||
|
* @param x tex coordinates |
||||||
|
* @param y tex coordinates |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static float areaElement(float x, float y) { |
||||||
|
return (float) Math.atan2(x * y, sqrt(x * x + y * y + 1)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Computes the 3 component vector coordinates for the given face and coords |
||||||
|
* |
||||||
|
* @param x the x texture coordinate |
||||||
|
* @param y the y texture coordinate |
||||||
|
* @param mapSize the size of a face of the cube map |
||||||
|
* @param face the face to consider |
||||||
|
* @param store a vector3f where the resulting vector will be stored |
||||||
|
* @param fixSeamsMethod the method to fix the seams |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Vector3f getVectorFromCubemapFaceTexCoord(int x, int y, int mapSize, int face, Vector3f store, FixSeamsMethod fixSeamsMethod) { |
||||||
|
if (store == null) { |
||||||
|
store = new Vector3f(); |
||||||
|
} |
||||||
|
|
||||||
|
float u; |
||||||
|
float v; |
||||||
|
|
||||||
|
if (fixSeamsMethod == FixSeamsMethod.Stretch) { |
||||||
|
/* Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp
|
||||||
|
* transform from [0..res - 1] to [-1 .. 1], match up edges exactly. */ |
||||||
|
u = (2.0f * (float) x / ((float) mapSize - 1.0f)) - 1.0f; |
||||||
|
v = (2.0f * (float) y / ((float) mapSize - 1.0f)) - 1.0f; |
||||||
|
} else { |
||||||
|
//Done if any other fix method or no fix method is set
|
||||||
|
/* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)] |
||||||
|
* (+ 0.5f is for texel center addressing) */ |
||||||
|
u = (2.0f * ((float) x + 0.5f) / (float) (mapSize)) - 1.0f; |
||||||
|
v = (2.0f * ((float) y + 0.5f) / (float) (mapSize)) - 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
if (fixSeamsMethod == FixSeamsMethod.Wrap) { |
||||||
|
// Warp texel centers in the proximity of the edges.
|
||||||
|
float a = pow((float) mapSize, 2.0f) / pow(((float) mapSize - 1f), 3.0f); |
||||||
|
u = a * pow(u, 3f) + u; |
||||||
|
v = a * pow(v, 3f) + v; |
||||||
|
} |
||||||
|
|
||||||
|
//compute vector depending on the face
|
||||||
|
// Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp
|
||||||
|
switch (face) { |
||||||
|
case 0: |
||||||
|
store.set(1f, -v, -u); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
store.set(-1f, -v, u); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
store.set(u, 1f, v); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
store.set(u, -1f, -v); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
store.set(u, -v, 1f); |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
store.set(-u, -v, -1.0f); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return store.normalizeLocal(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Computes the texture coortinates and the face of the cube map from the |
||||||
|
* given vector |
||||||
|
* |
||||||
|
* @param texelVect the vector to fetch texelt from the cube map |
||||||
|
* @param fixSeamsMethod the method to fix the seams |
||||||
|
* @param mapSize the size of one face of the cube map |
||||||
|
* @param store a Vector2f where the texture coordinates will be stored |
||||||
|
* @return the face from which to fetch the texel |
||||||
|
*/ |
||||||
|
public static int getCubemapFaceTexCoordFromVector(Vector3f texelVect, int mapSize, Vector2f store, FixSeamsMethod fixSeamsMethod) { |
||||||
|
|
||||||
|
float u = 0, v = 0, bias = 0; |
||||||
|
int face; |
||||||
|
float absX = abs(texelVect.x); |
||||||
|
float absY = abs(texelVect.y); |
||||||
|
float absZ = abs(texelVect.z); |
||||||
|
float max = Math.max(Math.max(absX, absY), absZ); |
||||||
|
if (max == absX) { |
||||||
|
face = texelVect.x > 0 ? 0 : 1; |
||||||
|
} else if (max == absY) { |
||||||
|
face = texelVect.y > 0 ? 2 : 3; |
||||||
|
} else { |
||||||
|
face = texelVect.z > 0 ? 4 : 5; |
||||||
|
} |
||||||
|
|
||||||
|
//compute vector depending on the face
|
||||||
|
// Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp
|
||||||
|
switch (face) { |
||||||
|
case 0: |
||||||
|
//store.set(1f, -v, -u, 0);
|
||||||
|
bias = 1f / texelVect.x; |
||||||
|
u = -texelVect.z; |
||||||
|
v = -texelVect.y; |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
// store.set(-1f, -v, u, 0);
|
||||||
|
bias = -1f / texelVect.x; |
||||||
|
u = texelVect.z; |
||||||
|
v = -texelVect.y; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
//store.set(u, 1f, v, 0);
|
||||||
|
bias = 1f / texelVect.y; |
||||||
|
u = texelVect.x; |
||||||
|
v = texelVect.z; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
//store.set(u, -1f, -v, 0);
|
||||||
|
bias = -1f / texelVect.y; |
||||||
|
u = texelVect.x; |
||||||
|
v = -texelVect.z; |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
//store.set(u, -v, 1f, 0);
|
||||||
|
bias = 1f / texelVect.z; |
||||||
|
u = texelVect.x; |
||||||
|
v = -texelVect.y; |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
//store.set(-u, -v, -1.0f, 0);
|
||||||
|
bias = -1f / texelVect.z; |
||||||
|
u = -texelVect.x; |
||||||
|
v = -texelVect.y; |
||||||
|
break; |
||||||
|
} |
||||||
|
u *= bias; |
||||||
|
v *= bias; |
||||||
|
|
||||||
|
if (fixSeamsMethod == FixSeamsMethod.Stretch) { |
||||||
|
/* Code from Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp
|
||||||
|
* transform from [0..res - 1] to [-1 .. 1], match up edges exactly. */ |
||||||
|
u = Math.round((u + 1.0f) * ((float) mapSize - 1.0f) * 0.5f); |
||||||
|
v = Math.round((v + 1.0f) * ((float) mapSize - 1.0f) * 0.5f); |
||||||
|
} else { |
||||||
|
//Done if any other fix method or no fix method is set
|
||||||
|
/* transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)] |
||||||
|
* (+ 0.5f is for texel center addressing) */ |
||||||
|
u = Math.round((u + 1.0f) * ((float) mapSize) * 0.5f - 0.5f); |
||||||
|
v = Math.round((v + 1.0f) * ((float) mapSize) * 0.5f - 0.5f); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
store.set(u, v); |
||||||
|
return face; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
public static void main(String... argv) { |
||||||
|
|
||||||
|
// for (int givenFace = 0; givenFace < 6; givenFace++) {
|
||||||
|
//
|
||||||
|
// //int givenFace = 1;
|
||||||
|
// for (int x = 0; x < 128; x++) {
|
||||||
|
// for (int y = 0; y < 128; y++) {
|
||||||
|
// Vector3f v = EnvMapUtils.getVectorFromCubemapFaceTexCoord(x, y, 128, givenFace, null, FixSeamsMethod.None);
|
||||||
|
// Vector2f uvs = new Vector2f();
|
||||||
|
// int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(v, 128, uvs, FixSeamsMethod.None);
|
||||||
|
//
|
||||||
|
// if ((int) uvs.x != x || (int) uvs.y != y) {
|
||||||
|
// System.err.println("error " + uvs + " should be " + x + "," + y + " vect was " + v);
|
||||||
|
// }
|
||||||
|
// if (givenFace != face) {
|
||||||
|
// System.err.println("error face: " + face + " should be " + givenFace);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// System.err.println("done ");
|
||||||
|
int total = 0; |
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
int size = (int) pow(2, 7 - i); |
||||||
|
int samples = EnvMapUtils.getSampleFromMip(i, 6); |
||||||
|
int iterations = (samples * size * size); |
||||||
|
total += iterations; |
||||||
|
float roughness = EnvMapUtils.getRoughnessFromMip(i, 6); |
||||||
|
System.err.println("roughness " + i + " : " + roughness + " , map : " + size + " , samples : " + samples + " , iterations : " + iterations); |
||||||
|
System.err.println("reverse " + EnvMapUtils.getMipFromRoughness(roughness, 6)); |
||||||
|
|
||||||
|
} |
||||||
|
System.err.println("total " + total); |
||||||
|
System.err.println(128 * 128 * 1024); |
||||||
|
System.err.println("test " + EnvMapUtils.getMipFromRoughness(0.9999f, 6)); |
||||||
|
System.err.println("nb mip = " + (Math.log(128) / Math.log(2) - 1)); |
||||||
|
|
||||||
|
}*/ |
||||||
|
|
||||||
|
public static int getSampleFromMip(int mipLevel, int miptot) { |
||||||
|
return mipLevel==0?1:Math.min(1 << (miptot - 1 + (mipLevel) * 2 ), 8192); |
||||||
|
} |
||||||
|
|
||||||
|
public static float getRoughnessFromMip(int miplevel, int miptot) { |
||||||
|
float mipScale = 1.0f; |
||||||
|
float mipOffset = -0.3f; |
||||||
|
|
||||||
|
return pow(2, (miplevel - (miptot - 1) + mipOffset) / mipScale); |
||||||
|
} |
||||||
|
|
||||||
|
public static float getMipFromRoughness(float roughness, int miptot) { |
||||||
|
float mipScale = 1.0f; |
||||||
|
float Lod = (float) (Math.log(roughness) / Math.log(2)) * mipScale + miptot - 1.0f; |
||||||
|
|
||||||
|
return (float) Math.max(0.0, Lod); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* same as |
||||||
|
* {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap, com.jme3.utils.EnvMapUtils.FixSeamsMethod)} |
||||||
|
* the fix method used is {@link FixSeamsMethod#Wrap} |
||||||
|
* |
||||||
|
* @param cubeMap the environment cube map to compute SH for |
||||||
|
* @return an array of 9 vector3f representing thos coefficients for each |
||||||
|
* r,g,b channnel |
||||||
|
*/ |
||||||
|
public static Vector3f[] getSphericalHarmonicsCoefficents(TextureCubeMap cubeMap) { |
||||||
|
return getSphericalHarmonicsCoefficents(cubeMap, FixSeamsMethod.Wrap); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Spherical Harmonics coefficients for this cube map. |
||||||
|
* |
||||||
|
* The method used is the one from this article : |
||||||
|
* http://graphics.stanford.edu/papers/envmap/envmap.pdf
|
||||||
|
* |
||||||
|
* Also good resources on spherical harmonics |
||||||
|
* http://dickyjim.wordpress.com/2013/09/04/spherical-harmonics-for-beginners/
|
||||||
|
* |
||||||
|
* @param cubeMap the environment cube map to compute SH for |
||||||
|
* @param fixSeamsMethod method to fix seams when computing the SH |
||||||
|
* coefficients |
||||||
|
* @return an array of 9 vector3f representing thos coefficients for each |
||||||
|
* r,g,b channnel |
||||||
|
*/ |
||||||
|
public static Vector3f[] getSphericalHarmonicsCoefficents(TextureCubeMap cubeMap, FixSeamsMethod fixSeamsMethod) { |
||||||
|
|
||||||
|
Vector3f[] shCoef = new Vector3f[NUM_SH_COEFFICIENT]; |
||||||
|
|
||||||
|
float[] shDir = new float[9]; |
||||||
|
float weightAccum = 0.0f; |
||||||
|
float weight; |
||||||
|
|
||||||
|
if (cubeMap.getImage().getData(0) == null) { |
||||||
|
throw new IllegalStateException("The cube map must contain Efficient data, if you rendered the cube map on the GPU plase use renderer.readFrameBuffer, to create a CPU image"); |
||||||
|
} |
||||||
|
|
||||||
|
int width = cubeMap.getImage().getWidth(); |
||||||
|
int height = cubeMap.getImage().getHeight(); |
||||||
|
|
||||||
|
Vector3f texelVect = new Vector3f(); |
||||||
|
ColorRGBA color = new ColorRGBA(); |
||||||
|
|
||||||
|
CubeMapWrapper envMapReader = new CubeMapWrapper(cubeMap); |
||||||
|
for (int face = 0; face < 6; face++) { |
||||||
|
for (int y = 0; y < height; y++) { |
||||||
|
for (int x = 0; x < width; x++) { |
||||||
|
|
||||||
|
weight = getSolidAngleAndVector(x, y, width, face, texelVect, fixSeamsMethod); |
||||||
|
|
||||||
|
evalShBasis(texelVect, shDir); |
||||||
|
|
||||||
|
envMapReader.getPixel(x, y, face, color); |
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SH_COEFFICIENT; i++) { |
||||||
|
|
||||||
|
if (shCoef[i] == null) { |
||||||
|
shCoef[i] = new Vector3f(); |
||||||
|
} |
||||||
|
|
||||||
|
shCoef[i].setX(shCoef[i].x + color.r * shDir[i] * weight); |
||||||
|
shCoef[i].setY(shCoef[i].y + color.g * shDir[i] * weight); |
||||||
|
shCoef[i].setZ(shCoef[i].z + color.b * shDir[i] * weight); |
||||||
|
} |
||||||
|
|
||||||
|
weightAccum += weight; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Normalization - The sum of solid angle should be equal to the solid angle of the sphere (4 PI), so |
||||||
|
* normalize in order our weightAccum exactly match 4 PI. */ |
||||||
|
for (int i = 0; i < NUM_SH_COEFFICIENT; ++i) { |
||||||
|
shCoef[i].multLocal(4.0f * PI / weightAccum); |
||||||
|
} |
||||||
|
return shCoef; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes SH coefficient for a given textel dir The method used is the one |
||||||
|
* from this article : http://graphics.stanford.edu/papers/envmap/envmap.pdf
|
||||||
|
* |
||||||
|
* @param texelVect |
||||||
|
* @param shDir |
||||||
|
*/ |
||||||
|
public static void evalShBasis(Vector3f texelVect, float[] shDir) { |
||||||
|
|
||||||
|
float xV = texelVect.x; |
||||||
|
float yV = texelVect.y; |
||||||
|
float zV = texelVect.z; |
||||||
|
|
||||||
|
float pi = PI; |
||||||
|
float sqrtPi = sqrt(pi); |
||||||
|
float sqrt3Pi = sqrt(3f / pi); |
||||||
|
float sqrt5Pi = sqrt(5f / pi); |
||||||
|
float sqrt15Pi = sqrt(15f / pi); |
||||||
|
|
||||||
|
float x2 = xV * xV; |
||||||
|
float y2 = yV * yV; |
||||||
|
float z2 = zV * zV; |
||||||
|
|
||||||
|
shDir[0] = (1f / (2f * sqrtPi)); |
||||||
|
shDir[1] = -(sqrt3Pi * yV) / 2f; |
||||||
|
shDir[2] = (sqrt3Pi * zV) / 2f; |
||||||
|
shDir[3] = -(sqrt3Pi * xV) / 2f; |
||||||
|
shDir[4] = (sqrt15Pi * xV * yV) / 2f; |
||||||
|
shDir[5] = -(sqrt15Pi * yV * zV) / 2f; |
||||||
|
shDir[6] = (sqrt5Pi * (-1f + 3f * z2)) / 4f; |
||||||
|
shDir[7] = -(sqrt15Pi * xV * zV) / 2f; |
||||||
|
shDir[8] = sqrt15Pi * (x2 - y2) / 4f; |
||||||
|
|
||||||
|
// shDir[0] = (1f/(2.f*sqrtPi));
|
||||||
|
//
|
||||||
|
// shDir[1] = -(sqrt(3f/pi)*yV)/2.f;
|
||||||
|
// shDir[2] = (sqrt(3/pi)*zV)/2.f;
|
||||||
|
// shDir[3] = -(sqrt(3/pi)*xV)/2.f;
|
||||||
|
//
|
||||||
|
// shDir[4] = (sqrt(15f/pi)*xV*yV)/2.f;
|
||||||
|
// shDir[5] = -(sqrt(15f/pi)*yV*zV)/2.f;
|
||||||
|
// shDir[6] = (sqrt(5f/pi)*(-1 + 3f*z2))/4.f;
|
||||||
|
// shDir[7] = -(sqrt(15f/pi)*xV*zV)/2.f;
|
||||||
|
// shDir[8] = sqrt(15f/pi)*(x2 - y2)/4.f;
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link EnvMapUtils#generateIrradianceMap(com.jme3.math.Vector3f[], com.jme3.texture.TextureCubeMap, int, com.jme3.utils.EnvMapUtils.FixSeamsMethod) |
||||||
|
* } |
||||||
|
* |
||||||
|
* @param shCoeffs the spherical harmonics coefficients to use |
||||||
|
* @param targetMapSize the size of the target map |
||||||
|
* @return the irradiance map. |
||||||
|
*/ |
||||||
|
public static TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize) { |
||||||
|
return generateIrradianceMap(shCoeffs, targetMapSize, FixSeamsMethod.Wrap, 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)} |
||||||
|
* Note that the output cube map is in RGBA8 format. |
||||||
|
* |
||||||
|
* @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 static TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize, FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { |
||||||
|
TextureCubeMap irrCubeMap = store; |
||||||
|
if (irrCubeMap == null) { |
||||||
|
irrCubeMap = new TextureCubeMap(targetMapSize, targetMapSize, Image.Format.RGB16F); |
||||||
|
irrCubeMap.setMagFilter(Texture.MagFilter.Bilinear); |
||||||
|
irrCubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||||
|
irrCubeMap.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
ByteBuffer buf = BufferUtils.createByteBuffer(targetMapSize * targetMapSize * irrCubeMap.getImage().getFormat().getBitsPerPixel()/8); |
||||||
|
irrCubeMap.getImage().setData(i, buf); |
||||||
|
} |
||||||
|
|
||||||
|
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++) { |
||||||
|
getVectorFromCubemapFaceTexCoord(x, y, targetMapSize, face, texelVect, fixSeamsMethod); |
||||||
|
evalShBasis(texelVect, shDir); |
||||||
|
color.set(0, 0, 0, 0); |
||||||
|
for (int i = 0; i < 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return irrCubeMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates the prefiltered env map (used for image based specular |
||||||
|
* lighting) With the GGX/Shlick brdf |
||||||
|
* {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)} |
||||||
|
* Note that the output cube map is in RGBA8 format. |
||||||
|
* |
||||||
|
* @param sourceEnvMap |
||||||
|
* @param targetMapSize the size of the irradiance map to generate |
||||||
|
* @param store |
||||||
|
* @param fixSeamsMethod the method to fix seams |
||||||
|
* @return The irradiance cube map for the given coefficients |
||||||
|
*/ |
||||||
|
public static TextureCubeMap generatePrefilteredEnvMap(TextureCubeMap sourceEnvMap, int targetMapSize, FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { |
||||||
|
TextureCubeMap pem = store; |
||||||
|
if (pem == null) { |
||||||
|
pem = new TextureCubeMap(targetMapSize, targetMapSize, Image.Format.RGB16F); |
||||||
|
pem.setMagFilter(Texture.MagFilter.Bilinear); |
||||||
|
pem.setMinFilter(Texture.MinFilter.Trilinear); |
||||||
|
pem.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
} |
||||||
|
|
||||||
|
int nbMipMap = (int) (Math.log(targetMapSize) / Math.log(2) - 1); |
||||||
|
|
||||||
|
CubeMapWrapper sourceWrapper = new CubeMapWrapper(sourceEnvMap); |
||||||
|
CubeMapWrapper targetWrapper = new CubeMapWrapper(pem); |
||||||
|
targetWrapper.initMipMaps(nbMipMap); |
||||||
|
|
||||||
|
Vector3f texelVect = new Vector3f(); |
||||||
|
Vector3f color = new Vector3f(); |
||||||
|
ColorRGBA outColor = new ColorRGBA(); |
||||||
|
for (int mipLevel = 0; mipLevel < nbMipMap; mipLevel++) { |
||||||
|
System.err.println("mip level " + mipLevel); |
||||||
|
float roughness = getRoughnessFromMip(mipLevel, nbMipMap); |
||||||
|
int nbSamples = getSampleFromMip(mipLevel, nbMipMap); |
||||||
|
int targetMipMapSize = (int) pow(2, nbMipMap + 1 - mipLevel); |
||||||
|
for (int face = 0; face < 6; face++) { |
||||||
|
System.err.println("face " + face); |
||||||
|
for (int y = 0; y < targetMipMapSize; y++) { |
||||||
|
for (int x = 0; x < targetMipMapSize; x++) { |
||||||
|
color.set(0, 0, 0); |
||||||
|
getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, FixSeamsMethod.Wrap); |
||||||
|
prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color); |
||||||
|
outColor.set(color.x, color.y, color.z, 1.0f); |
||||||
|
// System.err.println("coords " + x + "," + y);
|
||||||
|
targetWrapper.setPixel(x, y, face, mipLevel, outColor); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return pem; |
||||||
|
} |
||||||
|
|
||||||
|
public static Vector4f getHammersleyPoint(int i, final int nbrSample, Vector4f store) { |
||||||
|
if (store == null) { |
||||||
|
store = new Vector4f(); |
||||||
|
} |
||||||
|
float phi; |
||||||
|
long ui = i; |
||||||
|
store.setX((float) i / (float) nbrSample); |
||||||
|
|
||||||
|
/* From http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
|
||||||
|
* Radical Inverse : Van der Corput */ |
||||||
|
ui = (ui << 16) | (ui >> 16); |
||||||
|
ui = ((ui & 0x55555555) << 1) | ((ui & 0xAAAAAAAA) >>> 1); |
||||||
|
ui = ((ui & 0x33333333) << 2) | ((ui & 0xCCCCCCCC) >>> 2); |
||||||
|
ui = ((ui & 0x0F0F0F0F) << 4) | ((ui & 0xF0F0F0F0) >>> 4); |
||||||
|
ui = ((ui & 0x00FF00FF) << 8) | ((ui & 0xFF00FF00) >>> 8); |
||||||
|
|
||||||
|
ui = ui & 0xffffffff; |
||||||
|
store.setY(2.3283064365386963e-10f * (float) (ui)); /* 0x100000000 */ |
||||||
|
|
||||||
|
phi = 2.0f * PI * store.y; |
||||||
|
store.setZ(cos(phi)); |
||||||
|
store.setW(sin(phi)); |
||||||
|
|
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
private static Vector3f prefilterEnvMapTexel(CubeMapWrapper envMapReader, float roughness, Vector3f N, int numSamples, Vector3f store) { |
||||||
|
|
||||||
|
Vector3f prefilteredColor = store; |
||||||
|
float totalWeight = 0.0f; |
||||||
|
|
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
Vector4f Xi = vars.vect4f1; |
||||||
|
Vector3f H = vars.vect1; |
||||||
|
Vector3f tmp = vars.vect2; |
||||||
|
ColorRGBA c = vars.color; |
||||||
|
// a = roughness² and a2 = a²
|
||||||
|
float a2 = roughness * roughness; |
||||||
|
a2 *= a2; |
||||||
|
a2 *= 10; |
||||||
|
for (int i = 0; i < numSamples; i++) { |
||||||
|
Xi = getHammersleyPoint(i, numSamples, Xi); |
||||||
|
H = importanceSampleGGX(Xi, a2, N, H, vars); |
||||||
|
|
||||||
|
H.normalizeLocal(); |
||||||
|
tmp.set(H); |
||||||
|
float NoH = N.dot(tmp); |
||||||
|
|
||||||
|
Vector3f L = tmp.multLocal(NoH * 2).subtractLocal(N); |
||||||
|
float NoL = clamp(N.dot(L), 0.0f, 1.0f); |
||||||
|
if (NoL > 0) { |
||||||
|
envMapReader.getPixel(L, c); |
||||||
|
prefilteredColor.setX(prefilteredColor.x + c.r * NoL); |
||||||
|
prefilteredColor.setY(prefilteredColor.y + c.g * NoL); |
||||||
|
prefilteredColor.setZ(prefilteredColor.z + c.b * NoL); |
||||||
|
|
||||||
|
totalWeight += NoL; |
||||||
|
} |
||||||
|
} |
||||||
|
vars.release(); |
||||||
|
return prefilteredColor.divideLocal(totalWeight); |
||||||
|
} |
||||||
|
|
||||||
|
public static Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f normal, Vector3f store, TempVars vars) { |
||||||
|
if (store == null) { |
||||||
|
store = new Vector3f(); |
||||||
|
} |
||||||
|
|
||||||
|
float cosTheta = sqrt((1f - xi.x) / (1f + (a2 - 1f) * xi.x)); |
||||||
|
float sinTheta = sqrt(1f - cosTheta * cosTheta); |
||||||
|
|
||||||
|
float sinThetaCosPhi = sinTheta * xi.z;//xi.z is cos(phi)
|
||||||
|
float sinThetaSinPhi = sinTheta * xi.w;//xi.w is sin(phi)
|
||||||
|
|
||||||
|
Vector3f upVector = Vector3f.UNIT_X; |
||||||
|
|
||||||
|
if (abs(normal.z) < 0.999) { |
||||||
|
upVector = Vector3f.UNIT_Y; |
||||||
|
} |
||||||
|
|
||||||
|
Vector3f tangentX = vars.vect3.set(upVector).crossLocal(normal).normalizeLocal(); |
||||||
|
Vector3f tangentY = vars.vect4.set(normal).crossLocal(tangentX); |
||||||
|
|
||||||
|
// Tangent to world space
|
||||||
|
tangentX.multLocal(sinThetaCosPhi); |
||||||
|
tangentY.multLocal(sinThetaSinPhi); |
||||||
|
vars.vect5.set(normal).multLocal(cosTheta); |
||||||
|
|
||||||
|
// Tangent to world space
|
||||||
|
store.set(tangentX).addLocal(tangentY).addLocal(vars.vect5); |
||||||
|
|
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a debug Node of the given cube map to attach to the gui node |
||||||
|
* |
||||||
|
* the cube map is layered this way : |
||||||
|
* <pre> |
||||||
|
* _____ |
||||||
|
* | | |
||||||
|
* | +Y | |
||||||
|
* _____|_____|_____ _____ |
||||||
|
* | | | | | |
||||||
|
* | -X | +Z | +X | -Z | |
||||||
|
* |_____|_____|_____|_____| |
||||||
|
* | | |
||||||
|
* | -Y | |
||||||
|
* |_____| |
||||||
|
* |
||||||
|
*</pre> |
||||||
|
* |
||||||
|
* @param cubeMap the cube map |
||||||
|
* @param assetManager the asset Manager |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Node getCubeMapCrossDebugView(TextureCubeMap cubeMap, AssetManager assetManager) { |
||||||
|
Node n = new Node("CubeMapDebug" + cubeMap.getName()); |
||||||
|
int size = cubeMap.getImage().getWidth(); |
||||||
|
Picture[] pics = new Picture[6]; |
||||||
|
|
||||||
|
float ratio = 128f / (float) size; |
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
pics[i] = new Picture("bla"); |
||||||
|
Texture2D tex = new Texture2D(new Image(cubeMap.getImage().getFormat(), size, size, cubeMap.getImage().getData(i), cubeMap.getImage().getColorSpace())); |
||||||
|
|
||||||
|
pics[i].setTexture(assetManager, tex, true); |
||||||
|
pics[i].setWidth(size); |
||||||
|
pics[i].setHeight(size); |
||||||
|
n.attachChild(pics[i]); |
||||||
|
} |
||||||
|
|
||||||
|
pics[0].setLocalTranslation(size, size * 2, 1); |
||||||
|
pics[0].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[1].setLocalTranslation(size * 3, size * 2, 1); |
||||||
|
pics[1].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[2].setLocalTranslation(size * 2, size * 3, 1); |
||||||
|
pics[2].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[3].setLocalTranslation(size * 2, size, 1); |
||||||
|
pics[3].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[4].setLocalTranslation(size * 2, size * 2, 1); |
||||||
|
pics[4].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[5].setLocalTranslation(size * 4, size * 2, 1); |
||||||
|
pics[5].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
|
||||||
|
Quad q = new Quad(size * 4, size * 3); |
||||||
|
Geometry g = new Geometry("bg", q); |
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.setColor("Color", ColorRGBA.Black); |
||||||
|
g.setMaterial(mat); |
||||||
|
g.setLocalTranslation(0, 0, 0); |
||||||
|
|
||||||
|
n.attachChild(g); |
||||||
|
n.setLocalScale(ratio); |
||||||
|
return n; |
||||||
|
} |
||||||
|
|
||||||
|
public static Node getCubeMapCrossDebugViewWithMipMaps(TextureCubeMap cubeMap, AssetManager assetManager) { |
||||||
|
Node n = new Node("CubeMapDebug" + cubeMap.getName()); |
||||||
|
int size = cubeMap.getImage().getWidth(); |
||||||
|
int nbMips = cubeMap.getImage().getMipMapSizes().length; |
||||||
|
Picture[] pics = new Picture[6*nbMips]; |
||||||
|
|
||||||
|
float ratio = 1f;// 128f / (float) size;
|
||||||
|
|
||||||
|
int offset = 0; |
||||||
|
int guiOffset = 0; |
||||||
|
for (int mipLevel = 0; mipLevel < nbMips; mipLevel++) { |
||||||
|
size = Math.max(1, cubeMap.getImage().getWidth() >> mipLevel); |
||||||
|
int dataSize = cubeMap.getImage().getMipMapSizes()[mipLevel]; |
||||||
|
byte[] dataArray = new byte[dataSize]; |
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
|
||||||
|
ByteBuffer bb = cubeMap.getImage().getData(i); |
||||||
|
|
||||||
|
bb.rewind(); |
||||||
|
bb.position(offset); |
||||||
|
bb.get(dataArray, 0, dataSize); |
||||||
|
ByteBuffer data = BufferUtils.createByteBuffer(dataArray); |
||||||
|
|
||||||
|
pics[i] = new Picture("bla"); |
||||||
|
Texture2D tex = new Texture2D(new Image(cubeMap.getImage().getFormat(), size, size, data, cubeMap.getImage().getColorSpace())); |
||||||
|
|
||||||
|
pics[i].setTexture(assetManager, tex, true); |
||||||
|
pics[i].setWidth(size); |
||||||
|
pics[i].setHeight(size); |
||||||
|
n.attachChild(pics[i]); |
||||||
|
} |
||||||
|
pics[0].setLocalTranslation(guiOffset + size, guiOffset + size * 2, 1); |
||||||
|
pics[0].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[1].setLocalTranslation(guiOffset + size * 3, guiOffset + size * 2, 1); |
||||||
|
pics[1].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[2].setLocalTranslation(guiOffset + size * 2, guiOffset + size * 3, 1); |
||||||
|
pics[2].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[3].setLocalTranslation(guiOffset + size * 2, guiOffset + size, 1); |
||||||
|
pics[3].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[4].setLocalTranslation(guiOffset + size * 2, guiOffset + size * 2, 1); |
||||||
|
pics[4].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
pics[5].setLocalTranslation(guiOffset + size * 4, guiOffset + size * 2, 1); |
||||||
|
pics[5].setLocalRotation(new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Z)); |
||||||
|
|
||||||
|
guiOffset+=size *2+1; |
||||||
|
offset += dataSize; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Quad q = new Quad(cubeMap.getImage().getWidth() * 4 + nbMips, guiOffset + size); |
||||||
|
Geometry g = new Geometry("bg", q); |
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.setColor("Color", ColorRGBA.Black); |
||||||
|
g.setMaterial(mat); |
||||||
|
g.setLocalTranslation(0, 0, 0); |
||||||
|
|
||||||
|
n.attachChild(g); |
||||||
|
n.setLocalScale(ratio); |
||||||
|
return n; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* initialize the Irradiancemap |
||||||
|
* @param size the size of the map |
||||||
|
* @param imageFormat the format of the image |
||||||
|
* @return the initialized Irradiance map |
||||||
|
*/ |
||||||
|
public static TextureCubeMap createIrradianceMap(int size, Image.Format imageFormat) { |
||||||
|
|
||||||
|
TextureCubeMap irrMap = new TextureCubeMap(size, size, imageFormat); |
||||||
|
irrMap.setMagFilter(Texture.MagFilter.Bilinear); |
||||||
|
irrMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||||
|
irrMap.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
return irrMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* initialize the pem map |
||||||
|
* @param size the size of the map |
||||||
|
* @param imageFormat the format of the image |
||||||
|
* @return the initialized prefiltered env map |
||||||
|
*/ |
||||||
|
public static TextureCubeMap createPrefilteredEnvMap(int size, Image.Format imageFormat) { |
||||||
|
|
||||||
|
TextureCubeMap pem = new TextureCubeMap(size, size, imageFormat); |
||||||
|
pem.setMagFilter(Texture.MagFilter.Bilinear); |
||||||
|
pem.setMinFilter(Texture.MinFilter.Trilinear); |
||||||
|
pem.getImage().setColorSpace(ColorSpace.Linear); |
||||||
|
int nbMipMap = (int) (Math.log(size) / Math.log(2) - 1); |
||||||
|
CubeMapWrapper targetWrapper = new CubeMapWrapper(pem); |
||||||
|
targetWrapper.initMipMaps(nbMipMap); |
||||||
|
return pem; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,214 @@ |
|||||||
|
/* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import com.jme3.app.Application; |
||||||
|
import com.jme3.app.state.BaseAppState; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.light.Light; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* A debug state that will display LIght gizmos on screen. |
||||||
|
* Still a wip and for now it only displays light probes. |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class LightsDebugState extends BaseAppState { |
||||||
|
|
||||||
|
private Node debugNode; |
||||||
|
private final Map<LightProbe, Node> probeMapping = new HashMap<LightProbe, Node>(); |
||||||
|
private final List<LightProbe> garbage = new ArrayList<LightProbe>(); |
||||||
|
private Geometry debugGeom; |
||||||
|
private Geometry debugBounds; |
||||||
|
private Material debugMaterial; |
||||||
|
private DebugMode debugMode = DebugMode.PrefilteredEnvMap; |
||||||
|
private float probeScale = 1.0f; |
||||||
|
private Spatial scene = null; |
||||||
|
private final List<LightProbe> probes = new ArrayList<LightProbe>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Debug mode for light probes |
||||||
|
*/ |
||||||
|
public enum DebugMode { |
||||||
|
|
||||||
|
/** |
||||||
|
* Displays the prefiltered env maps on the debug sphere |
||||||
|
*/ |
||||||
|
PrefilteredEnvMap, |
||||||
|
/** |
||||||
|
* displays the Irradiance map on the debug sphere |
||||||
|
*/ |
||||||
|
IrradianceMap |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initialize(Application app) { |
||||||
|
debugNode = new Node("Environment debug Node"); |
||||||
|
Sphere s = new Sphere(16, 16, 1); |
||||||
|
debugGeom = new Geometry("debugEnvProbe", s); |
||||||
|
debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md"); |
||||||
|
debugGeom.setMaterial(debugMaterial); |
||||||
|
debugBounds = BoundingSphereDebug.createDebugSphere(app.getAssetManager()); |
||||||
|
if (scene == null) { |
||||||
|
scene = app.getViewPort().getScenes().get(0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void update(float tpf) { |
||||||
|
for (Light light : scene.getWorldLightList()) { |
||||||
|
switch (light.getType()) { |
||||||
|
|
||||||
|
case Probe: |
||||||
|
LightProbe probe = (LightProbe) light; |
||||||
|
probes.add(probe); |
||||||
|
Node n = probeMapping.get(probe); |
||||||
|
if (n == null) { |
||||||
|
n = new Node("DebugProbe"); |
||||||
|
n.attachChild(debugGeom.clone(true)); |
||||||
|
n.attachChild(debugBounds.clone(false)); |
||||||
|
debugNode.attachChild(n); |
||||||
|
probeMapping.put(probe, n); |
||||||
|
} |
||||||
|
Geometry probeGeom = ((Geometry) n.getChild(0)); |
||||||
|
Material m = probeGeom.getMaterial(); |
||||||
|
probeGeom.setLocalScale(probeScale); |
||||||
|
if (probe.isReady()) { |
||||||
|
if (debugMode == DebugMode.IrradianceMap) { |
||||||
|
m.setTexture("CubeMap", probe.getIrradianceMap()); |
||||||
|
} else { |
||||||
|
m.setTexture("CubeMap", probe.getPrefilteredEnvMap()); |
||||||
|
} |
||||||
|
} |
||||||
|
n.setLocalTranslation(probe.getPosition()); |
||||||
|
n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius()); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
debugNode.updateLogicalState(tpf); |
||||||
|
debugNode.updateGeometricState(); |
||||||
|
cleanProbes(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the scenes for wich to render light gizmos. |
||||||
|
* @param scene |
||||||
|
*/ |
||||||
|
public void setScene(Spatial scene) { |
||||||
|
this.scene = scene; |
||||||
|
} |
||||||
|
|
||||||
|
private void cleanProbes() { |
||||||
|
if (probes.size() != probeMapping.size()) { |
||||||
|
for (LightProbe probe : probeMapping.keySet()) { |
||||||
|
if (!probes.contains(probe)) { |
||||||
|
garbage.add(probe); |
||||||
|
} |
||||||
|
} |
||||||
|
for (LightProbe probe : garbage) { |
||||||
|
probeMapping.remove(probe); |
||||||
|
} |
||||||
|
garbage.clear(); |
||||||
|
probes.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void render(RenderManager rm) { |
||||||
|
rm.renderScene(debugNode, getApplication().getViewPort()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @see DebugMode |
||||||
|
* @return the debug mode |
||||||
|
*/ |
||||||
|
public DebugMode getDebugMode() { |
||||||
|
return debugMode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the debug mode |
||||||
|
* @see DebugMode |
||||||
|
* @param debugMode the debug mode |
||||||
|
*/ |
||||||
|
public void setDebugMode(DebugMode debugMode) { |
||||||
|
this.debugMode = debugMode; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the scale of the probe's debug sphere |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public float getProbeScale() { |
||||||
|
return probeScale; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the scale of the probe's debug sphere |
||||||
|
* @param probeScale |
||||||
|
*/ |
||||||
|
public void setProbeScale(float probeScale) { |
||||||
|
this.probeScale = probeScale; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void cleanup(Application app) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onEnable() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDisable() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
/* |
||||||
|
* 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.light; |
||||||
|
|
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* This strategy returns the closest probe from the rendered object. |
||||||
|
* |
||||||
|
* This is the most basic strategy : The fastest and the easiest. |
||||||
|
* Though it has severe graphical draw backs as there might be very visible seams |
||||||
|
* on static object and some "poping" on dynamic objects. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class BasicProbeBlendingStrategy implements LightProbeBlendingStrategy { |
||||||
|
|
||||||
|
List<LightProbe> lightProbes = new ArrayList<LightProbe>(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void registerProbe(LightProbe probe) { |
||||||
|
lightProbes.add(probe); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void populateProbes(Geometry g, LightList lightList) { |
||||||
|
if (!lightProbes.isEmpty()) { |
||||||
|
//The first probe is actually the closest to the geometry since the
|
||||||
|
//light list is sorted according to the distance to the geom.
|
||||||
|
LightProbe p = lightProbes.get(0); |
||||||
|
if (p.isReady()) { |
||||||
|
lightList.add(p); |
||||||
|
} |
||||||
|
//clearing the list for next pass.
|
||||||
|
lightProbes.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,266 @@ |
|||||||
|
/* |
||||||
|
* 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.light; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.bounding.BoundingBox; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.bounding.BoundingVolume; |
||||||
|
import com.jme3.environment.EnvironmentCamera; |
||||||
|
import com.jme3.environment.LightProbeFactory; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.export.InputCapsule; |
||||||
|
import com.jme3.export.JmeExporter; |
||||||
|
import com.jme3.export.JmeImporter; |
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* A LightProbe is not exactly a light. It holds environment map information used for Image Based Lighting. |
||||||
|
* This is used for indirect lighting in the Physically Based Rendering pipeline. |
||||||
|
* |
||||||
|
* A light probe has a position in world space. This is the position from where the Environment Map are rendered. |
||||||
|
* There are two environment maps held by the LightProbe : |
||||||
|
* - The irradiance map (used for indirect diffuse lighting in the PBR pipeline). |
||||||
|
* - The prefiltered environment map (used for indirect specular lighting and reflection in the PBE pipeline). |
||||||
|
* Note that when instanciating the LightProbe, both those maps are null. |
||||||
|
* To render them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} |
||||||
|
* and {@link EnvironmentCamera}. |
||||||
|
* |
||||||
|
* The light probe has an area of effect that is a bounding volume centered on its position. (for now only Bounding spheres are supported). |
||||||
|
* |
||||||
|
* A LightProbe will only be taken into account when it's marked as ready. |
||||||
|
* A light probe is ready when it has valid environment map data set. |
||||||
|
* Note that you should never call setReady yourself. |
||||||
|
* |
||||||
|
* @see LightProbeFactory |
||||||
|
* @see EnvironmentCamera |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class LightProbe extends Light implements Savable { |
||||||
|
|
||||||
|
private TextureCubeMap irradianceMap; |
||||||
|
private TextureCubeMap prefilteredEnvMap; |
||||||
|
private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO); |
||||||
|
private boolean ready = false; |
||||||
|
private Vector3f position = new Vector3f(); |
||||||
|
private Node debugNode; |
||||||
|
|
||||||
|
/** |
||||||
|
* Empty constructor used for serialization. |
||||||
|
* You should never call it, use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} instead |
||||||
|
*/ |
||||||
|
public LightProbe() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the irradiance map texture of this Light probe. |
||||||
|
* Note that this Texture may not have image data yet if the LightProbe is not ready |
||||||
|
* @return the irradiance map |
||||||
|
*/ |
||||||
|
public TextureCubeMap getIrradianceMap() { |
||||||
|
return irradianceMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the irradiance map |
||||||
|
* @param irradianceMap the irradiance map |
||||||
|
*/ |
||||||
|
public void setIrradianceMap(TextureCubeMap irradianceMap) { |
||||||
|
this.irradianceMap = irradianceMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the prefiltered environment map texture of this light probe |
||||||
|
* Note that this Texture may not have image data yet if the LightProbe is not ready |
||||||
|
* @return the prefiltered environment map |
||||||
|
*/ |
||||||
|
public TextureCubeMap getPrefilteredEnvMap() { |
||||||
|
return prefilteredEnvMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the prefiltered environment map |
||||||
|
* @param prefileteredEnvMap the prefiltered environment map |
||||||
|
*/ |
||||||
|
public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) { |
||||||
|
this.prefilteredEnvMap = prefileteredEnvMap; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
super.write(ex); |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
oc.write(irradianceMap, "irradianceMap", null); |
||||||
|
oc.write(prefilteredEnvMap, "prefilteredEnvMap", null); |
||||||
|
oc.write(position, "position", null); |
||||||
|
oc.write(bounds, "bounds", new BoundingSphere(1.0f, Vector3f.ZERO)); |
||||||
|
oc.write(ready, "ready", false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
super.read(im); |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
irradianceMap = (TextureCubeMap) ic.readSavable("irradianceMap", null); |
||||||
|
prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null); |
||||||
|
position = (Vector3f) ic.readSavable("position", this); |
||||||
|
bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO)); |
||||||
|
ready = ic.readBoolean("ready", false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the bounding volume of this LightProbe |
||||||
|
* @return a bounding volume. |
||||||
|
*/ |
||||||
|
public BoundingVolume getBounds() { |
||||||
|
return bounds; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the bounds of this LightProbe |
||||||
|
* Note that for now only BoundingSphere is supported and this method will |
||||||
|
* throw an UnsupportedOperationException with any other BoundingVolume type |
||||||
|
* @param bounds the bounds of the LightProbe |
||||||
|
*/ |
||||||
|
public void setBounds(BoundingVolume bounds) { |
||||||
|
if( bounds.getType()!= BoundingVolume.Type.Sphere){ |
||||||
|
throw new UnsupportedOperationException("For not only BoundingSphere are suported for LightProbe"); |
||||||
|
} |
||||||
|
this.bounds = bounds; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return true if the LightProbe is ready, meaning the Environment maps have |
||||||
|
* been loaded or rnedered and are ready to be used by a material |
||||||
|
* @return the LightProbe ready state |
||||||
|
*/ |
||||||
|
public boolean isReady() { |
||||||
|
return ready; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Don't call this method directly. |
||||||
|
* It's meant to be called by additional systems that will load or render |
||||||
|
* the Environment maps of the LightProbe |
||||||
|
* @param ready the ready state of the LightProbe. |
||||||
|
*/ |
||||||
|
public void setReady(boolean ready) { |
||||||
|
this.ready = ready; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* For debuging porpose only |
||||||
|
* Will return a Node meant to be added to a GUI presenting the 2 cube maps in a cross pattern with all the mip maps. |
||||||
|
* |
||||||
|
* @param manager the asset manager |
||||||
|
* @return a debug node |
||||||
|
*/ |
||||||
|
public Node getDebugGui(AssetManager manager) { |
||||||
|
if (!ready) { |
||||||
|
throw new UnsupportedOperationException("This EnvProbeis not ready yet, try to test isReady()"); |
||||||
|
} |
||||||
|
if (debugNode == null) { |
||||||
|
debugNode = new Node("debug gui probe"); |
||||||
|
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager); |
||||||
|
Node debugIrrCm = EnvMapUtils.getCubeMapCrossDebugView(getIrradianceMap(), manager); |
||||||
|
|
||||||
|
debugNode.attachChild(debugIrrCm); |
||||||
|
debugNode.attachChild(debugPfemCm); |
||||||
|
debugPfemCm.setLocalTranslation(520, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
return debugNode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the position of the LightProbe in world space |
||||||
|
* @return the wolrd space position |
||||||
|
*/ |
||||||
|
public Vector3f getPosition() { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the position of the LightProbe in world space |
||||||
|
* @param position the wolrd space position |
||||||
|
*/ |
||||||
|
public void setPosition(Vector3f position) { |
||||||
|
this.position.set(position); |
||||||
|
getBounds().setCenter(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean intersectsBox(BoundingBox box, TempVars vars) { |
||||||
|
return getBounds().intersectsBoundingBox(box); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean intersectsFrustum(Camera camera, TempVars vars) { |
||||||
|
return camera.contains(bounds) != Camera.FrustumIntersect.Outside; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void computeLastDistance(Spatial owner) { |
||||||
|
if (owner.getWorldBound() != null) { |
||||||
|
BoundingVolume bv = owner.getWorldBound(); |
||||||
|
lastDistance = bv.distanceSquaredTo(position); |
||||||
|
} else { |
||||||
|
lastDistance = owner.getWorldTranslation().distanceSquared(position); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Type getType() { |
||||||
|
return Type.Probe; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "Light Probe : " + name + " at " + position + " / " + bounds; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) { |
||||||
|
return getBounds().intersectsSphere(sphere); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
/* |
||||||
|
* 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.light; |
||||||
|
|
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.post.SceneProcessor; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.FrameBuffer; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* this processor allows to blend several light probes maps together according to a Point of Interest. |
||||||
|
* This is all based on this article by Sebastien lagarde |
||||||
|
* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class LightProbeBlendingProcessor implements SceneProcessor { |
||||||
|
|
||||||
|
private ViewPort viewPort; |
||||||
|
private LightFilter prevFilter; |
||||||
|
private RenderManager renderManager; |
||||||
|
private LightProbe probe = new LightProbe(); |
||||||
|
private Spatial poi; |
||||||
|
|
||||||
|
public LightProbeBlendingProcessor(Spatial poi) { |
||||||
|
this.poi = poi; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void initialize(RenderManager rm, ViewPort vp) { |
||||||
|
viewPort = vp; |
||||||
|
renderManager = rm; |
||||||
|
prevFilter = rm.getLightFilter(); |
||||||
|
rm.setLightFilter(new PoiLightProbeLightFilter(this)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void reshape(ViewPort vp, int w, int h) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isInitialized() { |
||||||
|
return viewPort != null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void preFrame(float tpf) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** 1. For POI take a spatial in the constructor and make all calculation against its world pos |
||||||
|
* - Alternatively compute an arbitrary POI by casting rays from the camera |
||||||
|
* (one in the center and one for each corner and take the median point) |
||||||
|
* 2. Take the 4 most weighted probes for default. Maybe allow the user to change this |
||||||
|
* 3. For the inner influence radius take half of the radius for a start we'll see then how to change this. |
||||||
|
* |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void postQueue(RenderQueue rq) { |
||||||
|
List<BlendFactor> blendFactors = new ArrayList<BlendFactor>(); |
||||||
|
float sumBlendFactors = computeBlendFactors(blendFactors); |
||||||
|
|
||||||
|
//Sort blend factors according to their weight
|
||||||
|
Collections.sort(blendFactors); |
||||||
|
|
||||||
|
//normalize blend factors;
|
||||||
|
float normalizer = 1f / sumBlendFactors; |
||||||
|
for (BlendFactor blendFactor : blendFactors) { |
||||||
|
blendFactor.ndf *= normalizer; |
||||||
|
// System.err.println(blendFactor);
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//for now just pick the first probe.
|
||||||
|
if(!blendFactors.isEmpty()){ |
||||||
|
probe = blendFactors.get(0).lightProbe; |
||||||
|
}else{ |
||||||
|
probe = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private float computeBlendFactors(List<BlendFactor> blendFactors) { |
||||||
|
float sumBlendFactors = 0; |
||||||
|
for (Spatial scene : viewPort.getScenes()) { |
||||||
|
for (Light light : scene.getWorldLightList()) { |
||||||
|
if(light.getType() == Light.Type.Probe){ |
||||||
|
LightProbe p = (LightProbe)light; |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
boolean intersect = p.intersectsFrustum(viewPort.getCamera(), vars); |
||||||
|
vars.release(); |
||||||
|
//check if the probe is inside the camera frustum
|
||||||
|
if(intersect){ |
||||||
|
|
||||||
|
//is the poi inside the bounds of this probe
|
||||||
|
if(poi.getWorldBound().intersects(p.getBounds())){ |
||||||
|
|
||||||
|
//computing the distance as we need it to check if th epoi in in the inner radius and later to compute the weight
|
||||||
|
float outerRadius = ((BoundingSphere)p.getBounds()).getRadius(); |
||||||
|
float innerRadius = outerRadius * 0.5f; |
||||||
|
float distance = p.getBounds().getCenter().distance(poi.getWorldTranslation()); |
||||||
|
|
||||||
|
// if the poi in inside the inner range of this probe, then this probe is the only one that matters.
|
||||||
|
if( distance < innerRadius ){ |
||||||
|
blendFactors.clear(); |
||||||
|
blendFactors.add(new BlendFactor(p, 1.0f)); |
||||||
|
return 1.0f; |
||||||
|
} |
||||||
|
//else we need to compute the weight of this probe and collect it for blending
|
||||||
|
float ndf = (distance - innerRadius) / (outerRadius - innerRadius); |
||||||
|
sumBlendFactors += ndf; |
||||||
|
blendFactors.add(new BlendFactor(p, ndf)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return sumBlendFactors; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postFrame(FrameBuffer out) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cleanup() { |
||||||
|
viewPort = null; |
||||||
|
renderManager.setLightFilter(prevFilter); |
||||||
|
} |
||||||
|
|
||||||
|
public void populateProbe(LightList lightList){ |
||||||
|
if(probe != null && probe.isReady()){ |
||||||
|
lightList.add(probe); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Spatial getPoi() { |
||||||
|
return poi; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPoi(Spatial poi) { |
||||||
|
this.poi = poi; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private class BlendFactor implements Comparable<BlendFactor>{ |
||||||
|
|
||||||
|
LightProbe lightProbe; |
||||||
|
float ndf; |
||||||
|
|
||||||
|
public BlendFactor(LightProbe lightProbe, float ndf) { |
||||||
|
this.lightProbe = lightProbe; |
||||||
|
this.ndf = ndf; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "BlendFactor{" + "lightProbe=" + lightProbe + ", ndf=" + ndf + '}'; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compareTo(BlendFactor o) { |
||||||
|
if(o.ndf > ndf){ |
||||||
|
return -1; |
||||||
|
}else if(o.ndf < ndf){ |
||||||
|
return 1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
/* |
||||||
|
* 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.light; |
||||||
|
|
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the interface to implement if you want to make your own LightProbe blending strategy. |
||||||
|
* The strategy sets the way multiple LightProbes will be handled for a given object. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public interface LightProbeBlendingStrategy { |
||||||
|
|
||||||
|
/** |
||||||
|
* Registers a probe with this strategy |
||||||
|
* @param probe |
||||||
|
*/ |
||||||
|
public void registerProbe(LightProbe probe); |
||||||
|
/** |
||||||
|
* Populates the resulting light probes into the given light list. |
||||||
|
* @param g the geometry for wich the light list is computed |
||||||
|
* @param lightList the result light list |
||||||
|
*/ |
||||||
|
public void populateProbes(Geometry g, LightList lightList); |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/* |
||||||
|
* 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.light; |
||||||
|
|
||||||
|
import com.jme3.bounding.BoundingBox; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.bounding.BoundingVolume; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
import java.util.HashSet; |
||||||
|
|
||||||
|
public final class PoiLightProbeLightFilter implements LightFilter { |
||||||
|
|
||||||
|
private Camera camera; |
||||||
|
private final HashSet<Light> processedLights = new HashSet<Light>(); |
||||||
|
private final LightProbeBlendingProcessor processor; |
||||||
|
|
||||||
|
public PoiLightProbeLightFilter(LightProbeBlendingProcessor processor) { |
||||||
|
this.processor = processor; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setCamera(Camera camera) { |
||||||
|
this.camera = camera; |
||||||
|
for (Light light : processedLights) { |
||||||
|
light.frustumCheckNeeded = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void filterLights(Geometry geometry, LightList filteredLightList) { |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
try { |
||||||
|
LightList worldLights = geometry.getWorldLightList(); |
||||||
|
|
||||||
|
for (int i = 0; i < worldLights.size(); i++) { |
||||||
|
Light light = worldLights.get(i); |
||||||
|
|
||||||
|
if (light.getType() == Light.Type.Probe) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (light.frustumCheckNeeded) { |
||||||
|
processedLights.add(light); |
||||||
|
light.frustumCheckNeeded = false; |
||||||
|
light.intersectsFrustum = light.intersectsFrustum(camera, vars); |
||||||
|
} |
||||||
|
|
||||||
|
if (!light.intersectsFrustum) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
BoundingVolume bv = geometry.getWorldBound(); |
||||||
|
|
||||||
|
if (bv instanceof BoundingBox) { |
||||||
|
if (!light.intersectsBox((BoundingBox) bv, vars)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
} else if (bv instanceof BoundingSphere) { |
||||||
|
if (!Float.isInfinite(((BoundingSphere) bv).getRadius())) { |
||||||
|
if (!light.intersectsSphere((BoundingSphere) bv, vars)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
filteredLightList.add(light); |
||||||
|
} |
||||||
|
|
||||||
|
processor.populateProbe(filteredLightList); |
||||||
|
|
||||||
|
} finally { |
||||||
|
vars.release(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,251 @@ |
|||||||
|
/* |
||||||
|
* 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.material.logic; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.light.*; |
||||||
|
import com.jme3.material.*; |
||||||
|
import com.jme3.material.RenderState.BlendMode; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.renderer.*; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.shader.*; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
|
||||||
|
import java.util.EnumSet; |
||||||
|
|
||||||
|
public final class SinglePassAndImageBasedLightingLogic extends DefaultTechniqueDefLogic { |
||||||
|
|
||||||
|
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING"; |
||||||
|
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS"; |
||||||
|
private static final RenderState ADDITIVE_LIGHT = new RenderState(); |
||||||
|
|
||||||
|
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1); |
||||||
|
|
||||||
|
static { |
||||||
|
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive); |
||||||
|
ADDITIVE_LIGHT.setDepthWrite(false); |
||||||
|
} |
||||||
|
|
||||||
|
private final int singlePassLightingDefineId; |
||||||
|
private final int nbLightsDefineId; |
||||||
|
|
||||||
|
public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) { |
||||||
|
super(techniqueDef); |
||||||
|
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean); |
||||||
|
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, |
||||||
|
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) { |
||||||
|
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3); |
||||||
|
defines.set(singlePassLightingDefineId, true); |
||||||
|
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Uploads the lights in the light list as two uniform arrays.<br/><br/> * |
||||||
|
* <p> |
||||||
|
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
|
||||||
|
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
|
||||||
|
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
|
||||||
|
* 2 = Spot. <br/> <br/> |
||||||
|
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
|
||||||
|
* g_LightPosition.xyz is the position of the light (for point lights)<br/> |
||||||
|
* // or the direction of the light (for directional lights).<br/> //
|
||||||
|
* g_LightPosition.w is the inverse radius (1/r) of the light (for |
||||||
|
* attenuation) <br/> </p> |
||||||
|
*/ |
||||||
|
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex, int lastTexUnit) { |
||||||
|
if (numLights == 0) { // this shader does not do lighting, ignore.
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
Uniform lightData = shader.getUniform("g_LightData"); |
||||||
|
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
||||||
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); |
||||||
|
Uniform lightProbeData = shader.getUniform("g_LightProbeData"); |
||||||
|
lightProbeData.setVector4Length(1); |
||||||
|
Uniform lightProbeIrrMap = shader.getUniform("g_IrradianceMap"); |
||||||
|
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap"); |
||||||
|
|
||||||
|
LightProbe lightProbe = null; |
||||||
|
if (startIndex != 0) { |
||||||
|
// apply additive blending for 2nd and future passes
|
||||||
|
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT); |
||||||
|
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black); |
||||||
|
}else{ |
||||||
|
lightProbe = extractIndirectLights(lightList,true); |
||||||
|
ambientColor.setValue(VarType.Vector4, ambientLightColor); |
||||||
|
} |
||||||
|
|
||||||
|
//If there is a lightProbe in the list we force it's render on the first pass
|
||||||
|
if(lightProbe != null){ |
||||||
|
BoundingSphere s = (BoundingSphere)lightProbe.getBounds(); |
||||||
|
lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f/s.getRadius(), 0); |
||||||
|
//assigning new texture indexes
|
||||||
|
int irrUnit = lastTexUnit++; |
||||||
|
int pemUnit = lastTexUnit++; |
||||||
|
|
||||||
|
rm.getRenderer().setTexture(irrUnit, lightProbe.getIrradianceMap()); |
||||||
|
lightProbeIrrMap.setValue(VarType.Int, irrUnit); |
||||||
|
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap()); |
||||||
|
lightProbePemMap.setValue(VarType.Int, pemUnit); |
||||||
|
} else { |
||||||
|
//Disable IBL for this pass
|
||||||
|
lightProbeData.setVector4InArray(0,0,0,-1, 0); |
||||||
|
} |
||||||
|
|
||||||
|
int lightDataIndex = 0; |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
Vector4f tmpVec = vars.vect4f1; |
||||||
|
int curIndex; |
||||||
|
int endIndex = numLights + startIndex; |
||||||
|
boolean useIBL = false; |
||||||
|
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) { |
||||||
|
|
||||||
|
|
||||||
|
Light l = lightList.get(curIndex); |
||||||
|
if(l.getType() == Light.Type.Ambient){ |
||||||
|
endIndex++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
ColorRGBA color = l.getColor(); |
||||||
|
//Color
|
||||||
|
|
||||||
|
if(l.getType() != Light.Type.Probe){ |
||||||
|
lightData.setVector4InArray(color.getRed(), |
||||||
|
color.getGreen(), |
||||||
|
color.getBlue(), |
||||||
|
l.getType().getId(), |
||||||
|
lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
} |
||||||
|
|
||||||
|
switch (l.getType()) { |
||||||
|
case Directional: |
||||||
|
DirectionalLight dl = (DirectionalLight) l; |
||||||
|
Vector3f dir = dl.getDirection(); |
||||||
|
//Data directly sent in view space to avoid a matrix mult for each pixel
|
||||||
|
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f); |
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
//PADDING
|
||||||
|
lightData.setVector4InArray(0,0,0,0, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
break; |
||||||
|
case Point: |
||||||
|
PointLight pl = (PointLight) l; |
||||||
|
Vector3f pos = pl.getPosition(); |
||||||
|
float invRadius = pl.getInvRadius(); |
||||||
|
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f); |
||||||
|
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
//PADDING
|
||||||
|
lightData.setVector4InArray(0,0,0,0, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
break; |
||||||
|
case Spot: |
||||||
|
SpotLight sl = (SpotLight) l; |
||||||
|
Vector3f pos2 = sl.getPosition(); |
||||||
|
Vector3f dir2 = sl.getDirection(); |
||||||
|
float invRange = sl.getInvSpotRange(); |
||||||
|
float spotAngleCos = sl.getPackedAngleCos(); |
||||||
|
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f); |
||||||
|
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
|
||||||
|
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f); |
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new UnsupportedOperationException("Unknown type of light: " + l.getType()); |
||||||
|
} |
||||||
|
} |
||||||
|
vars.release(); |
||||||
|
|
||||||
|
//Padding of unsued buffer space
|
||||||
|
while(lightDataIndex < numLights * 3) { |
||||||
|
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex); |
||||||
|
lightDataIndex++; |
||||||
|
} |
||||||
|
return curIndex; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) { |
||||||
|
int nbRenderedLights = 0; |
||||||
|
Renderer renderer = renderManager.getRenderer(); |
||||||
|
int batchSize = renderManager.getSinglePassLightBatchSize(); |
||||||
|
if (lights.size() == 0) { |
||||||
|
updateLightListUniforms(shader, geometry, lights,batchSize, renderManager, 0, lastTexUnit); |
||||||
|
renderer.setShader(shader); |
||||||
|
renderMeshFromGeometry(renderer, geometry); |
||||||
|
} else { |
||||||
|
while (nbRenderedLights < lights.size()) { |
||||||
|
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights, lastTexUnit); |
||||||
|
renderer.setShader(shader); |
||||||
|
renderMeshFromGeometry(renderer, geometry); |
||||||
|
} |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
protected LightProbe extractIndirectLights(LightList lightList, boolean removeLights) { |
||||||
|
ambientLightColor.set(0, 0, 0, 1); |
||||||
|
LightProbe probe = null; |
||||||
|
for (int j = 0; j < lightList.size(); j++) { |
||||||
|
Light l = lightList.get(j); |
||||||
|
if (l instanceof AmbientLight) { |
||||||
|
ambientLightColor.addLocal(l.getColor()); |
||||||
|
if(removeLights){ |
||||||
|
lightList.remove(l); |
||||||
|
j--; |
||||||
|
} |
||||||
|
} |
||||||
|
if (l instanceof LightProbe) { |
||||||
|
probe = (LightProbe)l; |
||||||
|
if(removeLights){ |
||||||
|
lightList.remove(l); |
||||||
|
j--; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
ambientLightColor.a = 1.0f; |
||||||
|
return probe; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,238 @@ |
|||||||
|
#import "Common/ShaderLib/Parallax.glsllib" |
||||||
|
#import "Common/ShaderLib/PBR.glsllib" |
||||||
|
#import "Common/ShaderLib/Lighting.glsllib" |
||||||
|
|
||||||
|
varying vec2 texCoord; |
||||||
|
#ifdef SEPARATE_TEXCOORD |
||||||
|
varying vec2 texCoord2; |
||||||
|
#endif |
||||||
|
|
||||||
|
varying vec4 Color; |
||||||
|
|
||||||
|
uniform vec4 g_LightData[NB_LIGHTS]; |
||||||
|
|
||||||
|
uniform vec3 g_CameraPosition; |
||||||
|
|
||||||
|
uniform float m_Roughness; |
||||||
|
uniform float m_Metallic; |
||||||
|
|
||||||
|
varying vec3 wPosition; |
||||||
|
|
||||||
|
|
||||||
|
//#ifdef INDIRECT_LIGHTING |
||||||
|
// uniform sampler2D m_IntegrateBRDF; |
||||||
|
uniform samplerCube g_PrefEnvMap; |
||||||
|
uniform samplerCube g_IrradianceMap; |
||||||
|
uniform vec4 g_LightProbeData; |
||||||
|
//#endif |
||||||
|
|
||||||
|
#ifdef BASECOLORMAP |
||||||
|
uniform sampler2D m_BaseColorMap; |
||||||
|
#endif |
||||||
|
#ifdef METALLICMAP |
||||||
|
uniform sampler2D m_MetallicMap; |
||||||
|
#endif |
||||||
|
#ifdef ROUGHNESSMAP |
||||||
|
uniform sampler2D m_RoughnessMap; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef EMISSIVE |
||||||
|
uniform vec4 m_Emissive; |
||||||
|
#endif |
||||||
|
#ifdef EMISSIVEMAP |
||||||
|
uniform sampler2D m_EmissiveMap; |
||||||
|
#endif |
||||||
|
#if defined(EMISSIVE) || defined(EMISSIVEMAP) |
||||||
|
uniform float m_EmissivePower; |
||||||
|
uniform float m_EmissiveIntensity; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef SPECGLOSSPIPELINE |
||||||
|
uniform sampler2D m_SpecularMap; |
||||||
|
uniform sampler2D m_GlossMap; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef PARALLAXMAP |
||||||
|
uniform sampler2D m_ParallaxMap; |
||||||
|
#endif |
||||||
|
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) |
||||||
|
uniform float m_ParallaxHeight; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef LIGHTMAP |
||||||
|
uniform sampler2D m_LightMap; |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(NORMALMAP) || defined(PARALLAXMAP) |
||||||
|
uniform sampler2D m_NormalMap; |
||||||
|
varying vec4 wTangent; |
||||||
|
#endif |
||||||
|
varying vec3 wNormal; |
||||||
|
|
||||||
|
#ifdef DISCARD_ALPHA |
||||||
|
uniform float m_AlphaDiscardThreshold; |
||||||
|
#endif |
||||||
|
|
||||||
|
void main(){ |
||||||
|
vec2 newTexCoord; |
||||||
|
vec3 viewDir = normalize(g_CameraPosition - wPosition); |
||||||
|
|
||||||
|
#if defined(NORMALMAP) || defined(PARALLAXMAP) |
||||||
|
mat3 tbnMat = mat3(wTangent.xyz, wTangent.w * cross( (wNormal), (wTangent.xyz)), wNormal.xyz); |
||||||
|
#endif |
||||||
|
|
||||||
|
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) |
||||||
|
vec3 vViewDir = viewDir * tbnMat; |
||||||
|
#ifdef STEEP_PARALLAX |
||||||
|
#ifdef NORMALMAP_PARALLAX |
||||||
|
//parallax map is stored in the alpha channel of the normal map |
||||||
|
newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); |
||||||
|
#else |
||||||
|
//parallax map is a texture |
||||||
|
newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); |
||||||
|
#endif |
||||||
|
#else |
||||||
|
#ifdef NORMALMAP_PARALLAX |
||||||
|
//parallax map is stored in the alpha channel of the normal map |
||||||
|
newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); |
||||||
|
#else |
||||||
|
//parallax map is a texture |
||||||
|
newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
#else |
||||||
|
newTexCoord = texCoord; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef BASECOLORMAP |
||||||
|
vec4 albedo = texture2D(m_BaseColorMap, newTexCoord); |
||||||
|
#else |
||||||
|
vec4 albedo = Color; |
||||||
|
#endif |
||||||
|
#ifdef ROUGHNESSMAP |
||||||
|
float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness, 1e-8); |
||||||
|
#else |
||||||
|
float Roughness = max(m_Roughness, 1e-8); |
||||||
|
#endif |
||||||
|
#ifdef METALLICMAP |
||||||
|
float Metallic = texture2D(m_MetallicMap, newTexCoord).r; |
||||||
|
#else |
||||||
|
float Metallic = max(m_Metallic, 0.0); |
||||||
|
#endif |
||||||
|
|
||||||
|
float alpha = Color.a * albedo.a; |
||||||
|
|
||||||
|
#ifdef DISCARD_ALPHA |
||||||
|
if(alpha < m_AlphaDiscardThreshold){ |
||||||
|
discard; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
// *********************** |
||||||
|
// Read from textures |
||||||
|
// *********************** |
||||||
|
#if defined(NORMALMAP) |
||||||
|
vec4 normalHeight = texture2D(m_NormalMap, newTexCoord); |
||||||
|
//Note the -2.0 and -1.0. We invert the green channel of the normal map, |
||||||
|
//as it's complient with normal maps generated with blender. |
||||||
|
//see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898 |
||||||
|
//for more explanation. |
||||||
|
vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0))); |
||||||
|
normal = normalize(tbnMat * normal); |
||||||
|
//normal = normalize(normal * inverse(tbnMat)); |
||||||
|
#else |
||||||
|
vec3 normal = normalize(wNormal); |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#ifdef LIGHTMAP |
||||||
|
vec3 lightMapColor; |
||||||
|
#ifdef SEPARATE_TEXCOORD |
||||||
|
lightMapColor = texture2D(m_LightMap, texCoord2).rgb; |
||||||
|
#else |
||||||
|
lightMapColor = texture2D(m_LightMap, texCoord).rgb; |
||||||
|
#endif |
||||||
|
specularColor.rgb *= lightMapColor; |
||||||
|
albedo.rgb *= lightMapColor; |
||||||
|
#endif |
||||||
|
|
||||||
|
float specular = 0.5; |
||||||
|
#ifdef SPECGLOSSPIPELINE |
||||||
|
vec4 specularColor = texture2D(m_SpecularMap, newTexCoord); |
||||||
|
vec4 diffuseColor = albedo; |
||||||
|
Roughness = 1.0 - texture2D(m_GlossMap, newTexCoord).r; |
||||||
|
#else |
||||||
|
float nonMetalSpec = 0.08 * specular; |
||||||
|
vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic; |
||||||
|
vec4 diffuseColor = albedo - albedo * Metallic; |
||||||
|
#endif |
||||||
|
|
||||||
|
gl_FragColor.rgb = vec3(0.0); |
||||||
|
float ndotv = max( dot( normal, viewDir ),0.0); |
||||||
|
for( int i = 0;i < NB_LIGHTS; i+=3){ |
||||||
|
vec4 lightColor = g_LightData[i]; |
||||||
|
vec4 lightData1 = g_LightData[i+1]; |
||||||
|
vec4 lightDir; |
||||||
|
vec3 lightVec; |
||||||
|
lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, lightVec); |
||||||
|
|
||||||
|
float fallOff = 1.0; |
||||||
|
#if __VERSION__ >= 110 |
||||||
|
// allow use of control flow |
||||||
|
if(lightColor.w > 1.0){ |
||||||
|
#endif |
||||||
|
fallOff = computeSpotFalloff(g_LightData[i+2], lightVec); |
||||||
|
#if __VERSION__ >= 110 |
||||||
|
} |
||||||
|
#endif |
||||||
|
//point light attenuation |
||||||
|
fallOff *= lightDir.w; |
||||||
|
|
||||||
|
lightDir.xyz = normalize(lightDir.xyz); |
||||||
|
vec3 directDiffuse; |
||||||
|
vec3 directSpecular; |
||||||
|
|
||||||
|
PBR_ComputeDirectLight(normal, lightDir.xyz, viewDir, |
||||||
|
lightColor.rgb,specular, Roughness, ndotv, |
||||||
|
directDiffuse, directSpecular); |
||||||
|
|
||||||
|
vec3 directLighting = diffuseColor.rgb *directDiffuse + directSpecular * specularColor.rgb; |
||||||
|
|
||||||
|
gl_FragColor.rgb += directLighting * fallOff; |
||||||
|
} |
||||||
|
|
||||||
|
vec3 rv = reflect(-viewDir.xyz, normal.xyz); |
||||||
|
//prallax fix for spherical bounds from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ |
||||||
|
// g_LightProbeData.w is 1/probe radius, g_LightProbeData.xyz is the position of the lightProbe. |
||||||
|
rv = g_LightProbeData.w * (wPosition - g_LightProbeData.xyz) +rv; |
||||||
|
|
||||||
|
//horizon fade from http://marmosetco.tumblr.com/post/81245981087 |
||||||
|
float horiz = dot(rv, wNormal.xyz); |
||||||
|
float horizFadePower= 1.0 - Roughness; |
||||||
|
horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 ); |
||||||
|
horiz *= horiz; |
||||||
|
|
||||||
|
vec3 indirectDiffuse = vec3(0.0); |
||||||
|
vec3 indirectSpecular = vec3(0.0); |
||||||
|
indirectDiffuse = textureCube(g_IrradianceMap, normal.xyz).rgb * diffuseColor.rgb; |
||||||
|
|
||||||
|
indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, rv.xyz); |
||||||
|
indirectSpecular *= vec3(horiz); |
||||||
|
|
||||||
|
vec3 indirectLighting = indirectDiffuse + indirectSpecular; |
||||||
|
|
||||||
|
gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting * step( 0.0, g_LightProbeData.w); |
||||||
|
|
||||||
|
#if defined(EMISSIVE) || defined (EMISSIVEMAP) |
||||||
|
#ifdef EMISSIVEMAP |
||||||
|
vec4 emissive = texture2D(m_EmissiveMap, newTexCoord); |
||||||
|
#else |
||||||
|
vec4 emissive = m_Emissive; |
||||||
|
#endif |
||||||
|
gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity; |
||||||
|
#endif |
||||||
|
|
||||||
|
gl_FragColor.a = alpha; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,307 @@ |
|||||||
|
MaterialDef PBR Lighting { |
||||||
|
|
||||||
|
MaterialParameters { |
||||||
|
|
||||||
|
// Alpha threshold for fragment discarding |
||||||
|
Float AlphaDiscardThreshold (AlphaTestFallOff) |
||||||
|
|
||||||
|
//metalness of the material |
||||||
|
Float Metallic : 0.0 |
||||||
|
//Roughness of the material |
||||||
|
Float Roughness : 1.0 |
||||||
|
// Base material color |
||||||
|
Color BaseColor |
||||||
|
// The emissive color of the object |
||||||
|
Color Emissive |
||||||
|
// the emissive power |
||||||
|
Float EmissivePower : 3.0 |
||||||
|
// the emissive intensity |
||||||
|
Float EmissiveIntensity : 1.0 |
||||||
|
|
||||||
|
// BaseColor map |
||||||
|
Texture2D BaseColorMap |
||||||
|
|
||||||
|
// Specular/gloss map |
||||||
|
Texture2D MetallicMap -LINEAR |
||||||
|
|
||||||
|
// Roughness Map |
||||||
|
Texture2D RoughnessMap -LINEAR |
||||||
|
|
||||||
|
// Texture of the emissive parts of the material |
||||||
|
Texture2D EmissiveMap |
||||||
|
|
||||||
|
// Normal map |
||||||
|
Texture2D NormalMap -LINEAR |
||||||
|
|
||||||
|
// For Spec gloss pipeline |
||||||
|
Texture2D SpecularMap |
||||||
|
Texture2D GlossMap |
||||||
|
|
||||||
|
Vector4 ProbeData |
||||||
|
|
||||||
|
// Prefiltered Env Map for indirect specular lighting |
||||||
|
TextureCubeMap PrefEnvMap -LINEAR |
||||||
|
|
||||||
|
// Irradiance map for indirect diffuse lighting |
||||||
|
TextureCubeMap IrradianceMap -LINEAR |
||||||
|
|
||||||
|
//integrate BRDF map for indirect Lighting |
||||||
|
Texture2D IntegrateBRDF -LINEAR |
||||||
|
|
||||||
|
// Parallax/height map |
||||||
|
Texture2D ParallaxMap -LINEAR |
||||||
|
|
||||||
|
//Set to true is parallax map is stored in the alpha channel of the normal map |
||||||
|
Boolean PackedNormalParallax |
||||||
|
|
||||||
|
//Sets the relief height for parallax mapping |
||||||
|
Float ParallaxHeight : 0.05 |
||||||
|
|
||||||
|
//Set to true to activate Steep Parallax mapping |
||||||
|
Boolean SteepParallax |
||||||
|
|
||||||
|
// Set to Use Lightmap |
||||||
|
Texture2D LightMap |
||||||
|
|
||||||
|
// Set to use TexCoord2 for the lightmap sampling |
||||||
|
Boolean SeparateTexCoord |
||||||
|
|
||||||
|
//shadows |
||||||
|
Int FilterMode |
||||||
|
Boolean HardwareShadows |
||||||
|
|
||||||
|
Texture2D ShadowMap0 |
||||||
|
Texture2D ShadowMap1 |
||||||
|
Texture2D ShadowMap2 |
||||||
|
Texture2D ShadowMap3 |
||||||
|
//pointLights |
||||||
|
Texture2D ShadowMap4 |
||||||
|
Texture2D ShadowMap5 |
||||||
|
|
||||||
|
Float ShadowIntensity |
||||||
|
Vector4 Splits |
||||||
|
Vector2 FadeInfo |
||||||
|
|
||||||
|
Matrix4 LightViewProjectionMatrix0 |
||||||
|
Matrix4 LightViewProjectionMatrix1 |
||||||
|
Matrix4 LightViewProjectionMatrix2 |
||||||
|
Matrix4 LightViewProjectionMatrix3 |
||||||
|
//pointLight |
||||||
|
Matrix4 LightViewProjectionMatrix4 |
||||||
|
Matrix4 LightViewProjectionMatrix5 |
||||||
|
Vector3 LightPos |
||||||
|
Vector3 LightDir |
||||||
|
|
||||||
|
Float PCFEdge |
||||||
|
Float ShadowMapSize |
||||||
|
|
||||||
|
// For hardware skinning |
||||||
|
Int NumberOfBones |
||||||
|
Matrix4Array BoneMatrices |
||||||
|
|
||||||
|
//For instancing |
||||||
|
Boolean UseInstancing |
||||||
|
|
||||||
|
//For Vertex Color |
||||||
|
Boolean UseVertexColor |
||||||
|
} |
||||||
|
|
||||||
|
Technique { |
||||||
|
LightMode SinglePassAndImageBased |
||||||
|
|
||||||
|
VertexShader GLSL100: Common/MatDefs/Light/PBRLighting.vert |
||||||
|
FragmentShader GLSL100: Common/MatDefs/Light/PBRLighting.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
CameraPosition |
||||||
|
WorldMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
BASECOLORMAP : BaseColorMap |
||||||
|
NORMALMAP : NormalMap |
||||||
|
METALLICMAP : MetallicMap |
||||||
|
ROUGHNESSMAP : RoughnessMap |
||||||
|
EMISSIVEMAP : EmissiveMap |
||||||
|
EMISSIVE : Emissive |
||||||
|
SPECGLOSSPIPELINE : SpecularMap |
||||||
|
PARALLAXMAP : ParallaxMap |
||||||
|
NORMALMAP_PARALLAX : PackedNormalParallax |
||||||
|
STEEP_PARALLAX : SteepParallax |
||||||
|
LIGHTMAP : LightMap |
||||||
|
SEPARATE_TEXCOORD : SeparateTexCoord |
||||||
|
DISCARD_ALPHA : AlphaDiscardThreshold |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
//INDIRECT_LIGHTING : IntegrateBRDF |
||||||
|
VERTEX_COLOR : UseVertexColor |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Technique PreShadow { |
||||||
|
|
||||||
|
VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert |
||||||
|
FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldViewMatrix |
||||||
|
ViewProjectionMatrix |
||||||
|
ViewMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
COLOR_MAP : ColorMap |
||||||
|
DISCARD_ALPHA : AlphaDiscardThreshold |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
} |
||||||
|
|
||||||
|
ForcedRenderState { |
||||||
|
FaceCull Off |
||||||
|
DepthTest On |
||||||
|
DepthWrite On |
||||||
|
PolyOffset 5 3 |
||||||
|
ColorWrite Off |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Technique PostShadow15{ |
||||||
|
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert |
||||||
|
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldMatrix |
||||||
|
ViewProjectionMatrix |
||||||
|
ViewMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
HARDWARE_SHADOWS : HardwareShadows |
||||||
|
FILTER_MODE : FilterMode |
||||||
|
PCFEDGE : PCFEdge |
||||||
|
DISCARD_ALPHA : AlphaDiscardThreshold |
||||||
|
COLOR_MAP : ColorMap |
||||||
|
SHADOWMAP_SIZE : ShadowMapSize |
||||||
|
FADE : FadeInfo |
||||||
|
PSSM : Splits |
||||||
|
POINTLIGHT : LightViewProjectionMatrix5 |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
} |
||||||
|
|
||||||
|
ForcedRenderState { |
||||||
|
Blend Modulate |
||||||
|
DepthWrite Off |
||||||
|
PolyOffset -0.1 0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Technique PostShadow{ |
||||||
|
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert |
||||||
|
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldMatrix |
||||||
|
ViewProjectionMatrix |
||||||
|
ViewMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
HARDWARE_SHADOWS : HardwareShadows |
||||||
|
FILTER_MODE : FilterMode |
||||||
|
PCFEDGE : PCFEdge |
||||||
|
DISCARD_ALPHA : AlphaDiscardThreshold |
||||||
|
COLOR_MAP : ColorMap |
||||||
|
SHADOWMAP_SIZE : ShadowMapSize |
||||||
|
FADE : FadeInfo |
||||||
|
PSSM : Splits |
||||||
|
POINTLIGHT : LightViewProjectionMatrix5 |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
} |
||||||
|
|
||||||
|
ForcedRenderState { |
||||||
|
Blend Modulate |
||||||
|
DepthWrite Off |
||||||
|
PolyOffset -0.1 0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Technique PreNormalPass { |
||||||
|
|
||||||
|
VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert |
||||||
|
FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldViewMatrix |
||||||
|
NormalMatrix |
||||||
|
ViewProjectionMatrix |
||||||
|
ViewMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
DIFFUSEMAP_ALPHA : DiffuseMap |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Technique PreNormalPassDerivative { |
||||||
|
|
||||||
|
VertexShader GLSL100 : Common/MatDefs/MSSAO/normal.vert |
||||||
|
FragmentShader GLSL100 : Common/MatDefs/MSSAO/normal.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldViewMatrix |
||||||
|
NormalMatrix |
||||||
|
ViewProjectionMatrix |
||||||
|
ViewMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
DIFFUSEMAP_ALPHA : DiffuseMap |
||||||
|
NUM_BONES : NumberOfBones |
||||||
|
INSTANCING : UseInstancing |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Technique GBuf { |
||||||
|
|
||||||
|
VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert |
||||||
|
FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag |
||||||
|
|
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
NormalMatrix |
||||||
|
WorldViewMatrix |
||||||
|
WorldMatrix |
||||||
|
} |
||||||
|
|
||||||
|
Defines { |
||||||
|
VERTEX_COLOR : UseVertexColor |
||||||
|
MATERIAL_COLORS : UseMaterialColors |
||||||
|
V_TANGENT : VTangent |
||||||
|
MINNAERT : Minnaert |
||||||
|
WARDISO : WardIso |
||||||
|
|
||||||
|
DIFFUSEMAP : DiffuseMap |
||||||
|
NORMALMAP : NormalMap |
||||||
|
SPECULARMAP : SpecularMap |
||||||
|
PARALLAXMAP : ParallaxMap |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
#import "Common/ShaderLib/Instancing.glsllib" |
||||||
|
#import "Common/ShaderLib/Skinning.glsllib" |
||||||
|
|
||||||
|
uniform vec4 m_BaseColor; |
||||||
|
|
||||||
|
uniform vec4 g_AmbientLightColor; |
||||||
|
varying vec2 texCoord; |
||||||
|
|
||||||
|
#ifdef SEPARATE_TEXCOORD |
||||||
|
varying vec2 texCoord2; |
||||||
|
attribute vec2 inTexCoord2; |
||||||
|
#endif |
||||||
|
|
||||||
|
varying vec4 Color; |
||||||
|
|
||||||
|
attribute vec3 inPosition; |
||||||
|
attribute vec2 inTexCoord; |
||||||
|
attribute vec3 inNormal; |
||||||
|
|
||||||
|
#ifdef VERTEX_COLOR |
||||||
|
attribute vec4 inColor; |
||||||
|
#endif |
||||||
|
|
||||||
|
varying vec3 wNormal; |
||||||
|
varying vec3 wPosition; |
||||||
|
#if defined(NORMALMAP) || defined(PARALLAXMAP) |
||||||
|
attribute vec4 inTangent; |
||||||
|
varying vec4 wTangent; |
||||||
|
#endif |
||||||
|
|
||||||
|
void main(){ |
||||||
|
vec4 modelSpacePos = vec4(inPosition, 1.0); |
||||||
|
vec3 modelSpaceNorm = inNormal; |
||||||
|
|
||||||
|
#if ( defined(NORMALMAP) || defined(PARALLAXMAP)) && !defined(VERTEX_LIGHTING) |
||||||
|
vec3 modelSpaceTan = inTangent.xyz; |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef NUM_BONES |
||||||
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) |
||||||
|
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan); |
||||||
|
#else |
||||||
|
Skinning_Compute(modelSpacePos, modelSpaceNorm); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
gl_Position = TransformWorldViewProjection(modelSpacePos); |
||||||
|
texCoord = inTexCoord; |
||||||
|
#ifdef SEPARATE_TEXCOORD |
||||||
|
texCoord2 = inTexCoord2; |
||||||
|
#endif |
||||||
|
|
||||||
|
wPosition = TransformWorld(modelSpacePos).xyz; |
||||||
|
wNormal = TransformWorldNormal(modelSpaceNorm); |
||||||
|
|
||||||
|
#if defined(NORMALMAP) || defined(PARALLAXMAP) |
||||||
|
wTangent = vec4(TransformWorldNormal(modelSpaceTan),inTangent.w); |
||||||
|
#endif |
||||||
|
|
||||||
|
Color = m_BaseColor; |
||||||
|
|
||||||
|
#ifdef VERTEX_COLOR |
||||||
|
Color *= inColor; |
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
MaterialDef Simple { |
||||||
|
MaterialParameters { |
||||||
|
TextureCubeMap CubeMap |
||||||
|
} |
||||||
|
Technique { |
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
WorldMatrix |
||||||
|
CameraPosition |
||||||
|
} |
||||||
|
VertexShaderNodes { |
||||||
|
ShaderNode Reflect { |
||||||
|
Definition : Reflect : Common/MatDefs/ShaderNodes/Environment/reflect.j3sn |
||||||
|
InputMappings { |
||||||
|
normal = Attr.inNormal |
||||||
|
position = Global.position.xyz |
||||||
|
worldMatrix = WorldParam.WorldMatrix |
||||||
|
camPosition = WorldParam.CameraPosition |
||||||
|
} |
||||||
|
} |
||||||
|
ShaderNode CommonVert { |
||||||
|
Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn |
||||||
|
InputMappings { |
||||||
|
worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix |
||||||
|
modelPosition = Global.position.xyz |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.position = projPosition |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
FragmentShaderNodes { |
||||||
|
ShaderNode EnvMapping { |
||||||
|
Definition : EnvMapping : Common/MatDefs/ShaderNodes/Environment/envMapping.j3sn |
||||||
|
InputMappings { |
||||||
|
refVec = Reflect.refVec |
||||||
|
cubeMap = MatParam.CubeMap |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.color = color |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
ShaderNodeDefinitions{ |
||||||
|
ShaderNodeDefinition EnvMapping { |
||||||
|
Type: Fragment |
||||||
|
|
||||||
|
Shader GLSL100: Common/MatDefs/ShaderNodes/Environment/envMapping100.frag |
||||||
|
Shader GLSL130: Common/MatDefs/ShaderNodes/Environment/envMapping130.frag |
||||||
|
|
||||||
|
Documentation{ |
||||||
|
fetches a texel in a cube map |
||||||
|
@input vec3 refVec the reflection vector |
||||||
|
@input samplerCube cubeMap the cube map |
||||||
|
@output vec4 color the output color |
||||||
|
} |
||||||
|
Input { |
||||||
|
vec3 refVec |
||||||
|
samplerCube cubeMap |
||||||
|
} |
||||||
|
Output { |
||||||
|
vec4 color |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
|
||||||
|
void main(){ |
||||||
|
//@input vec3 refVec the reflection vector |
||||||
|
//@input samplerCube cubeMap the cube map |
||||||
|
//@output vec4 color the output color |
||||||
|
|
||||||
|
color = textureCubeLod(cubeMap, refVec, 0.0); |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
|
||||||
|
void main(){ |
||||||
|
//@input vec3 refVec the reflection vector |
||||||
|
//@input samplerCube cubeMap the cube map |
||||||
|
//@output vec4 color the output color |
||||||
|
|
||||||
|
color = textureLod(cubeMap, refVec, 0.0); |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
ShaderNodeDefinitions{ |
||||||
|
ShaderNodeDefinition Reflect { |
||||||
|
Type: Vertex |
||||||
|
|
||||||
|
Shader GLSL100: Common/MatDefs/ShaderNodes/Environment/reflect100.vert |
||||||
|
|
||||||
|
Documentation{ |
||||||
|
Computes the relfection vector necessary to do some environment mapping |
||||||
|
@input vec3 position position in model space |
||||||
|
@input vec3 normal the normal of the vertex |
||||||
|
@input vec3 camPosition camera position in world space |
||||||
|
@input mat4 worldMatrix the world matrix |
||||||
|
@output vec3 refVec the reflection vector |
||||||
|
} |
||||||
|
Input { |
||||||
|
vec3 position |
||||||
|
vec3 normal |
||||||
|
vec3 camPosition |
||||||
|
mat4 worldMatrix |
||||||
|
} |
||||||
|
Output { |
||||||
|
vec3 refVec |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
|
||||||
|
void main(){ |
||||||
|
//@input vec3 position position in model space |
||||||
|
//@input vec3 normal the normal of the vertex |
||||||
|
//@input vec3 camPosition camera position in world space |
||||||
|
//@input mat4 worldMatrix the world view matrix |
||||||
|
//@output vec3 refVec the reflection vector |
||||||
|
|
||||||
|
vec3 worldPos = (worldMatrix * vec4(position, 1.0)).xyz; |
||||||
|
vec3 N = normalize((worldMatrix * vec4(normal, 0.0)).xyz); |
||||||
|
vec3 I = normalize( camPosition - worldPos ).xyz; |
||||||
|
refVec.xyz = reflect(-I, N); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
|
||||||
|
#ifndef PI |
||||||
|
#define PI 3.14159265358979323846264 |
||||||
|
#endif |
||||||
|
|
||||||
|
//Specular fresnel computation |
||||||
|
vec3 F_Shlick(float vh, vec3 F0){ |
||||||
|
float fresnelFact = pow(2.0, (-5.55473*vh - 6.98316) * vh); |
||||||
|
return mix(F0, vec3(1.0, 1.0, 1.0), fresnelFact); |
||||||
|
} |
||||||
|
|
||||||
|
void PBR_ComputeDirectLightSpecWF(vec3 normal, vec3 lightDir, vec3 viewDir, |
||||||
|
vec3 lightColor, vec3 specColor, float roughness, float ndotv, |
||||||
|
out vec3 outDiffuse, out vec3 outSpecular){ |
||||||
|
// Compute halfway vector. |
||||||
|
vec3 halfVec = normalize(lightDir + viewDir); |
||||||
|
|
||||||
|
// Compute ndotl, ndoth, vdoth terms which are needed later. |
||||||
|
float ndotl = max( dot(normal, lightDir), 0.0); |
||||||
|
float ndoth = max( dot(normal, halfVec), 0.0); |
||||||
|
float hdotv = max( dot(viewDir, halfVec), 0.0); |
||||||
|
|
||||||
|
// Compute diffuse using energy-conserving Lambert. |
||||||
|
// Alternatively, use Oren-Nayar for really rough |
||||||
|
// materials or if you have lots of processing power ... |
||||||
|
outDiffuse = vec3(ndotl) * lightColor; |
||||||
|
|
||||||
|
//cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf |
||||||
|
|
||||||
|
float alpha = roughness * roughness; |
||||||
|
|
||||||
|
//D, GGX normaal Distribution function |
||||||
|
float alpha2 = alpha * alpha; |
||||||
|
float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0); |
||||||
|
float denom = PI * sum * sum; |
||||||
|
float D = alpha2 / denom; |
||||||
|
|
||||||
|
// Compute Fresnel function via Schlick's approximation. |
||||||
|
vec3 fresnel = F_Shlick(hdotv, specColor); |
||||||
|
|
||||||
|
//G Shchlick GGX Gometry shadowing term, k = alpha/2 |
||||||
|
float k = alpha * 0.5; |
||||||
|
|
||||||
|
// UE4 way to optimise shlick GGX Gometry shadowing term |
||||||
|
//http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html |
||||||
|
float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k ); |
||||||
|
float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k ); |
||||||
|
// the max here is to avoid division by 0 that may cause some small glitches. |
||||||
|
float G = 1.0/max( G_V * G_L ,0.01); |
||||||
|
|
||||||
|
float specular = D * G * ndotl; |
||||||
|
|
||||||
|
outSpecular = fresnel * vec3(specular) * lightColor; |
||||||
|
} |
||||||
|
|
||||||
|
void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir, |
||||||
|
vec3 lightColor, float fZero, float roughness, float ndotv, |
||||||
|
out vec3 outDiffuse, out vec3 outSpecular){ |
||||||
|
// Compute halfway vector. |
||||||
|
vec3 halfVec = normalize(lightDir + viewDir); |
||||||
|
|
||||||
|
// Compute ndotl, ndoth, vdoth terms which are needed later. |
||||||
|
float ndotl = max( dot(normal, lightDir), 0.0); |
||||||
|
float ndoth = max( dot(normal, halfVec), 0.0); |
||||||
|
float hdotv = max( dot(viewDir, halfVec), 0.0); |
||||||
|
|
||||||
|
// Compute diffuse using energy-conserving Lambert. |
||||||
|
// Alternatively, use Oren-Nayar for really rough |
||||||
|
// materials or if you have lots of processing power ... |
||||||
|
outDiffuse = vec3(ndotl) * lightColor; |
||||||
|
|
||||||
|
//cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf |
||||||
|
|
||||||
|
float alpha = roughness * roughness; |
||||||
|
|
||||||
|
//D, GGX normaal Distribution function |
||||||
|
float alpha2 = alpha * alpha; |
||||||
|
float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0); |
||||||
|
float denom = PI * sum * sum; |
||||||
|
float D = alpha2 / denom; |
||||||
|
|
||||||
|
// Compute Fresnel function via Schlick's approximation. |
||||||
|
float fresnel = fZero + ( 1.0 - fZero ) * pow( 2.0, (-5.55473 * hdotv - 6.98316) * hdotv); |
||||||
|
|
||||||
|
//G Shchlick GGX Gometry shadowing term, k = alpha/2 |
||||||
|
float k = alpha * 0.5; |
||||||
|
|
||||||
|
/* |
||||||
|
//classic Schlick ggx |
||||||
|
float G_V = ndotv / (ndotv * (1.0 - k) + k); |
||||||
|
float G_L = ndotl / (ndotl * (1.0 - k) + k); |
||||||
|
float G = ( G_V * G_L ); |
||||||
|
|
||||||
|
float specular =(D* fresnel * G) /(4 * ndotv); |
||||||
|
*/ |
||||||
|
|
||||||
|
// UE4 way to optimise shlick GGX Gometry shadowing term |
||||||
|
//http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html |
||||||
|
float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k ); |
||||||
|
float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k ); |
||||||
|
// the max here is to avoid division by 0 that may cause some small glitches. |
||||||
|
float G = 1.0/max( G_V * G_L ,0.01); |
||||||
|
|
||||||
|
float specular = D * fresnel * G * ndotl; |
||||||
|
|
||||||
|
outSpecular = vec3(specular) * lightColor; |
||||||
|
} |
||||||
|
|
||||||
|
//https://knarkowicz.wordpress.com/2014/12/27/analytical-dfg-term-for-ibl/ |
||||||
|
vec3 EnvDFGPolynomial( vec3 specularColor, float roughness, float ndotv ){ |
||||||
|
float x = 1.0 - roughness; |
||||||
|
float y = ndotv; |
||||||
|
|
||||||
|
float b1 = -0.1688; |
||||||
|
float b2 = 1.895; |
||||||
|
float b3 = 0.9903; |
||||||
|
float b4 = -4.853; |
||||||
|
float b5 = 8.404; |
||||||
|
float b6 = -5.069; |
||||||
|
float bias = clamp( min( b1 * x + b2 * x * x, b3 + b4 * y + b5 * y * y + b6 * y * y * y ), 0.0, 1.0 ); |
||||||
|
|
||||||
|
float d0 = 0.6045; |
||||||
|
float d1 = 1.699; |
||||||
|
float d2 = -0.5228; |
||||||
|
float d3 = -3.603; |
||||||
|
float d4 = 1.404; |
||||||
|
float d5 = 0.1939; |
||||||
|
float d6 = 2.661; |
||||||
|
float delta = clamp(( d0 + d1 * x + d2 * y + d3 * x * x + d4 * x * y + d5 * y * y + d6 * x * x * x ), 0.0, 1.0); |
||||||
|
float scale = delta - bias; |
||||||
|
|
||||||
|
bias *= clamp( 2.5 / (roughness) * specularColor.y, 0.0, 1.0 ); |
||||||
|
return specularColor * scale + bias; |
||||||
|
} |
||||||
|
|
||||||
|
vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ |
||||||
|
//TODO magic values should be replaced by defines. |
||||||
|
float Lod = log2(Roughness) * 1.1 + 6.0 - 2.0; |
||||||
|
vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb; |
||||||
|
vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg; |
||||||
|
return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y ); |
||||||
|
} |
||||||
|
|
||||||
|
vec3 ApproximateSpecularIBLPolynomial(samplerCube envMap, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){ |
||||||
|
//TODO magic values should be replaced by defines. |
||||||
|
float Lod = log2(Roughness) * 1.5 + 6.0 - 1.0; |
||||||
|
vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz, Lod).rgb; |
||||||
|
return PrefilteredColor * EnvDFGPolynomial(SpecularColor, Roughness, ndotv); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,354 @@ |
|||||||
|
/* |
||||||
|
* 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.texture.plugins.ktx; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo; |
||||||
|
import com.jme3.asset.AssetLoader; |
||||||
|
import com.jme3.asset.TextureKey; |
||||||
|
import com.jme3.renderer.Caps; |
||||||
|
import com.jme3.renderer.opengl.GLImageFormat; |
||||||
|
import com.jme3.renderer.opengl.GLImageFormats; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import com.jme3.util.LittleEndien; |
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.EnumSet; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* A KTX file loader |
||||||
|
* KTX file format is an image container defined by the Kronos group |
||||||
|
* See specs here https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
|
||||||
|
* |
||||||
|
* This loader doesn't support compressed files yet. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class KTXLoader implements AssetLoader { |
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger(KTXLoader.class.getName()); |
||||||
|
|
||||||
|
private final static byte[] fileIdentifier = { |
||||||
|
(byte) 0xAB, (byte) 0x4B, (byte) 0x54, (byte) 0x58, (byte) 0x20, (byte) 0x31, (byte) 0x31, (byte) 0xBB, (byte) 0x0D, (byte) 0x0A, (byte) 0x1A, (byte) 0x0A |
||||||
|
}; |
||||||
|
private boolean slicesInside = false; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object load(AssetInfo info) throws IOException { |
||||||
|
if (!(info.getKey() instanceof TextureKey)) { |
||||||
|
throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey"); |
||||||
|
} |
||||||
|
|
||||||
|
InputStream in = null; |
||||||
|
try { |
||||||
|
in = info.openStream(); |
||||||
|
Image img = load(in); |
||||||
|
return img; |
||||||
|
} finally { |
||||||
|
if (in != null) { |
||||||
|
in.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Image load(InputStream stream) { |
||||||
|
|
||||||
|
byte[] fileId = new byte[12]; |
||||||
|
|
||||||
|
DataInput in = new DataInputStream(stream); |
||||||
|
try { |
||||||
|
stream.read(fileId, 0, 12); |
||||||
|
if (!checkFileIdentifier(fileId)) { |
||||||
|
throw new IllegalArgumentException("Unrecognized ktx file identifier : " + new String(fileId) + " should be " + new String(fileIdentifier)); |
||||||
|
} |
||||||
|
|
||||||
|
int endianness = in.readInt(); |
||||||
|
//opposite endianness
|
||||||
|
if (endianness == 0x01020304) { |
||||||
|
in = new LittleEndien(stream); |
||||||
|
} |
||||||
|
int glType = in.readInt(); |
||||||
|
int glTypeSize = in.readInt(); |
||||||
|
int glFormat = in.readInt(); |
||||||
|
int glInternalFormat = in.readInt(); |
||||||
|
int glBaseInternalFormat = in.readInt(); |
||||||
|
int pixelWidth = in.readInt(); |
||||||
|
int pixelHeight = in.readInt(); |
||||||
|
int pixelDepth = in.readInt(); |
||||||
|
int numberOfArrayElements = in.readInt(); |
||||||
|
int numberOfFaces = in.readInt(); |
||||||
|
int numberOfMipmapLevels = in.readInt(); |
||||||
|
int bytesOfKeyValueData = in.readInt(); |
||||||
|
|
||||||
|
log.log(Level.FINE, "glType = {0}", glType); |
||||||
|
log.log(Level.FINE, "glTypeSize = {0}", glTypeSize); |
||||||
|
log.log(Level.FINE, "glFormat = {0}", glFormat); |
||||||
|
log.log(Level.FINE, "glInternalFormat = {0}", glInternalFormat); |
||||||
|
log.log(Level.FINE, "glBaseInternalFormat = {0}", glBaseInternalFormat); |
||||||
|
log.log(Level.FINE, "pixelWidth = {0}", pixelWidth); |
||||||
|
log.log(Level.FINE, "pixelHeight = {0}", pixelHeight); |
||||||
|
log.log(Level.FINE, "pixelDepth = {0}", pixelDepth); |
||||||
|
log.log(Level.FINE, "numberOfArrayElements = {0}", numberOfArrayElements); |
||||||
|
log.log(Level.FINE, "numberOfFaces = {0}", numberOfFaces); |
||||||
|
log.log(Level.FINE, "numberOfMipmapLevels = {0}", numberOfMipmapLevels); |
||||||
|
log.log(Level.FINE, "bytesOfKeyValueData = {0}", bytesOfKeyValueData); |
||||||
|
|
||||||
|
if((numberOfFaces >1 && pixelDepth >1) || (numberOfFaces >1 && numberOfArrayElements >1) || (pixelDepth >1 && numberOfArrayElements >1)){ |
||||||
|
throw new UnsupportedOperationException("jME doesn't support cube maps of 3D textures or arrays of 3D texture or arrays of cube map of 3d textures"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
PixelReader pixelReader = parseMetaData(bytesOfKeyValueData, in); |
||||||
|
if (pixelReader == null){ |
||||||
|
pixelReader = new SrTuRoPixelReader(); |
||||||
|
} |
||||||
|
|
||||||
|
//some of the values may be 0 we need them at least to be 1
|
||||||
|
pixelDepth = Math.max(1, pixelDepth); |
||||||
|
numberOfArrayElements = Math.max(1, numberOfArrayElements); |
||||||
|
numberOfFaces = Math.max(1, numberOfFaces); |
||||||
|
numberOfMipmapLevels = Math.max(1, numberOfMipmapLevels); |
||||||
|
|
||||||
|
int nbSlices = Math.max(numberOfFaces,numberOfArrayElements); |
||||||
|
|
||||||
|
Image.Format imgFormat = getImageFormat(glFormat, glInternalFormat, glType); |
||||||
|
log.log(Level.FINE, "img format {0}", imgFormat.toString()); |
||||||
|
|
||||||
|
|
||||||
|
int bytePerPixel = imgFormat.getBitsPerPixel() / 8; |
||||||
|
int byteBuffersSize = computeBuffersSize(numberOfMipmapLevels, pixelWidth, pixelHeight, bytePerPixel, pixelDepth); |
||||||
|
log.log(Level.FINE, "data size {0}", byteBuffersSize); |
||||||
|
|
||||||
|
int[] mipMapSizes = new int[numberOfMipmapLevels]; |
||||||
|
|
||||||
|
Image image = createImage(nbSlices, byteBuffersSize, imgFormat, pixelWidth, pixelHeight, pixelDepth); |
||||||
|
|
||||||
|
byte[] pixelData = new byte[bytePerPixel]; |
||||||
|
|
||||||
|
int offset = 0; |
||||||
|
//iterate over data
|
||||||
|
for (int mipLevel = 0; mipLevel < numberOfMipmapLevels; mipLevel++) { |
||||||
|
//size of the image in byte.
|
||||||
|
//this value is bogus in many example, when using mipmaps.
|
||||||
|
//instead we compute the theorical size and display a warning when it does not match.
|
||||||
|
int fileImageSize = in.readInt(); |
||||||
|
|
||||||
|
int width = Math.max(1, pixelWidth >> mipLevel); |
||||||
|
int height = Math.max(1, pixelHeight >> mipLevel); |
||||||
|
|
||||||
|
int imageSize = width * height * bytePerPixel; |
||||||
|
mipMapSizes[mipLevel] = imageSize; |
||||||
|
log.log(Level.FINE, "current mip size {0}", imageSize); |
||||||
|
if(fileImageSize != imageSize){ |
||||||
|
log.log(Level.WARNING, "Mip map size is wrong in the file for mip level {0} size is {1} should be {2}", new Object[]{mipLevel, fileImageSize, imageSize}); |
||||||
|
} |
||||||
|
|
||||||
|
for (int arrayElem = 0; arrayElem < numberOfArrayElements; arrayElem++) { |
||||||
|
for (int face = 0; face < numberOfFaces; face++) { |
||||||
|
int nbPixelRead = 0; |
||||||
|
for (int depth = 0; depth < pixelDepth; depth++) { |
||||||
|
ByteBuffer byteBuffer = image.getData(getSlice(face, arrayElem)); |
||||||
|
|
||||||
|
log.log(Level.FINE, "position {0}", byteBuffer.position()); |
||||||
|
byteBuffer.position(offset); |
||||||
|
nbPixelRead = pixelReader.readPixels(width, height, pixelData, byteBuffer, in); |
||||||
|
} |
||||||
|
//cube padding
|
||||||
|
if (numberOfFaces == 6 && numberOfArrayElements == 0) { |
||||||
|
in.skipBytes(3 - ((nbPixelRead + 3) % 4)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
//mip padding
|
||||||
|
log.log(Level.FINE, "skipping {0}", (3 - ((imageSize + 3) % 4))); |
||||||
|
in.skipBytes(3 - ((imageSize + 3) % 4)); |
||||||
|
offset+=imageSize; |
||||||
|
} |
||||||
|
//there are loaded mip maps we set the sizes
|
||||||
|
if(numberOfMipmapLevels >1){ |
||||||
|
image.setMipMapSizes(mipMapSizes); |
||||||
|
} |
||||||
|
//if 3D texture and slices' orientation is inside, we reverse the data array.
|
||||||
|
if(pixelDepth > 1 && slicesInside){ |
||||||
|
Collections.reverse(image.getData()); |
||||||
|
} |
||||||
|
return image; |
||||||
|
|
||||||
|
} catch (IOException ex) { |
||||||
|
Logger.getLogger(KTXLoader.class.getName()).log(Level.SEVERE, null, ex); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the slice from the face and the array index |
||||||
|
* @param face the face |
||||||
|
* @param arrayElem the array index |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static int getSlice(int face, int arrayElem) { |
||||||
|
return Math.max(face, arrayElem); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes a buffer size from given parameters |
||||||
|
* @param numberOfMipmapLevels |
||||||
|
* @param pixelWidth |
||||||
|
* @param pixelHeight |
||||||
|
* @param bytePerPixel |
||||||
|
* @param pixelDepth |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private int computeBuffersSize(int numberOfMipmapLevels, int pixelWidth, int pixelHeight, int bytePerPixel, int pixelDepth) { |
||||||
|
int byteBuffersSize = 0; |
||||||
|
for (int mipLevel = 0; mipLevel < numberOfMipmapLevels; mipLevel++) { |
||||||
|
int width = Math.max(1, pixelWidth >> mipLevel); |
||||||
|
int height = Math.max(1, pixelHeight >> mipLevel); |
||||||
|
byteBuffersSize += width * height * bytePerPixel; |
||||||
|
log.log(Level.FINE, "mip level size : {0} : {1}", new Object[]{mipLevel, width * height * bytePerPixel}); |
||||||
|
} |
||||||
|
return byteBuffersSize * pixelDepth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an image with given parameters |
||||||
|
* @param nbSlices |
||||||
|
* @param byteBuffersSize |
||||||
|
* @param imgFormat |
||||||
|
* @param pixelWidth |
||||||
|
* @param pixelHeight |
||||||
|
* @param depth |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private Image createImage(int nbSlices, int byteBuffersSize, Image.Format imgFormat, int pixelWidth, int pixelHeight, int depth) { |
||||||
|
ArrayList<ByteBuffer> imageData = new ArrayList<ByteBuffer>(nbSlices); |
||||||
|
for (int i = 0; i < nbSlices; i++) { |
||||||
|
imageData.add(BufferUtils.createByteBuffer(byteBuffersSize)); |
||||||
|
} |
||||||
|
Image image = new Image(imgFormat, pixelWidth, pixelHeight, depth, imageData, ColorSpace.sRGB); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parse the file metaData to select the PixelReader that suits the file |
||||||
|
* coordinates orientation |
||||||
|
* @param bytesOfKeyValueData |
||||||
|
* @param in |
||||||
|
* @return |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private PixelReader parseMetaData(int bytesOfKeyValueData, DataInput in) throws IOException { |
||||||
|
PixelReader pixelReader = null; |
||||||
|
for (int i = 0; i < bytesOfKeyValueData;) { |
||||||
|
//reading key values
|
||||||
|
int keyAndValueByteSize = in.readInt(); |
||||||
|
byte[] keyValue = new byte[keyAndValueByteSize]; |
||||||
|
in.readFully(keyValue); |
||||||
|
|
||||||
|
|
||||||
|
//parsing key values
|
||||||
|
String[] kv = new String(keyValue).split("\0"); |
||||||
|
for (int j = 0; j < kv.length; j += 2) { |
||||||
|
System.err.println("key : " + kv[j]); |
||||||
|
System.err.println("value : " + kv[j + 1]); |
||||||
|
if(kv[j].equalsIgnoreCase("KTXorientation")){ |
||||||
|
if(kv[j + 1].startsWith("S=r,T=d") ){ |
||||||
|
pixelReader = new SrTdRiPixelReader(); |
||||||
|
}else{ |
||||||
|
pixelReader = new SrTuRoPixelReader(); |
||||||
|
} |
||||||
|
if(kv[j + 1].contains("R=i")){ |
||||||
|
slicesInside = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//padding
|
||||||
|
int padding = 3 - ((keyAndValueByteSize + 3) % 4); |
||||||
|
if (padding > 0) { |
||||||
|
in.skipBytes(padding); |
||||||
|
} |
||||||
|
i += 4 + keyAndValueByteSize + padding; |
||||||
|
} |
||||||
|
return pixelReader; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Chacks the file id |
||||||
|
* @param b |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private boolean checkFileIdentifier(byte[] b) { |
||||||
|
boolean check = true; |
||||||
|
for (int i = 0; i < 12; i++) { |
||||||
|
if (b[i] != fileIdentifier[i]) { |
||||||
|
check = false; |
||||||
|
} |
||||||
|
} |
||||||
|
return check; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the JME image format from gl formats and types. |
||||||
|
* @param glFormat |
||||||
|
* @param glInternalFormat |
||||||
|
* @param glType |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private Image.Format getImageFormat(int glFormat, int glInternalFormat, int glType) { |
||||||
|
EnumSet<Caps> caps = EnumSet.allOf(Caps.class); |
||||||
|
GLImageFormat[][] formats = GLImageFormats.getFormatsForCaps(caps); |
||||||
|
for (GLImageFormat[] format : formats) { |
||||||
|
for (int j = 0; j < format.length; j++) { |
||||||
|
GLImageFormat glImgFormat = format[j]; |
||||||
|
if (glImgFormat != null) { |
||||||
|
if (glImgFormat.format == glFormat && glImgFormat.dataType == glType) { |
||||||
|
if (glFormat == glInternalFormat || glImgFormat.internalFormat == glInternalFormat) { |
||||||
|
return Image.Format.values()[j]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,279 @@ |
|||||||
|
/* |
||||||
|
* 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.texture.plugins.ktx; |
||||||
|
|
||||||
|
import com.jme3.renderer.Caps; |
||||||
|
import com.jme3.renderer.opengl.GLImageFormat; |
||||||
|
import com.jme3.renderer.opengl.GLImageFormats; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.Texture3D; |
||||||
|
import com.jme3.texture.TextureArray; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import java.io.DataOutput; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.EnumSet; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* This class allows one to write a KTX file. |
||||||
|
* It doesn't support compressed data yet. |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class KTXWriter { |
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger(KTXWriter.class.getName()); |
||||||
|
|
||||||
|
private final String filePath; |
||||||
|
|
||||||
|
private final static byte[] fileIdentifier = { |
||||||
|
(byte) 0xAB, (byte) 0x4B, (byte) 0x54, (byte) 0x58, (byte) 0x20, (byte) 0x31, (byte) 0x31, (byte) 0xBB, (byte) 0x0D, (byte) 0x0A, (byte) 0x1A, (byte) 0x0A |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a KTXWriter that will write files in the given path |
||||||
|
* @param path |
||||||
|
*/ |
||||||
|
public KTXWriter(String path) { |
||||||
|
filePath = path; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a 2D image from the given image in a KTX file named from the fileName param |
||||||
|
* Note that the fileName should contain the extension (.ktx sounds like a wise choice) |
||||||
|
* @param image the image to write |
||||||
|
* @param fileName the name of the file to write |
||||||
|
*/ |
||||||
|
public void write(Image image, String fileName) { |
||||||
|
write(image, Texture2D.class, fileName); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes an image with the given params |
||||||
|
* |
||||||
|
* textureType, allows one to write textureArrays, Texture3D, and TextureCubeMaps. |
||||||
|
* Texture2D will write a 2D image. |
||||||
|
* Note that the fileName should contain the extension (.ktx sounds like a wise choice) |
||||||
|
* @param image the image to write |
||||||
|
* @param textureType the texture type |
||||||
|
* @param fileName the name of the file to write |
||||||
|
*/ |
||||||
|
public void write(Image image, Class<? extends Texture> textureType, String fileName) { |
||||||
|
|
||||||
|
FileOutputStream outs = null; |
||||||
|
try { |
||||||
|
File file = new File(filePath + "/" + fileName); |
||||||
|
outs = new FileOutputStream(file); |
||||||
|
|
||||||
|
DataOutput out = new DataOutputStream(outs); |
||||||
|
|
||||||
|
//fileID
|
||||||
|
out.write(fileIdentifier); |
||||||
|
//endianness
|
||||||
|
out.writeInt(0x04030201); |
||||||
|
GLImageFormat format = getGlFormat(image.getFormat()); |
||||||
|
//glType
|
||||||
|
out.writeInt(format.dataType); |
||||||
|
//glTypeSize
|
||||||
|
out.writeInt(1); |
||||||
|
//glFormat
|
||||||
|
out.writeInt(format.format); |
||||||
|
//glInernalFormat
|
||||||
|
out.writeInt(format.internalFormat); |
||||||
|
//glBaseInternalFormat
|
||||||
|
out.writeInt(format.format); |
||||||
|
//pixelWidth
|
||||||
|
out.writeInt(image.getWidth()); |
||||||
|
//pixelHeight
|
||||||
|
out.writeInt(image.getHeight()); |
||||||
|
|
||||||
|
int pixelDepth = 1; |
||||||
|
int numberOfArrayElements = 1; |
||||||
|
int numberOfFaces = 1; |
||||||
|
if (image.getDepth() > 1) { |
||||||
|
//pixelDepth
|
||||||
|
if (textureType == Texture3D.class) { |
||||||
|
pixelDepth = image.getDepth(); |
||||||
|
} |
||||||
|
} |
||||||
|
if(image.getData().size()>1){ |
||||||
|
//numberOfArrayElements
|
||||||
|
if (textureType == TextureArray.class) { |
||||||
|
numberOfArrayElements = image.getData().size(); |
||||||
|
} |
||||||
|
//numberOfFaces
|
||||||
|
if (textureType == TextureCubeMap.class) { |
||||||
|
numberOfFaces = image.getData().size(); |
||||||
|
} |
||||||
|
} |
||||||
|
out.writeInt(pixelDepth); |
||||||
|
out.writeInt(numberOfArrayElements); |
||||||
|
out.writeInt(numberOfFaces); |
||||||
|
|
||||||
|
int numberOfMipmapLevels = 1; |
||||||
|
//numberOfMipmapLevels
|
||||||
|
if (image.hasMipmaps()) { |
||||||
|
numberOfMipmapLevels = image.getMipMapSizes().length; |
||||||
|
} |
||||||
|
out.writeInt(numberOfMipmapLevels); |
||||||
|
|
||||||
|
//bytesOfKeyValueData
|
||||||
|
String keyValues = "KTXorientation\0S=r,T=u\0"; |
||||||
|
int bytesOfKeyValueData = keyValues.length() + 4; |
||||||
|
int padding = 3 - ((bytesOfKeyValueData + 3) % 4); |
||||||
|
bytesOfKeyValueData += padding; |
||||||
|
out.writeInt(bytesOfKeyValueData); |
||||||
|
|
||||||
|
//keyAndValueByteSize
|
||||||
|
out.writeInt(bytesOfKeyValueData - 4 - padding); |
||||||
|
//values
|
||||||
|
out.writeBytes(keyValues); |
||||||
|
pad(padding, out); |
||||||
|
|
||||||
|
int offset = 0; |
||||||
|
//iterate over data
|
||||||
|
for (int mipLevel = 0; mipLevel < numberOfMipmapLevels; mipLevel++) { |
||||||
|
|
||||||
|
int width = Math.max(1, image.getWidth() >> mipLevel); |
||||||
|
int height = Math.max(1, image.getHeight() >> mipLevel); |
||||||
|
|
||||||
|
int imageSize; |
||||||
|
|
||||||
|
if (image.hasMipmaps()) { |
||||||
|
imageSize = image.getMipMapSizes()[mipLevel]; |
||||||
|
} else { |
||||||
|
imageSize = width * height * image.getFormat().getBitsPerPixel() / 8; |
||||||
|
} |
||||||
|
out.writeInt(imageSize); |
||||||
|
|
||||||
|
for (int arrayElem = 0; arrayElem < numberOfArrayElements; arrayElem++) { |
||||||
|
for (int face = 0; face < numberOfFaces; face++) { |
||||||
|
int nbPixelWritten = 0; |
||||||
|
for (int depth = 0; depth < pixelDepth; depth++) { |
||||||
|
ByteBuffer byteBuffer = image.getData(getSlice(face, arrayElem)); |
||||||
|
// BufferUtils.ensureLargeEnough(byteBuffer, imageSize);
|
||||||
|
log.log(Level.FINE, "position {0}", byteBuffer.position()); |
||||||
|
byteBuffer.position(offset); |
||||||
|
byte[] b = getByteBufferArray(byteBuffer, imageSize); |
||||||
|
out.write(b); |
||||||
|
|
||||||
|
nbPixelWritten = b.length; |
||||||
|
} |
||||||
|
//cube padding
|
||||||
|
if (numberOfFaces == 6 && numberOfArrayElements == 0) { |
||||||
|
padding = 3 - ((nbPixelWritten + 3) % 4); |
||||||
|
pad(padding, out); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
//mip padding
|
||||||
|
log.log(Level.FINE, "skipping {0}", (3 - ((imageSize + 3) % 4))); |
||||||
|
padding = 3 - ((imageSize + 3) % 4); |
||||||
|
pad(padding, out); |
||||||
|
offset += imageSize; |
||||||
|
} |
||||||
|
|
||||||
|
} catch (FileNotFoundException ex) { |
||||||
|
Logger.getLogger(KTXWriter.class.getName()).log(Level.SEVERE, null, ex); |
||||||
|
} catch (IOException ex) { |
||||||
|
Logger.getLogger(KTXWriter.class.getName()).log(Level.SEVERE, null, ex); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if(outs != null){ |
||||||
|
outs.close(); |
||||||
|
} |
||||||
|
} catch (IOException ex) { |
||||||
|
Logger.getLogger(KTXWriter.class.getName()).log(Level.SEVERE, null, ex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* writes padding data to the output padding times. |
||||||
|
* @param padding |
||||||
|
* @param out |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private void pad(int padding, DataOutput out) throws IOException { |
||||||
|
//padding
|
||||||
|
for (int i = 0; i < padding; i++) { |
||||||
|
out.write('\0'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a byte array from a byte buffer. |
||||||
|
* @param byteBuffer the byte buffer |
||||||
|
* @param size the size of the resulting array |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private byte[] getByteBufferArray(ByteBuffer byteBuffer, int size) { |
||||||
|
byte[] b; |
||||||
|
if (byteBuffer.hasArray()) { |
||||||
|
b = byteBuffer.array(); |
||||||
|
} else { |
||||||
|
b = new byte[size]; |
||||||
|
byteBuffer.get(b, 0, size); |
||||||
|
} |
||||||
|
return b; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get the glformat from JME image Format |
||||||
|
* @param format |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private GLImageFormat getGlFormat(Image.Format format) { |
||||||
|
EnumSet<Caps> caps = EnumSet.allOf(Caps.class); |
||||||
|
GLImageFormat[][] formats = GLImageFormats.getFormatsForCaps(caps); |
||||||
|
return formats[0][format.ordinal()]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get a slice from the face and the array index |
||||||
|
* @param face |
||||||
|
* @param arrayElem |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static int getSlice(int face,int arrayElem) { |
||||||
|
return Math.max(face, arrayElem); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* 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.texture.plugins.ktx; |
||||||
|
|
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* Interface used to read a set of pixels in a KTX file |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public interface PixelReader { |
||||||
|
|
||||||
|
public int readPixels(int pixelWidth, int pixelHeight, byte[] pixelData, ByteBuffer buffer, DataInput in) throws IOException; |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* 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.texture.plugins.ktx; |
||||||
|
|
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* reads the pixels of an image whose origin is the top left corner |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class SrTdRiPixelReader implements PixelReader { |
||||||
|
|
||||||
|
@Override |
||||||
|
public int readPixels(int pixelWidth, int pixelHeight, byte[] pixelData, ByteBuffer buffer, DataInput in) throws IOException { |
||||||
|
int pixelRead = 0; |
||||||
|
for (int row = pixelHeight - 1; row >= 0; row--) { |
||||||
|
for (int pixel = 0; pixel < pixelWidth; pixel++) { |
||||||
|
in.readFully(pixelData); |
||||||
|
for (int i = 0; i < pixelData.length; i++) { |
||||||
|
buffer.put((row * pixelWidth + pixel) * pixelData.length + i, pixelData[i]); |
||||||
|
} |
||||||
|
pixelRead += pixelData.length; |
||||||
|
} |
||||||
|
} |
||||||
|
return pixelRead; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* 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.texture.plugins.ktx; |
||||||
|
|
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* reads the pixels of an image whose origin is the bottom left corner |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class SrTuRoPixelReader implements PixelReader { |
||||||
|
|
||||||
|
@Override |
||||||
|
public int readPixels(int pixelWidth, int pixelHeight, byte[] pixelData, ByteBuffer buffer, DataInput in) throws IOException { |
||||||
|
int pixelRead = 0; |
||||||
|
for (int row = 0; row < pixelHeight; row++) { |
||||||
|
for (int pixel = 0; pixel < pixelWidth; pixel++) { |
||||||
|
in.readFully(pixelData); |
||||||
|
buffer.put(pixelData); |
||||||
|
pixelRead += pixelData.length; |
||||||
|
} |
||||||
|
} |
||||||
|
return pixelRead; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
package jme3test.light; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.ChaseCamera; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.AnalogListener; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.post.FilterPostProcessor; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.shadow.DirectionalLightShadowFilter; |
||||||
|
import com.jme3.shadow.DirectionalLightShadowRenderer; |
||||||
|
|
||||||
|
public class TestColorApp extends SimpleApplication { |
||||||
|
public static void main(String[] args) { |
||||||
|
TestColorApp app = new TestColorApp(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
// Lights
|
||||||
|
DirectionalLight sun = new DirectionalLight(); |
||||||
|
Vector3f sunPosition = new Vector3f(1, -1, 1); |
||||||
|
sun.setDirection(sunPosition); |
||||||
|
sun.setColor(new ColorRGBA(1f,1f,1f,1f)); |
||||||
|
rootNode.addLight(sun); |
||||||
|
|
||||||
|
//DirectionalLightShadowFilter sun_renderer = new DirectionalLightShadowFilter(assetManager, 2048, 4);
|
||||||
|
DirectionalLightShadowRenderer sun_renderer = new DirectionalLightShadowRenderer(assetManager, 2048, 1); |
||||||
|
sun_renderer.setLight(sun); |
||||||
|
viewPort.addProcessor(sun_renderer); |
||||||
|
|
||||||
|
// FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
||||||
|
// fpp.addFilter(sun_renderer);
|
||||||
|
// viewPort.addProcessor(fpp);
|
||||||
|
|
||||||
|
rootNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); |
||||||
|
|
||||||
|
// Camera
|
||||||
|
viewPort.setBackgroundColor(new ColorRGBA(.6f, .6f, .6f, 1f)); |
||||||
|
ChaseCamera chaseCam = new ChaseCamera(cam, inputManager); |
||||||
|
|
||||||
|
|
||||||
|
// Objects
|
||||||
|
// Ground Object
|
||||||
|
final Geometry groundBoxWhite = new Geometry("Box", new Box(7.5f, 7.5f, .25f)); |
||||||
|
float[] f = {-FastMath.PI / 2, 3 * FastMath.PI / 2, 0f}; |
||||||
|
groundBoxWhite.setLocalRotation(new Quaternion(f)); |
||||||
|
groundBoxWhite.move(7.5f, -.75f, 7.5f); |
||||||
|
final Material groundMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
groundMaterial.setColor("Diffuse", new ColorRGBA(.9f, .9f, .9f, .9f)); |
||||||
|
groundBoxWhite.setMaterial(groundMaterial); |
||||||
|
groundBoxWhite.addControl(chaseCam); |
||||||
|
rootNode.attachChild(groundBoxWhite); |
||||||
|
|
||||||
|
// Planter
|
||||||
|
Geometry planterBox = new Geometry("Box", new Box(.5f, .5f, .5f)); |
||||||
|
final Material planterMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
planterMaterial.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg")); |
||||||
|
planterBox.setMaterial(groundMaterial); |
||||||
|
planterBox.setLocalTranslation(10, 0, 9); |
||||||
|
rootNode.attachChild(planterBox); |
||||||
|
|
||||||
|
// Action!
|
||||||
|
inputManager.addMapping("on", new KeyTrigger(KeyInput.KEY_Z)); |
||||||
|
inputManager.addMapping("off", new KeyTrigger(KeyInput.KEY_X)); |
||||||
|
|
||||||
|
inputManager.addListener(new AnalogListener() { |
||||||
|
@Override |
||||||
|
public void onAnalog(String s, float v, float v1) { |
||||||
|
if (s.equals("on")) { |
||||||
|
groundBoxWhite.setMaterial(planterMaterial); |
||||||
|
} |
||||||
|
if (s.equals("off")) { |
||||||
|
groundBoxWhite.setMaterial(groundMaterial); |
||||||
|
} |
||||||
|
} |
||||||
|
}, "on", "off"); |
||||||
|
|
||||||
|
inputEnabled = true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.light; |
||||||
|
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
import com.jme3.light.SpotLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.shadow.EdgeFilteringMode; |
||||||
|
import com.jme3.shadow.PointLightShadowRenderer; |
||||||
|
import com.jme3.shadow.SpotLightShadowRenderer; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
|
||||||
|
|
||||||
|
public class TestShadowBug extends SimpleApplication { |
||||||
|
public static void main(String[] args) { |
||||||
|
TestShadowBug app = new TestShadowBug(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
flyCam.setMoveSpeed(100f); |
||||||
|
rootNode.attachChild(makeFloor()); |
||||||
|
|
||||||
|
Node characters = new Node("Characters"); |
||||||
|
characters.setShadowMode(ShadowMode.Cast); |
||||||
|
rootNode.attachChild(characters); |
||||||
|
|
||||||
|
Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml"); |
||||||
|
golem.scale(0.5f); |
||||||
|
golem.setLocalTranslation(200.0f, -6f, 200f); |
||||||
|
golem.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
characters.attachChild(golem); |
||||||
|
|
||||||
|
DirectionalLight sun = new DirectionalLight(); |
||||||
|
sun.setDirection(new Vector3f(-1f, -1f, 1f)); |
||||||
|
sun.setColor(ColorRGBA.White.mult(1.3f)); |
||||||
|
rootNode.addLight(sun); |
||||||
|
characters.addLight(sun); |
||||||
|
|
||||||
|
SpotLight spot = new SpotLight(); |
||||||
|
spot.setSpotRange(13f); // distance
|
||||||
|
spot.setSpotInnerAngle(15f * FastMath.DEG_TO_RAD); // inner light cone (central beam)
|
||||||
|
spot.setSpotOuterAngle(20f * FastMath.DEG_TO_RAD); // outer light cone (edge of the light)
|
||||||
|
spot.setColor(ColorRGBA.White.mult(1.3f)); // light color
|
||||||
|
spot.setPosition(new Vector3f(192.0f, -1f, 192f)); |
||||||
|
spot.setDirection(new Vector3f(1, -0.5f, 1)); |
||||||
|
rootNode.addLight(spot); |
||||||
|
|
||||||
|
PointLight lamp_light = new PointLight(); |
||||||
|
lamp_light.setColor(ColorRGBA.Yellow); |
||||||
|
lamp_light.setRadius(20f); |
||||||
|
lamp_light.setPosition(new Vector3f(210.0f, 0f, 210f)); |
||||||
|
rootNode.addLight(lamp_light); |
||||||
|
|
||||||
|
SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512); |
||||||
|
slsr.setLight(spot); |
||||||
|
slsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); |
||||||
|
slsr.setShadowIntensity(0.6f); |
||||||
|
slsr.setFlushQueues(false); |
||||||
|
viewPort.addProcessor(slsr); |
||||||
|
|
||||||
|
PointLightShadowRenderer plsr = new PointLightShadowRenderer(assetManager, 512); |
||||||
|
plsr.setLight(lamp_light); |
||||||
|
plsr.setShadowIntensity(0.6f); |
||||||
|
plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); |
||||||
|
plsr.setFlushQueues(false); |
||||||
|
viewPort.addProcessor(plsr); |
||||||
|
|
||||||
|
viewPort.getCamera().setLocation(new Vector3f(192.0f, 10f, 192f)); |
||||||
|
float[] angles = new float[]{3.14f/2, 3.14f/2, 0}; |
||||||
|
viewPort.getCamera().setRotation(new Quaternion(angles)); |
||||||
|
} |
||||||
|
|
||||||
|
protected Geometry makeFloor() { |
||||||
|
Box box = new Box(220, .2f, 220); |
||||||
|
box.scaleTextureCoordinates(new Vector2f(10, 10)); |
||||||
|
Geometry floor = new Geometry("the Floor", box); |
||||||
|
floor.setLocalTranslation(200, -9, 200); |
||||||
|
Material matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); |
||||||
|
grass.setWrap(WrapMode.Repeat); |
||||||
|
matGroundL.setTexture("DiffuseMap", grass); |
||||||
|
floor.setMaterial(matGroundL); |
||||||
|
floor.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
return floor; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.light; |
||||||
|
|
||||||
|
import com.jme3.app.ChaseCameraAppState; |
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.light.AmbientLight; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.util.TangentBinormalGenerator; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class TestTangentCube extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String... args) { |
||||||
|
TestTangentCube app = new TestTangentCube(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
Box aBox = new Box(1, 1, 1); |
||||||
|
Geometry aGeometry = new Geometry("Box", aBox); |
||||||
|
TangentBinormalGenerator.generate(aBox); |
||||||
|
|
||||||
|
Material aMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
aMaterial.setTexture("DiffuseMap", |
||||||
|
assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg")); |
||||||
|
aMaterial.setTexture("NormalMap", |
||||||
|
assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_normal.jpg")); |
||||||
|
aMaterial.setBoolean("UseMaterialColors", false); |
||||||
|
aMaterial.setColor("Diffuse", ColorRGBA.White); |
||||||
|
aMaterial.setColor("Specular", ColorRGBA.White); |
||||||
|
aMaterial.setFloat("Shininess", 64f); |
||||||
|
aGeometry.setMaterial(aMaterial); |
||||||
|
|
||||||
|
// Rotate 45 degrees to see multiple faces
|
||||||
|
aGeometry.rotate(FastMath.QUARTER_PI, FastMath.QUARTER_PI, 0.0f); |
||||||
|
rootNode.attachChild(aGeometry); |
||||||
|
|
||||||
|
/** |
||||||
|
* Must add a light to make the lit object visible! |
||||||
|
*/ |
||||||
|
PointLight aLight = new PointLight(); |
||||||
|
aLight.setPosition(new Vector3f(0, 3, 3)); |
||||||
|
aLight.setColor(ColorRGBA.Red); |
||||||
|
rootNode.addLight(aLight); |
||||||
|
//
|
||||||
|
// AmbientLight bLight = new AmbientLight();
|
||||||
|
// bLight.setColor(ColorRGBA.Gray);
|
||||||
|
// rootNode.addLight(bLight);
|
||||||
|
|
||||||
|
|
||||||
|
ChaseCameraAppState chaser = new ChaseCameraAppState(); |
||||||
|
chaser.setTarget(aGeometry); |
||||||
|
getStateManager().attach(chaser); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.light.pbr; |
||||||
|
|
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* A basic logger for environment map rendering progress. |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class ConsoleProgressReporter extends JobProgressAdapter<LightProbe>{ |
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ConsoleProgressReporter.class.getName()); |
||||||
|
|
||||||
|
long time; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
time = System.currentTimeMillis(); |
||||||
|
logger.log(Level.INFO,"Starting generation"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void progress(double value) { |
||||||
|
logger.log(Level.INFO, "Progress : {0}%", (value * 100)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void step(String message) { |
||||||
|
logger.info(message); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(LightProbe result) { |
||||||
|
long end = System.currentTimeMillis(); |
||||||
|
logger.log(Level.INFO, "Generation done in {0}", ((float)(end - time) / 1000f)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
package jme3test.light.pbr; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.environment.EnvironmentCamera; |
||||||
|
import com.jme3.environment.LightProbeFactory; |
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.environment.util.EnvMapUtils; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.ui.Picture; |
||||||
|
import com.jme3.util.MaterialDebugAppState; |
||||||
|
|
||||||
|
/** |
||||||
|
* test |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class RefEnv extends SimpleApplication { |
||||||
|
|
||||||
|
private Node tex; |
||||||
|
private Node ref; |
||||||
|
private Picture refDE; |
||||||
|
private Picture refM; |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
RefEnv app = new RefEnv(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
|
||||||
|
cam.setLocation(new Vector3f(-2.3324413f, 2.9567573f, 4.6054406f)); |
||||||
|
cam.setRotation(new Quaternion(0.06310794f, 0.9321281f, -0.29613864f, 0.1986369f)); |
||||||
|
Spatial sc = assetManager.loadModel("Scenes/PBR/spheres.j3o"); |
||||||
|
rootNode.attachChild(sc); |
||||||
|
rootNode.getChild("Scene").setCullHint(Spatial.CullHint.Always); |
||||||
|
|
||||||
|
ref = new Node("reference pictures"); |
||||||
|
refDE = new Picture("refDE"); |
||||||
|
refDE.setHeight(cam.getHeight()); |
||||||
|
refDE.setWidth(cam.getWidth()); |
||||||
|
refDE.setImage(assetManager,"jme3test/light/pbr/spheresRefDE.png", false); |
||||||
|
refM = new Picture("refM"); |
||||||
|
refM.setImage(assetManager,"jme3test/light/pbr/spheresRefM.png", false); |
||||||
|
refM.setHeight(cam.getHeight()); |
||||||
|
refM.setWidth(cam.getWidth()); |
||||||
|
|
||||||
|
ref.attachChild(refDE); |
||||||
|
|
||||||
|
stateManager.attach(new EnvironmentCamera()); |
||||||
|
|
||||||
|
inputManager.addMapping("tex", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
inputManager.addMapping("switch", new KeyTrigger(KeyInput.KEY_RETURN)); |
||||||
|
inputManager.addMapping("ref", new KeyTrigger(KeyInput.KEY_R)); |
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (name.equals("tex") && isPressed) { |
||||||
|
if (tex == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (tex.getParent() == null) { |
||||||
|
guiNode.attachChild(tex); |
||||||
|
} else { |
||||||
|
tex.removeFromParent(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("switch") && isPressed) { |
||||||
|
switchMat(rootNode.getChild("Scene")); |
||||||
|
} |
||||||
|
if (name.equals("ref") && isPressed) { |
||||||
|
if (ref.getParent() == null) { |
||||||
|
guiNode.attachChild(ref); |
||||||
|
} else { |
||||||
|
ref.removeFromParent(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, "tex", "switch", "ref"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void switchMat(Spatial s) { |
||||||
|
if (s instanceof Node) { |
||||||
|
Node n = (Node) s; |
||||||
|
for (Spatial children : n.getChildren()) { |
||||||
|
switchMat(children); |
||||||
|
} |
||||||
|
} else if (s instanceof Geometry) { |
||||||
|
Geometry g = (Geometry) s; |
||||||
|
Material mat = g.getMaterial(); |
||||||
|
if (((Float) mat.getParam("Metallic").getValue()) == 1f) { |
||||||
|
mat.setFloat("Metallic", 0); |
||||||
|
mat.setColor("BaseColor", ColorRGBA.Black); |
||||||
|
ref.attachChild(refDE); |
||||||
|
refM.removeFromParent(); |
||||||
|
} else { |
||||||
|
mat.setFloat("Metallic", 1); |
||||||
|
mat.setColor("BaseColor", ColorRGBA.White); |
||||||
|
ref.attachChild(refM); |
||||||
|
refDE.removeFromParent(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int frame = 0; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
frame++; |
||||||
|
|
||||||
|
if (frame == 2) { |
||||||
|
final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter<LightProbe>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(LightProbe result) { |
||||||
|
System.err.println("Done rendering env maps"); |
||||||
|
tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager); |
||||||
|
// guiNode.attachChild(tex);
|
||||||
|
rootNode.getChild("Scene").setCullHint(Spatial.CullHint.Dynamic); |
||||||
|
} |
||||||
|
}); |
||||||
|
((BoundingSphere) probe.getBounds()).setRadius(100); |
||||||
|
rootNode.addLight(probe); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.light.pbr; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.environment.LightProbeFactory; |
||||||
|
import com.jme3.environment.EnvironmentCamera; |
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.environment.util.LightsDebugState; |
||||||
|
import com.jme3.input.ChaseCamera; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.post.FilterPostProcessor; |
||||||
|
import com.jme3.post.filters.FXAAFilter; |
||||||
|
import com.jme3.post.filters.ToneMapFilter; |
||||||
|
import com.jme3.post.ssao.SSAOFilter; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.texture.plugins.ktx.KTXLoader; |
||||||
|
import com.jme3.util.MaterialDebugAppState; |
||||||
|
import com.jme3.util.SkyFactory; |
||||||
|
import com.jme3.util.mikktspace.MikktspaceTangentGenerator; |
||||||
|
|
||||||
|
/** |
||||||
|
* A test case for PBR lighting. |
||||||
|
* Still experimental. |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class TestPBRLighting extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestPBRLighting app = new TestPBRLighting(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
private Geometry model; |
||||||
|
private DirectionalLight dl; |
||||||
|
private Node modelNode; |
||||||
|
private int frame = 0; |
||||||
|
private Material pbrMat; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
assetManager.registerLoader(KTXLoader.class, "ktx"); |
||||||
|
|
||||||
|
viewPort.setBackgroundColor(ColorRGBA.White); |
||||||
|
modelNode = (Node) new Node("modelNode"); |
||||||
|
model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o"); |
||||||
|
MikktspaceTangentGenerator.generate(model); |
||||||
|
modelNode.attachChild(model); |
||||||
|
|
||||||
|
dl = new DirectionalLight(); |
||||||
|
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); |
||||||
|
rootNode.addLight(dl); |
||||||
|
dl.setColor(ColorRGBA.White); |
||||||
|
rootNode.attachChild(modelNode); |
||||||
|
|
||||||
|
|
||||||
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||||
|
// fpp.addFilter(new FXAAFilter());
|
||||||
|
fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f))); |
||||||
|
// fpp.addFilter(new SSAOFilter(0.5f, 3, 0.2f, 0.2f));
|
||||||
|
viewPort.addProcessor(fpp); |
||||||
|
|
||||||
|
//Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap);
|
||||||
|
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); |
||||||
|
//Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", SkyFactory.EnvMapType.CubeMap);
|
||||||
|
//Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/road.hdr", SkyFactory.EnvMapType.EquirectMap);
|
||||||
|
rootNode.attachChild(sky); |
||||||
|
|
||||||
|
pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m"); |
||||||
|
model.setMaterial(pbrMat); |
||||||
|
|
||||||
|
|
||||||
|
final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0)); |
||||||
|
stateManager.attach(envCam); |
||||||
|
|
||||||
|
// EnvironmentManager envManager = new EnvironmentManager();
|
||||||
|
// stateManager.attach(envManager);
|
||||||
|
|
||||||
|
// envManager.setScene(rootNode);
|
||||||
|
|
||||||
|
LightsDebugState debugState = new LightsDebugState(); |
||||||
|
stateManager.attach(debugState); |
||||||
|
|
||||||
|
ChaseCamera chaser = new ChaseCamera(cam, modelNode, inputManager); |
||||||
|
chaser.setDragToRotate(true); |
||||||
|
chaser.setMinVerticalRotation(-FastMath.HALF_PI); |
||||||
|
chaser.setMaxDistance(1000); |
||||||
|
chaser.setSmoothMotion(true); |
||||||
|
chaser.setRotationSensitivity(10); |
||||||
|
chaser.setZoomSensitivity(5); |
||||||
|
flyCam.setEnabled(false); |
||||||
|
//flyCam.setMoveSpeed(100);
|
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (name.equals("debug") && isPressed) { |
||||||
|
//envCam.toggleDebug();
|
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("up") && isPressed) { |
||||||
|
model.move(0, tpf * 100f, 0); |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("down") && isPressed) { |
||||||
|
model.move(0, -tpf * 100f, 0); |
||||||
|
} |
||||||
|
if (name.equals("left") && isPressed) { |
||||||
|
model.move(0, 0, tpf * 100f); |
||||||
|
} |
||||||
|
if (name.equals("right") && isPressed) { |
||||||
|
model.move(0, 0, -tpf * 100f); |
||||||
|
} |
||||||
|
if (name.equals("light") && isPressed) { |
||||||
|
dl.setDirection(cam.getDirection().normalize()); |
||||||
|
} |
||||||
|
} |
||||||
|
}, "toggle", "light", "up", "down", "left", "right", "debug"); |
||||||
|
|
||||||
|
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_RETURN)); |
||||||
|
inputManager.addMapping("light", new KeyTrigger(KeyInput.KEY_F)); |
||||||
|
inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_UP)); |
||||||
|
inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN)); |
||||||
|
inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT)); |
||||||
|
inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT)); |
||||||
|
inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_D)); |
||||||
|
|
||||||
|
|
||||||
|
MaterialDebugAppState debug = new MaterialDebugAppState(); |
||||||
|
debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", rootNode); |
||||||
|
getStateManager().attach(debug); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
frame++; |
||||||
|
|
||||||
|
if (frame == 2) { |
||||||
|
modelNode.removeFromParent(); |
||||||
|
final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode, new JobProgressAdapter<LightProbe>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(LightProbe result) { |
||||||
|
System.err.println("Done rendering env maps"); |
||||||
|
} |
||||||
|
}); |
||||||
|
((BoundingSphere)probe.getBounds()).setRadius(100); |
||||||
|
rootNode.addLight(probe); |
||||||
|
//getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe);
|
||||||
|
|
||||||
|
} |
||||||
|
if (frame > 10 && modelNode.getParent() == null) { |
||||||
|
rootNode.attachChild(modelNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,387 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.light.pbr; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.input.CameraInput; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.MouseInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.input.controls.MouseAxisTrigger; |
||||||
|
import com.jme3.light.AmbientLight; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
import com.jme3.shadow.DirectionalLightShadowRenderer; |
||||||
|
import com.jme3.shadow.EdgeFilteringMode; |
||||||
|
|
||||||
|
import com.jme3.environment.LightProbeFactory; |
||||||
|
import com.jme3.environment.EnvironmentCamera; |
||||||
|
import com.jme3.environment.util.LightsDebugState; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.material.TechniqueDef; |
||||||
|
import com.jme3.post.FilterPostProcessor; |
||||||
|
import com.jme3.post.filters.BloomFilter; |
||||||
|
import com.jme3.post.filters.FXAAFilter; |
||||||
|
import com.jme3.post.filters.ToneMapFilter; |
||||||
|
import com.jme3.post.ssao.SSAOFilter; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.texture.plugins.ktx.KTXLoader; |
||||||
|
import com.jme3.util.SkyFactory; |
||||||
|
import com.jme3.util.TangentBinormalGenerator; |
||||||
|
|
||||||
|
public class TestPbrEnv extends SimpleApplication implements ActionListener { |
||||||
|
|
||||||
|
public static final int SHADOWMAP_SIZE = 1024; |
||||||
|
private Spatial[] obj; |
||||||
|
private Material[] mat; |
||||||
|
private DirectionalLightShadowRenderer dlsr; |
||||||
|
private LightsDebugState debugState; |
||||||
|
|
||||||
|
private EnvironmentCamera envCam; |
||||||
|
|
||||||
|
private Geometry ground; |
||||||
|
private Material matGroundU; |
||||||
|
private Material matGroundL; |
||||||
|
|
||||||
|
private Geometry camGeom; |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestPbrEnv app = new TestPbrEnv(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void loadScene() { |
||||||
|
|
||||||
|
renderManager.setPreferredLightMode(TechniqueDef.LightMode.SinglePass); |
||||||
|
renderManager.setSinglePassLightBatchSize(3); |
||||||
|
obj = new Spatial[2]; |
||||||
|
// Setup first view
|
||||||
|
|
||||||
|
mat = new Material[2]; |
||||||
|
mat[0] = assetManager.loadMaterial("jme3test/light/pbr/pbrMat.j3m"); |
||||||
|
//mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
|
||||||
|
mat[1] = assetManager.loadMaterial("jme3test/light/pbr/pbrMat2.j3m"); |
||||||
|
// mat[1].setBoolean("UseMaterialColors", true);
|
||||||
|
// mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f));
|
||||||
|
// mat[1].setColor("Diffuse", ColorRGBA.White.clone());
|
||||||
|
|
||||||
|
obj[0] = new Geometry("sphere", new Sphere(30, 30, 2)); |
||||||
|
obj[0].setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f)); |
||||||
|
obj[1].setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
TangentBinormalGenerator.generate(obj[1]); |
||||||
|
TangentBinormalGenerator.generate(obj[0]); |
||||||
|
|
||||||
|
// for (int i = 0; i < 60; i++) {
|
||||||
|
// Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false);
|
||||||
|
// t.setName("Cube" + i);
|
||||||
|
// t.setLocalScale(FastMath.nextRandomFloat() * 10f);
|
||||||
|
// t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]);
|
||||||
|
// rootNode.attachChild(t);
|
||||||
|
// t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f));
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) { |
||||||
|
Spatial t = obj[0].clone(false); |
||||||
|
t.setName("Cube" + i); |
||||||
|
t.setLocalScale( 10f); |
||||||
|
t.setMaterial(mat[1].clone()); |
||||||
|
rootNode.attachChild(t); |
||||||
|
t.setLocalTranslation(i * 200f+ 100f, 50, 800f * (i)); |
||||||
|
} |
||||||
|
|
||||||
|
Box b = new Box(1000, 2, 1000); |
||||||
|
b.scaleTextureCoordinates(new Vector2f(20, 20)); |
||||||
|
ground = new Geometry("soil", b); |
||||||
|
TangentBinormalGenerator.generate(ground); |
||||||
|
ground.setLocalTranslation(0, 10, 550); |
||||||
|
matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
matGroundU.setColor("Color", ColorRGBA.Green); |
||||||
|
|
||||||
|
// matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
// Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
|
||||||
|
// grass.setWrap(WrapMode.Repeat);
|
||||||
|
// matGroundL.setTexture("DiffuseMap", grass);
|
||||||
|
|
||||||
|
matGroundL = assetManager.loadMaterial("jme3test/light/pbr/pbrMat4.j3m"); |
||||||
|
|
||||||
|
ground.setMaterial(matGroundL); |
||||||
|
|
||||||
|
//ground.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
rootNode.attachChild(ground); |
||||||
|
|
||||||
|
l = new DirectionalLight(); |
||||||
|
l.setColor(ColorRGBA.White); |
||||||
|
//l.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
|
||||||
|
l.setDirection(new Vector3f(-0.2823181f, -0.41889593f, 0.863031f).normalizeLocal()); |
||||||
|
|
||||||
|
rootNode.addLight(l); |
||||||
|
|
||||||
|
AmbientLight al = new AmbientLight(); |
||||||
|
al.setColor(ColorRGBA.White.mult(0.5f)); |
||||||
|
// rootNode.addLight(al);
|
||||||
|
|
||||||
|
//Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", SkyFactory.EnvMapType.CubeMap);
|
||||||
|
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap); |
||||||
|
sky.setLocalScale(350); |
||||||
|
|
||||||
|
rootNode.attachChild(sky); |
||||||
|
} |
||||||
|
DirectionalLight l; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
assetManager.registerLoader(KTXLoader.class, "ktx"); |
||||||
|
|
||||||
|
|
||||||
|
// put the camera in a bad position
|
||||||
|
cam.setLocation(new Vector3f(-52.433647f, 68.69636f, -118.60924f)); |
||||||
|
cam.setRotation(new Quaternion(0.10294232f, 0.25269797f, -0.027049713f, 0.96167296f)); |
||||||
|
|
||||||
|
flyCam.setMoveSpeed(100); |
||||||
|
|
||||||
|
loadScene(); |
||||||
|
|
||||||
|
dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 4); |
||||||
|
dlsr.setLight(l); |
||||||
|
//dlsr.setLambda(0.55f);
|
||||||
|
dlsr.setShadowIntensity(0.5f); |
||||||
|
dlsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); |
||||||
|
//dlsr.displayDebug();
|
||||||
|
// viewPort.addProcessor(dlsr);
|
||||||
|
|
||||||
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||||
|
|
||||||
|
fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(6.0f))); |
||||||
|
SSAOFilter ssao = new SSAOFilter(); |
||||||
|
ssao.setIntensity(5); |
||||||
|
|
||||||
|
fpp.addFilter(ssao); |
||||||
|
|
||||||
|
BloomFilter bloomFilter = new BloomFilter(); |
||||||
|
fpp.addFilter(bloomFilter); |
||||||
|
fpp.addFilter(new FXAAFilter()); |
||||||
|
//viewPort.addProcessor(fpp);
|
||||||
|
|
||||||
|
initInputs(); |
||||||
|
|
||||||
|
// envManager = new EnvironmentManager();
|
||||||
|
// getStateManager().attach(envManager);
|
||||||
|
//
|
||||||
|
envCam = new EnvironmentCamera(); |
||||||
|
getStateManager().attach(envCam); |
||||||
|
|
||||||
|
debugState = new LightsDebugState(); |
||||||
|
debugState.setProbeScale(5); |
||||||
|
getStateManager().attach(debugState); |
||||||
|
|
||||||
|
camGeom = new Geometry("camGeom", new Sphere(16, 16, 2)); |
||||||
|
// Material m = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md");
|
||||||
|
// m.setColor("Color", ColorRGBA.Green);
|
||||||
|
Material m = assetManager.loadMaterial("jme3test/light/pbr/pbrMat3.j3m"); |
||||||
|
camGeom.setMaterial(m); |
||||||
|
camGeom.setLocalTranslation(0, 20, 0); |
||||||
|
camGeom.setLocalScale(5); |
||||||
|
rootNode.attachChild(camGeom); |
||||||
|
|
||||||
|
// envManager.setScene(rootNode);
|
||||||
|
|
||||||
|
// MaterialDebugAppState debug = new MaterialDebugAppState();
|
||||||
|
// debug.registerBinding("MatDefs/PBRLighting.frag", rootNode);
|
||||||
|
// getStateManager().attach(debug);
|
||||||
|
|
||||||
|
flyCam.setDragToRotate(true); |
||||||
|
setPauseOnLostFocus(false); |
||||||
|
|
||||||
|
// cam.lookAt(camGeom.getWorldTranslation(), Vector3f.UNIT_Y);
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void fixFLyCamInputs() { |
||||||
|
inputManager.deleteMapping(CameraInput.FLYCAM_LEFT); |
||||||
|
inputManager.deleteMapping(CameraInput.FLYCAM_RIGHT); |
||||||
|
inputManager.deleteMapping(CameraInput.FLYCAM_UP); |
||||||
|
inputManager.deleteMapping(CameraInput.FLYCAM_DOWN); |
||||||
|
|
||||||
|
inputManager.addMapping(CameraInput.FLYCAM_LEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true)); |
||||||
|
|
||||||
|
inputManager.addMapping(CameraInput.FLYCAM_RIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, false)); |
||||||
|
|
||||||
|
inputManager.addMapping(CameraInput.FLYCAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, false)); |
||||||
|
|
||||||
|
inputManager.addMapping(CameraInput.FLYCAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true)); |
||||||
|
|
||||||
|
inputManager.addListener(flyCam, CameraInput.FLYCAM_LEFT, CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_UP, CameraInput.FLYCAM_DOWN); |
||||||
|
} |
||||||
|
|
||||||
|
private void initInputs() { |
||||||
|
inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M)); |
||||||
|
inputManager.addMapping("snapshot", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
inputManager.addMapping("fc", new KeyTrigger(KeyInput.KEY_F)); |
||||||
|
inputManager.addMapping("debugProbe", new KeyTrigger(KeyInput.KEY_RETURN)); |
||||||
|
inputManager.addMapping("debugTex", new KeyTrigger(KeyInput.KEY_T)); |
||||||
|
inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_UP)); |
||||||
|
inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN)); |
||||||
|
inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT)); |
||||||
|
inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT)); |
||||||
|
inputManager.addMapping("delete", new KeyTrigger(KeyInput.KEY_DELETE)); |
||||||
|
|
||||||
|
inputManager.addListener(this, "delete","switchGroundMat", "snapshot", "debugTex", "debugProbe", "fc", "up", "down", "left", "right"); |
||||||
|
} |
||||||
|
|
||||||
|
private LightProbe lastProbe; |
||||||
|
private Node debugGui ; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean keyPressed, float tpf) { |
||||||
|
|
||||||
|
if (name.equals("switchGroundMat") && keyPressed) { |
||||||
|
if (ground.getMaterial() == matGroundL) { |
||||||
|
ground.setMaterial(matGroundU); |
||||||
|
} else { |
||||||
|
|
||||||
|
ground.setMaterial(matGroundL); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("snapshot") && keyPressed) { |
||||||
|
envCam.setPosition(camGeom.getWorldTranslation()); |
||||||
|
lastProbe = LightProbeFactory.makeProbe(envCam, rootNode, new ConsoleProgressReporter()); |
||||||
|
((BoundingSphere)lastProbe.getBounds()).setRadius(200); |
||||||
|
rootNode.addLight(lastProbe); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("delete") && keyPressed) { |
||||||
|
System.err.println(rootNode.getWorldLightList().size()); |
||||||
|
rootNode.removeLight(lastProbe); |
||||||
|
System.err.println("deleted"); |
||||||
|
System.err.println(rootNode.getWorldLightList().size()); |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("fc") && keyPressed) { |
||||||
|
|
||||||
|
flyCam.setEnabled(true); |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("debugProbe") && keyPressed) { |
||||||
|
//getStateManager().getState(EnvironmentCamera.class).toggleDebug();
|
||||||
|
if (!debugState.isEnabled()) { |
||||||
|
debugState.setEnabled(true); |
||||||
|
debugState.setDebugMode(LightsDebugState.DebugMode.IrradianceMap); |
||||||
|
} else if (debugState.getDebugMode() == LightsDebugState.DebugMode.IrradianceMap) { |
||||||
|
debugState.setDebugMode(LightsDebugState.DebugMode.PrefilteredEnvMap); |
||||||
|
} else if (debugState.getDebugMode() == LightsDebugState.DebugMode.PrefilteredEnvMap) { |
||||||
|
debugState.setEnabled(false); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("debugTex") && keyPressed) { |
||||||
|
if(debugGui == null || debugGui.getParent() == null){ |
||||||
|
debugGui = lastProbe.getDebugGui(assetManager); |
||||||
|
debugGui.setLocalTranslation(10, 200, 0); |
||||||
|
guiNode.attachChild(debugGui); |
||||||
|
} else if(debugGui != null){ |
||||||
|
debugGui.removeFromParent(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (name.equals("up")) { |
||||||
|
up = keyPressed; |
||||||
|
} |
||||||
|
if (name.equals("down")) { |
||||||
|
down = keyPressed; |
||||||
|
} |
||||||
|
if (name.equals("right")) { |
||||||
|
right = keyPressed; |
||||||
|
} |
||||||
|
if (name.equals("left")) { |
||||||
|
left = keyPressed; |
||||||
|
} |
||||||
|
if (name.equals("fwd")) { |
||||||
|
fwd = keyPressed; |
||||||
|
} |
||||||
|
if (name.equals("back")) { |
||||||
|
back = keyPressed; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
boolean up = false; |
||||||
|
boolean down = false; |
||||||
|
boolean left = false; |
||||||
|
boolean right = false; |
||||||
|
boolean fwd = false; |
||||||
|
boolean back = false; |
||||||
|
float time = 0; |
||||||
|
float s = 50f; |
||||||
|
boolean initialized = false; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
|
||||||
|
if (!initialized) { |
||||||
|
fixFLyCamInputs(); |
||||||
|
initialized = true; |
||||||
|
} |
||||||
|
float val = tpf * s; |
||||||
|
if (up) { |
||||||
|
camGeom.move(0, 0, val); |
||||||
|
} |
||||||
|
if (down) { |
||||||
|
camGeom.move(0, 0, -val); |
||||||
|
|
||||||
|
} |
||||||
|
if (right) { |
||||||
|
camGeom.move(-val, 0, 0); |
||||||
|
|
||||||
|
} |
||||||
|
if (left) { |
||||||
|
camGeom.move(val, 0, 0); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 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 jme3test.material; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.AnalogListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.shape.Quad; |
||||||
|
import com.jme3.util.SkyFactory; |
||||||
|
import com.jme3.util.TangentBinormalGenerator; |
||||||
|
|
||||||
|
public class TestParallaxPBR extends SimpleApplication { |
||||||
|
|
||||||
|
private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal(); |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestParallaxPBR app = new TestParallaxPBR(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setupSkyBox() { |
||||||
|
rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false)); |
||||||
|
} |
||||||
|
DirectionalLight dl; |
||||||
|
|
||||||
|
public void setupLighting() { |
||||||
|
|
||||||
|
dl = new DirectionalLight(); |
||||||
|
dl.setDirection(lightDir); |
||||||
|
dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1)); |
||||||
|
rootNode.addLight(dl); |
||||||
|
} |
||||||
|
Material mat; |
||||||
|
|
||||||
|
public void setupFloor() { |
||||||
|
mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWallPBR.j3m"); |
||||||
|
//mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWallPBR2.j3m");
|
||||||
|
|
||||||
|
Node floorGeom = new Node("floorGeom"); |
||||||
|
Quad q = new Quad(100, 100); |
||||||
|
q.scaleTextureCoordinates(new Vector2f(10, 10)); |
||||||
|
Geometry g = new Geometry("geom", q); |
||||||
|
g.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X)); |
||||||
|
floorGeom.attachChild(g); |
||||||
|
|
||||||
|
|
||||||
|
TangentBinormalGenerator.generate(floorGeom); |
||||||
|
floorGeom.setLocalTranslation(-50, 22, 60); |
||||||
|
//floorGeom.setLocalScale(100);
|
||||||
|
|
||||||
|
floorGeom.setMaterial(mat); |
||||||
|
rootNode.attachChild(floorGeom); |
||||||
|
} |
||||||
|
|
||||||
|
public void setupSignpost() { |
||||||
|
Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml"); |
||||||
|
Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); |
||||||
|
TangentBinormalGenerator.generate(signpost); |
||||||
|
signpost.setMaterial(mat); |
||||||
|
signpost.rotate(0, FastMath.HALF_PI, 0); |
||||||
|
signpost.setLocalTranslation(12, 23.5f, 30); |
||||||
|
signpost.setLocalScale(4); |
||||||
|
signpost.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
rootNode.attachChild(signpost); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
cam.setLocation(new Vector3f(-15.445636f, 30.162927f, 60.252777f)); |
||||||
|
cam.setRotation(new Quaternion(0.05173137f, 0.92363626f, -0.13454558f, 0.35513034f)); |
||||||
|
flyCam.setMoveSpeed(30); |
||||||
|
|
||||||
|
|
||||||
|
setupLighting(); |
||||||
|
setupSkyBox(); |
||||||
|
setupFloor(); |
||||||
|
setupSignpost(); |
||||||
|
|
||||||
|
inputManager.addListener(new AnalogListener() { |
||||||
|
|
||||||
|
public void onAnalog(String name, float value, float tpf) { |
||||||
|
if ("heightUP".equals(name)) { |
||||||
|
parallaxHeigh += 0.01; |
||||||
|
mat.setFloat("ParallaxHeight", parallaxHeigh); |
||||||
|
} |
||||||
|
if ("heightDown".equals(name)) { |
||||||
|
parallaxHeigh -= 0.01; |
||||||
|
parallaxHeigh = Math.max(parallaxHeigh, 0); |
||||||
|
mat.setFloat("ParallaxHeight", parallaxHeigh); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}, "heightUP", "heightDown"); |
||||||
|
inputManager.addMapping("heightUP", new KeyTrigger(KeyInput.KEY_I)); |
||||||
|
inputManager.addMapping("heightDown", new KeyTrigger(KeyInput.KEY_K)); |
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
|
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (isPressed && "toggleSteep".equals(name)) { |
||||||
|
steep = !steep; |
||||||
|
mat.setBoolean("SteepParallax", steep); |
||||||
|
} |
||||||
|
} |
||||||
|
}, "toggleSteep"); |
||||||
|
inputManager.addMapping("toggleSteep", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
} |
||||||
|
float parallaxHeigh = 0.05f; |
||||||
|
float time = 0; |
||||||
|
boolean steep = false; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
// time+=tpf;
|
||||||
|
// lightDir.set(FastMath.sin(time), -1, FastMath.cos(time));
|
||||||
|
// bsr.setDirection(lightDir);
|
||||||
|
// dl.setDirection(lightDir);
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,180 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.post; |
||||||
|
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.post.FilterPostProcessor; |
||||||
|
import com.jme3.post.filters.BloomFilter; |
||||||
|
import com.jme3.post.filters.BloomFilter.GlowMode; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.debug.WireFrustum; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.util.SkyFactory; |
||||||
|
|
||||||
|
public class TestBloomAlphaThreshold extends SimpleApplication |
||||||
|
{ |
||||||
|
|
||||||
|
float angle; |
||||||
|
Spatial lightMdl; |
||||||
|
Spatial teapot; |
||||||
|
Geometry frustumMdl; |
||||||
|
WireFrustum frustum; |
||||||
|
boolean active = true; |
||||||
|
FilterPostProcessor fpp; |
||||||
|
|
||||||
|
public static void main(String[] args) |
||||||
|
{ |
||||||
|
TestBloomAlphaThreshold app = new TestBloomAlphaThreshold(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() |
||||||
|
{ |
||||||
|
// put the camera in a bad position
|
||||||
|
cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -10)); |
||||||
|
cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f)); |
||||||
|
// cam.setFrustumFar(1000);
|
||||||
|
|
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
mat.setFloat("Shininess", 15f); |
||||||
|
mat.setBoolean("UseMaterialColors", true); |
||||||
|
mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f)); |
||||||
|
mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f)); |
||||||
|
mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f)); |
||||||
|
mat.setColor("GlowColor", ColorRGBA.Green); |
||||||
|
|
||||||
|
Material matSoil = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
matSoil.setFloat("Shininess", 15f); |
||||||
|
matSoil.setBoolean("UseMaterialColors", true); |
||||||
|
matSoil.setColor("Ambient", ColorRGBA.Gray); |
||||||
|
matSoil.setColor("Diffuse", ColorRGBA.Black); |
||||||
|
matSoil.setColor("Specular", ColorRGBA.Gray); |
||||||
|
|
||||||
|
teapot = assetManager.loadModel("Models/Teapot/Teapot.obj"); |
||||||
|
teapot.setLocalTranslation(0, 0, 10); |
||||||
|
|
||||||
|
teapot.setMaterial(mat); |
||||||
|
teapot.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
teapot.setLocalScale(10.0f); |
||||||
|
rootNode.attachChild(teapot); |
||||||
|
|
||||||
|
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700)); |
||||||
|
soil.setMaterial(matSoil); |
||||||
|
soil.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
rootNode.attachChild(soil); |
||||||
|
|
||||||
|
Material matBox = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
matBox.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png")); |
||||||
|
matBox.setFloat("AlphaDiscardThreshold", 0.5f); |
||||||
|
|
||||||
|
Geometry box = new Geometry("box", new Box(new Vector3f(-3.5f, 10, -2), 2, 2, 2)); |
||||||
|
box.setMaterial(matBox); |
||||||
|
box.setQueueBucket(RenderQueue.Bucket.Translucent); |
||||||
|
// box.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
rootNode.attachChild(box); |
||||||
|
|
||||||
|
DirectionalLight light = new DirectionalLight(); |
||||||
|
light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); |
||||||
|
light.setColor(ColorRGBA.White.mult(1.5f)); |
||||||
|
rootNode.addLight(light); |
||||||
|
|
||||||
|
// load sky
|
||||||
|
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false); |
||||||
|
sky.setCullHint(Spatial.CullHint.Never); |
||||||
|
rootNode.attachChild(sky); |
||||||
|
|
||||||
|
fpp = new FilterPostProcessor(assetManager); |
||||||
|
int numSamples = getContext().getSettings().getSamples(); |
||||||
|
if (numSamples > 0) |
||||||
|
{ |
||||||
|
fpp.setNumSamples(numSamples); |
||||||
|
} |
||||||
|
|
||||||
|
BloomFilter bloom = new BloomFilter(GlowMode.Objects); |
||||||
|
bloom.setDownSamplingFactor(2); |
||||||
|
bloom.setBlurScale(1.37f); |
||||||
|
bloom.setExposurePower(3.30f); |
||||||
|
bloom.setExposureCutOff(0.2f); |
||||||
|
bloom.setBloomIntensity(2.45f); |
||||||
|
BloomUI ui = new BloomUI(inputManager, bloom); |
||||||
|
|
||||||
|
viewPort.addProcessor(fpp); |
||||||
|
fpp.addFilter(bloom); |
||||||
|
initInputs(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void initInputs() |
||||||
|
{ |
||||||
|
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||||
|
|
||||||
|
ActionListener acl = new ActionListener() |
||||||
|
{ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean keyPressed, float tpf) |
||||||
|
{ |
||||||
|
if (name.equals("toggle") && keyPressed) |
||||||
|
{ |
||||||
|
if (active) |
||||||
|
{ |
||||||
|
active = false; |
||||||
|
viewPort.removeProcessor(fpp); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
active = true; |
||||||
|
viewPort.addProcessor(fpp); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
inputManager.addListener(acl, "toggle"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/* |
||||||
|
* 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 jme3test.texture.ktx; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.asset.TextureKey; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.texture.plugins.ktx.KTXLoader; |
||||||
|
import com.jme3.ui.Picture; |
||||||
|
|
||||||
|
/** |
||||||
|
* test |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class TestLoadKtx extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestLoadKtx app = new TestLoadKtx(); |
||||||
|
//app.setShowSettings(false);
|
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
viewPort.setBackgroundColor(ColorRGBA.DarkGray); |
||||||
|
assetManager.registerLoader(KTXLoader.class, "ktx"); |
||||||
|
|
||||||
|
|
||||||
|
Texture2D t = (Texture2D)assetManager.loadTexture("Textures/ktx/down-reference.ktx"); |
||||||
|
Picture p = new Picture("bla", false); |
||||||
|
p.setTexture(assetManager, t, false); |
||||||
|
p.setLocalTranslation(200, 200, 0); |
||||||
|
p.setWidth(t.getImage().getWidth()); |
||||||
|
p.setHeight(t.getImage().getHeight()); |
||||||
|
guiNode.attachChild(p); |
||||||
|
|
||||||
|
|
||||||
|
Texture2D t2 = (Texture2D)assetManager.loadTexture("Textures/ktx/up-reference.ktx"); |
||||||
|
Picture p2 = new Picture("bla", false); |
||||||
|
p2.setTexture(assetManager, t2, false); |
||||||
|
p2.setLocalTranslation(400, 200, 0); |
||||||
|
p2.setWidth(t2.getImage().getWidth()); |
||||||
|
p2.setHeight(t2.getImage().getHeight()); |
||||||
|
guiNode.attachChild(p2); |
||||||
|
|
||||||
|
Texture2D t3 = (Texture2D)assetManager.loadTexture("Textures/ktx/rgba-reference.ktx"); |
||||||
|
Picture p3 = new Picture("bla", false); |
||||||
|
p3.setTexture(assetManager, t3, false); |
||||||
|
p3.setLocalTranslation(200, 400, 0); |
||||||
|
p3.setWidth(t3.getImage().getWidth()); |
||||||
|
p3.setHeight(t3.getImage().getHeight()); |
||||||
|
guiNode.attachChild(p3); |
||||||
|
|
||||||
|
|
||||||
|
Texture2D t4 = (Texture2D)assetManager.loadTexture("Textures/ktx/rgb-amg-reference.ktx"); |
||||||
|
Picture p4 = new Picture("bla", false); |
||||||
|
p4.setTexture(assetManager, t4, false); |
||||||
|
p4.setLocalTranslation(400, 400, 0); |
||||||
|
p4.setWidth(t4.getImage().getWidth()); |
||||||
|
p4.setHeight(t4.getImage().getHeight()); |
||||||
|
guiNode.attachChild(p4); |
||||||
|
|
||||||
|
|
||||||
|
flyCam.setDragToRotate(true); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
//TODO: add update code
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleRender(RenderManager rm) { |
||||||
|
//TODO: add render code
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
Material My Material : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
Roughness : 0.1 |
||||||
|
BaseColor : 1.0 0.2 0.2 1.0 |
||||||
|
Metallic : 0.0 |
||||||
|
EmissivePower : 0.0 |
||||||
|
EmissiveIntensity : 0.0 |
||||||
|
Emissive : 1.0 0.8 0.8 1.0 |
||||||
|
ParallaxHeight : 0.0 |
||||||
|
} |
||||||
|
AdditionalRenderState { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
Material My Material : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
Metallic : 0.0 |
||||||
|
Roughness : 0.0 |
||||||
|
BaseColor : 0.8 0.81960785 0.39607844 1.0 |
||||||
|
EmissiveIntensity : 5.0 |
||||||
|
EmissivePower : 2.0 |
||||||
|
Emissive : 1.0 0.0 0.0 1.0 |
||||||
|
} |
||||||
|
AdditionalRenderState { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
Material My Material : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
BaseColor : 0.6 0.6 0.6 1.0 |
||||||
|
Metallic : 1.0 |
||||||
|
Roughness : 0.05 |
||||||
|
} |
||||||
|
AdditionalRenderState { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
Material My Material : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
BaseColorMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg |
||||||
|
Roughness : 0.01 |
||||||
|
NormalMap : Repeat Textures/Terrain/BrickWall/BrickWall_normal.jpg |
||||||
|
Metallic : 1.0 |
||||||
|
BaseColor : 1.0 1.0 1.0 1.0 |
||||||
|
} |
||||||
|
AdditionalRenderState { |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 508 KiB |
After Width: | Height: | Size: 555 KiB |
After Width: | Height: | Size: 3.7 MiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 12 MiB |
After Width: | Height: | Size: 1.7 MiB |
@ -0,0 +1,14 @@ |
|||||||
|
Material Tank : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
|
||||||
|
MetallicMap : Flip Models/Tank/Tank_Metallic.png |
||||||
|
RoughnessMap : Flip Models/Tank/Tank_Roughness.png |
||||||
|
NormalMap : Flip Models/Tank/Tank_Normal.png |
||||||
|
BaseColorMap : Flip Models/Tank/Tank_Base_Color.png |
||||||
|
EmissiveMap : Flip Models/Tank/Tank_Emissive.png |
||||||
|
EmissiveIntensity : 2.0 |
||||||
|
|
||||||
|
} |
||||||
|
AdditionalRenderState { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
# |
||||||
|
#Sat Apr 11 15:27:27 CEST 2015 |
||||||
|
ORIGINAL_PATH=Models/Tank/tank.obj |
@ -0,0 +1,9 @@ |
|||||||
|
Material Pong Rock PBR : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
Roughness : 1.0 |
||||||
|
Metallic : 0.0 |
||||||
|
BaseColorMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg |
||||||
|
NormalMap : Repeat Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds |
||||||
|
PackedNormalParallax: true |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
Material Pong Rock PBR : Common/MatDefs/Light/PBRLighting.j3md { |
||||||
|
MaterialParameters { |
||||||
|
Roughness : 1.0 |
||||||
|
Metallic : 0.0 |
||||||
|
BaseColorMap : Repeat Textures/Terrain/BrickWall/BrickWall.jpg |
||||||
|
ParallaxMap : Repeat Textures/Terrain/BrickWall/BrickWall_height.jpg |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2010 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.gde.core.sceneexplorer.nodes; |
||||||
|
|
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import java.beans.PropertyEditor; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import org.openide.nodes.PropertySupport; |
||||||
|
import org.openide.nodes.Sheet; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author normenhansen |
||||||
|
*/ |
||||||
|
@org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class) |
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"}) |
||||||
|
public class JmeLightProbe extends JmeLight{ |
||||||
|
protected LightProbe lightProbe; |
||||||
|
|
||||||
|
public JmeLightProbe() { |
||||||
|
} |
||||||
|
|
||||||
|
public JmeLightProbe(Spatial spatial, LightProbe lightProbe) { |
||||||
|
super(spatial, lightProbe); |
||||||
|
this.lightProbe = lightProbe; |
||||||
|
lookupContents.add(lightProbe); |
||||||
|
setName("LightProbe"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Sheet createSheet() { |
||||||
|
//TODO: multithreading..
|
||||||
|
Sheet sheet = super.createSheet(); |
||||||
|
Sheet.Set set = Sheet.createPropertiesSet(); |
||||||
|
set.setDisplayName("LightProbe"); |
||||||
|
set.setName(LightProbe.class.getName()); |
||||||
|
LightProbe obj = lightProbe; |
||||||
|
if (obj == null) { |
||||||
|
return sheet; |
||||||
|
} |
||||||
|
|
||||||
|
set.put(makeProperty(obj, Vector3f.class, "getPosition", "setPosition", "Position")); |
||||||
|
set.put(makeEmbedProperty(obj.getBounds(), obj.getBounds().getClass(), float.class, "getRadius", "setRadius", "Radius")); |
||||||
|
set.put(createButtonProperty()); |
||||||
|
sheet.put(set); |
||||||
|
return sheet; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public LightProbe getLightProbe() { |
||||||
|
return lightProbe; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class getExplorerObjectClass() { |
||||||
|
return LightProbe.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class getExplorerNodeClass() { |
||||||
|
return JmeLightProbe.class; |
||||||
|
} |
||||||
|
|
||||||
|
protected void setModified(){ |
||||||
|
java.awt.EventQueue.invokeLater(new Runnable() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
fireSave(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private Property createButtonProperty() { |
||||||
|
return new PropertySupport.ReadWrite<Object>("update", Object.class, "Refresh maps", "Click here to refresh environment maps ") { |
||||||
|
JmeLightProbeButtonProperty pe; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getValue() throws IllegalAccessException, InvocationTargetException { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public PropertyEditor getPropertyEditor() { |
||||||
|
if (pe == null) { |
||||||
|
pe = new JmeLightProbeButtonProperty(JmeLightProbe.this, (Node)getSpatial()); |
||||||
|
pe.attachEnv(pe.env); |
||||||
|
} |
||||||
|
return pe; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setValue(Object t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
package com.jme3.gde.core.sceneexplorer.nodes; |
||||||
|
|
||||||
|
import com.jme3.environment.EnvironmentCamera; |
||||||
|
import com.jme3.environment.LightProbeFactory; |
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.gde.core.scene.SceneApplication; |
||||||
|
import com.jme3.gde.core.scene.controller.SceneToolController; |
||||||
|
import com.jme3.gde.core.util.ButtonInplaceEditor; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.beans.PropertyEditorSupport; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import org.netbeans.api.progress.ProgressHandle; |
||||||
|
import org.netbeans.api.progress.ProgressHandleFactory; |
||||||
|
import org.openide.explorer.propertysheet.ExPropertyEditor; |
||||||
|
import org.openide.explorer.propertysheet.InplaceEditor; |
||||||
|
import org.openide.explorer.propertysheet.PropertyEnv; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class JmeLightProbeButtonProperty extends PropertyEditorSupport implements ExPropertyEditor, InplaceEditor.Factory { |
||||||
|
|
||||||
|
JmeLightProbe probe; |
||||||
|
Node node; |
||||||
|
|
||||||
|
public JmeLightProbeButtonProperty(JmeLightProbe pe, Node node) { |
||||||
|
super(); |
||||||
|
this.node = node; |
||||||
|
this.probe = pe; |
||||||
|
} |
||||||
|
PropertyEnv env; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void attachEnv(PropertyEnv env) { |
||||||
|
this.env = env; |
||||||
|
env.registerInplaceEditorFactory(this); |
||||||
|
} |
||||||
|
private ButtonInplaceEditor ed = null; |
||||||
|
|
||||||
|
@Override |
||||||
|
public InplaceEditor getInplaceEditor() { |
||||||
|
if (ed == null) { |
||||||
|
ed = new ButtonInplaceEditor("Refresh"); |
||||||
|
ed.addActionListener(new ActionListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
|
||||||
|
SceneApplication.getApplication().enqueue(new Callable<Object>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object call() throws Exception { |
||||||
|
|
||||||
|
EnvironmentCamera envCam = SceneApplication.getApplication().getStateManager().getState(EnvironmentCamera.class); |
||||||
|
SceneToolController toolController = SceneApplication.getApplication().getStateManager().getState(SceneToolController.class); |
||||||
|
if (toolController != null) { |
||||||
|
envCam.setPosition(toolController.getCursorLocation()); |
||||||
|
} else { |
||||||
|
envCam.setPosition(new Vector3f(0, 0, 0)); |
||||||
|
} |
||||||
|
LightProbeFactory.updateProbe(probe.getLightProbe(), envCam, node, new JmeLightProbeProgressHandler()); |
||||||
|
|
||||||
|
probe.setModified(); |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
return ed; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isPaintable() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paintValue(Graphics gfx, Rectangle box) { |
||||||
|
if (ed == null) { |
||||||
|
getInplaceEditor(); |
||||||
|
} |
||||||
|
ed.setSize(box.width, box.height); |
||||||
|
ed.doLayout(); |
||||||
|
Graphics g = gfx.create(box.x, box.y, box.width, box.height); |
||||||
|
ed.setOpaque(false); |
||||||
|
ed.paint(g); |
||||||
|
g.dispose(); |
||||||
|
probe.refresh(false); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
|
||||||
|
package com.jme3.gde.core.sceneexplorer.nodes; |
||||||
|
|
||||||
|
import com.jme3.environment.generation.JobProgressAdapter; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import org.netbeans.api.progress.ProgressHandle; |
||||||
|
import org.netbeans.api.progress.ProgressHandleFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
public class JmeLightProbeProgressHandler extends JobProgressAdapter<LightProbe> { |
||||||
|
|
||||||
|
int lastProgress; |
||||||
|
|
||||||
|
ProgressHandle handle = ProgressHandleFactory.createHandle("Generating environment maps"); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
handle.start(100); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void progress(double value) { |
||||||
|
lastProgress = (int) (value * 100); |
||||||
|
handle.progress(lastProgress); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void step(String message) { |
||||||
|
handle.progress(message, lastProgress); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(LightProbe t) { |
||||||
|
handle.finish(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.gde.scenecomposer.gizmo.light; |
||||||
|
|
||||||
|
import com.jme3.light.Light; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.control.BillboardControl; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import org.openide.util.Exceptions; |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the marker's position whenever the light has moved. It is also a |
||||||
|
* BillboardControl, so this marker always faces the camera |
||||||
|
*/ |
||||||
|
public class LightGizmoControl extends BillboardControl { |
||||||
|
|
||||||
|
private final Vector3f lastPos = new Vector3f(); |
||||||
|
private Vector3f lightPos; |
||||||
|
|
||||||
|
LightGizmoControl(Light light) { |
||||||
|
super(); |
||||||
|
|
||||||
|
try { |
||||||
|
Method getPosition = light.getClass().getMethod("getPosition"); |
||||||
|
lightPos = (Vector3f) getPosition.invoke(light); |
||||||
|
} catch (NoSuchMethodException ex) { |
||||||
|
//light type doesn't have a get position method, silancing the exception
|
||||||
|
} catch (SecurityException ex) { |
||||||
|
Exceptions.printStackTrace(ex); |
||||||
|
} catch (IllegalAccessException ex) { |
||||||
|
Exceptions.printStackTrace(ex); |
||||||
|
} catch (IllegalArgumentException ex) { |
||||||
|
Exceptions.printStackTrace(ex); |
||||||
|
} catch (InvocationTargetException ex) { |
||||||
|
Exceptions.printStackTrace(ex); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void controlUpdate(float f) { |
||||||
|
super.controlUpdate(f); |
||||||
|
|
||||||
|
if (!lightPos.equals(lastPos)) { |
||||||
|
if (getSpatial() != null) { |
||||||
|
lastPos.set(lightPos); |
||||||
|
getSpatial().setLocalTranslation(lastPos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.gde.scenecomposer.gizmo.light; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.environment.util.BoundingSphereDebug; |
||||||
|
import com.jme3.gde.scenecomposer.gizmo.light.shape.ProbeRadiusShape; |
||||||
|
import com.jme3.light.Light; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.material.RenderState; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.control.BillboardControl; |
||||||
|
import com.jme3.scene.shape.Quad; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
|
||||||
|
/** |
||||||
|
* Handles the creation of the appropriate light gizmo according to the light type. |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class LightGizmoFactory { |
||||||
|
|
||||||
|
public static Spatial createGizmo(AssetManager assetManager, Light light){ |
||||||
|
switch (light.getType()){ |
||||||
|
case Probe: |
||||||
|
return createLightProbeGizmo(assetManager, light); |
||||||
|
default: |
||||||
|
return createDefaultGizmo(assetManager, light); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private static Spatial createDefaultGizmo(AssetManager assetManager, Light light){ |
||||||
|
Quad q = new Quad(0.5f, 0.5f); |
||||||
|
Geometry g = new Geometry(light.getName(), q); |
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
Texture tex = assetManager.loadTexture("com/jme3/gde/scenecomposer/lightbulb32.png"); |
||||||
|
mat.setTexture("ColorMap", tex); |
||||||
|
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); |
||||||
|
g.setMaterial(mat); |
||||||
|
g.addControl(new LightGizmoControl(light)); |
||||||
|
g.setQueueBucket(RenderQueue.Bucket.Transparent); |
||||||
|
return g; |
||||||
|
} |
||||||
|
|
||||||
|
private static Spatial createLightProbeGizmo(AssetManager assetManager, Light light){ |
||||||
|
Node debugNode = new Node("Environment debug Node"); |
||||||
|
Sphere s = new Sphere(16, 16, 0.5f); |
||||||
|
Geometry debugGeom = new Geometry(light.getName(), s); |
||||||
|
Material debugMaterial = new Material(assetManager, "Common/MatDefs/Misc/reflect.j3md"); |
||||||
|
debugGeom.setMaterial(debugMaterial); |
||||||
|
Spatial debugBounds = ProbeRadiusShape.createShape(assetManager); |
||||||
|
|
||||||
|
debugNode.attachChild(debugGeom); |
||||||
|
debugNode.attachChild(debugBounds); |
||||||
|
debugNode.addControl(new LightProbeGizmoControl((LightProbe)light)); |
||||||
|
|
||||||
|
return debugNode; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* To change this license header, choose License Headers in Project Properties. |
||||||
|
* To change this template file, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.gde.scenecomposer.gizmo.light; |
||||||
|
|
||||||
|
import com.jme3.bounding.BoundingSphere; |
||||||
|
import com.jme3.environment.util.LightsDebugState; |
||||||
|
import com.jme3.light.LightProbe; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.control.AbstractControl; |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the marker's position whenever the light probe has moved. |
||||||
|
* Also update the gizmo radius according to the probe radius. |
||||||
|
*/ |
||||||
|
public class LightProbeGizmoControl extends AbstractControl{ |
||||||
|
|
||||||
|
private final Vector3f lastPos = new Vector3f(); |
||||||
|
private final LightProbe lightProbe; |
||||||
|
|
||||||
|
LightProbeGizmoControl(LightProbe light) { |
||||||
|
lightProbe = light; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void controlUpdate(float f) { |
||||||
|
|
||||||
|
if (!lightProbe.getPosition().equals(lastPos)) { |
||||||
|
if (getSpatial() != null) { |
||||||
|
lastPos.set(lightProbe.getPosition()); |
||||||
|
getSpatial().setLocalTranslation(lastPos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Geometry probeGeom = (Geometry) ((Node) getSpatial()).getChild(0); |
||||||
|
Material m = probeGeom.getMaterial(); |
||||||
|
if (lightProbe.isReady()) { |
||||||
|
m.setTexture("CubeMap", lightProbe.getPrefilteredEnvMap()); |
||||||
|
} |
||||||
|
Geometry probeRadius = (Geometry) ((Node) getSpatial()).getChild(1); |
||||||
|
probeRadius.setLocalScale(((BoundingSphere) lightProbe.getBounds()).getRadius()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
ShaderNodeDefinitions{ |
||||||
|
ShaderNodeDefinition Dashed { |
||||||
|
Type: Fragment |
||||||
|
|
||||||
|
Shader GLSL100: com/jme3/gde/scenecomposer/gizmo/light/mat/dashed/Dashed100.frag |
||||||
|
|
||||||
|
Documentation{ |
||||||
|
Renders dashed lines |
||||||
|
@input vec2 texCoord The texture coordinates |
||||||
|
@input float size the size of the dashes |
||||||
|
@input vec4 inColor the color of the fragment so far |
||||||
|
@outColor vec4 color the output color |
||||||
|
} |
||||||
|
Input { |
||||||
|
vec2 texCoord |
||||||
|
vec4 inColor |
||||||
|
float size |
||||||
|
} |
||||||
|
Output { |
||||||
|
vec4 outColor |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
void main(){ |
||||||
|
//@input vec2 texCoord The texture coordinates |
||||||
|
//@input float size the size of the dashes |
||||||
|
//@output vec4 color the output color |
||||||
|
|
||||||
|
//insert glsl code here |
||||||
|
outColor = inColor; |
||||||
|
outColor.a = step(1.0 - size, texCoord.x); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
MaterialDef Simple { |
||||||
|
MaterialParameters { |
||||||
|
Float DashSize |
||||||
|
} |
||||||
|
Technique { |
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
} |
||||||
|
VertexShaderNodes { |
||||||
|
ShaderNode CommonVert { |
||||||
|
Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn |
||||||
|
InputMappings { |
||||||
|
worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix |
||||||
|
modelPosition = Global.position.xyz |
||||||
|
texCoord1 = Attr.inTexCoord |
||||||
|
vertColor = Attr.inColor |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.position = projPosition |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
FragmentShaderNodes { |
||||||
|
ShaderNode Dashed { |
||||||
|
Definition : Dashed : com/jme3/gde/scenecomposer/gizmo/light/mat/dashed/Dashed.j3sn |
||||||
|
InputMappings { |
||||||
|
texCoord = CommonVert.texCoord1 |
||||||
|
inColor = CommonVert.vertColor |
||||||
|
size = MatParam.DashSize |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.color = outColor |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,168 @@ |
|||||||
|
/* |
||||||
|
* 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.gde.scenecomposer.gizmo.light.shape; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.material.RenderState; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Mesh; |
||||||
|
import com.jme3.scene.VertexBuffer.Type; |
||||||
|
import com.jme3.scene.control.BillboardControl; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import java.nio.FloatBuffer; |
||||||
|
import java.nio.ShortBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* A debuging shape for a BoundingSphere |
||||||
|
* Consists of 3 axis aligned circles. |
||||||
|
* |
||||||
|
* @author nehon |
||||||
|
*/ |
||||||
|
public class ProbeRadiusShape extends Mesh { |
||||||
|
|
||||||
|
protected int vertCount; |
||||||
|
protected int triCount; |
||||||
|
protected int radialSamples = 256; |
||||||
|
protected boolean useEvenSlices; |
||||||
|
protected boolean interior; |
||||||
|
/** |
||||||
|
* the distance from the center point each point falls on |
||||||
|
*/ |
||||||
|
public float radius; |
||||||
|
|
||||||
|
public float getRadius() { |
||||||
|
return radius; |
||||||
|
} |
||||||
|
|
||||||
|
public ProbeRadiusShape() { |
||||||
|
setGeometryData(); |
||||||
|
setIndexData(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* builds the vertices based on the radius |
||||||
|
*/ |
||||||
|
private void setGeometryData() { |
||||||
|
setMode(Mode.Lines); |
||||||
|
|
||||||
|
FloatBuffer posBuf = BufferUtils.createVector3Buffer((radialSamples + 1)); |
||||||
|
FloatBuffer colBuf = BufferUtils.createFloatBuffer((radialSamples + 1) * 4); |
||||||
|
FloatBuffer texBuf = BufferUtils.createVector2Buffer(radialSamples + 1); |
||||||
|
|
||||||
|
|
||||||
|
setBuffer(Type.Position, 3, posBuf); |
||||||
|
setBuffer(Type.Color, 4, colBuf); |
||||||
|
setBuffer(Type.TexCoord, 2, texBuf); |
||||||
|
|
||||||
|
// generate geometry
|
||||||
|
float fInvRS = 1.0f / radialSamples; |
||||||
|
|
||||||
|
// Generate points on the unit circle to be used in computing the mesh
|
||||||
|
// points on a sphere slice.
|
||||||
|
float[] afSin = new float[(radialSamples + 1)]; |
||||||
|
float[] afCos = new float[(radialSamples + 1)]; |
||||||
|
for (int iR = 0; iR < radialSamples; iR++) { |
||||||
|
float fAngle = FastMath.TWO_PI * fInvRS * iR; |
||||||
|
afCos[iR] = FastMath.cos(fAngle); |
||||||
|
afSin[iR] = FastMath.sin(fAngle); |
||||||
|
} |
||||||
|
afSin[radialSamples] = afSin[0]; |
||||||
|
afCos[radialSamples] = afCos[0]; |
||||||
|
|
||||||
|
for (int iR = 0; iR <= radialSamples; iR++) { |
||||||
|
posBuf.put(afCos[iR]) |
||||||
|
.put(afSin[iR]) |
||||||
|
.put(0); |
||||||
|
colBuf.put(ColorRGBA.Orange.r) |
||||||
|
.put(ColorRGBA.Orange.g) |
||||||
|
.put(ColorRGBA.Orange.b) |
||||||
|
.put(ColorRGBA.Orange.a); |
||||||
|
texBuf.put(iR % 2f) |
||||||
|
.put(iR % 2f); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
updateBound(); |
||||||
|
setStatic(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* sets the indices for rendering the sphere. |
||||||
|
*/ |
||||||
|
private void setIndexData() { |
||||||
|
// allocate connectivity
|
||||||
|
int nbSegments = (radialSamples);// * 3;
|
||||||
|
|
||||||
|
ShortBuffer idxBuf = BufferUtils.createShortBuffer(2 * nbSegments); |
||||||
|
setBuffer(Type.Index, 2, idxBuf); |
||||||
|
|
||||||
|
int idx = 0; |
||||||
|
int segDone = 0; |
||||||
|
while (segDone < nbSegments) { |
||||||
|
idxBuf.put((short) idx); |
||||||
|
idxBuf.put((short) (idx + 1)); |
||||||
|
idx++; |
||||||
|
segDone++; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience factory method that creates a debuging bounding sphere geometry |
||||||
|
* @param assetManager the assetManager |
||||||
|
* @return the bounding sphere debug geometry. |
||||||
|
*/ |
||||||
|
public static Geometry createShape(AssetManager assetManager) { |
||||||
|
ProbeRadiusShape b = new ProbeRadiusShape(); |
||||||
|
Geometry geom = new Geometry("BoundingDebug", b); |
||||||
|
|
||||||
|
Material mat = new Material(assetManager, "com/jme3/gde/scenecomposer/gizmo/light/mat/dashed/dashed.j3md"); |
||||||
|
mat.getAdditionalRenderState().setWireframe(true); |
||||||
|
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); |
||||||
|
mat.getAdditionalRenderState().setDepthWrite(false); |
||||||
|
mat.getAdditionalRenderState().setDepthTest(false); |
||||||
|
mat.setFloat("DashSize", 0.5f); |
||||||
|
geom.setQueueBucket(RenderQueue.Bucket.Transparent); |
||||||
|
geom.addControl(new BillboardControl()); |
||||||
|
|
||||||
|
|
||||||
|
geom.setMaterial(mat); |
||||||
|
return geom; |
||||||
|
|
||||||
|
} |
||||||
|
} |