diff --git a/vtm/resources/assets/shaders/extrusion_layer_ext.glsl b/vtm/resources/assets/shaders/extrusion_layer_ext.glsl
index c83c7be3..95a8fe51 100644
--- a/vtm/resources/assets/shaders/extrusion_layer_ext.glsl
+++ b/vtm/resources/assets/shaders/extrusion_layer_ext.glsl
@@ -13,15 +13,31 @@ varying vec4 color;
//varying float depth;
const float ff = 255.0;
+#ifdef SHADOW
+uniform mat4 u_light_mvp;
+varying vec4 v_shadow_coords;
+#endif
+
/**
* The diffuse of surface dependent on the light position.
*
* @param r_norm the normal vector of vertex's face
*/
-float diffuse(in vec3 r_norm) {
+float diffuse(in vec3 r_norm, out bool hasLight) {
float l = dot(normalize(r_norm), normalize(u_light));
+ hasLight = l > 0.0;
l = clamp((1.0 + l) / 2.0, 0.0, 1.0);
- return(0.8 + l * 0.2);
+ #ifdef SHADOW
+ if (hasLight) {
+ //l = (l + (1.0 - r_norm.z)) * 0.5;
+ l = 0.8 + l * 0.2;
+ } else {
+ l = 0.5 + l * 0.3;
+ }
+ #else
+ l = 0.8 + l * 0.2;
+ #endif
+ return (l);
}
void main() {
@@ -31,6 +47,8 @@ void main() {
height = u_zlimit;
}
gl_Position = u_mvp * vec4(a_pos.xy, height, 1.0);
+ bool hasLight = false;
+
//depth = gl_Position.z;
if (u_mode == -1) {
;
@@ -40,7 +58,7 @@ void main() {
// roof / depth pass
r_norm = vec3(0.0, 0.0, 1.0);
color = u_color[0] * u_alpha;
- color.rgb *= diffuse(r_norm);
+ color.rgb *= diffuse(r_norm, hasLight);
} else {
float lightX = u_mode == 1 ? a_normal.y : a_normal.x;
r_norm.x = (lightX / ff) * 2.0 - 1.0;
@@ -59,7 +77,7 @@ void main() {
// sides 2 - use 0x00ff
color = u_color[2];
}
- color.rgb *= diffuse(r_norm) * z * h;
+ color.rgb *= diffuse(r_norm, hasLight) * z * h;
}
color *= u_alpha;
} else if (u_mode == 3) {
@@ -67,6 +85,15 @@ void main() {
float z = (0.98 - gl_Position.z * 0.02);
color = u_color[3] * z;
}
+ #ifdef SHADOW
+ if (hasLight) {
+ vec4 positionFromLight = u_light_mvp * a_pos;
+ v_shadow_coords = (positionFromLight / positionFromLight.w);
+ } else {
+ // Discard shadow on unlighted faces
+ v_shadow_coords = vec4(-1.0);
+ }
+ #endif
}
$$
@@ -76,6 +103,76 @@ precision highp float;
#endif
varying vec4 color;
-void main() {
- gl_FragColor = color;
+#ifdef SHADOW
+varying vec4 v_shadow_coords; // the coords in shadow map
+
+uniform sampler2D u_shadowMap;
+uniform vec4 u_lightColor;
+uniform float u_shadowRes;
+
+const bool DEBUG = false;
+
+const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
+const float minTrans = 1.0 - transitionDistance;
+
+const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
+const float biasOffset = 0.005; // offset to remove shadow acne
+const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
+
+#if GLVERSION == 20
+float decodeFloat (vec4 color) {
+ const vec4 bitShift = vec4(
+ 1.0 / (256.0 * 256.0 * 256.0),
+ 1.0 / (256.0 * 256.0),
+ 1.0 / 256.0,
+ 1.0
+ );
+ return dot(color, bitShift);
+}
+#endif
+#endif
+
+void main() {
+ #ifdef SHADOW
+ float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
+ float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
+ if (shadowX > 1.0 || shadowY > 1.0) {
+ // Outside the light texture set to 0.0
+ gl_FragColor = vec4(color.rgb * u_lightColor.rgb, color.a);
+ if (DEBUG) {
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
+ }
+ } else {
+ // Inside set to 1.0; make a transition to the border
+ float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
+ (1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
+ float distanceToLight = clamp(v_shadow_coords.z - biasOffset, 0.0, 1.0); // avoid unexpected shadow
+
+ // Smooth shadow at borders
+ float shadowDiffuse = 0.0;
+ float texelSize = 1.0 / u_shadowRes;
+ for (int x = -pcfCount; x <= pcfCount; x++) {
+ for (int y = -pcfCount; y <= pcfCount; y++) {
+ #if GLVERSION == 30
+ float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
+ #else
+ float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
+ #endif
+ if (distanceToLight > depth) {
+ shadowDiffuse += 1.0;
+ }
+ }
+ }
+ shadowDiffuse /= pcfTexels;
+ shadowDiffuse *= shadowOpacity;
+
+ if (DEBUG && shadowDiffuse < 1.0) {
+ gl_FragColor = vec4(shadowDiffuse, color.gb, 0.1);
+ } else {
+ gl_FragColor = vec4((color.rgb * u_lightColor.rgb) * (1.0 - u_lightColor.a * shadowDiffuse), color.a);
+ }
+ }
+ #else
+ gl_FragColor = color;
+ #endif
}
diff --git a/vtm/resources/assets/shaders/extrusion_layer_mesh.glsl b/vtm/resources/assets/shaders/extrusion_layer_mesh.glsl
index 8f7d5ae4..411b2583 100644
--- a/vtm/resources/assets/shaders/extrusion_layer_mesh.glsl
+++ b/vtm/resources/assets/shaders/extrusion_layer_mesh.glsl
@@ -9,6 +9,11 @@ attribute vec4 a_pos;
attribute vec2 a_normal;
varying vec4 color;
+#ifdef SHADOW
+uniform mat4 u_light_mvp;
+varying vec4 v_shadow_coords;
+#endif
+
void main() {
// change height by u_alpha
vec4 pos = a_pos;
@@ -28,18 +33,40 @@ void main() {
float l = dot(r_norm, normalize(u_light));
- //l *= 0.8
+ #ifdef SHADOW
+ bool hasLight = l > 0.0;
+ #endif
+
+ //l *= 0.8;
//vec3 opp_light_dir = normalize(vec3(-u_light.xy, u_light.z));
//l += dot(r_norm, opp_light_dir) * 0.2;
// [-1,1] to range [0,1]
l = (1.0 + l) / 2.0;
+ #ifdef SHADOW
+ if (hasLight) {
+ l = 0.75 + l * 0.25;
+ } else {
+ l = 0.5 + l * 0.3;
+ }
+ #else
l = 0.75 + l * 0.25;
+ #endif
// extreme fake-ssao by height
l += (clamp(a_pos.z / 2048.0, 0.0, 0.1) - 0.05);
color = vec4(u_color.rgb * (clamp(l, 0.0, 1.0)), u_color.a) * u_alpha;
+
+ #ifdef SHADOW
+ if (hasLight) {
+ vec4 positionFromLight = u_light_mvp * a_pos;
+ v_shadow_coords = (positionFromLight / positionFromLight.w);
+ } else {
+ // Discard shadow on unlighted faces
+ v_shadow_coords = vec4(-1.0);
+ }
+ #endif
}
$$
@@ -49,6 +76,77 @@ precision highp float;
#endif
varying vec4 color;
-void main() {
- gl_FragColor = color;
+#ifdef SHADOW
+varying vec4 v_shadow_coords; // the coords in shadow map
+
+uniform sampler2D u_shadowMap;
+uniform vec4 u_lightColor;
+uniform float u_shadowRes;
+uniform int u_mode;
+
+const bool DEBUG = false;
+
+const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
+const float minTrans = 1.0 - transitionDistance;
+
+const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
+const float biasOffset = 0.005; // offset to remove shadow acne
+const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
+
+#if GLVERSION == 20
+float decodeFloat (vec4 color) {
+ const vec4 bitShift = vec4(
+ 1.0 / (256.0 * 256.0 * 256.0),
+ 1.0 / (256.0 * 256.0),
+ 1.0 / 256.0,
+ 1.0
+ );
+ return dot(color, bitShift);
+}
+#endif
+#endif
+
+void main() {
+ #ifdef SHADOW
+ float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
+ float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
+ if (shadowX > 1.0 || shadowY > 1.0) {
+ // Outside the light texture set to 0.0
+ gl_FragColor = vec4(color.rgb * u_lightColor.rgb, color.a);
+ if (DEBUG) {
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
+ }
+ } else {
+ // Inside set to 1.0; make a transition to the border
+ float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
+ (1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
+ float distanceToLight = clamp(v_shadow_coords.z - biasOffset, 0.0, 1.0); // avoid unexpected shadow
+
+ // smooth shadow at borders
+ float shadowDiffuse = 0.0;
+ float texelSize = 1.0 / u_shadowRes;
+ for (int x = -pcfCount; x <= pcfCount; x++) {
+ for (int y = -pcfCount; y <= pcfCount; y++) {
+ #if GLVERSION == 30
+ float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
+ #else
+ float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
+ #endif
+ if (distanceToLight > depth) {
+ shadowDiffuse += 1.0;
+ }
+ }
+ }
+ shadowDiffuse /= pcfTexels;
+ shadowDiffuse *= shadowOpacity;
+
+ if (DEBUG && shadowDiffuse < 1.0) {
+ gl_FragColor = vec4(shadowDiffuse, color.gb, 0.1);
+ } else {
+ gl_FragColor = vec4((color.rgb * u_lightColor.rgb) * (1.0 - u_lightColor.a * shadowDiffuse), color.a);
+ }
+ }
+ #else
+ gl_FragColor = color;
+ #endif
}
diff --git a/vtm/resources/assets/shaders/extrusion_shadow_ground.glsl b/vtm/resources/assets/shaders/extrusion_shadow_ground.glsl
new file mode 100644
index 00000000..f67dc91b
--- /dev/null
+++ b/vtm/resources/assets/shaders/extrusion_shadow_ground.glsl
@@ -0,0 +1,91 @@
+#ifdef GLES
+precision highp float;
+#endif
+attribute vec4 a_pos;
+uniform mat4 u_mvp;
+uniform mat4 u_light_mvp;
+
+varying vec4 v_shadow_coords;
+
+void main(void) {
+ gl_Position = u_mvp * a_pos;
+ vec4 positionFromLight = u_light_mvp * a_pos;
+ v_shadow_coords = (positionFromLight / positionFromLight.w);
+}
+
+$$
+
+#ifdef GLES
+precision highp float;
+#endif
+varying vec4 v_shadow_coords;
+
+uniform sampler2D u_shadowMap;
+uniform vec4 u_lightColor;
+uniform float u_shadowRes;
+
+const bool DEBUG = false;
+
+const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
+const float minTrans = 1.0 - transitionDistance;
+
+const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
+const float biasOffset = 0.005; // offset to remove shadow acne
+const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
+
+#if GLVERSION == 20
+float decodeFloat (vec4 color) {
+ const vec4 bitShift = vec4(
+ 1.0 / (256.0 * 256.0 * 256.0),
+ 1.0 / (256.0 * 256.0),
+ 1.0 / 256.0,
+ 1.0
+ );
+ return dot(color, bitShift);
+}
+#endif
+
+void main() {
+ float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
+ float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
+ if (shadowX > 1.0 || shadowY > 1.0) {
+ // Outside the light texture set to 0.0
+ gl_FragColor = vec4(u_lightColor.rgb, 1.0);
+ if (DEBUG) {
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
+ }
+ } else {
+ // Inside set to 1.0; make a transition to the border
+ float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
+ (1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
+ #if GLVERSION == 30
+ float distanceToLight = v_shadow_coords.z; // remove shadow acne
+ #else
+ float distanceToLight = v_shadow_coords.z - biasOffset; // remove shadow acne
+ #endif
+ distanceToLight = clamp(distanceToLight, 0.0, 1.0); // avoid unexpected far shadow
+ // smooth shadow at borders
+ float shadowDiffuse = 0.0;
+ float texelSize = 1.0 / u_shadowRes;
+ for (int x = -pcfCount; x <= pcfCount; x++) {
+ for (int y = -pcfCount; y <= pcfCount; y++) {
+ #if GLVERSION == 30
+ float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
+ #else
+ float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
+ #endif
+ if (distanceToLight > depth) {
+ shadowDiffuse += 1.0;
+ }
+ }
+ }
+ shadowDiffuse /= pcfTexels;
+ shadowDiffuse *= shadowOpacity;
+
+ if (DEBUG && shadowDiffuse < 1.0) {
+ gl_FragColor = vec4(shadowDiffuse, 0.0, 0.0, 0.1);
+ } else {
+ gl_FragColor = vec4(u_lightColor.rgb * (1.0 - u_lightColor.a * shadowDiffuse), 1.0);
+ }
+ }
+}
diff --git a/vtm/resources/assets/shaders/extrusion_shadow_light.glsl b/vtm/resources/assets/shaders/extrusion_shadow_light.glsl
new file mode 100644
index 00000000..7292e2d9
--- /dev/null
+++ b/vtm/resources/assets/shaders/extrusion_shadow_light.glsl
@@ -0,0 +1,53 @@
+#ifdef GLES
+precision highp float;
+precision highp int;
+#endif
+uniform mat4 u_mvp;
+uniform vec4 u_color[4];
+uniform int u_mode;
+uniform float u_zlimit;
+attribute vec4 a_pos;
+
+void main(void) {
+ gl_Position = u_mvp * a_pos;
+}
+
+$$
+
+#ifdef GLES
+precision highp float;
+precision highp int;
+#endif
+
+uniform float u_alpha;
+
+#if GLVERSION == 20
+vec4 encodeFloat (float depth) {
+ const vec4 bitShift = vec4(
+ 256.0 * 256.0 * 256.0,
+ 256.0 * 256.0,
+ 256.0,
+ 1.0
+ );
+ const vec4 bitMask = vec4(
+ 0.0,
+ 1.0 / 256.0,
+ 1.0 / 256.0,
+ 1.0 / 256.0
+ );
+ vec4 comp = fract(depth * bitShift);
+ comp -= comp.xxyz * bitMask;
+ return comp;
+}
+#endif
+
+void main(void) {
+ if (u_alpha < 0.8)
+ discard; // remove shadow when alpha is too small
+
+ #if GLVERSION == 30
+ gl_FragColor = vec4(1.0);
+ #else
+ gl_FragColor = encodeFloat(gl_FragCoord.z);
+ #endif
+}
diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
index e3dd4b51..03f61d55 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
@@ -2,7 +2,7 @@
* Copyright 2013 Hannes Janetzek
* Copyright 2016-2018 devemux86
* Copyright 2016 Robin Boldt
- * Copyright 2017-2018 Gustl22
+ * Copyright 2017-2019 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@@ -32,6 +32,7 @@ import org.oscim.renderer.OffscreenRenderer;
import org.oscim.renderer.OffscreenRenderer.Mode;
import org.oscim.renderer.bucket.ExtrusionBuckets;
import org.oscim.renderer.bucket.RenderBuckets;
+import org.oscim.renderer.light.ShadowRenderer;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.theme.styles.RenderStyle;
@@ -59,6 +60,11 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
*/
public static boolean RAW_DATA = false;
+ /**
+ * Use shadow rendering.
+ */
+ public static boolean SHADOW = false;
+
/**
* Let vanish extrusions / meshes which are covered by others.
* {@link org.oscim.renderer.bucket.RenderBucket#EXTRUSION}: roofs are always translucent.
@@ -113,7 +119,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
mZoomLimiter = new ZoomLimiter(tileLayer.getManager(), zoomMin, zoomMax, zoomMin);
mRenderer = mExtrusionRenderer = new BuildingRenderer(tileLayer.tileRenderer(), mZoomLimiter, mesh, TRANSLUCENT);
- if (POST_AA)
+ // TODO Allow SHADOW and POST_AA at same time
+ if (SHADOW)
+ mRenderer = new ShadowRenderer(mExtrusionRenderer);
+ else if (POST_AA)
mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
mRenderTheme = tileLayer.getTheme();
diff --git a/vtm/src/org/oscim/renderer/ExtrusionRenderer.java b/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
index 208acf4f..630d5850 100644
--- a/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
+++ b/vtm/src/org/oscim/renderer/ExtrusionRenderer.java
@@ -24,6 +24,7 @@ import org.oscim.core.Tile;
import org.oscim.renderer.bucket.ExtrusionBucket;
import org.oscim.renderer.bucket.ExtrusionBuckets;
import org.oscim.renderer.bucket.RenderBuckets;
+import org.oscim.renderer.light.ShadowRenderer;
import org.oscim.renderer.light.Sun;
import org.oscim.utils.FastMath;
import org.slf4j.Logger;
@@ -49,6 +50,7 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
private Sun mSun;
private boolean mEnableCurrentSunPos;
+ private boolean mUseLight = true;
public ExtrusionRenderer(boolean mesh, boolean translucent) {
mMesh = mesh;
@@ -106,7 +108,11 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
int uZLimit;
public Shader(String shader) {
- if (!create(shader))
+ this(shader, null);
+ }
+
+ public Shader(String shader, String directives) {
+ if (!createDirective(shader, directives))
return;
uMVP = getUniform("u_mvp");
@@ -120,12 +126,20 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
}
}
+ public void enableCurrentSunPos(boolean enableSunPos) {
+ mEnableCurrentSunPos = enableSunPos;
+ }
+
+ public Shader getShader() {
+ return mShader;
+ }
+
public Sun getSun() {
return mSun;
}
- public void enableCurrentSunPos(boolean enableSunPos) {
- mEnableCurrentSunPos = enableSunPos;
+ public boolean isMesh() {
+ return mMesh;
}
@Override
@@ -209,6 +223,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
gl.uniform1i(s.uMode, -1);
for (int i = 0; i < mBucketsCnt; i++) {
+ if (ebs[i] == null)
+ return;
if (ebs[i].ibo == null)
return;
@@ -232,7 +248,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
gl.depthFunc(GL.EQUAL);
}
- GLState.blend(true);
+ // Depth cannot be transparent (in GL20)
+ GLState.blend(mUseLight);
GLState.enableVertexArrays(s.aPos, s.aNormal);
@@ -266,8 +283,9 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
gl.vertexAttribPointer(s.aPos, 3, GL.SHORT,
false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset());
- gl.vertexAttribPointer(s.aNormal, 2, GL.UNSIGNED_BYTE,
- false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset() + RenderBuckets.SHORT_BYTES * 3);
+ if (mUseLight)
+ gl.vertexAttribPointer(s.aNormal, 2, GL.UNSIGNED_BYTE,
+ false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset() + RenderBuckets.SHORT_BYTES * 3);
/* draw extruded outlines (mMesh == false) */
if (eb.idx[0] > 0) {
@@ -319,7 +337,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
}
/* just a temporary reference! */
- ebs[i] = null;
+ /* But for shadows we use them multiple times */
+ //ebs[i] = null;
}
if (!mTranslucent)
@@ -345,8 +364,16 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
float x = (float) ((l.x - v.pos.x) * curScale);
float y = (float) ((l.y - v.pos.y) * curScale);
+ // Create model matrix
v.mvp.setTransScale(x, y, scale / COORD_SCALE);
v.mvp.setValue(10, scale / 10);
+
+ // Create shadow map converter
+ // TODO may code it cleaner
+ if (s instanceof ShadowRenderer.Shader)
+ ((ShadowRenderer.Shader) s).setLightMVP(v.mvp);
+
+ // Apply model matrix to VP-Matrix
v.mvp.multiplyLhs(v.viewproj);
if (mTranslucent) {
@@ -359,7 +386,15 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
v.mvp.setAsUniform(s.uMVP);
}
+ public void setShader(Shader shader) {
+ mShader = shader;
+ }
+
public void setZLimit(float zLimit) {
mZLimit = zLimit;
}
+
+ public void useLight(boolean useLight) {
+ mUseLight = useLight;
+ }
}
diff --git a/vtm/src/org/oscim/renderer/OffscreenRenderer.java b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
index 72174d72..3f514004 100644
--- a/vtm/src/org/oscim/renderer/OffscreenRenderer.java
+++ b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
@@ -195,7 +195,7 @@ public class OffscreenRenderer extends LayerRenderer {
GLState.bindFramebuffer(fb);
GLState.viewport(texW, texH);
gl.depthMask(true);
- GLState.setClearColor(mClearColor);
+ GLState.setClearColor(mClearColor); // FIXME SHADOW remove to use default clear color
gl.clear(GL.DEPTH_BUFFER_BIT | GL.COLOR_BUFFER_BIT);
mRenderer.render(viewport);
@@ -223,6 +223,7 @@ public class OffscreenRenderer extends LayerRenderer {
GLState.test(false, false);
GLState.blend(true);
+ // FIXME SHADOW to work with ShadowRenderer: gl.blendFunc(GL.ZERO, GL.SRC_COLOR);
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
GLUtils.checkGlError(getClass().getName() + ": render() end");
}
diff --git a/vtm/src/org/oscim/renderer/light/ShadowFrameBuffer.java b/vtm/src/org/oscim/renderer/light/ShadowFrameBuffer.java
new file mode 100644
index 00000000..17870179
--- /dev/null
+++ b/vtm/src/org/oscim/renderer/light/ShadowFrameBuffer.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2019 Gustl22
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see
+ * See ThinMatrix on Youtube: https://youtu.be/o6zDfDkOFIc
+ */
+public class ShadowFrameBuffer {
+
+ private final int WIDTH;
+ private final int HEIGHT;
+ private int defaultWidth;
+ private int defaultHeight;
+ private int defaultFrameBuffer;
+ private int defaultTexture;
+ private int fbo;
+ private int shadowMap;
+
+ /**
+ * Initialises the frame buffer and shadow map of a certain size.
+ *
+ * @param width - the width of the shadow map in pixels.
+ * @param height - the height of the shadow map in pixels.
+ */
+ protected ShadowFrameBuffer(int width, int height) {
+ this.WIDTH = width;
+ this.HEIGHT = height;
+
+ updateViewportDimensions();
+
+ fbo = createFrameBuffer();
+ shadowMap = createDepthBufferAttachment(width, height);
+ unbindFrameBuffer();
+ }
+
+ /**
+ * Deletes the frame buffer and shadow map texture when the game closes.
+ */
+ protected void cleanUp() {
+ GLUtils.glDeleteFrameBuffers(1, new int[]{fbo});
+ GLUtils.glDeleteTextures(1, new int[]{shadowMap});
+ }
+
+ /**
+ * Unbinds the frame buffer, setting the default frame buffer as the current
+ * render target.
+ */
+ protected void unbindFrameBuffer() {
+ GLState.bindFramebuffer(defaultFrameBuffer);
+ GLState.viewport(defaultWidth, defaultHeight);
+ }
+
+ /**
+ * @return The ID of the shadow map texture.
+ */
+ protected int getShadowMap() {
+ return shadowMap;
+ }
+
+ /**
+ * Binds the frame buffer as the current render target.
+ */
+ public void bindFrameBuffer() {
+ updateViewportDimensions();
+ GLState.bindTex2D(defaultTexture);
+
+ defaultFrameBuffer = GLState.getFramebuffer();
+ GLState.bindFramebuffer(fbo);
+ GLState.viewport(WIDTH, HEIGHT);
+ }
+
+ /**
+ * Creates a frame buffer and binds it so that attachments can be added to
+ * it. The draw buffer is set to none, indicating that there's no colour
+ * buffer to be rendered to.
+ *
+ * @return The newly created frame buffer's ID.
+ */
+ private static int createFrameBuffer() {
+ int frameBuffer = GLUtils.glGenFrameBuffers(1)[0];
+ GLState.bindFramebuffer(frameBuffer);
+ if (GLAdapter.isGL30()) {
+ GLUtils.glDrawBuffers(1, new int[]{GL.NONE});
+ gl30.readBuffer(GL.NONE);
+ }
+ return frameBuffer;
+ }
+
+ /**
+ * Creates a depth buffer texture attachment.
+ *
+ * @param width - the width of the texture.
+ * @param height - the height of the texture.
+ * @return The ID of the depth texture.
+ */
+ private int createDepthBufferAttachment(int width, int height) {
+ defaultTexture = GLState.getTexture();
+ int[] texture = GLUtils.glGenTextures(1);
+ GLState.bindTex2D(texture[0]);
+ if (GLAdapter.isGL30()) {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl30.DEPTH_COMPONENT16,
+ width, height, 0, gl30.DEPTH_COMPONENT,
+ gl.UNSIGNED_SHORT, null);
+ } else {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
+ width, height, 0, gl.RGBA,
+ gl.UNSIGNED_BYTE, null);
+ }
+ // Alternatively set to 32 bit float texture
+// gl.texImage2D(gl.TEXTURE_2D, 0, gl30.DEPTH_COMPONENT32F, width, height, 0,
+// gl.DEPTH_COMPONENT, gl.FLOAT, null);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
+ if (GLAdapter.isGL30()) {
+ gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, texture[0], 0);
+ } else {
+ gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture[0], 0);
+ }
+ return texture[0];
+ }
+
+ private void updateViewportDimensions() {
+ defaultWidth = GLState.getViewportWidth();
+ defaultHeight = GLState.getViewportHeight();
+ }
+}
\ No newline at end of file
diff --git a/vtm/src/org/oscim/renderer/light/ShadowRenderer.java b/vtm/src/org/oscim/renderer/light/ShadowRenderer.java
new file mode 100644
index 00000000..ae2532e0
--- /dev/null
+++ b/vtm/src/org/oscim/renderer/light/ShadowRenderer.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2019 Gustl22
+ * Copyright 2019 schedul-xor
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see