Class: Bestguigui::GLTFMath

Inherits:
Object
  • Object
show all
Defined in:
lib/bestguigui_lab/gltf_math.rb

Overview

Petite boite a outils matrices 4x4 / quaternions pour GLTFModel — matrices colonne-majeur (convention glTF). Jamais envoyees a la pile OpenGL (glLoadMatrixf) : uniquement du calcul CPU pour le skinning (voir GLTFPrimitive#skin!), donc aucune contrainte de convention avec le reste du kit (Render3D/ObjModel s'appuient sur glTranslatef/ glRotatef, jamais sur des matrices explicites). rubocop:disable Naming/MethodParameterName, Metrics/AbcSize, Style/ParallelAssignment -- noms mathematiques usuels (a, b, m, p, q, r, s, t, v...) et arithmetique vectorielle deroulee (pas de boucle a extraire) partout dans cette classe.

Class Method Summary collapse

Class Method Details

.eliminate_column(rows, inverse, col) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/bestguigui_lab/gltf_math.rb', line 135

def self.eliminate_column(rows, inverse, col)
    4.times do |row|
        next if row == col

        factor = rows[row][col]
        4.times do |k|
            rows[row][k] -= factor * rows[col][k]
            inverse[row][k] -= factor * inverse[col][k]
        end
    end
end

.identity ⇒ Object



14
15
16
17
18
19
20
21
# File 'lib/bestguigui_lab/gltf_math.rb', line 14

def self.identity
    [
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0
    ]
end

.invert(m) ⇒ Object

Inverse generale d'une matrice 4x4, par elimination de Gauss-Jordan — necessaire pour repasser de l'espace monde d'un noeud de maillage a son espace local (cf. GLTFSkeleton#joint_matrices). Rare dans la boucle de rendu (quelques appels par frame, pas par sommet) : la simplicite prime ici sur la performance. rubocop:disable-next Metrics/MethodLength -- élimination de Gauss-Jordan, ne se découpe pas proprement



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/bestguigui_lab/gltf_math.rb', line 113

def self.invert(m)
    rows = m.each_slice(4).to_a.transpose.map(&:dup)
    inverse = Array.new(4) { |i| Array.new(4) { |j| i == j ? 1.0 : 0.0 } }

    4.times do |col|
        pivot_row = (col...4).max_by { |r| rows[r][col].abs }
        rows[col], rows[pivot_row] = rows[pivot_row], rows[col]
        inverse[col], inverse[pivot_row] = inverse[pivot_row], inverse[col]

        pivot = rows[col][col]
        raise ArgumentError, 'matrice non inversible' if pivot.abs < 1e-9

        4.times do |k|
            rows[col][k] /= pivot
            inverse[col][k] /= pivot
        end
        eliminate_column(rows, inverse, col)
    end

    inverse.transpose.flatten
end

.matrix_from_trs(t, r, s) ⇒ Object

Compose T * R * S : t/s par defaut [0,0,0]/[1,1,1], r (quaternion [x, y, z, w]) par defaut la rotation identite.



25
26
27
28
29
30
31
# File 'lib/bestguigui_lab/gltf_math.rb', line 25

def self.matrix_from_trs(t, r, s)
    m = quat_to_matrix(r || [0.0, 0.0, 0.0, 1.0])
    scale = s || [1.0, 1.0, 1.0]
    (0..2).each { |col| (0..2).each { |row| m[(col * 4) + row] *= scale[col] } }
    m[12], m[13], m[14] = t || [0.0, 0.0, 0.0]
    m
end

.multiply(a, b) ⇒ Object

c = a * b (b applique en premier).



47
48
49
50
51
52
53
# File 'lib/bestguigui_lab/gltf_math.rb', line 47

def self.multiply(a, b)
    Array.new(16) do |i|
        col = i / 4
        row = i % 4
        (0..3).sum { |k| a[(k * 4) + row] * b[(col * 4) + k] }
    end
end

.negate(v) ⇒ Object



91
# File 'lib/bestguigui_lab/gltf_math.rb', line 91

def self.negate(v) = v.map(&:-@)

.normalize_quat(q) ⇒ Object



93
94
95
96
# File 'lib/bestguigui_lab/gltf_math.rb', line 93

def self.normalize_quat(q)
    length = Math.sqrt(q.sum { |c| c * c })
    q.map { |c| c / length }
end

.quat_slerp(a, b, t) ⇒ Object

Interpolation spherique entre deux quaternions [x, y, z, w] ; bascule sur un lerp normalise si l'angle est trop petit pour diviser dessus.



81
82
83
84
85
86
87
# File 'lib/bestguigui_lab/gltf_math.rb', line 81

def self.quat_slerp(a, b, t)
    dot = (0..3).sum { |i| a[i] * b[i] }
    b, dot = negate(b), -dot if dot.negative?
    return normalize_quat(vec4_lerp(a, b, t)) if dot > 0.9995

    slerp_by_angle(a, b, dot, t)
end

.quat_to_matrix(q) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bestguigui_lab/gltf_math.rb', line 33

def self.quat_to_matrix(q)
    x, y, z, w = q
    xx, yy, zz = x * x, y * y, z * z
    xy, xz, yz = x * y, x * z, y * z
    wx, wy, wz = w * x, w * y, w * z
    [
        1 - (2 * (yy + zz)), 2 * (xy + wz), 2 * (xz - wy), 0.0,
        2 * (xy - wz), 1 - (2 * (xx + zz)), 2 * (yz + wx), 0.0,
        2 * (xz + wy), 2 * (yz - wx), 1 - (2 * (xx + yy)), 0.0,
        0.0, 0.0, 0.0, 1.0
    ]
end

.slerp_by_angle(a, b, dot, t) ⇒ Object



98
99
100
101
102
103
104
105
# File 'lib/bestguigui_lab/gltf_math.rb', line 98

def self.slerp_by_angle(a, b, dot, t)
    theta0 = Math.acos(dot.clamp(-1.0, 1.0))
    sin_theta0 = Math.sin(theta0)
    theta = theta0 * t
    s0 = Math.cos(theta) - (dot * Math.sin(theta) / sin_theta0)
    s1 = Math.sin(theta) / sin_theta0
    (0..3).map { |i| (a[i] * s0) + (b[i] * s1) }
end

.transform_point(m, p) ⇒ Object



55
56
57
58
59
60
61
62
# File 'lib/bestguigui_lab/gltf_math.rb', line 55

def self.transform_point(m, p)
    x, y, z = p
    [
        (m[0] * x) + (m[4] * y) + (m[8] * z) + m[12],
        (m[1] * x) + (m[5] * y) + (m[9] * z) + m[13],
        (m[2] * x) + (m[6] * y) + (m[10] * z) + m[14]
    ]
end

.transform_vector(m, v) ⇒ Object

Comme transform_point, sans la translation — pour une normale. Simplification : ignore l'inverse-transposee (correcte pour une echelle uniforme, l'essentiel des rigs de jam ; peut legerement deformer une normale sous une echelle non uniforme sur un joint).



68
69
70
71
72
73
74
75
# File 'lib/bestguigui_lab/gltf_math.rb', line 68

def self.transform_vector(m, v)
    x, y, z = v
    [
        (m[0] * x) + (m[4] * y) + (m[8] * z),
        (m[1] * x) + (m[5] * y) + (m[9] * z),
        (m[2] * x) + (m[6] * y) + (m[10] * z)
    ]
end

.vec3_lerp(a, b, t) ⇒ Object



77
# File 'lib/bestguigui_lab/gltf_math.rb', line 77

def self.vec3_lerp(a, b, t) = (0..2).map { |i| a[i] + ((b[i] - a[i]) * t) }

.vec4_lerp(a, b, t) ⇒ Object



89
# File 'lib/bestguigui_lab/gltf_math.rb', line 89

def self.vec4_lerp(a, b, t) = (0..3).map { |i| a[i] + ((b[i] - a[i]) * t) }