Skip to content

Instantly share code, notes, and snippets.

@praeclarum
Created April 9, 2023 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save praeclarum/467535fcc92c085a8796812e48b5da37 to your computer and use it in GitHub Desktop.
Save praeclarum/467535fcc92c085a8796812e48b5da37 to your computer and use it in GitHub Desktop.
Renders a 3D globe using SHP files
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