bestguigui_lab 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/bestguigui_lab/assets.rb +2 -2
- data/lib/bestguigui_lab/gl_buffer.rb +18 -0
- data/lib/bestguigui_lab/gouraud_shading.rb +43 -0
- data/lib/bestguigui_lab/obj_model.rb +36 -26
- data/lib/bestguigui_lab/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8ae9a4f1ed462793b85d3bc6c0307ca45958e7f4cfa47e47e8714709f397e28
|
|
4
|
+
data.tar.gz: cb1b88bb9b5fbf2e163de51e59e1bb3e787339782557a09f7194a4f5f04c610c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92b8120063fa68d38ee3146420f700afa121d18cb0b701df124cc69bd893fb1d9b18bb724ba816f4938aa27371530c1188037c911b9cd421047eee639ec1477d
|
|
7
|
+
data.tar.gz: 5eb225c89974c30a6a2cff05464d7f9b5204e9accebb5a8d7fa9a38020bff9b80ec8f987a18977ff6ceee20f62ab5b01762f802676b9de2191fd837e492af41f
|
|
@@ -10,7 +10,7 @@ module Bestguigui
|
|
|
10
10
|
@sounds = Hash.new { |cache, path| cache[path] = Gosu::Sample.new(path) }
|
|
11
11
|
@songs = Hash.new { |cache, path| cache[path] = Gosu::Song.new(path) }
|
|
12
12
|
@fonts = Hash.new { |cache, key| cache[key] = Gosu::Font.new(key[1], name: key[0]) }
|
|
13
|
-
@models = Hash.new { |cache,
|
|
13
|
+
@models = Hash.new { |cache, key| cache[key] = ObjModel.new(key[0], shading: key[1]) }
|
|
14
14
|
@textures = Hash.new { |cache, path| cache[path] = GLTexture.new(path) }
|
|
15
15
|
@frames = Hash.new { |cache, key| cache[key] = Gosu::Image.load_tiles(key[0], key[1], key[2], retro: true) }
|
|
16
16
|
|
|
@@ -28,7 +28,7 @@ module Bestguigui
|
|
|
28
28
|
@fonts[[path, size]]
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def self.model(name) = @models[resolve(name)]
|
|
31
|
+
def self.model(name, shading: :texture) = @models[[resolve(name), shading]]
|
|
32
32
|
def self.texture(name) = @textures[resolve(name)]
|
|
33
33
|
def self.frames(name, tile_width, tile_height) = @frames[[resolve(name), tile_width, tile_height]]
|
|
34
34
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Envoie un buffer de floats vers un VBO OpenGL (GL_ARRAY_BUFFER) et
|
|
5
|
+
# renvoie son nom — utilisé par ObjModel pour charger sa géométrie.
|
|
6
|
+
class GLBuffer
|
|
7
|
+
def self.upload(data)
|
|
8
|
+
name_buffer = "\x00" * 4
|
|
9
|
+
glGenBuffers(1, name_buffer)
|
|
10
|
+
vbo = name_buffer.unpack1('L')
|
|
11
|
+
|
|
12
|
+
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
|
13
|
+
glBufferData(GL_ARRAY_BUFFER, data.bytesize, data, GL_STATIC_DRAW)
|
|
14
|
+
|
|
15
|
+
vbo
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Calcule une couleur par sommet à partir de sa normale et d'une lumière
|
|
5
|
+
# fixe, quantifiée en peu de nuances par canal — utilisé par ObjModel en
|
|
6
|
+
# mode shading: :gouraud pour simuler, en plus de la texture (elle la
|
|
7
|
+
# module, ne la remplace pas), un rendu façon modèles de perso PSX
|
|
8
|
+
# (FF7 & co) : pas de lumière OpenGL temps réel, un effet "en bandes"
|
|
9
|
+
# plutôt qu'un dégradé lisse.
|
|
10
|
+
class GouraudShading
|
|
11
|
+
LIGHT_DIRECTION = [0.4, 0.8, 0.4].freeze # direction fixe
|
|
12
|
+
AMBIENT = 0.35 # part de lumière reçue même côté ombre
|
|
13
|
+
COLOR_LEVELS = 16 # teintes par canal : peu de nuances, façon banding PSX
|
|
14
|
+
|
|
15
|
+
def initialize(light_direction: LIGHT_DIRECTION, ambient: AMBIENT, color_levels: COLOR_LEVELS)
|
|
16
|
+
@light_direction = normalize(light_direction)
|
|
17
|
+
@ambient = ambient
|
|
18
|
+
@color_levels = color_levels
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def vertex_color(normal, material_color)
|
|
22
|
+
intensity = @ambient + ((1.0 - @ambient) * [dot(normal, @light_direction), 0.0].max)
|
|
23
|
+
material_color.map { |channel| quantize(channel * intensity) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def dot(vector_a, vector_b) = vector_a.zip(vector_b).sum { |x, y| x * y }
|
|
29
|
+
|
|
30
|
+
def normalize(vector)
|
|
31
|
+
length = Math.sqrt(vector.sum { |c| c * c })
|
|
32
|
+
vector.map { |c| c / length }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Réduit à COLOR_LEVELS nuances entre 0 et 1 : le dégradé de lumière
|
|
36
|
+
# devient visiblement "en bandes", comme la faible précision couleur
|
|
37
|
+
# de la PSX.
|
|
38
|
+
def quantize(value)
|
|
39
|
+
step = 1.0 / (@color_levels - 1)
|
|
40
|
+
(value.clamp(0.0, 1.0) / step).round * step
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Bestguigui
|
|
4
|
-
# Charge un modèle .obj/.mtl et le dessine via un VBO :
|
|
4
|
+
# Charge un modèle .obj/.mtl et le dessine via un VBO texturé. shading:
|
|
5
|
+
# :gouraud ajoute, en plus de la texture (ou d'un blanc si absente), une
|
|
6
|
+
# teinte par sommet calculée une fois au chargement à partir de sa
|
|
7
|
+
# normale et d'une lumière fixe (voir GouraudShading) : simule le rendu
|
|
8
|
+
# bas-poly des perso PSX (FF7 & co), sans lumière OpenGL temps réel.
|
|
5
9
|
class ObjModel
|
|
6
|
-
# position (3) + texcoord (2) + normale (3) par sommet.
|
|
10
|
+
# position (3) + texcoord (2) + normale ou teinte Gouraud (3) par sommet.
|
|
7
11
|
FLOATS_PER_VERTEX = 8
|
|
8
12
|
STRIDE = FLOATS_PER_VERTEX * 4 # 4 octets par float
|
|
9
13
|
|
|
10
|
-
def initialize(obj_path)
|
|
14
|
+
def initialize(obj_path, shading: :texture)
|
|
11
15
|
super()
|
|
12
|
-
@
|
|
13
|
-
|
|
14
|
-
@normals = []
|
|
15
|
-
@faces = []
|
|
16
|
+
@shading = shading
|
|
17
|
+
# rubocop:disable-next Style/ParallelAssignment -- reste sous la limite de Metrics/MethodLength
|
|
18
|
+
@vertices, @tex_coords, @normals, @faces = [], [], [], []
|
|
16
19
|
@texture = nil
|
|
17
|
-
|
|
20
|
+
@material_color = [1.0, 1.0, 1.0]
|
|
18
21
|
parse_obj(obj_path)
|
|
19
22
|
@texture ||= GLTexture.blank
|
|
20
23
|
@vertex_count = @faces.size
|
|
@@ -75,53 +78,60 @@ module Bestguigui
|
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
|
|
78
|
-
# Un seul matériau
|
|
81
|
+
# Un seul matériau : dernière texture (map_Kd) et couleur diffuse (Kd) trouvées, Kd sert de base au Gouraud.
|
|
79
82
|
def load_material(path)
|
|
80
83
|
directory = File.dirname(path)
|
|
81
84
|
|
|
82
85
|
File.foreach(path) do |line|
|
|
83
86
|
tokens = line.split
|
|
84
|
-
next if tokens.empty?
|
|
87
|
+
next if tokens.empty?
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
keyword = tokens.shift
|
|
90
|
+
case keyword
|
|
91
|
+
when 'Kd' then @material_color = tokens.map(&:to_f)
|
|
92
|
+
when 'map_Kd' then @texture = GLTexture.new(File.join(directory, tokens.first))
|
|
93
|
+
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
# Buffer plat non indexé : les sommets partagés sont dupliqués, pas
|
|
91
98
|
# d'index buffer pour rester simple.
|
|
92
99
|
def build_vertex_buffer
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
glGenBuffers(1, name_buffer)
|
|
97
|
-
vbo = name_buffer.unpack1('L')
|
|
98
|
-
|
|
99
|
-
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
|
100
|
-
glBufferData(GL_ARRAY_BUFFER, data.bytesize, data, GL_STATIC_DRAW)
|
|
101
|
-
|
|
102
|
-
vbo
|
|
100
|
+
gouraud = GouraudShading.new if @shading == :gouraud
|
|
101
|
+
data = @faces.flat_map { |v, vt, vn| vertex_attributes(v, vt, vn, gouraud) }.pack('F*')
|
|
102
|
+
GLBuffer.upload(data)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
# extra : la normale (mode :texture, inutilisée) ou une teinte Gouraud qui modulera la texture au dessin.
|
|
106
|
+
def vertex_attributes(vertex_index, tex_coord_index, normal_index, gouraud)
|
|
106
107
|
position = @vertices[vertex_index]
|
|
107
108
|
tex_coord = tex_coord_index ? @tex_coords[tex_coord_index] : [0.0, 0.0]
|
|
108
109
|
normal = normal_index ? @normals[normal_index] : [0.0, 0.0, 1.0]
|
|
110
|
+
extra = gouraud ? gouraud.vertex_color(normal, @material_color) : normal
|
|
109
111
|
|
|
110
|
-
position + tex_coord +
|
|
112
|
+
position + tex_coord + extra
|
|
111
113
|
end
|
|
112
114
|
|
|
113
115
|
def enable_vertex_arrays
|
|
114
116
|
glEnableClientState(GL_VERTEX_ARRAY)
|
|
115
117
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
|
|
116
|
-
glEnableClientState(GL_NORMAL_ARRAY)
|
|
117
|
-
|
|
118
118
|
glVertexPointer(3, GL_FLOAT, STRIDE, 0)
|
|
119
119
|
glTexCoordPointer(2, GL_FLOAT, STRIDE, 3 * 4)
|
|
120
|
+
@shading == :gouraud ? enable_color_array : enable_normal_array
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def enable_color_array
|
|
124
|
+
glEnableClientState(GL_COLOR_ARRAY)
|
|
125
|
+
glColorPointer(3, GL_FLOAT, STRIDE, 5 * 4)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def enable_normal_array
|
|
129
|
+
glEnableClientState(GL_NORMAL_ARRAY)
|
|
120
130
|
glNormalPointer(GL_FLOAT, STRIDE, 5 * 4)
|
|
121
131
|
end
|
|
122
132
|
|
|
123
133
|
def disable_vertex_arrays
|
|
124
|
-
glDisableClientState(GL_NORMAL_ARRAY)
|
|
134
|
+
glDisableClientState(@shading == :gouraud ? GL_COLOR_ARRAY : GL_NORMAL_ARRAY)
|
|
125
135
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
|
|
126
136
|
glDisableClientState(GL_VERTEX_ARRAY)
|
|
127
137
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bestguigui_lab
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bestguigui
|
|
@@ -45,7 +45,9 @@ files:
|
|
|
45
45
|
- lib/bestguigui_lab/animation.rb
|
|
46
46
|
- lib/bestguigui_lab/assets.rb
|
|
47
47
|
- lib/bestguigui_lab/assets/splash_jingle.wav
|
|
48
|
+
- lib/bestguigui_lab/gl_buffer.rb
|
|
48
49
|
- lib/bestguigui_lab/gl_texture.rb
|
|
50
|
+
- lib/bestguigui_lab/gouraud_shading.rb
|
|
49
51
|
- lib/bestguigui_lab/helpers.rb
|
|
50
52
|
- lib/bestguigui_lab/image_pixels.rb
|
|
51
53
|
- lib/bestguigui_lab/input.rb
|