1 #include "engine.h"
2 
triintersect(tri & t,const vec & o,const vec & ray,float maxdist,float & dist,int mode,tri * noclip)3 bool BIH::triintersect(tri &t, const vec &o, const vec &ray, float maxdist, float &dist, int mode, tri *noclip)
4 {
5     vec p;
6     p.cross(ray, t.c);
7     float det = t.b.dot(p);
8     if(det == 0) return false;
9     vec r(o);
10     r.sub(t.a);
11     float u = r.dot(p) / det;
12     if(u < 0 || u > 1) return false;
13     vec q;
14     q.cross(r, t.b);
15     float v = ray.dot(q) / det;
16     if(v < 0 || u + v > 1) return false;
17     float f = t.c.dot(q) / det;
18     if(f < 0 || f > maxdist) return false;
19     if(!(mode&RAY_SHADOW) && &t >= noclip) return false;
20     if(t.tex && (mode&RAY_ALPHAPOLY)==RAY_ALPHAPOLY && (t.tex->alphamask || (loadalphamask(t.tex), t.tex->alphamask)))
21     {
22         int si = clamp(int(t.tex->xs * (t.tc[0] + u*(t.tc[2] - t.tc[0]) + v*(t.tc[4] - t.tc[0]))), 0, t.tex->xs-1),
23             ti = clamp(int(t.tex->ys * (t.tc[1] + u*(t.tc[3] - t.tc[1]) + v*(t.tc[5] - t.tc[1]))), 0, t.tex->ys-1);
24         if(!(t.tex->alphamask[ti*((t.tex->xs+7)/8) + si/8] & (1<<(si%8)))) return false;
25     }
26     if(!(mode&RAY_SHADOW))
27     {
28         extern vec hitsurface;
29         hitsurface.cross(t.b, t.c).normalize();
30         if(hitsurface.dot(ray) > 0) hitsurface.neg();
31     }
32     dist = f;
33     return true;
34 }
35 
36 struct BIHStack
37 {
38     BIHNode *node;
39     float tmin, tmax;
40 };
41 
traverse(const vec & o,const vec & ray,float maxdist,float & dist,int mode)42 bool BIH::traverse(const vec &o, const vec &ray, float maxdist, float &dist, int mode)
43 {
44     if(!numnodes) return false;
45 
46     vec invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f);
47     float tmin, tmax;
48     float t1 = (bbmin.x - o.x)*invray.x,
49           t2 = (bbmax.x - o.x)*invray.x;
50     if(invray.x > 0) { tmin = t1; tmax = t2; } else { tmin = t2; tmax = t1; }
51     t1 = (bbmin.y - o.y)*invray.y;
52     t2 = (bbmax.y - o.y)*invray.y;
53     if(invray.y > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
54     t1 = (bbmin.z - o.z)*invray.z;
55     t2 = (bbmax.z - o.z)*invray.z;
56     if(invray.z > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
57     if(tmin >= maxdist || tmin>=tmax) return false;
58     tmax = min(tmax, maxdist);
59 
60     static vector<BIHStack> stack;
61     stack.setsizenodelete(0);
62 
63     ivec order(ray.x>0 ? 0 : 1, ray.y>0 ? 0 : 1, ray.z>0 ? 0 : 1);
64     BIHNode *curnode = &nodes[0];
65     bool hit = false;
66     for(;;)
67     {
68         int axis = curnode->axis();
69         int nearidx = order[axis], faridx = nearidx^1;
70         float nearsplit = (curnode->split[nearidx] - o[axis])*invray[axis],
71               farsplit = (curnode->split[faridx] - o[axis])*invray[axis];
72 
73         if(nearsplit <= tmin)
74         {
75             if(farsplit < tmax)
76             {
77                 if(!curnode->isleaf(faridx))
78                 {
79                     curnode = &nodes[curnode->childindex(faridx)];
80                     tmin = max(tmin, farsplit);
81                     continue;
82                 }
83                 else if(triintersect(tris[curnode->childindex(faridx)], o, ray, maxdist, maxdist, mode, noclip))
84                 {
85                     if(mode&RAY_SHADOW) { dist = maxdist; return true; }
86                     hit = true;
87                 }
88             }
89         }
90         else if(curnode->isleaf(nearidx))
91         {
92             if(triintersect(tris[curnode->childindex(nearidx)], o, ray, maxdist, maxdist, mode, noclip))
93             {
94                 if(mode&RAY_SHADOW) { dist = maxdist; return true; }
95                 hit = true;
96             }
97             if(farsplit < tmax)
98             {
99                 if(!curnode->isleaf(faridx))
100                 {
101                     curnode = &nodes[curnode->childindex(faridx)];
102                     tmin = max(tmin, farsplit);
103                     continue;
104                 }
105                 else if(triintersect(tris[curnode->childindex(faridx)], o, ray, maxdist, maxdist, mode, noclip))
106                 {
107                     if(mode&RAY_SHADOW) { dist = maxdist; return true; }
108                     hit = true;
109                 }
110             }
111         }
112         else
113         {
114             if(farsplit < tmax)
115             {
116                 if(!curnode->isleaf(faridx))
117                 {
118                     BIHStack &save = stack.add();
119                     save.node = &nodes[curnode->childindex(faridx)];
120                     save.tmin = max(tmin, farsplit);
121                     save.tmax = tmax;
122                 }
123                 else if(triintersect(tris[curnode->childindex(faridx)], o, ray, maxdist, maxdist, mode, noclip))
124                 {
125                     if(mode&RAY_SHADOW) { dist = maxdist; return true; }
126                     hit = true;
127                 }
128             }
129             curnode = &nodes[curnode->childindex(nearidx)];
130             tmax = min(tmax, nearsplit);
131             continue;
132         }
133         if(stack.empty()) { if(hit) { dist = maxdist; return true; } return false; }
134         BIHStack &restore = stack.pop();
135         curnode = restore.node;
136         tmin = restore.tmin;
137         tmax = restore.tmax;
138     }
139 }
140 
141 static BIH::tri *sorttris = NULL;
142 static int sortaxis = 0;
143 
bihsort(const ushort * x,const ushort * y)144 static int bihsort(const ushort *x, const ushort *y)
145 {
146     BIH::tri &xtri = sorttris[*x], &ytri = sorttris[*y];
147     float xmin = min(xtri.a[sortaxis], min(xtri.b[sortaxis], xtri.c[sortaxis])),
148           ymin = min(ytri.a[sortaxis], min(ytri.b[sortaxis], ytri.c[sortaxis]));
149     if(xmin < ymin) return -1;
150     if(xmin > ymin) return 1;
151     return 0;
152 }
153 
build(vector<BIHNode> & buildnodes,ushort * indices,int numindices,int depth)154 void BIH::build(vector<BIHNode> &buildnodes, ushort *indices, int numindices, int depth)
155 {
156     maxdepth = max(maxdepth, depth);
157 
158     vec vmin(1e16f, 1e16f, 1e16f), vmax(-1e16f, -1e16f, -1e16f);
159     loopi(numindices)
160     {
161         tri &tri = tris[indices[i]];
162         loopk(3)
163         {
164             float amin = min(tri.a[k], min(tri.b[k], tri.c[k])),
165                   amax = max(tri.a[k], max(tri.b[k], tri.c[k]));
166             vmin[k] = min(vmin[k], amin);
167             vmax[k] = max(vmax[k], amax);
168         }
169     }
170     if(depth==1)
171     {
172         bbmin = vmin;
173         bbmax = vmax;
174     }
175 
176     int axis = 2;
177     loopk(2) if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis]) axis = k;
178 
179 /*
180     sorttris = tris;
181     sortaxis = axis;
182     quicksort(indices, numindices, bihsort);
183     tri &median = tris[numindices/2];
184     float split = min(median.a[axis], min(median.b[axis], median.c[axis]));
185 */
186 
187     float split = 0.5f*(vmax[axis] + vmin[axis]);
188 
189     float splitleft = SHRT_MIN, splitright = SHRT_MAX;
190     int left, right;
191     for(left = 0, right = numindices; left < right;)
192     {
193         tri &tri = tris[indices[left]];
194         float amin = min(tri.a[axis], min(tri.b[axis], tri.c[axis])),
195               amax = max(tri.a[axis], max(tri.b[axis], tri.c[axis]));
196         if(max(split - amin, 0.0f) > max(amax - split, 0.0f))
197         {
198             ++left;
199             splitleft = max(splitleft, amax);
200         }
201         else
202         {
203             --right;
204             swap(indices[left], indices[right]);
205             splitright = min(splitright, amin);
206         }
207     }
208 
209     if(!left || right==numindices)
210     {
211         sorttris = tris;
212         sortaxis = axis;
213         quicksort(indices, numindices, bihsort);
214 
215         left = right = numindices/2;
216         splitleft = SHRT_MIN;
217         splitright = SHRT_MAX;
218         loopi(numindices)
219         {
220             tri &tri = tris[indices[i]];
221             if(i < left) splitleft = max(splitleft, max(tri.a[axis], max(tri.b[axis], tri.c[axis])));
222             else splitright = min(splitright, min(tri.a[axis], min(tri.b[axis], tri.c[axis])));
223         }
224     }
225 
226     int node = buildnodes.length();
227     buildnodes.add();
228     buildnodes[node].split[0] = short(ceil(splitleft));
229     buildnodes[node].split[1] = short(floor(splitright));
230 
231     if(left==1) buildnodes[node].child[0] = (axis<<14) | indices[0];
232     else
233     {
234         buildnodes[node].child[0] = (axis<<14) | buildnodes.length();
235         build(buildnodes, indices, left, depth+1);
236     }
237 
238     if(numindices-right==1) buildnodes[node].child[1] = (1<<15) | (left==1 ? 1<<14 : 0) | indices[right];
239     else
240     {
241         buildnodes[node].child[1] = (left==1 ? 1<<14 : 0) | buildnodes.length();
242         build(buildnodes, &indices[right], numindices-right, depth+1);
243     }
244 }
245 
BIH(vector<tri> * t)246 BIH::BIH(vector<tri> *t)
247 {
248     numtris = t[0].length() + t[1].length();
249     if(!numtris)
250     {
251         tris = NULL;
252         numnodes = 0;
253         nodes = NULL;
254         maxdepth = 0;
255         return;
256     }
257 
258     tris = new tri[numtris];
259     noclip = &tris[t[0].length()];
260     memcpy(tris, t[0].getbuf(), t[0].length()*sizeof(tri));
261     memcpy(noclip, t[1].getbuf(), t[1].length()*sizeof(tri));
262 
263     vector<BIHNode> buildnodes;
264     ushort *indices = new ushort[numtris];
265     loopi(numtris) indices[i] = i;
266 
267     maxdepth = 0;
268 
269     build(buildnodes, indices, numtris);
270 
271     delete[] indices;
272 
273     numnodes = buildnodes.length();
274     nodes = new BIHNode[numnodes];
275     memcpy(nodes, buildnodes.getbuf(), numnodes*sizeof(BIHNode));
276 
277     // convert tri.b/tri.c to edges
278     loopi(numtris)
279     {
280         tri &tri = tris[i];
281         tri.b.sub(tri.a);
282         tri.c.sub(tri.a);
283     }
284 }
285 
yawray(vec & o,vec & ray,float angle)286 static inline void yawray(vec &o, vec &ray, float angle)
287 {
288     angle *= RAD;
289     float c = cosf(angle), s = sinf(angle),
290           ox = o.x, oy = o.y,
291           rx = ray.x, ry = ray.y;
292     o.x = ox*c - oy*s;
293     o.y = oy*c + ox*s;
294     ray.x = rx*c - ry*s;
295     ray.y = ry*c + rx*s;
296     ray.normalize();
297 }
298 
mmintersect(const extentity & e,const vec & o,const vec & ray,float maxdist,int mode,float & dist)299 bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist, int mode, float &dist)
300 {
301     model *m = loadmodel(NULL, e.attrs[0]);
302     if(!m) return false;
303     if(mode&RAY_SHADOW)
304     {
305         if(!m->shadow || e.lastemit || e.attrs[5]&MMT_NOSHADOW) return false;
306     }
307     else if((mode&RAY_ENTS)!=RAY_ENTS && !m->collide) return false;
308     if(!m->bih && !m->setBIH()) return false;
309     if(!maxdist) maxdist = 1e16f;
310     vec yo(o);
311     yo.sub(e.o);
312     float yaw = -180.0f-(float)(e.attrs[1]%360);
313     vec yray(ray);
314     if(yaw != 0) yawray(yo, yray, yaw);
315     if(m->bih->traverse(yo, yray, maxdist, dist, mode))
316     {
317         if(!(mode&RAY_SHADOW) && yaw != 0) hitsurface.rotate_around_z(-yaw*RAD);
318         return true;
319     }
320     return false;
321 }
322 
323