bestguigui_jam_kit 0.1.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 +7 -0
- data/lib/gosu_jam_kit/assets.rb +31 -0
- data/lib/gosu_jam_kit/gl_texture.rb +42 -0
- data/lib/gosu_jam_kit/obj_model.rb +128 -0
- data/lib/gosu_jam_kit/render_3d.rb +45 -0
- data/lib/gosu_jam_kit/version.rb +5 -0
- data/lib/gosu_jam_kit.rb +15 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9eab78344f9a0d2b2fbef670ffa2549ccf9095f617c76aa4fb04e0bcf30cf45b
|
|
4
|
+
data.tar.gz: f719011fee460de08633a08350ffac7a9982720e3e342902bf3f8fd184104670
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6444d4704051d68a6c3f255b6a06ced4c5c70c18bc4701ff3b882798b96bc9deb3e3a77856d3b0c2e150cf53a8e0db8628c32abc7ba9e92c18e6eb3180e4c12b
|
|
7
|
+
data.tar.gz: 03c8e61a6632efa0a5d20a3ba88ef719046e8ea889073080bc39de908420191f4ce2428114589adf375284673a5ac20a14c600742f738551d0866864ed0294f9
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Charge et met en cache les assets (images, sons, modèles 3D...) pour éviter de les recharger plusieurs fois.
|
|
5
|
+
class Assets
|
|
6
|
+
@root = Dir.pwd
|
|
7
|
+
|
|
8
|
+
@images = Hash.new { |cache, path| cache[path] = Gosu::Image.new(path, retro: true) }
|
|
9
|
+
@sounds = Hash.new { |cache, path| cache[path] = Gosu::Sample.new(path) }
|
|
10
|
+
@songs = Hash.new { |cache, path| cache[path] = Gosu::Song.new(path) }
|
|
11
|
+
@fonts = Hash.new { |cache, key| cache[key] = Gosu::Font.new(key[1], name: key[0]) }
|
|
12
|
+
@models = Hash.new { |cache, path| cache[path] = ObjModel.new(path) }
|
|
13
|
+
@textures = Hash.new { |cache, path| cache[path] = GLTexture.new(path) }
|
|
14
|
+
|
|
15
|
+
# À fixer une fois au démarrage (ex. `Assets.root = __dir__` dans ta
|
|
16
|
+
# fenêtre principale) : dossier depuis lequel résoudre les chemins
|
|
17
|
+
# relatifs passés aux méthodes ci-dessous.
|
|
18
|
+
class << self
|
|
19
|
+
attr_writer :root
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.resolve(name) = File.expand_path(name, @root)
|
|
23
|
+
|
|
24
|
+
def self.image(name) = @images[resolve(name)]
|
|
25
|
+
def self.sound(name) = @sounds[resolve(name)]
|
|
26
|
+
def self.song(name) = @songs[resolve(name)]
|
|
27
|
+
def self.font(name, size = 20) = @fonts[[resolve(name), size]]
|
|
28
|
+
def self.model(name) = @models[resolve(name)]
|
|
29
|
+
def self.texture(name) = @textures[resolve(name)]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Texture OpenGL indépendante de l'atlas partagé de Gosu, construite à
|
|
5
|
+
# partir des pixels bruts (`to_blob`) — permet un vrai GL_REPEAT natif.
|
|
6
|
+
class GLTexture
|
|
7
|
+
attr_reader :tex_name
|
|
8
|
+
|
|
9
|
+
def initialize(path)
|
|
10
|
+
super()
|
|
11
|
+
image = Gosu::Image.new(path, retro: true)
|
|
12
|
+
@tex_name = upload(image)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def upload(image)
|
|
18
|
+
name_buffer = "\x00" * 4
|
|
19
|
+
glGenTextures(1, name_buffer)
|
|
20
|
+
tex_name = name_buffer.unpack1('L')
|
|
21
|
+
|
|
22
|
+
# Garder les pixels dans une variable (pas `image.to_blob` inline
|
|
23
|
+
# en argument) : sinon Ruby peut libérer la String pendant
|
|
24
|
+
# l'appel FFI, avant qu'OpenGL l'ait lue (texture à zéro, sans
|
|
25
|
+
# erreur OpenGL).
|
|
26
|
+
pixels = image.to_blob
|
|
27
|
+
|
|
28
|
+
glBindTexture(GL_TEXTURE_2D, tex_name)
|
|
29
|
+
configure_wrapping
|
|
30
|
+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
|
|
31
|
+
|
|
32
|
+
tex_name
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def configure_wrapping
|
|
36
|
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
|
|
37
|
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
|
|
38
|
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
|
|
39
|
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Charge un modèle .obj/.mtl et le dessine via un VBO : géométrie
|
|
5
|
+
class ObjModel
|
|
6
|
+
# position (3) + texcoord (2) + normale (3) par sommet.
|
|
7
|
+
FLOATS_PER_VERTEX = 8
|
|
8
|
+
STRIDE = FLOATS_PER_VERTEX * 4 # 4 octets par float
|
|
9
|
+
|
|
10
|
+
def initialize(obj_path)
|
|
11
|
+
super()
|
|
12
|
+
@vertices = []
|
|
13
|
+
@tex_coords = []
|
|
14
|
+
@normals = []
|
|
15
|
+
@faces = []
|
|
16
|
+
@texture = nil
|
|
17
|
+
|
|
18
|
+
parse_obj(obj_path)
|
|
19
|
+
@vertex_count = @faces.size
|
|
20
|
+
@vbo = build_vertex_buffer
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def draw
|
|
24
|
+
glEnable(GL_TEXTURE_2D)
|
|
25
|
+
glBindTexture(GL_TEXTURE_2D, @texture.tex_name)
|
|
26
|
+
glBindBuffer(GL_ARRAY_BUFFER, @vbo)
|
|
27
|
+
|
|
28
|
+
enable_vertex_arrays
|
|
29
|
+
glDrawArrays(GL_TRIANGLES, 0, @vertex_count)
|
|
30
|
+
disable_vertex_arrays
|
|
31
|
+
|
|
32
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
33
|
+
glDisable(GL_TEXTURE_2D)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def parse_obj(path)
|
|
39
|
+
directory = File.dirname(path)
|
|
40
|
+
|
|
41
|
+
File.foreach(path) do |line|
|
|
42
|
+
tokens = line.split
|
|
43
|
+
next if tokens.empty?
|
|
44
|
+
|
|
45
|
+
keyword = tokens.shift
|
|
46
|
+
parse_line(keyword, tokens, directory)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_line(keyword, tokens, directory)
|
|
51
|
+
case keyword
|
|
52
|
+
when 'v', 'vt', 'vn' then parse_coordinates(keyword, tokens)
|
|
53
|
+
when 'f' then @faces.concat(parse_face(tokens))
|
|
54
|
+
when 'mtllib' then load_material(File.join(directory, tokens.first))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parse_coordinates(keyword, tokens)
|
|
59
|
+
values = tokens.map(&:to_f)
|
|
60
|
+
|
|
61
|
+
case keyword
|
|
62
|
+
when 'v' then @vertices << values
|
|
63
|
+
when 'vt' then @tex_coords << values
|
|
64
|
+
when 'vn' then @normals << values
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# "f" contient des groupes "v/vt/vn" (indices 1-indexés, vt/vn
|
|
69
|
+
# parfois absents).
|
|
70
|
+
def parse_face(tokens)
|
|
71
|
+
tokens.map do |group|
|
|
72
|
+
v, vt, vn = group.split('/').map { |i| i.nil? || i.empty? ? nil : i.to_i - 1 }
|
|
73
|
+
[v, vt, vn]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Un seul matériau par modèle : la dernière texture (map_Kd) trouvée.
|
|
78
|
+
def load_material(path)
|
|
79
|
+
directory = File.dirname(path)
|
|
80
|
+
|
|
81
|
+
File.foreach(path) do |line|
|
|
82
|
+
tokens = line.split
|
|
83
|
+
next if tokens.empty? || tokens.shift != 'map_Kd'
|
|
84
|
+
|
|
85
|
+
@texture = GLTexture.new(File.join(directory, tokens.first))
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Buffer plat non indexé : les sommets partagés sont dupliqués, pas
|
|
90
|
+
# d'index buffer pour rester simple.
|
|
91
|
+
def build_vertex_buffer
|
|
92
|
+
data = @faces.flat_map { |v, vt, vn| vertex_attributes(v, vt, vn) }.pack('F*')
|
|
93
|
+
|
|
94
|
+
name_buffer = "\x00" * 4
|
|
95
|
+
glGenBuffers(1, name_buffer)
|
|
96
|
+
vbo = name_buffer.unpack1('L')
|
|
97
|
+
|
|
98
|
+
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
|
99
|
+
glBufferData(GL_ARRAY_BUFFER, data.bytesize, data, GL_STATIC_DRAW)
|
|
100
|
+
|
|
101
|
+
vbo
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def vertex_attributes(vertex_index, tex_coord_index, normal_index)
|
|
105
|
+
position = @vertices[vertex_index]
|
|
106
|
+
tex_coord = tex_coord_index ? @tex_coords[tex_coord_index] : [0.0, 0.0]
|
|
107
|
+
normal = normal_index ? @normals[normal_index] : [0.0, 0.0, 1.0]
|
|
108
|
+
|
|
109
|
+
position + tex_coord + normal
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def enable_vertex_arrays
|
|
113
|
+
glEnableClientState(GL_VERTEX_ARRAY)
|
|
114
|
+
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
|
|
115
|
+
glEnableClientState(GL_NORMAL_ARRAY)
|
|
116
|
+
|
|
117
|
+
glVertexPointer(3, GL_FLOAT, STRIDE, 0)
|
|
118
|
+
glTexCoordPointer(2, GL_FLOAT, STRIDE, 3 * 4)
|
|
119
|
+
glNormalPointer(GL_FLOAT, STRIDE, 5 * 4)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def disable_vertex_arrays
|
|
123
|
+
glDisableClientState(GL_NORMAL_ARRAY)
|
|
124
|
+
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
|
|
125
|
+
glDisableClientState(GL_VERTEX_ARRAY)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bestguigui
|
|
4
|
+
# Instancie-la une fois, garde une référence, utilise-la dans draw :
|
|
5
|
+
# @renderer = Render3D.new
|
|
6
|
+
# @renderer.render_3d(self) { @renderer.look_at(...) ; @model.draw }
|
|
7
|
+
class Render3D
|
|
8
|
+
# fov : angle de champ de vision vertical, en degrés.
|
|
9
|
+
# clip : [proche, lointain] — tout ce qui est en dehors n'est pas dessiné.
|
|
10
|
+
def render_3d(window, z_order: 0, fov: 60.0, clip: [0.1, 1000.0], &block)
|
|
11
|
+
w, h = window.fullscreen? ? [Gosu.screen_width, Gosu.screen_height] : [window.width, window.height]
|
|
12
|
+
Gosu.gl(z_order) { render_3d_frame(w, h, fov, clip, &block) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# À appeler dans le bloc de render_3d, avant de dessiner, pour
|
|
16
|
+
# positionner la caméra : eye = position, center = point regardé.
|
|
17
|
+
def look_at(eye, center, up_vector = [0, 1, 0])
|
|
18
|
+
gluLookAt(*eye, *center, *up_vector)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Gosu.gl isole déjà l'état (glPushAttrib/glPopAttrib) et remet sa
|
|
24
|
+
# propre projection/modelview en sortant du bloc — pas besoin de
|
|
25
|
+
# push/pop matrice de notre côté, juste de poser nos valeurs.
|
|
26
|
+
def render_3d_frame(width, height, fov, clip)
|
|
27
|
+
glEnable(GL_DEPTH_TEST)
|
|
28
|
+
glDepthFunc(GL_LESS)
|
|
29
|
+
|
|
30
|
+
setup_projection(width, height, fov, clip)
|
|
31
|
+
glMatrixMode(GL_MODELVIEW)
|
|
32
|
+
glLoadIdentity
|
|
33
|
+
|
|
34
|
+
yield
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def setup_projection(width, height, fov, clip)
|
|
38
|
+
near, far = clip
|
|
39
|
+
|
|
40
|
+
glMatrixMode(GL_PROJECTION)
|
|
41
|
+
glLoadIdentity
|
|
42
|
+
gluPerspective(fov, width.to_f / height, near, far)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/gosu_jam_kit.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'gosu'
|
|
4
|
+
require 'opengl'
|
|
5
|
+
require 'glu'
|
|
6
|
+
|
|
7
|
+
include OpenGL
|
|
8
|
+
include GLU
|
|
9
|
+
|
|
10
|
+
# Sans ça, les fonctions OpenGL au-delà de la version 1.1 plantent en
|
|
11
|
+
# silence (NoMethodError sur nil).
|
|
12
|
+
OpenGL.load_lib
|
|
13
|
+
GLU.load_lib
|
|
14
|
+
|
|
15
|
+
Dir[File.join(__dir__, 'gosu_jam_kit', '*.rb')].each { |fn| require_relative fn }
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bestguigui_jam_kit
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Bestguigui
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: gosu
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.4'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.4'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: opengl-bindings
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.6'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.6'
|
|
40
|
+
executables: []
|
|
41
|
+
extensions: []
|
|
42
|
+
extra_rdoc_files: []
|
|
43
|
+
files:
|
|
44
|
+
- lib/gosu_jam_kit.rb
|
|
45
|
+
- lib/gosu_jam_kit/assets.rb
|
|
46
|
+
- lib/gosu_jam_kit/gl_texture.rb
|
|
47
|
+
- lib/gosu_jam_kit/obj_model.rb
|
|
48
|
+
- lib/gosu_jam_kit/render_3d.rb
|
|
49
|
+
- lib/gosu_jam_kit/version.rb
|
|
50
|
+
homepage: https://github.com/guillaumequillet/bestguigui_jam_kit
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata:
|
|
54
|
+
rubygems_mfa_required: 'true'
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '3.0'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 4.0.16
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: A Game Jam Starter Kit to optimize Game Jams
|
|
72
|
+
test_files: []
|