lib/gamnit: intro the globe examples
authorAlexis Laferrière <alexis.laf@xymus.net>
Sun, 6 Dec 2015 01:07:31 +0000 (20:07 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Thu, 10 Dec 2015 21:02:59 +0000 (16:02 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

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

diff --git a/lib/gamnit/examples/globe/Makefile b/lib/gamnit/examples/globe/Makefile
new file mode 100644 (file)
index 0000000..d18b689
--- /dev/null
@@ -0,0 +1,41 @@
+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
new file mode 100644 (file)
index 0000000..0234f7f
--- /dev/null
@@ -0,0 +1,13 @@
+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
new file mode 100644 (file)
index 0000000..71612a4
Binary files /dev/null and b/lib/gamnit/examples/globe/art/icon.png differ
diff --git a/lib/gamnit/examples/globe/art/icon.xcf b/lib/gamnit/examples/globe/art/icon.xcf
new file mode 100644 (file)
index 0000000..efcf652
Binary files /dev/null and b/lib/gamnit/examples/globe/art/icon.xcf differ
diff --git a/lib/gamnit/examples/globe/assets/hd/clouds.png b/lib/gamnit/examples/globe/assets/hd/clouds.png
new file mode 100644 (file)
index 0000000..c4319d7
Binary files /dev/null and b/lib/gamnit/examples/globe/assets/hd/clouds.png differ
diff --git a/lib/gamnit/examples/globe/assets/hd/earth.jpg b/lib/gamnit/examples/globe/assets/hd/earth.jpg
new file mode 100644 (file)
index 0000000..79f6f2c
Binary files /dev/null and b/lib/gamnit/examples/globe/assets/hd/earth.jpg differ
diff --git a/lib/gamnit/examples/globe/assets/hd/elevation.jpg b/lib/gamnit/examples/globe/assets/hd/elevation.jpg
new file mode 100644 (file)
index 0000000..3ec70c0
Binary files /dev/null and b/lib/gamnit/examples/globe/assets/hd/elevation.jpg differ
diff --git a/lib/gamnit/examples/globe/assets/hd/lights.jpg b/lib/gamnit/examples/globe/assets/hd/lights.jpg
new file mode 100644 (file)
index 0000000..240df35
Binary files /dev/null and b/lib/gamnit/examples/globe/assets/hd/lights.jpg differ
diff --git a/lib/gamnit/examples/globe/assets/hd/seas.jpg b/lib/gamnit/examples/globe/assets/hd/seas.jpg
new file mode 100644 (file)
index 0000000..52ad2ac
Binary files /dev/null and b/lib/gamnit/examples/globe/assets/hd/seas.jpg differ
diff --git a/lib/gamnit/examples/globe/bin/.gitignore b/lib/gamnit/examples/globe/bin/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/lib/gamnit/examples/globe/org.nitlanguage.globe.txt b/lib/gamnit/examples/globe/org.nitlanguage.globe.txt
new file mode 100644 (file)
index 0000000..845e177
--- /dev/null
@@ -0,0 +1,10 @@
+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
new file mode 100644 (file)
index 0000000..b4e8861
--- /dev/null
@@ -0,0 +1,12 @@
+[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
new file mode 100644 (file)
index 0000000..3fedd6c
--- /dev/null
@@ -0,0 +1,466 @@
+# 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 [texture_seas, texture_earth, texture_night, texture_elevation, texture_clouds] 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
+       do
+               var display = display
+               if display != null then
+                       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
+       end
+
+       redef fun on_stop
+       do
+               # Clean up
+               program.delete
+
+               # Close gamnit
+               var display = display
+               if display != null then display.close
+       end
+end