fixed missing last tile, and changed bvh node to binary
[cyberay] / src / level.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <float.h>
4 #include "level.h"
5 #include "treestore.h"
6 #include "mesh.h"
7
8 static struct material *add_material(struct level *lvl, struct material *mtl);
9 static int append_polygons(struct bvhnode *bnode, struct triangle *faces, int num_faces, struct material *mtl);
10
11 int load_level(struct level *lvl, const char *fname)
12 {
13         char *dirname, *ptr;
14         char path[256];
15         struct ts_node *root, *node;
16         struct scenefile scn;
17         struct mesh *mesh;
18         struct material *mtl;
19
20         memset(lvl, 0, sizeof *lvl);
21         if(!(lvl->st_root = calloc(1, sizeof *lvl->st_root)) ||
22                         !(lvl->dyn_root = calloc(1, sizeof *lvl->dyn_root))) {
23                 free(lvl->st_root);
24                 fprintf(stderr, "load_level: failed to allocate bvh root nodes\n");
25                 return -1;
26         }
27
28         cgm_vcons(&lvl->st_root->aabb.vmin, FLT_MAX, FLT_MAX, FLT_MAX);
29         cgm_vcons(&lvl->st_root->aabb.vmax, -FLT_MAX, -FLT_MAX, -FLT_MAX);
30         lvl->dyn_root->aabb = lvl->st_root->aabb;
31
32         dirname = alloca(strlen(fname) + 1);
33         strcpy(dirname, fname);
34         if((ptr = strrchr(dirname, '/'))) {
35                 ptr[1] = 0;
36         } else {
37                 *dirname = 0;
38         }
39
40         if(!(root = ts_load(fname))) {
41                 fprintf(stderr, "load_level: failed to load: %s\n", fname);
42                 return -1;
43         }
44         if(strcmp(root->name, "level") != 0) {
45                 fprintf(stderr, "load_level: invalid level file %s, root is not \"level\"\n", fname);
46                 ts_free_tree(root);
47                 return -1;
48         }
49
50         node = root->child_list;
51         while(node) {
52                 if(strcmp(node->name, "scene") == 0) {
53                         if(!(fname = ts_get_attr_str(node, "file", 0))) {
54                                 fprintf(stderr, "load_level: ignoring \"scene\" without a \"file\" attribute\n");
55                                 goto cont;
56                         }
57                         snprintf(path, sizeof path, "%s%s", dirname, fname);
58                         printf("loading scene file: %s\n", path);
59
60                         if(load_scenefile(&scn, path) == -1) {
61                                 goto cont;
62                         }
63                         mesh = scn.meshlist;
64                         while(mesh) {
65                                 mtl = add_material(lvl, &mesh->mtl);
66                                 append_polygons(lvl->st_root, mesh->faces, mesh->num_faces, mtl);
67                                 mesh = mesh->next;
68                         }
69
70                         destroy_scenefile(&scn);
71                 }
72 cont:   node = node->next;
73         }
74
75         ts_free_tree(root);
76         return 0;
77 }
78
79 void destroy_level(struct level *lvl)
80 {
81         free_bvh_tree(lvl->st_root);
82         free_bvh_tree(lvl->dyn_root);
83         free(lvl->mtls);
84 }
85
86
87 int ray_level(cgm_ray *ray, struct level *lvl, float tmax, struct rayhit *hit)
88 {
89         int found = 0;
90         struct rayhit hit0;
91
92         if(!hit) {
93                 if(ray_bvhnode(ray, lvl->st_root, tmax, 0)) return 1;
94                 if(ray_bvhnode(ray, lvl->dyn_root, tmax, 0)) return 1;
95                 return 0;
96         }
97
98         hit0.t = FLT_MAX;
99         if(ray_bvhnode(ray, lvl->st_root, tmax, hit)) {
100                 hit0 = *hit;
101                 found = 1;
102         }
103         if(ray_bvhnode(ray, lvl->dyn_root, tmax, hit) && hit->t < hit0.t) {
104                 hit0 = *hit;
105                 found = 1;
106         }
107
108         if(found) {
109                 *hit = hit0;
110                 return 1;
111         }
112         return 0;
113 }
114
115 static void draw_level_rec(struct bvhnode *bn)
116 {
117         int i, j;
118         struct triangle *tri;
119         struct material *curmtl;
120         float color[4] = {0, 0, 0, 1};
121
122         if(!bn) return;
123
124         if(bn->faces) {
125                 tri = bn->faces;
126                 curmtl = tri->mtl;
127
128                 glBegin(GL_TRIANGLES);
129                 for(i=0; i<bn->num_faces; i++) {
130                         if(tri->mtl != curmtl) {
131                                 glEnd();
132                                 color[0] = tri->mtl->color.x;
133                                 color[1] = tri->mtl->color.y;
134                                 color[2] = tri->mtl->color.z;
135                                 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
136                                 curmtl = tri->mtl;
137                                 glBegin(GL_TRIANGLES);
138                         }
139
140                         for(j=0; j<3; j++) {
141                                 glNormal3fv(&tri->v[j].norm.x);
142                                 glTexCoord2fv(&tri->v[j].tex.x);
143                                 glVertex3fv(&tri->v[j].pos.x);
144                         }
145                         tri++;
146                 }
147                 glEnd();
148         }
149
150         draw_level_rec(bn->left);
151         draw_level_rec(bn->right);
152 }
153
154 void draw_level(struct level *lvl)
155 {
156         draw_level_rec(lvl->st_root);
157         draw_level_rec(lvl->dyn_root);
158 }
159
160 static struct material *add_material(struct level *lvl, struct material *mtl)
161 {
162         int i, newsz;
163         struct material *tmp;
164
165         for(i=0; i<lvl->num_mtls; i++) {
166                 if(memcmp(lvl->mtls + i, mtl, sizeof *mtl) == 0) {
167                         return lvl->mtls + i;
168                 }
169         }
170
171         if(lvl->num_mtls >= lvl->max_mtls) {
172                 newsz = lvl->max_mtls ? lvl->max_mtls * 2 : 16;
173                 if(!(tmp = realloc(lvl->mtls, newsz * sizeof *lvl->mtls))) {
174                         fprintf(stderr, "add_material: failed to resize materials array to %d\n", newsz);
175                         return 0;
176                 }
177                 lvl->mtls = tmp;
178                 lvl->max_mtls = newsz;
179         }
180         lvl->mtls[lvl->num_mtls] = *mtl;
181
182         return lvl->mtls + lvl->num_mtls++;
183 }
184
185 static int append_polygons(struct bvhnode *bnode, struct triangle *faces, int num_faces, struct material *mtl)
186 {
187         int i, j, newsz;
188         struct triangle *tri;
189
190         newsz = bnode->num_faces + num_faces;
191         if(!(tri = realloc(bnode->faces, newsz * sizeof *bnode->faces))) {
192                 fprintf(stderr, "append_polygons: failed to resize faces array to %d\n", newsz);
193                 return -1;
194         }
195         bnode->faces = tri;
196         tri += bnode->num_faces;
197         bnode->num_faces = newsz;
198
199         for(i=0; i<num_faces; i++) {
200                 *tri = *faces++;
201                 tri->mtl = mtl;
202
203                 for(j=0; j<3; j++) {
204                         cgm_vec3 *p = &tri->v[j].pos;
205                         if(p->x < bnode->aabb.vmin.x) bnode->aabb.vmin.x = p->x;
206                         if(p->x > bnode->aabb.vmax.x) bnode->aabb.vmax.x = p->x;
207                         if(p->y < bnode->aabb.vmin.y) bnode->aabb.vmin.y = p->y;
208                         if(p->y > bnode->aabb.vmax.y) bnode->aabb.vmax.y = p->y;
209                         if(p->z < bnode->aabb.vmin.z) bnode->aabb.vmin.z = p->z;
210                         if(p->z > bnode->aabb.vmax.z) bnode->aabb.vmax.z = p->z;
211                 }
212                 tri++;
213         }
214         return 0;
215 }