1 
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 
6 #include "../core/system.h"
7 #include "../core/vmath.h"
8 #include "../core/polygon.h"
9 #include "../core/obb.h"
10 #include "../room.h"
11 #include "render.h"
12 #include "frustum.h"
13 #include "camera.h"
14 
15 
16 #define SPLIT_EMPTY         (0x00)
17 #define SPLIT_SUCCES        (0x01)
18 
CFrustumManager(uint32_t buffer_size)19 CFrustumManager::CFrustumManager(uint32_t buffer_size)
20 {
21     m_buffer_size = buffer_size;
22     m_allocated = 0;
23     m_buffer = (uint8_t*)malloc(buffer_size * sizeof(uint8_t));
24     memset(m_buffer, 0, (buffer_size * sizeof(uint8_t)));
25     m_need_realloc = false;
26 }
27 
~CFrustumManager()28 CFrustumManager::~CFrustumManager()
29 {
30     if(m_buffer != NULL)
31     {
32         free(m_buffer);
33         m_buffer = NULL;
34     }
35 }
36 
Reset()37 void CFrustumManager::Reset()
38 {
39     m_allocated = 0;
40     if(m_need_realloc)
41     {
42         uint32_t new_buffer_size = m_buffer_size * 1.5;
43         uint8_t *new_buffer = (uint8_t*)malloc(new_buffer_size * sizeof(uint8_t));
44         if(new_buffer != NULL)
45         {
46             free(m_buffer);
47             m_buffer = new_buffer;
48             m_buffer_size = new_buffer_size;
49         }
50         m_need_realloc = false;
51     }
52 }
53 
CreateFrustum()54 frustum_p CFrustumManager::CreateFrustum()
55 {
56     if((!m_need_realloc) && (m_allocated + sizeof(frustum_t) < m_buffer_size))
57     {
58         frustum_p ret = (frustum_p)(m_buffer + m_allocated);
59         m_allocated += sizeof(frustum_t);
60         ret->vertex_count = 0;
61         ret->parents_count = 0;
62         ret->next = NULL;
63         ret->parent = NULL;
64         ret->planes = NULL;
65         ret->vertex = NULL;
66         ret->cam_pos = NULL;
67         vec4_set_zero(ret->norm);
68         return ret;
69     }
70 
71     m_need_realloc = true;
72     return NULL;
73 }
74 
Alloc(uint32_t size)75 float *CFrustumManager::Alloc(uint32_t size)
76 {
77     size *= sizeof(float);
78     if((!m_need_realloc) && (m_allocated + size < m_buffer_size))
79     {
80         float *ret = (float*)(m_buffer + m_allocated);
81         m_allocated += size;
82         return ret;
83     }
84 
85     m_need_realloc = true;
86     return NULL;
87 }
88 
SplitPrepare(frustum_p frustum,struct portal_s * p,frustum_p emitter)89 void CFrustumManager::SplitPrepare(frustum_p frustum, struct portal_s *p, frustum_p emitter)
90 {
91     frustum->vertex_count = p->vertex_count;
92     frustum->vertex = this->Alloc(3 * (p->vertex_count + emitter->vertex_count + 1));
93     if(frustum->vertex != NULL)
94     {
95         memcpy(frustum->vertex, p->vertex, 3 * p->vertex_count * sizeof(float));
96         vec4_copy_inv(frustum->norm, p->norm);
97     }
98     else
99     {
100         frustum->vertex_count = 0;
101         m_need_realloc = true;
102     }
103     frustum->parent = NULL;
104 }
105 
SplitByPlane(frustum_p p,float n[4],float * buf)106 int CFrustumManager::SplitByPlane(frustum_p p, float n[4], float *buf)
107 {
108     if(!m_need_realloc)
109     {
110         float *curr_v, *prev_v, *v, t, dir[3];
111         float dist[2];
112         uint16_t added = 0;
113 
114         curr_v = p->vertex;
115         prev_v = p->vertex + 3*(p->vertex_count-1);
116         dist[0] = vec3_plane_dist(n, prev_v);
117         v = buf;
118         for(uint16_t i = 0; i < p->vertex_count; i++)
119         {
120             dist[1] = vec3_plane_dist(n, curr_v);
121 
122             if(dist[1] > SPLIT_EPSILON)
123             {
124                 if(dist[0] < -SPLIT_EPSILON)
125                 {
126                     vec3_sub(dir, curr_v, prev_v);
127                     vec3_ray_plane_intersect(prev_v, dir, n, v, t);
128                     v += 3;
129                     added++;
130                 }
131                 vec3_copy(v, curr_v);
132                 v += 3;
133                 added++;
134             }
135             else if(dist[1] < -SPLIT_EPSILON)
136             {
137                 if(dist[0] > SPLIT_EPSILON)
138                 {
139                     vec3_sub(dir, curr_v, prev_v);
140                     vec3_ray_plane_intersect(prev_v, dir, n, v, t);
141                     v += 3;
142                     added++;
143                 }
144             }
145             else
146             {
147                 vec3_copy(v, curr_v);
148                 v += 3;
149                 added++;
150             }
151 
152             prev_v = curr_v;
153             curr_v += 3;
154             dist[0] = dist[1];
155         }
156 
157         if(added <= 2)
158         {
159             p->vertex_count = 0;
160             return SPLIT_EMPTY;
161         }
162 
163     #if 0
164         p->vertex_count = added;
165         memcpy(p->vertex, buf, added*3*sizeof(float));
166     #else       // filter repeating (too closest) points
167         curr_v = buf;
168         prev_v = buf + 3 * (added - 1);
169         v = p->vertex;
170         p->vertex_count = 0;
171         for(uint16_t i = 0; i < added; i++)
172         {
173             if(vec3_dist_sq(prev_v, curr_v) > SPLIT_EPSILON * SPLIT_EPSILON)
174             {
175                 vec3_copy(v, curr_v);
176                 v += 3;
177                 p->vertex_count++;
178             }
179             prev_v = curr_v;
180             curr_v += 3;
181         }
182 
183         if(p->vertex_count <= 2)
184         {
185             p->vertex_count = 0;
186             return SPLIT_EMPTY;
187         }
188     #endif
189         return SPLIT_SUCCES;
190     }
191 
192     return SPLIT_EMPTY;
193 }
194 
GenClipPlanes(frustum_p p,struct camera_s * cam)195 void CFrustumManager::GenClipPlanes(frustum_p p, struct camera_s *cam)
196 {
197     if(m_allocated + p->vertex_count * 4 * sizeof(float) >= m_buffer_size)
198     {
199         m_need_realloc = true;
200     }
201 
202     if((!m_need_realloc) && (p->vertex_count > 0))
203     {
204         float V1[3], V2[3], *prev_v, *curr_v, *next_v, *r;
205         p->planes = this->Alloc(4 * p->vertex_count);
206 
207         next_v = p->vertex;
208         curr_v = p->vertex + 3 * (p->vertex_count - 1);
209         prev_v = curr_v - 3;
210         r = p->planes;
211 
212         //==========================================================================
213 
214         for(uint16_t i = 0; i < p->vertex_count; i++, r += 4)
215         {
216             float t;
217             vec3_sub(V1, prev_v, cam->transform.M4x4 + 12);
218             vec3_sub(V2, curr_v, prev_v);
219             vec3_norm(V1, t);
220             vec3_norm(V2, t);
221             vec3_cross(r, V1, V2)
222             vec3_norm(r, t);
223             r[3] = -vec3_dot(r, curr_v);
224             vec4_inv(r);
225 
226             prev_v = curr_v;
227             curr_v = next_v;
228             next_v += 3;
229         }
230 
231         p->cam_pos = cam->transform.M4x4 + 12;
232     }
233 }
234 
PortalFrustumIntersect(struct portal_s * portal,frustum_p emitter,struct camera_s * cam)235 frustum_p CFrustumManager::PortalFrustumIntersect(struct portal_s *portal, frustum_p emitter, struct camera_s *cam)
236 {
237     if(!m_need_realloc)
238     {
239         room_p dest_room = portal->dest_room->real_room;
240         int in_dist = 0, in_face = 0;
241         float *n = cam->frustum->norm;
242         float *v = portal->vertex;
243 
244         if((dest_room == cam->current_room) || vec3_plane_dist(portal->norm, cam->transform.M4x4 + 12) < -SPLIT_EPSILON)            // non face or degenerate to the line portal
245         {
246             return NULL;
247         }
248 
249         for(uint16_t i = 0; i < portal->vertex_count; i++, v += 3)
250         {
251             if((in_dist == 0) && (vec3_plane_dist(n, v) < cam->dist_far))
252             {
253                 in_dist = 1;
254             }
255             if((in_face == 0) && (vec3_plane_dist(emitter->norm, v) > 0.0))
256             {
257                 in_face = 1;
258             }
259         }
260 
261         if((in_dist == 0) || (in_face == 0))
262         {
263             return NULL;
264         }
265 
266         /*
267          * Search for the first free room's frustum
268          */
269         uint32_t original_allocated = m_allocated;
270         frustum_p prev = NULL, current_gen = NULL;
271         if(dest_room->frustum == NULL)
272         {
273             current_gen = dest_room->frustum = this->CreateFrustum();
274         }
275         else
276         {
277             prev = dest_room->frustum;
278             while(prev->next)
279             {
280                 prev = prev->next;
281             }
282             current_gen = prev->next = this->CreateFrustum();                   // generate new frustum.
283         }
284 
285         if(m_need_realloc)
286         {
287             return NULL;
288         }
289 
290         this->SplitPrepare(current_gen, portal, emitter);                       // prepare to the clipping
291         if(m_need_realloc)
292         {
293             if(prev)
294             {
295                 prev->next = NULL;
296             }
297             else
298             {
299                 dest_room->frustum = NULL;
300             }
301             return NULL;
302         }
303 
304         int buf_size = (current_gen->vertex_count + emitter->vertex_count + 4) * 3 * sizeof(float);
305         float *tmp = (float*)Sys_GetTempMem(buf_size);
306         if(this->SplitByPlane(current_gen, emitter->norm, tmp))                 // splitting by main frustum clip plane
307         {
308             n = emitter->planes;
309             for(uint16_t i = 0; i < emitter->vertex_count; i++, n += 4)
310             {
311                 if(!this->SplitByPlane(current_gen, n, tmp))
312                 {
313                     if(prev)
314                     {
315                         prev->next = NULL;
316                     }
317                     else
318                     {
319                         dest_room->frustum = NULL;
320                     }
321                     Sys_ReturnTempMem(buf_size);
322                     m_allocated = original_allocated;
323                     return NULL;
324                 }
325             }
326 
327             this->GenClipPlanes(current_gen, cam);                              // all is OK, let us generate clipplanes
328             if(m_need_realloc)
329             {
330                 if(prev)
331                 {
332                     prev->next = NULL;
333                 }
334                 else
335                 {
336                     dest_room->frustum = NULL;
337                 }
338                 Sys_ReturnTempMem(buf_size);
339                 m_allocated = original_allocated;
340                 return NULL;
341             }
342 
343             current_gen->parent = emitter;                                      // add parent pointer
344             current_gen->parents_count = emitter->parents_count + 1;
345             Sys_ReturnTempMem(buf_size);
346             return current_gen;
347         }
348 
349         if(prev)
350         {
351             prev->next = NULL;
352         }
353         else
354         {
355             dest_room->frustum = NULL;
356         }
357         m_allocated = original_allocated;
358         Sys_ReturnTempMem(buf_size);
359     }
360 
361     return NULL;
362 }
363 
364 /*
365  ************************* END FRUSTUM MANAGER IMPLEMENTATION*******************
366  */
367 
368 /**
369  * we need that checking to avoid infinite recursions
370  */
Frustum_HaveParent(frustum_p parent,frustum_p frustum)371 bool Frustum_HaveParent(frustum_p parent, frustum_p frustum)
372 {
373     while(frustum)
374     {
375         if(parent == frustum)
376         {
377             return true;
378         }
379         frustum = frustum->parent;
380     }
381     return false;
382 }
383 
Frustum_IsPolyVisible(struct polygon_s * p,struct frustum_s * frustum,bool check_backface)384 bool Frustum_IsPolyVisible(struct polygon_s *p, struct frustum_s *frustum, bool check_backface)
385 {
386     float t, dir[3], T[3], dist[2];
387     float *prev_n, *curr_n, *next_n;
388     vertex_p curr_v, prev_v;
389     char ins, outs;
390 
391     if(check_backface && ((vec3_plane_dist(p->plane, frustum->cam_pos)) < 0.0f))
392     {
393         return false;
394     }
395 
396     vec3_sub(dir, frustum->vertex, frustum->cam_pos);
397     if(Polygon_RayIntersect(p, dir, frustum->cam_pos, &t))
398     {
399         return true;
400     }
401 
402     next_n = frustum->planes;
403     curr_n = frustum->planes + 4*(frustum->vertex_count-1);
404     prev_n = curr_n - 4;
405     ins = 1;
406     for(uint16_t i = 0; i < frustum->vertex_count; i++)
407     {
408         curr_v = p->vertices;
409         prev_v = p->vertices + p->vertex_count - 1;
410         dist[0] = vec3_plane_dist(curr_n, prev_v->position);
411         outs = 1;
412         for(uint16_t j = 0; j < p->vertex_count; j++)
413         {
414             dist[1] = vec3_plane_dist(curr_n, curr_v->position);
415             if(ABS(dist[0]) < SPLIT_EPSILON)
416             {
417                 if((vec3_plane_dist(prev_n, prev_v->position) > -SPLIT_EPSILON) &&
418                    (vec3_plane_dist(next_n, prev_v->position) > -SPLIT_EPSILON) &&
419                    (vec3_plane_dist(frustum->norm, prev_v->position) > -SPLIT_EPSILON))
420                 {
421                     return true;
422                 }
423             }
424 
425             if((dist[0] * dist[1] < 0) && ABS(dist[1]) >= SPLIT_EPSILON)
426             {
427                 vec3_sub(dir, curr_v->position, prev_v->position)
428                 vec3_ray_plane_intersect(prev_v->position, dir, curr_n, T, t)
429                 if((vec3_plane_dist(prev_n, T) > -SPLIT_EPSILON) && (vec3_plane_dist(next_n, T) > -SPLIT_EPSILON))
430                 {
431                     return 1;
432                 }
433             }
434 
435             if(dist[1] < -SPLIT_EPSILON)
436             {
437                 ins = 0;
438             }
439             else
440             {
441                 outs = 0;
442             }
443 
444             prev_v = curr_v;
445             curr_v ++;
446             dist[0] = dist[1];
447         }
448 
449         if(outs)
450         {
451             return false;
452         }
453         prev_n = curr_n;
454         curr_n = next_n;
455         next_n += 4;
456     }
457     if(ins)
458     {
459         return true;
460     }
461 
462     return false;
463 }
464 
465 /**
466  *
467  * @param bbmin - aabb corner (x_min, y_min, z_min)
468  * @param bbmax - aabb corner (x_max, y_max, z_max)
469  * @param frustum - test frustum
470  * @return 1 if aabb is in frustum.
471  */
Frustum_IsAABBVisible(float bbmin[3],float bbmax[3],struct frustum_s * frustum)472 bool Frustum_IsAABBVisible(float bbmin[3], float bbmax[3], struct frustum_s *frustum)
473 {
474     bool inside = true;
475     polygon_t poly;
476     vertex_t vert[4];
477 
478     poly.vertices = vert;
479     poly.vertex_count = 4;
480 
481     /* X_AXIS */
482 
483     poly.plane[1] = 0.0;
484     poly.plane[2] = 0.0;
485     if(frustum->cam_pos[0] < bbmin[0])
486     {
487         poly.plane[0] = -1.0;
488         poly.plane[3] = bbmin[0];
489         vert[0].position[0] = bbmin[0];
490         vert[0].position[1] = bbmax[1];
491         vert[0].position[2] = bbmax[2];
492 
493         vert[1].position[0] = bbmin[0];
494         vert[1].position[1] = bbmin[1];
495         vert[1].position[2] = bbmax[2];
496 
497         vert[2].position[0] = bbmin[0];
498         vert[2].position[1] = bbmin[1];
499         vert[2].position[2] = bbmin[2];
500 
501         vert[3].position[0] = bbmin[0];
502         vert[3].position[1] = bbmax[1];
503         vert[3].position[2] = bbmin[2];
504 
505         if(Frustum_IsPolyVisible(&poly, frustum, true))
506         {
507             return true;
508         }
509         inside = false;
510     }
511     else if(frustum->cam_pos[0] > bbmax[0])
512     {
513         poly.plane[0] = 1.0;
514         poly.plane[3] =-bbmax[0];
515         vert[0].position[0] = bbmax[0];
516         vert[0].position[1] = bbmax[1];
517         vert[0].position[2] = bbmax[2];
518 
519         vert[1].position[0] = bbmax[0];
520         vert[1].position[1] = bbmin[1];
521         vert[1].position[2] = bbmax[2];
522 
523         vert[2].position[0] = bbmax[0];
524         vert[2].position[1] = bbmin[1];
525         vert[2].position[2] = bbmin[2];
526 
527         vert[3].position[0] = bbmax[0];
528         vert[3].position[1] = bbmax[1];
529         vert[3].position[2] = bbmin[2];
530 
531         if(Frustum_IsPolyVisible(&poly, frustum, true))
532         {
533             return true;
534         }
535         inside = false;
536     }
537 
538     /* Y AXIS */
539 
540     poly.plane[0] = 0.0;
541     poly.plane[2] = 0.0;
542     if(frustum->cam_pos[1] < bbmin[1])
543     {
544         poly.plane[1] = -1.0;
545         poly.plane[3] = bbmin[1];
546         vert[0].position[0] = bbmax[0];
547         vert[0].position[1] = bbmin[1];
548         vert[0].position[2] = bbmax[2];
549 
550         vert[1].position[0] = bbmin[0];
551         vert[1].position[1] = bbmin[1];
552         vert[1].position[2] = bbmax[2];
553 
554         vert[2].position[0] = bbmin[0];
555         vert[2].position[1] = bbmin[1];
556         vert[2].position[2] = bbmin[2];
557 
558         vert[3].position[0] = bbmax[0];
559         vert[3].position[1] = bbmin[1];
560         vert[3].position[2] = bbmin[2];
561 
562         if(Frustum_IsPolyVisible(&poly, frustum, true))
563         {
564             return true;
565         }
566         inside = false;
567     }
568     else if(frustum->cam_pos[1] > bbmax[1])
569     {
570         poly.plane[1] = 1.0;
571         poly.plane[3] = -bbmax[1];
572         vert[0].position[0] = bbmax[0];
573         vert[0].position[1] = bbmax[1];
574         vert[0].position[2] = bbmax[2];
575 
576         vert[1].position[0] = bbmin[0];
577         vert[1].position[1] = bbmax[1];
578         vert[1].position[2] = bbmax[2];
579 
580         vert[2].position[0] = bbmin[0];
581         vert[2].position[1] = bbmax[1];
582         vert[2].position[2] = bbmin[2];
583 
584         vert[3].position[0] = bbmax[0];
585         vert[3].position[1] = bbmax[1];
586         vert[3].position[2] = bbmin[2];
587 
588         if(Frustum_IsPolyVisible(&poly, frustum, true))
589         {
590             return true;
591         }
592         inside = false;
593     }
594 
595     /* Z AXIS */
596 
597     poly.plane[0] = 0.0;
598     poly.plane[1] = 0.0;
599     if(frustum->cam_pos[2] < bbmin[2])
600     {
601         poly.plane[2] = -1.0;
602         poly.plane[3] = bbmin[2];
603         vert[0].position[0] = bbmax[0];
604         vert[0].position[1] = bbmax[1];
605         vert[0].position[2] = bbmin[2];
606 
607         vert[1].position[0] = bbmin[0];
608         vert[1].position[1] = bbmax[1];
609         vert[1].position[2] = bbmin[2];
610 
611         vert[2].position[0] = bbmin[0];
612         vert[2].position[1] = bbmin[1];
613         vert[2].position[2] = bbmin[2];
614 
615         vert[3].position[0] = bbmax[0];
616         vert[3].position[1] = bbmin[1];
617         vert[3].position[2] = bbmin[2];
618 
619         if(Frustum_IsPolyVisible(&poly, frustum, true))
620         {
621             return true;
622         }
623         inside = false;
624     }
625     else if(frustum->cam_pos[2] > bbmax[2])
626     {
627         poly.plane[2] = 1.0;
628         poly.plane[3] = -bbmax[2];
629         vert[0].position[0] = bbmax[0];
630         vert[0].position[1] = bbmax[1];
631         vert[0].position[2] = bbmax[2];
632 
633         vert[1].position[0] = bbmin[0];
634         vert[1].position[1] = bbmax[1];
635         vert[1].position[2] = bbmax[2];
636 
637         vert[2].position[0] = bbmin[0];
638         vert[2].position[1] = bbmin[1];
639         vert[2].position[2] = bbmax[2];
640 
641         vert[3].position[0] = bbmax[0];
642         vert[3].position[1] = bbmin[1];
643         vert[3].position[2] = bbmax[2];
644 
645         if(Frustum_IsPolyVisible(&poly, frustum, true))
646         {
647             return true;
648         }
649         inside = false;
650     }
651 
652     return inside;
653 }
654 
655 
Frustum_IsOBBVisible(struct obb_s * obb,struct frustum_s * frustum)656 bool Frustum_IsOBBVisible(struct obb_s *obb, struct frustum_s *frustum)
657 {
658     bool inside = true;
659     float t;
660     polygon_p p = obb->polygons;
661 
662     for(int i = 0; i < 6; i++, p++)
663     {
664         t = vec3_plane_dist(p->plane, frustum->cam_pos);
665         if((t >= 0.0f) && Frustum_IsPolyVisible(p, frustum, true))
666         {
667             return true;
668         }
669         if(inside && (t > 0.0f))
670         {
671             inside = false;
672         }
673     }
674 
675     return inside;
676 }
677 
Frustum_IsOBBVisibleInFrustumList(struct obb_s * obb,struct frustum_s * frustum)678 bool Frustum_IsOBBVisibleInFrustumList(struct obb_s *obb, struct frustum_s *frustum)
679 {
680     for(; frustum; frustum = frustum->next)
681     {
682         if(Frustum_IsOBBVisible(obb, frustum))
683         {
684             return true;
685         }
686     }
687 
688     return false;
689 }
690 
691 /*
692  * PORTALS
693  */
Portal_Create(unsigned int vcount)694 portal_p Portal_Create(unsigned int vcount)
695 {
696     portal_p p = (portal_p)malloc(sizeof(portal_t));
697     p->vertex_count = vcount;
698     p->vertex = (float*)malloc(3*vcount*sizeof(float));
699     p->dest_room = NULL;
700     return p;
701 }
702 
Portal_Clear(portal_p p)703 void Portal_Clear(portal_p p)
704 {
705     if(p)
706     {
707         if(p->vertex)
708         {
709             free(p->vertex);
710             p->vertex = NULL;
711         }
712         p->vertex_count = 0;
713         p->dest_room = NULL;
714     }
715 }
716 
Portal_Move(portal_p p,float mv[3])717 void Portal_Move(portal_p p, float mv[3])
718 {
719     float *v = p->vertex;
720 
721     vec3_add(p->centre, p->centre, mv);
722     for(uint16_t i = 0; i < p->vertex_count; i++, v+=3)
723     {
724         vec3_add(v, v, mv);
725     }
726 
727     p->norm[3] = -vec3_dot(p->norm, p->vertex);
728 }
729 
730 /**
731  * Барицентрический метод определения пересечения луча и треугольника
732  * p - tested portal
733  * dir - ray directionа
734  * dot - point of ray - plane intersection
735  */
Portal_RayIntersect(portal_p p,float dir[3],float dot[3])736 bool Portal_RayIntersect(portal_p p, float dir[3], float dot[3])
737 {
738     float t, u, v, E1[3], E2[3], P[3], Q[3], T[3], *vd;
739 
740     u = vec3_dot(p->norm, dir);
741     if(ABS(u) < SPLIT_EPSILON)
742     {
743         return false;
744     }
745     t = - p->norm[3] - vec3_dot(p->norm, dot);
746     t /= u;
747     if(0.0 > t)
748     {
749         return false;
750     }
751 
752     vd = p->vertex;
753     vec3_sub(T, dot, vd)
754 
755     vec3_sub(E2, vd+3, vd)
756     for(uint16_t i = 0; i < p->vertex_count - 2; i++, vd += 3)
757     {
758         vec3_copy(E1, E2)                                                       // PREV
759         vec3_sub(E2, vd+6, p->vertex)                                           // NEXT
760 
761         vec3_cross(P, dir, E2)
762         vec3_cross(Q, T, E1)
763 
764         t = vec3_dot(P, E1);
765         u = vec3_dot(P, T);
766         u /= t;
767         v = vec3_dot(Q, dir);
768         v /= t;
769         t = 1.0 - u - v;
770         if((u <= 1.0) && (u >= 0.0) && (v <= 1.0) && (v >= 0.0) && (t <= 1.0) && (t >= 0.0))
771         {
772             return true;
773         }
774     }
775 
776     return false;
777 }
778 
Portal_GenNormale(portal_p p)779 void Portal_GenNormale(portal_p p)
780 {
781     float v1[3], v2[3];
782 
783     vec3_sub(v1, p->vertex+3, p->vertex)
784     vec3_sub(v2, p->vertex+6, p->vertex+3)
785     vec3_cross(p->norm, v1, v2)
786     p->norm[3] = vec3_abs(p->norm);
787     vec3_norm_plane(p->norm, p->vertex, p->norm[3])
788 }
789