983b0fc8836f3888087bc535322f41e572c39978
[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]
33
34 # Diffuse color when covered by a light source
35 var diffuse_color: Array[Float]
36
37 # Specular color affecting reflections
38 var specular_color: Array[Float]
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 var need_tex_coord = false
106
107 var texture = ambient_texture
108 if texture != null then
109 glActiveTexture gl_TEXTURE0
110 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
111 program.use_map_ambient.uniform true
112 program.map_ambient.uniform 0
113 need_tex_coord = true
114 else
115 program.use_map_ambient.uniform false
116 end
117
118 texture = diffuse_texture
119 if texture != null then
120 glActiveTexture gl_TEXTURE1
121 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
122 program.use_map_diffuse.uniform true
123 program.map_diffuse.uniform 1
124 need_tex_coord = true
125 else
126 program.use_map_diffuse.uniform false
127 end
128
129 texture = specular_texture
130 if texture != null then
131 glActiveTexture gl_TEXTURE2
132 glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
133 program.use_map_specular.uniform true
134 program.map_specular.uniform 2
135 need_tex_coord = true
136 else
137 program.use_map_specular.uniform false
138 end
139
140 program.translation.uniform(actor.center.x, -actor.center.y, actor.center.z, 0.0)
141 program.scale.uniform actor.scale
142
143 program.tex_coord.array_enabled = need_tex_coord
144 program.tex_coord.array(mesh.texture_coords, 2)
145
146 program.coord.array_enabled = true
147 program.coord.array(mesh.vertices, 3)
148 program.rotation.uniform new Matrix.rotation(actor.rotation, 0.0, 1.0, 0.0)
149
150 program.ambient_color.uniform(ambient_color[0], ambient_color[1], ambient_color[2], ambient_color[3]*actor.alpha)
151 program.diffuse_color.uniform(diffuse_color[0], diffuse_color[1], diffuse_color[2], diffuse_color[3]*actor.alpha)
152 program.specular_color.uniform(specular_color[0], specular_color[1], specular_color[2], specular_color[3]*actor.alpha)
153
154 program.normal.array_enabled = true
155 program.normal.array(mesh.normals, 3)
156
157 program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
158 program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
159
160 if mesh.indices.is_empty then
161 glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
162 else
163 glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
164 end
165 end
166 end
167
168 # Simple material using the normals of the surface as color
169 #
170 # Each axis composing the normals are translated to color values.
171 # This material is useful for debugging normals or display models in a colorful way.
172 class NormalsMaterial
173 super Material
174
175 redef fun draw(actor, model)
176 do
177 var program = app.normals_program
178 program.use
179 program.mvp.uniform app.world_camera.mvp_matrix
180
181 var mesh = model.mesh
182
183 # TODO apply normal map
184
185 program.translation.uniform(actor.center.x, -actor.center.y, actor.center.z, 0.0)
186 program.scale.uniform actor.scale
187
188 program.tex_coord.array_enabled = true
189 program.tex_coord.array(mesh.texture_coords, 2)
190
191 program.coord.array_enabled = true
192 program.coord.array(mesh.vertices, 3)
193 program.rotation.uniform new Matrix.rotation(actor.rotation, 0.0, 1.0, 0.0)
194
195 program.normal.array_enabled = true
196 program.normal.array(mesh.normals, 3)
197
198 if mesh.indices.is_empty then
199 glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
200 else
201 glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
202 end
203 end
204 end
205
206 # Graphic program to display 3D models with Lamber diffuse lighting
207 class LambertProgram
208 super GamnitProgramFromSource
209
210 redef var vertex_shader_source = """
211 // Vertex coordinates
212 attribute vec4 coord;
213
214 // Vertex translation
215 uniform vec4 translation;
216
217 // Vertex scaling
218 uniform float scale;
219
220 // Vertex coordinates on textures
221 attribute vec2 tex_coord;
222
223 // Vertex normal
224 attribute vec3 normal;
225
226 // Model view projection matrix
227 uniform mat4 mvp;
228
229 uniform mat4 rotation;
230
231 // Lights config
232 uniform vec3 light_center;
233
234 // Coordinates of the camera
235 uniform vec3 camera;
236
237 // Output for the fragment shader
238 varying vec2 v_tex_coord;
239 varying vec3 v_normal;
240 varying vec4 v_light_center;
241 varying vec4 v_camera;
242
243 void main()
244 {
245 // Pass varyings to the fragment shader
246 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
247 v_normal = normalize(vec4(normal, 0.0) * rotation * mvp).xyz;
248
249 gl_Position = (vec4(coord.xyz * scale, 1.0) * rotation + translation) * mvp;
250
251 // TODO compute v_light_center and v_camera on the CPU side and pass as uniforms
252 v_light_center = vec4(light_center, 0.0) * mvp;
253 v_camera = vec4(camera, 0.0) * mvp;
254 }
255 """ @ glsl_vertex_shader
256
257 redef var fragment_shader_source = """
258 precision mediump float;
259
260 // Input from the vertex shader
261 varying vec2 v_tex_coord;
262 varying vec3 v_normal;
263 varying vec4 v_light_center;
264 varying vec4 v_camera;
265
266 // Colors
267 uniform vec4 ambient_color;
268 uniform vec4 diffuse_color;
269 uniform vec4 specular_color;
270
271 // Ambient map
272 uniform bool use_map_ambient;
273 uniform sampler2D map_ambient;
274
275 // Diffuse map
276 uniform bool use_map_diffuse;
277 uniform sampler2D map_diffuse;
278
279 // Specular map
280 uniform bool use_map_specular;
281 uniform sampler2D map_specular;
282
283 // Bump map
284 uniform bool use_map_bump;
285 uniform sampler2D map_bump;
286
287 // Normal map
288 uniform bool use_map_normal;
289 uniform sampler2D map_normal;
290
291 void main()
292 {
293 // Lambert diffusion
294 vec3 light_dir = normalize(v_light_center.xyz);
295 float lambert = max(dot(light_dir, v_normal), 0.0);
296
297 if (use_map_ambient)
298 gl_FragColor = ambient_color + texture2D(map_ambient, v_tex_coord);
299 else
300 gl_FragColor = ambient_color;
301
302 if (use_map_diffuse)
303 gl_FragColor += lambert * diffuse_color * texture2D(map_diffuse, v_tex_coord);
304 else
305 gl_FragColor += lambert * diffuse_color;
306 }
307 """ @ glsl_fragment_shader
308
309 # Vertices coordinates
310 var coord = attributes["coord"].as(AttributeVec4) is lazy
311
312 # Should this program use the texture `map_ambient`?
313 var use_map_ambient = uniforms["use_map_ambient"].as(UniformBool) is lazy
314
315 # Ambient texture unit
316 var map_ambient = uniforms["map_ambient"].as(UniformSampler2D) is lazy
317
318 # Should this program use the texture `map_diffuse`?
319 var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
320
321 # Diffuser texture unit
322 var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
323
324 # Should this program use the texture `map_specular`?
325 var use_map_specular = uniforms["use_map_specular"].as(UniformBool) is lazy
326
327 # Specularity texture unit
328 var map_specular = uniforms["map_specular"].as(UniformSampler2D) is lazy
329
330 # Normal per vertex
331 var normal = attributes["normal"].as(AttributeVec3) is lazy
332
333 # Coordinates on the textures, per vertex
334 var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
335
336 # Ambient color
337 var ambient_color = uniforms["ambient_color"].as(UniformVec4) is lazy
338
339 # Diffuse color
340 var diffuse_color = uniforms["diffuse_color"].as(UniformVec4) is lazy
341
342 # Specular color
343 var specular_color = uniforms["specular_color"].as(UniformVec4) is lazy
344
345 # Center position of the light
346 var light_center = uniforms["light_center"].as(UniformVec3) is lazy
347
348 # Camera position
349 var camera = uniforms["camera"].as(UniformVec3) is lazy
350
351 # Translation applied to each vertex
352 var translation = uniforms["translation"].as(UniformVec4) is lazy
353
354 # Rotation matrix
355 var rotation = uniforms["rotation"].as(UniformMat4) is lazy
356
357 # Scaling per vertex
358 var scale = uniforms["scale"].as(UniformFloat) is lazy
359
360 # Model view projection matrix
361 var mvp = uniforms["mvp"].as(UniformMat4) is lazy
362 end
363
364 # Program to color objects from their normal vectors
365 class NormalProgram
366 super LambertProgram
367
368 redef var fragment_shader_source = """
369 precision mediump float;
370
371 // Input from the vertex shader
372 varying vec3 v_normal;
373
374 void main()
375 {
376 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
377 }
378 """ @ glsl_fragment_shader
379 end
380
381 redef class App
382 private var versatile_program = new LambertProgram is lazy
383
384 private var normals_program = new NormalProgram is lazy
385 end