Created
April 9, 2023 16:02
-
-
Save praeclarum/467535fcc92c085a8796812e48b5da37 to your computer and use it in GitHub Desktop.
Renders a 3D globe using SHP files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Globe : Scene | |
{ | |
const double PI = Math.PI; | |
const double DegToRad = PI / 180.0; | |
public const double EarthRadius = 2.0; | |
const double EarthFlattening = 1.0/298.257223563; | |
const double EarthFF = (1.0 - EarthFlattening) * (1.0 - EarthFlattening); | |
public const double CameraDistance = 50.0; | |
Node carNode = new Node(new Geometry("../models/volkswagen_golf.obj") { | |
Material = new PhongMaterial(), | |
}); | |
Node latLngNode; | |
Node coastlineNode; | |
public Globe() | |
{ | |
CameraLookAt = new Vector3(0, 0, 0); | |
CameraPosition = new Vector3(0, (float)CameraDistance, 0); | |
CameraUp = Vector3.UnitZ; | |
CameraFov = 4.5f; | |
latLngNode = new Node(MakeLatLngLines()); | |
coastlineNode = new Node(LoadShp("../data/coastlines.shp")); | |
} | |
protected override void Update(double time) | |
{ | |
base.Update(time); | |
latLngNode.Transform = Matrix4.CreateRotationZ((float)(time * 0.1)); | |
coastlineNode.Transform = latLngNode.Transform; | |
} | |
protected override void Draw(double time, ref Matrix4 view, ref Matrix4 projection) | |
{ | |
// carNode.Draw(time, ref view, ref projection); | |
latLngNode.Draw(time, ref view, ref projection); | |
coastlineNode.Draw(time, ref view, ref projection); | |
} | |
class GeometryBuilder | |
{ | |
List<Vector3> verts = new List<Vector3>(); | |
List<Vector3> norms = new List<Vector3>(); | |
List<Vector3> colors = new List<Vector3>(); | |
public float Alpha { get; set; } = 1.0f; | |
public Geometry ToGeometry() { | |
var g = new Geometry(verts, norms, colors); | |
g.PrimitiveType = All.Lines; | |
g.Material = new GlobeMaterial(alpha: Alpha); | |
return g; | |
} | |
public void AddLine(double lat, double lng, double lat2, double lng2, Vector3 color) | |
{ | |
var p1 = LatLngToPoint(lat, lng); | |
var p2 = LatLngToPoint(lat2, lng2); | |
verts.Add(p1); | |
verts.Add(p2); | |
norms.Add(Vector3.UnitZ); | |
norms.Add(Vector3.UnitZ); | |
colors.Add(color); | |
colors.Add(color); | |
} | |
public static Vector3 LatLngToPoint (double latitude, double longitude, double aboveSea = 0.0) | |
{ | |
var latr = latitude * DegToRad; | |
var lngr = longitude * DegToRad; | |
var clat = Math.Cos (latr); | |
var slat = Math.Sin (latr); | |
var C = 1.0 / (Math.Sqrt (clat*clat + EarthFF * slat*slat)); | |
var S = C * EarthFF; | |
return new Vector3( | |
(float)((EarthRadius * C + aboveSea)*clat * (Math.Cos (lngr))), | |
(float)((EarthRadius * C + aboveSea)*clat * (Math.Sin (lngr))), | |
(float)((EarthRadius * S + aboveSea)*slat)); | |
} | |
} | |
Geometry MakeLatLngLines() | |
{ | |
var gb = new GeometryBuilder(); | |
var dlng = 15.0; | |
var dlat = 15.0; | |
// Lines of latitude | |
for (double lng = -180.0; lng < 180.0; lng += dlng/100.0) | |
{ | |
for (double lat = -90.0; lat <= 90.0; lat += dlat) | |
{ | |
gb.AddLine(lat, lng, lat, lng + dlng/100.0, new Vector3(0.0f, 1.0f, 0.0f)); | |
} | |
} | |
// Lines of longitude | |
for (double lat = -90.0; lat <= 90.0; lat += dlat/100.0) | |
{ | |
for (double lng = -180.0; lng < 180.0; lng += dlng) | |
{ | |
gb.AddLine(lat, lng, lat + dlat/100.0, lng, new Vector3(1.0f, 0.0f, 0.0f)); | |
} | |
} | |
gb.Alpha = 0.4f; | |
return gb.ToGeometry(); | |
} | |
Geometry LoadShp(string path) { | |
using var f = File.OpenRead(path); | |
var fileLength = f.Length; | |
using var reader = new BinaryReader(f); | |
var gb = new GeometryBuilder(); | |
// Skip the 0x64-length header | |
var header = reader.ReadBytes(0x64); | |
var numReadBytes = 0x64; | |
// Read all of the records | |
while (numReadBytes < fileLength) { | |
var recordNumber = reader.ReadInt32(); | |
var recordLength = reader.ReadInt32(); | |
var shapeType = reader.ReadInt32(); | |
var bbox0 = reader.ReadDouble(); | |
var bbox1 = reader.ReadDouble(); | |
var bbox2 = reader.ReadDouble(); | |
var bbox3 = reader.ReadDouble(); | |
var numParts = reader.ReadInt32(); | |
var numPoints = reader.ReadInt32(); | |
numReadBytes += 5*4 + 4*8; | |
// Skip the parts | |
var parts = new List<int> (); | |
for (int i = 0; i < numParts; i++) { | |
parts.Add(reader.ReadInt32()); | |
numReadBytes += 4; | |
} | |
// Read the points | |
var lastLng = 0.0; | |
var lastLat = 0.0; | |
for (int i = 0; i < numPoints; i++) { | |
var lng = reader.ReadDouble(); | |
var lat = reader.ReadDouble(); | |
numReadBytes += 8*2; | |
if (i > 0) { | |
gb.AddLine(lastLat, lastLng, lat, lng, new Vector3(1.0f, 0.0f, 0.0f)); | |
} | |
lastLng = lng; | |
lastLat = lat; | |
} | |
} | |
return gb.ToGeometry(); | |
} | |
} | |
class GlobeMaterial : Material | |
{ | |
public float Alpha { get; private set; } | |
public GlobeMaterial(float alpha = 1.0f) { | |
Alpha = alpha; | |
} | |
protected override string GetFragmentShaderSource() | |
{ | |
var prec = isES ? "precision mediump float;\n" : "\n"; | |
// var alpha = Alpha.ToString("0.0"); | |
return prec + | |
"varying vec3 vColor; \n" + | |
"varying vec3 vLocalPosition;\n" + | |
"varying vec3 vViewPosition;\n" + | |
"varying vec3 vViewNormal;\n" + | |
"varying vec3 vViewLight0Position;\n" + | |
"uniform float uTime;\n" + | |
"uniform float uObjectRadius;\n" + | |
"const float ambient = 0.2;\n" + | |
$"const float maxAlpha = {Alpha:0.0};\n" + | |
$"const float earthRadius = {Globe.EarthRadius:0.0};\n" + | |
$"const float cameraDistance = {Globe.CameraDistance:0.0};\n" + | |
"void main() {\n" + | |
" vec3 lightDir = normalize(vViewLight0Position - vViewPosition);\n" + | |
" vec3 reflectDir = reflect(-lightDir, vViewNormal);\n" + | |
" vec3 viewDir = normalize(-vViewPosition);\n" + | |
" float alpha = 1.0-(-vViewPosition.z - cameraDistance + earthRadius)/(earthRadius*2.0);\n" + | |
" alpha = maxAlpha * (0.75*alpha + 0.25);\n" + | |
" float diff = 1.0;//max(dot(lightDir, vViewNormal), 0.0);\n" + | |
" vec3 diffuse = diff * 1.1*vec3(1.0,1.0,1.0) * vec3(0.0,1.0,0.0);// vColor.rgb;\n" + | |
" vec3 color = ambient * vColor.rgb + diffuse;\n" + | |
" gl_FragColor = vec4(color*alpha, alpha);\n" + | |
"}\n"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment