|
|
/*==========================================================================;
* * Copyright (C) 1997 Microsoft Corporation. All Rights Reserved. * * File: dpclip.c * Content: DrawPrimitive clipper * ***************************************************************************/
#include "pch.cpp"
#pragma hdrstop
//----------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::Clip(D3DFE_PROCESSVERTICES *pv, ClipVertex *cv1, ClipVertex *cv2, ClipVertex *cv3) { ClipTriangle newtri; LPVOID saveVer = pv->lpvOut; // For indexed primitive
DWORD numVer = pv->dwNumVertices; // For indexed primitive
newtri.v[0] = cv1; newtri.v[1] = cv2; newtri.v[2] = cv3;
int count; ClipVertex** ver;
cv1->clip |= CLIPPED_ENABLE; cv2->clip |= CLIPPED_ENABLE; cv3->clip |= CLIPPED_ENABLE; // For the flat shading mode we have to use first vertex color as
// color for all vertices
D3DCOLOR diffuse1; // Original colors
D3DCOLOR specular1; D3DCOLOR diffuse2; D3DCOLOR specular2; if (pv->lpdwRStates[D3DRS_SHADEMODE] == D3DSHADE_FLAT) { // It is easier to set all vertices to the same color here
D3DCOLOR diffuse = cv1->color; // Exclude fog factor
D3DCOLOR specular = cv1->specular & 0x00FFFFFF;
//Save original colors
diffuse1 = cv2->color; specular1 = cv2->specular; diffuse2 = cv3->color; specular2 = cv3->specular;
// Copy the same color to all vertices but preserve fog factor, because
// fog factor should be interpolated
cv2->color = diffuse; cv3->color = diffuse; cv2->specular = (cv2->specular & 0xFF000000) | specular; cv3->specular = (cv3->specular & 0xFF000000) | specular; }
if (count = pv->pGeometryFuncs->ClipSingleTriangle(pv, &newtri, &ver)) { int i; HRESULT ret; BYTE *pTLV = pv->ClipperState.clipBuf; BYTE *p = pTLV;
for (i = 0; i < count; i++) { MAKE_TL_VERTEX_FVF(pv, p, ver[i]); p += pv->dwOutputSize; } ret = DRAW_CLIPPED_PRIM(pv, D3DPT_TRIANGLEFAN, pTLV, count, count-2); if (ret) return ret; } // CLIPPED_ENABLE bit could be set in the ClipSingleTriangle.
// If this bit is not cleared, clipping will be wrong. Because, clip
// vertices are re-used by next triangles.
// This bit should be cleared *after* drawing command. Otherwise, edge flags
// will be incorrect
cv1->clip &= ~CLIPPED_ENABLE; cv2->clip &= ~CLIPPED_ENABLE; cv3->clip &= ~CLIPPED_ENABLE;
if (pv->lpdwRStates[D3DRENDERSTATE_SHADEMODE] == D3DSHADE_FLAT) { // Restore original colors
cv2->color = diffuse1; cv2->specular = specular1; cv3->color = diffuse2; cv3->specular = specular2; } pv->lpvOut = saveVer; pv->dwNumVertices = numVer; return D3D_OK; } //----------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::ClipLine(D3DFE_PROCESSVERTICES *pv, ClipVertex *v1, ClipVertex *v2) { ClipTriangle newline; LPVOID saveVer = pv->lpvOut; // For indexed primitive
DWORD numVer = pv->dwNumVertices; // For indexed primitive
ClipVertex cv1 = *v1; ClipVertex cv2 = *v2; newline.v[0] = &cv1; newline.v[1] = &cv2;
int count; ClipVertex** ver;
if (pv->lpdwRStates[D3DRENDERSTATE_SHADEMODE] == D3DSHADE_FLAT) { // Copy the same color to all vertices but preserve fog factor, because
// fog factor should be interpolated
cv2.color = cv1.color; cv2.specular = (cv2.specular & 0xFF000000)|(cv1.specular & 0x00FFFFFF); }
if (ClipSingleLine(pv, &newline)) { BYTE *pTLV = pv->ClipperState.clipBuf; BYTE *p = pTLV; MAKE_TL_VERTEX_FVF(pv, p, newline.v[0]); p += pv->dwOutputSize; MAKE_TL_VERTEX_FVF(pv, p, newline.v[1]); HRESULT ret = DRAW_CLIPPED_PRIM(pv, D3DPT_LINELIST, pTLV, 2, 1); if (ret) return ret; } pv->lpvOut = saveVer; pv->dwNumVertices = numVer; return D3D_OK; } //------------------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::ProcessClippedTriangleFan(D3DFE_PROCESSVERTICES *pv) { BYTE *p1; DWORD f1; D3DFE_CLIPCODE *clipCode; DWORD i; HRESULT ret; BYTE *vertex; BYTE *startVertex; int vertexCount; DWORD vertexSize; ClipVertex cv[3]; BOOL vertexTransformed; vertexTransformed = pv->dwFlags & D3DPV_TLVCLIP;; clipCode = pv->lpClipFlags; vertex = (BYTE*)pv->lpvOut; startVertex = (BYTE*)pv->lpvOut; vertexSize = pv->dwOutputSize; vertexCount = 0;
f1 = clipCode[0]; p1 = vertex; clipCode++; vertex += vertexSize; // In the clipper color from the first vertex is propagated to all
// vertices for FLAT shade mode. In triangle fans the second vertex defines
// the color in FLAT shade mode. So we will make the vertex order: 1, 2, 0
MAKE_CLIP_VERTEX_FVF(pv, cv[2], p1, f1, vertexTransformed); for (i = pv->dwNumVertices-2; i; i--) { DWORD f2, f3; // vertex clip flags
f2 = clipCode[0]; f3 = clipCode[1];
BOOL needClip = FALSE; BOOL offFrustum = FALSE; if (f1 & f2 & f3) offFrustum = TRUE; else if ((f1 | f2 | f3) & pv->dwClipMaskOffScreen) needClip = TRUE;
if (offFrustum || needClip) { // if this tri does need clipping
if (vertexCount) { // first draw the ones that didn't need clipping
BYTE tmp[__MAX_VERTEX_SIZE]; BYTE *pStart = startVertex; if (startVertex != p1) { pStart -= vertexSize; memcpy (tmp, pStart, vertexSize); memcpy (pStart, p1, vertexSize); // Mark this call as gen by clipper, but set non clipped bit
pv->dwFlags |= D3DPV_NONCLIPPED; ret = DRAW_CLIPPED_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); pv->dwFlags &= ~D3DPV_NONCLIPPED; } else { ret = DRAW_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); } if (startVertex != p1) memcpy (pStart, tmp, vertexSize); if (ret) return ret; } // reset count and start ptr
vertexCount = 0; startVertex = vertex + vertexSize;
// now deal with the single clipped triangle
// first check if it should just be tossed or if it should be clipped
if (!offFrustum) { BYTE *p2 = vertex; BYTE *p3 = vertex + vertexSize;
MAKE_CLIP_VERTEX_FVF(pv, cv[0], p2, f2, vertexTransformed); MAKE_CLIP_VERTEX_FVF(pv, cv[1], p3, f3, vertexTransformed);
ret = Clip(pv, &cv[0], &cv[1], &cv[2]); if (ret) return ret; } } else vertexCount++; clipCode++; vertex += vertexSize; } // draw final batch, if any
if (vertexCount) { BYTE tmp[__MAX_VERTEX_SIZE]; BYTE *pStart = startVertex; if (startVertex == p1) { ret = DRAW_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); } else { pStart -= vertexSize; memcpy(tmp, pStart, vertexSize); memcpy(pStart, p1, vertexSize); // Mark this call as gen by clipper
pv->dwFlags |= D3DPV_NONCLIPPED; ret = DRAW_CLIPPED_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); pv->dwFlags &= ~D3DPV_NONCLIPPED; } if (startVertex != p1) memcpy(pStart, tmp, vertexSize); if (ret) return ret; } return D3D_OK; } //------------------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::ProcessClippedIndexedTriangleFan(D3DFE_PROCESSVERTICES *pv) { DWORD f1; // Clip code for the first vertex
D3DFE_CLIPCODE *clipCode; DWORD i; HRESULT ret; // Vertex array
BYTE *vertex; // Start indexed of the current in-screen triangle batch
LPBYTE startIndex; // Pointer to second index of the current triangle
LPBYTE index = (LPBYTE)pv->lpwIndices; int vertexCount; DWORD vertexSize; ClipVertex cv[3]; DWORD dwIndexSize = pv->dwIndexSize; DWORD dwFirstIndex; // First index of the primitive
BOOL vertexTransformed; // If there was a off-screen or clipped triangle we copy the first primitive
// index to the start of the next in-screen triangle batch
BOOL bWasClipping = FALSE;
vertexTransformed = pv->dwFlags & D3DPV_TLVCLIP; clipCode = pv->lpClipFlags; vertex = (BYTE*)pv->lpvOut; startIndex = (LPBYTE)pv->lpwIndices; vertexSize = pv->dwOutputSize; vertexCount = 0; // Update the address of the vertex array to handle the index base
if (pv->dwIndexOffset != 0) { vertex -= pv->dwIndexOffset * vertexSize; clipCode -= pv->dwIndexOffset; }
if (dwIndexSize == 2) dwFirstIndex = *(WORD*)index; else dwFirstIndex = *(DWORD*)index; f1 = clipCode[dwFirstIndex]; LPBYTE ver; // First vertex
ver = vertex + dwFirstIndex * vertexSize; index += dwIndexSize; // In the clipper color from the first vertex is propagated to all
// vertices for FLAT shade mode. In triangle fans the second vertex defines
// the color in FLAT shade mode. So we will make the vertex order: 1, 2, 0
MAKE_CLIP_VERTEX_FVF(pv, cv[2], ver, f1, vertexTransformed); for (i = pv->dwNumPrimitives; i; i--) { DWORD f2, f3; // vertex clip flags
DWORD v1, v2; if (dwIndexSize == 2) { v1 = *(WORD*)index; v2 = *(WORD*)(index + 2); } else { v1 = *(DWORD*)index; v2 = *(DWORD*)(index + 4); } f2 = clipCode[v1]; f3 = clipCode[v2]; BOOL needClip = FALSE; BOOL offFrustum = FALSE; if (f1 & f2 & f3) offFrustum = TRUE; else if ((f1 | f2 | f3) & pv->dwClipMaskOffScreen) needClip = TRUE;
if (offFrustum || needClip) { // if this tri does need clipping
if (vertexCount) { // first draw the ones that didn't need clipping
WORD* pStart = (WORD*)startIndex; DWORD tmp; if (bWasClipping) { // Save old value of the index before the current start
// index and copy the first primitive index there. This
// will the start of the current unclipped batch
if (dwIndexSize == 2) { pStart--; tmp = *pStart; *pStart = (WORD)dwFirstIndex; } else { pStart -= 2; tmp = *(DWORD*)pStart; *(DWORD*)pStart = dwFirstIndex; } } ret = DRAW_INDEX_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); if (bWasClipping) { // Restore old value
if (dwIndexSize == 2) *pStart = (WORD)tmp; else *(DWORD*)pStart = tmp; } if (ret) return ret;
} bWasClipping = TRUE; // reset count and start ptr
vertexCount = 0; startIndex = index + dwIndexSize;
// now deal with the single clipped triangle
// first check if it should just be tossed or if it should be clipped
if (!offFrustum) { BYTE *p2 = vertex + v1*vertexSize; BYTE *p3 = vertex + v2*vertexSize;
MAKE_CLIP_VERTEX_FVF(pv, cv[0], p2, f2, vertexTransformed); MAKE_CLIP_VERTEX_FVF(pv, cv[1], p3, f3, vertexTransformed);
ret = Clip(pv, &cv[0], &cv[1], &cv[2]); if (ret) return ret; } } else vertexCount++; index += dwIndexSize; } // draw final batch, if any
if (vertexCount) { WORD* pStart = (WORD*)startIndex; DWORD tmp; if (bWasClipping) { // Save old value of the index before the current start
// index and copy the first primitive index there. This
// will the start of the current unclipped batch
if (dwIndexSize == 2) { pStart--; tmp = *pStart; *pStart = (WORD)dwFirstIndex; } else { pStart -= 2; tmp = *(DWORD*)pStart; *(DWORD*)pStart = dwFirstIndex; } } ret = DRAW_INDEX_PRIM(pv, D3DPT_TRIANGLEFAN, pStart, vertexCount+2, vertexCount); if (bWasClipping) { // Restore old value
if (dwIndexSize == 2) *pStart = (WORD)tmp; else *(DWORD*)pStart = tmp; } if (ret) return ret;
} return D3D_OK; }
#define __PROCESS_LINE_NAME ProcessClippedLine
#define __PROCESS_TRI_LIST_NAME ProcessClippedTriangleList
#define __PROCESS_TRI_STRIP_NAME ProcessClippedTriangleStrip
#include "clipprim.h"
#define __INDEX_PRIM
#define __PROCESS_TRI_LIST_NAME ProcessClippedIndexedTriangleList
#define __PROCESS_TRI_STRIP_NAME ProcessClippedIndexedTriangleStrip
#define __PROCESS_LINE_NAME ProcessClippedIndexedLine
#include "clipprim.h"
//---------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::ProcessClippedPoints(D3DFE_PROCESSVERTICES *pv) { DWORD i; WORD count; BYTE *lpStartVertex; BYTE *lpCurVertex; HRESULT ret; D3DFE_CLIPCODE *clipCode; const DWORD nVertices = pv->dwNumVertices;
clipCode = pv->lpClipFlags; count = 0; lpStartVertex = lpCurVertex = (BYTE*)pv->lpvOut; for (i=0; i < nVertices; i++) { if (clipCode[i]) { // if this point is clipped
if (count) { // first draw the ones that didn't need clipping
ret = DRAW_PRIM(pv, D3DPT_POINTLIST, lpStartVertex, count, count); if (ret) return ret; } // reset count and start ptr
count = 0; lpCurVertex += pv->dwOutputSize; lpStartVertex = lpCurVertex; pv->pDDI->SkipVertices(1); } else { count++; lpCurVertex += pv->dwOutputSize; } } // draw final batch, if any
if (count) { ret = DRAW_PRIM(pv, D3DPT_POINTLIST, lpStartVertex, count, count); if (ret) return ret; } return D3D_OK; } //---------------------------------------------------------------------
// We do not throw away point sprites which centers are off screeen.
// We detect this case and compute screen coordinates for those sprites
//
HRESULT ProcessClippedPointSprites(D3DFE_PROCESSVERTICES *pv) { DWORD i; WORD count; BYTE *lpStartVertex; BYTE *lpCurVertex; HRESULT ret; D3DFE_CLIPCODE *clipCode; const DWORD nVertices = pv->dwNumVertices;
clipCode = pv->lpClipFlags; count = 0; lpStartVertex = lpCurVertex = (BYTE*)pv->lpvOut; for (i=0; i < nVertices; i++) { // If a point is outside screen or guard band, the sprite could still
// be visible (when the guard band is small enough
if (clipCode[i] & ~(D3DCS_LEFT | D3DCS_RIGHT | D3DCS_TOP | D3DCS_BOTTOM | __D3DCLIPGB_ALL)) { // This point is off viewing frustum
if (count) { // first draw the ones that didn't need clipping
ret = DRAW_PRIM(pv, D3DPT_POINTLIST, lpStartVertex, count, count); if (ret) return ret; } // reset count and start ptr
count = 0; lpCurVertex += pv->dwOutputSize; lpStartVertex = lpCurVertex; if (!(pv->dwDeviceFlags & D3DDEV_DOPOINTSPRITEEMULATION)) pv->pDDI->SkipVertices(1); } else { if (clipCode[i]) { // When we are here, the point sprite center is off-screen, but
// could be visible
// Non zero when the point is outside guard band
DWORD gbBits = clipCode[i] & __D3DCLIPGB_ALL;
// Screen coordinates were not computed for the point if there is
// no guard band or the point is outside the guard band
if (!(pv->dwDeviceFlags & D3DDEV_GUARDBAND) || (pv->dwDeviceFlags & D3DDEV_GUARDBAND) && gbBits) { D3DVECTORH* p = (D3DVECTORH*)lpCurVertex; float w = 1.0f/p->w; p->x = p->x * w * pv->vcache.scaleX + pv->vcache.offsetX; p->y = p->y * w * pv->vcache.scaleY + pv->vcache.offsetY; p->z = p->z * w * pv->vcache.scaleZ + pv->vcache.offsetZ; p->w = w; } } count++; lpCurVertex += pv->dwOutputSize; } } // draw final batch, if any
if (count) { ret = DRAW_PRIM(pv, D3DPT_POINTLIST, lpStartVertex, count, count); if (ret) return ret; } return D3D_OK; } //---------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::ProcessClippedIndexedPoints(D3DFE_PROCESSVERTICES *pv) { DWORD i; WORD count; BYTE *lpStartIndex; BYTE *lpCurIndex; HRESULT ret; D3DFE_CLIPCODE *clipCode; const DWORD nIndices = pv->dwNumIndices; DWORD dwIndexSize = pv->dwIndexSize; LPBYTE pIndices = (LPBYTE)pv->lpwIndices;
clipCode = pv->lpClipFlags; count = 0; lpStartIndex = lpCurIndex = (BYTE*)pv->lpwIndices; // Update the address of the vertex array to handle the index base
clipCode -= pv->dwIndexOffset;
for (i=0; i < nIndices; i++) { DWORD index; if (dwIndexSize == 2) index = *(WORD*)pIndices; else index = *(DWORD*)pIndices; pIndices += dwIndexSize; if (clipCode[index]) { // if this point is clipped
if (count) { // first draw the ones that didn't need clipping
ret = DRAW_INDEX_PRIM(pv, D3DPT_POINTLIST, (WORD*)lpStartIndex, count, count); if (ret) return ret; } // reset count and start ptr
count = 0; lpCurIndex += pv->dwIndexSize; lpStartIndex = lpCurIndex; } else { count++; lpCurIndex += pv->dwIndexSize; } } // draw final batch, if any
if (count) { ret = DRAW_INDEX_PRIM(pv, D3DPT_POINTLIST, (WORD*)lpStartIndex, count, count); if (ret) return ret; } return D3D_OK; }
|