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