Merge: gamnit: miscellaneous services and a few fixes
authorJean Privat <jean@pryen.org>
Sat, 21 May 2016 05:40:41 +0000 (01:40 -0400)
committerJean Privat <jean@pryen.org>
Sat, 21 May 2016 05:40:41 +0000 (01:40 -0400)
This PR groups features required by my recent unpublished tech demos built on gamnit.

Here are more details on the less intuitive commits:

* The change to `current_fps` lets a program use it right from the beginning and gives a value which is somewhat realistic (vs returning 0).
* The `placeholder_model` can be useful when quickly writing the prototype of a game to get something visible in the 3D space.
* The doc of `subtexture` clarifies an ambiguity as to whether the method expected pixel offsets or proportional values out of 1.0.
* The tool `texture_atlas_parser` can be useful to other games.
* `draw_mode` is very useful to create optimized `Mesh`, using `gl_TRIANGLE_STRIP` and `gl_TRIANGLE_FAN`.
* The old API of `triangulate` was counter-intuitive. The result was put in a parameter and the list of points was cleared in the process. This PR offers a simple method and the recursive implementation as an optimized alternative. ping @BlackMinou
* `tinks_vr` is not playable but it shows how to convert a gamnit game to a basic VR version.

Pull-Request: #2115
Reviewed-by: Jean Privat <jean@pryen.org>

15 files changed:
contrib/asteronits/Makefile
contrib/tinks/src/client/tinks_vr.nit [new file with mode: 0644]
lib/gamnit/depth/depth_core.nit
lib/gamnit/depth/more_materials.nit
lib/gamnit/depth/more_meshes.nit
lib/gamnit/depth/more_models.nit
lib/gamnit/display.nit
lib/gamnit/display_android.nit
lib/gamnit/display_linux.nit
lib/gamnit/egl.nit
lib/gamnit/limit_fps.nit
lib/gamnit/programs.nit
lib/gamnit/texture_atlas_parser.nit [moved from contrib/asteronits/src/texture_atlas_parser.nit with 98% similarity]
lib/gamnit/textures.nit
lib/geometry/polygon.nit

index f110c83..a5e910a 100644 (file)
@@ -6,17 +6,17 @@ all: bin/asteronits
 bin/asteronits: $(shell ${NITLS} -M src/asteronits.nit linux) ${NITC} pre-build
        ${NITC} src/asteronits.nit -m linux -o $@
 
-bin/texture_atlas_parser: src/texture_atlas_parser.nit
-       ${NITC} src/texture_atlas_parser.nit -o $@
+bin/texture_atlas_parser: ../../lib/gamnit/texture_atlas_parser.nit
+       ${NITC} ../../lib/gamnit/texture_atlas_parser.nit -o $@
 
 src/controls.nit: art/controls.svg
        make -C ../inkscape_tools/
        ../inkscape_tools/bin/svg_to_png_and_nit art/controls.svg -a assets/ -s src/ -x 2.0 -g
 
-src/spritesheet_city.nit: bin/texture_atlas_parser
+src/spritesheet.nit: bin/texture_atlas_parser
        bin/texture_atlas_parser art/sheet.xml --dir src/ -n spritesheet
 
-pre-build: src/controls.nit src/spritesheet_city.nit
+pre-build: src/controls.nit src/spritesheet.nit
 
 check: bin/asteronits
        NIT_TESTING=true bin/asteronits
diff --git a/contrib/tinks/src/client/tinks_vr.nit b/contrib/tinks/src/client/tinks_vr.nit
new file mode 100644 (file)
index 0000000..0e2a300
--- /dev/null
@@ -0,0 +1,22 @@
+# 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.
+
+# VR mode for Android with Google Cardboard
+#
+# This version is not playable and very laggy as it is not modified
+# or optimized in any way for VR.
+# This module is made available as a minimal example of a VR game.
+module tinks_vr
+
+import gamnit::vr
index 03a3970..f1e5803 100644 (file)
@@ -103,6 +103,9 @@ class Mesh
        # Coordinates on the texture per vertex
        var texture_coords = new Array[Float] is lazy, writable
 
+       # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
+       fun draw_mode: GLDrawMode do return gl_TRIANGLES
+
        # Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
        init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
        do
index 613ff1a..b77c85e 100644 (file)
@@ -75,9 +75,9 @@ class SmoothMaterial
 
                # Execute draw
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
@@ -154,11 +154,13 @@ class TexturedMaterial
                                var xd = sample_used_texture.offset_right - xa
                                var ya = sample_used_texture.offset_top
                                var yd = sample_used_texture.offset_bottom - ya
+                               xd *= 0.999
+                               yd *= 0.999
 
                                var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
                                for i in [0..mesh.texture_coords.length/2[ do
                                        tex_coords[i*2]   = xa + xd * mesh.texture_coords[i*2]
-                                       tex_coords[i*2+1] = ya + yd * mesh.texture_coords[i*2+1]
+                                       tex_coords[i*2+1] = 1.0 - (ya + yd * mesh.texture_coords[i*2+1])
                                end
 
                                program.tex_coord.array(tex_coords, 2)
@@ -180,9 +182,9 @@ class TexturedMaterial
                program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
 
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
@@ -218,9 +220,9 @@ class NormalsMaterial
                program.normal.array(mesh.normals, 3)
 
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
index 9f2318a..569973b 100644 (file)
@@ -124,7 +124,8 @@ class Cube
                var d = [1.0, 0.0]
 
                var texture_coords = new Array[Float]
-               for v in [c, d, a, a, d, b] do for i in 6.times do texture_coords.add_all v
+               var face = [a, c, d, a, d, b]
+               for i in 6.times do for v in face do texture_coords.add_all v
                return texture_coords
        end
 
index a126223..c2b9664 100644 (file)
@@ -50,7 +50,7 @@ class ModelAsset
 
                if leaves.is_empty then
                        # Nothing was loaded, use a cube with the default material
-                       var leaf = new LeafModel(new Cube, new SmoothMaterial.default)
+                       var leaf = placeholder_model
                        leaves.add leaf
                end
        end
@@ -168,7 +168,7 @@ private class ModelFromObj
                # Load textures need for these materials
                for name in texture_names do
                        if not asset_textures_by_name.keys.has(name) then
-                               var tex = new GamnitAssetTexture(name)
+                               var tex = new TextureAsset(name)
                                asset_textures_by_name[name] = tex
                        end
                end
@@ -379,8 +379,13 @@ end
 
 redef class Sys
        # Textures loaded from .mtl files for models
-       var asset_textures_by_name = new Map[String, GamnitAssetTexture]
+       var asset_textures_by_name = new Map[String, TextureAsset]
 
        # All instantiated asset models
        var models = new Set[ModelAsset]
+
+       # Blue cube of 1 unit on each side, acting as placeholder for models failing to load
+       #
+       # This model can be freely used by any `Actor` as placeholder or for debugging.
+       var placeholder_model = new LeafModel(new Cube, new SmoothMaterial.default) is lazy
 end
index 69ddb41..6e7c58c 100644 (file)
@@ -44,6 +44,15 @@ class GamnitDisplay
        # Only affects the desktop implementations.
        var show_cursor: Bool = true is writable
 
+       # Number of bits used for the red value in the color buffer
+       fun red_bits: Int do return 8
+
+       # Number of bits used for the green value in the color buffer
+       fun green_bits: Int do return 8
+
+       # Number of bits used for the blue value in the color buffer
+       fun blue_bits: Int do return 8
+
        # Prepare this display
        #
        # The implementation varies per platform.
index c412e9f..e9d1b4a 100644 (file)
@@ -37,7 +37,7 @@ redef class GamnitDisplay
                setup_egl_display native_display
 
                # We need 8 bits per color for selection by color
-               select_egl_config(8, 8, 8, 0, 8, 0, 0)
+               select_egl_config(red_bits, green_bits, blue_bits, 0, 8, 0, 0)
 
                var format = egl_config.attribs(egl_display).native_visual_id
                native_window.set_buffers_geometry(0, 0, format)
@@ -48,7 +48,7 @@ redef class GamnitDisplay
        redef fun close do close_egl
 end
 
-redef class GamnitAssetTexture
+redef class TextureAsset
 
        redef fun load_from_platform
        do
index 8f115e7..1dbee21 100644 (file)
@@ -48,7 +48,7 @@ redef class GamnitDisplay
                setup_egl_display x11_display
 
                if debug_gamnit then print "Setting up EGL context"
-               select_egl_config(8, 8, 8, 8, 8, 0, 0)
+               select_egl_config(red_bits, green_bits, blue_bits, 8, 8, 0, 0)
                setup_egl_context window_handle
        end
 
@@ -95,7 +95,7 @@ redef class GamnitDisplay
        end
 end
 
-redef class GamnitAssetTexture
+redef class TextureAsset
 
        redef fun load_from_platform
        do
index 3fcb8c8..b451362 100644 (file)
@@ -46,14 +46,14 @@ redef class GamnitDisplay
        end
 
        # Select an EGL config
-       protected fun select_egl_config(blue, green, red, alpha, depth, stencil, sample: Int)
+       protected fun select_egl_config(red, green, blue, alpha, depth, stencil, sample: Int)
        do
                var config_chooser = new EGLConfigChooser
                config_chooser.renderable_type_egl
                config_chooser.surface_type_egl
-               config_chooser.blue_size = blue
-               config_chooser.green_size = green
                config_chooser.red_size = red
+               config_chooser.green_size = green
+               config_chooser.blue_size = blue
                if alpha > 0 then config_chooser.alpha_size = alpha
                if depth > 0 then config_chooser.depth_size = depth
                if stencil > 0 then config_chooser.stencil_size = stencil
index a300b54..f16609f 100644 (file)
@@ -29,8 +29,8 @@ redef class App
 
        # Current frame-rate
        #
-       # Updated each 5 seconds.
-       var current_fps = 0.0
+       # Updated each 5 seconds, initialized at the value of `maximum_fps`.
+       var current_fps: Float = maximum_fps is lazy
 
        redef fun frame_full
        do
@@ -47,7 +47,7 @@ redef class App
        private var frame_count = 0
 
        # Deadline used to compute `current_fps`
-       private var frame_count_deadline = 0
+       private var frame_count_deadline = 5
 
        # Check and sleep to maintain a frame-rate bellow `maximum_fps`
        #
index 1a995ef..6859c76 100644 (file)
@@ -428,6 +428,23 @@ abstract class GamnitProgram
                end
        end
 
+       # Diagnose possible problems with the shaders of the program
+       #
+       # Lists to the console inactive uniforms and attributes.
+       # These may not be problematic but they can help to debug the program.
+       fun diagnose
+       do
+               if gl_program == null then compile_and_link
+
+               print "# Diagnose {class_name}"
+               for k,v in uniforms do
+                       if not v.is_active then print "* Uniform {v.name} is inactive"
+               end
+               for k,v in attributes do
+                       if not v.is_active then print "* Attribute {v.name} is inactive"
+               end
+       end
+
        # Attributes of this program organized by name
        #
        # Active attributes are gathered at `compile_and_link`.
similarity index 98%
rename from contrib/asteronits/src/texture_atlas_parser.nit
rename to lib/gamnit/texture_atlas_parser.nit
index 113b887..a0e4d5f 100644 (file)
@@ -44,7 +44,7 @@ var attributes = new Array[String]
 # Insert the first attribute, to load the root texture
 var png_file = "images" / xml_file.basename("xml") + "png"
 attributes.add """
-       var root_texture = new Texture("{{{png_file}}}")"""
+       var root_texture = new TextureAsset("{{{png_file}}}")"""
 
 # Read XML file
 var content = xml_file.to_path.read_all
index 415aa90..d644d60 100644 (file)
@@ -21,7 +21,7 @@ import display
 abstract class Texture
 
        # Prepare a texture located at `path` within the `assets` folder
-       new (path: Text) do return new GamnitAssetTexture(path.to_s)
+       new (path: Text) do return new TextureAsset(path.to_s)
 
        # Root texture of which `self` is derived
        fun root: GamnitRootTexture is abstract
@@ -41,7 +41,7 @@ abstract class Texture
        # OpenGL handle to this texture
        fun gl_texture: Int do return root.gl_texture
 
-       # Prepare a subtexture from this texture
+       # Prepare a subtexture from this texture, from the given pixel offsets
        fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
        do
                # Setup the subtexture
@@ -143,7 +143,7 @@ class GamnitRootTexture
 end
 
 # Texture loaded from the assets folder
-class GamnitAssetTexture
+class TextureAsset
        super GamnitRootTexture
 
        # Path to this texture within the `assets` folder
index 94ad1c9..1be832e 100644 (file)
@@ -441,31 +441,40 @@ fun turn_left(p1, p2, p3: Point[Float]): Bool do
 end
 
 # Split a polygon into triangles
-# Useful for converting a concave polygon into multiple convex ones
-fun triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
-       var poly = new Polygon(pts)
-       pts = poly.points
-       recursive_triangulate(pts, results)
+#
+# Useful for converting a concave polygon into multiple convex ones.
+#
+# See: the alternative `triangulate_recursive` uses arrays in-place.
+fun triangulate(points: Array[Point[Float]]): Array[ConvexPolygon]
+do
+       var results = new Array[ConvexPolygon]
+       triangulate_recursive(points.clone, results)
+       return results
 end
 
-private fun recursive_triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
-       if pts.length == 3 then
-               results.add(new ConvexPolygon(pts))
+# Split a polygon into triangles using arrays in-place
+#
+# Consumes the content of `points` and add the triangles to `results`.
+#
+# See: the alternative `triangulate` which does not modify `points` and returns a new array.
+fun triangulate_recursive(points: Array[Point[Float]], results: Array[ConvexPolygon]) do
+       if points.length == 3 then
+               results.add(new ConvexPolygon(points))
                return
        end
-       var prev = pts[pts.length - 1]
-       var curr = pts[0]
-       var next = pts[1]
-       for i in [1..pts.length[ do
+       var prev = points[points.length - 1]
+       var curr = points[0]
+       var next = points[1]
+       for i in [1..points.length[ do
                if turn_left(prev, curr, next) then
-                       prev = pts[i-1]
+                       prev = points[i-1]
                        curr = next
-                       if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+                       if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
                        continue
                end
                var contains = false
                var triangle = new ConvexPolygon(new Array[Point[Float]].with_items(prev, curr, next))
-               for p in pts do
+               for p in points do
                        if p != prev and p != curr and p != next then
                                if triangle.contain(p) then
                                        contains = true
@@ -475,12 +484,12 @@ private fun recursive_triangulate(pts: Array[Point[Float]], results: Array[Conve
                end
                if not contains then
                        results.add(triangle)
-                       pts.remove(curr)
-                       recursive_triangulate(pts, results)
+                       points.remove(curr)
+                       triangulate_recursive(points, results)
                        break
                end
-               prev = pts[i-1]
+               prev = points[i-1]
                curr = next
-               if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+               if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
        end
 end