c3context: Make sure lights are 'drawn' first
[simavr] / examples / shared / libc3 / src / c3context.c
1 /*
2         c3context.c
3
4         Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
5
6         This file is part of libc3.
7
8         libc3 is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         libc3 is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with libc3.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <math.h>
23 #include "c3context.h"
24 #include "c3object.h"
25 #include "c3light.h"
26 #include "c3driver_context.h"
27
28 c3context_p
29 c3context_new(
30                 int w,
31                 int h)
32 {
33         c3context_p res = malloc(sizeof(*res));
34         return c3context_init(res, w, h);
35 }
36
37 c3context_p
38 c3context_init(
39                 c3context_p c,
40                 int w,
41                 int h)
42 {
43         memset(c, 0, sizeof(*c));
44
45         c3context_view_t v = {
46                         .type = C3_CONTEXT_VIEW_EYE,
47                         .size = c3vec2f(w, h),
48                         .dirty = 1,
49                         .index = c->views.count,
50         };
51         c3cam_init(&v.cam);
52         c3context_view_array_add(&c->views, v);
53         c->root = c3object_new(NULL);
54         c->root->context = c;
55
56         return c;
57 }
58
59 void
60 c3context_dispose(
61                 c3context_p c)
62 {
63         c3object_dispose(c->root);
64         for (int i = 0; i < c->views.count; i++)
65                 c3geometry_array_free(&c->views.e[i].projected);
66         free(c);
67 }
68
69 static c3context_view_p qsort_view;
70
71 /*
72  * Computes the distance from the 'eye' of the camera, sort by this value
73  */
74 static int
75 _c3_z_sorter(
76                 const void *_p1,
77                 const void *_p2)
78 {
79         c3geometry_p g1 = *(c3geometry_p*)_p1;
80         c3geometry_p g2 = *(c3geometry_p*)_p2;
81         // get center of bboxes
82         c3vec3 c1 = c3vec3_add(g1->bbox.min, c3vec3_divf(c3vec3_sub(g1->bbox.max, g1->bbox.min), 2));
83         c3vec3 c2 = c3vec3_add(g2->bbox.min, c3vec3_divf(c3vec3_sub(g2->bbox.max, g2->bbox.min), 2));
84
85         c3cam_p cam = &qsort_view->cam;
86         c3f d1 = c3vec3_length2(c3vec3_sub(c1, cam->eye));
87         c3f d2 = c3vec3_length2(c3vec3_sub(c2, cam->eye));
88
89         if (d1 > qsort_view->z.max) qsort_view->z.max = d1;
90         if (d1 < qsort_view->z.min) qsort_view->z.min = d1;
91         if (d2 > qsort_view->z.max) qsort_view->z.max = d2;
92         if (d2 < qsort_view->z.min) qsort_view->z.min = d2;
93         /*
94          * make sure transparent items are drawn after everyone else
95          */
96         if (g1->mat.color.n[3] < 1)
97                 d1 -= 100000.0;
98         if (g2->mat.color.n[3] < 1)
99                 d2 -= 100000.0;
100         if (g1->type.type == C3_LIGHT_TYPE)
101                 d1 = -200000 + (int)(((c3light_p)g1)->light_id);
102         if (g2->type.type == C3_LIGHT_TYPE)
103                 d2 = -200000 + (int)(((c3light_p)g2)->light_id);
104
105         return d1 < d2 ? 1 : d1 > d2 ? -1 : 0;
106 }
107
108 void
109 c3context_project(
110                 c3context_p c)
111 {
112         if (!c->root)
113                 return;
114
115         /*
116          * if the root object is dirty, all the views are also
117          * dirty since the geometry has changed
118          */
119         if (c->root->dirty) {
120                 for (int ci = 0; ci < c->views.count; ci++)
121                         c->views.e[ci].dirty = 1;
122                 c3mat4 m = identity3D();
123                 c3object_project(c->root, &m);
124         }
125
126         /*
127          * if the current view is dirty, gather all the geometry
128          * and Z sort it in a basic way
129          */
130         c3context_view_p v = qsort_view = c3context_view_get(c);
131         if (v->dirty) {
132             c3cam_update_matrix(&v->cam);
133
134                 c3geometry_array_p  array = &c3context_view_get(c)->projected;
135                 c3geometry_array_clear(array);
136                 c3object_get_geometry(c->root, array);
137
138                 v->z.min = 1000000000;
139                 v->z.max = -1000000000;
140
141                 qsort(v->projected.e,
142                                 v->projected.count, sizeof(v->projected.e[0]),
143                         _c3_z_sorter);
144                 v->z.min = sqrt(v->z.min);
145                 v->z.max = sqrt(v->z.max);
146
147                 v->dirty = 0;
148         }
149 }
150
151 void
152 c3context_draw(
153                 c3context_p c)
154 {
155         c3context_project(c);
156
157         c3context_view_p v = c3context_view_get(c);
158
159         C3_DRIVER(c, context_view_draw, v);
160
161         c3geometry_array_p  array = &v->projected;
162         for (int gi = 0; gi < array->count; gi++) {
163                 c3geometry_p g = array->e[gi];
164                 c3geometry_draw(g);
165         }
166 }
167