some meshgen and map rendering updates
[voxelands-alt.git] / src / graphics / material.c
blobdbb20dc9315f14c5452756cb6a0dcb35bde14a82
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 /* create a material from a colour */
151 material_t *mat_from_colour(colour_t *c)
153 char buff[256];
154 texture_t *tex;
155 material_t *mat;
156 snprintf(buff,256,"rgba-%d-%d-%d-%d",c->r,c->g,c->b,c->a);
158 tex = tex_from_rgba(1,1,c->r,c->g,c->b,c->a);
159 if (!tex)
160 return NULL;
162 mat = mat_create();
163 array_push_ptr(&mat->textures,tex);
165 strcpy(mat->name,buff);
167 return mat;
170 /* add a second texture to a material from an image */
171 int mat_add_image(material_t *mat, char* type, char* file)
173 texture_t *tex;
174 tex = tex_from_image(type,file);
175 if (!tex)
176 return 1;
178 array_push_ptr(&mat->textures,tex);
180 return 0;
183 /* add a new texture to a material */
184 int mat_add_tex(material_t *mat, texture_t *tex)
186 if (!tex)
187 return 1;
189 array_push_ptr(&mat->textures,tex);
191 return 0;
194 /* add a new texture from a colour to a material */
195 int mat_add_colour(material_t *mat, colour_t *c)
197 texture_t *tex;
198 if (!c)
199 return 1;
201 tex = tex_from_rgba(1,1,c->r,c->g,c->b,c->a);
202 if (!tex)
203 return 1;
205 array_push_ptr(&mat->textures,tex);
207 return 0;
210 static void mat_apply_opts(uint32_t options)
212 /* back face culling */
213 if ((options&MATOPT_BFCULL) == MATOPT_BFCULL) {
214 if (!mat_data.culling) {
215 glEnable(GL_CULL_FACE);
216 glCullFace(GL_BACK);
217 mat_data.culling = 1;
219 }else if (mat_data.culling) {
220 glDisable(GL_CULL_FACE);
221 mat_data.culling = 0;
224 /* blending */
225 if ((options&MATOPT_ADDITIVE_BLEND) == MATOPT_ADDITIVE_BLEND) {
226 if (!mat_data.blended) {
227 glEnable(GL_BLEND);
228 glEnable(GL_LINE_SMOOTH);
229 mat_data.blended = 1;
231 if (mat_data.blend_mode != GL_ONE) {
232 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
233 mat_data.blend_mode = GL_ONE;
235 }else if ((options&MATOPT_ALPHA_BLEND) == MATOPT_ALPHA_BLEND) {
236 if (!mat_data.blended) {
237 glEnable(GL_BLEND);
238 glEnable(GL_LINE_SMOOTH);
239 mat_data.blended = 1;
241 if (mat_data.blend_mode != GL_ONE_MINUS_SRC_ALPHA) {
242 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
243 mat_data.blend_mode = GL_ONE_MINUS_SRC_ALPHA;
245 }else if (mat_data.blended) {
246 glDisable(GL_BLEND);
247 mat_data.blended = 0;
248 mat_data.blend_mode = GL_ZERO;
251 if ((options&MATOPT_SDF_ALPHA) == MATOPT_SDF_ALPHA) {
252 if (opengl_has_psdf()) {
253 shader_uniform_float(NULL,"alphafunc",2.0);
254 }else{
255 shader_uniform_float(NULL,"alphafunc",1.0);
257 }else if ((options&MATOPT_ALPHA_TEST) == MATOPT_ALPHA_TEST) {
258 shader_uniform_float(NULL,"alphafunc",1.0);
259 }else{
260 shader_uniform_float(NULL,"alphafunc",0.0);
264 static void mat_bind(GLuint tex, GLenum type, uint8_t slot)
266 char buff[32];
267 if (slot < 0 || slot > 7)
268 return;
270 if (mat_data.current_texture[slot] == tex)
271 return;
273 switch (slot) {
274 case 0:
275 glActiveTexture(GL_TEXTURE0);
276 break;
277 case 1:
278 glActiveTexture(GL_TEXTURE1);
279 break;
280 case 2:
281 glActiveTexture(GL_TEXTURE2);
282 break;
283 case 3:
284 glActiveTexture(GL_TEXTURE3);
285 break;
286 case 4:
287 glActiveTexture(GL_TEXTURE4);
288 break;
289 case 5:
290 glActiveTexture(GL_TEXTURE5);
291 break;
292 case 6:
293 glActiveTexture(GL_TEXTURE6);
294 break;
295 case 7:
296 glActiveTexture(GL_TEXTURE7);
297 break;
298 default:
299 return;
302 snprintf(buff,32,"texture%u",slot);
304 glBindTexture(type,tex);
306 mat_data.current_texture[slot] = tex;
308 shader_uniform_int(NULL,buff,slot);
311 /* bind a texture and apply options */
312 int mat_bind_with_opts(GLuint tex, uint32_t options)
314 uint8_t i;
315 mat_bind(tex,GL_TEXTURE_2D,0);
316 mat_apply_opts(options);
318 for (i=1; i<8; i++) {
319 mat_bind(0,GL_TEXTURE_2D,i);
323 return 0;
326 /* use a material */
327 void mat_use(material_t *mat, shader_t *shader)
329 if (mat->textures.length) {
330 texture_t *tex;
331 int i;
332 for (i=0; i<8; i++) {
333 tex = array_get_ptr(&mat->textures,i);
334 if (tex) {
335 if (tex->state != 1)
336 tex_generate(tex);
337 mat_bind(tex->glid,tex->type,i);
338 }else if (mat_data.current_texture[i]) {
339 mat_bind(0,GL_TEXTURE_2D,i);
342 mat_apply_opts(mat->options);
343 }else{
344 mat_bind_with_opts(0,mat->options);
347 /* specular values */
348 shader_uniform_float(shader,"shininess",mat->shininess);
349 shader_uniform_float(shader,"reflectivity",mat->reflectivity);
350 shader_uniform_float(shader,"bumpiness",mat->bumpiness);
353 /* set the shininess and reflectivity of a material */
354 void mat_shininess(material_t *mat, float shininess, float reflectivity)
356 mat->shininess = shininess;
357 mat->reflectivity = reflectivity;
360 /* set the bumpiness (for bump mapping) of a material */
361 void mat_bumpiness(material_t *mat, float bumpiness)
363 mat->bumpiness = bumpiness;
366 /* set the name of a material */
367 void mat_name(material_t *mat, char* name)
369 strncpy(mat->name,name,256);
372 /* set the options for a material, return the previous options */
373 uint32_t mat_options(material_t *mat, uint32_t opt)
375 uint32_t o = mat->options;
376 if (opt != MATOPT_GET)
377 mat->options = opt;
378 return o;