quake-rb 0.1.0 → 0.2.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/README.md +136 -0
- data/bin/quake +18 -1
- data/lib/quake/bsp/reader.rb +241 -38
- data/lib/quake/bsp/types.rb +49 -5
- data/lib/quake/bsp/vis.rb +2 -137
- data/lib/quake/camera.rb +73 -16
- data/lib/quake/entity.rb +413 -25
- data/lib/quake/game/brush_entities.rb +1814 -65
- data/lib/quake/game/engine.rb +4376 -57
- data/lib/quake/game/item_pickups.rb +584 -33
- data/lib/quake/game/player_state.rb +518 -21
- data/lib/quake/mdl/reader.rb +88 -7
- data/lib/quake/mdl/types.rb +2 -2
- data/lib/quake/pak/reader.rb +9 -3
- data/lib/quake/palette.rb +3 -4
- data/lib/quake/physics/hull_trace.rb +77 -4
- data/lib/quake/physics/player.rb +409 -112
- data/lib/quake/renderer/anorm_dots.rb +554 -0
- data/lib/quake/renderer/gl_alias_model.rb +418 -69
- data/lib/quake/renderer/gl_brush_model.rb +129 -17
- data/lib/quake/renderer/gl_hud.rb +384 -31
- data/lib/quake/renderer/gl_lightmap.rb +224 -48
- data/lib/quake/renderer/gl_particles.rb +390 -50
- data/lib/quake/renderer/gl_sky.rb +83 -10
- data/lib/quake/renderer/gl_texture_manager.rb +38 -4
- data/lib/quake/renderer/gl_textured.rb +53 -31
- data/lib/quake/renderer/gl_view_blend.rb +130 -0
- data/lib/quake/renderer/gl_viewmodel.rb +46 -11
- data/lib/quake/renderer/gl_warp_subdivision.rb +74 -0
- data/lib/quake/renderer/gl_water.rb +4 -76
- data/lib/quake/sound/events.rb +126 -2
- data/lib/quake/sound/mixer.rb +44 -9
- data/lib/quake/version.rb +1 -1
- data/lib/quake/wad/reader.rb +18 -8
- data/lib/quake/window.rb +3 -0
- metadata +5 -1
|
@@ -8,6 +8,8 @@ module Quake
|
|
|
8
8
|
# Each brush entity references a model in the BSP models array (models[1], [2], etc.)
|
|
9
9
|
# and has a position/angle from its entity definition.
|
|
10
10
|
class GLBrushModel
|
|
11
|
+
BACKFACE_EPSILON = 0.01
|
|
12
|
+
|
|
11
13
|
def initialize(level, texture_manager, lightmap)
|
|
12
14
|
@level = level
|
|
13
15
|
@texture_manager = texture_manager
|
|
@@ -20,25 +22,30 @@ module Quake
|
|
|
20
22
|
|
|
21
23
|
# Render all brush entities at their current positions.
|
|
22
24
|
# entities: array of Entity objects that have model_index set
|
|
23
|
-
def render(entities)
|
|
25
|
+
def render(entities, view_origin: Math::Vec3::ORIGIN, time: 0.0, frustum: nil)
|
|
24
26
|
GL.Enable(GL::TEXTURE_2D)
|
|
25
27
|
GL.Color3f(1.0, 1.0, 1.0)
|
|
26
28
|
|
|
27
29
|
entities.each do |ent|
|
|
28
30
|
next unless ent.brush_entity?
|
|
31
|
+
next if ent.removed?
|
|
29
32
|
surfaces = @model_surfaces[ent.model_index]
|
|
30
33
|
next unless surfaces
|
|
34
|
+
next if frustum && culled_by_frustum?(ent, frustum)
|
|
31
35
|
|
|
32
36
|
GL.PushMatrix
|
|
33
37
|
GL.Translatef(ent.position.x, ent.position.y, ent.position.z)
|
|
34
38
|
|
|
35
|
-
if ent
|
|
36
|
-
GL.Rotatef(ent.angles.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if brush_model_rotated?(ent)
|
|
40
|
+
GL.Rotatef(ent.angles.y, 0.0, 0.0, 1.0)
|
|
41
|
+
# Net +pitch: R_DrawBrushModel negates pitch before
|
|
42
|
+
# R_RotateForEntity's -pitch rotation ("stupid quake bug")
|
|
43
|
+
GL.Rotatef(ent.angles.x, 0.0, 1.0, 0.0)
|
|
44
|
+
GL.Rotatef(ent.angles.z, 1.0, 0.0, 0.0)
|
|
39
45
|
end
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
modelorg = brush_model_modelorg(view_origin, ent)
|
|
48
|
+
render_model_surfaces(surfaces, modelorg, time: time, alternate: ent.frame.to_i.positive?)
|
|
42
49
|
|
|
43
50
|
GL.PopMatrix
|
|
44
51
|
end
|
|
@@ -70,7 +77,9 @@ module Quake
|
|
|
70
77
|
texcoords: surf.texcoords,
|
|
71
78
|
miptex_index: texinfo.miptex_index,
|
|
72
79
|
face_index: face_index,
|
|
73
|
-
texinfo_index: face.texinfo_index
|
|
80
|
+
texinfo_index: face.texinfo_index,
|
|
81
|
+
plane_index: face.plane_index,
|
|
82
|
+
flags: face.flags
|
|
74
83
|
}
|
|
75
84
|
end
|
|
76
85
|
|
|
@@ -81,16 +90,18 @@ module Quake
|
|
|
81
90
|
puts "Precomputed #{total} brush model surfaces across #{@model_surfaces.size} sub-models"
|
|
82
91
|
end
|
|
83
92
|
|
|
84
|
-
def render_model_surfaces(surfaces)
|
|
93
|
+
def render_model_surfaces(surfaces, modelorg, time:, alternate:)
|
|
94
|
+
surfaces = surfaces.select { |surf| brush_surface_visible?(surf, modelorg) }
|
|
95
|
+
|
|
85
96
|
# Group by texture for fewer bind calls
|
|
86
97
|
current_miptex = -1
|
|
87
98
|
|
|
88
99
|
if @lightmap
|
|
89
|
-
render_surfaces_with_lightmaps(surfaces)
|
|
100
|
+
render_surfaces_with_lightmaps(surfaces, time: time, alternate: alternate)
|
|
90
101
|
else
|
|
91
102
|
surfaces.each do |surf|
|
|
92
103
|
if surf[:miptex_index] != current_miptex
|
|
93
|
-
@texture_manager.bind(surf[:miptex_index])
|
|
104
|
+
@texture_manager.bind(surf[:miptex_index], time: time, alternate: alternate)
|
|
94
105
|
current_miptex = surf[:miptex_index]
|
|
95
106
|
end
|
|
96
107
|
|
|
@@ -105,15 +116,26 @@ module Quake
|
|
|
105
116
|
end
|
|
106
117
|
end
|
|
107
118
|
|
|
108
|
-
def render_surfaces_with_lightmaps(surfaces)
|
|
119
|
+
def render_surfaces_with_lightmaps(surfaces, time:, alternate:)
|
|
109
120
|
# Pass 1: diffuse textures
|
|
110
121
|
GL.TexEnvi(GL::TEXTURE_ENV, GL::TEXTURE_ENV_MODE, GL::REPLACE)
|
|
111
122
|
current_miptex = -1
|
|
112
123
|
|
|
124
|
+
fence = false
|
|
113
125
|
surfaces.each do |surf|
|
|
114
126
|
if surf[:miptex_index] != current_miptex
|
|
115
|
-
@texture_manager.bind(surf[:miptex_index])
|
|
127
|
+
@texture_manager.bind(surf[:miptex_index], time: time, alternate: alternate)
|
|
116
128
|
current_miptex = surf[:miptex_index]
|
|
129
|
+
surf_fence = @texture_manager.fence?(current_miptex)
|
|
130
|
+
if surf_fence != fence
|
|
131
|
+
fence = surf_fence
|
|
132
|
+
if fence
|
|
133
|
+
GL.AlphaFunc(GL::GREATER, 0.666)
|
|
134
|
+
GL.Enable(GL::ALPHA_TEST)
|
|
135
|
+
else
|
|
136
|
+
GL.Disable(GL::ALPHA_TEST)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
117
139
|
end
|
|
118
140
|
|
|
119
141
|
GL.Begin(GL::TRIANGLE_FAN)
|
|
@@ -124,15 +146,18 @@ module Quake
|
|
|
124
146
|
end
|
|
125
147
|
GL.End
|
|
126
148
|
end
|
|
149
|
+
GL.Disable(GL::ALPHA_TEST) if fence
|
|
127
150
|
|
|
128
151
|
# Pass 2: lightmap multiply
|
|
129
152
|
GL.Enable(GL::BLEND)
|
|
130
|
-
GL.BlendFunc(GL::
|
|
153
|
+
GL.BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA)
|
|
131
154
|
GL.DepthMask(GL::FALSE)
|
|
132
|
-
GL.DepthFunc(GL::LEQUAL)
|
|
133
155
|
|
|
134
156
|
last_lm_tex = -1
|
|
135
157
|
surfaces.each do |surf|
|
|
158
|
+
# Fences skip the light pass -- holes carry no pass-1 depth/diffuse
|
|
159
|
+
next if @texture_manager.fence?(surf[:miptex_index])
|
|
160
|
+
|
|
136
161
|
face_idx = surf[:face_index]
|
|
137
162
|
next unless @lightmap.face_lightmaps[face_idx]
|
|
138
163
|
|
|
@@ -142,10 +167,13 @@ module Quake
|
|
|
142
167
|
last_lm_tex = lm_info.gl_texture
|
|
143
168
|
end
|
|
144
169
|
|
|
145
|
-
|
|
170
|
+
lm_coords = surf[:lm_texcoords] ||= begin
|
|
171
|
+
texinfo = @level.texinfo[surf[:texinfo_index]]
|
|
172
|
+
surf[:vertices].map { |v| @lightmap.lightmap_texcoords(face_idx, v, texinfo) }
|
|
173
|
+
end
|
|
146
174
|
GL.Begin(GL::TRIANGLE_FAN)
|
|
147
175
|
surf[:vertices].each_with_index do |v, i|
|
|
148
|
-
ls, lt =
|
|
176
|
+
ls, lt = lm_coords[i]
|
|
149
177
|
GL.TexCoord2f(ls, lt)
|
|
150
178
|
GL.Vertex3f(v.x, v.y, v.z)
|
|
151
179
|
end
|
|
@@ -153,10 +181,94 @@ module Quake
|
|
|
153
181
|
end
|
|
154
182
|
|
|
155
183
|
GL.DepthMask(GL::TRUE)
|
|
156
|
-
GL.DepthFunc(GL::LESS)
|
|
157
184
|
GL.Disable(GL::BLEND)
|
|
158
185
|
GL.TexEnvi(GL::TEXTURE_ENV, GL::TEXTURE_ENV_MODE, GL::MODULATE)
|
|
159
186
|
end
|
|
187
|
+
|
|
188
|
+
# R_CullBox against the view frustum (R_DrawBrushModel). Rotated
|
|
189
|
+
# entities use a conservative radius box like the C reference.
|
|
190
|
+
def culled_by_frustum?(ent, frustum)
|
|
191
|
+
model = @level.models[ent.model_index]
|
|
192
|
+
return false unless model&.mins && model.maxs
|
|
193
|
+
|
|
194
|
+
if brush_model_rotated?(ent)
|
|
195
|
+
radius = radius_from_bounds(model.mins, model.maxs)
|
|
196
|
+
extent = Math::Vec3.new(radius, radius, radius)
|
|
197
|
+
mins = ent.position - extent
|
|
198
|
+
maxs = ent.position + extent
|
|
199
|
+
else
|
|
200
|
+
mins = ent.position + model.mins
|
|
201
|
+
maxs = ent.position + model.maxs
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
cull_box?(mins, maxs, frustum)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def cull_box?(mins, maxs, frustum)
|
|
208
|
+
frustum.any? do |normal, dist|
|
|
209
|
+
px = normal.x >= 0 ? maxs.x : mins.x
|
|
210
|
+
py = normal.y >= 0 ? maxs.y : mins.y
|
|
211
|
+
pz = normal.z >= 0 ? maxs.z : mins.z
|
|
212
|
+
(normal.x * px) + (normal.y * py) + (normal.z * pz) - dist < 0
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def radius_from_bounds(mins, maxs)
|
|
217
|
+
x = [mins.x.abs, maxs.x.abs].max
|
|
218
|
+
y = [mins.y.abs, maxs.y.abs].max
|
|
219
|
+
z = [mins.z.abs, maxs.z.abs].max
|
|
220
|
+
::Math.sqrt((x * x) + (y * y) + (z * z))
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def brush_surface_visible?(surf, modelorg)
|
|
224
|
+
plane = @level.planes[surf[:plane_index]]
|
|
225
|
+
return true unless plane
|
|
226
|
+
|
|
227
|
+
dot = modelorg.dot(plane.normal) - plane.dist
|
|
228
|
+
if (surf[:flags].to_i & Bsp::Face::SURF_PLANEBACK) != 0
|
|
229
|
+
dot < -BACKFACE_EPSILON
|
|
230
|
+
else
|
|
231
|
+
dot > BACKFACE_EPSILON
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def brush_model_rotated?(ent)
|
|
236
|
+
ent.angles != Math::Vec3::ORIGIN
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def brush_model_modelorg(view_origin, ent)
|
|
240
|
+
modelorg = view_origin - ent.position
|
|
241
|
+
return modelorg unless brush_model_rotated?(ent)
|
|
242
|
+
|
|
243
|
+
forward, right, up = quake_angle_vectors(ent.angles)
|
|
244
|
+
Math::Vec3.new(
|
|
245
|
+
modelorg.dot(forward),
|
|
246
|
+
-modelorg.dot(right),
|
|
247
|
+
modelorg.dot(up)
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def quake_angle_vectors(angles)
|
|
252
|
+
sy = ::Math.sin(deg2rad(angles.y))
|
|
253
|
+
cy = ::Math.cos(deg2rad(angles.y))
|
|
254
|
+
sp = ::Math.sin(deg2rad(angles.x))
|
|
255
|
+
cp = ::Math.cos(deg2rad(angles.x))
|
|
256
|
+
sr = ::Math.sin(deg2rad(angles.z))
|
|
257
|
+
cr = ::Math.cos(deg2rad(angles.z))
|
|
258
|
+
|
|
259
|
+
forward = Math::Vec3.new(cp * cy, cp * sy, -sp)
|
|
260
|
+
right = Math::Vec3.new((-sr * sp * cy) + (cr * sy),
|
|
261
|
+
(-sr * sp * sy) - (cr * cy),
|
|
262
|
+
-sr * cp)
|
|
263
|
+
up = Math::Vec3.new((cr * sp * cy) + (sr * sy),
|
|
264
|
+
(cr * sp * sy) - (sr * cy),
|
|
265
|
+
cr * cp)
|
|
266
|
+
[forward, right, up]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def deg2rad(deg)
|
|
270
|
+
deg * ::Math::PI / 180.0
|
|
271
|
+
end
|
|
160
272
|
end
|
|
161
273
|
end
|
|
162
274
|
end
|