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