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>
# 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
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);
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)
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)
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)
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
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
# ~~~
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`
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`
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
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
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
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
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
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)
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)
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;
// 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;
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;
// Point light (and others?)
v_to_light = normalize(vec4(light_center, 1.0) - pos);
}
+
+ v_alpha = alpha;
}
""" @ glsl_vertex_shader
varying vec4 v_to_light;
varying vec4 v_to_camera;
varying vec4 v_depth_pos;
+ varying float v_alpha;
// Colors
uniform vec4 ambient_color;
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);
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)
}
// 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;
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;
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
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
# 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
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
# 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
# ---
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
var pixels = surface.pixels
load_from_pixels(pixels, surface.w, surface.h, format)
+
+ surface.free
end
end
#
# 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
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`
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
# 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
#
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
# 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
# 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
# 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]
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
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
load_from_pixels(cpixels.native_array, width, height, gl_RGBA)
- cpixels.destroy
loaded = true
end
end
# 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
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;
# 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`
# 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
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
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
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
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
#
# 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
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
`}
# 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 ) `{
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;
# 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;
# 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