lib/core/stream: LineIterator use CachedIterator
[nit.git] / lib / gamnit / depth / shadow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Shadow mapping using a depth texture
16 #
17 # The default light does not cast any shadows. It can be changed to a
18 # `ParallelLight` in client games to cast sun-like shadows:
19 #
20 # ~~~
21 # import more_lights
22 #
23 # var sun = new ParallelLight
24 # sun.pitch = 0.25*pi
25 # sun.yaw = 0.25*pi
26 # app.light = sun
27 # ~~~
28 module shadow
29
30 intrude import gamnit::depth_core
31
32 redef class App
33
34 # Resolution of the shadow texture, defaults to 4096 pixels
35 #
36 # TODO make configurable / ask the hardware for gl_MAX_TEXTURE_SIZE
37 var shadow_resolution = 4096
38
39 # Are shadows supported by the current hardware configuration?
40 #
41 # The implementation may change in the future, but it currently relies on
42 # the GL extension `GL_EOS_depth_texture`.
43 var supports_shadows: Bool is lazy do
44 return display.as(not null).gl_extensions.has("GL_OES_depth_texture")
45 end
46
47 # Is `shadow_context.depth_texture` ready to be used?
48 fun shadow_depth_texture_available: Bool
49 do return supports_shadows and shadow_context.depth_texture != -1
50
51 private var shadow_depth_program = new ShadowDepthProgram
52
53 private var perf_clock_shadow = new Clock is lazy
54
55 redef fun create_gamnit
56 do
57 super
58
59 var program = shadow_depth_program
60 program.compile_and_link
61 var error = program.error
62 assert error == null else print_error error
63 end
64
65 private var shadow_context: ShadowContext = create_shadow_context is lazy
66
67 private fun create_shadow_context: ShadowContext
68 do
69 var display = display
70 assert display != null
71
72 var context = new ShadowContext
73 context.prepare_once(display, shadow_resolution)
74 return context
75 end
76
77 # Update the depth texture from the light point of view
78 #
79 # This method updates `shadow_context.depth_texture`.
80 protected fun frame_core_shadow_prep(display: GamnitDisplay)
81 do
82 if not supports_shadows then return
83
84 var light = app.light
85 if not light isa LightCastingShadows then return
86
87 # Make sure there's no errors pending
88 assert glGetError == gl_NO_ERROR
89
90 # Bind the framebuffer and make sure it is OK
91 glBindFramebuffer(gl_FRAMEBUFFER, shadow_context.light_view_framebuffer)
92 assert glGetError == gl_NO_ERROR
93 assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
94
95 # Draw to fill the depth texture and only the depth
96 glViewport(0, 0, shadow_resolution, shadow_resolution)
97 glColorMask(false, false, false, false)
98 glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
99 assert glGetError == gl_NO_ERROR
100
101 # Update light position
102 var camera = light.camera
103 camera.position.x = app.world_camera.position.x
104 camera.position.y = app.world_camera.position.y
105 camera.position.z = app.world_camera.position.z
106
107 # Draw all actors
108 for actor in actors do
109 for leaf in actor.model.leaves do
110 leaf.material.draw_depth(actor, leaf, camera)
111 end
112 end
113
114 # Take down, bring back default values
115 bind_screen_framebuffer shadow_context.screen_framebuffer
116 glColorMask(true, true, true, true)
117 end
118
119 # ---
120 # Debug: show light view in the bottom left of the screen
121
122 # Lazy load the debugging program
123 private var shadow_debug_program: LightPointOfViewProgram is lazy do
124 var program = new LightPointOfViewProgram
125 program.compile_and_link
126 var error = program.error
127 assert error == null else print_error error
128 return program
129 end
130
131 # Draw the light view in the bottom left of the screen, for debugging only
132 #
133 # The shadow depth texture is a square that can be deformed by this projection.
134 protected fun frame_core_shadow_debug(display: GamnitDisplay)
135 do
136 if not supports_shadows then
137 print_error "Error: Shadows are not supported by the current hardware configuration"
138 return
139 end
140
141 perf_clock_shadow.lapse
142
143 var program = shadow_debug_program
144
145 glBindBuffer(gl_ARRAY_BUFFER, shadow_context.buffer_array)
146 glViewport(0, 0, display.width/3, display.height/3)
147 glClear gl_DEPTH_BUFFER_BIT
148 program.use
149
150 # Uniforms
151 glActiveTexture gl_TEXTURE0
152 glBindTexture(gl_TEXTURE_2D, shadow_context.depth_texture)
153 program.texture.uniform 0
154
155 # Attributes
156 var sizeof_gl_float = 4
157 var n_floats = 3
158 glEnableVertexAttribArray program.coord.location
159 glVertexAttribPointeri(program.coord.location, n_floats, gl_FLOAT, false, 0, 0)
160 var offset = 4 * n_floats * sizeof_gl_float
161
162 n_floats = 2
163 glEnableVertexAttribArray program.tex_coord.location
164 glVertexAttribPointeri(program.tex_coord.location, n_floats, gl_FLOAT, false, 0, offset)
165 var gl_error = glGetError
166 assert gl_error == gl_NO_ERROR else print_error gl_error
167
168 # Draw
169 glDrawArrays(gl_TRIANGLE_STRIP, 0, 4)
170 gl_error = glGetError
171 assert gl_error == gl_NO_ERROR else print_error gl_error
172
173 # Take down
174 glBindBuffer(gl_ARRAY_BUFFER, 0)
175 gl_error = glGetError
176 assert gl_error == gl_NO_ERROR else print_error gl_error
177
178 sys.perfs["gamnit shadow debug"].add app.perf_clock_shadow.lapse
179 end
180 end
181
182 # Handles to reused GL buffers and texture
183 private class ShadowContext
184
185 # Real screen framebuffer
186 var screen_framebuffer: Int = -1
187
188 # Framebuffer for the light point of view
189 var light_view_framebuffer: Int = -1
190
191 # Depth attached to `light_view_framebuffer`
192 var depth_texture: Int = -1
193
194 # Buffer name for vertex data
195 var buffer_array: Int = -1
196
197 # Prepare all attributes once per resolution change
198 fun prepare_once(display: GamnitDisplay, shadow_resolution: Int)
199 do
200 assert display.gl_extensions.has("GL_OES_depth_texture")
201
202 # Set aside the real screen framebuffer name
203 var screen_framebuffer = glGetIntegerv(gl_FRAMEBUFFER_BINDING, 0)
204 self.screen_framebuffer = screen_framebuffer
205
206 # Framebuffer
207 var framebuffer = glGenFramebuffers(1).first
208 glBindFramebuffer(gl_FRAMEBUFFER, framebuffer)
209 assert glIsFramebuffer(framebuffer)
210 self.light_view_framebuffer = framebuffer
211 var gl_error = glGetError
212 assert gl_error == gl_NO_ERROR else print_error gl_error
213
214 # Depth & texture/color
215 var textures = glGenTextures(1)
216 self.depth_texture = textures[0]
217 gl_error = glGetError
218 assert gl_error == gl_NO_ERROR else print_error gl_error
219
220 resize(display, shadow_resolution)
221
222 # Array buffer
223 buffer_array = glGenBuffers(1).first
224 glBindBuffer(gl_ARRAY_BUFFER, buffer_array)
225 assert glIsBuffer(buffer_array)
226 gl_error = glGetError
227 assert gl_error == gl_NO_ERROR else print_error gl_error
228
229 ## coord
230 var data = new Array[Float]
231 data.add_all([-1.0, -1.0, 0.0,
232 1.0, -1.0, 0.0,
233 -1.0, 1.0, 0.0,
234 1.0, 1.0, 0.0])
235 ## tex_coord
236 data.add_all([0.0, 0.0,
237 1.0, 0.0,
238 0.0, 1.0,
239 1.0, 1.0])
240 var c_data = new GLfloatArray.from(data)
241 glBufferData(gl_ARRAY_BUFFER, data.length*4, c_data.native_array, gl_STATIC_DRAW)
242
243 glBindBuffer(gl_ARRAY_BUFFER, 0)
244
245 gl_error = glGetError
246 assert gl_error == gl_NO_ERROR else print_error gl_error
247 end
248
249 # Init size or resize `depth_texture`
250 fun resize(display: GamnitDisplay, shadow_resolution: Int)
251 do
252 glBindFramebuffer(gl_FRAMEBUFFER, light_view_framebuffer)
253 var gl_error = glGetError
254 assert gl_error == gl_NO_ERROR else print_error gl_error
255
256 # Depth texture
257 var depth_texture = self.depth_texture
258 glActiveTexture gl_TEXTURE0
259 glBindTexture(gl_TEXTURE_2D, depth_texture)
260 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
261 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
262 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
263 glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
264 gl_error = glGetError
265 assert gl_error == gl_NO_ERROR else print_error gl_error
266
267 # TODO support hardware shadows with GL ES 3.0 or GL_EXT_shadow_samplers
268 #glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_COMPARE_MODE, ...)
269
270 glTexImage2D(gl_TEXTURE_2D, 0, gl_DEPTH_COMPONENT,
271 shadow_resolution, shadow_resolution,
272 0, gl_DEPTH_COMPONENT, gl_UNSIGNED_SHORT, new Pointer.nul)
273 gl_error = glGetError
274 assert gl_error == gl_NO_ERROR else print_error gl_error
275
276 glFramebufferTexture2D(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_TEXTURE_2D, depth_texture, 0)
277 gl_error = glGetError
278 assert gl_error == gl_NO_ERROR else print_error gl_error
279
280 # Check if the framebuffer is complete and valid
281 assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
282
283 # Take down
284 glBindTexture(gl_TEXTURE_2D, 0)
285 glBindFramebuffer(gl_FRAMEBUFFER, 0)
286 gl_error = glGetError
287 assert gl_error == gl_NO_ERROR else print_error gl_error
288 end
289
290 var destroyed = false
291
292 fun destroy
293 do
294 if destroyed then return
295 destroyed = true
296
297 # Free the buffer
298 glDeleteBuffers([buffer_array])
299 var gl_error = glGetError
300 assert gl_error == gl_NO_ERROR else print_error gl_error
301 buffer_array = -1
302
303 # Free the array and framebuffer plus its attachments
304 glDeleteBuffers([buffer_array])
305 glDeleteFramebuffers([light_view_framebuffer])
306 glDeleteTextures([depth_texture])
307 end
308 end
309
310 redef class Material
311 # Optimized draw of `model`, a part of `actor`, from the view of `camera`
312 #
313 # This drawing should only produce usable depth data. The default behavior,
314 # uses `shadow_depth_program`.
315 protected fun draw_depth(actor: Actor, model: LeafModel, camera: Camera)
316 do
317 var program = app.shadow_depth_program
318 program.use
319 program.mvp.uniform camera.mvp_matrix
320
321 var mesh = model.mesh
322
323 program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
324 program.scale.uniform actor.scale
325 program.use_map_diffuse.uniform false
326
327 program.tex_coord.array_enabled = true
328 program.tex_coord.array(mesh.texture_coords, 2)
329
330 program.coord.array_enabled = true
331 program.coord.array(mesh.vertices, 3)
332
333 program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
334
335 if mesh.indices.is_empty then
336 glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
337 else
338 glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
339 end
340 end
341
342 end
343
344 # Efficiently draw actors from the light view
345 class ShadowDepthProgram
346 super GamnitProgramFromSource
347
348 redef var vertex_shader_source = """
349 // Vertex coordinates
350 attribute vec4 coord;
351
352 // Vertex translation
353 uniform vec4 translation;
354
355 // Vertex scaling
356 uniform float scale;
357
358 // Vertex coordinates on textures
359 attribute vec2 tex_coord;
360
361 // Vertex normal
362 attribute vec3 normal;
363
364 // Model view projection matrix
365 uniform mat4 mvp;
366
367 // Rotation matrix
368 uniform mat4 rotation;
369
370 // Output for the fragment shader
371 varying vec2 v_tex_coord;
372
373 void main()
374 {
375 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
376 gl_Position = pos * mvp;
377
378 // Pass varyings to the fragment shader
379 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
380 }
381 """ @ glsl_vertex_shader
382
383 redef var fragment_shader_source = """
384 precision mediump float;
385
386 // Diffuse map
387 uniform bool use_map_diffuse;
388 uniform sampler2D map_diffuse;
389
390 varying vec2 v_tex_coord;
391
392 void main()
393 {
394 if (use_map_diffuse && texture2D(map_diffuse, v_tex_coord).a <= 0.01) {
395 discard;
396 }
397 }
398 """ @ glsl_fragment_shader
399
400 # Vertices coordinates
401 var coord = attributes["coord"].as(AttributeVec4) is lazy
402
403 # Should this program use the texture `map_diffuse`?
404 var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
405
406 # Diffuse texture unit
407 var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
408
409 # Coordinates on the textures, per vertex
410 var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
411
412 # Diffuse color
413 var diffuse_color = uniforms["diffuse_color"].as(UniformVec4) is lazy
414
415 # Translation applied to each vertex
416 var translation = uniforms["translation"].as(UniformVec4) is lazy
417
418 # Rotation matrix
419 var rotation = uniforms["rotation"].as(UniformMat4) is lazy
420
421 # Scaling per vertex
422 var scale = uniforms["scale"].as(UniformFloat) is lazy
423
424 # Model view projection matrix
425 var mvp = uniforms["mvp"].as(UniformMat4) is lazy
426 end
427
428 # Draw the camera point of view on screen
429 private class LightPointOfViewProgram
430 super GamnitProgramFromSource
431
432 redef var vertex_shader_source = """
433 // Vertex coordinates
434 attribute vec3 coord;
435
436 // Vertex coordinates on textures
437 attribute vec2 tex_coord;
438
439 // Output to the fragment shader
440 varying vec2 v_coord;
441
442 void main()
443 {
444 gl_Position = vec4(coord, 1.0);
445 v_coord = tex_coord;
446 }
447 """ @ glsl_vertex_shader
448
449 redef var fragment_shader_source = """
450 precision mediump float;
451
452 // Virtual screen texture / color attachment
453 uniform sampler2D texture0;
454
455 // Input from the vertex shader
456 varying vec2 v_coord;
457
458 void main()
459 {
460 gl_FragColor = texture2D(texture0, v_coord);
461 }
462 """ @ glsl_fragment_shader
463
464 # Vertices coordinates
465 var coord = attributes["coord"].as(AttributeVec3) is lazy
466
467 # Coordinates on the textures, per vertex
468 var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
469
470 # Visible texture
471 var texture = uniforms["texture0"].as(UniformSampler2D) is lazy
472 end