Class: Bestguigui::Render3D::Camera
- Inherits:
-
Object
- Object
- Bestguigui::Render3D::Camera
- Includes:
- OpenGL
- Defined in:
- lib/bestguigui/render_3d/camera.rb
Overview
Caméra perspective en orbite autour d'une cible (angle horizontal + vertical + distance) — le classique d'une vue 3ème personne. Calcule ses matrices vue/projection à la main plutôt que de piloter la pile figée (gluPerspective/gluLookAt) : le pipeline moderne (shaders) n'a pas de pile de matrices globale, chaque objet reçoit les siennes en uniform.
@camera = Bestguigui::Render3D::Camera.new(app)
def update(dt)
@camera.update(@hero.x, @hero.y, @hero.z)
end
def draw
app.gl do
@camera.prepare_frame
@map.draw(@camera)
@hero.draw(@camera)
@camera.finish_frame
end
end
Instance Attribute Summary collapse
-
#angle ⇒ Object
readonly
Returns the value of attribute angle.
-
#angle_v ⇒ Object
readonly
Returns the value of attribute angle_v.
-
#projection_matrix ⇒ Object
readonly
Returns the value of attribute projection_matrix.
-
#view_matrix ⇒ Object
readonly
Returns the value of attribute view_matrix.
Instance Method Summary collapse
-
#finish_frame ⇒ Object
À appeler juste après avoir fini de dessiner en 3D, avant de rendre la main à Gosu (HUD, minimap, ou un tout autre state 2D à la frame suivante).
-
#frame(app) ⇒ Object
Encadre le rendu 3D d'une frame : ouvre le contexte GL de Gosu, prépare l'état GL (depth test), exécute le bloc, puis nettoie l'état avant de rendre la main à Gosu.
-
#initialize(app, distance: 128, fovy: 45, near: 1, far: 1000, angle: 90, angle_v: 14, height: 32) ⇒ Camera
constructor
A new instance of Camera.
-
#prepare_frame ⇒ Object
À appeler une fois par frame, en tout début de ton bloc `app.gl do ...
- #rotate(delta_angle: 0, delta_angle_v: 0) ⇒ Object
-
#update(target_x, target_y, target_z) ⇒ Object
Repositionne la caméra en orbite autour de (target_x, target_y, target_z) et recalcule vue/projection.
Constructor Details
#initialize(app, distance: 128, fovy: 45, near: 1, far: 1000, angle: 90, angle_v: 14, height: 32) ⇒ Camera
Returns a new instance of Camera.
29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/bestguigui/render_3d/camera.rb', line 29 def initialize(app, distance: 128, fovy: 45, near: 1, far: 1000, angle: 90, angle_v: 14, height: 32) @app = app @distance = distance @fovy = fovy @near = near @far = far @angle = angle @angle_v = angle_v @height = height @view_matrix = Mat4.identity @projection_matrix = Mat4.identity end |
Instance Attribute Details
#angle ⇒ Object (readonly)
Returns the value of attribute angle.
27 28 29 |
# File 'lib/bestguigui/render_3d/camera.rb', line 27 def angle @angle end |
#angle_v ⇒ Object (readonly)
Returns the value of attribute angle_v.
27 28 29 |
# File 'lib/bestguigui/render_3d/camera.rb', line 27 def angle_v @angle_v end |
#projection_matrix ⇒ Object (readonly)
Returns the value of attribute projection_matrix.
27 28 29 |
# File 'lib/bestguigui/render_3d/camera.rb', line 27 def projection_matrix @projection_matrix end |
#view_matrix ⇒ Object (readonly)
Returns the value of attribute view_matrix.
27 28 29 |
# File 'lib/bestguigui/render_3d/camera.rb', line 27 def view_matrix @view_matrix end |
Instance Method Details
#finish_frame ⇒ Object
À appeler juste après avoir fini de dessiner en 3D, avant de rendre la main à Gosu (HUD, minimap, ou un tout autre state 2D à la frame suivante). Sans ça, GL_DEPTH_TEST reste actif et surtout NOTRE shader/VAO restent liés (glUseProgram/glBindVertexArray) — Gosu dessinerait alors son propre 2D à travers notre shader (qui attend des uniforms de caméra 3D), donnant un écran noir. Inutile si tu utilises #frame, qui l'appelle pour toi.
104 105 106 107 108 |
# File 'lib/bestguigui/render_3d/camera.rb', line 104 def finish_frame glDisable(GL_DEPTH_TEST) glUseProgram(0) glBindVertexArray(0) end |
#frame(app) ⇒ Object
Encadre le rendu 3D d'une frame : ouvre le contexte GL de Gosu, prépare l'état GL (depth test), exécute le bloc, puis nettoie l'état avant de rendre la main à Gosu. Remplace le couple manuel prepare_frame/finish_frame — plus rien à oublier ni à déséquilibrer :
def draw
@camera.frame(app) do
@map.draw(@camera)
@hero.draw(@camera)
end
draw_hud # 2D, hors du bloc
end
78 79 80 81 82 83 84 |
# File 'lib/bestguigui/render_3d/camera.rb', line 78 def frame(app) app.gl do prepare_frame yield finish_frame end end |
#prepare_frame ⇒ Object
À appeler une fois par frame, en tout début de ton bloc app.gl do ... end, avant de dessiner quoi que ce soit en 3D. Active le test
de profondeur et vide le depth buffer — sans ça, l'ordre d'affichage
dépend juste de l'ordre des appels draw, pas de la vraie distance à
la caméra (objets qui s'affichent par-dessus d'autres censés être
devant). Inutile si tu utilises #frame, qui l'appelle pour toi.
92 93 94 95 |
# File 'lib/bestguigui/render_3d/camera.rb', line 92 def prepare_frame glEnable(GL_DEPTH_TEST) glClear(GL_DEPTH_BUFFER_BIT) end |
#rotate(delta_angle: 0, delta_angle_v: 0) ⇒ Object
61 62 63 64 |
# File 'lib/bestguigui/render_3d/camera.rb', line 61 def rotate(delta_angle: 0, delta_angle_v: 0) @angle += delta_angle @angle_v += delta_angle_v end |
#update(target_x, target_y, target_z) ⇒ Object
Repositionne la caméra en orbite autour de (target_x, target_y, target_z) et recalcule vue/projection.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/bestguigui/render_3d/camera.rb', line 45 def update(target_x, target_y, target_z) t_x = target_x t_y = target_y + @height t_z = target_z v = Math.cos(@angle_v * Math::PI / 180.0) x = t_x + (@distance * v * Math.cos(@angle * Math::PI / 180.0)) z = t_z + (@distance * v * Math.sin(@angle * Math::PI / 180.0)) y = t_y + (@distance * Math.sin(@angle_v * Math::PI / 180.0)) ratio = @app.fullscreen? ? Gosu.screen_width.to_f / Gosu.screen_height : @app.width.to_f / @app.height @view_matrix = Mat4.look_at([x, y, z], [t_x, t_y, t_z], [0, 1, 0]) @projection_matrix = Mat4.perspective(@fovy, ratio, @near, @far) end |