Merge: gamnit: new services and a lot of bug fixes and performance improvements
authorJean Privat <jean@pryen.org>
Wed, 29 Nov 2017 15:40:53 +0000 (10:40 -0500)
committerJean Privat <jean@pryen.org>
Wed, 29 Nov 2017 15:40:53 +0000 (10:40 -0500)
This PR groups small fixes to gamnit and related packages. It is a general cleanup in preparation for optimizations to the gamnit depth 3D API.

Intro a few new services:
* `ParticleSystem::clear` to remove all live particles.
* `CustomTexture` can be modified and reloaded in GPU memory after the first call to `load`.
* Don't apply dynamic resolution to UI sprites as they are usually lightweight and any change in resolution is easily noticable.

Optimizations:
* Matrix creation and manipulation, in both gamnit and the matrix package.
* Free pixel data from both Nit and Java memory after loading them from the assets folder into the GPU memory.
* Avoids using mallocs in `realtime` services.

Fixes:
* Fix the left and right anchors of the `UICamera`.
* Fix a constant used to ask for antialiasing in the `egl` package.
* Avoid long attribute names in Blinn-Phong shader in case their name is truncated or the attribute is optimized out.
* Fix pointer to the parent GPU texture name in subtextures.
* Improve a few API doc.

Pull-Request: #2586
Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr>
Reviewed-by: Jean Privat <jean@pryen.org>

21 files changed:
contrib/asteronits/src/asteronits.nit
lib/android/assets_and_resources.nit
lib/egl.nit
lib/gamnit/cameras.nit
lib/gamnit/depth/depth.nit
lib/gamnit/depth/depth_core.nit
lib/gamnit/depth/more_materials.nit
lib/gamnit/depth/particles.nit
lib/gamnit/depth/shadow.nit
lib/gamnit/display_android.nit
lib/gamnit/display_linux.nit
lib/gamnit/dynamic_resolution.nit
lib/gamnit/flat/flat_core.nit
lib/gamnit/limit_fps.nit
lib/gamnit/programs.nit
lib/gamnit/textures.nit
lib/glesv2/glesv2.nit
lib/matrix/matrix.nit
lib/matrix/projection.nit
lib/performance_analysis.nit
lib/realtime.nit

index cc9f6a8..628fc69 100644 (file)
@@ -70,6 +70,8 @@ redef class App
 
                # Move the camera to show all the world world in the screen range
                world_camera.reset_height(world.half_height * 2.0)
+
+               ui_camera.reset_height 720.0
        end
 
        # Main spritesheet with ships, asteroids and beams
index 40dace2..930bc17 100644 (file)
@@ -327,6 +327,10 @@ private extern class NativeBitmap in "Java" `{ android.graphics.Bitmap `}
                return self.hasAlpha();
        `}
 
+       fun recycle in "Java" `{
+               self.recycle();
+       `}
+
        # HACK for bug #845
        redef fun new_global_ref import sys, Sys.jni_env `{
                Sys sys = NativeBitmap_sys(self);
index a39b1c6..5d8f4f9 100644 (file)
@@ -216,6 +216,8 @@ class EGLConfigAttribs
        fun red_size: Int do return display.config_attrib(config, 0x3024)
        fun depth_size: Int do return display.config_attrib(config, 0x3025)
        fun stencil_size: Int do return display.config_attrib(config, 0x3026)
+       fun samples: Int do return display.config_attrib(config, 0x3031)
+       fun sample_buffers: Int do return display.config_attrib(config, 0x3032)
 
        fun native_visual_id: Int do return display.config_attrib(config, 0x302E)
        fun native_visual_type: Int do return display.config_attrib(config, 0x302F)
@@ -427,7 +429,8 @@ class EGLConfigChooser
        fun alpha_size=(size: Int) do insert_attrib_with_val(0x3021, size)
        fun depth_size=(size: Int) do insert_attrib_with_val(0x3025, size)
        fun stencil_size=(size: Int) do insert_attrib_with_val(0x3026, size)
-       fun sample_buffers=(size: Int) do insert_attrib_with_val(0x3031, size)
+       fun samples=(count: Int) do insert_attrib_with_val(0x3031, count)
+       fun sample_buffers=(size: Int) do insert_attrib_with_val(0x3032, size)
 
        fun caveat=(caveat: EGLConfigCaveat) do insert_attrib_with_val(0x3050, caveat.to_i)
 
index c83ace3..33aac41 100644 (file)
@@ -245,10 +245,10 @@ class UICamera
        var bottom: IPoint3d[Float] = new CameraAnchor(self, 0.5, -1.0)
 
        # Center of the left border of the screen, at z = 0
-       var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
+       var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -0.5)
 
        # Center of the right border of the screen, at z = 0
-       var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
+       var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -0.5)
 
        # Top left corner of the screen, at z = 0
        var top_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, 0.0)
index 2e4fc82..a7864f9 100644 (file)
@@ -42,7 +42,7 @@ redef class App
                glCullFace gl_BACK
 
                # Prepare programs
-               var programs = [versatile_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
+               var programs = [blinn_phong_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
                for program in programs do
                        program.compile_and_link
                        var gamnit_error = program.error
@@ -87,11 +87,12 @@ redef class App
                glDepthMask true
                perfs["gamnit depth particles"].add frame_core_depth_clock.lapse
 
+               # Stop using the dynamic resolution before drawing UI sprites
+               frame_core_dynamic_resolution_after display
+
                frame_core_ui_sprites display
                perfs["gamnit depth ui_sprites"].add frame_core_depth_clock.lapse
 
-               frame_core_dynamic_resolution_after display
-
                # Debug, show the light point of view
                #frame_core_shadow_debug display
        end
index 0e45e3d..e7e42de 100644 (file)
@@ -185,7 +185,10 @@ end
 # ~~~
 class Mesh
 
-       # Vertices coordinates
+       # Number for vertices
+       fun n_vertices: Int do return vertices.length / 3
+
+       # Vertices relative coordinates, 3 floats per vertex
        var vertices = new Array[Float] is lazy, writable
 
        # Indices to draw triangles with `glDrawElements`
@@ -195,10 +198,10 @@ class Mesh
 
        private var indices_c = new CUInt16Array.from(indices) is lazy, writable
 
-       # Normals on each vertex
+       # Normals, 3 floats per vertex
        var normals = new Array[Float] is lazy, writable
 
-       # Coordinates on the texture per vertex
+       # Coordinates on the texture, 2 floats per vertex
        var texture_coords = new Array[Float] is lazy, writable
 
        # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
index 8de6f9d..6674c04 100644 (file)
@@ -49,16 +49,20 @@ class SmoothMaterial
 
        redef fun draw(actor, model, camera)
        do
-               var program = app.versatile_program
+               var program = app.blinn_phong_program
                program.use
                program.mvp.uniform camera.mvp_matrix
 
                var mesh = model.mesh
 
                # Actor specs
+               glDisableVertexAttribArray program.translation.location
+               glDisableVertexAttribArray program.scale.location
+
                program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
                program.scale.uniform actor.scale
-               program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+               program.alpha.uniform actor.alpha
+               program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
 
                # From mesh
                program.coord.array_enabled = true
@@ -77,15 +81,14 @@ class SmoothMaterial
                program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
 
                # Colors from the material
-               var a = actor.alpha
-               program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
-                                             ambient_color[2]*a, ambient_color[3]*a)
-               program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
-                                             diffuse_color[2]*a, diffuse_color[3]*a)
-               program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
-                                              specular_color[2]*a, specular_color[3]*a)
+               program.ambient_color.uniform(ambient_color[0], ambient_color[1],
+                                             ambient_color[2], ambient_color[3])
+               program.diffuse_color.uniform(diffuse_color[0], diffuse_color[1],
+                                             diffuse_color[2], diffuse_color[3])
+               program.specular_color.uniform(specular_color[0], specular_color[1],
+                                              specular_color[2], specular_color[3])
 
-               setup_lights(actor, model, camera, program)
+               setup_lights(camera, program)
 
                # Execute draw
                if mesh.indices.is_empty then
@@ -97,7 +100,7 @@ class SmoothMaterial
                assert glGetError == gl_NO_ERROR
        end
 
-       private fun setup_lights(actor: Actor, model: LeafModel, camera: Camera, program: BlinnPhongProgram)
+       private fun setup_lights(camera: Camera, program: BlinnPhongProgram)
        do
                # TODO use a list of lights
 
@@ -158,7 +161,7 @@ class TexturedMaterial
        do
                var mesh = model.mesh
 
-               var program = app.versatile_program
+               var program = app.blinn_phong_program
                program.use
 
                # One of the textures used, if any
@@ -208,9 +211,13 @@ class TexturedMaterial
                        program.use_map_bump.uniform false
                end
 
+               glDisableVertexAttribArray program.translation.location
+               glDisableVertexAttribArray program.scale.location
+
                program.mvp.uniform camera.mvp_matrix
                program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
                program.scale.uniform actor.scale
+               program.alpha.uniform actor.alpha
 
                # If using a texture, set `texture_coords`
                program.tex_coord.array_enabled = sample_used_texture != null
@@ -239,21 +246,20 @@ class TexturedMaterial
                program.coord.array_enabled = true
                program.coord.array(mesh.vertices, 3)
 
-               program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+               program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
 
-               var a = actor.alpha
-               program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
-                                             ambient_color[2]*a, ambient_color[3]*a)
-               program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
-                                             diffuse_color[2]*a, diffuse_color[3]*a)
-               program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
-                                              specular_color[2]*a, specular_color[3]*a)
+               program.ambient_color.uniform(ambient_color[0], ambient_color[1],
+                                             ambient_color[2], ambient_color[3])
+               program.diffuse_color.uniform(diffuse_color[0], diffuse_color[1],
+                                             diffuse_color[2], diffuse_color[3])
+               program.specular_color.uniform(specular_color[0], specular_color[1],
+                                              specular_color[2], specular_color[3])
 
                program.normal.array_enabled = true
                program.normal.array(mesh.normals, 3)
 
                # Light
-               setup_lights(actor, model, camera, program)
+               setup_lights(camera, program)
 
                # Camera
                program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
@@ -292,7 +298,7 @@ class NormalsMaterial
                program.coord.array_enabled = true
                program.coord.array(mesh.vertices, 3)
 
-               program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+               program.rotation = new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
 
                program.normal.array_enabled = true
                program.normal.array(mesh.normals, 3)
@@ -314,10 +320,12 @@ class BlinnPhongProgram
                attribute vec4 coord;
 
                // Vertex translation
-               uniform vec4 translation;
+               attribute vec4 translation;
 
                // Vertex scaling
-               uniform float scale;
+               attribute float scale;
+
+               attribute float alpha;
 
                // Vertex coordinates on textures
                attribute vec2 tex_coord;
@@ -328,7 +336,16 @@ class BlinnPhongProgram
                // Camera model view projection matrix
                uniform mat4 mvp;
 
-               uniform mat4 rotation;
+               // Actor rotation
+               attribute vec4 rotation_row0;
+               attribute vec4 rotation_row1;
+               attribute vec4 rotation_row2;
+               attribute vec4 rotation_row3;
+
+               mat4 rotation()
+               {
+                       return mat4(rotation_row0, rotation_row1, rotation_row2, rotation_row3);
+               }
 
                // Lights config
                uniform lowp int light_kind;
@@ -344,9 +361,11 @@ class BlinnPhongProgram
                varying vec4 v_to_light;
                varying vec4 v_to_camera;
                varying vec4 v_depth_pos;
+               varying float v_alpha;
 
                void main()
                {
+                       mat4 rotation = rotation();
                        vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
                        gl_Position = pos * mvp;
                        v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
@@ -365,6 +384,8 @@ class BlinnPhongProgram
                                // Point light (and others?)
                                v_to_light = normalize(vec4(light_center, 1.0) - pos);
                        }
+
+                       v_alpha = alpha;
                }
                """ @ glsl_vertex_shader
 
@@ -377,6 +398,7 @@ class BlinnPhongProgram
                varying vec4 v_to_light;
                varying vec4 v_to_camera;
                varying vec4 v_depth_pos;
+               varying float v_alpha;
 
                // Colors
                uniform vec4 ambient_color;
@@ -407,13 +429,13 @@ class BlinnPhongProgram
                uniform lowp int light_kind;
                uniform bool use_shadows;
                uniform sampler2D depth_texture;
-               uniform float depth_texture_size;
-               uniform int depth_texture_taps;
+               uniform float depth_size;
+               uniform int depth_taps;
 
                // Shadow effect on the diffuse colors of the fragment at offset `x, y`
                float shadow_lookup(vec2 depth_coord, float x, float y) {
                        float tap_width = 1.0;
-                       float pixel_size = tap_width/depth_texture_size;
+                       float pixel_size = tap_width/depth_size;
 
                        vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
                                           y * pixel_size * v_depth_pos.w);
@@ -448,7 +470,7 @@ class BlinnPhongProgram
 
                        vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
 
-                       float taps = float(depth_texture_taps);
+                       float taps = float(depth_taps);
                        float tap_step = 2.00/taps;
                        float sum = 0.0;
                        for (float x = -1.0; x <= 0.99; x += tap_step)
@@ -467,13 +489,13 @@ class BlinnPhongProgram
                        }
 
                        // Ambient light
-                       vec4 ambient = ambient_color;
+                       vec4 ambient = ambient_color * v_alpha;
                        if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
 
                        if (light_kind == 0) {
                                // No light, show diffuse and ambient
 
-                               vec4 diffuse = diffuse_color;
+                               vec4 diffuse = diffuse_color * v_alpha;
                                if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
 
                                gl_FragColor = ambient + diffuse;
@@ -499,7 +521,7 @@ class BlinnPhongProgram
                                        diffuse *= shadow();
                                }
 
-                               vec4 specular = s * specular_color;
+                               vec4 specular = s * specular_color * v_alpha;
                                if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
 
                                gl_FragColor = ambient + diffuse + specular;
@@ -569,22 +591,49 @@ class BlinnPhongProgram
        var depth_texture = uniforms["depth_texture"].as(UniformSampler2D) is lazy
 
        # Size, in pixels, of `depth_texture`
-       var depth_texture_size = uniforms["depth_texture_size"].as(UniformFloat) is lazy
+       var depth_texture_size = uniforms["depth_size"].as(UniformFloat) is lazy
 
        # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
-       var depth_texture_taps = uniforms["depth_texture_taps"].as(UniformInt) is lazy
+       var depth_texture_taps = uniforms["depth_taps"].as(UniformInt) is lazy
 
        # Camera position
        var camera = uniforms["camera"].as(UniformVec3) is lazy
 
        # Translation applied to each vertex
-       var translation = uniforms["translation"].as(UniformVec4) is lazy
+       var translation = attributes["translation"].as(AttributeVec4) is lazy # TODO attribute
 
-       # Rotation matrix
-       var rotation = uniforms["rotation"].as(UniformMat4) is lazy
+       # Set `mat` at the uniform rotation matrix
+       fun rotation=(mat: Matrix)
+       do
+               var i = 0
+               for r in [rotation_row0, rotation_row1, rotation_row2, rotation_row3] do
+                       if r.is_active then
+                               glDisableVertexAttribArray r.location
+                               r.uniform(mat[0, i], mat[1, i], mat[2, i], mat[3, i])
+                       end
+                       i += 1
+               end
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+       end
+
+       # Rotation matrix, row0
+       var rotation_row0 = attributes["rotation_row0"].as(AttributeVec4) is lazy
+
+       # Rotation matrix, row 1
+       var rotation_row1 = attributes["rotation_row1"].as(AttributeVec4) is lazy
+
+       # Rotation matrix, row 2
+       var rotation_row2 = attributes["rotation_row2"].as(AttributeVec4) is lazy
+
+       # Rotation matrix, row 3
+       var rotation_row3 = attributes["rotation_row3"].as(AttributeVec4) is lazy
+
+       # Scaling per vertex
+       var scale = attributes["scale"].as(AttributeFloat) is lazy
 
        # Scaling per vertex
-       var scale = uniforms["scale"].as(UniformFloat) is lazy
+       var alpha = attributes["alpha"].as(AttributeFloat) is lazy
 
        # Camera model view projection matrix
        var mvp = uniforms["mvp"].as(UniformMat4) is lazy
@@ -610,7 +659,7 @@ class NormalProgram
 end
 
 redef class App
-       private var versatile_program = new BlinnPhongProgram is lazy
+       private var blinn_phong_program = new BlinnPhongProgram is lazy
 
        private var normals_program = new NormalProgram is lazy
 end
index bfe3392..557e694 100644 (file)
@@ -89,6 +89,16 @@ class ParticleSystem
        # Time-to-live of each particle
        private var ttls = new Array[Float]
 
+       # Clear all particles
+       fun clear
+       do
+               centers.clear
+               ots.clear
+               scales.clear
+               ttls.clear
+               total_particles = 0
+       end
+
        # Add a particle at `center` with `scale`, living for `ttl` from `time_offset`
        #
        # `time_offset` is added to the creation time, it can be used to delay the
index d65a3cb..75178f5 100644 (file)
@@ -84,8 +84,6 @@ redef class App
                var light = app.light
                if not light isa LightCastingShadows then return
 
-               perf_clock_shadow.lapse
-
                # Make sure there's no errors pending
                assert glGetError == gl_NO_ERROR
 
@@ -116,8 +114,6 @@ redef class App
                # Take down, bring back default values
                glBindFramebuffer(gl_FRAMEBUFFER, shadow_context.screen_framebuffer)
                glColorMask(true, true, true, true)
-
-               perfs["gamnit shadows prep"].add perf_clock_shadow.lapse
        end
 
        # ---
index b14cb33..6b069d3 100644 (file)
@@ -74,6 +74,8 @@ redef class TextureAsset
                var pixels = buf.native_array
 
                load_from_pixels(pixels, bmp.width, bmp.height, gl_RGBA)
+               buf.destroy
+               bmp.recycle
 
                jni_env.pop_local_frame
        end
index dcfd963..e9af008 100644 (file)
@@ -139,5 +139,7 @@ redef class TextureAsset
                var pixels = surface.pixels
 
                load_from_pixels(pixels, surface.w, surface.h, format)
+
+               surface.free
        end
 end
index 7399dd9..0219db4 100644 (file)
@@ -42,7 +42,7 @@ redef class App
        #
        # This value is applied to both X and Y, so it has an exponential effect on
        # the number of pixels.
-       var dynamic_resolution_ratio = 1.0
+       var dynamic_resolution_ratio = 1.0 is writable
 
        # Minimum dynamic screen resolution
        var min_dynamic_resolution_ratio = 0.0125 is writable
index 2ab18f6..22ea7b3 100644 (file)
@@ -80,27 +80,27 @@ class Sprite
        var texture: Texture is writable(texture_direct=)
 
        # Texture drawn to screen
-       fun texture=(value: Texture)
+       fun texture=(texture: Texture)
        do
-               if isset _texture and value != texture then
+               if isset _texture and texture != self.texture then
                        needs_update
-                       if value.root != texture.root then needs_remap
+                       if texture.root != self.texture.root then needs_remap
                end
-               texture_direct = value
+               texture_direct = texture
        end
 
        # Center position of this sprite in world coordinates
        var center: Point3d[Float] is writable(center_direct=), noautoinit
 
        # Center position of this sprite in world coordinates
-       fun center=(value: Point3d[Float]) is autoinit do
-               if isset _center and value != center then
+       fun center=(center: Point3d[Float]) is autoinit do
+               if isset _center and center != self.center then
                        needs_update
-                       center.sprites_remove self
+                       self.center.sprites_remove self
                end
 
-               value.sprites_add self
-               center_direct = value
+               center.sprites_add self
+               center_direct = center
        end
 
        # Last animation set with `animate`
@@ -1753,10 +1753,11 @@ end
 redef class GLfloatArray
        private fun fill_from_matrix(matrix: Matrix, dst_offset: nullable Int)
        do
-               dst_offset = dst_offset or else 0
+               dst_offset = dst_offset or else add_index
                var mat_len = matrix.width*matrix.height
                assert length >= mat_len + dst_offset
                native_array.fill_from_matrix_native(matrix.items, dst_offset, mat_len)
+               add_index += mat_len
        end
 end
 
index 896f07f..ed73f5f 100644 (file)
@@ -25,7 +25,7 @@ redef class App
        # Zero (or a negative value) means no limit.
        #
        # Applications can modify this value even during the main-loop.
-       var maximum_fps = 60.0 is writable
+       var maximum_fps = 0.0 is writable
 
        # Current frame-rate
        #
index fc02530..9df20cd 100644 (file)
@@ -93,7 +93,7 @@ class Attribute
 
                var native = native_float_array
                if native == null or array.length > native.length then
-                       if native != null then native.destroy
+                       if native != null then native.finalize
                        native = new GLfloatArray.from(array)
                        self.native_float_array = native
                else
@@ -566,8 +566,8 @@ redef extern class NativeGLfloatArray
        # Overwrite this matrix with the identity matrix
        fun set_identity
        do
-               for i in 4.times do
-                       for j in 4.times do
+               for i in [0..4[ do
+                       for j in [0..4[ do
                                matrix_set(i, j, if i == j then 1.0 else 0.0)
                        end
                end
@@ -584,8 +584,8 @@ redef class Matrix
        # Copy content of this matrix to a `NativeGLfloatArray`
        fun fill_native(native: NativeGLfloatArray)
        do
-               for i in width.times do
-                       for j in height.times do
+               for i in [0..width[ do
+                       for j in [0..height[ do
                                native.matrix_set(i, j, self[i, j])
                        end
                end
index 60cec5a..4383c2b 100644 (file)
@@ -154,10 +154,9 @@ class CustomTexture
        # The argument `color` should be an array of up to 4 floats (RGBA).
        # If `color` has less than 4 items, the missing items are replaced by 1.0.
        #
-       # Require: `not loaded and x < width.to_i and y < height.to_i`
+       # Require: `x < width.to_i and y < height.to_i`
        fun []=(x, y: Int, color: Array[Float])
        do
-               assert not loaded else print_error "{class_name}::[]= already loaded"
                assert x < width.to_i and y < height.to_i else print_error "{class_name}::[] out of bounds"
 
                # Simple conversion from [0.0..1.0] to [0..255]
@@ -166,18 +165,16 @@ class CustomTexture
 
                var offset = 4*(x + y*width.to_i)
                for i in [0..4[ do cpixels[offset+i] = bytes[i]
+
+               loaded = false
        end
 
        # Overwrite all pixels with `color`, return `self`
        #
        # The argument `color` should be an array of up to 4 floats (RGBA).
        # If `color` has less than 4 items, the missing items are replaced by 1.0.
-       #
-       # Require: `not loaded`
        fun fill(color: Array[Float]): SELF
        do
-               assert not loaded else print_error "{class_name}::fill already loaded"
-
                # Simple conversion from [0.0..1.0] to [0..255]
                var bytes = [for c in color do (c*255.0).round.to_i.clamp(0, 255).to_bytes.last]
                while bytes.length < 4 do bytes.add 255u8
@@ -190,12 +187,20 @@ class CustomTexture
                        end
                end
 
+               loaded = false
                return self
        end
 
        redef fun load(force)
        do
-               if loaded then return
+               force = force or else false
+               if loaded and not force then return
+
+               if force and glIsTexture(gl_texture) then
+                       # Was already loaded, free the previous GL name
+                       glDeleteTextures([gl_texture])
+               end
+               gl_texture = -1
 
                # Round down the desired dimension
                var width = width.to_i
@@ -205,7 +210,6 @@ class CustomTexture
 
                load_from_pixels(cpixels.native_array, width, height, gl_RGBA)
 
-               cpixels.destroy
                loaded = true
        end
 end
@@ -338,7 +342,7 @@ abstract class Subtexture
        # Parent texture, from which this texture was created
        var parent: Texture
 
-       redef var root = parent.root is lateinit
+       redef fun root do return parent.root
 
        redef fun load(force) do root.load(force)
 end
@@ -392,7 +396,7 @@ class TextureSet
 end
 
 redef class Pointer
-       # Multiply RBG values by their alpha value
+       # Multiply RGB values by their alpha value
        private fun premultiply_alpha(width, height: Int) `{
                uint8_t *bytes = (uint8_t *)self;
                int x, y, i = 0;
index c4ac331..b8e0bcb 100644 (file)
@@ -434,12 +434,27 @@ fun glUniform4f(index: Int, x, y, z, w: Float) `{ glUniform4f(index, x, y, z, w)
 
 # Low level array of `Float`
 class GLfloatArray
-       super CArray[Float]
-       redef type NATIVE: NativeGLfloatArray
+       super FinalizableOnce
 
-       redef init(length)
+       var length: Int
+
+       var native_array = new NativeGLfloatArray(length) is lateinit
+
+       fun [](index: Int): Float do return native_array[index]
+
+       fun []=(index: Int, val: Float) do native_array[index] = val
+
+       var add_index = 0
+
+       fun reset_add do add_index = 0
+
+       # Require: `add_index < length`
+       fun add(value: Float)
        do
-               native_array = new NativeGLfloatArray(length)
+               var index = add_index
+               assert index < length
+               native_array[index] = value
+               self.add_index = index + 1
        end
 
        # Create with the content of `array`
@@ -458,26 +473,27 @@ class GLfloatArray
        # Require: `length >= array.length + dst_offset or else 0`
        fun fill_from(array: Array[Float], dst_offset: nullable Int)
        do
-               dst_offset = dst_offset or else 0
+               dst_offset = dst_offset or else add_index
 
                assert length >= array.length + dst_offset
                for k in [0..array.length[ do
                        self[dst_offset+k] = array[k]
                end
        end
+
+       redef fun finalize_once do native_array.free
 end
 
 # An array of `GLfloat` in C (`GLfloat*`)
 extern class NativeGLfloatArray `{ GLfloat* `}
-       super NativeCArray
-       redef type E: Float
 
        new(size: Int) `{ return calloc(size, sizeof(GLfloat)); `}
 
-       redef fun [](index) `{ return self[index]; `}
-       redef fun []=(index, val) `{ self[index] = val; `}
+       fun [](index: Int): Float `{ return self[index]; `}
+
+       fun []=(index: Int, val: Float) `{ self[index] = val; `}
 
-       redef fun +(offset) `{ return self + offset; `}
+       fun +(offset: Int): NativeGLfloatArray `{ return self + offset; `}
 end
 
 # General type for OpenGL enumerations
index 5d9a89d..0c3acac 100644 (file)
@@ -52,8 +52,8 @@ class Matrix
 
                for j in height.times do assert items[j].length == width
 
-               for j in height.times do
-                       for i in width.times do
+               for j in [0..height[ do
+                       for i in [0..width[ do
                                self[j, i] = items[j][i]
                        end
                end
@@ -123,8 +123,8 @@ class Matrix
                assert size >= 0
 
                var matrix = new Matrix(size, size)
-               for i in size.times do
-                       for j in size.times do
+               for i in [0..size[ do
+                       for j in [0..size[ do
                                matrix[j, i] = if i == j then 1.0 else 0.0
                        end
                end
@@ -213,13 +213,7 @@ class Matrix
                assert self.width == other.height
 
                var out = new Matrix(other.width, self.height)
-               for j in self.height.times do
-                       for i in other.width.times do
-                               var sum = items[0].zero
-                               for k in self.width.times do sum += self[j, k] * other[k, i]
-                               out[j, i] = sum
-                       end
-               end
+               out.items.mul(items, other.items, self.width, self.height, other.width)
                return out
        end
 
@@ -335,4 +329,14 @@ private extern class NativeDoubleArray `{ double* `}
                        r = r * 3 / 2 + (long)(i*1024.0);
                return r;
        `}
+
+       fun mul(a, b: NativeDoubleArray, a_width, a_height, b_width: Int) `{
+               int i, j, k;
+               for (j = 0; j < a_height; j ++)
+                       for (i = 0; i < b_width; i ++) {
+                               float sum = 0.0;
+                               for (k = 0; k < a_width; k ++) sum += a[j*a_width + k] * b[k*b_width + i];
+                               self[j*b_width + i] = sum;
+                       }
+       `}
 end
index a9bf8b8..d42fa2a 100644 (file)
@@ -159,18 +159,69 @@ redef class Matrix
        #
        # This service aims to respect the world axes and logic of `gamnit`,
        # it may not correspond to all needs.
+       #
+       # The returned `Matrix` may be cached, it must not be modified.
        new gamnit_euler_rotation(pitch, yaw, roll: Float)
        do
+               if pitch == 0.0 and yaw == 0.0 and roll == 0.0 then
+                       return once new Matrix.identity(4)
+               end
+
+               if rotation_pitch == pitch and rotation_yaw == yaw and rotation_roll == roll then
+                       var rot = rotation_matrix_cache
+                       if rot != null then return rot
+               end
+
                var c1 = pitch.cos
                var s1 = pitch.sin
                var c2 = yaw.cos
                var s2 = yaw.sin
                var c3 = roll.cos
                var s3 = roll.sin
-               return new Matrix.from(
-                       [[          c2*c3,          -c2*s3,   -s2, 0.0],
-                        [ c1*s3+c3*s1*s2,  c1*c3-s1*s2*s3, c2*s1, 0.0],
-                        [-s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0],
-                        [            0.0,             0.0,   0.0, 1.0]])
+
+               var rot = new Matrix(4, 4)
+               rot.items.mat4_set(
+                                 c2*c3,          -c2*s3,   -s2, 0.0,
+                        c1*s3+c3*s1*s2,  c1*c3-s1*s2*s3, c2*s1, 0.0,
+                       -s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0,
+                                   0.0,             0.0,   0.0, 1.0)
+
+               rotation_matrix_cache = rot
+               rotation_pitch = pitch
+               rotation_yaw = yaw
+               rotation_roll = roll
+               return rot
        end
 end
+
+redef class NativeDoubleArray
+       fun mat4_set(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15: Float) `{
+               self[ 0] =  f0;
+               self[ 1] =  f1;
+               self[ 2] =  f2;
+               self[ 3] =  f3;
+
+               self[ 4] =  f4;
+               self[ 5] =  f5;
+               self[ 6] =  f6;
+               self[ 7] =  f7;
+
+               self[ 8] =  f8;
+               self[ 9] =  f9;
+               self[10] = f10;
+               self[11] = f11;
+
+               self[12] = f12;
+               self[13] = f13;
+               self[14] = f14;
+               self[15] = f15;
+       `}
+end
+
+redef class Sys
+
+       private var rotation_matrix_cache: nullable Matrix = null
+       private var rotation_pitch = 0.0
+       private var rotation_yaw = 0.0
+       private var rotation_roll = 0.0
+end
index b9e436a..61f0186 100644 (file)
@@ -60,9 +60,14 @@ class PerfMap
                return ts
        end
 
+       # Number of digits to the right of the decimal points in reports created by `to_s`
+       #
+       # Defaults to 4.
+       var precision = 4 is writable
+
        redef fun to_s
        do
-               var prec = 3
+               var prec = precision
 
                var table = new Map[String, Array[String]]
                for event, stats in self do
index a400619..33708b6 100644 (file)
@@ -44,7 +44,7 @@ void clock_gettime(clock_t clock_name, struct timespec *ts) {
 `}
 
 # Elapsed time representation.
-extern class Timespec `{struct timespec*`}
+private extern class Timespec `{struct timespec*`}
 
        # Init a new Timespec from `s` seconds and `ns` nanoseconds.
        new ( s, ns : Int ) `{
@@ -86,6 +86,18 @@ extern class Timespec `{struct timespec*`}
                return tv;
        `}
 
+       # Set `self` to `a` - `b`
+       fun minus(a, b: Timespec) `{
+               time_t s = a->tv_sec - b->tv_sec;
+               long ns = a->tv_nsec - b->tv_nsec;
+               if (ns < 0) {
+                       s -= 1;
+                       ns += 1000000000l;
+               }
+               self->tv_sec = s;
+               self->tv_nsec = ns;
+       `}
+
        # Number of whole seconds of elapsed time.
        fun sec : Int `{
                return self->tv_sec;
@@ -143,13 +155,15 @@ class Clock
        # TODO use less mallocs
 
        # Time at creation
-       protected var time_at_beginning = new Timespec.monotonic_now
+       private var time_at_beginning = new Timespec.monotonic_now
 
        # Time at last time a lapse method was called
-       protected var time_at_last_lapse = new Timespec.monotonic_now
+       private var time_at_last_lapse = new Timespec.monotonic_now
+
+       private var temp = new Timespec.monotonic_now
 
        # Smallest time frame reported by clock
-       fun resolution: Timespec `{
+       private fun resolution: Timespec `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
 #if defined(__MACH__) && !defined(CLOCK_REALTIME)
                clock_serv_t cclock;
@@ -169,40 +183,40 @@ class Clock
        # Seconds since the creation of this instance
        fun total: Float
        do
-               var now = new Timespec.monotonic_now
-               var diff = now - time_at_beginning
-               var r = diff.to_f
-               diff.free
-               now.free
-               return r
+               var now = temp
+               now.update
+               now.minus(now, time_at_beginning)
+               return now.to_f
        end
 
        # Seconds since the last call to `lapse`
        fun lapse: Float
        do
-               var nt = new Timespec.monotonic_now
-               var dt = nt - time_at_last_lapse
-               var r = dt.to_f
-               dt.free
-               time_at_last_lapse.free
-               time_at_last_lapse = nt
+               var time_at_last_lapse = time_at_last_lapse
+               var now = temp
+               now.update
+               time_at_last_lapse.minus(now, time_at_last_lapse)
+               var r = time_at_last_lapse.to_f
+
+               self.temp = time_at_last_lapse
+               self.time_at_last_lapse = now
+
                return r
        end
 
        # Seconds since the last call to `lapse`, without resetting the lapse counter
        fun peek_lapse: Float
        do
-               var nt = new Timespec.monotonic_now
-               var dt = nt - time_at_last_lapse
-               var r = dt.to_f
-               nt.free
-               dt.free
-               return r
+               var now = temp
+               now.update
+               now.minus(now, time_at_last_lapse)
+               return now.to_f
        end
 
        redef fun finalize_once
        do
                time_at_beginning.free
                time_at_last_lapse.free
+               temp.free
        end
 end