contrib/model_viewer: move the globe example as a custom model
authorAlexis Laferrière <alexis.laf@xymus.net>
Sat, 16 Jan 2016 17:16:50 +0000 (12:16 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Tue, 19 Jan 2016 20:03:51 +0000 (15:03 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

20 files changed:
contrib/model_viewer/assets/globe/clouds.png [new file with mode: 0644]
contrib/model_viewer/assets/globe/earth.jpg [new file with mode: 0644]
contrib/model_viewer/assets/globe/elevation.jpg [new file with mode: 0644]
contrib/model_viewer/assets/globe/lights.jpg [new file with mode: 0644]
contrib/model_viewer/assets/globe/seas.jpg [new file with mode: 0644]
contrib/model_viewer/src/globe.nit [new file with mode: 0644]
contrib/model_viewer/src/model_viewer.nit
lib/gamnit/examples/globe/Makefile [deleted file]
lib/gamnit/examples/globe/README [deleted file]
lib/gamnit/examples/globe/art/icon.png [deleted file]
lib/gamnit/examples/globe/art/icon.xcf [deleted file]
lib/gamnit/examples/globe/assets/hd/clouds.png [deleted file]
lib/gamnit/examples/globe/assets/hd/earth.jpg [deleted file]
lib/gamnit/examples/globe/assets/hd/elevation.jpg [deleted file]
lib/gamnit/examples/globe/assets/hd/lights.jpg [deleted file]
lib/gamnit/examples/globe/assets/hd/seas.jpg [deleted file]
lib/gamnit/examples/globe/bin/.gitignore [deleted file]
lib/gamnit/examples/globe/org.nitlanguage.globe.txt [deleted file]
lib/gamnit/examples/globe/package.ini [deleted file]
lib/gamnit/examples/globe/src/globe.nit [deleted file]

diff --git a/contrib/model_viewer/assets/globe/clouds.png b/contrib/model_viewer/assets/globe/clouds.png
new file mode 100644 (file)
index 0000000..6a34285
Binary files /dev/null and b/contrib/model_viewer/assets/globe/clouds.png differ
diff --git a/contrib/model_viewer/assets/globe/earth.jpg b/contrib/model_viewer/assets/globe/earth.jpg
new file mode 100644 (file)
index 0000000..53388c8
Binary files /dev/null and b/contrib/model_viewer/assets/globe/earth.jpg differ
diff --git a/contrib/model_viewer/assets/globe/elevation.jpg b/contrib/model_viewer/assets/globe/elevation.jpg
new file mode 100644 (file)
index 0000000..48737a0
Binary files /dev/null and b/contrib/model_viewer/assets/globe/elevation.jpg differ
diff --git a/contrib/model_viewer/assets/globe/lights.jpg b/contrib/model_viewer/assets/globe/lights.jpg
new file mode 100644 (file)
index 0000000..48dff8d
Binary files /dev/null and b/contrib/model_viewer/assets/globe/lights.jpg differ
diff --git a/contrib/model_viewer/assets/globe/seas.jpg b/contrib/model_viewer/assets/globe/seas.jpg
new file mode 100644 (file)
index 0000000..a078e66
Binary files /dev/null and b/contrib/model_viewer/assets/globe/seas.jpg differ
diff --git a/contrib/model_viewer/src/globe.nit b/contrib/model_viewer/src/globe.nit
new file mode 100644 (file)
index 0000000..9da1c59
--- /dev/null
@@ -0,0 +1,351 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Intro a custom model, material and graphics program to draw a globe
+module globe
+
+intrude import gamnit::depth # To access `Mesh::indices_c`
+
+# Number of vertices to create parallels, meridians are double of this
+#
+# The minimum should be 3 for an octahedron planet.
+fun n_parallels: Int do return 25
+
+redef class App
+
+       # Program for the graphic card
+       private var globe_program = new GlobeProgram
+
+       # Texture of the reflexive surface of the earth (with the seas in white)
+       private var texture_seas = new Texture("globe/seas.jpg")
+
+       # Texture of the surface of the earth
+       private var texture_earth = new Texture("globe/earth.jpg")
+
+       # Texture of the lights at night on earth
+       private var texture_night = new Texture("globe/lights.jpg")
+
+       # Elevation map of earth
+       private var texture_elevation = new Texture("globe/elevation.jpg")
+
+       # Texture of the clouds above the earth
+       private var texture_clouds = new Texture("globe/clouds.png")
+
+       redef fun on_create
+       do
+               super
+
+               # Compile globe_program
+               var program = app.globe_program
+               program.compile_and_link
+
+               # Catch any errors
+               var gamnit_error = program.error
+               assert gamnit_error == null else print_error gamnit_error
+       end
+end
+
+# Full model of a globe, with a surface, clouds layer and atmosphere
+class GlobeModel
+       super CompositeModel
+
+       redef fun load
+       do
+               leaves.add new LeafModel(
+                       new Mesh.uv_sphere(1.0, 2*n_parallels, n_parallels),
+                       new GlobeMaterial.surface)
+               leaves.add new LeafModel(
+                       new Mesh.uv_sphere(1.1, 2*n_parallels, n_parallels),
+                       new GlobeMaterial.clouds)
+               leaves.add new LeafModel(
+                       new Mesh.uv_sphere(1.2, 2*n_parallels, n_parallels),
+                       new GlobeMaterial.atmo)
+       end
+end
+
+# Parameterizable material to draw the 3 layers of the globe
+class GlobeMaterial
+       super Material
+
+       # Id of the texture for diffuse colors, if any
+       var texture_id: nullable Int
+
+       # Draw as a surface, using the elevation map and the night lights
+       var is_surface: Bool
+
+       # Ambient color
+       var color: Array[Float]
+
+       # Create and configure a material for the earth surface
+       init surface do init(0, true, [1.0, 1.0, 1.0, 1.0])
+
+       # Create and configure a material for the cloud layer
+       init clouds do init(4, false, [1.0, 1.0, 1.0, 0.25])
+
+       # Create and configure a material for the visible atmosphere
+       init atmo do init(null, false, [0.0, 0.8, 1.0, 0.05])
+
+       redef fun draw(actor, model)
+       do
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print gl_error
+
+               var mesh = model.mesh
+
+               var program = app.globe_program
+               program.use
+
+               # Set constant program values
+               program.use
+
+               # Bind textures
+               glActiveTexture gl_TEXTURE0
+               glBindTexture(gl_TEXTURE_2D, app.texture_earth.gl_texture)
+
+               glActiveTexture gl_TEXTURE1
+               glBindTexture(gl_TEXTURE_2D, app.texture_seas.gl_texture)
+
+               glActiveTexture gl_TEXTURE2
+               glBindTexture(gl_TEXTURE_2D, app.texture_night.gl_texture)
+
+               glActiveTexture gl_TEXTURE3
+               glBindTexture(gl_TEXTURE_2D, app.texture_elevation.gl_texture)
+
+               glActiveTexture gl_TEXTURE4
+               glBindTexture(gl_TEXTURE_2D, app.texture_clouds.gl_texture)
+
+               # Set samplers
+               program.tex_specular.uniform 1
+               program.tex_night.uniform 2
+               program.tex_displace.uniform 3
+
+               # Update camera view and light
+               var p = app.world_camera.position
+               program.camera.uniform(p.x, p.y, p.z)
+               program.mvp.uniform app.world_camera.mvp_matrix
+               program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
+
+               # Set attributes
+               program.coord.array_enabled = true
+               program.coord.array(mesh.vertices, 3)
+
+               program.tex_coord.array_enabled = true
+               program.tex_coord.array(mesh.texture_coords, 2)
+
+               program.normal.array_enabled = true
+               program.normal.array(mesh.normals, 3)
+
+               # Set uniforms
+               program.scale.uniform 1.0
+               program.rotation.uniform new Matrix.rotation(actor.rotation, 0.0, 1.0, 0.0)
+               program.translation.uniform(actor.center.x, -actor.center.y, actor.center.z, 0.0)
+               program.color.uniform(color[0], color[1], color[2], color[3])
+               program.is_surface.uniform is_surface
+
+               # Set the color texture?
+               var tex = texture_id
+               if tex == null then
+                       program.use_texture.uniform false
+               else
+                       program.use_texture.uniform true
+                       program.tex.uniform tex
+               end
+
+               # Execute draw, support only meshes with `indices`
+               glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+
+               # Catch any errors
+               gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print gl_error
+       end
+end
+
+# Graphical program to draw a planet with Phong lighting
+class GlobeProgram
+       super GamnitProgramFromSource
+
+       redef var vertex_shader_source = """
+               // Vertex coordinates
+               attribute vec4 coord;
+
+               // Vertex color tint
+               uniform vec4 color;
+
+               // Vertex translation
+               uniform vec4 translation;
+
+               // Vertex scaling
+               uniform float scale;
+
+               // Vertex coordinates on textures
+               attribute vec2 tex_coord;
+
+               // Vertex normal
+               attribute vec3 normal;
+
+               // Model view projection matrix
+               uniform mat4 mvp;
+
+               // Model rotation
+               uniform mat4 rotation;
+
+               // Lights config
+               uniform vec3 light_center;
+
+               // Texture of surface elevation to displace vertices
+               uniform sampler2D tex_displace;
+
+               // Draw this as a planet surface?
+               uniform bool is_surface;
+
+               // Output for the fragment shader
+               varying vec4 v_color;
+               varying vec2 v_tex_coord;
+               varying vec3 v_normal;
+               varying vec4 v_light_center;
+
+               void main()
+               {
+                       v_color = color;
+                       v_tex_coord = tex_coord;
+                       v_normal = normalize(vec4(normal, 0.0) * rotation * mvp).xyz;
+                       v_light_center = vec4(light_center, 0.0) * mvp;
+
+                       // Apply displacement map
+                       float s = scale;
+                       if (is_surface)
+                               s += 0.05 * texture2D(tex_displace, tex_coord).r;
+
+                       gl_Position = (vec4(coord.xyz * s, 1.0) * rotation + translation) * mvp;
+
+               }
+               """ @ glsl_vertex_shader
+
+       redef var fragment_shader_source = """
+               precision mediump float;
+
+               // Input from the vertex shader
+               varying vec4 v_color;
+               varying vec2 v_tex_coord;
+               varying vec3 v_normal;
+               varying vec4 v_light_center;
+
+               // Coordinates of the camera
+               uniform vec3 camera;
+
+               // Does this object use a texture?
+               uniform bool use_texture;
+
+               // Texture to apply on this object
+               uniform sampler2D tex;
+
+               // Reflection map to apply to the Phong logic
+               uniform sampler2D tex_specular;
+
+               // Texture for the dark side of the earth
+               uniform sampler2D tex_night;
+
+               // Draw this as a planet surface?
+               uniform bool is_surface;
+
+               // Colors config
+               vec4 ambient_color = vec4(0.2, 0.2, 0.2, 1.0);
+               vec4 diffuse_color = vec4(1.0, 1.0, 1.0, 1.0);
+               vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
+
+               void main()
+               {
+                       // Lambert diffusion
+                       vec3 to_light = normalize(v_light_center.xyz);
+                       float lambert = max(dot(to_light, v_normal), 0.0);
+
+                       // Phong specular
+                       float specular = 0.0;
+                       if (lambert > 0.0) {
+                               vec3 to_camera = normalize(camera);
+                               vec3 normal = normalize(v_normal);
+                               vec3 light_reflect = reflect(to_light, normal);
+                               float spec_angle = max(dot(light_reflect, to_camera), 0.0);
+                               specular = pow(spec_angle, 16.0);
+
+                               if (is_surface)
+                                       specular *= texture2D(tex_specular, v_tex_coord).x;
+                               else specular *= 0.2;
+                       }
+
+                       if(use_texture) {
+                               gl_FragColor = v_color * texture2D(tex, v_tex_coord);
+                       } else {
+                               gl_FragColor = v_color;
+                       }
+
+                       gl_FragColor *= ambient_color + lambert * diffuse_color;
+                       gl_FragColor += specular * specular_color;
+
+                       if (is_surface && lambert < 0.2) {
+                               // Show city lights at night
+                               float p_night = (0.2 - lambert) * 5.0;
+                               gl_FragColor += p_night*texture2D(tex_night, v_tex_coord);
+                       }
+               }
+               """ @ glsl_fragment_shader
+
+       # Vertices coordinates
+       var coord = attributes["coord"].as(AttributeVec4) is lazy
+
+       # Color tint per vertex
+       var color = uniforms["color"].as(UniformVec4) is lazy
+
+       # Scaling per vertex
+       var scale = uniforms["scale"].as(UniformFloat) is lazy
+
+       # Coordinates on the textures, per vertex
+       var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
+
+       # Normal per vertex
+       var normal = attributes["normal"].as(AttributeVec3) is lazy
+
+       # Model view projection matrix
+       var mvp = uniforms["mvp"].as(UniformMat4) is lazy
+
+       # Should this program use the texture `tex`?
+       var use_texture = uniforms["use_texture"].as(UniformBool) is lazy
+
+       # Main visible texture unit
+       var tex = uniforms["tex"].as(UniformSampler2D) is lazy
+
+       # Texture unit for reflection effect
+       var tex_specular = uniforms["tex_specular"].as(UniformSampler2D) is lazy
+
+       # Texture of the earth at night
+       var tex_night = uniforms["tex_night"].as(UniformSampler2D) is lazy
+
+       # Texture with elevation data
+       var tex_displace = uniforms["tex_displace"].as(UniformSampler2D) is lazy
+
+       # Position of the camera
+       var camera = uniforms["camera"].as(UniformVec3) is lazy
+
+       # Execute this program to draw a planet surface?
+       var is_surface = uniforms["is_surface"].as(UniformBool) is lazy
+
+       # Translation applied to each vertex
+       var translation = uniforms["translation"].as(UniformVec4) is lazy
+
+       # Rotation matrix
+       var rotation = uniforms["rotation"].as(UniformMat4) is lazy
+
+       # Center position of the light
+       var light_center = uniforms["light_center"].as(UniformVec3) is lazy
+end
index dd1fa98..f147823 100644 (file)
@@ -24,6 +24,8 @@ end
 
 import gamnit::depth
 
+import globe
+
 redef class App
 
        # All available models
@@ -33,7 +35,8 @@ redef class App
                new LeafModel(new Mesh.uv_sphere(4.0, 32, 16), new NormalsMaterial),
                new Model("models/Tree_01.obj"),
                new Model("models/Oak_Fall_01.obj"),
-               new Model("models/Quandtum_BA-2_v1_1.obj")]
+               new Model("models/Quandtum_BA-2_v1_1.obj"),
+               new GlobeModel]
 
        # Index of the current model in `models`
        var model_index = 0
@@ -92,7 +95,7 @@ redef class App
                actor.center.z -= model.mesh.center.z
 
                var height = model.mesh.dimensions.y
-               world_camera.reset_height(height * 4.0)
+               world_camera.reset_height(height * 3.0)
 
                actors.clear
                actors.add actor
diff --git a/lib/gamnit/examples/globe/Makefile b/lib/gamnit/examples/globe/Makefile
deleted file mode 100644 (file)
index d18b689..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-NITC=../../../../bin/nitc
-NITLS=../../../../bin/nitls
-
-all: bin/globe
-
-bin/globe: $(shell ${NITLS} -M src/globe.nit linux) ${NITC}
-       ${NITC} src/globe.nit -m linux -o $@ -D n_parallels=100
-
-check: bin/globe
-       bin/globe
-
-# ---
-# Android
-
-android: bin/globe.apk
-bin/globe.apk: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
-       ${NITC} src/globe.nit -m android -o $@
-
-android-release: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
-       ${NITC} src/globe.nit -m android -o bin/globe.apk --release
-
-android-wear: $(shell ${NITLS} -M src/globe.nit android) ${NITC} res/drawable-hdpi/icon.png assets/ld/earth.jpg
-       ${NITC} src/globe.nit -m android -o bin/planet_wear.apk -D n_parallels=10
-
-res/drawable-hdpi/icon.png: art/icon.png
-       mkdir -p res/drawable-ldpi/ res/drawable-mdpi/ res/drawable-hdpi/ \
-               res/drawable-xhdpi/ res/drawable-xxhdpi/ res/drawable-xxxhdpi/
-       convert -resize 36x36   art/icon.png res/drawable-ldpi/icon.png
-       convert -resize 48x48   art/icon.png res/drawable-mdpi/icon.png
-       convert -resize 72x72   art/icon.png res/drawable-hdpi/icon.png
-       convert -resize 96x96   art/icon.png res/drawable-xhdpi/icon.png
-       convert -resize 144x144 art/icon.png res/drawable-xxhdpi/icon.png
-       convert -resize 192x192 art/icon.png res/drawable-xxxhdpi/icon.png
-
-assets/ld/earth.jpg:
-       mkdir -p assets/ld
-       convert -resize 2048x1024 assets/hd/earth.jpg assets/ld/earth.jpg
-       convert -resize 2048x1024 assets/hd/seas.jpg assets/ld/seas.jpg
-       convert -resize 2048x1024 assets/hd/clouds.png assets/ld/clouds.png
-       convert -resize 2048x1024 assets/hd/elevation.jpg assets/ld/elevation.jpg
-       convert -resize 2048x1024 assets/hd/lights.jpg assets/ld/lights.jpg
diff --git a/lib/gamnit/examples/globe/README b/lib/gamnit/examples/globe/README
deleted file mode 100644 (file)
index 0234f7f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Sample gamnit app rendering the earth with elevated surface, cloud layer, city lights and Phong lighting.
-
-# Possible variations
-
-It would have been possible to achieve the same result with different approaches.
-They should be explored as they could provide better performances or be easier to understand.
-
-* Use a single set of vertices, normals and indices because we only need spheres.
-* Alternatively, use one program per sphere.
-
-# Art
-
-Images credit: NASA, Visible Earth
diff --git a/lib/gamnit/examples/globe/art/icon.png b/lib/gamnit/examples/globe/art/icon.png
deleted file mode 100644 (file)
index 71612a4..0000000
Binary files a/lib/gamnit/examples/globe/art/icon.png and /dev/null differ
diff --git a/lib/gamnit/examples/globe/art/icon.xcf b/lib/gamnit/examples/globe/art/icon.xcf
deleted file mode 100644 (file)
index efcf652..0000000
Binary files a/lib/gamnit/examples/globe/art/icon.xcf and /dev/null differ
diff --git a/lib/gamnit/examples/globe/assets/hd/clouds.png b/lib/gamnit/examples/globe/assets/hd/clouds.png
deleted file mode 100644 (file)
index c4319d7..0000000
Binary files a/lib/gamnit/examples/globe/assets/hd/clouds.png and /dev/null differ
diff --git a/lib/gamnit/examples/globe/assets/hd/earth.jpg b/lib/gamnit/examples/globe/assets/hd/earth.jpg
deleted file mode 100644 (file)
index 79f6f2c..0000000
Binary files a/lib/gamnit/examples/globe/assets/hd/earth.jpg and /dev/null differ
diff --git a/lib/gamnit/examples/globe/assets/hd/elevation.jpg b/lib/gamnit/examples/globe/assets/hd/elevation.jpg
deleted file mode 100644 (file)
index 3ec70c0..0000000
Binary files a/lib/gamnit/examples/globe/assets/hd/elevation.jpg and /dev/null differ
diff --git a/lib/gamnit/examples/globe/assets/hd/lights.jpg b/lib/gamnit/examples/globe/assets/hd/lights.jpg
deleted file mode 100644 (file)
index 240df35..0000000
Binary files a/lib/gamnit/examples/globe/assets/hd/lights.jpg and /dev/null differ
diff --git a/lib/gamnit/examples/globe/assets/hd/seas.jpg b/lib/gamnit/examples/globe/assets/hd/seas.jpg
deleted file mode 100644 (file)
index 52ad2ac..0000000
Binary files a/lib/gamnit/examples/globe/assets/hd/seas.jpg and /dev/null differ
diff --git a/lib/gamnit/examples/globe/bin/.gitignore b/lib/gamnit/examples/globe/bin/.gitignore
deleted file mode 100644 (file)
index 72e8ffc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*
diff --git a/lib/gamnit/examples/globe/org.nitlanguage.globe.txt b/lib/gamnit/examples/globe/org.nitlanguage.globe.txt
deleted file mode 100644 (file)
index 845e177..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Categories:Nit,Multimedia
-License:Apache2
-Web Site:http://nitlanguage.org
-Source Code:http://nitlanguage.org/nit.git/tree/HEAD:/lib/gamnit/examples/globe
-Issue Tracker:https://github.com/nitlang/nit/issues
-
-Summary:Simple rotating globe of the earth
-Description:
-Non-interactive sample gamnit app rendering the earth with elevated surface, cloud layer, city lights and Phong lighting.
-.
diff --git a/lib/gamnit/examples/globe/package.ini b/lib/gamnit/examples/globe/package.ini
deleted file mode 100644 (file)
index b4e8861..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name=globe
-tags=example
-maintainer=Alexis Laferrière <alexis.laf@xymus.net>
-license=Apache-2.0
-[upstream]
-browse=https://github.com/nitlang/nit/tree/master/lib/gamnit/examples/globe/
-git=https://github.com/nitlang/nit.git
-git.directory=lib/gamnit/examples/globe/
-homepage=http://nitlanguage.org
-issues=https://github.com/nitlang/nit/issues
-apk=http://nitlanguage.org/fdroid/apk/globe.apk
diff --git a/lib/gamnit/examples/globe/src/globe.nit b/lib/gamnit/examples/globe/src/globe.nit
deleted file mode 100644 (file)
index 570cb5d..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Portable app using gamnit textures and programs with custom calls to OpenGL ES 2.0
-module globe is
-       app_name "gamnit Globe"
-       app_namespace "org.nitlanguage.globe"
-       app_version(1, 0, git_revision)
-
-       android_manifest_activity """android:screenOrientation="portrait""""
-       android_api_target 15
-end
-
-import realtime
-import matrix::projection
-
-import gamnit
-import gamnit::cameras
-import gamnit::limit_fps
-
-private import more_collections
-
-# Graphical program to draw a planet with Phong lighting
-class GlobeProgram
-       super GamnitProgramFromSource
-
-       redef var vertex_shader_source = """
-               // Vertex coordinates
-               attribute vec4 coord;
-
-               // Vertex color tint
-               attribute vec4 color;
-
-               // Vertex translation
-               attribute vec4 translation;
-
-               // Vertex scaling
-               attribute float scale;
-
-               // Vertex coordinates on textures
-               attribute vec2 tex_coord;
-
-               // Vertex normal
-               attribute vec3 normal;
-
-               // Model view projection matrix
-               uniform mat4 mvp;
-
-               // Texture of surface elevation to displace vertices
-               uniform sampler2D tex_displace;
-
-               // Draw this as a planet surface?
-               uniform bool is_surface;
-
-               // Output for the fragment shader
-               varying vec4 v_color;
-               varying vec2 v_tex_coord;
-               varying vec3 v_normal;
-
-               void main()
-               {
-                       // Pass varyings to the fragment shader
-                       v_color = color;
-                       v_tex_coord = tex_coord;
-                       v_normal = normal;
-
-                       // Apply bump map
-                       float s = scale;
-                       if (is_surface)
-                               s += 0.05 * texture2D(tex_displace, tex_coord).r;
-
-                       gl_Position = (vec4(coord.xyz * s, 1.0) + translation) * mvp;
-               }
-               """ @ glsl_vertex_shader
-
-       redef var fragment_shader_source = """
-               precision mediump float;
-
-               // Input from the vertex shader
-               varying vec4 v_color;
-               varying vec2 v_tex_coord;
-               varying vec3 v_normal;
-
-               // Coordinates of the camera
-               uniform vec3 camera;
-
-               // Does this object use a texture?
-               uniform bool use_texture;
-
-               // Texture to apply on this object
-               uniform sampler2D tex;
-
-               // Reflection map to apply the the phong logic
-               uniform sampler2D tex_shiny;
-
-               // Texture for the dark side of the earth
-               uniform sampler2D tex_night;
-
-               // Draw this as a planet surface?
-               uniform bool is_surface;
-
-               // Lights config
-               // TODO configure from outside the shader
-               vec3 light_dir = normalize(vec3(-1.0, 0.0, -1.0));
-               vec4 ambient_color = vec4(0.2, 0.2, 0.2, 1.0);
-               vec4 diffuse_color = vec4(1.0, 1.0, 1.0, 1.0);
-               vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
-
-               void main()
-               {
-                       if(use_texture) {
-                               gl_FragColor = v_color * texture2D(tex, v_tex_coord);
-                       } else {
-                               gl_FragColor = v_color;
-                       }
-
-                       // Lambert diffusion
-                       float lambert = max(dot(light_dir, v_normal), 0.0);
-
-                       // Phong specular
-                       float specular = 0.0;
-                       if (lambert > 0.0) {
-                               vec3 to_camera = normalize(camera);
-                               vec3 light_reflect = reflect(light_dir, v_normal);
-                               float specularAngle = max(dot(light_reflect, to_camera), 0.0);
-                               specular = pow(specularAngle, 16.0);
-
-                               if (is_surface)
-                                       specular *= texture2D(tex_shiny, v_tex_coord).x;
-                               else specular *= 0.2;
-                       }
-
-                       gl_FragColor *= ambient_color + lambert * diffuse_color;
-                       gl_FragColor += specular * specular_color;
-
-                       if (is_surface && lambert < 0.2) {
-                               // Show city lights at night
-                               float p_night = (0.2 - lambert) * 5.0;
-                               gl_FragColor += p_night*texture2D(tex_night, v_tex_coord);
-                       }
-               }
-               """ @ glsl_fragment_shader
-
-               # Vertices coordinates
-               var coord = attributes["coord"].as(AttributeVec4) is lazy
-
-               # Color tint per vertex
-               var color = attributes["color"].as(AttributeVec4) is lazy
-
-               # Scaling per vertex
-               var scale = attributes["scale"].as(AttributeFloat) is lazy
-
-               # Coordinates on the textures, per vertex
-               var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
-
-               # Normal per vertex
-               var normal = attributes["normal"].as(AttributeVec3) is lazy
-
-               # Model view projection matrix
-               var mvp = uniforms["mvp"].as(UniformMat4) is lazy
-
-               # Should this program use the texture `tex`?
-               var use_texture = uniforms["use_texture"].as(UniformBool) is lazy
-
-               # Main visible texture unit
-               var tex = uniforms["tex"].as(UniformSampler2D) is lazy
-
-               # Texture unit for reflection effect
-               var tex_shiny = uniforms["tex_shiny"].as(UniformSampler2D) is lazy
-
-               # Texture of the earth at night
-               var tex_night = uniforms["tex_night"].as(UniformSampler2D) is lazy
-
-               # Texture with elevation data
-               var tex_displace = uniforms["tex_displace"].as(UniformSampler2D) is lazy
-
-               # Position of the camera
-               var camera = uniforms["camera"].as(UniformVec3) is lazy
-
-               # Execute this program to draw a planet surface?
-               var is_surface = uniforms["is_surface"].as(UniformBool) is lazy
-end
-
-# Mesh with all information for drawing
-class Mesh
-
-       # Vertices coordinates
-       var vertices: Array[Float]
-
-       # Indices to draw triangles
-       var indices: Array[Int]
-
-       private var indices_c = new CUInt16Array.from(indices) is lazy
-
-       # Normals on each vertex
-       var normals: Array[Float]
-
-       # Coordinates on the texture per vertex
-       var texture_coords: Array[Float]
-
-       # Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
-       init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
-       do
-               var w = n_meridians
-               var h = n_parallels
-
-               var vertices = new Array[Float].with_capacity(w*h*3)
-               var texture_coords = new Array[Float].with_capacity(w*h*2)
-               var normals = new Array[Float].with_capacity(w*h*3)
-
-               # Build vertices
-               for m in [0..w[ do
-                       for p in [0..h[ do
-                               var u = m.to_f * 2.0 * pi / (w-1).to_f
-                               var v = p.to_f * pi / (h-1).to_f
-
-                               vertices.add radius * u.cos * v.sin
-                               vertices.add radius * v.cos
-                               vertices.add radius * u.sin * v.sin
-
-                               texture_coords.add (1.0 - m.to_f/(w-1).to_f)
-                               texture_coords.add(p.to_f/(h-1).to_f)
-
-                               normals.add u.cos * v.sin
-                               normals.add v.cos
-                               normals.add u.sin * v.sin
-                       end
-               end
-
-               # Build faces
-               var indices = new Array[Int].with_capacity((w-1)*(h-1)*6)
-               for m in [0..w-1[ do
-                       for p in [0..h-1[ do
-                               var a = m*h + p
-
-                               indices.add a
-                               indices.add(a+h)
-                               indices.add(a+1)
-
-                               indices.add(a+h)
-                               indices.add(a+h+1)
-                               indices.add(a+1)
-                       end
-               end
-
-               init(vertices, indices, normals, texture_coords)
-       end
-end
-
-# Number of vertices to create parallels, meridians are double of this
-#
-# The minimum should be 3 for an octahedron planet.
-fun n_parallels: Int do return 25
-
-redef class GamnitAssetTexture
-       # All images are either within the hd or ld folder
-       redef fun path do return app.definition / super
-end
-
-redef class App
-
-       # Earth
-       var planet = new Mesh.uv_sphere(2.0, 2*n_parallels, n_parallels)
-
-       # Cloud layer
-       var clouds = new Mesh.uv_sphere(2.08, 2*n_parallels, n_parallels)
-
-       # Visible atmosphere
-       var atmo = new Mesh.uv_sphere(2.16, 2*n_parallels, n_parallels)
-
-       # Program for the graphic card
-       var program = new GlobeProgram
-
-       private var definition: String is lazy do
-               var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE)
-
-               # HD textures max sizes reange from 2048 px to 5400 px
-               if max_texture_size < 5400 then
-                       return "ld"
-               else return "hd"
-       end
-
-       # Texture of the reflexive surface of the earth (with the seas in white)
-       var texture_seas = new Texture("seas.jpg")
-
-       # Texture of the surface of the earth
-       var texture_earth = new Texture("earth.jpg")
-
-       # Texture of the lights at night on earth
-       var texture_night = new Texture("lights.jpg")
-
-       # Elevation map of earth
-       var texture_elevation = new Texture("elevation.jpg")
-
-       # Texture of the clouds above the earth
-       var texture_clouds = new Texture("clouds.png")
-
-       # Camera for the only view
-       var camera: EulerCamera is lazy do
-               var camera = new EulerCamera(app.display.as(not null))
-               camera.move(0.0, 0.1, -5.0)
-               return camera
-       end
-
-       redef fun on_create
-       do
-               super
-
-               var display = display
-               assert display != null
-
-               var gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               # Prepare program
-               var program = program
-               program.compile_and_link
-
-               var gamnit_error = program.error
-               assert gamnit_error == null else print_error gamnit_error
-
-               # Blend
-               gl.capabilities.blend.enable
-               glBlendFunc(gl_SRC_ALPHA, gl_ONE_MINUS_SRC_ALPHA)
-
-               gl.capabilities.depth_test.enable
-               glDepthFunc gl_LEQUAL
-               glDepthMask true
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               # Prepare to draw
-               for tex in all_root_textures do
-                       tex.load
-                       glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
-                       glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
-
-                       gamnit_error = tex.error
-                       assert gamnit_error == null else print_error gamnit_error
-               end
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               program.use
-
-               glViewport(0, 0, display.width, display.height)
-               glClearColor(0.0, 0.02, 0.0, 1.0)
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               # Assign all textures to a unit
-               glActiveTexture gl_TEXTURE0
-               glBindTexture(gl_TEXTURE_2D, texture_earth.gl_texture)
-
-               glActiveTexture gl_TEXTURE1
-               glBindTexture(gl_TEXTURE_2D, texture_seas.gl_texture)
-
-               glActiveTexture gl_TEXTURE2
-               glBindTexture(gl_TEXTURE_2D, texture_night.gl_texture)
-
-               glActiveTexture gl_TEXTURE3
-               glBindTexture(gl_TEXTURE_2D, texture_elevation.gl_texture)
-
-               glActiveTexture gl_TEXTURE4
-               glBindTexture(gl_TEXTURE_2D, texture_clouds.gl_texture)
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               # Constant program values
-               program.coord.array_enabled = true
-               program.tex_coord.array_enabled = true
-               program.normal.array_enabled = true
-
-               program.scale.uniform 1.0
-
-               program.use_texture.uniform true
-
-               program.tex_shiny.uniform 1
-               program.tex_night.uniform 2
-               program.tex_displace.uniform 3
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-       end
-
-       private var clock = new Clock
-
-       redef fun frame_core(display)
-       do
-               var t = clock.total.to_f/3.0 + 0.75*pi
-
-               var gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-
-               glClear(gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT)
-               program.use
-
-               # Rotate world
-               # FIXME do this cleanly by moving the camera
-               var mvp = new Matrix.rotation(t, 0.0, 1.0, 0.0) * camera.mvp_matrix
-               program.mvp.uniform mvp
-               program.camera.uniform(-t.sin, -0.8, -t.cos)
-
-               # Planet
-               program.coord.array(planet.vertices, 3)
-               program.tex_coord.array(planet.texture_coords, 2)
-               program.normal.array(planet.normals, 3)
-               program.tex.uniform 0
-               program.use_texture.uniform true
-               program.color.uniform(1.0, 1.0, 1.0, 1.0)
-               program.is_surface.uniform true
-               glDrawElements(gl_TRIANGLES, planet.indices.length, gl_UNSIGNED_SHORT, planet.indices_c.native_array)
-
-               # Clouds
-               program.coord.array(clouds.vertices, 3)
-               program.tex_coord.array(clouds.texture_coords, 2)
-               program.normal.array(clouds.normals, 3)
-               program.tex.uniform 4
-               program.color.uniform(1.0, 1.0, 1.0, 0.5) # Half transparency
-               program.is_surface.uniform false
-               glDrawElements(gl_TRIANGLES, clouds.indices.length, gl_UNSIGNED_SHORT, clouds.indices_c.native_array)
-
-               # Atmo
-               program.coord.array(atmo.vertices, 3)
-               program.tex_coord.array(atmo.texture_coords, 2)
-               program.normal.array(atmo.normals, 3)
-               program.color.uniform(0.0, 0.8, 1.0, 0.02) # Blueish
-               program.is_surface.uniform false
-               program.use_texture.uniform false
-               glDrawElements(gl_TRIANGLES, atmo.indices.length, gl_UNSIGNED_SHORT, atmo.indices_c.native_array)
-               assert program.use_texture.is_active
-
-               display.flip
-
-               gl_error = glGetError
-               assert gl_error == gl_NO_ERROR else print gl_error
-       end
-
-       redef fun on_stop
-       do
-               # Clean up
-               program.delete
-
-               # Close gamnit
-               var display = display
-               if display != null then display.close
-       end
-end