1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Various material implementations
18 intrude import depth_core
22 # Get the default blueish material
23 new do return new SmoothMaterial(
29 # Simple material with static colors
33 # Ambient color, always visible
35 # The RGB values should be premultiplied by the alpha value.
36 var ambient_color
: Array[Float] is writable
38 # Diffuse color when covered by a light source
40 # The RGB values should be premultiplied by the alpha value.
41 var diffuse_color
: Array[Float] is writable
43 # Specular color affecting reflections
45 # The RGB values should be premultiplied by the alpha value.
46 var specular_color
: Array[Float] is writable
48 redef fun draw
(actor
, model
, camera
)
50 var program
= app
.versatile_program
52 program
.mvp
.uniform camera
.mvp_matrix
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
)
62 program
.coord
.array_enabled
= true
63 program
.coord
.array
(mesh
.vertices
, 3)
65 program
.normal
.array_enabled
= true
66 program
.normal
.array
(mesh
.normals
, 3)
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
75 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
78 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
80 # Colors from the material
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
)
90 if mesh
.indices
.is_empty
then
91 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
93 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
98 # Material with potential `diffuse_texture` and `specular_texture`
99 class TexturedMaterial
102 # Texture applied to the ambient_color
103 var ambient_texture
: nullable Texture = null is writable
105 # Texture applied to the diffuse color
106 var diffuse_texture
: nullable Texture = null is writable
108 # Texture applied to the specular color
109 var specular_texture
: nullable Texture = null is writable
112 private var normals_texture
: nullable Texture = null is writable
114 redef fun draw
(actor
, model
, camera
)
116 var mesh
= model
.mesh
118 var program
= app
.versatile_program
121 # One of the textures used, if any
122 var sample_used_texture
= null
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
132 program
.use_map_ambient
.uniform
false
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
143 program
.use_map_diffuse
.uniform
false
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
154 program
.use_map_specular
.uniform
false
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
165 program
.use_map_bump
.uniform
false
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
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)
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
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])
194 program
.tex_coord
.array
(tex_coords
, 2)
198 program
.coord
.array_enabled
= true
199 program
.coord
.array
(mesh
.vertices
, 3)
201 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
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
)
211 program
.normal
.array_enabled
= true
212 program
.normal
.array
(mesh
.normals
, 3)
214 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
217 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
219 if mesh
.indices
.is_empty
then
220 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
222 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
227 # Simple material using the normals of the surface as color
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
234 redef fun draw
(actor
, model
, camera
)
236 var program
= app
.normals_program
238 program
.mvp
.uniform camera
.mvp_matrix
240 var mesh
= model
.mesh
242 # TODO apply normal map
244 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
245 program
.scale
.uniform actor
.scale
247 program
.tex_coord
.array_enabled
= true
248 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
250 program
.coord
.array_enabled
= true
251 program
.coord
.array
(mesh
.vertices
, 3)
253 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
255 program
.normal
.array_enabled
= true
256 program
.normal
.array
(mesh
.normals
, 3)
258 if mesh
.indices
.is_empty
then
259 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
261 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
266 # Graphic program to display 3D models with Blinn-Phong specular lighting
267 class BlinnPhongProgram
268 super GamnitProgramFromSource
270 redef var vertex_shader_source
= """
271 // Vertex coordinates
272 attribute vec4 coord;
274 // Vertex translation
275 uniform vec4 translation;
280 // Vertex coordinates on textures
281 attribute vec2 tex_coord;
284 attribute vec3 normal;
286 // Model view projection matrix
289 uniform mat4 rotation;
292 uniform vec3 light_center;
294 // Coordinates of the camera
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;
305 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
306 gl_Position = pos * mvp;
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);
314 """ @ glsl_vertex_shader
316 redef var fragment_shader_source
= """
317 precision mediump float;
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;
326 uniform vec4 ambient_color;
327 uniform vec4 diffuse_color;
328 uniform vec4 specular_color;
331 uniform bool use_map_ambient;
332 uniform sampler2D map_ambient;
335 uniform bool use_map_diffuse;
336 uniform sampler2D map_diffuse;
339 uniform bool use_map_specular;
340 uniform sampler2D map_specular;
343 uniform bool use_map_bump;
344 uniform sampler2D map_bump;
347 uniform bool use_map_normal;
348 uniform sampler2D map_normal;
353 vec3 normal = v_normal;
356 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
360 vec4 ambient = ambient_color;
361 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
363 // Diffuse Lambert light
364 vec3 to_light = v_to_light.xyz;
365 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
367 vec4 diffuse = lambert * diffuse_color;
368 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
370 // Specular Phong light
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
378 vec4 specular = s * specular_color;
379 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
381 gl_FragColor = ambient + diffuse + specular;
382 if (gl_FragColor.a < 0.01) discard;
384 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug
386 """ @ glsl_fragment_shader
388 # Vertices coordinates
389 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
391 # Should this program use the texture `map_ambient`?
392 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
394 # Ambient texture unit
395 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
397 # Should this program use the texture `map_diffuse`?
398 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
400 # Diffuse texture unit
401 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
403 # Should this program use the texture `map_specular`?
404 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
406 # Specularity texture unit
407 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
409 # Should this program use the texture `map_bump`?
410 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
413 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
416 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
418 # Coordinates on the textures, per vertex
419 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
422 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
425 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
428 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
430 # Center position of the light
431 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
434 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
436 # Translation applied to each vertex
437 var translation
= uniforms
["translation"].as(UniformVec4) is lazy
440 var rotation
= uniforms
["rotation"].as(UniformMat4) is lazy
443 var scale
= uniforms
["scale"].as(UniformFloat) is lazy
445 # Model view projection matrix
446 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
449 # Program to color objects from their normal vectors
451 # May be used in place of `BlinnPhongProgram` for debugging or effect.
453 super BlinnPhongProgram
455 redef var fragment_shader_source
= """
456 precision mediump float;
458 // Input from the vertex shader
459 varying vec3 v_normal;
463 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
465 """ @ glsl_fragment_shader
469 private var versatile_program
= new BlinnPhongProgram is lazy
471 private var normals_program
= new NormalProgram is lazy