From: Jean Privat Date: Sat, 21 May 2016 05:40:41 +0000 (-0400) Subject: Merge: gamnit: miscellaneous services and a few fixes X-Git-Url: http://nitlanguage.org?hp=e64164276317dab27de8adc453f73ba945f89b8d Merge: gamnit: miscellaneous services and a few fixes 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 --- diff --git a/contrib/asteronits/Makefile b/contrib/asteronits/Makefile index f110c83..a5e910a 100644 --- a/contrib/asteronits/Makefile +++ b/contrib/asteronits/Makefile @@ -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 index 0000000..0e2a300 --- /dev/null +++ b/contrib/tinks/src/client/tinks_vr.nit @@ -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 diff --git a/lib/gamnit/depth/depth_core.nit b/lib/gamnit/depth/depth_core.nit index 03a3970..f1e5803 100644 --- a/lib/gamnit/depth/depth_core.nit +++ b/lib/gamnit/depth/depth_core.nit @@ -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 diff --git a/lib/gamnit/depth/more_materials.nit b/lib/gamnit/depth/more_materials.nit index 613ff1a..b77c85e 100644 --- a/lib/gamnit/depth/more_materials.nit +++ b/lib/gamnit/depth/more_materials.nit @@ -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 diff --git a/lib/gamnit/depth/more_meshes.nit b/lib/gamnit/depth/more_meshes.nit index 9f2318a..569973b 100644 --- a/lib/gamnit/depth/more_meshes.nit +++ b/lib/gamnit/depth/more_meshes.nit @@ -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 diff --git a/lib/gamnit/depth/more_models.nit b/lib/gamnit/depth/more_models.nit index a126223..c2b9664 100644 --- a/lib/gamnit/depth/more_models.nit +++ b/lib/gamnit/depth/more_models.nit @@ -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 diff --git a/lib/gamnit/display.nit b/lib/gamnit/display.nit index 69ddb41..6e7c58c 100644 --- a/lib/gamnit/display.nit +++ b/lib/gamnit/display.nit @@ -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. diff --git a/lib/gamnit/display_android.nit b/lib/gamnit/display_android.nit index c412e9f..e9d1b4a 100644 --- a/lib/gamnit/display_android.nit +++ b/lib/gamnit/display_android.nit @@ -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 diff --git a/lib/gamnit/display_linux.nit b/lib/gamnit/display_linux.nit index 8f115e7..1dbee21 100644 --- a/lib/gamnit/display_linux.nit +++ b/lib/gamnit/display_linux.nit @@ -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 diff --git a/lib/gamnit/egl.nit b/lib/gamnit/egl.nit index 3fcb8c8..b451362 100644 --- a/lib/gamnit/egl.nit +++ b/lib/gamnit/egl.nit @@ -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 diff --git a/lib/gamnit/limit_fps.nit b/lib/gamnit/limit_fps.nit index a300b54..f16609f 100644 --- a/lib/gamnit/limit_fps.nit +++ b/lib/gamnit/limit_fps.nit @@ -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` # diff --git a/lib/gamnit/programs.nit b/lib/gamnit/programs.nit index 1a995ef..6859c76 100644 --- a/lib/gamnit/programs.nit +++ b/lib/gamnit/programs.nit @@ -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`. diff --git a/contrib/asteronits/src/texture_atlas_parser.nit b/lib/gamnit/texture_atlas_parser.nit similarity index 98% rename from contrib/asteronits/src/texture_atlas_parser.nit rename to lib/gamnit/texture_atlas_parser.nit index 113b887..a0e4d5f 100644 --- a/contrib/asteronits/src/texture_atlas_parser.nit +++ b/lib/gamnit/texture_atlas_parser.nit @@ -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 diff --git a/lib/gamnit/textures.nit b/lib/gamnit/textures.nit index 415aa90..d644d60 100644 --- a/lib/gamnit/textures.nit +++ b/lib/gamnit/textures.nit @@ -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 diff --git a/lib/geometry/polygon.nit b/lib/geometry/polygon.nit index 94ad1c9..1be832e 100644 --- a/lib/geometry/polygon.nit +++ b/lib/geometry/polygon.nit @@ -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