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