4 typedef struct clipsurf_s
6 struct clipsurf_s *next, *prev;
11 void (*callback)(void *nativedata, void *nativedata2);
14 float wstepx, wstepy, w00;
15 // wcurrent is a cached copy of w00 + wstepy * y,
16 // updated each time the surface is added to the stack,
17 // for quicker comparisons.
22 typedef struct clipedge_s
24 float x, realx, realxstep;
25 struct clipedge_s *next, *prev, *nextremove;
32 clipsurf_t *pavailsurf, *clipsurfs, *clipsurfsend;
33 clipedge_t *pavailedge, *clipedges, *clipedgesend, *newedges, **removeedges;
36 clipedge_t edgehead, edgetail;
37 clipedge_t maxedge = {2000000000.0f};
39 cvar_t r_clipwidth = {0, "r_clipwidth", "800"};
40 cvar_t r_clipheight = {0, "r_clipheight", "600"};
41 cvar_t r_clipedges = {CVAR_SAVE, "r_clipedges", "32768"};
42 cvar_t r_clipsurfaces = {CVAR_SAVE, "r_clipsurfaces", "8192"};
44 int clipwidth = 0, clipheight = 0;
45 int maxclipsurfs = 0, maxclipedges = 0;
46 int needededges, neededsurfs;
51 float w; // inverse depth (1/z)
55 clippixel_t *clipbuffer;
58 float r_clip_viewmatrix[3][3], r_clip_viewmulx, r_clip_viewmuly, r_clip_viewcenterx, r_clip_viewcentery;
59 tinyplane_t r_clip_viewplane[5];
61 mempool_t *r_clip_mempool;
63 void R_Clip_MakeViewMatrix(void)
65 float pixelaspect, screenaspect, horizontalfieldofview, verticalfieldofview;
66 pixelaspect = (float) clipheight / (float) clipwidth * 320 / 240.0;
67 horizontalfieldofview = 2.0 * tan (r_refdef.fov_x/360*M_PI);
68 screenaspect = clipwidth * pixelaspect / clipheight;
69 verticalfieldofview = horizontalfieldofview / screenaspect;
70 r_clip_viewcenterx = clipwidth * 0.5 - 0.5;
71 r_clip_viewcentery = clipheight * 0.5 - 0.5;
72 r_clip_viewmulx = clipwidth / horizontalfieldofview;
73 r_clip_viewmuly = r_clip_viewmulx * pixelaspect;
74 // this constructs a transposed rotation matrix for the view (transposed matrices do the opposite of their normal behavior)
75 VectorCopy (vright, r_clip_viewmatrix[0]);
76 VectorNegate (vup, r_clip_viewmatrix[1]);
77 VectorCopy (vpn, r_clip_viewmatrix[2]);
78 VectorCopy (vpn, r_clip_viewplane[0].normal);
79 r_clip_viewplane[0].dist = DotProduct(r_origin, vpn);
80 memcpy(&r_clip_viewplane[1], &frustum[0], sizeof(tinyplane_t));
81 memcpy(&r_clip_viewplane[2], &frustum[1], sizeof(tinyplane_t));
82 memcpy(&r_clip_viewplane[3], &frustum[2], sizeof(tinyplane_t));
83 memcpy(&r_clip_viewplane[4], &frustum[3], sizeof(tinyplane_t));
86 void R_Clip_StartFrame(void)
89 int newwidth, newheight, newmaxedges, newmaxsurfs;
90 newwidth = bound(80, r_clipwidth.integer, vid.realwidth * 2);
91 newheight = bound(60, r_clipheight.integer, vid.realheight * 2);
92 newmaxedges = bound(128, r_clipedges.integer, 262144);
93 newmaxsurfs = bound(32, r_clipsurfaces.integer, 65536);
94 if (newwidth != clipwidth || newheight != clipheight || maxclipedges != newmaxedges || maxclipsurfs != newmaxsurfs)
107 Mem_Free(removeedges);
108 clipwidth = newwidth;
109 clipheight = newheight;
110 maxclipedges = newmaxedges;
111 maxclipsurfs = newmaxsurfs;
113 clipbuffer = Mem_Alloc(r_clip_mempool, clipwidth * clipheight * sizeof(clippixel_t));
115 clipedges = Mem_Alloc(r_clip_mempool, maxclipedges * sizeof(clipedge_t));
116 clipsurfs = Mem_Alloc(r_clip_mempool, maxclipsurfs * sizeof(clipsurf_t));
117 newedges = Mem_Alloc(r_clip_mempool, clipheight * sizeof(clipedge_t));
118 removeedges = Mem_Alloc(r_clip_mempool, clipheight * sizeof(clipedge_t *));
119 clipedgesend = clipedges + maxclipedges;
120 clipsurfsend = clipsurfs + maxclipsurfs;
123 memset(clipbuffer, 0, clipwidth * clipheight * sizeof(clippixel_t));
125 pavailedge = clipedges;
126 pavailsurf = clipsurfs;
127 // Clear the lists of edges to add and remove on each scan line.
131 for (i = 0;i < clipheight;i++)
133 newedges[i].next = &maxedge;
134 removeedges[i] = NULL;
137 R_Clip_MakeViewMatrix();
140 void ScanEdges (void);
142 void R_Clip_EndFrame(void)
145 if (maxclipedges < needededges)
147 Con_Printf("R_Clip: ran out of edges, increasing limit from %d to %d\n", maxclipedges, needededges);
148 Cvar_SetValue("r_clipedges", needededges);
150 if (maxclipsurfs < neededsurfs)
152 Con_Printf("R_Clip: ran out of surfaces, increasing limit from %d to %d\n", maxclipsurfs, neededsurfs);
153 Cvar_SetValue("r_clipsurfaces", neededsurfs);
157 void r_clip_start(void)
159 r_clip_mempool = Mem_AllocPool("R_Clip");
162 void r_clip_shutdown(void)
164 Mem_FreePool(&r_clip_mempool);
176 void r_clip_newmap(void)
180 void R_Clip_Init(void)
182 Cvar_RegisterVariable(&r_clipwidth);
183 Cvar_RegisterVariable(&r_clipheight);
184 Cvar_RegisterVariable(&r_clipedges);
185 Cvar_RegisterVariable(&r_clipsurfaces);
186 R_RegisterModule("R_Clip", r_clip_start, r_clip_shutdown, r_clip_newmap);
189 int R_Clip_TriangleToPlane(vec3_t point1, vec3_t point2, vec3_t point3, tinyplane_t *p)
193 VectorSubtract(point1, point2, v1);
194 VectorSubtract(point3, point2, v2);
195 CrossProduct(v1, v2, p->normal);
196 number = DotProduct(p->normal, p->normal);
199 *((int *)&y) = 0x5f3759df - ((* (int *) &number) >> 1);
200 y = y * (1.5f - (number * 0.5f * y * y));
201 VectorScale(p->normal, y, p->normal);
202 p->dist = DotProduct(point1, p->normal);
210 int R_Clip_ClipPolygonToPlane(float *in, float *out, int inpoints, int stride, tinyplane_t *plane)
212 int i, outpoints, prevside, side;
213 float *prevpoint, prevdist, dist, dot;
215 // begin with the last point, then enter the loop with the first point as current
216 prevpoint = (float *) ((qbyte *)in + stride * (inpoints - 1));
217 prevdist = DotProduct(prevpoint, plane->normal) - plane->dist;
218 prevside = prevdist >= 0 ? SIDE_FRONT : SIDE_BACK;
222 for (;i < inpoints;i++)
227 (qbyte *)in += stride;
230 dist = DotProduct(in, plane->normal) - plane->dist;
231 side = dist >= 0 ? SIDE_FRONT : SIDE_BACK;
233 if (prevside == SIDE_FRONT)
235 VectorCopy(prevpoint, out);
238 if (side == SIDE_FRONT)
241 else if (side == SIDE_BACK)
244 // generate a split point
245 dot = prevdist / (prevdist - dist);
246 out[0] = prevpoint[0] + dot * (in[0] - prevpoint[0]);
247 out[1] = prevpoint[1] + dot * (in[1] - prevpoint[1]);
248 out[2] = prevpoint[2] + dot * (in[2] - prevpoint[2]);
256 float tempverts[256][3];
257 float tempverts2[256][3];
258 float screenverts[256][3];
260 // LordHavoc: this code is based primarily on the ddjzsort code
262 // Clips polygon to view frustum and nearclip, transforms polygon to viewspace, perspective projects polygon to screenspace,
263 // and adds polygon's edges to the global edge table.
264 void R_Clip_AddPolygon (vec_t *points, int numverts, int stride, int solid, void (*callback)(void *nativedata, void *nativedata2), void *nativedata, void *nativedata2, tinyplane_t *polyplane)
266 float deltax, deltay, vx, vy, vz, fx;
267 int i, j, k, nextvert, temp, topy, bottomy, height, addededges;
269 tinyplane_t localplane;
272 if (polyplane == NULL)
274 polyplane = &localplane;
275 // calculate the plane for the polygon
276 if (!R_Clip_TriangleToPlane((float *) points, (float *) ((qbyte *)points + stride), (float *) ((qbyte *)points + 2 * stride), polyplane))
278 for (i = 0;i < numverts;i++)
279 for (j = i + 1;j < numverts;j++)
280 for (k = j + 1;k < numverts;k++)
281 if (R_Clip_TriangleToPlane((float *) ((qbyte *)points + i * stride), (float *) ((qbyte *)points + j * stride), (float *) ((qbyte *)points + k * stride), polyplane))
287 // caller hasn't checked if this polygon faces the view, so we have to check
288 if (DotProduct(r_origin, polyplane->normal) < (polyplane->dist + 0.5f))
292 // for adaptive limits
293 needededges += numverts;
296 if (pavailsurf >= clipsurfsend)
299 // clip to view frustum and nearclip
300 if (numverts < 3) return;numverts = R_Clip_ClipPolygonToPlane(points , tempverts2[0], numverts, stride, &r_clip_viewplane[0]);
301 if (numverts < 3) return;numverts = R_Clip_ClipPolygonToPlane(tempverts2[0], tempverts[0], numverts, sizeof(float) * 3, &r_clip_viewplane[1]);
302 if (numverts < 3) return;numverts = R_Clip_ClipPolygonToPlane(tempverts[0], tempverts2[0], numverts, sizeof(float) * 3, &r_clip_viewplane[2]);
303 if (numverts < 3) return;numverts = R_Clip_ClipPolygonToPlane(tempverts2[0], tempverts[0], numverts, sizeof(float) * 3, &r_clip_viewplane[3]);
304 if (numverts < 3) return;numverts = R_Clip_ClipPolygonToPlane(tempverts[0], tempverts2[0], numverts, sizeof(float) * 3, &r_clip_viewplane[4]);
308 Sys_Error("R_Clip_AddPolygon: polygon exceeded 256 vertex buffer\n");
310 // it survived the clipping, transform to viewspace and project to screenspace
312 if (pavailedge + numverts > clipedgesend)
315 for (i = 0;i < numverts;i++)
317 vx = tempverts2[i][0] - r_origin[0];
318 vy = tempverts2[i][1] - r_origin[1];
319 vz = tempverts2[i][2] - r_origin[2];
320 screenverts[i][2] = 1.0f / (r_clip_viewmatrix[2][0] * vx + r_clip_viewmatrix[2][1] * vy + r_clip_viewmatrix[2][2] * vz);
321 screenverts[i][0] = (r_clip_viewmatrix[0][0] * vx + r_clip_viewmatrix[0][1] * vy + r_clip_viewmatrix[0][2] * vz) * r_clip_viewmulx * screenverts[i][2] + r_clip_viewcenterx;
322 screenverts[i][1] = (r_clip_viewmatrix[1][0] * vx + r_clip_viewmatrix[1][1] * vy + r_clip_viewmatrix[1][2] * vz) * r_clip_viewmuly * screenverts[i][2] + r_clip_viewcentery;
325 // calculate the plane for the polygon
326 if (!R_Clip_TriangleToPlane(screenverts[0], screenverts[1], screenverts[2], &localplane))
328 for (i = 0;i < numverts;i++)
329 for (j = i + 1;j < numverts;j++)
330 for (k = j + 1;k < numverts;k++)
331 if (R_Clip_TriangleToPlane(screenverts[i], screenverts[j], screenverts[k], &localplane))
337 // Set up the 1/z gradients from the polygon, calculating the
338 // base value at screen coordinate 0,0 so we can use screen
339 // coordinates directly when calculating 1/z from the gradients
340 distinv = 1.0f / localplane.normal[2];
341 pavailsurf->wstepx = -(localplane.normal[0] * distinv);
342 pavailsurf->wstepy = -(localplane.normal[1] * distinv);
343 pavailsurf->w00 = localplane.dist * distinv;
347 // Add each edge in turn
348 for (i = 0;i < numverts;i++)
351 if (nextvert >= numverts)
354 topy = (int)ceil(screenverts[i][1]);
355 bottomy = (int)ceil(screenverts[nextvert][1]);
356 height = bottomy - topy;
358 continue; // doesn't cross any scan lines
367 if (bottomy > clipheight)
368 bottomy = clipheight;
372 pavailedge->leading = 1;
374 deltax = screenverts[i][0] - screenverts[nextvert][0];
375 deltay = screenverts[i][1] - screenverts[nextvert][1];
377 pavailedge->realxstep = deltax / deltay;
378 pavailedge->realx = screenverts[nextvert][0] + ((float)topy - screenverts[nextvert][1]) * pavailedge->realxstep;
385 if (bottomy > clipheight)
386 bottomy = clipheight;
390 pavailedge->leading = 0;
392 deltax = screenverts[nextvert][0] - screenverts[i][0];
393 deltay = screenverts[nextvert][1] - screenverts[i][1];
395 pavailedge->realxstep = deltax / deltay;
396 pavailedge->realx = screenverts[i][0] + ((float)topy - screenverts[i][1]) * pavailedge->realxstep;
399 // Put the edge on the list to be added on top scan
400 fx = pavailedge->x = bound(0.0f, pavailedge->realx, clipwidth - 0.5f);
401 pedge = &newedges[topy];
402 while (fx > pedge->next->x)
404 pavailedge->next = pedge->next;
405 pedge->next = pavailedge;
407 // Put the edge on the list to be removed after final scan
408 pavailedge->nextremove = removeedges[bottomy - 1];
409 removeedges[bottomy - 1] = pavailedge;
411 // Associate the edge with the surface
412 pavailedge->psurf = pavailsurf;
422 // Create the surface, so we'll know how to sort and draw from the edges
423 pavailsurf->next = NULL;
424 pavailsurf->prev = NULL;
425 pavailsurf->state = 0;
426 pavailsurf->visible = false;
427 pavailsurf->callback = callback;
428 pavailsurf->nativedata = nativedata;
429 pavailsurf->nativedata2 = nativedata2;
430 pavailsurf->solid = solid;
431 pavailsurf->removed = false;
435 // Scan all the edges in the global edge table into spans.
436 void ScanEdges (void)
439 float fx, fy, w, w2, clipwidthf = clipwidth - 0.5f;
440 clipedge_t *pedge, *pedge2, *ptemp;
441 clipsurf_t *psurf, *psurf2;
449 // Set up the active edge list as initially empty, containing
450 // only the sentinels (which are also the background fill). Most
451 // of these fields could be set up just once at start-up
452 edgehead.next = &edgetail;
453 edgehead.prev = NULL;
454 edgehead.x = edgehead.realx = -0.9999f; // left edge of screen
455 edgehead.realxstep = 0;
456 edgehead.leading = 1;
457 edgehead.psurf = &surfstack;
459 edgetail.next = NULL; // mark end of list
460 edgetail.prev = &edgehead;
461 edgetail.x = edgetail.realx = clipwidth + 0.5f; // right edge of screen
462 edgetail.realxstep = 0;
463 edgetail.leading = 0;
464 edgetail.psurf = &surfstack;
466 // The background surface is the entire stack initially, and
467 // is infinitely far away, so everything sorts in front of it.
468 // This could be set just once at start-up
469 surfstack.solid = true;
470 surfstack.visible = true; // no callback
471 surfstack.next = surfstack.prev = &surfstack;
472 surfstack.wcurrent = surfstack.w00 = -999999.0;
473 surfstack.wstepx = surfstack.wstepy = 0.0;
474 surfstack.removed = false;
476 // rescan causes the edges to be compared at the span level
477 // it is false if the scanline will be identical to the previous
479 for (y = 0;y < clipheight;y++)
483 cb = clipbuffer + y * clipwidth;
486 // Sort in any edges that start on this scan
487 if (newedges[y].next != &maxedge)
490 pedge = newedges[y].next;
492 while (pedge != &maxedge)
494 if (pedge->psurf->removed)
500 while (pedge->x > pedge2->next->x)
501 pedge2 = pedge2->next;
504 pedge->next = pedge2->next;
505 pedge->prev = pedge2;
506 pedge2->next->prev = pedge;
507 pedge2->next = pedge;
514 // Scan out the active edges into spans
516 // Start out with the left background edge already inserted, and the surface stack containing only the background
520 // must always rescan if rendering to wbuffer
525 for (pedge = edgehead.next;pedge;pedge = pedge->next)
528 psurf = pedge->psurf;
531 pedge2 = pedge->next;
532 pedge->prev->next = pedge->next;
533 pedge->next->prev = pedge->prev;
534 pedge->next = pedge->prev = pedge;
544 // It's a leading edge. Figure out where it is
545 // relative to the current surfaces and insert in
546 // the surface stack; if it's on top, emit the span
547 // for the current top.
548 // First, make sure the edges don't cross
549 if (++psurf->state == 1)
552 // Calculate the surface's 1/z value at this pixel, and cache the y depth for quick compares later
553 w = (psurf->wcurrent = psurf->w00 + psurf->wstepy * fy) + psurf->wstepx * fx;
555 // See if that makes it a new top surface
556 psurf2 = surfstack.next;
557 w2 = psurf2->wcurrent + psurf2->wstepx * fx;
561 // It's a new top surface
562 // emit the span for the current top
563 if (fx > cx && !psurf2->visible)
565 psurf2->visible = true;
566 psurf2->callback(psurf2->nativedata, psurf2->nativedata2);
570 for (x = ceil(cx), x2 = ceil(fx) >= clipwidth ? clipwidth : ceil(fx), zi = psurf2->wcurrent + psurf2->wstepx * x;x < x2;x++, zi += psurf2->wstepx)
576 // Add the edge to the stack
577 psurf->next = psurf2;
578 psurf2->prev = psurf;
579 surfstack.next = psurf;
580 psurf->prev = &surfstack;
584 // Not a new top; sort into the surface stack.
585 // Guaranteed to terminate due to sentinel background surface
588 psurf2 = psurf2->next;
589 w2 = psurf2->wcurrent + psurf2->wstepx * fx;
593 // Insert the surface into the stack
594 psurf->next = psurf2;
595 psurf->prev = psurf2->prev;
596 psurf2->prev->next = psurf;
597 psurf2->prev = psurf;
603 // It's a trailing edge; if this was the top surface,
604 // emit the span and remove it.
605 // First, make sure the edges didn't cross
606 if (--psurf->state == 0)
608 if (surfstack.next == psurf)
612 // It's on top, emit the span
613 if (fx > cx && !psurf->visible)
615 psurf->visible = true;
616 psurf->callback(psurf->nativedata, psurf->nativedata2);
621 for (x = ceil(cx), x2 = ceil(fx) >= clipwidth ? clipwidth : ceil(fx), zi = psurf->w00 + psurf->wstepx * x + psurf->wstepy * fy;x < x2;x++, zi += psurf->wstepx)
628 // Remove the surface from the stack
629 psurf->next->prev = psurf->prev;
630 psurf->prev->next = psurf->next;
634 // mark and remove all non-solid surfaces that are ontop
635 while (!surfstack.next->solid)
637 psurf = surfstack.next;
640 psurf->visible = true;
641 psurf->callback(psurf->nativedata, psurf->nativedata2);
643 psurf->removed = true;
644 psurf->next->prev = psurf->prev;
645 psurf->prev->next = psurf->next;
646 // isolate the surface
647 psurf->next = psurf->prev = psurf;
653 // Remove edges that are done
654 pedge = removeedges[y];
659 if (!pedge->psurf->removed)
661 pedge->prev->next = pedge->next;
662 pedge->next->prev = pedge->prev;
663 if (pedge->psurf->visible)
666 pedge = pedge->nextremove;
670 // Step the remaining edges one scan line, and re-sort
671 for (pedge = edgehead.next;pedge != &edgetail;)
674 if (pedge->psurf->removed)
676 pedge->next->prev = pedge->prev;
677 pedge->prev->next = pedge->next;
678 pedge->next = pedge->prev = pedge;
684 if (pedge->realxstep)
686 pedge->realx += pedge->realxstep;
687 pedge->x = bound(0.0f, pedge->realx, clipwidthf);
691 // Move the edge back to the proper sorted location, if necessary
692 while (fx < pedge->prev->x)
694 if (!rescan && (pedge->psurf->solid || pedge->prev->psurf->solid))
696 pedge2 = pedge->prev;
697 pedge2->next = pedge->next;
698 pedge->next->prev = pedge2;
699 pedge2->prev->next = pedge;
700 pedge->prev = pedge2->prev;
701 pedge->next = pedge2;
702 pedge2->prev = pedge;
710 void R_Clip_DisplayBuffer(void)
714 float boxpoints[4*3];
716 #define R_Clip_MinsBoxPolygon(axis, axisvalue, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, callback, nativedata, nativedata2, plane) \
718 if (r_origin[(axis)] < ((axisvalue) - 0.5f))\
720 (plane)->dist = -axisvalue;\
721 boxpoints[ 0] = x1;boxpoints[ 1] = y1;boxpoints[ 2] = z1;\
722 boxpoints[ 3] = x2;boxpoints[ 4] = y2;boxpoints[ 5] = z2;\
723 boxpoints[ 6] = x3;boxpoints[ 7] = y3;boxpoints[ 8] = z3;\
724 boxpoints[ 9] = x4;boxpoints[10] = y4;boxpoints[11] = z4;\
725 R_Clip_AddPolygon (boxpoints, 4, sizeof(float[3]), false, callback, nativedata, nativedata2, plane);\
729 #define R_Clip_MaxsBoxPolygon(axis, axisvalue, x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, callback, nativedata, nativedata2, plane) \
731 if (r_origin[(axis)] > ((axisvalue) + 0.5f))\
733 (plane)->dist = axisvalue;\
734 boxpoints[ 0] = x1;boxpoints[ 1] = y1;boxpoints[ 2] = z1;\
735 boxpoints[ 3] = x2;boxpoints[ 4] = y2;boxpoints[ 5] = z2;\
736 boxpoints[ 6] = x3;boxpoints[ 7] = y3;boxpoints[ 8] = z3;\
737 boxpoints[ 9] = x4;boxpoints[10] = y4;boxpoints[11] = z4;\
738 R_Clip_AddPolygon (boxpoints, 4, sizeof(float[3]), false, callback, nativedata, nativedata2, plane);\
742 tinyplane_t clipboxplane[6] =
752 void R_Clip_AddBox(float *a, float *b, void (*callback)(void *nativedata, void *nativedata2), void *nativedata, void *nativedata2)
754 if (r_origin[0] >= (a[0] - 5.0f) && r_origin[0] < (b[0] + 5.0f)
755 && r_origin[1] >= (a[1] - 5.0f) && r_origin[1] < (b[1] + 5.0f)
756 && r_origin[2] >= (a[2] - 5.0f) && r_origin[2] < (b[2] + 5.0f))
758 callback(nativedata, nativedata2);
765 R_Clip_MinsBoxPolygon
772 callback, nativedata, nativedata2, &clipboxplane[0]
774 R_Clip_MaxsBoxPolygon
781 callback, nativedata, nativedata2, &clipboxplane[1]
783 R_Clip_MinsBoxPolygon
790 callback, nativedata, nativedata2, &clipboxplane[2]
792 R_Clip_MaxsBoxPolygon
799 callback, nativedata, nativedata2, &clipboxplane[3]
801 R_Clip_MinsBoxPolygon
808 callback, nativedata, nativedata2, &clipboxplane[4]
810 R_Clip_MaxsBoxPolygon
817 callback, nativedata, nativedata2, &clipboxplane[5]