9843b3771fa0ca7733645d8c7628d1d9610a9439
[nit.git] / lib / gamnit / depth / more_materials.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 # Various material implementations
16 module more_materials
17
18 intrude import depth_core
19 intrude import flat
20
21 redef class Material
22 # Get the default blueish material
23 new do return new SmoothMaterial(
24 [0.0, 0.0, 0.3, 1.0],
25 [0.0, 0.0, 0.6, 1.0],
26 [1.0, 1.0, 1.0, 1.0])
27 end
28
29 # Simple material with static colors
30 class SmoothMaterial
31 super Material
32
33 # Ambient color, always visible
34 #
35 # The RGB values should be premultiplied by the alpha value.
36 var ambient_color: Array[Float] is writable
37
38 # Diffuse color when covered by a light source
39 #
40 # The RGB values should be premultiplied by the alpha value.
41 var diffuse_color: Array[Float] is writable
42
43 # Specular color affecting reflections
44 #
45 # The RGB values should be premultiplied by the alpha value.
46 var specular_color: Array[Float] is writable
47
48 redef fun draw(actor, model, camera)
49 do
50 var program = app.versatile_program
51 program.use
52 program.mvp.uniform camera.mvp_matrix
53
54 var mesh = model.mesh
55
56 # Actor specs
57 program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
58 program.scale.uniform actor.scale
59 program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
60
61 # From mesh
62 program.coord.array_enabled = true
63 program.coord.array(mesh.vertices, 3)
64
65 program.normal.array_enabled = true
66 program.normal.array(mesh.normals, 3)
67
68 # No textures
69 program.use_map_ambient.uniform false
70 program.use_map_diffuse.uniform false
71 program.use_map_specular.uniform false
72 program.tex_coord.array_enabled = false
73
74 # Lights
75 program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
76
77 # Camera
78 program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
79
80 # Colors from the material
81 var a = actor.alpha
82 program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
83 ambient_color[2]*a, ambient_color[3]*a)
84 program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
85 diffuse_color[2]*a, diffuse_color[3]*a)
86 program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
87 specular_color[2]*a, specular_color[3]*a)
88
89 # Execute draw
90 if mesh.indices.is_empty then
91 glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
92 else
93 glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
94 end
95 end
96 end
97
98 # Material with potential `diffuse_texture` and `specular_texture`
99 class TexturedMaterial
100 super SmoothMaterial
101
102 # Texture applied to the ambient_color
103 var ambient_texture: nullable Texture = null is writable
104
105 # Texture applied to the diffuse color
106 var diffuse_texture: nullable Texture = null is writable
107
108 # Texture applied to the specular color
109 var specular_texture: nullable Texture = null is writable
110
111 # Bump map TODO
112 private var normals_texture: nullable Texture = null is writable
113
114 redef fun draw(actor, model, camera)
115 do
116 var mesh = model.mesh
117
118 var program = app.versatile_program
119 program.use
120
121 # One of the textures used, if any
122 var sample_used_texture = null
123
124 var texture = ambient_texture
125 if texture != null then
126 glActiveTexture gl_TEXTURE0
127 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
128 program.use_map_ambient.uniform true
129 program.map_ambient.uniform 0
130 sample_used_texture = texture
131 else
132 program.use_map_ambient.uniform false
133 end
134
135 texture = diffuse_texture
136 if texture != null then
137 glActiveTexture gl_TEXTURE1
138 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
139 program.use_map_diffuse.uniform true
140 program.map_diffuse.uniform 1
141 sample_used_texture = texture
142 else
143 program.use_map_diffuse.uniform false
144 end
145
146 texture = specular_texture
147 if texture != null then
148 glActiveTexture gl_TEXTURE2
149 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
150 program.use_map_specular.uniform true
151 program.map_specular.uniform 2
152 sample_used_texture = texture
153 else
154 program.use_map_specular.uniform false
155 end
156
157 texture = normals_texture
158 if texture != null then
159 glActiveTexture gl_TEXTURE3
160 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
161 program.use_map_bump.uniform true
162 program.map_bump.uniform 3
163 sample_used_texture = texture
164 else
165 program.use_map_bump.uniform false
166 end
167
168 program.mvp.uniform camera.mvp_matrix
169 program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
170 program.scale.uniform actor.scale
171
172 # If using a texture, set `texture_coords`
173 program.tex_coord.array_enabled = sample_used_texture != null
174 if sample_used_texture != null then
175 if sample_used_texture isa RootTexture then
176 # Coordinates are directly valid
177 program.tex_coord.array(mesh.texture_coords, 2)
178 else
179 # Correlate texture coordinates from the substexture and the mesh.
180 # This is slow, but should be cached on the GPU.
181 var xa = sample_used_texture.offset_left
182 var xd = sample_used_texture.offset_right - xa
183 var ya = sample_used_texture.offset_top
184 var yd = sample_used_texture.offset_bottom - ya
185 xd *= 0.999
186 yd *= 0.999
187
188 var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
189 for i in [0..mesh.texture_coords.length/2[ do
190 tex_coords[i*2] = xa + xd * mesh.texture_coords[i*2]
191 tex_coords[i*2+1] = 1.0 - (ya + yd * mesh.texture_coords[i*2+1])
192 end
193
194 program.tex_coord.array(tex_coords, 2)
195 end
196 end
197
198 program.coord.array_enabled = true
199 program.coord.array(mesh.vertices, 3)
200
201 program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
202
203 var a = actor.alpha
204 program.ambient_color.uniform(ambient_color[0]*a, ambient_color[1]*a,
205 ambient_color[2]*a, ambient_color[3]*a)
206 program.diffuse_color.uniform(diffuse_color[0]*a, diffuse_color[1]*a,
207 diffuse_color[2]*a, diffuse_color[3]*a)
208 program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
209 specular_color[2]*a, specular_color[3]*a)
210
211 program.normal.array_enabled = true
212 program.normal.array(mesh.normals, 3)
213
214 program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
215
216 # Camera
217 program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
218
219 if mesh.indices.is_empty then
220 glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
221 else
222 glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
223 end
224 end
225 end
226
227 # Simple material using the normals of the surface as color
228 #
229 # Each axis composing the normals are translated to color values.
230 # This material is useful for debugging normals or display models in a colorful way.
231 class NormalsMaterial
232 super Material
233
234 redef fun draw(actor, model, camera)
235 do
236 var program = app.normals_program
237 program.use
238 program.mvp.uniform camera.mvp_matrix
239
240 var mesh = model.mesh
241
242 # TODO apply normal map
243
244 program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
245 program.scale.uniform actor.scale
246
247 program.tex_coord.array_enabled = true
248 program.tex_coord.array(mesh.texture_coords, 2)
249
250 program.coord.array_enabled = true
251 program.coord.array(mesh.vertices, 3)
252
253 program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
254
255 program.normal.array_enabled = true
256 program.normal.array(mesh.normals, 3)
257
258 if mesh.indices.is_empty then
259 glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
260 else
261 glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
262 end
263 end
264 end
265
266 # Graphic program to display 3D models with Blinn-Phong specular lighting
267 class BlinnPhongProgram
268 super GamnitProgramFromSource
269
270 redef var vertex_shader_source = """
271 // Vertex coordinates
272 attribute vec4 coord;
273
274 // Vertex translation
275 uniform vec4 translation;
276
277 // Vertex scaling
278 uniform float scale;
279
280 // Vertex coordinates on textures
281 attribute vec2 tex_coord;
282
283 // Vertex normal
284 attribute vec3 normal;
285
286 // Model view projection matrix
287 uniform mat4 mvp;
288
289 uniform mat4 rotation;
290
291 // Lights config
292 uniform vec3 light_center;
293
294 // Coordinates of the camera
295 uniform vec3 camera;
296
297 // Output for the fragment shader
298 varying vec2 v_tex_coord;
299 varying vec3 v_normal;
300 varying vec4 v_to_light;
301 varying vec4 v_to_camera;
302
303 void main()
304 {
305 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
306 gl_Position = pos * mvp;
307
308 // Pass varyings to the fragment shader
309 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
310 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
311 v_to_light = normalize(vec4(light_center, 1.0) - pos);
312 v_to_camera = normalize(vec4(camera, 1.0) - pos);
313 }
314 """ @ glsl_vertex_shader
315
316 redef var fragment_shader_source = """
317 precision mediump float;
318
319 // Input from the vertex shader
320 varying vec2 v_tex_coord;
321 varying vec3 v_normal;
322 varying vec4 v_to_light;
323 varying vec4 v_to_camera;
324
325 // Colors
326 uniform vec4 ambient_color;
327 uniform vec4 diffuse_color;
328 uniform vec4 specular_color;
329
330 // Ambient map
331 uniform bool use_map_ambient;
332 uniform sampler2D map_ambient;
333
334 // Diffuse map
335 uniform bool use_map_diffuse;
336 uniform sampler2D map_diffuse;
337
338 // Specular map
339 uniform bool use_map_specular;
340 uniform sampler2D map_specular;
341
342 // Bump map
343 uniform bool use_map_bump;
344 uniform sampler2D map_bump;
345
346 // Normal map
347 uniform bool use_map_normal;
348 uniform sampler2D map_normal;
349
350 void main()
351 {
352 // Normal
353 vec3 normal = v_normal;
354 if (use_map_bump) {
355 // TODO
356 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
357 }
358
359 // Ambient light
360 vec4 ambient = ambient_color;
361 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
362
363 // Diffuse Lambert light
364 vec3 to_light = v_to_light.xyz;
365 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
366
367 vec4 diffuse = lambert * diffuse_color;
368 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
369
370 // Specular Phong light
371 float s = 0.0;
372 if (lambert > 0.0) {
373 vec3 l = reflect(-to_light, normal);
374 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
375 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
376 }
377
378 vec4 specular = s * specular_color;
379 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
380
381 gl_FragColor = ambient + diffuse + specular;
382 if (gl_FragColor.a < 0.01) discard;
383
384 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug
385 }
386 """ @ glsl_fragment_shader
387
388 # Vertices coordinates
389 var coord = attributes["coord"].as(AttributeVec4) is lazy
390
391 # Should this program use the texture `map_ambient`?
392 var use_map_ambient = uniforms["use_map_ambient"].as(UniformBool) is lazy
393
394 # Ambient texture unit
395 var map_ambient = uniforms["map_ambient"].as(UniformSampler2D) is lazy
396
397 # Should this program use the texture `map_diffuse`?
398 var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
399
400 # Diffuse texture unit
401 var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
402
403 # Should this program use the texture `map_specular`?
404 var use_map_specular = uniforms["use_map_specular"].as(UniformBool) is lazy
405
406 # Specularity texture unit
407 var map_specular = uniforms["map_specular"].as(UniformSampler2D) is lazy
408
409 # Should this program use the texture `map_bump`?
410 var use_map_bump = uniforms["use_map_bump"].as(UniformBool) is lazy
411
412 # Bump texture unit
413 var map_bump = uniforms["map_bump"].as(UniformSampler2D) is lazy
414
415 # Normal per vertex
416 var normal = attributes["normal"].as(AttributeVec3) is lazy
417
418 # Coordinates on the textures, per vertex
419 var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
420
421 # Ambient color
422 var ambient_color = uniforms["ambient_color"].as(UniformVec4) is lazy
423
424 # Diffuse color
425 var diffuse_color = uniforms["diffuse_color"].as(UniformVec4) is lazy
426
427 # Specular color
428 var specular_color = uniforms["specular_color"].as(UniformVec4) is lazy
429
430 # Center position of the light
431 var light_center = uniforms["light_center"].as(UniformVec3) is lazy
432
433 # Camera position
434 var camera = uniforms["camera"].as(UniformVec3) is lazy
435
436 # Translation applied to each vertex
437 var translation = uniforms["translation"].as(UniformVec4) is lazy
438
439 # Rotation matrix
440 var rotation = uniforms["rotation"].as(UniformMat4) is lazy
441
442 # Scaling per vertex
443 var scale = uniforms["scale"].as(UniformFloat) is lazy
444
445 # Model view projection matrix
446 var mvp = uniforms["mvp"].as(UniformMat4) is lazy
447 end
448
449 # Program to color objects from their normal vectors
450 #
451 # May be used in place of `BlinnPhongProgram` for debugging or effect.
452 class NormalProgram
453 super BlinnPhongProgram
454
455 redef var fragment_shader_source = """
456 precision mediump float;
457
458 // Input from the vertex shader
459 varying vec3 v_normal;
460
461 void main()
462 {
463 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
464 }
465 """ @ glsl_fragment_shader
466 end
467
468 redef class App
469 private var versatile_program = new BlinnPhongProgram is lazy
470
471 private var normals_program = new NormalProgram is lazy
472 end