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
)
50 var program
= app
.versatile_program
56 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
57 program
.scale
.uniform actor
.scale
58 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
61 program
.coord
.array_enabled
= true
62 program
.coord
.array
(mesh
.vertices
, 3)
64 program
.normal
.array_enabled
= true
65 program
.normal
.array
(mesh
.normals
, 3)
68 program
.use_map_ambient
.uniform
false
69 program
.use_map_diffuse
.uniform
false
70 program
.use_map_specular
.uniform
false
71 program
.tex_coord
.array_enabled
= false
74 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
77 program
.camera
.uniform
(app
.world_camera
.position
.x
, app
.world_camera
.position
.y
, app
.world_camera
.position
.z
)
79 # Colors from the material
81 program
.ambient_color
.uniform
(ambient_color
[0]*a
, ambient_color
[1]*a
,
82 ambient_color
[2]*a
, ambient_color
[3]*a
)
83 program
.diffuse_color
.uniform
(diffuse_color
[0]*a
, diffuse_color
[1]*a
,
84 diffuse_color
[2]*a
, diffuse_color
[3]*a
)
85 program
.specular_color
.uniform
(specular_color
[0]*a
, specular_color
[1]*a
,
86 specular_color
[2]*a
, specular_color
[3]*a
)
89 if mesh
.indices
.is_empty
then
90 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
92 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
97 # Material with potential `diffuse_texture` and `specular_texture`
98 class TexturedMaterial
101 # Texture applied to the ambient_color
102 var ambient_texture
: nullable Texture = null is writable
104 # Texture applied to the diffuse color
105 var diffuse_texture
: nullable Texture = null is writable
107 # Texture applied to the specular color
108 var specular_texture
: nullable Texture = null is writable
111 private var normals_texture
: nullable Texture = null is writable
113 redef fun draw
(actor
, model
)
115 var mesh
= model
.mesh
117 var program
= app
.versatile_program
120 # One of the textures used, if any
121 var sample_used_texture
= null
123 var texture
= ambient_texture
124 if texture
!= null then
125 glActiveTexture gl_TEXTURE0
126 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
127 program
.use_map_ambient
.uniform
true
128 program
.map_ambient
.uniform
0
129 sample_used_texture
= texture
131 program
.use_map_ambient
.uniform
false
134 texture
= diffuse_texture
135 if texture
!= null then
136 glActiveTexture gl_TEXTURE1
137 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
138 program
.use_map_diffuse
.uniform
true
139 program
.map_diffuse
.uniform
1
140 sample_used_texture
= texture
142 program
.use_map_diffuse
.uniform
false
145 texture
= specular_texture
146 if texture
!= null then
147 glActiveTexture gl_TEXTURE2
148 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
149 program
.use_map_specular
.uniform
true
150 program
.map_specular
.uniform
2
151 sample_used_texture
= texture
153 program
.use_map_specular
.uniform
false
156 texture
= normals_texture
157 if texture
!= null then
158 glActiveTexture gl_TEXTURE3
159 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
160 program
.use_map_bump
.uniform
true
161 program
.map_bump
.uniform
3
162 sample_used_texture
= texture
164 program
.use_map_bump
.uniform
false
167 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
168 program
.scale
.uniform actor
.scale
170 # If using a texture, set `texture_coords`
171 program
.tex_coord
.array_enabled
= sample_used_texture
!= null
172 if sample_used_texture
!= null then
173 if sample_used_texture
isa RootTexture then
174 # Coordinates are directly valid
175 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
177 # Correlate texture coordinates from the substexture and the mesh.
178 # This is slow, but should be cached on the GPU.
179 var xa
= sample_used_texture
.offset_left
180 var xd
= sample_used_texture
.offset_right
- xa
181 var ya
= sample_used_texture
.offset_top
182 var yd
= sample_used_texture
.offset_bottom
- ya
186 var tex_coords
= new Array[Float].with_capacity
(mesh
.texture_coords
.length
)
187 for i
in [0..mesh
.texture_coords
.length
/2[ do
188 tex_coords
[i
*2] = xa
+ xd
* mesh
.texture_coords
[i
*2]
189 tex_coords
[i
*2+1] = 1.0 - (ya
+ yd
* mesh
.texture_coords
[i
*2+1])
192 program
.tex_coord
.array
(tex_coords
, 2)
196 program
.coord
.array_enabled
= true
197 program
.coord
.array
(mesh
.vertices
, 3)
199 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
202 program
.ambient_color
.uniform
(ambient_color
[0]*a
, ambient_color
[1]*a
,
203 ambient_color
[2]*a
, ambient_color
[3]*a
)
204 program
.diffuse_color
.uniform
(diffuse_color
[0]*a
, diffuse_color
[1]*a
,
205 diffuse_color
[2]*a
, diffuse_color
[3]*a
)
206 program
.specular_color
.uniform
(specular_color
[0]*a
, specular_color
[1]*a
,
207 specular_color
[2]*a
, specular_color
[3]*a
)
209 program
.normal
.array_enabled
= true
210 program
.normal
.array
(mesh
.normals
, 3)
212 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
213 program
.camera
.uniform
(app
.world_camera
.position
.x
, app
.world_camera
.position
.y
, app
.world_camera
.position
.z
)
215 if mesh
.indices
.is_empty
then
216 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
218 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
223 # Simple material using the normals of the surface as color
225 # Each axis composing the normals are translated to color values.
226 # This material is useful for debugging normals or display models in a colorful way.
227 class NormalsMaterial
230 redef fun draw
(actor
, model
)
232 var program
= app
.normals_program
234 program
.mvp
.uniform app
.world_camera
.mvp_matrix
236 var mesh
= model
.mesh
238 # TODO apply normal map
240 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
241 program
.scale
.uniform actor
.scale
243 program
.tex_coord
.array_enabled
= true
244 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
246 program
.coord
.array_enabled
= true
247 program
.coord
.array
(mesh
.vertices
, 3)
249 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
251 program
.normal
.array_enabled
= true
252 program
.normal
.array
(mesh
.normals
, 3)
254 if mesh
.indices
.is_empty
then
255 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
257 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
262 # Graphic program to display 3D models with Blinn-Phong specular lighting
263 class BlinnPhongProgram
264 super GamnitProgramFromSource
266 redef var vertex_shader_source
= """
267 // Vertex coordinates
268 attribute vec4 coord;
270 // Vertex translation
271 uniform vec4 translation;
276 // Vertex coordinates on textures
277 attribute vec2 tex_coord;
280 attribute vec3 normal;
282 // Model view projection matrix
285 uniform mat4 rotation;
288 uniform vec3 light_center;
290 // Coordinates of the camera
293 // Output for the fragment shader
294 varying vec2 v_tex_coord;
295 varying vec3 v_normal;
296 varying vec4 v_to_light;
297 varying vec4 v_to_camera;
301 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
302 gl_Position = pos * mvp;
304 // Pass varyings to the fragment shader
305 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
306 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
307 v_to_light = normalize(vec4(light_center, 1.0) - pos);
308 v_to_camera = normalize(vec4(camera, 1.0) - pos);
310 """ @ glsl_vertex_shader
312 redef var fragment_shader_source
= """
313 precision mediump float;
315 // Input from the vertex shader
316 varying vec2 v_tex_coord;
317 varying vec3 v_normal;
318 varying vec4 v_to_light;
319 varying vec4 v_to_camera;
322 uniform vec4 ambient_color;
323 uniform vec4 diffuse_color;
324 uniform vec4 specular_color;
327 uniform bool use_map_ambient;
328 uniform sampler2D map_ambient;
331 uniform bool use_map_diffuse;
332 uniform sampler2D map_diffuse;
335 uniform bool use_map_specular;
336 uniform sampler2D map_specular;
339 uniform bool use_map_bump;
340 uniform sampler2D map_bump;
343 uniform bool use_map_normal;
344 uniform sampler2D map_normal;
349 vec3 normal = v_normal;
352 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
356 vec4 ambient = ambient_color;
357 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
359 // Diffuse Lambert light
360 vec3 to_light = v_to_light.xyz;
361 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
363 vec4 diffuse = lambert * diffuse_color;
364 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
366 // Specular Phong light
369 vec3 l = reflect(-to_light, normal);
370 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
371 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
374 vec4 specular = s * specular_color;
375 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
377 gl_FragColor = ambient + diffuse + specular;
378 if (gl_FragColor.a < 0.01) discard;
380 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug
382 """ @ glsl_fragment_shader
384 # Vertices coordinates
385 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
387 # Should this program use the texture `map_ambient`?
388 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
390 # Ambient texture unit
391 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
393 # Should this program use the texture `map_diffuse`?
394 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
396 # Diffuser texture unit
397 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
399 # Should this program use the texture `map_specular`?
400 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
402 # Specularity texture unit
403 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
405 # Should this program use the texture `map_bump`?
406 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
409 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
412 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
414 # Coordinates on the textures, per vertex
415 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
418 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
421 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
424 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
426 # Center position of the light
427 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
430 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
432 # Translation applied to each vertex
433 var translation
= uniforms
["translation"].as(UniformVec4) is lazy
436 var rotation
= uniforms
["rotation"].as(UniformMat4) is lazy
439 var scale
= uniforms
["scale"].as(UniformFloat) is lazy
441 # Model view projection matrix
442 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
445 # Program to color objects from their normal vectors
447 # May be used in place of `BlinnPhongProgram` for debugging or effect.
449 super BlinnPhongProgram
451 redef var fragment_shader_source
= """
452 precision mediump float;
454 // Input from the vertex shader
455 varying vec3 v_normal;
459 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
461 """ @ glsl_fragment_shader
465 private var versatile_program
= new BlinnPhongProgram is lazy
467 private var normals_program
= new NormalProgram is lazy