From eb3efa7a6928990eee4594376f83a7ed8a8c0d28 Mon Sep 17 00:00:00 2001 From: Gustl22 <user.rebo@gmx.de> Date: Tue, 12 Feb 2019 10:46:17 +0100 Subject: [PATCH] Sun calculator (#655) --- .../tile/buildings/BuildingRenderer.java | 1 + .../org/oscim/renderer/ExtrusionRenderer.java | 31 +- vtm/src/org/oscim/renderer/light/Sun.java | 267 ++++++++++++++++++ 3 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 vtm/src/org/oscim/renderer/light/Sun.java diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java index 71955f24..314a3ef2 100644 --- a/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java +++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingRenderer.java @@ -71,6 +71,7 @@ public class BuildingRenderer extends ExtrusionRenderer { @Override public void update(GLViewport v) { + super.update(v); int diff = (v.pos.zoomLevel - mZoomLimiter.getMinZoom()); diff --git a/vtm/src/org/oscim/renderer/ExtrusionRenderer.java b/vtm/src/org/oscim/renderer/ExtrusionRenderer.java index 4315e275..208acf4f 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.Sun; import org.oscim.utils.FastMath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,11 +46,15 @@ public abstract class ExtrusionRenderer extends LayerRenderer { protected float mAlpha = 1; private float mZLimit = Float.MAX_VALUE; - private float[] mLightPos = new float[]{0.3f, 0.3f, 1f}; + + private Sun mSun; + private boolean mEnableCurrentSunPos; public ExtrusionRenderer(boolean mesh, boolean translucent) { mMesh = mesh; mTranslucent = translucent; + + mSun = new Sun(); } public static class Shader extends GLShader { @@ -115,6 +120,14 @@ public abstract class ExtrusionRenderer extends LayerRenderer { } } + public Sun getSun() { + return mSun; + } + + public void enableCurrentSunPos(boolean enableSunPos) { + mEnableCurrentSunPos = enableSunPos; + } + @Override public boolean setup() { if (!mMesh) @@ -125,6 +138,20 @@ public abstract class ExtrusionRenderer extends LayerRenderer { return true; } + @Override + public void update(GLViewport viewport) { + if (mEnableCurrentSunPos) { + float lat = (float) viewport.pos.getLatitude(); + float lon = (float) viewport.pos.getLongitude(); + if (FastMath.abs(mSun.getLatitude() - lat) > 0.2f + || Math.abs(mSun.getLongitude() - lon) > 0.2f) { + // location is only updated if necessary (not every frame) + mSun.setCoordinates(lat, lon); + } + mSun.update(); + } + } + private void renderCombined(int vertexPointer, ExtrusionBuckets ebs) { for (ExtrusionBucket eb = ebs.buckets(); eb != null; eb = eb.next()) { @@ -171,7 +198,7 @@ public abstract class ExtrusionRenderer extends LayerRenderer { gl.depthFunc(GL.LESS); gl.uniform1f(s.uAlpha, mAlpha); gl.uniform1f(s.uZLimit, mZLimit); - GLUtils.glUniform3fv(s.uLight, 1, mLightPos); + GLUtils.glUniform3fv(s.uLight, 1, mSun.getPosition()); ExtrusionBuckets[] ebs = mExtrusionBucketSet; diff --git a/vtm/src/org/oscim/renderer/light/Sun.java b/vtm/src/org/oscim/renderer/light/Sun.java new file mode 100644 index 00000000..80f86406 --- /dev/null +++ b/vtm/src/org/oscim/renderer/light/Sun.java @@ -0,0 +1,267 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ +package org.oscim.renderer.light; + +import org.oscim.backend.DateTimeAdapter; +import org.oscim.backend.canvas.Color; +import org.oscim.utils.ColorUtil; +import org.oscim.utils.geom.GeometryUtils; +import org.oscim.utils.math.MathUtils; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * See https://lexikon.astronomie.info/zeitgleichung + */ +public class Sun { + + public static float SHADOW_ALPHA = 0.3f; + + private static final DateTimeAdapter date = DateTimeAdapter.instance; + private float mSunrise; // in hours + private float mSunset; // in hours + private float mLatitude; // in degree + private float mLongitude; // in degree + private int mDayOfYear; // up to 366 + private float mProgress; // 0 to 2 + + private float[] mSunPos = new float[3]; + private int mLightColor; + private Map<Float, Integer> mColorMap; + + /** + * Track sun position (accuracy of ~1 minute). + */ + public Sun() { + // Init defaults + mDayOfYear = date.getDayOfYear(); + setCoordinates(0, 0); + mLightColor = Color.get(SHADOW_ALPHA, 255, 255, 255); + setProgress(0.4f); + updatePosition(); + } + + /** + * @return the latitude where the sun is in zenith + */ + private float declination() { + return (float) (0.4095 * Math.sin(0.016906 * (mDayOfYear - 80.086))); + } + + /** + * The discrepancy of mean time and sun time. + * + * @return discrepancy in hours + */ + private float discrepancyMeanTime() { + return (float) (-0.171 * Math.sin(0.0337 * mDayOfYear + 0.465) - 0.1299 * Math.sin(0.01787 * mDayOfYear - 0.168)); + } + + /** + * RGB - the color of sun. + * A - the diffuse of shadow. + */ + public int getColor() { + return mLightColor; + } + + /** + * Get the colors of day cycle. + * + * @return the color map + */ + public Map<Float, Integer> getColorMap() { + return mColorMap; + } + + public float getLatitude() { + return mLatitude; + } + + public float getLongitude() { + return mLongitude; + } + + public float[] getPosition() { + return mSunPos; + } + + public float getProgress() { + return mProgress; + } + + /** + * @return the local sunrise time in hours. + */ + public float getSunrise() { + return mSunrise; + } + + /** + * @return the local sunset time in hours. + */ + public float getSunset() { + return mSunset; + } + + private void initDefaultColorMap() { + mColorMap = new HashMap<>(); + mColorMap.put(0.0f, Color.get((int) (255 * SHADOW_ALPHA), 150, 120, 140)); // Sunrise + mColorMap.put(0.04f, Color.get((int) (255 * SHADOW_ALPHA), 205, 170, 160)); + mColorMap.put(0.1f, Color.get((int) (255 * SHADOW_ALPHA), 245, 240, 215)); + mColorMap.put(0.2f, Color.get((int) (255 * SHADOW_ALPHA), 255, 255, 255)); // Forenoon + mColorMap.put(0.8f, Color.get((int) (255 * SHADOW_ALPHA), 255, 255, 255)); // Afternoon + mColorMap.put(0.99f, Color.get((int) (255 * SHADOW_ALPHA), 255, 220, 230)); + mColorMap.put(1.0f, Color.get((int) (255 * SHADOW_ALPHA), 100, 100, 130)); // Sunset + mColorMap.put(1.9f, Color.get((int) (255 * SHADOW_ALPHA), 100, 100, 130)); // Night + } + + /** + * @param color RGB - the color of sun, A - the diffuse of shadow + */ + public void setColor(int color) { + mLightColor = color; + } + + /** + * Set the colors of day cycle. + */ + public void setColorMap(Map<Float, Integer> colorMap) { + mColorMap = colorMap; + } + + public void setCoordinates(float latitude, float longitude) { + mLatitude = latitude; + mLongitude = longitude; + updateToDay(); + } + + /** + * Customize day of the year + */ + public void setDayOfYear(int day) { + mDayOfYear = day; + } + + public void setPosition(float x, float y, float z) { + mSunPos[0] = x; + mSunPos[1] = y; + mSunPos[2] = z; + mSunPos = GeometryUtils.normalize(mSunPos); + } + + /** + * Customize progress. + */ + public void setProgress(float progress) { + mProgress = progress; + } + + /** + * @param h the offset of horizon (in radians) e.g. bend of earth / atmosphere etc. + * @return the difference of sunrise or sunset to noon + */ + private float timeDiff(float h) { + float lat = mLatitude * MathUtils.degreesToRadians; + float declination = declination(); + return (float) (12 * Math.acos((Math.sin(h) - Math.sin(lat) * Math.sin(declination)) / (Math.cos(lat) * Math.cos(declination))) / Math.PI); + } + + /** + * Update sun progress, position and color to current date time. + */ + public void update() { + updateProgress(); + updatePosition(); + updateColor(); + } + + public int updateColor() { + if (mColorMap == null) + initDefaultColorMap(); + + float progressStart, progressEnd; + Iterator<Float> prIter = mColorMap.keySet().iterator(); + progressStart = progressEnd = prIter.next(); + + while (prIter.hasNext()) { + float progress = prIter.next(); + if (((mProgress + 2f - progress) % 2f) < ((mProgress + 2f - progressStart) % 2f)) + progressStart = progress; + else if (((progress + 2f - mProgress) % 2f) < ((progressEnd + 2f - mProgress) % 2f)) + progressEnd = progress; + } + + float fraction = ((mProgress + 2f - progressStart) % 2f) / ((progressEnd + 2f - progressStart) % 2f); + + int colorStart = mColorMap.get(progressStart); + int colorEnd = mColorMap.get(progressEnd); + mLightColor = ColorUtil.blend(colorStart, colorEnd, fraction); + return mLightColor; + } + + /** + * Very simple normalized sun coordinates. + */ + public float[] updatePosition() { + mSunPos[0] = (float) Math.cos(mProgress * Math.PI); + mSunPos[1] = (float) Math.sin(mProgress * Math.PI); + mSunPos[2] = 3 * mSunPos[1]; + mSunPos = GeometryUtils.normalize(mSunPos); + return mSunPos; + } + + /** + * The progress + * of the daylight in range 0 (sunrise) to 1 (sunset) and + * of the night in range 1 (sunset) to 2 (sunrise). + * + * @return the progress in range 0 to 2 + */ + public float updateProgress() { + // TODO Java8 replace with LocalDateTime + // LocalDateTime date = new LocalDateTime(); + // float time = date.getHour(); + // time += date.getMinute() / 60f; + // time += date.getSecond() / 3600f; + + float time = date.getHour(); + time += date.getMinute() / 60f; + time += date.getSecond() / 3600f; + + float progress = (time - mSunrise) / (mSunset - mSunrise); + if (progress > 1f || progress < 0f) { + progress = ((time + 24 - mSunset) % 24) / (mSunrise + 24 - mSunset); + progress += 1; + } + mProgress = MathUtils.clamp(progress, 0f, 2f); + return mProgress; + } + + /** + * Calculate the sunrise and sunset of set day (local time). + */ + public void updateToDay() { + float h = -0.0145f; // -50 latitude minutes + + float diff = timeDiff(h); + float discp = discrepancyMeanTime(); + float calc = 12 - discp - (mLongitude / 15f) + (date.getTimeZoneOffset() / (60f * 60f * 1000f)); + mSunrise = calc - diff; + mSunset = calc + diff; + } +}