OpenGL: more work on fixed function lighting implementation as GLSL.
[blender.git] / source / blender / gpu / intern / gpu_simple_shader.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2013 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Brecht Van Lommel.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/gpu/intern/gpu_simple_shader.c
29  *  \ingroup gpu
30  */
31
32 /* GLSL shaders to replace fixed function OpenGL materials and lighting. These
33  * are deprecated in newer OpenGL versions and missing in OpenGL ES 2.0. Also,
34  * two sided lighting is no longer natively supported on NVidia cards which
35  * results in slow software fallback.
36  *
37  * Todo:
38  * - Replace glLight and glMaterial functions entirely with GLSL uniforms, to
39  *   make OpenGL ES 2.0 work.
40  * - Replace glTexCoord and glColor with generic attributes.
41  * - Optimize for case where fewer than 3 or 8 lights are used.
42  * - Optimize for case where specular is not used.
43  * - Optimize for case where no texture matrix is used.
44  */
45
46 #include "GL/glew.h"
47
48 #include "BLI_math.h"
49 #include "BLI_utildefines.h"
50
51 #include "DNA_mesh_types.h"
52 #include "DNA_object_types.h"
53
54 #include "GPU_extensions.h"
55 #include "GPU_simple_shader.h"
56
57 /* State */
58
59 #define NUM_OPENGL_LIGHTS 8
60
61 static struct {
62         GPUShader *cached_shaders[GPU_SHADER_OPTION_COMBINATIONS];
63         bool failed_shaders[GPU_SHADER_OPTION_COMBINATIONS];
64
65         bool need_normals;
66
67         int lights_enabled;
68         int lights_directional;
69 } GPU_MATERIAL_STATE;
70
71 /* Init / exit */
72
73 void GPU_simple_shaders_init()
74 {
75         memset(&GPU_MATERIAL_STATE, 0, sizeof(GPU_MATERIAL_STATE));
76 }
77
78 void GPU_simple_shaders_exit()
79 {
80         int i;
81         
82         for (i = 0; i < GPU_SHADER_OPTION_COMBINATIONS; i++)
83                 if (GPU_MATERIAL_STATE.cached_shaders[i])
84                         GPU_shader_free(GPU_MATERIAL_STATE.cached_shaders[i]);
85 }
86
87 /* Shader lookup / create */
88
89 static bool solid_compatible_lighting(void)
90 {
91         int enabled = GPU_MATERIAL_STATE.lights_enabled;
92         int directional = GPU_MATERIAL_STATE.lights_directional;
93
94         /* more than 3 lights? */
95         if (enabled >= (1 << 3))
96                 return false;
97
98         /* all directional? */
99         return ((directional & enabled) == enabled);
100 }
101
102 #if 0
103 static int detect_options()
104 {
105         GLint two_sided;
106         int options = 0;
107
108         if (glIsEnabled(GL_TEXTURE_2D))
109                 options |= GPU_SHADER_TEXTURE_2D;
110         if (glIsEnabled(GL_COLOR_MATERIAL))
111                 options |= GPU_SHADER_OVERRIDE_DIFFUSE;
112
113         if (glIsEnabled(GL_LIGHTING))
114                 options |= GPU_SHADER_LIGHTING;
115
116         glGetIntegerv(GL_LIGHT_MODEL_TWO_SIDE, &two_sided);
117         if (two_sided == GL_TRUE)
118                 options |= GPU_SHADER_TWO_SIDED;
119         
120         return options;
121 }
122 #endif
123
124 static GPUShader *gpu_simple_shader(int options)
125 {
126         /* glsl code */
127         extern char datatoc_gpu_shader_simple_vert_glsl[];
128         extern char datatoc_gpu_shader_simple_frag_glsl[];
129         GPUShader *shader;
130
131         /* detect if we can do faster lighting for solid draw mode */
132         if (options & GPU_SHADER_LIGHTING)
133                 if (solid_compatible_lighting())
134                         options |= GPU_SHADER_SOLID_LIGHTING;
135
136         /* cached shaders */
137         shader = GPU_MATERIAL_STATE.cached_shaders[options];
138
139         if (!shader && !GPU_MATERIAL_STATE.failed_shaders[options]) {
140                 /* create shader if it doesn't exist yet */
141                 char defines[64*GPU_SHADER_OPTIONS_NUM] = "";
142
143                 if (options & GPU_SHADER_OVERRIDE_DIFFUSE)
144                         strcat(defines, "#define USE_COLOR\n");
145                 if (options & GPU_SHADER_TWO_SIDED)
146                         strcat(defines, "#define USE_TWO_SIDED\n");
147                 if (options & GPU_SHADER_TEXTURE_2D)
148                         strcat(defines, "#define USE_TEXTURE\n");
149
150                 if (options & GPU_SHADER_SOLID_LIGHTING)
151                         strcat(defines, "#define USE_SOLID_LIGHTING\n");
152                 else if (options & GPU_SHADER_LIGHTING)
153                         strcat(defines, "#define USE_SCENE_LIGHTING\n");
154
155                 shader = GPU_shader_create(
156                         datatoc_gpu_shader_simple_vert_glsl,
157                         datatoc_gpu_shader_simple_frag_glsl,
158                         NULL, defines);
159                 
160                 if (shader) {
161                         /* set texture map to first texture unit */
162                         if (options & GPU_SHADER_TEXTURE_2D)
163                                 glUniform1i(GPU_shader_get_uniform(shader, "texture_map"), 0);
164
165                         GPU_MATERIAL_STATE.cached_shaders[options] = shader;
166                 }
167                 else
168                         GPU_MATERIAL_STATE.failed_shaders[options] = true;
169         }
170
171         return shader;
172 }
173
174 /* Bind / unbind */
175
176 void GPU_simple_shader_bind(int options)
177 {
178         if (GPU_glsl_support()) {
179                 GPUShader *shader = gpu_simple_shader(options);
180
181                 if (shader)
182                         GPU_shader_bind(shader);
183         }
184         else {
185                 // XXX where does this fit, depends on ortho/persp?
186
187                 if (options & GPU_SHADER_LIGHTING)
188                         glEnable(GL_LIGHTING);
189
190                 if (options & GPU_SHADER_TWO_SIDED)
191                         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
192
193                 if (options & GPU_SHADER_OVERRIDE_DIFFUSE) {
194                         glEnable(GL_COLOR_MATERIAL);
195                         glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
196                 }
197
198                 if (options & GPU_SHADER_TEXTURE_2D)
199                         glEnable(GL_TEXTURE_2D);
200         }
201
202         /* temporary hack, should be solved outside of this file */
203         GPU_MATERIAL_STATE.need_normals = (options & GPU_SHADER_LIGHTING);
204 }
205
206 void GPU_simple_shader_unbind()
207 {
208         if (GPU_glsl_support()) {
209                 GPU_shader_unbind();
210         }
211         else {
212                 glDisable(GL_LIGHTING);
213                 glDisable(GL_COLOR_MATERIAL);
214                 glDisable(GL_TEXTURE_2D);
215                 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
216         }
217 }
218
219 /* Material Colors */
220
221 void GPU_simple_shader_colors(const float diffuse[3], const float specular[3],
222         int shininess, float alpha)
223 {
224         float gl_diffuse[4], gl_specular[4];
225
226         copy_v3_v3(gl_diffuse, diffuse);
227         gl_diffuse[3] = alpha;
228
229         copy_v3_v3(gl_specular, specular);
230         gl_specular[3] = 1.0f;
231
232         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, gl_diffuse);
233         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, gl_specular);
234         glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, CLAMPIS(shininess, 1, 128));
235 }
236
237 bool GPU_simple_shader_need_normals()
238 {
239         return GPU_MATERIAL_STATE.need_normals;
240 }
241
242 void GPU_simple_shader_light_set(int light_num, GPULightData *light)
243 {
244         int light_bit = (1 << light_num);
245
246         GPU_MATERIAL_STATE.lights_enabled &= ~light_bit;
247         GPU_MATERIAL_STATE.lights_directional &= ~light_bit;
248
249         if (light) {
250                 glEnable(GL_LIGHT0+light_num);
251
252                 glLightfv(GL_LIGHT0+light_num, GL_POSITION, light->position); 
253                 glLightfv(GL_LIGHT0+light_num, GL_DIFFUSE, light->diffuse);
254                 glLightfv(GL_LIGHT0+light_num, GL_SPECULAR, light->specular);
255
256                 glLightf(GL_LIGHT0+light_num, GL_CONSTANT_ATTENUATION, light->constant_attenuation);
257                 glLightf(GL_LIGHT0+light_num, GL_LINEAR_ATTENUATION, light->linear_attenuation);
258                 glLightf(GL_LIGHT0+light_num, GL_QUADRATIC_ATTENUATION, light->quadratic_attenuation);
259
260                 glLightfv(GL_LIGHT0+light_num, GL_SPOT_DIRECTION, light->spot_direction);
261                 glLightf(GL_LIGHT0+light_num, GL_SPOT_CUTOFF, light->spot_cutoff);
262                 glLightf(GL_LIGHT0+light_num, GL_SPOT_EXPONENT, light->spot_exponent);
263
264                 GPU_MATERIAL_STATE.lights_enabled |= light_bit;
265                 if(light->position[3] == 0.0f)
266                         GPU_MATERIAL_STATE.lights_directional |= light_bit;
267         }
268         else {
269                 const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
270
271                 glLightfv(GL_LIGHT0+light_num, GL_POSITION, zero); 
272                 glLightfv(GL_LIGHT0+light_num, GL_DIFFUSE, zero); 
273                 glLightfv(GL_LIGHT0+light_num, GL_SPECULAR, zero);
274
275                 glDisable(GL_LIGHT0+light_num);
276         }
277 }
278
279 void GPU_simple_shader_light_set_viewer(bool local)
280 {
281         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, (local)? GL_TRUE: GL_FALSE);
282 }
283