1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Portable app using gamnit textures and programs with custom calls to OpenGL ES 2.0
17 app_name
"gamnit Globe"
18 app_namespace
"org.nitlanguage.globe"
19 app_version
(1, 0, git_revision
)
21 android_manifest_activity
"""android:screenOrientation="portrait""""
26 import matrix::projection
29 import gamnit::cameras
30 import gamnit::limit_fps
32 private import more_collections
34 # Graphical program to draw a planet with Phong lighting
36 super GamnitProgramFromSource
38 redef var vertex_shader_source = """
46 attribute vec4 translation;
49 attribute float scale;
51 // Vertex coordinates on textures
52 attribute vec2 tex_coord;
55 attribute vec3 normal;
57 // Model view projection matrix
60 // Texture of surface elevation to displace vertices
61 uniform sampler2D tex_displace;
63 // Draw this as a planet surface?
64 uniform bool is_surface;
66 // Output for the fragment shader
68 varying vec2 v_tex_coord;
69 varying vec3 v_normal;
73 // Pass varyings to the fragment shader
75 v_tex_coord = tex_coord;
81 s += 0.05 * texture2D(tex_displace, tex_coord).r;
83 gl_Position = (vec4(coord.xyz * s, 1.0) + translation) * mvp;
85 """ @ glsl_vertex_shader
87 redef var fragment_shader_source = """
88 precision mediump float;
90 // Input from the vertex shader
92 varying vec2 v_tex_coord;
93 varying vec3 v_normal;
95 // Coordinates of the camera
98 // Does this object use a texture?
99 uniform bool use_texture;
101 // Texture to apply on this object
102 uniform sampler2D tex;
104 // Reflection map to apply the the phong logic
105 uniform sampler2D tex_shiny;
107 // Texture for the dark side of the earth
108 uniform sampler2D tex_night;
110 // Draw this as a planet surface?
111 uniform bool is_surface;
114 // TODO configure from outside the shader
115 vec3 light_dir = normalize(vec3(-1.0, 0.0, -1.0));
116 vec4 ambient_color = vec4(0.2, 0.2, 0.2, 1.0);
117 vec4 diffuse_color = vec4(1.0, 1.0, 1.0, 1.0);
118 vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
123 gl_FragColor = v_color * texture2D(tex, v_tex_coord);
125 gl_FragColor = v_color;
129 float lambert = max(dot(light_dir, v_normal), 0.0);
132 float specular = 0.0;
134 vec3 to_camera = normalize(camera);
135 vec3 light_reflect = reflect(light_dir, v_normal);
136 float specularAngle = max(dot(light_reflect, to_camera), 0.0);
137 specular = pow(specularAngle, 16.0);
140 specular *= texture2D(tex_shiny, v_tex_coord).x;
141 else specular *= 0.2;
144 gl_FragColor *= ambient_color + lambert * diffuse_color;
145 gl_FragColor += specular * specular_color;
147 if (is_surface && lambert < 0.2) {
148 // Show city lights at night
149 float p_night = (0.2 - lambert) * 5.0;
150 gl_FragColor += p_night*texture2D(tex_night, v_tex_coord);
153 """ @ glsl_fragment_shader
155 # Vertices coordinates
156 var coord = attributes["coord
"].as(AttributeVec4) is lazy
158 # Color tint per vertex
159 var color = attributes["color
"].as(AttributeVec4) is lazy
162 var scale = attributes["scale
"].as(AttributeFloat) is lazy
164 # Coordinates on the textures, per vertex
165 var tex_coord = attributes["tex_coord
"].as(AttributeVec2) is lazy
168 var normal = attributes["normal
"].as(AttributeVec3) is lazy
170 # Model view projection matrix
171 var mvp = uniforms["mvp
"].as(UniformMat4) is lazy
173 # Should this program use the texture `tex`?
174 var use_texture = uniforms["use_texture
"].as(UniformBool) is lazy
176 # Main visible texture unit
177 var tex = uniforms["tex
"].as(UniformSampler2D) is lazy
179 # Texture unit for reflection effect
180 var tex_shiny = uniforms["tex_shiny
"].as(UniformSampler2D) is lazy
182 # Texture of the earth at night
183 var tex_night = uniforms["tex_night
"].as(UniformSampler2D) is lazy
185 # Texture with elevation data
186 var tex_displace = uniforms["tex_displace
"].as(UniformSampler2D) is lazy
188 # Position of the camera
189 var camera = uniforms["camera
"].as(UniformVec3) is lazy
191 # Execute this program to draw a planet surface?
192 var is_surface = uniforms["is_surface
"].as(UniformBool) is lazy
195 # Mesh with all information for drawing
198 # Vertices coordinates
199 var vertices: Array[Float]
201 # Indices to draw triangles
202 var indices: Array[Int]
204 private var indices_c = new CUInt16Array.from(indices) is lazy
206 # Normals on each vertex
207 var normals: Array[Float]
209 # Coordinates on the texture per vertex
210 var texture_coords: Array[Float]
212 # Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
213 init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
218 var vertices = new Array[Float].with_capacity(w*h*3)
219 var texture_coords = new Array[Float].with_capacity(w*h*2)
220 var normals = new Array[Float].with_capacity(w*h*3)
225 var u = m.to_f * 2.0 * pi / (w-1).to_f
226 var v = p.to_f * pi / (h-1).to_f
228 vertices.add radius * u.cos * v.sin
229 vertices.add radius * v.cos
230 vertices.add radius * u.sin * v.sin
232 texture_coords.add (1.0 - m.to_f/(w-1).to_f)
233 texture_coords.add(p.to_f/(h-1).to_f)
235 normals.add u.cos * v.sin
237 normals.add u.sin * v.sin
242 var indices = new Array[Int].with_capacity((w-1)*(h-1)*6)
257 init(vertices, indices, normals, texture_coords)
261 # Number of vertices to create parallels, meridians are double of this
263 # The minimum should be 3 for an octahedron planet.
264 fun n_parallels: Int do return 25
266 redef class GamnitAssetTexture
267 # All images are either within the hd or ld folder
268 redef fun path do return app.definition / super
274 var planet = new Mesh.uv_sphere(2.0, 2*n_parallels, n_parallels)
277 var clouds = new Mesh.uv_sphere(2.08, 2*n_parallels, n_parallels)
280 var atmo = new Mesh.uv_sphere(2.16, 2*n_parallels, n_parallels)
282 # Program for the graphic card
283 var program = new GlobeProgram
285 private var definition: String is lazy do
286 var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE)
288 # HD textures max sizes reange from 2048 px to 5400 px
289 if max_texture_size < 5400 then
294 # Texture of the reflexive surface of the earth (with the seas in white)
295 var texture_seas = new Texture("seas
.jpg
")
297 # Texture of the surface of the earth
298 var texture_earth = new Texture("earth
.jpg
")
300 # Texture of the lights at night on earth
301 var texture_night = new Texture("lights
.jpg
")
303 # Elevation map of earth
304 var texture_elevation = new Texture("elevation
.jpg
")
306 # Texture of the clouds above the earth
307 var texture_clouds = new Texture("clouds
.png
")
309 # Camera for the only view
310 var camera: EulerCamera is lazy do
311 var camera = new EulerCamera(app.display.as(not null))
312 camera.move(0.0, 0.1, -5.0)
320 var display = display
321 assert display != null
323 var gl_error = glGetError
324 assert gl_error == gl_NO_ERROR else print gl_error
327 var program = program
328 program.compile_and_link
330 var gamnit_error = program.error
331 assert gamnit_error == null else print_error gamnit_error
334 gl.capabilities.blend.enable
335 glBlendFunc(gl_SRC_ALPHA, gl_ONE_MINUS_SRC_ALPHA)
337 gl.capabilities.depth_test.enable
338 glDepthFunc gl_LEQUAL
341 gl_error = glGetError
342 assert gl_error == gl_NO_ERROR else print gl_error
345 for tex in all_root_textures do
347 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
348 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
350 gamnit_error = tex.error
351 assert gamnit_error == null else print_error gamnit_error
354 gl_error = glGetError
355 assert gl_error == gl_NO_ERROR else print gl_error
359 glViewport(0, 0, display.width, display.height)
360 glClearColor(0.0, 0.02, 0.0, 1.0)
362 gl_error = glGetError
363 assert gl_error == gl_NO_ERROR else print gl_error
365 # Assign all textures to a unit
366 glActiveTexture gl_TEXTURE0
367 glBindTexture(gl_TEXTURE_2D, texture_earth.gl_texture)
369 glActiveTexture gl_TEXTURE1
370 glBindTexture(gl_TEXTURE_2D, texture_seas.gl_texture)
372 glActiveTexture gl_TEXTURE2
373 glBindTexture(gl_TEXTURE_2D, texture_night.gl_texture)
375 glActiveTexture gl_TEXTURE3
376 glBindTexture(gl_TEXTURE_2D, texture_elevation.gl_texture)
378 glActiveTexture gl_TEXTURE4
379 glBindTexture(gl_TEXTURE_2D, texture_clouds.gl_texture)
381 gl_error = glGetError
382 assert gl_error == gl_NO_ERROR else print gl_error
384 # Constant program values
385 program.coord.array_enabled = true
386 program.tex_coord.array_enabled = true
387 program.normal.array_enabled = true
389 program.scale.uniform 1.0
391 program.use_texture.uniform true
393 program.tex_shiny.uniform 1
394 program.tex_night.uniform 2
395 program.tex_displace.uniform 3
397 gl_error = glGetError
398 assert gl_error == gl_NO_ERROR else print gl_error
401 private var clock = new Clock
403 redef fun frame_core(display)
405 var t = clock.total.to_f/3.0 + 0.75*pi
407 var gl_error = glGetError
408 assert gl_error == gl_NO_ERROR else print gl_error
410 glClear(gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT)
414 # FIXME do this cleanly by moving the camera
415 var mvp = new Matrix.rotation(t, 0.0, 1.0, 0.0) * camera.mvp_matrix
416 program.mvp.uniform mvp
417 program.camera.uniform(-t.sin, -0.8, -t.cos)
420 program.coord.array(planet.vertices, 3)
421 program.tex_coord.array(planet.texture_coords, 2)
422 program.normal.array(planet.normals, 3)
423 program.tex.uniform 0
424 program.use_texture.uniform true
425 program.color.uniform(1.0, 1.0, 1.0, 1.0)
426 program.is_surface.uniform true
427 glDrawElements(gl_TRIANGLES, planet.indices.length, gl_UNSIGNED_SHORT, planet.indices_c.native_array)
430 program.coord.array(clouds.vertices, 3)
431 program.tex_coord.array(clouds.texture_coords, 2)
432 program.normal.array(clouds.normals, 3)
433 program.tex.uniform 4
434 program.color.uniform(1.0, 1.0, 1.0, 0.5) # Half transparency
435 program.is_surface.uniform false
436 glDrawElements(gl_TRIANGLES, clouds.indices.length, gl_UNSIGNED_SHORT, clouds.indices_c.native_array)
439 program.coord.array(atmo.vertices, 3)
440 program.tex_coord.array(atmo.texture_coords, 2)
441 program.normal.array(atmo.normals, 3)
442 program.color.uniform(0.0, 0.8, 1.0, 0.02) # Blueish
443 program.is_surface.uniform false
444 program.use_texture.uniform false
445 glDrawElements(gl_TRIANGLES, atmo.indices.length, gl_UNSIGNED_SHORT, atmo.indices_c.native_array)
446 assert program.use_texture.is_active
450 gl_error = glGetError
451 assert gl_error == gl_NO_ERROR else print gl_error
460 var display = display
461 if display != null then display.close