2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
16 #include <SDL_rwops.h>
27 #include "base_image.h"
28 #include "base_config.h"
31 #include "solid_draw.h"
32 #include "solid_all.h"
34 /*---------------------------------------------------------------------------*/
36 static void sol_transform(const struct s_vary *vary,
37 const struct v_body *bp)
45 /* Compute the body transform. */
47 sol_body_p(p, vary, bp->pi, bp->t);
48 sol_body_e(e, vary, bp, 0);
50 q_as_axisangle(e, v, &a);
52 glTranslatef(p[0], p[1], p[2]);
53 glRotatef(V_DEG(a), v[0], v[1], v[2]);
55 /* Compute the shadow texture transform */
57 glActiveTexture_(GL_TEXTURE1);
58 glMatrixMode(GL_TEXTURE);
64 float k = 0.25f / vary->uv->r;
66 v_sub(d, vary->uv->p, p);
68 glTranslatef(0.5f - k * d[0],
69 0.5f - k * d[2], 0.0f);
73 glMatrixMode(GL_MODELVIEW);
74 glActiveTexture_(GL_TEXTURE0);
77 /*---------------------------------------------------------------------------*/
79 static void sol_load_bill(struct s_draw *draw)
81 static const GLfloat data[] = {
82 0.0f, 0.0f, -0.5f, -0.5f,
83 1.0f, 0.0f, 0.5f, -0.5f,
84 0.0f, 1.0f, -0.5f, 0.5f,
85 1.0f, 1.0f, 0.5f, 0.5f,
88 /* Initialize a vertex buffer object for billboard drawing. */
90 glGenBuffers(1, &draw->bill);
91 glBindBuffer(GL_ARRAY_BUFFER, draw->bill);
92 glBufferData(GL_ARRAY_BUFFER, sizeof (data), data, GL_STATIC_DRAW);
93 glBindBuffer(GL_ARRAY_BUFFER, 0);
96 static void sol_free_bill(struct s_draw *draw)
98 if (glIsBuffer(draw->bill))
99 glDeleteBuffers(1, &draw->bill);
102 static void sol_bill_enable(const struct s_draw *draw)
104 const size_t s = sizeof (GLfloat);
106 glEnableClientState(GL_VERTEX_ARRAY);
107 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
109 glBindBuffer(GL_ARRAY_BUFFER, draw->bill);
111 glTexCoordPointer(2, GL_FLOAT, s * 4, (GLvoid *) ( 0));
112 glVertexPointer (2, GL_FLOAT, s * 4, (GLvoid *) (s * 2));
115 static void sol_bill_disable(void)
117 glBindBuffer(GL_ARRAY_BUFFER, 0);
119 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
120 glDisableClientState(GL_VERTEX_ARRAY);
123 static void sol_draw_bill(GLfloat w, GLfloat h, GLboolean edge)
127 glScalef(w, h, 1.0f);
130 glTranslatef(0.0f, 0.5f, 0.0f);
132 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
137 /*---------------------------------------------------------------------------*/
139 #define tobyte(f) ((GLubyte) (f * 255.0f))
141 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
142 tobyte((a)[1]) == tobyte((b)[1]) && \
143 tobyte((a)[2]) == tobyte((b)[2]) && \
144 tobyte((a)[3]) == tobyte((b)[3]))
146 static struct b_mtrl default_base_mtrl =
148 { 0.8f, 0.8f, 0.8f, 1.0f },
149 { 0.2f, 0.2f, 0.2f, 1.0f },
150 { 0.0f, 0.0f, 0.0f, 1.0f },
151 { 0.0f, 0.0f, 0.0f, 1.0f },
152 { 0.0f }, 0.0f, M_OPAQUE, ""
155 static struct d_mtrl default_draw_mtrl =
157 &default_base_mtrl, 0
160 static const struct d_mtrl *sol_apply_mtrl(const struct d_mtrl *mp_draw,
161 const struct d_mtrl *mq_draw)
163 const struct b_mtrl *mp_base = mp_draw->base;
164 const struct b_mtrl *mq_base = mq_draw->base;
166 /* Bind the texture. */
168 if (mp_draw->o != mq_draw->o)
169 glBindTexture(GL_TEXTURE_2D, mp_draw->o);
171 /* Set material properties. */
173 if (!color_cmp(mp_base->a, mq_base->a))
174 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp_base->a);
175 if (!color_cmp(mp_base->d, mq_base->d))
176 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp_base->d);
177 if (!color_cmp(mp_base->s, mq_base->s))
178 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp_base->s);
179 if (!color_cmp(mp_base->e, mq_base->e))
180 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp_base->e);
181 if (tobyte(mp_base->h[0]) != tobyte(mq_base->h[0]))
182 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp_base->h);
184 /* Enable ball shadow. */
186 if ((mp_base->fl & M_SHADOWED) && !(mq_base->fl & M_SHADOWED))
191 /* Disable ball shadow. */
193 if (!(mp_base->fl & M_SHADOWED) && (mq_base->fl & M_SHADOWED))
198 /* Enable environment mapping. */
200 if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
202 glEnable(GL_TEXTURE_GEN_S);
203 glEnable(GL_TEXTURE_GEN_T);
205 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
206 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
209 /* Disable environment mapping. */
211 if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
213 glDisable(GL_TEXTURE_GEN_S);
214 glDisable(GL_TEXTURE_GEN_T);
217 /* Enable additive blending. */
219 if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
220 glBlendFunc(GL_ONE, GL_ONE);
222 /* Enable standard blending. */
224 if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
225 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
227 /* Enable visibility-from-behind. */
229 if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
231 glDisable(GL_CULL_FACE);
232 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
235 /* Disable visibility-from-behind. */
237 if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
239 glEnable(GL_CULL_FACE);
240 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
243 /* Enable decal offset. */
245 if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
247 glEnable(GL_POLYGON_OFFSET_FILL);
248 glPolygonOffset(-1.0f, -2.0f);
251 /* Disable decal offset. */
253 if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
254 glDisable(GL_POLYGON_OFFSET_FILL);
259 static GLuint sol_find_texture(const char *name)
266 /* Prefer a lossless copy of the texture over a lossy compression. */
268 strncpy(png, name, PATHMAX); strcat(png, ".png");
269 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
271 /* Check for a PNG. */
273 if ((o = make_image_from_file(png)))
276 /* Check for a JPG. */
278 if ((o = make_image_from_file(jpg)))
284 static void sol_load_mtrl(struct d_mtrl *mp,
285 const struct b_mtrl *mq,
290 if ((mp->o = sol_find_texture(_(mq->f))))
292 /* Set the texture to clamp or repeat based on material type. */
294 if (mq->fl & M_CLAMP_S)
295 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
297 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
299 if (mq->fl & M_CLAMP_T)
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
304 /* If at least one material is reflective, mark it in the SOL. */
306 if (mq->fl & M_REFLECTIVE)
307 draw->reflective = 1;
311 static void sol_free_mtrl(struct d_mtrl *mp)
313 if (glIsTexture(mp->o))
314 glDeleteTextures(1, &mp->o);
317 static int sol_test_mtrl(const struct d_mtrl *mp, int f0, int f1)
319 /* Test whether the material flags exclude f0 and include f1. */
321 return ((mp->base->fl & f1) == f1 &&
322 (mp->base->fl & f0) == 0);
325 /*---------------------------------------------------------------------------*/
327 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
331 /* The arguments g0 and gc specify a range of the index array. These */
332 /* indices refer to geoms. Determine how many of these geoms use the */
335 for (gi = 0; gi < gc; gi++)
336 if (base->gv[base->iv[g0 + gi]].mi == mi)
342 static int sol_count_body(const struct b_body *bp,
343 const struct s_base *base, int mi)
347 /* Count all lump geoms with the given material. */
349 for (li = 0; li < bp->lc; li++)
350 c += sol_count_geom(base, base->lv[bp->l0 + li].g0,
351 base->lv[bp->l0 + li].gc, mi);
353 /* Count all body geoms with the given material. */
355 c += sol_count_geom(base, bp->g0, bp->gc, mi);
360 /*---------------------------------------------------------------------------*/
362 static void sol_mesh_vert(struct d_vert *vp,
363 const struct s_base *base, int oi)
365 /* Gather all vertex attributes for the given offs. */
367 const struct b_texc *tq = base->tv + base->ov[oi].ti;
368 const struct b_side *sq = base->sv + base->ov[oi].si;
369 const struct b_vert *vq = base->vv + base->ov[oi].vi;
386 static void sol_mesh_geom(struct d_vert *vv, int *vn,
387 struct d_geom *gv, int *gn,
388 const struct s_base *base, int *iv, int g0, int gc, int mi)
392 /* Insert all geoms with material mi into the vertex and element data. */
394 for (gi = 0; gi < gc; gi++)
396 const struct b_geom *gq = base->gv + base->iv[g0 + gi];
400 /* Insert a d_vert into the VBO data for each referenced b_off. */
402 if (iv[gq->oi] == -1)
405 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
407 if (iv[gq->oj] == -1)
410 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
412 if (iv[gq->ok] == -1)
415 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
418 /* Populate the EBO data using remapped b_off indices. */
420 gv[*gn].i = iv[gq->oi];
421 gv[*gn].j = iv[gq->oj];
422 gv[*gn].k = iv[gq->ok];
429 static void sol_load_mesh(struct d_mesh *mp,
430 const struct b_body *bp,
431 const struct s_draw *draw, int mi)
433 const size_t vs = sizeof (struct d_vert);
434 const size_t gs = sizeof (struct d_geom);
436 struct d_vert *vv = 0;
437 struct d_geom *gv = 0;
440 int oc = draw->base->oc;
444 const int gc = sol_count_body(bp, draw->base, mi);
446 /* Get temporary storage for vertex and element array creation. */
448 if ((vv = (struct d_vert *) calloc(oc, vs)) &&
449 (gv = (struct d_geom *) calloc(gc, gs)) &&
450 (iv = (int *) calloc(oc, sizeof (int))))
454 /* Initialize the index remapping. */
456 for (i = 0; i < oc; ++i) iv[i] = -1;
458 /* Include all matching lump geoms in the arrays. */
460 for (li = 0; li < bp->lc; li++)
461 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv,
462 draw->base->lv[bp->l0 + li].g0,
463 draw->base->lv[bp->l0 + li].gc, mi);
465 /* Include all matching body geoms in the arrays. */
467 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
469 /* Initialize buffer objects for all data. */
471 glGenBuffers(1, &mp->vbo);
472 glBindBuffer(GL_ARRAY_BUFFER, mp->vbo);
473 glBufferData(GL_ARRAY_BUFFER, vn * vs, vv, GL_STATIC_DRAW);
474 glBindBuffer(GL_ARRAY_BUFFER, 0);
476 glGenBuffers(1, &mp->ebo);
477 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
478 glBufferData(GL_ELEMENT_ARRAY_BUFFER, gn * gs, gv, GL_STATIC_DRAW);
479 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
481 mp->mp = draw->mv + mi;
490 static void sol_free_mesh(struct d_mesh *mp)
492 if (glIsBuffer(mp->ebo))
493 glDeleteBuffers(1, &mp->ebo);
494 if (glIsBuffer(mp->vbo))
495 glDeleteBuffers(1, &mp->vbo);
498 static const struct d_mtrl *sol_draw_mesh(const struct d_mesh *mp,
499 const struct d_mtrl *mq,
502 /* If this mesh has material matching the given flags... */
504 if (sol_test_mtrl(mp->mp, f0, f1))
506 const size_t s = sizeof (struct d_vert);
507 const GLenum T = GL_FLOAT;
509 /* Apply the material state. */
511 mq = sol_apply_mtrl(mp->mp, mq);
513 /* Bind the mesh data. */
515 glBindBuffer(GL_ARRAY_BUFFER, mp->vbo);
516 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
518 glVertexPointer (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
519 glNormalPointer ( T, s, (GLvoid *) offsetof (struct d_vert, n));
521 glClientActiveTexture(GL_TEXTURE1);
522 glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, u));
523 glClientActiveTexture(GL_TEXTURE0);
524 glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, t));
528 glDrawElements(GL_TRIANGLES, mp->ebc, GL_UNSIGNED_SHORT, 0);
534 /*---------------------------------------------------------------------------*/
536 static void sol_load_body(struct d_body *bp,
537 const struct b_body *bq,
538 const struct s_draw *draw)
545 /* Determine how many materials this body uses. */
547 for (mi = 0; mi < draw->mc; ++mi)
548 if (sol_count_body(bq, draw->base, mi))
551 /* Allocate and initialize a mesh for each material. */
553 if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
557 for (mi = 0; mi < draw->mc; ++mi)
558 if (sol_count_body(bq, draw->base, mi))
559 sol_load_mesh(bp->mv + mj++, bq, draw, mi);
563 static void sol_free_body(struct d_body *bp)
567 for (mi = 0; mi < bp->mc; ++mi)
568 sol_free_mesh(bp->mv + mi);
573 static const struct d_mtrl *sol_draw_body(const struct d_body *bp,
574 const struct d_mtrl *mq,
579 for (i = 0; i < bp->mc; ++i)
580 mq = sol_draw_mesh(bp->mv + i, mq, f0, f1);
585 /*---------------------------------------------------------------------------*/
587 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
591 memset(draw, 0, sizeof (struct s_draw));
594 draw->base = vary->base;
596 /* Initialize all materials for this file. */
600 if ((draw->mv = calloc(draw->base->mc, sizeof (*draw->mv))))
602 draw->mc = draw->base->mc;
604 for (i = 0; i < draw->mc; i++)
605 sol_load_mtrl(draw->mv + i, draw->base->mv + i, draw);
609 /* Initialize all bodies for this file. */
613 if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
615 draw->bc = draw->base->bc;
617 for (i = 0; i < draw->bc; i++)
618 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
627 void sol_free_draw(struct s_draw *draw)
633 for (i = 0; i < draw->bc; i++)
634 sol_free_body(draw->bv + i);
635 for (i = 0; i < draw->mc; i++)
636 sol_free_mtrl(draw->mv + i);
639 /*---------------------------------------------------------------------------*/
641 static const struct d_mtrl *sol_draw_all(const struct s_draw *draw,
642 const struct d_mtrl *mq,
647 /* Draw all meshes of all bodies matching the given material flags. */
649 for (bi = 0; bi < draw->bc; ++bi)
653 sol_transform(draw->vary, draw->vary->bv + bi);
654 mq = sol_draw_body(draw->bv + bi, mq, f0, f1);
661 void sol_draw_enable(void)
663 glEnableClientState(GL_VERTEX_ARRAY);
664 glEnableClientState(GL_NORMAL_ARRAY);
666 glClientActiveTexture(GL_TEXTURE1);
667 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
668 glClientActiveTexture(GL_TEXTURE0);
669 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
672 void sol_draw_disable(void)
674 glClientActiveTexture(GL_TEXTURE1);
675 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
676 glClientActiveTexture(GL_TEXTURE0);
677 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
679 glDisableClientState(GL_NORMAL_ARRAY);
680 glDisableClientState(GL_VERTEX_ARRAY);
683 /*---------------------------------------------------------------------------*/
685 void sol_draw(const struct s_draw *draw, int mask, int test)
689 const struct d_mtrl *mq = &default_draw_mtrl;
691 /* Render all opaque geometry, decals last. */
693 mq = sol_draw_all(draw, mq, M_TRANSPARENT | M_REFLECTIVE | M_DECAL, 0);
694 mq = sol_draw_all(draw, mq, M_TRANSPARENT | M_REFLECTIVE, M_DECAL);
696 /* Render all transparent geometry, decals first. */
698 if (!test) glDisable(GL_DEPTH_TEST);
699 if (!mask) glDepthMask(GL_FALSE);
701 mq = sol_draw_all(draw, mq, M_REFLECTIVE, M_DECAL | M_TRANSPARENT);
702 mq = sol_draw_all(draw, mq, M_REFLECTIVE | M_DECAL, M_TRANSPARENT);
704 if (!mask) glDepthMask(GL_TRUE);
705 if (!test) glEnable(GL_DEPTH_TEST);
707 /* Revert the material state. */
709 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
711 /* Revert the buffer object state. */
713 glBindBuffer(GL_ARRAY_BUFFER, 0);
714 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
719 void sol_refl(const struct s_draw *draw)
723 const struct d_mtrl *mq = &default_draw_mtrl;
725 /* Render all reflective geometry. */
727 mq = sol_draw_all(draw, mq, 0, M_REFLECTIVE);
728 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
730 /* Revert the buffer object state. */
732 glBindBuffer(GL_ARRAY_BUFFER, 0);
733 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
738 void sol_back(const struct s_draw *draw, float n, float f, float t)
740 glDisable(GL_LIGHTING);
741 glDepthMask(GL_FALSE);
743 sol_bill_enable(draw);
745 const struct d_mtrl *mq = &default_draw_mtrl;
749 /* Consider each billboard. */
751 for (ri = 0; ri < draw->base->rc; ri++)
753 const struct b_bill *rp = draw->base->rv + ri;
755 /* Render only billboards at distances between n and f. */
757 if (n <= rp->d && rp->d < f)
759 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0;
761 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
762 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
764 /* Render only billboards facing the viewer. */
768 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
769 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
770 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
774 glRotatef(ry, 0.0f, 1.0f, 0.0f);
775 glRotatef(rx, 1.0f, 0.0f, 0.0f);
776 glTranslatef(0.0f, 0.0f, -rp->d);
780 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
781 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
784 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
786 glRotatef(rz, 0.0f, 0.0f, 1.0f);
788 mq = sol_apply_mtrl(draw->mv + rp->mi, mq);
790 sol_draw_bill(w, h, rp->fl & B_EDGE);
796 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
800 glDepthMask(GL_TRUE);
801 glEnable(GL_LIGHTING);
804 void sol_bill(const struct s_draw *draw, const float *M, float t)
806 sol_bill_enable(draw);
808 const struct d_mtrl *mq = &default_draw_mtrl;
812 for (ri = 0; ri < draw->base->rc; ++ri)
814 const struct b_bill *rp = draw->base->rv + ri;
819 float w = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
820 float h = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
821 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
822 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
823 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
825 mq = sol_apply_mtrl(draw->mv + rp->mi, mq);
829 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
831 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
833 if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
834 if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
835 if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
837 sol_draw_bill(w, h, GL_FALSE);
841 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
846 void sol_shad(const struct s_draw *draw, int ui)
851 /*---------------------------------------------------------------------------*/
853 int sol_load_full(struct s_full *full, const char *filename, int s)
855 if (sol_load_base(&full->base, filename))
857 sol_load_vary(&full->vary, &full->base);
858 sol_load_draw(&full->draw, &full->vary, s);
866 void sol_free_full(struct s_full *full)
868 sol_free_draw(&full->draw);
869 sol_free_vary(&full->vary);
870 sol_free_base(&full->base);
873 /*---------------------------------------------------------------------------*/