graphics updates
[voxelands-alt.git] / src / graphics / material.c
blob499d1dd93e9f167f6f24a1fcc0af95ef10f48946
1 /************************************************************************
2 * material.c
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
20 #include "common.h"
21 #include "graphics.h"
22 #include "list.h"
23 #include "array.h"
25 #include <string.h>
27 static struct {
28 material_t *materials;
29 int ids;
30 GLuint current_texture[8];
31 uint8_t culling;
32 uint8_t blended;
33 GLenum blend_mode;
34 } mat_data ={
35 NULL,
37 {0,0,0,0,0,0,0,0},
40 GL_ZERO
43 material_t *mat_find(char* name)
45 material_t *m = mat_data.materials;
47 while (m) {
48 if (!strcmp(m->name,name))
49 return m;
50 m = m->next;
53 return m;
56 /* create a new blank material */
57 material_t *mat_create()
59 material_t *mat = malloc(sizeof(material_t));
60 mat->id = mat_data.ids++;
61 mat->name[0] = 0;
62 mat->options = MATOPT_BFCULL;
63 array_init(&mat->textures,ARRAY_TYPE_PTR);
64 mat->next = NULL;
65 mat->shininess = 1.0;
66 mat->reflectivity = 0.0;
67 mat->bumpiness = 0.0;
69 mat_data.materials = list_push(&mat_data.materials,mat);
71 return mat;
74 /* free a material */
75 void mat_free(material_t *mat)
77 texture_t *tex;
78 if (!mat)
79 return;
81 mat_data.materials = list_remove(&mat_data.materials,mat);
83 while ((tex = array_pop_ptr(&mat->textures))) {
84 tex_free(tex);
87 array_free(&mat->textures,0);
89 free(mat);
92 /* create a material from a texture */
93 material_t *mat_from_tex(texture_t *tex)
95 material_t *mat = mat_create();
96 array_push_ptr(&mat->textures,tex);
97 strcpy(mat->name,tex->name);
99 return mat;
102 /* create a material from pixel data */
103 material_t *mat_from_pixels(image_t *p)
105 texture_t *tex;
106 material_t *mat = mat_create();
107 tex = tex_from_pixels(p);
108 array_push_ptr(&mat->textures,tex);
109 strcpy(mat->name,tex->name);
111 return mat;
114 /* create a material from an image */
115 material_t *mat_from_image(char* type, char* file)
117 texture_t *tex;
118 material_t *mat;
120 tex = tex_from_image(type,file);
121 if (!tex)
122 return NULL;
124 mat = mat_create();
125 array_push_ptr(&mat->textures,tex);
127 strcpy(mat->name,tex->name);
129 return mat;
132 /* combine size images into a texture cube */
133 material_t *mat_from_box(char* front, char* back, char* left, char* right, char* top, char* bottom)
135 texture_t *tex;
136 material_t *mat;
138 tex = tex_from_box(front,back,left,right,top,bottom);
139 if (!tex)
140 return NULL;
142 mat = mat_create();
143 array_push_ptr(&mat->textures,tex);
145 strcpy(mat->name,tex->name);
147 return mat;
150 /* add a second texture to a material from an image */
151 int mat_add_image(material_t *mat, char* type, char* file)
153 texture_t *tex;
154 tex = tex_from_image(type,file);
155 if (!tex)
156 return 1;
158 array_push_ptr(&mat->textures,tex);
160 return 0;
163 /* add a new texture to a material */
164 int mat_add_tex(material_t *mat, texture_t *tex)
166 if (!tex)
167 return 1;
169 array_push_ptr(&mat->textures,tex);
171 return 0;
174 /* create a material from a colour */
175 material_t *mat_from_colour(colour_t *c)
177 char buff[256];
178 texture_t *tex;
179 material_t *mat;
180 snprintf(buff,256,"rgba-%d-%d-%d-%d",c->r,c->g,c->b,c->a);
182 tex = tex_from_rgba(1,1,c->r,c->g,c->b,c->a);
183 if (!tex)
184 return NULL;
186 mat = mat_create();
187 array_push_ptr(&mat->textures,tex);
189 strcpy(mat->name,buff);
191 return mat;
194 static void mat_apply_opts(uint32_t options)
196 /* back face culling */
197 if ((options&MATOPT_BFCULL) == MATOPT_BFCULL) {
198 if (!mat_data.culling) {
199 glEnable(GL_CULL_FACE);
200 glCullFace(GL_BACK);
201 mat_data.culling = 1;
203 }else if (mat_data.culling) {
204 glDisable(GL_CULL_FACE);
205 mat_data.culling = 0;
208 /* blending */
209 if ((options&MATOPT_ADDITIVE_BLEND) == MATOPT_ADDITIVE_BLEND) {
210 if (!mat_data.blended) {
211 glEnable(GL_BLEND);
212 glEnable(GL_LINE_SMOOTH);
213 mat_data.blended = 1;
215 if (mat_data.blend_mode != GL_ONE) {
216 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
217 mat_data.blend_mode = GL_ONE;
219 }else if ((options&MATOPT_ALPHA_BLEND) == MATOPT_ALPHA_BLEND) {
220 if (!mat_data.blended) {
221 glEnable(GL_BLEND);
222 glEnable(GL_LINE_SMOOTH);
223 mat_data.blended = 1;
225 if (mat_data.blend_mode != GL_ONE_MINUS_SRC_ALPHA) {
226 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
227 mat_data.blend_mode = GL_ONE_MINUS_SRC_ALPHA;
229 }else if (mat_data.blended) {
230 glDisable(GL_BLEND);
231 mat_data.blended = 0;
232 mat_data.blend_mode = GL_ZERO;
235 if ((options&MATOPT_SDF_ALPHA) == MATOPT_SDF_ALPHA) {
236 if (opengl_has_psdf()) {
237 shader_uniform_float(NULL,"alphafunc",2.0);
238 }else{
239 shader_uniform_float(NULL,"alphafunc",1.0);
241 }else if ((options&MATOPT_ALPHA_TEST) == MATOPT_ALPHA_TEST) {
242 shader_uniform_float(NULL,"alphafunc",1.0);
243 }else{
244 shader_uniform_float(NULL,"alphafunc",0.0);
248 static void mat_bind(GLuint tex, GLenum type, uint8_t slot)
250 char buff[32];
251 if (slot < 0 || slot > 7)
252 return;
254 if (mat_data.current_texture[slot] == tex)
255 return;
257 switch (slot) {
258 case 0:
259 glActiveTexture(GL_TEXTURE0);
260 break;
261 case 1:
262 glActiveTexture(GL_TEXTURE1);
263 break;
264 case 2:
265 glActiveTexture(GL_TEXTURE2);
266 break;
267 case 3:
268 glActiveTexture(GL_TEXTURE3);
269 break;
270 case 4:
271 glActiveTexture(GL_TEXTURE4);
272 break;
273 case 5:
274 glActiveTexture(GL_TEXTURE5);
275 break;
276 case 6:
277 glActiveTexture(GL_TEXTURE6);
278 break;
279 case 7:
280 glActiveTexture(GL_TEXTURE7);
281 break;
282 default:
283 return;
286 snprintf(buff,32,"texture%u",slot);
288 glBindTexture(type,tex);
290 mat_data.current_texture[slot] = tex;
292 shader_uniform_int(NULL,buff,slot);
295 /* bind a texture and apply options */
296 int mat_bind_with_opts(GLuint tex, uint32_t options)
298 uint8_t i;
299 mat_bind(tex,GL_TEXTURE_2D,0);
300 mat_apply_opts(options);
302 for (i=1; i<8; i++) {
303 mat_bind(0,GL_TEXTURE_2D,i);
307 return 0;
310 /* use a material */
311 void mat_use(material_t *mat, shader_t *shader)
313 if (mat->textures.length) {
314 texture_t *tex;
315 int i;
316 for (i=0; i<8; i++) {
317 tex = array_get_ptr(&mat->textures,i);
318 if (tex) {
319 if (tex->state != 1)
320 tex_generate(tex);
321 mat_bind(tex->glid,tex->type,i);
322 }else if (mat_data.current_texture[i]) {
323 mat_bind(0,GL_TEXTURE_2D,i);
326 mat_apply_opts(mat->options);
327 }else{
328 mat_bind_with_opts(0,mat->options);
331 /* specular values */
332 shader_uniform_float(shader,"shininess",mat->shininess);
333 shader_uniform_float(shader,"reflectivity",mat->reflectivity);
334 shader_uniform_float(shader,"bumpiness",mat->bumpiness);
337 /* set the shininess and reflectivity of a material */
338 void mat_shininess(material_t *mat, float shininess, float reflectivity)
340 mat->shininess = shininess;
341 mat->reflectivity = reflectivity;
344 /* set the bumpiness (for bump mapping) of a material */
345 void mat_bumpiness(material_t *mat, float bumpiness)
347 mat->bumpiness = bumpiness;
350 /* set the name of a material */
351 void mat_name(material_t *mat, char* name)
353 strncpy(mat->name,name,256);
356 /* set the options for a material, return the previous options */
357 uint32_t mat_options(material_t *mat, uint32_t opt)
359 uint32_t o = mat->options;
360 if (opt != MATOPT_GET)
361 mat->options = opt;
362 return o;