shader-nodes-enhancement
parent
824e99c96e
commit
83aef82d92
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -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); |
||||
|
||||
} |
||||
} |
Loading…
Reference in new issue