Adds support for light porbe blending and Oriented box light probes with corrected parallax
This commit is contained in:
parent
824e99c96e
commit
83aef82d92
@ -34,6 +34,7 @@ package com.jme3.bounding;
|
|||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Plane;
|
import com.jme3.math.Plane;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
@ -107,6 +108,15 @@ public final class Intersection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean intersect(Camera camera, Vector3f center,float radius){
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
if (camera.getWorldPlane(i).pseudoDistance(center) <= -radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// private boolean axisTest(float a, float b, float fa, float fb, Vector3f v0, Vector3f v1, )
|
// private boolean axisTest(float a, float b, float fa, float fb, Vector3f v0, Vector3f v1, )
|
||||||
// private boolean axisTestX01(float a, float b, float fa, float fb,
|
// private boolean axisTestX01(float a, float b, float fa, float fb,
|
||||||
// Vector3f center, Vector3f ext,
|
// Vector3f center, Vector3f ext,
|
||||||
|
@ -38,14 +38,9 @@ import com.jme3.environment.util.EnvMapUtils;
|
|||||||
import com.jme3.light.LightProbe;
|
import com.jme3.light.LightProbe;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.*;
|
||||||
import com.jme3.renderer.RenderManager;
|
|
||||||
import com.jme3.renderer.ViewPort;
|
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.*;
|
||||||
import com.jme3.texture.Image;
|
|
||||||
import com.jme3.texture.Texture2D;
|
|
||||||
import com.jme3.texture.TextureCubeMap;
|
|
||||||
import com.jme3.texture.image.ColorSpace;
|
import com.jme3.texture.image.ColorSpace;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
import com.jme3.util.MipMapGenerator;
|
import com.jme3.util.MipMapGenerator;
|
||||||
@ -119,7 +114,7 @@ public class EnvironmentCamera extends BaseAppState {
|
|||||||
private final List<SnapshotJob> jobs = new ArrayList<SnapshotJob>();
|
private final List<SnapshotJob> jobs = new ArrayList<SnapshotJob>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an EnvironmentCamera with a size of 128
|
* Creates an EnvironmentCamera with a size of 256
|
||||||
*/
|
*/
|
||||||
public EnvironmentCamera() {
|
public EnvironmentCamera() {
|
||||||
}
|
}
|
||||||
@ -322,7 +317,7 @@ public class EnvironmentCamera extends BaseAppState {
|
|||||||
final Camera offCamera = new Camera(mapSize, mapSize);
|
final Camera offCamera = new Camera(mapSize, mapSize);
|
||||||
offCamera.setLocation(worldPos);
|
offCamera.setLocation(worldPos);
|
||||||
offCamera.setAxes(axisX, axisY, axisZ);
|
offCamera.setAxes(axisX, axisY, axisZ);
|
||||||
offCamera.setFrustumPerspective(90f, 1f, 1, 1000);
|
offCamera.setFrustumPerspective(90f, 1f, 0.1f, 1000);
|
||||||
offCamera.setLocation(position);
|
offCamera.setLocation(position);
|
||||||
return offCamera;
|
return offCamera;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
package com.jme3.environment;
|
package com.jme3.environment;
|
||||||
|
|
||||||
import com.jme3.app.Application;
|
import com.jme3.app.Application;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.environment.generation.*;
|
import com.jme3.environment.generation.*;
|
||||||
import com.jme3.environment.util.EnvMapUtils;
|
import com.jme3.environment.util.EnvMapUtils;
|
||||||
import com.jme3.light.LightProbe;
|
import com.jme3.light.LightProbe;
|
||||||
@ -204,6 +205,26 @@ public class LightProbeFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 static Node getDebugGui(AssetManager manager, LightProbe probe) {
|
||||||
|
if (!probe.isReady()) {
|
||||||
|
throw new UnsupportedOperationException("This EnvProbe is not ready yet, try to test isReady()");
|
||||||
|
}
|
||||||
|
|
||||||
|
Node debugNode = new Node("debug gui probe");
|
||||||
|
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(probe.getPrefilteredEnvMap(), manager);
|
||||||
|
debugNode.attachChild(debugPfemCm);
|
||||||
|
debugPfemCm.setLocalTranslation(520, 0, 0);
|
||||||
|
|
||||||
|
return debugNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An inner class to keep the state of a generation process
|
* An inner class to keep the state of a generation process
|
||||||
*/
|
*/
|
||||||
|
@ -34,9 +34,8 @@ package com.jme3.environment.util;
|
|||||||
import com.jme3.app.Application;
|
import com.jme3.app.Application;
|
||||||
import com.jme3.app.state.BaseAppState;
|
import com.jme3.app.state.BaseAppState;
|
||||||
import com.jme3.bounding.BoundingSphere;
|
import com.jme3.bounding.BoundingSphere;
|
||||||
|
import com.jme3.light.*;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.light.LightProbe;
|
|
||||||
import com.jme3.light.Light;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
@ -68,7 +67,7 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
@Override
|
@Override
|
||||||
protected void initialize(Application app) {
|
protected void initialize(Application app) {
|
||||||
debugNode = new Node("Environment debug Node");
|
debugNode = new Node("Environment debug Node");
|
||||||
Sphere s = new Sphere(16, 16, 1);
|
Sphere s = new Sphere(16, 16, 0.15f);
|
||||||
debugGeom = new Geometry("debugEnvProbe", s);
|
debugGeom = new Geometry("debugEnvProbe", s);
|
||||||
debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md");
|
debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md");
|
||||||
debugGeom.setMaterial(debugMaterial);
|
debugGeom.setMaterial(debugMaterial);
|
||||||
@ -80,6 +79,16 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(float tpf) {
|
public void update(float tpf) {
|
||||||
|
if(!isEnabled()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateLights(scene);
|
||||||
|
debugNode.updateLogicalState(tpf);
|
||||||
|
debugNode.updateGeometricState();
|
||||||
|
cleanProbes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLights(Spatial scene) {
|
||||||
for (Light light : scene.getWorldLightList()) {
|
for (Light light : scene.getWorldLightList()) {
|
||||||
switch (light.getType()) {
|
switch (light.getType()) {
|
||||||
|
|
||||||
@ -101,16 +110,18 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
m.setTexture("CubeMap", probe.getPrefilteredEnvMap());
|
m.setTexture("CubeMap", probe.getPrefilteredEnvMap());
|
||||||
}
|
}
|
||||||
n.setLocalTranslation(probe.getPosition());
|
n.setLocalTranslation(probe.getPosition());
|
||||||
n.getChild(1).setLocalScale(((BoundingSphere) probe.getBounds()).getRadius());
|
n.getChild(1).setLocalScale(probe.getArea().getRadius());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debugNode.updateLogicalState(tpf);
|
if( scene instanceof Node){
|
||||||
debugNode.updateGeometricState();
|
Node n = (Node)scene;
|
||||||
cleanProbes();
|
for (Spatial spatial : n.getChildren()) {
|
||||||
|
updateLights(spatial);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +149,9 @@ public class LightsDebugState extends BaseAppState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderManager rm) {
|
public void render(RenderManager rm) {
|
||||||
|
if(!isEnabled()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
rm.renderScene(debugNode, getApplication().getViewPort());
|
rm.renderScene(debugNode, getApplication().getViewPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ public final class DefaultLightFilter implements LightFilter {
|
|||||||
|
|
||||||
private Camera camera;
|
private Camera camera;
|
||||||
private final HashSet<Light> processedLights = new HashSet<Light>();
|
private final HashSet<Light> processedLights = new HashSet<Light>();
|
||||||
private final LightProbeBlendingStrategy probeBlendStrat;
|
private LightProbeBlendingStrategy probeBlendStrat;
|
||||||
|
|
||||||
public DefaultLightFilter() {
|
public DefaultLightFilter() {
|
||||||
probeBlendStrat = new BasicProbeBlendingStrategy();
|
probeBlendStrat = new WeightedProbeBlendingStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultLightFilter(LightProbeBlendingStrategy probeBlendStrat) {
|
public DefaultLightFilter(LightProbeBlendingStrategy probeBlendStrat) {
|
||||||
@ -113,5 +113,9 @@ public final class DefaultLightFilter implements LightFilter {
|
|||||||
vars.release();
|
vars.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLightProbeBlendingStrategy(LightProbeBlendingStrategy strategy){
|
||||||
|
probeBlendStrat = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,24 +31,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.light;
|
package com.jme3.light;
|
||||||
|
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.bounding.*;
|
||||||
import com.jme3.bounding.BoundingBox;
|
|
||||||
import com.jme3.bounding.BoundingSphere;
|
|
||||||
import com.jme3.bounding.BoundingVolume;
|
|
||||||
import com.jme3.environment.EnvironmentCamera;
|
import com.jme3.environment.EnvironmentCamera;
|
||||||
import com.jme3.environment.LightProbeFactory;
|
import com.jme3.environment.LightProbeFactory;
|
||||||
import com.jme3.environment.util.EnvMapUtils;
|
import com.jme3.export.*;
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.math.*;
|
||||||
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.renderer.Camera;
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.texture.TextureCubeMap;
|
import com.jme3.texture.TextureCubeMap;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -56,7 +48,7 @@ import java.util.logging.Logger;
|
|||||||
/**
|
/**
|
||||||
* A LightProbe is not exactly a light. It holds environment map information used for Image Based Lighting.
|
* 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.
|
* 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.
|
* A light probe has a position in world space. This is the position from where the Environment Map are rendered.
|
||||||
* There are two environment data structure held by the LightProbe :
|
* There are two environment data structure held by the LightProbe :
|
||||||
* - The irradiance spherical harmonics factors (used for indirect diffuse lighting in the PBR pipeline).
|
* - The irradiance spherical harmonics factors (used for indirect diffuse lighting in the PBR pipeline).
|
||||||
@ -64,10 +56,10 @@ import java.util.logging.Logger;
|
|||||||
* Note that when instantiating the LightProbe, both of those structures are null.
|
* Note that when instantiating the LightProbe, both of those structures are null.
|
||||||
* To compute them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)}
|
* To compute them see {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)}
|
||||||
* and {@link EnvironmentCamera}.
|
* 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).
|
* The light probe has an area of effect centered on its position. It can have a Spherical area or an Oriented Box area
|
||||||
*
|
*
|
||||||
* A LightProbe will only be taken into account when it's marked as ready.
|
* A LightProbe will only be taken into account when it's marked as ready and enabled.
|
||||||
* A light probe is ready when it has valid environment map data set.
|
* A light probe is ready when it has valid environment map data set.
|
||||||
* Note that you should never call setReady yourself.
|
* Note that you should never call setReady yourself.
|
||||||
*
|
*
|
||||||
@ -78,20 +70,25 @@ import java.util.logging.Logger;
|
|||||||
public class LightProbe extends Light implements Savable {
|
public class LightProbe extends Light implements Savable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(LightProbe.class.getName());
|
private static final Logger logger = Logger.getLogger(LightProbe.class.getName());
|
||||||
|
public static final Matrix4f FALLBACK_MATRIX = new Matrix4f(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1);
|
||||||
|
|
||||||
private Vector3f[] shCoeffs;
|
private Vector3f[] shCoeffs;
|
||||||
private TextureCubeMap prefilteredEnvMap;
|
private TextureCubeMap prefilteredEnvMap;
|
||||||
private BoundingVolume bounds = new BoundingSphere(1.0f, Vector3f.ZERO);
|
private ProbeArea area = new SphereProbeArea(Vector3f.ZERO, 1.0f);
|
||||||
private boolean ready = false;
|
private boolean ready = false;
|
||||||
private Vector3f position = new Vector3f();
|
private Vector3f position = new Vector3f();
|
||||||
private Node debugNode;
|
|
||||||
private int nbMipMaps;
|
private int nbMipMaps;
|
||||||
|
|
||||||
|
public enum AreaType{
|
||||||
|
Spherical,
|
||||||
|
OrientedBox
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty constructor used for serialization.
|
* Empty constructor used for serialization.
|
||||||
* You should never call it, use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} instead
|
* You should never call it, use {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)} instead
|
||||||
*/
|
*/
|
||||||
public LightProbe() {
|
public LightProbe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,13 +101,59 @@ public class LightProbe extends Light implements Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the prefiltered environment map
|
* Sets the prefiltered environment map
|
||||||
* @param prefileteredEnvMap the prefiltered environment map
|
* @param prefileteredEnvMap the prefiltered environment map
|
||||||
*/
|
*/
|
||||||
public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) {
|
public void setPrefilteredMap(TextureCubeMap prefileteredEnvMap) {
|
||||||
this.prefilteredEnvMap = prefileteredEnvMap;
|
this.prefilteredEnvMap = prefileteredEnvMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data to send to the shader.
|
||||||
|
* This is a column major matrix that is not a classic transform matrix, it's laid out in a particular way
|
||||||
|
// 3x3 rot mat|
|
||||||
|
// 0 1 2 | 3
|
||||||
|
// 0 | ax bx cx | px | )
|
||||||
|
// 1 | ay by cy | py | probe position
|
||||||
|
// 2 | az bz cz | pz | )
|
||||||
|
// --|----------|
|
||||||
|
// 3 | sx sy sz sp | -> 1/probe radius + nbMipMaps
|
||||||
|
// --scale--
|
||||||
|
* <p>
|
||||||
|
* (ax, ay, az) is the pitch rotation axis
|
||||||
|
* (bx, by, bz) is the yaw rotation axis
|
||||||
|
* (cx, cy, cz) is the roll rotation axis
|
||||||
|
* Like in a standard 3x3 rotation matrix.
|
||||||
|
* It's also the valid rotation matrix of the probe in world space.
|
||||||
|
* Note that for the Spherical Probe area this part is a 3x3 identity matrix.
|
||||||
|
* <p>
|
||||||
|
* (px, py, pz) is the position of the center of the probe in world space
|
||||||
|
* Like in a valid 4x4 transform matrix.
|
||||||
|
* <p>
|
||||||
|
* (sx, sy, sy) is the extent of the probe ( the scale )
|
||||||
|
* In a standard transform matrix the scale is applied to the rotation matrix part.
|
||||||
|
* In the shader we need the rotation and the scale to be separated, doing this avoid to extract
|
||||||
|
* the scale from a classic transform matrix in the shader
|
||||||
|
* <p>
|
||||||
|
* (sp) is a special entry, it contains the packed number of mip maps of the probe and the inverse radius for the probe.
|
||||||
|
* since the inverse radius in lower than 1, it's packed in the decimal part of the float.
|
||||||
|
* The number of mip maps is packed in the integer part of the float.
|
||||||
|
* (ie: for 6 mip maps and a radius of 3, sp= 6.3333333)
|
||||||
|
* <p>
|
||||||
|
* The radius is obvious for a SphereProbeArea,
|
||||||
|
* but in the case of a OrientedBoxProbeArea it's the max of the extent vector's components.
|
||||||
|
*/
|
||||||
|
public Matrix4f getUniformMatrix(){
|
||||||
|
|
||||||
|
Matrix4f mat = area.getUniformMatrix();
|
||||||
|
|
||||||
|
// setting the (sp) entry of the matrix
|
||||||
|
mat.m33 = nbMipMaps + 1f / area.getRadius();
|
||||||
|
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
@ -118,7 +161,7 @@ public class LightProbe extends Light implements Savable {
|
|||||||
oc.write(shCoeffs, "shCoeffs", null);
|
oc.write(shCoeffs, "shCoeffs", null);
|
||||||
oc.write(prefilteredEnvMap, "prefilteredEnvMap", null);
|
oc.write(prefilteredEnvMap, "prefilteredEnvMap", null);
|
||||||
oc.write(position, "position", null);
|
oc.write(position, "position", null);
|
||||||
oc.write(bounds, "bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
oc.write(area, "area", new SphereProbeArea(Vector3f.ZERO, 1.0f));
|
||||||
oc.write(ready, "ready", false);
|
oc.write(ready, "ready", false);
|
||||||
oc.write(nbMipMaps, "nbMipMaps", 0);
|
oc.write(nbMipMaps, "nbMipMaps", 0);
|
||||||
}
|
}
|
||||||
@ -127,10 +170,16 @@ public class LightProbe extends Light implements Savable {
|
|||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
|
|
||||||
prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null);
|
prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null);
|
||||||
position = (Vector3f) ic.readSavable("position", null);
|
position = (Vector3f) ic.readSavable("position", null);
|
||||||
bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
area = (ProbeArea)ic.readSavable("area", null);
|
||||||
|
if(area == null) {
|
||||||
|
// retro compat
|
||||||
|
BoundingSphere bounds = (BoundingSphere) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
|
||||||
|
area = new SphereProbeArea(bounds.getCenter(), bounds.getRadius());
|
||||||
|
}
|
||||||
|
area.setCenter(position);
|
||||||
nbMipMaps = ic.readInt("nbMipMaps", 0);
|
nbMipMaps = ic.readInt("nbMipMaps", 0);
|
||||||
ready = ic.readBoolean("ready", false);
|
ready = ic.readBoolean("ready", false);
|
||||||
|
|
||||||
@ -146,25 +195,49 @@ public class LightProbe extends Light implements Savable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the bounding volume of this LightProbe
|
* returns the bounding volume of this LightProbe
|
||||||
* @return a bounding volume.
|
* @return a bounding volume.
|
||||||
|
* @deprecated use {@link LightProbe#getArea()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public BoundingVolume getBounds() {
|
public BoundingVolume getBounds() {
|
||||||
return bounds;
|
return new BoundingSphere(((SphereProbeArea)area).getRadius(), ((SphereProbeArea)area).getCenter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the bounds of this LightProbe
|
* Sets the bounds of this LightProbe
|
||||||
* Note that for now only BoundingSphere is supported and this method will
|
* Note that for now only BoundingSphere is supported and this method will
|
||||||
* throw an UnsupportedOperationException with any other BoundingVolume type
|
* throw an UnsupportedOperationException with any other BoundingVolume type
|
||||||
* @param bounds the bounds of the LightProbe
|
* @param bounds the bounds of the LightProbe
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setBounds(BoundingVolume bounds) {
|
public void setBounds(BoundingVolume bounds) {
|
||||||
if( bounds.getType()!= BoundingVolume.Type.Sphere){
|
}
|
||||||
throw new UnsupportedOperationException("For not only BoundingSphere are suported for LightProbe");
|
|
||||||
|
public ProbeArea getArea() {
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAreaType(AreaType type){
|
||||||
|
switch (type){
|
||||||
|
case Spherical:
|
||||||
|
area = new SphereProbeArea(Vector3f.ZERO, 1.0f);
|
||||||
|
break;
|
||||||
|
case OrientedBox:
|
||||||
|
area = new OrientedBoxProbeArea(new Transform());
|
||||||
|
area.setCenter(position);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
this.bounds = bounds;
|
}
|
||||||
|
|
||||||
|
public AreaType getAreaType(){
|
||||||
|
if(area instanceof SphereProbeArea){
|
||||||
|
return AreaType.Spherical;
|
||||||
|
}
|
||||||
|
return AreaType.OrientedBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,27 +259,6 @@ public class LightProbe extends Light implements Savable {
|
|||||||
this.ready = 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 EnvProbe is not ready yet, try to test isReady()");
|
|
||||||
}
|
|
||||||
if (debugNode == null) {
|
|
||||||
debugNode = new Node("debug gui probe");
|
|
||||||
Node debugPfemCm = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(getPrefilteredEnvMap(), manager);
|
|
||||||
debugNode.attachChild(debugPfemCm);
|
|
||||||
debugPfemCm.setLocalTranslation(520, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return debugNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f[] getShCoeffs() {
|
public Vector3f[] getShCoeffs() {
|
||||||
return shCoeffs;
|
return shCoeffs;
|
||||||
}
|
}
|
||||||
@ -229,7 +281,7 @@ public class LightProbe extends Light implements Savable {
|
|||||||
*/
|
*/
|
||||||
public void setPosition(Vector3f position) {
|
public void setPosition(Vector3f position) {
|
||||||
this.position.set(position);
|
this.position.set(position);
|
||||||
getBounds().setCenter(position);
|
area.setCenter(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNbMipMaps() {
|
public int getNbMipMaps() {
|
||||||
@ -242,12 +294,17 @@ public class LightProbe extends Light implements Savable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean intersectsBox(BoundingBox box, TempVars vars) {
|
public boolean intersectsBox(BoundingBox box, TempVars vars) {
|
||||||
return getBounds().intersectsBoundingBox(box);
|
return area.intersectsBox(box, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean intersectsFrustum(Camera camera, TempVars vars) {
|
public boolean intersectsFrustum(Camera camera, TempVars vars) {
|
||||||
return camera.contains(bounds) != Camera.FrustumIntersect.Outside;
|
return area.intersectsFrustum(camera, vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
|
||||||
|
return area.intersectsSphere(sphere, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -267,14 +324,8 @@ public class LightProbe extends Light implements Savable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Light Probe : " + name + " at " + position + " / " + bounds;
|
return "Light Probe : " + name + " at " + position + " / " + area;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
|
|
||||||
return getBounds().intersectsSphere(sphere);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.profile.AppProfiler;
|
|
||||||
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;
|
|
||||||
private AppProfiler prof;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setProfiler(AppProfiler profiler) {
|
|
||||||
this.prof = profiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
249
jme3-core/src/main/java/com/jme3/light/OrientedBoxProbeArea.java
Normal file
249
jme3-core/src/main/java/com/jme3/light/OrientedBoxProbeArea.java
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
package com.jme3.light;
|
||||||
|
|
||||||
|
import com.jme3.bounding.BoundingBox;
|
||||||
|
import com.jme3.bounding.BoundingSphere;
|
||||||
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.math.*;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class OrientedBoxProbeArea implements ProbeArea {
|
||||||
|
private Transform transform = new Transform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see LightProbe#getUniformMatrix()
|
||||||
|
* for this Area type, the matrix is updated when the probe is transformed,
|
||||||
|
* and its data is used for bound checks in the light culling process.
|
||||||
|
*/
|
||||||
|
private Matrix4f uniformMatrix = new Matrix4f();
|
||||||
|
|
||||||
|
public OrientedBoxProbeArea() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrientedBoxProbeArea(Transform transform) {
|
||||||
|
transform.set(transform);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsBox(BoundingBox box, TempVars vars) {
|
||||||
|
|
||||||
|
Vector3f axis1 = getScaledAxis(0, vars.vect1);
|
||||||
|
Vector3f axis2 = getScaledAxis(1, vars.vect2);
|
||||||
|
Vector3f axis3 = getScaledAxis(2, vars.vect3);
|
||||||
|
|
||||||
|
Vector3f tn = vars.vect4;
|
||||||
|
Plane p = vars.plane;
|
||||||
|
Vector3f c = box.getCenter();
|
||||||
|
|
||||||
|
p.setNormal(0, 0, -1);
|
||||||
|
p.setConstant(-(c.z + box.getZExtent()));
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
p.setNormal(0, 0, 1);
|
||||||
|
p.setConstant(c.z - box.getZExtent());
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
|
||||||
|
p.setNormal(0, -1, 0);
|
||||||
|
p.setConstant(-(c.y + box.getYExtent()));
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
p.setNormal(0, 1, 0);
|
||||||
|
p.setConstant(c.y - box.getYExtent());
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
p.setNormal(-1, 0, 0);
|
||||||
|
p.setConstant(-(c.x + box.getXExtent()));
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
p.setNormal(1, 0, 0);
|
||||||
|
p.setConstant(c.x - box.getXExtent());
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRadius() {
|
||||||
|
return Math.max(Math.max(transform.getScale().x, transform.getScale().y), transform.getScale().z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
|
||||||
|
|
||||||
|
Vector3f closestPoint = getClosestPoint(vars, sphere.getCenter());
|
||||||
|
// check if the point intersects with the sphere bound
|
||||||
|
if (sphere.intersects(closestPoint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsFrustum(Camera camera, TempVars vars) {
|
||||||
|
|
||||||
|
// extract the scaled axis
|
||||||
|
// this allows a small optimization.
|
||||||
|
Vector3f axis1 = getScaledAxis(0, vars.vect1);
|
||||||
|
Vector3f axis2 = getScaledAxis(1, vars.vect2);
|
||||||
|
Vector3f axis3 = getScaledAxis(2, vars.vect3);
|
||||||
|
|
||||||
|
Vector3f tn = vars.vect4;
|
||||||
|
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
Plane p = camera.getWorldPlane(i);
|
||||||
|
if (!insidePlane(p, axis1, axis2, axis3, tn)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3f getScaledAxis(int index, Vector3f store) {
|
||||||
|
Matrix4f u = uniformMatrix;
|
||||||
|
float x = 0, y = 0, z = 0, s = 1;
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
x = u.m00;
|
||||||
|
y = u.m10;
|
||||||
|
z = u.m20;
|
||||||
|
s = u.m30;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
x = u.m01;
|
||||||
|
y = u.m11;
|
||||||
|
z = u.m21;
|
||||||
|
s = u.m31;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
x = u.m02;
|
||||||
|
y = u.m12;
|
||||||
|
z = u.m22;
|
||||||
|
s = u.m32;
|
||||||
|
}
|
||||||
|
return store.set(x, y, z).multLocal(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean insidePlane(Plane p, Vector3f axis1, Vector3f axis2, Vector3f axis3, Vector3f tn) {
|
||||||
|
// transform the plane normal in the box local space.
|
||||||
|
tn.set(axis1.dot(p.getNormal()), axis2.dot(p.getNormal()), axis3.dot(p.getNormal()));
|
||||||
|
|
||||||
|
// distance check
|
||||||
|
float radius = FastMath.abs(tn.x) +
|
||||||
|
FastMath.abs(tn.y) +
|
||||||
|
FastMath.abs(tn.z);
|
||||||
|
|
||||||
|
float distance = p.pseudoDistance(transform.getTranslation());
|
||||||
|
|
||||||
|
if (distance < -radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3f getClosestPoint(TempVars vars, Vector3f point) {
|
||||||
|
// non normalized direction
|
||||||
|
Vector3f dir = vars.vect2.set(point).subtractLocal(transform.getTranslation());
|
||||||
|
// initialize the closest point with box center
|
||||||
|
Vector3f closestPoint = vars.vect3.set(transform.getTranslation());
|
||||||
|
|
||||||
|
//store extent in an array
|
||||||
|
float[] r = vars.fWdU;
|
||||||
|
r[0] = transform.getScale().x;
|
||||||
|
r[1] = transform.getScale().y;
|
||||||
|
r[2] = transform.getScale().z;
|
||||||
|
|
||||||
|
// computing closest point to sphere center
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
// extract the axis from the 3x3 matrix
|
||||||
|
Vector3f axis = getScaledAxis(i, vars.vect1);
|
||||||
|
// nomalize (here we just divide by the extent
|
||||||
|
axis.divideLocal(r[i]);
|
||||||
|
// distance to the closest point on this axis.
|
||||||
|
float d = FastMath.clamp(dir.dot(axis), -r[i], r[i]);
|
||||||
|
closestPoint.addLocal(vars.vect4.set(axis).multLocal(d));
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMatrix() {
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
Matrix3f r = vars.tempMat3;
|
||||||
|
Matrix4f u = uniformMatrix;
|
||||||
|
transform.getRotation().toRotationMatrix(r);
|
||||||
|
|
||||||
|
u.m00 = r.get(0,0);
|
||||||
|
u.m10 = r.get(1,0);
|
||||||
|
u.m20 = r.get(2,0);
|
||||||
|
u.m01 = r.get(0,1);
|
||||||
|
u.m11 = r.get(1,1);
|
||||||
|
u.m21 = r.get(2,1);
|
||||||
|
u.m02 = r.get(0,2);
|
||||||
|
u.m12 = r.get(1,2);
|
||||||
|
u.m22 = r.get(2,2);
|
||||||
|
|
||||||
|
//scale
|
||||||
|
u.m30 = transform.getScale().x;
|
||||||
|
u.m31 = transform.getScale().y;
|
||||||
|
u.m32 = transform.getScale().z;
|
||||||
|
|
||||||
|
//position
|
||||||
|
u.m03 = transform.getTranslation().x;
|
||||||
|
u.m13 = transform.getTranslation().y;
|
||||||
|
u.m23 = transform.getTranslation().z;
|
||||||
|
|
||||||
|
vars.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4f getUniformMatrix() {
|
||||||
|
return uniformMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getExtent() {
|
||||||
|
return transform.getScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtent(Vector3f extent) {
|
||||||
|
transform.setScale(extent);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getCenter() {
|
||||||
|
return transform.getTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCenter(Vector3f center) {
|
||||||
|
transform.setTranslation(center);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternion getRotation() {
|
||||||
|
return transform.getRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(Quaternion rotation) {
|
||||||
|
transform.setRotation(rotation);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OrientedBoxProbeArea clone() throws CloneNotSupportedException {
|
||||||
|
return new OrientedBoxProbeArea(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter e) throws IOException {
|
||||||
|
OutputCapsule oc = e.getCapsule(this);
|
||||||
|
oc.write(transform, "transform", new Transform());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter i) throws IOException {
|
||||||
|
InputCapsule ic = i.getCapsule(this);
|
||||||
|
transform = (Transform) ic.readSavable("transform", new Transform());
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -212,12 +212,7 @@ public class PointLight extends Light {
|
|||||||
if (this.radius == 0) {
|
if (this.radius == 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
for (int i = 5; i >= 0; i--) {
|
return Intersection.intersect(camera, position, radius);
|
||||||
if (camera.getWorldPlane(i).pseudoDistance(position) <= -radius) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
jme3-core/src/main/java/com/jme3/light/ProbeArea.java
Normal file
33
jme3-core/src/main/java/com/jme3/light/ProbeArea.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package com.jme3.light;
|
||||||
|
|
||||||
|
import com.jme3.bounding.BoundingBox;
|
||||||
|
import com.jme3.bounding.BoundingSphere;
|
||||||
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
public interface ProbeArea extends Savable, Cloneable{
|
||||||
|
|
||||||
|
public void setCenter(Vector3f center);
|
||||||
|
|
||||||
|
public float getRadius();
|
||||||
|
|
||||||
|
public Matrix4f getUniformMatrix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Light#intersectsBox(BoundingBox, TempVars)
|
||||||
|
*/
|
||||||
|
public boolean intersectsBox(BoundingBox box, TempVars vars);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Light#intersectsSphere(BoundingSphere, TempVars)
|
||||||
|
*/
|
||||||
|
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Light#intersectsFrustum(Camera, TempVars)
|
||||||
|
*/
|
||||||
|
public abstract boolean intersectsFrustum(Camera camera, TempVars vars);
|
||||||
|
}
|
102
jme3-core/src/main/java/com/jme3/light/SphereProbeArea.java
Normal file
102
jme3-core/src/main/java/com/jme3/light/SphereProbeArea.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package com.jme3.light;
|
||||||
|
|
||||||
|
import com.jme3.bounding.*;
|
||||||
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class SphereProbeArea implements ProbeArea {
|
||||||
|
|
||||||
|
private Vector3f center = new Vector3f();
|
||||||
|
private float radius = 1;
|
||||||
|
private Matrix4f uniformMatrix = new Matrix4f();
|
||||||
|
|
||||||
|
public SphereProbeArea() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SphereProbeArea(Vector3f center, float radius) {
|
||||||
|
this.center.set(center);
|
||||||
|
this.radius = radius;
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getCenter() {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCenter(Vector3f center) {
|
||||||
|
this.center.set(center);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Matrix4f getUniformMatrix() {
|
||||||
|
return uniformMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMatrix(){
|
||||||
|
//position
|
||||||
|
uniformMatrix.m03 = center.x;
|
||||||
|
uniformMatrix.m13 = center.y;
|
||||||
|
uniformMatrix.m23 = center.z;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsBox(BoundingBox box, TempVars vars) {
|
||||||
|
return Intersection.intersect(box, center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
|
||||||
|
return Intersection.intersect(sphere, center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersectsFrustum(Camera camera, TempVars vars) {
|
||||||
|
return Intersection.intersect(camera, center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SphereProbeArea{" +
|
||||||
|
"center=" + center +
|
||||||
|
", radius=" + radius +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SphereProbeArea clone() throws CloneNotSupportedException {
|
||||||
|
return new SphereProbeArea(center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter e) throws IOException {
|
||||||
|
OutputCapsule oc = e.getCapsule(this);
|
||||||
|
oc.write(center, "center", new Vector3f());
|
||||||
|
oc.write(radius, "radius", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter i) throws IOException {
|
||||||
|
InputCapsule ic = i.getCapsule(this);
|
||||||
|
center = (Vector3f) ic.readSavable("center", new Vector3f());
|
||||||
|
radius = ic.readFloat("radius", 1);
|
||||||
|
updateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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 3 closest probe from the rendered object.
|
||||||
|
* <p>
|
||||||
|
* Image based lighting will be blended between those probes in the shader according to their distance and range.
|
||||||
|
*
|
||||||
|
* @author Nehon
|
||||||
|
*/
|
||||||
|
public class WeightedProbeBlendingStrategy implements LightProbeBlendingStrategy {
|
||||||
|
|
||||||
|
private final static int MAX_PROBES = 3;
|
||||||
|
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 3 first probes are the closest to the geometry since the
|
||||||
|
//light list is sorted according to the distance to the geom.
|
||||||
|
int addedProbes = 0;
|
||||||
|
for (LightProbe p : lightProbes) {
|
||||||
|
if (p.isReady() && p.isEnabled()) {
|
||||||
|
lightList.add(p);
|
||||||
|
addedProbes ++;
|
||||||
|
}
|
||||||
|
if (addedProbes == MAX_PROBES) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//clearing the list for next pass.
|
||||||
|
lightProbes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,17 +42,17 @@ import com.jme3.scene.Geometry;
|
|||||||
import com.jme3.shader.*;
|
import com.jme3.shader.*;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.*;
|
||||||
|
|
||||||
public final class SinglePassAndImageBasedLightingLogic extends DefaultTechniqueDefLogic {
|
public final class SinglePassAndImageBasedLightingLogic extends DefaultTechniqueDefLogic {
|
||||||
|
|
||||||
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
|
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
|
||||||
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
|
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
|
||||||
private static final String DEFINE_INDIRECT_LIGHTING = "INDIRECT_LIGHTING";
|
private static final String DEFINE_NB_PROBES = "NB_PROBES";
|
||||||
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
||||||
|
|
||||||
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||||
private LightProbe lightProbe = null;
|
private List<LightProbe> lightProbes = new ArrayList<>(3);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
|
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
|
||||||
@ -61,13 +61,13 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
|
|
||||||
private final int singlePassLightingDefineId;
|
private final int singlePassLightingDefineId;
|
||||||
private final int nbLightsDefineId;
|
private final int nbLightsDefineId;
|
||||||
private final int indirectLightingDefineId;
|
private final int nbProbesDefineId;
|
||||||
|
|
||||||
public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) {
|
public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) {
|
||||||
super(techniqueDef);
|
super(techniqueDef);
|
||||||
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
|
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
|
||||||
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
|
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
|
||||||
indirectLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_INDIRECT_LIGHTING, VarType.Boolean);
|
nbProbesDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_PROBES, VarType.Int);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,12 +81,9 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
//Though the second pass should not render IBL as it is taken care of on first pass like ambient light in phong lighting.
|
//Though the second pass should not render IBL as it is taken care of on first pass like ambient light in phong lighting.
|
||||||
//We cannot change the define between passes and the old technique, and for some reason the code fails on mac (renders nothing).
|
//We cannot change the define between passes and the old technique, and for some reason the code fails on mac (renders nothing).
|
||||||
if(lights != null) {
|
if(lights != null) {
|
||||||
lightProbe = extractIndirectLights(lights, false);
|
lightProbes.clear();
|
||||||
if (lightProbe == null) {
|
extractIndirectLights(lights, false);
|
||||||
defines.set(indirectLightingDefineId, false);
|
defines.set(nbProbesDefineId, lightProbes.size());
|
||||||
} else {
|
|
||||||
defines.set(indirectLightingDefineId, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
|
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
|
||||||
@ -113,35 +110,44 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
Uniform lightData = shader.getUniform("g_LightData");
|
Uniform lightData = shader.getUniform("g_LightData");
|
||||||
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
||||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||||
Uniform lightProbeData = shader.getUniform("g_LightProbeData");
|
|
||||||
lightProbeData.setVector4Length(1);
|
|
||||||
|
|
||||||
//TODO These 2 uniforms should be packed in an array, to be able to have several probes and blend between them.
|
// Matrix4f
|
||||||
|
Uniform lightProbeData = shader.getUniform("g_LightProbeData");
|
||||||
|
Uniform lightProbeData2 = shader.getUniform("g_LightProbeData2");
|
||||||
|
Uniform lightProbeData3 = shader.getUniform("g_LightProbeData3");
|
||||||
|
|
||||||
Uniform shCoeffs = shader.getUniform("g_ShCoeffs");
|
Uniform shCoeffs = shader.getUniform("g_ShCoeffs");
|
||||||
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap");
|
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap");
|
||||||
|
Uniform shCoeffs2 = shader.getUniform("g_ShCoeffs2");
|
||||||
|
Uniform lightProbePemMap2 = shader.getUniform("g_PrefEnvMap2");
|
||||||
|
Uniform shCoeffs3 = shader.getUniform("g_ShCoeffs3");
|
||||||
|
Uniform lightProbePemMap3 = shader.getUniform("g_PrefEnvMap3");
|
||||||
|
|
||||||
lightProbe = null;
|
lightProbes.clear();
|
||||||
if (startIndex != 0) {
|
if (startIndex != 0) {
|
||||||
// apply additive blending for 2nd and future passes
|
// apply additive blending for 2nd and future passes
|
||||||
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
|
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
|
||||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||||
}else{
|
}else{
|
||||||
lightProbe = extractIndirectLights(lightList,true);
|
extractIndirectLights(lightList,true);
|
||||||
ambientColor.setValue(VarType.Vector4, ambientLightColor);
|
ambientColor.setValue(VarType.Vector4, ambientLightColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
//If there is a lightProbe in the list we force its render on the first pass
|
//If there is a lightProbe in the list we force its render on the first pass
|
||||||
if(lightProbe != null){
|
if (!lightProbes.isEmpty()) {
|
||||||
BoundingSphere s = (BoundingSphere)lightProbe.getBounds();
|
LightProbe lightProbe = lightProbes.get(0);
|
||||||
lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / s.getRadius() + lightProbe.getNbMipMaps(), 0);
|
lastTexUnit = setProbeData(rm, lastTexUnit, lightProbeData, shCoeffs, lightProbePemMap, lightProbe);
|
||||||
shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs());
|
if (lightProbes.size() > 1) {
|
||||||
//assigning new texture indexes
|
lightProbe = lightProbes.get(1);
|
||||||
int pemUnit = lastTexUnit++;
|
lastTexUnit = setProbeData(rm, lastTexUnit, lightProbeData2, shCoeffs2, lightProbePemMap2, lightProbe);
|
||||||
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap());
|
}
|
||||||
lightProbePemMap.setValue(VarType.Int, pemUnit);
|
if (lightProbes.size() > 2) {
|
||||||
|
lightProbe = lightProbes.get(2);
|
||||||
|
setProbeData(rm, lastTexUnit, lightProbeData3, shCoeffs3, lightProbePemMap3, lightProbe);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//Disable IBL for this pass
|
//Disable IBL for this pass
|
||||||
lightProbeData.setVector4InArray(0,0,0,-1, 0);
|
lightProbeData.setValue(VarType.Matrix4, LightProbe.FALLBACK_MATRIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lightDataIndex = 0;
|
int lightDataIndex = 0;
|
||||||
@ -222,6 +228,18 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
return curIndex;
|
return curIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int setProbeData(RenderManager rm, int lastTexUnit, Uniform lightProbeData, Uniform shCoeffs, Uniform lightProbePemMap, LightProbe lightProbe) {
|
||||||
|
|
||||||
|
lightProbeData.setValue(VarType.Matrix4, lightProbe.getUniformMatrix());
|
||||||
|
//setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / area.getRadius() + lightProbe.getNbMipMaps(), 0);
|
||||||
|
shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs());
|
||||||
|
//assigning new texture indexes
|
||||||
|
int pemUnit = lastTexUnit++;
|
||||||
|
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap());
|
||||||
|
lightProbePemMap.setValue(VarType.Int, pemUnit);
|
||||||
|
return lastTexUnit;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
|
||||||
int nbRenderedLights = 0;
|
int nbRenderedLights = 0;
|
||||||
@ -241,9 +259,8 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LightProbe extractIndirectLights(LightList lightList, boolean removeLights) {
|
protected void extractIndirectLights(LightList lightList, boolean removeLights) {
|
||||||
ambientLightColor.set(0, 0, 0, 1);
|
ambientLightColor.set(0, 0, 0, 1);
|
||||||
LightProbe probe = null;
|
|
||||||
for (int j = 0; j < lightList.size(); j++) {
|
for (int j = 0; j < lightList.size(); j++) {
|
||||||
Light l = lightList.get(j);
|
Light l = lightList.get(j);
|
||||||
if (l instanceof AmbientLight) {
|
if (l instanceof AmbientLight) {
|
||||||
@ -254,7 +271,7 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (l instanceof LightProbe) {
|
if (l instanceof LightProbe) {
|
||||||
probe = (LightProbe)l;
|
lightProbes.add((LightProbe) l);
|
||||||
if(removeLights){
|
if(removeLights){
|
||||||
lightList.remove(l);
|
lightList.remove(l);
|
||||||
j--;
|
j--;
|
||||||
@ -262,6 +279,5 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ambientLightColor.a = 1.0f;
|
ambientLightColor.a = 1.0f;
|
||||||
return probe;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,29 +42,39 @@ import java.nio.FloatBuffer;
|
|||||||
public class WireFrustum extends Mesh {
|
public class WireFrustum extends Mesh {
|
||||||
|
|
||||||
public WireFrustum(Vector3f[] points){
|
public WireFrustum(Vector3f[] points){
|
||||||
|
initGeom(this, points);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mesh makeFrustum(Vector3f[] points){
|
||||||
|
Mesh m = new Mesh();
|
||||||
|
initGeom(m, points);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initGeom(Mesh m, Vector3f[] points) {
|
||||||
if (points != null)
|
if (points != null)
|
||||||
setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));
|
m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));
|
||||||
|
|
||||||
setBuffer(Type.Index, 2,
|
m.setBuffer(Type.Index, 2,
|
||||||
new short[]{
|
new short[]{
|
||||||
0, 1,
|
0, 1,
|
||||||
1, 2,
|
1, 2,
|
||||||
2, 3,
|
2, 3,
|
||||||
3, 0,
|
3, 0,
|
||||||
|
|
||||||
4, 5,
|
4, 5,
|
||||||
5, 6,
|
5, 6,
|
||||||
6, 7,
|
6, 7,
|
||||||
7, 4,
|
7, 4,
|
||||||
|
|
||||||
0, 4,
|
0, 4,
|
||||||
1, 5,
|
1, 5,
|
||||||
2, 6,
|
2, 6,
|
||||||
3, 7,
|
3, 7,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
getBuffer(Type.Index).setUsage(Usage.Static);
|
m.getBuffer(Type.Index).setUsage(Usage.Static);
|
||||||
setMode(Mode.Lines);
|
m.setMode(Mode.Lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(Vector3f[] points){
|
public void update(Vector3f[] points){
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#import "Common/ShaderLib/Parallax.glsllib"
|
#import "Common/ShaderLib/Parallax.glsllib"
|
||||||
#import "Common/ShaderLib/Lighting.glsllib"
|
#import "Common/ShaderLib/Lighting.glsllib"
|
||||||
|
|
||||||
|
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
#ifdef SEPARATE_TEXCOORD
|
#ifdef SEPARATE_TEXCOORD
|
||||||
varying vec2 texCoord2;
|
varying vec2 texCoord2;
|
||||||
@ -12,7 +11,6 @@ varying vec2 texCoord;
|
|||||||
varying vec4 Color;
|
varying vec4 Color;
|
||||||
|
|
||||||
uniform vec4 g_LightData[NB_LIGHTS];
|
uniform vec4 g_LightData[NB_LIGHTS];
|
||||||
|
|
||||||
uniform vec3 g_CameraPosition;
|
uniform vec3 g_CameraPosition;
|
||||||
|
|
||||||
uniform float m_Roughness;
|
uniform float m_Roughness;
|
||||||
@ -21,11 +19,20 @@ uniform float m_Metallic;
|
|||||||
varying vec3 wPosition;
|
varying vec3 wPosition;
|
||||||
|
|
||||||
|
|
||||||
#ifdef INDIRECT_LIGHTING
|
#if NB_PROBES >= 1
|
||||||
// uniform sampler2D m_IntegrateBRDF;
|
|
||||||
uniform samplerCube g_PrefEnvMap;
|
uniform samplerCube g_PrefEnvMap;
|
||||||
uniform vec3 g_ShCoeffs[9];
|
uniform vec3 g_ShCoeffs[9];
|
||||||
uniform vec4 g_LightProbeData;
|
uniform mat4 g_LightProbeData;
|
||||||
|
#endif
|
||||||
|
#if NB_PROBES >= 2
|
||||||
|
uniform samplerCube g_PrefEnvMap2;
|
||||||
|
uniform vec3 g_ShCoeffs2[9];
|
||||||
|
uniform mat4 g_LightProbeData2;
|
||||||
|
#endif
|
||||||
|
#if NB_PROBES == 3
|
||||||
|
uniform samplerCube g_PrefEnvMap3;
|
||||||
|
uniform vec3 g_ShCoeffs3[9];
|
||||||
|
uniform mat4 g_LightProbeData3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BASECOLORMAP
|
#ifdef BASECOLORMAP
|
||||||
@ -87,6 +94,91 @@ varying vec3 wNormal;
|
|||||||
uniform float m_AlphaDiscardThreshold;
|
uniform float m_AlphaDiscardThreshold;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
float renderProbe(vec3 viewDir, vec3 normal, vec3 norm, float Roughness, vec4 diffuseColor, vec4 specularColor, float ndotv, vec3 ao, mat4 lightProbeData,vec3 shCoeffs[9],samplerCube prefEnvMap, inout vec3 color ){
|
||||||
|
|
||||||
|
// lightProbeData is a mat4 with this layout
|
||||||
|
// 3x3 rot mat|
|
||||||
|
// 0 1 2 | 3
|
||||||
|
// 0 | ax bx cx | px | )
|
||||||
|
// 1 | ay by cy | py | probe position
|
||||||
|
// 2 | az bz cz | pz | )
|
||||||
|
// --|----------|
|
||||||
|
// 3 | sx sy sz sp | -> 1/probe radius + nbMipMaps
|
||||||
|
// --scale--
|
||||||
|
// parallax fix for spherical / obb bounds and probe blending from
|
||||||
|
// from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
||||||
|
vec3 rv = reflect(-viewDir, normal);
|
||||||
|
vec4 probePos = lightProbeData[3];
|
||||||
|
float invRadius = fract( probePos.w);
|
||||||
|
float nbMipMaps = probePos.w - invRadius;
|
||||||
|
vec3 direction = wPosition - probePos.xyz;
|
||||||
|
float ndf = 0.0;
|
||||||
|
|
||||||
|
if(lightProbeData[0][3] != 0.0){
|
||||||
|
// oriented box probe
|
||||||
|
mat3 wToLocalRot = mat3(lightProbeData);
|
||||||
|
wToLocalRot = inverse(wToLocalRot);
|
||||||
|
vec3 scale = vec3(lightProbeData[0][3], lightProbeData[1][3], lightProbeData[2][3]);
|
||||||
|
#if NB_PROBES >= 2
|
||||||
|
// probe blending
|
||||||
|
// compute fragment position in probe local space
|
||||||
|
vec3 localPos = wToLocalRot * wPosition;
|
||||||
|
localPos -= probePos.xyz;
|
||||||
|
// compute normalized distance field
|
||||||
|
vec3 localDir = abs(localPos);
|
||||||
|
localDir /= scale;
|
||||||
|
ndf = max(max(localDir.x, localDir.y), localDir.z);
|
||||||
|
#endif
|
||||||
|
// parallax fix
|
||||||
|
vec3 rayLs = wToLocalRot * rv;
|
||||||
|
rayLs /= scale;
|
||||||
|
|
||||||
|
vec3 positionLs = wPosition - probePos.xyz;
|
||||||
|
positionLs = wToLocalRot * positionLs;
|
||||||
|
positionLs /= scale;
|
||||||
|
|
||||||
|
vec3 unit = vec3(1.0);
|
||||||
|
vec3 firstPlaneIntersect = (unit - positionLs) / rayLs;
|
||||||
|
vec3 secondPlaneIntersect = (-unit - positionLs) / rayLs;
|
||||||
|
vec3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect);
|
||||||
|
float distance = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);
|
||||||
|
|
||||||
|
vec3 intersectPositionWs = wPosition + rv * distance;
|
||||||
|
rv = intersectPositionWs - probePos.xyz;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// spherical probe
|
||||||
|
// paralax fix
|
||||||
|
rv = invRadius * direction + rv;
|
||||||
|
|
||||||
|
#if NB_PROBES >= 2
|
||||||
|
// probe blending
|
||||||
|
float dist = sqrt(dot(direction, direction));
|
||||||
|
ndf = dist * invRadius;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 indirectDiffuse = vec3(0.0);
|
||||||
|
vec3 indirectSpecular = vec3(0.0);
|
||||||
|
indirectDiffuse = sphericalHarmonics(normal.xyz, shCoeffs) * diffuseColor.rgb;
|
||||||
|
vec3 dominantR = getSpecularDominantDir( normal, rv.xyz, Roughness * Roughness );
|
||||||
|
indirectSpecular = ApproximateSpecularIBLPolynomial(prefEnvMap, specularColor.rgb, Roughness, ndotv, dominantR, nbMipMaps);
|
||||||
|
|
||||||
|
#ifdef HORIZON_FADE
|
||||||
|
//horizon fade from http://marmosetco.tumblr.com/post/81245981087
|
||||||
|
float horiz = dot(rv, norm);
|
||||||
|
float horizFadePower = 1.0 - Roughness;
|
||||||
|
horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 );
|
||||||
|
horiz *= horiz;
|
||||||
|
indirectSpecular *= vec3(horiz);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao;
|
||||||
|
|
||||||
|
color = indirectLighting * step( 0.0, probePos.w);
|
||||||
|
return ndf;
|
||||||
|
}
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
vec2 newTexCoord;
|
vec2 newTexCoord;
|
||||||
vec3 viewDir = normalize(g_CameraPosition - wPosition);
|
vec3 viewDir = normalize(g_CameraPosition - wPosition);
|
||||||
@ -250,32 +342,47 @@ void main(){
|
|||||||
gl_FragColor.rgb += directLighting * fallOff;
|
gl_FragColor.rgb += directLighting * fallOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INDIRECT_LIGHTING
|
#if NB_PROBES >= 1
|
||||||
vec3 rv = reflect(-viewDir.xyz, normal.xyz);
|
vec3 color1 = vec3(0.0);
|
||||||
//prallax fix for spherical bounds from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
vec3 color2 = vec3(0.0);
|
||||||
// g_LightProbeData.w is 1/probe radius + nbMipMaps, g_LightProbeData.xyz is the position of the lightProbe.
|
vec3 color3 = vec3(0.0);
|
||||||
float invRadius = fract( g_LightProbeData.w);
|
float weight1 = 1.0;
|
||||||
float nbMipMaps = g_LightProbeData.w - invRadius;
|
float weight2 = 0.0;
|
||||||
rv = invRadius * (wPosition - g_LightProbeData.xyz) +rv;
|
float weight3 = 0.0;
|
||||||
|
|
||||||
//horizon fade from http://marmosetco.tumblr.com/post/81245981087
|
float ndf = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData, g_ShCoeffs, g_PrefEnvMap, color1);
|
||||||
float horiz = dot(rv, norm);
|
#if NB_PROBES >= 2
|
||||||
float horizFadePower = 1.0 - Roughness;
|
float ndf2 = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData2, g_ShCoeffs2, g_PrefEnvMap2, color2);
|
||||||
horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 );
|
#endif
|
||||||
horiz *= horiz;
|
#if NB_PROBES == 3
|
||||||
|
float ndf3 = renderProbe(viewDir, normal, norm, Roughness, diffuseColor, specularColor, ndotv, ao, g_LightProbeData3, g_ShCoeffs3, g_PrefEnvMap3, color3);
|
||||||
|
#endif
|
||||||
|
|
||||||
vec3 indirectDiffuse = vec3(0.0);
|
#if NB_PROBES >= 2
|
||||||
vec3 indirectSpecular = vec3(0.0);
|
float invNdf = max(1.0 - ndf,0.0);
|
||||||
indirectDiffuse = sphericalHarmonics(normal.xyz, g_ShCoeffs) * diffuseColor.rgb;
|
float invNdf2 = max(1.0 - ndf2,0.0);
|
||||||
vec3 dominantR = getSpecularDominantDir( normal, rv.xyz, Roughness*Roughness );
|
float sumNdf = ndf + ndf2;
|
||||||
indirectSpecular = ApproximateSpecularIBLPolynomial(g_PrefEnvMap, specularColor.rgb, Roughness, ndotv, dominantR, nbMipMaps);
|
float sumInvNdf = invNdf + invNdf2;
|
||||||
indirectSpecular *= vec3(horiz);
|
#if NB_PROBES == 3
|
||||||
|
float invNdf3 = max(1.0 - ndf3,0.0);
|
||||||
|
sumNdf += ndf3;
|
||||||
|
sumInvNdf += invNdf3;
|
||||||
|
weight3 = ((1.0 - (ndf3 / sumNdf)) / (NB_PROBES - 1)) * (invNdf3 / sumInvNdf);
|
||||||
|
#endif
|
||||||
|
|
||||||
vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao;
|
weight1 = ((1.0 - (ndf / sumNdf)) / (NB_PROBES - 1)) * (invNdf / sumInvNdf);
|
||||||
|
weight2 = ((1.0 - (ndf2 / sumNdf)) / (NB_PROBES - 1)) * (invNdf2 / sumInvNdf);
|
||||||
|
|
||||||
|
float weightSum = weight1 + weight2 + weight3;
|
||||||
|
|
||||||
|
weight1 /= weightSum;
|
||||||
|
weight2 /= weightSum;
|
||||||
|
weight3 /= weightSum;
|
||||||
|
#endif
|
||||||
|
gl_FragColor.rgb += color1 * clamp(weight1,0.0,1.0) + color2 * clamp(weight2,0.0,1.0) + color3 * clamp(weight3,0.0,1.0);
|
||||||
|
|
||||||
gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting * step( 0.0, g_LightProbeData.w);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EMISSIVE) || defined (EMISSIVEMAP)
|
#if defined(EMISSIVE) || defined (EMISSIVEMAP)
|
||||||
#ifdef EMISSIVEMAP
|
#ifdef EMISSIVEMAP
|
||||||
vec4 emissive = texture2D(m_EmissiveMap, newTexCoord);
|
vec4 emissive = texture2D(m_EmissiveMap, newTexCoord);
|
||||||
|
@ -62,6 +62,9 @@ MaterialDef PBR Lighting {
|
|||||||
//Set to true to activate Steep Parallax mapping
|
//Set to true to activate Steep Parallax mapping
|
||||||
Boolean SteepParallax
|
Boolean SteepParallax
|
||||||
|
|
||||||
|
//Horizon fade
|
||||||
|
Boolean HorizonFade
|
||||||
|
|
||||||
// Set to Use Lightmap
|
// Set to Use Lightmap
|
||||||
Texture2D LightMap
|
Texture2D LightMap
|
||||||
|
|
||||||
@ -157,6 +160,7 @@ MaterialDef PBR Lighting {
|
|||||||
AO_MAP: LightMapAsAOMap
|
AO_MAP: LightMapAsAOMap
|
||||||
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
NUM_MORPH_TARGETS: NumberOfMorphTargets
|
||||||
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
|
||||||
|
HORIZON_FADE: HorizonFade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ public class TestConeVSFrustum extends SimpleApplication {
|
|||||||
|
|
||||||
float radius = FastMath.tan(spotLight.getSpotOuterAngle()) * spotLight.getSpotRange();
|
float radius = FastMath.tan(spotLight.getSpotOuterAngle()) * spotLight.getSpotRange();
|
||||||
|
|
||||||
Cylinder cylinder = new Cylinder(5, 16, 0, radius, spotLight.getSpotRange(), true, false);
|
Cylinder cylinder = new Cylinder(5, 16, 0.01f, radius, spotLight.getSpotRange(), true, false);
|
||||||
geom = new Geometry("light", cylinder);
|
geom = new Geometry("light", cylinder);
|
||||||
geom.setMaterial(new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"));
|
geom.setMaterial(new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"));
|
||||||
geom.getMaterial().setColor("Diffuse", ColorRGBA.White);
|
geom.getMaterial().setColor("Diffuse", ColorRGBA.White);
|
||||||
|
301
jme3-examples/src/main/java/jme3test/light/TestObbVsBounds.java
Normal file
301
jme3-examples/src/main/java/jme3test/light/TestObbVsBounds.java
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
* 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.light;
|
||||||
|
|
||||||
|
import com.jme3.app.ChaseCameraAppState;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.bounding.BoundingBox;
|
||||||
|
import com.jme3.bounding.BoundingSphere;
|
||||||
|
import com.jme3.export.binary.BinaryExporter;
|
||||||
|
import com.jme3.input.KeyInput;
|
||||||
|
import com.jme3.input.MouseInput;
|
||||||
|
import com.jme3.input.controls.*;
|
||||||
|
import com.jme3.light.*;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.*;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.scene.*;
|
||||||
|
import com.jme3.scene.debug.Grid;
|
||||||
|
import com.jme3.scene.debug.WireFrustum;
|
||||||
|
import com.jme3.scene.shape.*;
|
||||||
|
import com.jme3.shadow.ShadowUtil;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TestObbVsBounds extends SimpleApplication {
|
||||||
|
|
||||||
|
private Node ln;
|
||||||
|
private BoundingBox aabb = new BoundingBox();
|
||||||
|
private BoundingSphere sphere = new BoundingSphere(10, new Vector3f(-30, 0, -60));
|
||||||
|
|
||||||
|
private final static float MOVE_SPEED = 60;
|
||||||
|
private Vector3f tmp = new Vector3f();
|
||||||
|
private Quaternion tmpQuat = new Quaternion();
|
||||||
|
private boolean moving, shift;
|
||||||
|
private boolean panning;
|
||||||
|
|
||||||
|
private OrientedBoxProbeArea area = new OrientedBoxProbeArea();
|
||||||
|
private Camera frustumCam;
|
||||||
|
|
||||||
|
private Geometry areaGeom;
|
||||||
|
private Geometry frustumGeom;
|
||||||
|
private Geometry aabbGeom;
|
||||||
|
private Geometry sphereGeom;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestObbVsBounds app = new TestObbVsBounds();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
|
||||||
|
frustumCam = cam.clone();
|
||||||
|
frustumCam.setFrustumFar(25);
|
||||||
|
makeCamFrustum();
|
||||||
|
aabb.setCenter(20, 10, -60);
|
||||||
|
aabb.setXExtent(10);
|
||||||
|
aabb.setYExtent(5);
|
||||||
|
aabb.setZExtent(3);
|
||||||
|
makeBoxWire(aabb);
|
||||||
|
makeSphereWire(sphere);
|
||||||
|
|
||||||
|
rootNode.addLight(new DirectionalLight());
|
||||||
|
AmbientLight al = new AmbientLight();
|
||||||
|
al.setColor(ColorRGBA.White.mult(0.2f));
|
||||||
|
rootNode.addLight(al);
|
||||||
|
|
||||||
|
Grid grid = new Grid(50, 50, 5);
|
||||||
|
Geometry gridGeom = new Geometry("grid", grid);
|
||||||
|
gridGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
|
||||||
|
gridGeom.getMaterial().setColor("Color", ColorRGBA.Gray);
|
||||||
|
rootNode.attachChild(gridGeom);
|
||||||
|
gridGeom.setLocalTranslation(-125, -25, -125);
|
||||||
|
|
||||||
|
area.setCenter(Vector3f.ZERO);
|
||||||
|
area.setExtent(new Vector3f(4, 8, 5));
|
||||||
|
makeAreaGeom();
|
||||||
|
|
||||||
|
ln = new Node("lb");
|
||||||
|
ln.setLocalRotation(new Quaternion(-0.18826798f, -0.38304946f, -0.12780227f, 0.895261f));
|
||||||
|
ln.attachChild(areaGeom);
|
||||||
|
ln.setLocalScale(4,8,5);
|
||||||
|
rootNode.attachChild(ln);
|
||||||
|
|
||||||
|
inputManager.addMapping("click", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
|
||||||
|
inputManager.addMapping("shift", new KeyTrigger(KeyInput.KEY_LSHIFT), new KeyTrigger(KeyInput.KEY_RSHIFT));
|
||||||
|
inputManager.addMapping("middleClick", new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));
|
||||||
|
inputManager.addMapping("up", new MouseAxisTrigger(MouseInput.AXIS_Y, false));
|
||||||
|
inputManager.addMapping("down", new MouseAxisTrigger(MouseInput.AXIS_Y, true));
|
||||||
|
inputManager.addMapping("left", new MouseAxisTrigger(MouseInput.AXIS_X, true));
|
||||||
|
inputManager.addMapping("right", new MouseAxisTrigger(MouseInput.AXIS_X, false));
|
||||||
|
|
||||||
|
|
||||||
|
final Node camTarget = new Node("CamTarget");
|
||||||
|
rootNode.attachChild(camTarget);
|
||||||
|
|
||||||
|
ChaseCameraAppState chaser = new ChaseCameraAppState();
|
||||||
|
chaser.setTarget(camTarget);
|
||||||
|
chaser.setMaxDistance(150);
|
||||||
|
chaser.setDefaultDistance(70);
|
||||||
|
chaser.setDefaultHorizontalRotation(FastMath.HALF_PI);
|
||||||
|
chaser.setMinVerticalRotation(-FastMath.PI);
|
||||||
|
chaser.setMaxVerticalRotation(FastMath.PI * 2);
|
||||||
|
chaser.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
|
||||||
|
stateManager.attach(chaser);
|
||||||
|
flyCam.setEnabled(false);
|
||||||
|
|
||||||
|
inputManager.addListener(new AnalogListener() {
|
||||||
|
public void onAnalog(String name, float value, float tpf) {
|
||||||
|
Spatial s = null;
|
||||||
|
float mult = 1;
|
||||||
|
if (moving) {
|
||||||
|
s = ln;
|
||||||
|
}
|
||||||
|
if (panning) {
|
||||||
|
s = camTarget;
|
||||||
|
mult = -1;
|
||||||
|
}
|
||||||
|
if ((moving || panning) && s != null) {
|
||||||
|
if (shift) {
|
||||||
|
if (name.equals("left")) {
|
||||||
|
tmp.set(cam.getDirection());
|
||||||
|
s.rotate(tmpQuat.fromAngleAxis(value, tmp));
|
||||||
|
}
|
||||||
|
if (name.equals("right")) {
|
||||||
|
tmp.set(cam.getDirection());
|
||||||
|
s.rotate(tmpQuat.fromAngleAxis(-value, tmp));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value *= MOVE_SPEED * mult;
|
||||||
|
if (name.equals("up")) {
|
||||||
|
tmp.set(cam.getUp()).multLocal(value);
|
||||||
|
s.move(tmp);
|
||||||
|
}
|
||||||
|
if (name.equals("down")) {
|
||||||
|
tmp.set(cam.getUp()).multLocal(-value);
|
||||||
|
s.move(tmp);
|
||||||
|
}
|
||||||
|
if (name.equals("left")) {
|
||||||
|
tmp.set(cam.getLeft()).multLocal(value);
|
||||||
|
s.move(tmp);
|
||||||
|
}
|
||||||
|
if (name.equals("right")) {
|
||||||
|
tmp.set(cam.getLeft()).multLocal(-value);
|
||||||
|
s.move(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "up", "down", "left", "right");
|
||||||
|
|
||||||
|
inputManager.addListener(new ActionListener() {
|
||||||
|
public void onAction(String name, boolean isPressed, float tpf) {
|
||||||
|
if (name.equals("click")) {
|
||||||
|
if (isPressed) {
|
||||||
|
moving = true;
|
||||||
|
} else {
|
||||||
|
moving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name.equals("middleClick")) {
|
||||||
|
if (isPressed) {
|
||||||
|
panning = true;
|
||||||
|
} else {
|
||||||
|
panning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name.equals("shift")) {
|
||||||
|
if (isPressed) {
|
||||||
|
shift = true;
|
||||||
|
} else {
|
||||||
|
shift = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "click", "middleClick", "shift");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeAreaGeom() {
|
||||||
|
|
||||||
|
Vector3f[] points = new Vector3f[8];
|
||||||
|
|
||||||
|
for (int i = 0; i < points.length; i++) {
|
||||||
|
points[i] = new Vector3f();
|
||||||
|
}
|
||||||
|
|
||||||
|
points[0].set(-1, -1, 1);
|
||||||
|
points[1].set(-1, 1, 1);
|
||||||
|
points[2].set(1, 1, 1);
|
||||||
|
points[3].set(1, -1, 1);
|
||||||
|
|
||||||
|
points[4].set(-1, -1, -1);
|
||||||
|
points[5].set(-1, 1, -1);
|
||||||
|
points[6].set(1, 1, -1);
|
||||||
|
points[7].set(1, -1, -1);
|
||||||
|
|
||||||
|
Mesh box = WireFrustum.makeFrustum(points);
|
||||||
|
areaGeom = new Geometry("light", (Mesh)box);
|
||||||
|
areaGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
|
||||||
|
areaGeom.getMaterial().setColor("Color", ColorRGBA.White);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeCamFrustum() {
|
||||||
|
Vector3f[] points = new Vector3f[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
points[i] = new Vector3f();
|
||||||
|
}
|
||||||
|
ShadowUtil.updateFrustumPoints2(frustumCam, points);
|
||||||
|
WireFrustum frustumShape = new WireFrustum(points);
|
||||||
|
frustumGeom = new Geometry("frustum", frustumShape);
|
||||||
|
frustumGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
|
||||||
|
rootNode.attachChild(frustumGeom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeBoxWire(BoundingBox box) {
|
||||||
|
Vector3f[] points = new Vector3f[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
points[i] = new Vector3f();
|
||||||
|
}
|
||||||
|
points[0].set(-1, -1, 1);
|
||||||
|
points[1].set(-1, 1, 1);
|
||||||
|
points[2].set(1, 1, 1);
|
||||||
|
points[3].set(1, -1, 1);
|
||||||
|
|
||||||
|
points[4].set(-1, -1, -1);
|
||||||
|
points[5].set(-1, 1, -1);
|
||||||
|
points[6].set(1, 1, -1);
|
||||||
|
points[7].set(1, -1, -1);
|
||||||
|
|
||||||
|
WireFrustum frustumShape = new WireFrustum(points);
|
||||||
|
aabbGeom = new Geometry("box", frustumShape);
|
||||||
|
aabbGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
|
||||||
|
aabbGeom.getMaterial().getAdditionalRenderState().setWireframe(true);
|
||||||
|
aabbGeom.setLocalTranslation(box.getCenter());
|
||||||
|
aabbGeom.setLocalScale(box.getXExtent(), box.getYExtent(), box.getZExtent());
|
||||||
|
rootNode.attachChild(aabbGeom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeSphereWire(BoundingSphere sphere) {
|
||||||
|
|
||||||
|
sphereGeom = new Geometry("box", new Sphere(16, 16, 10));
|
||||||
|
sphereGeom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
|
||||||
|
sphereGeom.getMaterial().getAdditionalRenderState().setWireframe(true);
|
||||||
|
sphereGeom.setLocalTranslation(sphere.getCenter());
|
||||||
|
rootNode.attachChild(sphereGeom);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleUpdate(float tpf) {
|
||||||
|
|
||||||
|
area.setCenter(ln.getLocalTranslation());
|
||||||
|
area.setRotation(ln.getLocalRotation());
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
boolean intersectBox = area.intersectsBox(aabb, vars);
|
||||||
|
boolean intersectFrustum = area.intersectsFrustum(frustumCam, vars);
|
||||||
|
boolean intersectSphere = area.intersectsSphere(sphere, vars);
|
||||||
|
vars.release();
|
||||||
|
|
||||||
|
boolean intersect = intersectBox || intersectFrustum || intersectSphere;
|
||||||
|
|
||||||
|
areaGeom.getMaterial().setColor("Color", intersect ? ColorRGBA.Green : ColorRGBA.White);
|
||||||
|
sphereGeom.getMaterial().setColor("Color", intersectSphere ? ColorRGBA.Cyan : ColorRGBA.White);
|
||||||
|
frustumGeom.getMaterial().setColor("Color", intersectFrustum ? ColorRGBA.Cyan : ColorRGBA.White);
|
||||||
|
aabbGeom.getMaterial().setColor("Color", intersectBox ? ColorRGBA.Cyan : ColorRGBA.White);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package jme3test.light.pbr;
|
package jme3test.light.pbr;
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication;
|
import com.jme3.app.SimpleApplication;
|
||||||
import com.jme3.bounding.BoundingSphere;
|
|
||||||
import com.jme3.environment.EnvironmentCamera;
|
import com.jme3.environment.EnvironmentCamera;
|
||||||
import com.jme3.environment.LightProbeFactory;
|
import com.jme3.environment.LightProbeFactory;
|
||||||
import com.jme3.environment.generation.JobProgressAdapter;
|
import com.jme3.environment.generation.JobProgressAdapter;
|
||||||
@ -10,6 +9,7 @@ import com.jme3.input.KeyInput;
|
|||||||
import com.jme3.input.controls.ActionListener;
|
import com.jme3.input.controls.ActionListener;
|
||||||
import com.jme3.input.controls.KeyTrigger;
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
import com.jme3.light.LightProbe;
|
import com.jme3.light.LightProbe;
|
||||||
|
import com.jme3.light.SphereProbeArea;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.scene.*;
|
import com.jme3.scene.*;
|
||||||
@ -127,7 +127,7 @@ public class RefEnv extends SimpleApplication {
|
|||||||
rootNode.getChild(0).setCullHint(Spatial.CullHint.Dynamic);
|
rootNode.getChild(0).setCullHint(Spatial.CullHint.Dynamic);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
((BoundingSphere) probe.getBounds()).setRadius(100);
|
((SphereProbeArea) probe.getArea()).setRadius(100);
|
||||||
rootNode.addLight(probe);
|
rootNode.addLight(probe);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,7 @@ import com.jme3.input.ChaseCamera;
|
|||||||
import com.jme3.input.KeyInput;
|
import com.jme3.input.KeyInput;
|
||||||
import com.jme3.input.controls.ActionListener;
|
import com.jme3.input.controls.ActionListener;
|
||||||
import com.jme3.input.controls.KeyTrigger;
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
import com.jme3.light.DirectionalLight;
|
import com.jme3.light.*;
|
||||||
import com.jme3.light.LightProbe;
|
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.post.FilterPostProcessor;
|
import com.jme3.post.FilterPostProcessor;
|
||||||
@ -205,7 +204,7 @@ public class TestPBRLighting extends SimpleApplication {
|
|||||||
tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager);
|
tex = EnvMapUtils.getCubeMapCrossDebugViewWithMipMaps(result.getPrefilteredEnvMap(), assetManager);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
((BoundingSphere) probe.getBounds()).setRadius(100);
|
((SphereProbeArea) probe.getArea()).setRadius(100);
|
||||||
rootNode.addLight(probe);
|
rootNode.addLight(probe);
|
||||||
//getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe);
|
//getStateManager().getState(EnvironmentManager.class).addEnvProbe(probe);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user