1033 lines
32 KiB
C
1033 lines
32 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: mcdclip.c
|
|
*
|
|
* Contains the line and polygon clipping routines for an MCD driver.
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#include "mcdhw.h"
|
|
#include "mcdutil.h"
|
|
#include "mcdmath.h"
|
|
|
|
MCDCOORD __MCD_frustumClipPlanes[6] = {
|
|
{(MCDFLOAT) 1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // left
|
|
{(MCDFLOAT)-1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // right
|
|
{(MCDFLOAT) 0.0, (MCDFLOAT) 1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // bottom
|
|
{(MCDFLOAT) 0.0, (MCDFLOAT)-1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // top
|
|
{(MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0, (MCDFLOAT) 1.0 }, // zNear
|
|
{(MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT)-1.0, (MCDFLOAT) 1.0 }, // zFar
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Clipping macros used to build clip functions below.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#define __MCD_CLIP_POS(v, a, b, t) \
|
|
v->clipCoord.x = t*(a->clipCoord.x - b->clipCoord.x) + b->clipCoord.x; \
|
|
v->clipCoord.y = t*(a->clipCoord.y - b->clipCoord.y) + b->clipCoord.y; \
|
|
v->clipCoord.z = t*(a->clipCoord.z - b->clipCoord.z) + b->clipCoord.z; \
|
|
v->clipCoord.w = t*(a->clipCoord.w - b->clipCoord.w) + b->clipCoord.w
|
|
|
|
// Note that we compute the values needed for both "cheap" fog only...
|
|
|
|
#define __MCD_CLIP_FOG(v, a, b, t) \
|
|
v->fog = t * (a->fog - b->fog) + b->fog;
|
|
|
|
#define __MCD_CLIP_COLOR(v, a, b, t) \
|
|
v->colors[__MCD_FRONTFACE].r = t*(a->colors[__MCD_FRONTFACE].r \
|
|
- b->colors[__MCD_FRONTFACE].r) + b->colors[__MCD_FRONTFACE].r; \
|
|
v->colors[__MCD_FRONTFACE].g = t*(a->colors[__MCD_FRONTFACE].g \
|
|
- b->colors[__MCD_FRONTFACE].g) + b->colors[__MCD_FRONTFACE].g; \
|
|
v->colors[__MCD_FRONTFACE].b = t*(a->colors[__MCD_FRONTFACE].b \
|
|
- b->colors[__MCD_FRONTFACE].b) + b->colors[__MCD_FRONTFACE].b; \
|
|
v->colors[__MCD_FRONTFACE].a = t*(a->colors[__MCD_FRONTFACE].a \
|
|
- b->colors[__MCD_FRONTFACE].a) + b->colors[__MCD_FRONTFACE].a
|
|
|
|
#define __MCD_CLIP_BACKCOLOR(v, a, b, t) \
|
|
v->colors[__MCD_BACKFACE].r = t*(a->colors[__MCD_BACKFACE].r \
|
|
- b->colors[__MCD_BACKFACE].r) + b->colors[__MCD_BACKFACE].r; \
|
|
v->colors[__MCD_BACKFACE].g = t*(a->colors[__MCD_BACKFACE].g \
|
|
- b->colors[__MCD_BACKFACE].g) + b->colors[__MCD_BACKFACE].g; \
|
|
v->colors[__MCD_BACKFACE].b = t*(a->colors[__MCD_BACKFACE].b \
|
|
- b->colors[__MCD_BACKFACE].b) + b->colors[__MCD_BACKFACE].b; \
|
|
v->colors[__MCD_BACKFACE].a = t*(a->colors[__MCD_BACKFACE].a \
|
|
- b->colors[__MCD_BACKFACE].a) + b->colors[__MCD_BACKFACE].a
|
|
|
|
#define __MCD_CLIP_INDEX(v, a, b, t) \
|
|
v->colors[__MCD_FRONTFACE].r = t*(a->colors[__MCD_FRONTFACE].r \
|
|
- b->colors[__MCD_FRONTFACE].r) + b->colors[__MCD_FRONTFACE].r
|
|
|
|
#define __MCD_CLIP_BACKINDEX(v, a, b, t) \
|
|
v->colors[__MCD_BACKFACE].r = t*(a->colors[__MCD_BACKFACE].r \
|
|
- b->colors[__MCD_BACKFACE].r) + b->colors[__MCD_BACKFACE].r
|
|
|
|
#define __MCD_CLIP_TEXTURE(v, a, b, t) \
|
|
v->texCoord.x = t*(a->texCoord.x - b->texCoord.x) + b->texCoord.x; \
|
|
v->texCoord.y = t*(a->texCoord.y - b->texCoord.y) + b->texCoord.y;
|
|
#ifdef CLIP_TEXTURE_XFORM
|
|
v->texCoord.z = t*(a->texCoord.z - b->texCoord.z) + b->texCoord.z; \
|
|
v->texCoord.w = t*(a->texCoord.w - b->texCoord.w) + b->texCoord.w
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Clipping functions to clip vertices:
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
static VOID FASTCALL Clip(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipC(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipI(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBC(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_BACKCOLOR(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBI(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_BACKINDEX(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipIT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBIT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_BACKINDEX(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipCT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
|
|
static VOID FASTCALL ClipBCT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_BACKCOLOR(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipF(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipIF(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipCF(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBCF(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_BACKCOLOR(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBIF(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_BACKINDEX(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipFT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipIFT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBIFT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_INDEX(dst,a,b,t);
|
|
__MCD_CLIP_BACKINDEX(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
|
|
static VOID FASTCALL ClipCFT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID FASTCALL ClipBCFT(MCDVERTEX *dst, const MCDVERTEX *a,
|
|
const MCDVERTEX *b, MCDFLOAT t)
|
|
{
|
|
__MCD_CLIP_POS(dst,a,b,t);
|
|
__MCD_CLIP_COLOR(dst,a,b,t);
|
|
__MCD_CLIP_BACKCOLOR(dst,a,b,t);
|
|
__MCD_CLIP_FOG(dst,a,b,t);
|
|
__MCD_CLIP_TEXTURE(dst,a,b,t);
|
|
}
|
|
|
|
static VOID (FASTCALL *clipProcs[20])(MCDVERTEX*, const MCDVERTEX*,
|
|
const MCDVERTEX*, MCDFLOAT) =
|
|
{
|
|
Clip, ClipI, ClipC, ClipBI, ClipBC,
|
|
ClipF, ClipIF, ClipCF, ClipBIF, ClipBCF,
|
|
ClipT, ClipIT, ClipCT, ClipBIT, ClipBCT,
|
|
ClipFT, ClipIFT, ClipCFT, ClipBIFT, ClipBCFT,
|
|
};
|
|
|
|
|
|
VOID FASTCALL __MCDPickClipFuncs(DEVRC *pRc)
|
|
{
|
|
LONG line = 0, poly = 0;
|
|
BOOL twoSided = (pRc->MCDState.enables & MCD_LIGHTING_ENABLE) &&
|
|
(pRc->MCDState.twoSided);
|
|
|
|
if (pRc->bRGBMode) {
|
|
if (pRc->MCDState.shadeModel != GL_FLAT) {
|
|
line = 2;
|
|
poly = (twoSided ? 4 : 2);
|
|
}
|
|
} else {
|
|
if (pRc->MCDState.shadeModel != GL_FLAT) {
|
|
line = 1;
|
|
poly = (twoSided ? 3 : 1);
|
|
}
|
|
}
|
|
if ((pRc->bCheapFog) && !(pRc->MCDState.shadeModel == GL_SMOOTH)) {
|
|
{
|
|
line += 5;
|
|
poly += 5;
|
|
}
|
|
}
|
|
|
|
if (pRc->MCDState.textureEnabled) {
|
|
line += 10;
|
|
poly += 10;
|
|
}
|
|
|
|
pRc->lineClipParam = clipProcs[line];
|
|
pRc->polyClipParam = clipProcs[poly];
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// The real primitive clippers:
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
** The following is a discussion of the math used to do edge clipping against
|
|
** a clipping plane.
|
|
**
|
|
** P1 is an end point of the edge
|
|
** P2 is the other end point of the edge
|
|
**
|
|
** Q = t*P1 + (1 - t)*P2
|
|
** That is, Q lies somewhere on the line formed by P1 and P2.
|
|
**
|
|
** 0 <= t <= 1
|
|
** This constrains Q to lie between P1 and P2.
|
|
**
|
|
** C is the plane equation for the clipping plane
|
|
**
|
|
** D1 = P1 dot C
|
|
** D1 is the distance between P1 and C. If P1 lies on the plane
|
|
** then D1 will be zero. The sign of D1 will determine which side
|
|
** of the plane that P1 is on, with negative being outside.
|
|
**
|
|
** D2 = P2 dot C
|
|
** D2 is the distance between P2 and C. If P2 lies on the plane
|
|
** then D2 will be zero. The sign of D2 will determine which side
|
|
** of the plane that P2 is on, with negative being outside.
|
|
**
|
|
** Because we are trying to find the intersection of the P1 P2 line
|
|
** segment with the clipping plane we require that:
|
|
**
|
|
** Q dot C = 0
|
|
**
|
|
** Therefore
|
|
**
|
|
** (t*P1 + (1 - t)*P2) dot C = 0
|
|
**
|
|
** (t*P1 + P2 - t*P2) dot C = 0
|
|
**
|
|
** t*P1 dot C + P2 dot C - t*P2 dot C = 0
|
|
**
|
|
** Substituting D1 and D2 in
|
|
**
|
|
** t*D1 + D2 - t*D2 = 0
|
|
**
|
|
** Solving for t
|
|
**
|
|
** t = -D2 / (D1 - D2)
|
|
**
|
|
** t = D2 / (D2 - D1)
|
|
*/
|
|
|
|
|
|
static LONG clipToPlane(DEVRC *pRc, MCDVERTEX **iv, LONG niv,
|
|
MCDVERTEX **ov, MCDCOORD *plane)
|
|
{
|
|
LONG i, nout, generated;
|
|
MCDVERTEX *s, *p, *newVertex, *temp;
|
|
MCDFLOAT pDist, sDist, t;
|
|
MCDFLOAT zero = ZERO;
|
|
VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);
|
|
|
|
nout = 0;
|
|
generated = 0;
|
|
temp = pRc->pNextClipTemp;
|
|
clip = pRc->polyClipParam;
|
|
|
|
s = iv[niv-1];
|
|
sDist = (s->clipCoord.x * plane->x) + (s->clipCoord.y * plane->y) +
|
|
(s->clipCoord.z * plane->z) + (s->clipCoord.w * plane->w);
|
|
for (i = 0; i < niv; i++) {
|
|
p = iv[i];
|
|
pDist = (p->clipCoord.x * plane->x) + (p->clipCoord.y * plane->y) +
|
|
(p->clipCoord.z * plane->z) + (p->clipCoord.w * plane->w);
|
|
if (pDist >= zero) {
|
|
/* p is inside the clipping plane half space */
|
|
if (sDist >= zero) {
|
|
/* s is inside the clipping plane half space */
|
|
*ov++ = p;
|
|
nout++;
|
|
} else {
|
|
/* s is outside the clipping plane half space */
|
|
t = pDist / (pDist - sDist);
|
|
newVertex = temp++;
|
|
(*clip)(newVertex, s, p, t);
|
|
newVertex->flags = s->flags;
|
|
newVertex->clipCode = s->clipCode;
|
|
*ov++ = newVertex;
|
|
*ov++ = p;
|
|
nout += 2;
|
|
|
|
if (++generated >= 3) {
|
|
/* Toss the non-convex polygon */
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
/* p is outside the clipping plane half space */
|
|
if (sDist >= zero) {
|
|
/*
|
|
** s is inside the clipping plane half space
|
|
**
|
|
** NOTE: To avoid cracking in polygons with shared
|
|
** clipped edges we always compute "t" from the out
|
|
** vertex to the in vertex. The above clipping code gets
|
|
** this for free (p is in and s is out). In this code p
|
|
** is out and s is in, so we reverse the t computation
|
|
** and the argument order to __MCDDoClip.
|
|
*/
|
|
t = sDist / (sDist - pDist);
|
|
newVertex = temp++;
|
|
(*clip)(newVertex, p, s, t);
|
|
newVertex->flags = s->flags | MCDVERTEX_EDGEFLAG;
|
|
newVertex->clipCode = p->clipCode;
|
|
*ov++ = newVertex;
|
|
nout++;
|
|
|
|
if (++generated >= 3) {
|
|
/* Toss the non-convex polygon */
|
|
return 0;
|
|
}
|
|
} else {
|
|
/* both points are outside */
|
|
}
|
|
}
|
|
s = p;
|
|
sDist = pDist;
|
|
}
|
|
pRc->pNextClipTemp = temp;
|
|
return nout;
|
|
}
|
|
|
|
/*
|
|
** Identical to clipToPlane(), except that the clipping is done in eye
|
|
** space.
|
|
*/
|
|
static LONG clipToPlaneEye(DEVRC *pRc, MCDVERTEX **iv, LONG niv,
|
|
MCDVERTEX **ov, MCDCOORD *plane)
|
|
{
|
|
LONG i, nout, generated;
|
|
MCDVERTEX *s, *p, *newVertex, *temp;
|
|
MCDFLOAT pDist, sDist, t;
|
|
MCDFLOAT zero = __MCDZERO;
|
|
VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);
|
|
|
|
nout = 0;
|
|
generated = 0;
|
|
temp = pRc->pNextClipTemp;
|
|
clip = pRc->polyClipParam;
|
|
|
|
s = iv[niv-1];
|
|
sDist = (s->eyeCoord.x * plane->x) +
|
|
(s->eyeCoord.y * plane->y) +
|
|
(s->eyeCoord.z * plane->z) +
|
|
(s->eyeCoord.w * plane->w);
|
|
for (i = 0; i < niv; i++) {
|
|
p = iv[i];
|
|
pDist = (p->eyeCoord.x * plane->x) +
|
|
(p->eyeCoord.y * plane->y) +
|
|
(p->eyeCoord.z * plane->z) +
|
|
(p->eyeCoord.w * plane->w);
|
|
if (pDist >= zero) {
|
|
/* p is inside the clipping plane half space */
|
|
if (sDist >= zero) {
|
|
/* s is inside the clipping plane half space */
|
|
*ov++ = p;
|
|
nout++;
|
|
} else {
|
|
/* s is outside the clipping plane half space */
|
|
t = pDist / (pDist - sDist);
|
|
newVertex = temp++;
|
|
(*clip)(newVertex, s, p, t);
|
|
newVertex->eyeCoord.x = t*(s->eyeCoord.x - p->eyeCoord.x) + p->eyeCoord.x;
|
|
newVertex->eyeCoord.y = t*(s->eyeCoord.y - p->eyeCoord.y) + p->eyeCoord.y;
|
|
newVertex->eyeCoord.z = t*(s->eyeCoord.z - p->eyeCoord.z) + p->eyeCoord.z;
|
|
newVertex->eyeCoord.w = t*(s->eyeCoord.w - p->eyeCoord.w) + p->eyeCoord.w;
|
|
newVertex->flags = s->flags;
|
|
newVertex->clipCode = s->clipCode; //!!!!
|
|
*ov++ = newVertex;
|
|
*ov++ = p;
|
|
nout += 2;
|
|
|
|
if (++generated >= 3) {
|
|
/* Toss the non-convex polygon */
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
/* p is outside the clipping plane half space */
|
|
if (sDist >= zero) {
|
|
/*
|
|
** s is inside the clipping plane half space
|
|
**
|
|
** NOTE: To avoid cracking in polygons with shared
|
|
** clipped edges we always compute "t" from the out
|
|
** vertex to the in vertex. The above clipping code gets
|
|
** this for free (p is in and s is out). In this code p
|
|
** is out and s is in, so we reverse the t computation
|
|
** and the argument order to __MCDDoClip.
|
|
*/
|
|
t = sDist / (sDist - pDist);
|
|
newVertex = temp++;
|
|
(*clip)(newVertex, p, s, t);
|
|
newVertex->eyeCoord.x = t*(p->eyeCoord.x - s->eyeCoord.x) + s->eyeCoord.x;
|
|
newVertex->eyeCoord.y = t*(p->eyeCoord.y - s->eyeCoord.y) + s->eyeCoord.y;
|
|
newVertex->eyeCoord.z = t*(p->eyeCoord.z - s->eyeCoord.z) + s->eyeCoord.z;
|
|
newVertex->eyeCoord.w = t*(p->eyeCoord.w - s->eyeCoord.w) + s->eyeCoord.w;
|
|
newVertex->flags = s->flags | MCDVERTEX_EDGEFLAG;
|
|
newVertex->clipCode = p->clipCode;
|
|
*ov++ = newVertex;
|
|
nout++;
|
|
|
|
if (++generated >= 3) {
|
|
/* Toss the non-convex polygon */
|
|
return 0;
|
|
}
|
|
} else {
|
|
/* both points are outside */
|
|
}
|
|
}
|
|
s = p;
|
|
sDist = pDist;
|
|
}
|
|
pRc->pNextClipTemp = temp;
|
|
return nout;
|
|
}
|
|
|
|
/*
|
|
** Each clipping plane can add at most one vertex to a convex polygon (it may
|
|
** remove up to all of the vertices). The clipping will leave a polygon
|
|
** convex. Because of this the maximum number of verticies output from
|
|
** the clipToPlane procedure will be total number of clip planes (assuming
|
|
** each plane adds one new vertex) plus the original number of verticies
|
|
** (3 since this if for triangles).
|
|
*/
|
|
|
|
#define __MCD_TOTAL_CLIP_PLANES 6 + MCD_MAX_USER_CLIP_PLANES
|
|
#define __MCD_MAX_POLYGON_CLIP_SIZE 256
|
|
|
|
#define __MCD_MAX_CLIP_VERTEX (__MCD_TOTAL_CLIP_PLANES + __MCD_MAX_POLYGON_CLIP_SIZE)
|
|
|
|
|
|
void FASTCALL __MCDDoClippedPolygon(DEVRC *pRc, MCDVERTEX **iv, ULONG nout,
|
|
ULONG allClipCodes)
|
|
{
|
|
MCDVERTEX *ov[__MCD_TOTAL_CLIP_PLANES][__MCD_MAX_CLIP_VERTEX];
|
|
MCDVERTEX **ivp;
|
|
MCDVERTEX **ovp;
|
|
MCDVERTEX *p0, *p1, *p2;
|
|
MCDCOORD *plane;
|
|
LONG i;
|
|
MCDFLOAT one;
|
|
VOID (FASTCALL *rt)(DEVRC*, MCDVERTEX*, MCDVERTEX*, MCDVERTEX*);
|
|
MCDFLOAT llx, lly, urx, ury;
|
|
MCDFLOAT winx, winy;
|
|
ULONG clipCodes;
|
|
|
|
/*
|
|
** Reset nextClipTemp pointer for any new verticies that are generated
|
|
** during the clipping.
|
|
*/
|
|
|
|
pRc->pNextClipTemp = &pRc->clipTemp[0];
|
|
|
|
ivp = &iv[0];
|
|
|
|
/*
|
|
** Check each of the clipping planes by examining the allClipCodes
|
|
** mask. Note that no bits will be set in allClipCodes for clip
|
|
** planes that are not enabled.
|
|
*/
|
|
if (allClipCodes) {
|
|
|
|
/* Now clip against the clipping planes */
|
|
ovp = &ov[0][0];
|
|
|
|
/*
|
|
** Do user clip planes first, because we will maintain eye coordinates
|
|
** only while doing user clip planes. They are ignored for the
|
|
** frustum clipping planes.
|
|
*/
|
|
clipCodes = (allClipCodes >> 6) & __MCD_USER_CLIP_MASK;
|
|
if (clipCodes) {
|
|
plane = &pRc->MCDState.userClipPlanes[0];
|
|
do {
|
|
if (clipCodes & 1) {
|
|
nout = clipToPlaneEye(pRc, ivp, nout, ovp, plane);
|
|
if (nout < 3) {
|
|
return;
|
|
}
|
|
ivp = ovp;
|
|
ovp += __MCD_MAX_CLIP_VERTEX;
|
|
}
|
|
clipCodes >>= 1;
|
|
plane++;
|
|
} while (clipCodes);
|
|
}
|
|
|
|
allClipCodes &= MCD_CLIP_MASK;
|
|
if (allClipCodes) {
|
|
plane = &__MCD_frustumClipPlanes[0];
|
|
do {
|
|
if (allClipCodes & 1) {
|
|
nout = clipToPlane(pRc, ivp, nout, ovp, plane);
|
|
if (nout < 3) {
|
|
return;
|
|
}
|
|
ivp = ovp;
|
|
ovp += __MCD_MAX_CLIP_VERTEX;
|
|
}
|
|
allClipCodes >>= 1;
|
|
plane++;
|
|
} while (allClipCodes);
|
|
}
|
|
|
|
/*
|
|
** Calculate final screen coordinates. Next phase of polygon
|
|
** processing assumes that window coordinates are already computed.
|
|
*/
|
|
|
|
ovp = ivp;
|
|
one = __MCDONE;
|
|
|
|
llx = pRc->MCDViewport.xCenter - pRc->MCDViewport.xScale;
|
|
urx = pRc->MCDViewport.xCenter + pRc->MCDViewport.xScale;
|
|
|
|
if (pRc->MCDViewport.yScale > 0) {
|
|
lly = pRc->MCDViewport.yCenter - pRc->MCDViewport.yScale;
|
|
ury = pRc->MCDViewport.yCenter + pRc->MCDViewport.yScale;
|
|
} else {
|
|
lly = pRc->MCDViewport.yCenter + pRc->MCDViewport.yScale;
|
|
ury = pRc->MCDViewport.yCenter - pRc->MCDViewport.yScale;
|
|
}
|
|
|
|
for (i = nout; --i >= 0; ) {
|
|
MCDFLOAT wInv;
|
|
|
|
p0 = *ovp++;
|
|
|
|
if (p0->clipCoord.w == (MCDFLOAT) 0.0)
|
|
wInv = (MCDFLOAT) 0.0;
|
|
else
|
|
wInv = one / p0->clipCoord.w;
|
|
|
|
winx = p0->clipCoord.x * pRc->MCDViewport.xScale * wInv +
|
|
pRc->MCDViewport.xCenter;
|
|
|
|
winy = p0->clipCoord.y * pRc->MCDViewport.yScale * wInv +
|
|
pRc->MCDViewport.yCenter;
|
|
|
|
/*
|
|
** Check if these window coordinates are legal. At this
|
|
** point, it is quite possible that they are not. Trivially
|
|
** pull them into the legal viewport region if necessary.
|
|
*/
|
|
|
|
if (winx < llx) winx = llx;
|
|
else if (winx > urx) winx = urx;
|
|
if (winy < lly) winy = lly;
|
|
else if (winy > ury) winy = ury;
|
|
|
|
p0->windowCoord.x = winx;
|
|
p0->windowCoord.y = winy;
|
|
p0->windowCoord.z = p0->clipCoord.z * pRc->MCDViewport.zScale * wInv +
|
|
pRc->MCDViewport.zCenter;
|
|
p0->windowCoord.w = wInv;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Subdivide the clipped polygon into triangles. Only convex polys
|
|
** are supported so this is okay to do. Non-convex polys will do
|
|
** something odd here, but thats the clients fault.
|
|
*/
|
|
p0 = *ivp++;
|
|
p1 = *ivp++;
|
|
p2 = *ivp++;
|
|
rt = pRc->renderTri;
|
|
if (nout == 3) {
|
|
(*rt)(pRc, p0, p1, p2);
|
|
} else {
|
|
for (i = 0; i < (LONG)nout - 2; i++) {
|
|
ULONG t1, t2;
|
|
if (i == 0) {
|
|
/*
|
|
** Third edge of first sub-triangle is always non-boundary
|
|
*/
|
|
t1 = p2->flags & MCDVERTEX_EDGEFLAG;
|
|
p2->flags &= ~MCDVERTEX_EDGEFLAG;
|
|
(*rt)(pRc, p0, p1, p2);
|
|
p2->flags |= t1;
|
|
} else
|
|
if (i == (LONG)nout - 3) {
|
|
/*
|
|
** First edge of last sub-triangle is always non-boundary
|
|
*/
|
|
t1 = p0->flags & MCDVERTEX_EDGEFLAG;
|
|
p0->flags &= ~MCDVERTEX_EDGEFLAG;
|
|
(*rt)(pRc, p0, p1, p2);
|
|
p0->flags |= t1;
|
|
} else {
|
|
/*
|
|
** Interior sub-triangles have the first and last edge
|
|
** marked non-boundary
|
|
*/
|
|
|
|
t1 = p0->flags & MCDVERTEX_EDGEFLAG;
|
|
t2 = p2->flags & MCDVERTEX_EDGEFLAG;
|
|
p0->flags &= ~MCDVERTEX_EDGEFLAG;
|
|
p2->flags &= ~MCDVERTEX_EDGEFLAG;
|
|
(*rt)(pRc, p0, p1, p2);
|
|
p0->flags |= t1;
|
|
p2->flags |= t2;
|
|
}
|
|
p1 = p2;
|
|
p2 = (MCDVERTEX *) *ivp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID FASTCALL __MCDClipPolygon(DEVRC *pRc, MCDVERTEX *v0, ULONG nv)
|
|
{
|
|
MCDVERTEX *iv[__MCD_MAX_POLYGON_CLIP_SIZE];
|
|
|
|
MCDVERTEX **ivp;
|
|
LONG i;
|
|
ULONG andCodes, orCodes;
|
|
|
|
pRc->pvProvoking = v0;
|
|
|
|
/*
|
|
** Generate array of addresses of the verticies. And all the
|
|
** clip codes together while we are at it.
|
|
*/
|
|
ivp = &iv[0];
|
|
andCodes = 0;
|
|
orCodes = 0;
|
|
for (i = nv; --i >= 0; ) {
|
|
andCodes &= v0->clipCode;
|
|
orCodes |= v0->clipCode;
|
|
*ivp++ = v0++;
|
|
}
|
|
|
|
if (andCodes != 0) {
|
|
/*
|
|
** Trivially reject the polygon. If andCodes is non-zero then
|
|
** every vertex in the polygon is outside of the same set of
|
|
** clipping planes (at least one).
|
|
*/
|
|
return;
|
|
}
|
|
__MCDDoClippedPolygon(pRc, &iv[0], nv, orCodes);
|
|
}
|
|
|
|
void FASTCALL __MCDClipTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b,
|
|
MCDVERTEX *c, ULONG orCodes)
|
|
{
|
|
MCDVERTEX *iv[3];
|
|
|
|
iv[0] = a;
|
|
iv[1] = b;
|
|
iv[2] = c;
|
|
|
|
__MCDDoClippedPolygon(pRc, &iv[0], 3, orCodes);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Line clipping:
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Clip a line against the frustum clip planes and any user clipping planes.
|
|
// If an edge remains after clipping then compute the window coordinates
|
|
// and invoke the renderer.
|
|
//
|
|
// Notice: This algorithim is an example of an implementation that is
|
|
// different than what the spec says. This is equivalent in functionality
|
|
// and meets the spec, but doesn't clip in eye space. This clipper clips
|
|
// in NTVP (clip) space.
|
|
//
|
|
// Trivial accept/reject has already been dealt with.
|
|
//
|
|
|
|
VOID FASTCALL __MCDClipLine(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b,
|
|
BOOL bResetLine)
|
|
{
|
|
MCDVERTEX *provokingA = a;
|
|
MCDVERTEX *provokingB = b;
|
|
MCDVERTEX np1, np2;
|
|
MCDCOORD *plane;
|
|
ULONG needs, allClipCodes, clipCodes;
|
|
VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);
|
|
MCDFLOAT zero;
|
|
MCDFLOAT winx, winy;
|
|
MCDFLOAT vpXCenter, vpYCenter, vpZCenter;
|
|
MCDFLOAT vpXScale, vpYScale, vpZScale;
|
|
MCDVIEWPORT *vp;
|
|
MCDFLOAT x, y, z, wInv;
|
|
|
|
allClipCodes = a->clipCode | b->clipCode;
|
|
|
|
/*
|
|
** For each clipping plane that something is out on, clip
|
|
** check the verticies. Note that no bits will be set in
|
|
** allClipCodes for clip planes that are not enabled.
|
|
*/
|
|
zero = __MCDZERO;
|
|
clip = pRc->lineClipParam;
|
|
|
|
/*
|
|
** Do user clip planes first, because we will maintain eye coordinates
|
|
** only while doing user clip planes. They are ignored for the
|
|
** frustum clipping planes.
|
|
*/
|
|
clipCodes = (allClipCodes >> 6) & __MCD_USER_CLIP_MASK;
|
|
if (clipCodes) {
|
|
plane = &pRc->MCDState.userClipPlanes[0];
|
|
do {
|
|
/*
|
|
** See if this clip plane has anything out of it. If not,
|
|
** press onward to check the next plane. Note that we
|
|
** shift this mask to the right at the bottom of the loop.
|
|
*/
|
|
if (clipCodes & 1) {
|
|
MCDFLOAT t, d1, d2;
|
|
|
|
d1 = (plane->x * a->eyeCoord.x) +
|
|
(plane->y * a->eyeCoord.y) +
|
|
(plane->z * a->eyeCoord.z) +
|
|
(plane->w * a->eyeCoord.w);
|
|
d2 = (plane->x * b->eyeCoord.x) +
|
|
(plane->y * b->eyeCoord.y) +
|
|
(plane->z * b->eyeCoord.z) +
|
|
(plane->w * b->eyeCoord.w);
|
|
if (d1 < zero) {
|
|
/* a is out */
|
|
if (d2 < zero) {
|
|
/* a & b are out */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** A is out and B is in. Compute new A coordinate
|
|
** clipped to the plane.
|
|
*/
|
|
t = d2 / (d2 - d1);
|
|
(*clip)(&np1, a, b, t);
|
|
(&np1)->eyeCoord.x =
|
|
t*(a->eyeCoord.x - b->eyeCoord.x) + b->eyeCoord.x;
|
|
(&np1)->eyeCoord.y =
|
|
t*(a->eyeCoord.y - b->eyeCoord.y) + b->eyeCoord.y;
|
|
(&np1)->eyeCoord.z =
|
|
t*(a->eyeCoord.z - b->eyeCoord.z) + b->eyeCoord.z;
|
|
(&np1)->eyeCoord.w =
|
|
t*(a->eyeCoord.w - b->eyeCoord.w) + b->eyeCoord.w;
|
|
a = &np1;
|
|
a->flags = b->flags;
|
|
|
|
if (pRc->MCDState.shadeModel == GL_FLAT)
|
|
{
|
|
COPY_COLOR(a->colors[0], provokingA->colors[0]);
|
|
}
|
|
|
|
} else {
|
|
/* a is in */
|
|
if (d2 < zero) {
|
|
/*
|
|
** A is in and B is out. Compute new B
|
|
** coordinate clipped to the plane.
|
|
**
|
|
** NOTE: To avoid cracking in polygons with
|
|
** shared clipped edges we always compute "t"
|
|
** from the out vertex to the in vertex. The
|
|
** above clipping code gets this for free (b is
|
|
** in and a is out). In this code b is out and a
|
|
** is in, so we reverse the t computation and the
|
|
** argument order to (*clip).
|
|
*/
|
|
t = d1 / (d1 - d2);
|
|
(*clip)(&np2, b, a, t);
|
|
(&np2)->eyeCoord.x =
|
|
t*(b->eyeCoord.x - a->eyeCoord.x) + a->eyeCoord.x;
|
|
(&np2)->eyeCoord.y =
|
|
t*(b->eyeCoord.y - a->eyeCoord.y) + a->eyeCoord.y;
|
|
(&np2)->eyeCoord.z =
|
|
t*(b->eyeCoord.z - a->eyeCoord.z) + a->eyeCoord.z;
|
|
(&np2)->eyeCoord.w =
|
|
t*(b->eyeCoord.w - a->eyeCoord.w) + a->eyeCoord.w;
|
|
b = &np2;
|
|
b->flags = a->flags;
|
|
|
|
if (pRc->MCDState.shadeModel == GL_FLAT)
|
|
{
|
|
COPY_COLOR(b->colors[0], provokingB->colors[0]);
|
|
}
|
|
|
|
} else {
|
|
/* A and B are in */
|
|
}
|
|
}
|
|
}
|
|
plane++;
|
|
clipCodes >>= 1;
|
|
} while (clipCodes);
|
|
}
|
|
|
|
allClipCodes &= MCD_CLIP_MASK;
|
|
if (allClipCodes) {
|
|
plane = &__MCD_frustumClipPlanes[0];
|
|
do {
|
|
/*
|
|
** See if this clip plane has anything out of it. If not,
|
|
** press onward to check the next plane. Note that we
|
|
** shift this mask to the right at the bottom of the loop.
|
|
*/
|
|
if (allClipCodes & 1) {
|
|
MCDFLOAT t, d1, d2;
|
|
|
|
d1 = (plane->x * a->clipCoord.x) + (plane->y * a->clipCoord.y) +
|
|
(plane->z * a->clipCoord.z) + (plane->w * a->clipCoord.w);
|
|
d2 = (plane->x * b->clipCoord.x) + (plane->y * b->clipCoord.y) +
|
|
(plane->z * b->clipCoord.z) + (plane->w * b->clipCoord.w);
|
|
if (d1 < zero) {
|
|
/* a is out */
|
|
if (d2 < zero) {
|
|
/* a & b are out */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** A is out and B is in. Compute new A coordinate
|
|
** clipped to the plane.
|
|
*/
|
|
t = d2 / (d2 - d1);
|
|
(*clip)(&np1, a, b, t);
|
|
a = &np1;
|
|
a->flags = b->flags;
|
|
|
|
if (pRc->MCDState.shadeModel == GL_FLAT)
|
|
{
|
|
COPY_COLOR(a->colors[0], provokingA->colors[0]);
|
|
}
|
|
|
|
} else {
|
|
/* a is in */
|
|
if (d2 < zero) {
|
|
/*
|
|
** A is in and B is out. Compute new B
|
|
** coordinate clipped to the plane.
|
|
**
|
|
** NOTE: To avoid cracking in polygons with
|
|
** shared clipped edges we always compute "t"
|
|
** from the out vertex to the in vertex. The
|
|
** above clipping code gets this for free (b is
|
|
** in and a is out). In this code b is out and a
|
|
** is in, so we reverse the t computation and the
|
|
** argument order to (*clip).
|
|
*/
|
|
t = d1 / (d1 - d2);
|
|
(*clip)(&np2, b, a, t);
|
|
b = &np2;
|
|
b->flags = a->flags;
|
|
|
|
if (pRc->MCDState.shadeModel == GL_FLAT)
|
|
{
|
|
COPY_COLOR(b->colors[0], provokingB->colors[0]);
|
|
}
|
|
|
|
} else {
|
|
/* A and B are in */
|
|
}
|
|
}
|
|
}
|
|
plane++;
|
|
allClipCodes >>= 1;
|
|
} while (allClipCodes);
|
|
}
|
|
|
|
vp = &pRc->MCDViewport;
|
|
vpXCenter = vp->xCenter;
|
|
vpYCenter = vp->yCenter;
|
|
vpZCenter = vp->zCenter;
|
|
vpXScale = vp->xScale;
|
|
vpYScale = vp->yScale;
|
|
vpZScale = vp->zScale;
|
|
|
|
/* Compute window coordinates for both vertices. */
|
|
wInv = __MCDONE / a->clipCoord.w;
|
|
x = a->clipCoord.x;
|
|
y = a->clipCoord.y;
|
|
z = a->clipCoord.z;
|
|
winx = x * vpXScale * wInv + vpXCenter;
|
|
winy = y * vpYScale * wInv + vpYCenter;
|
|
a->windowCoord.z = z * vpZScale * wInv + vpZCenter;
|
|
a->windowCoord.w = wInv;
|
|
a->windowCoord.x = winx;
|
|
a->windowCoord.y = winy;
|
|
|
|
wInv = __MCDONE / b->clipCoord.w;
|
|
x = b->clipCoord.x;
|
|
y = b->clipCoord.y;
|
|
z = b->clipCoord.z;
|
|
winx = x * vpXScale * wInv + vpXCenter;
|
|
winy = y * vpYScale * wInv + vpYCenter;
|
|
b->windowCoord.z = z * vpZScale * wInv + vpZCenter;
|
|
b->windowCoord.w = wInv;
|
|
b->windowCoord.x = winx;
|
|
b->windowCoord.y = winy;
|
|
|
|
/* Validate line state */
|
|
if (pRc->MCDState.shadeModel == GL_FLAT) {
|
|
|
|
// Add the vertices then restore the b color pointer
|
|
//
|
|
// Note that although b is the only new vertex, up
|
|
// to two vertices can be added because each new vertex
|
|
// generated by clipping must be added. For a line where
|
|
// both endpoints are out of the clipping region, both
|
|
// an entry and an exit vertex must be added
|
|
if (provokingA->clipCode != 0)
|
|
{
|
|
// a was out so a new vertex was added at the point of
|
|
// entry
|
|
bResetLine = TRUE;
|
|
}
|
|
// b is always added since either:
|
|
// b was in and is new so it needs to be added
|
|
// b was out so a new vertex was added at the exit point
|
|
|
|
(*pRc->renderLine)(pRc, a, b, bResetLine);
|
|
|
|
} else {
|
|
|
|
if (provokingA->clipCode != 0)
|
|
{
|
|
bResetLine = TRUE;
|
|
}
|
|
(*pRc->renderLine)(pRc, a, b, bResetLine);
|
|
}
|
|
}
|