1430 lines
51 KiB
Plaintext
1430 lines
51 KiB
Plaintext
/*============================ ==============================================;
|
|
*
|
|
* Copyright (C) 1998 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: pvone.mcp
|
|
* Content: Clipping and vertex processing in one loop
|
|
*
|
|
***************************************************************************/
|
|
#include "pch.cpp"
|
|
#pragma hdrstop
|
|
|
|
include(`pvvid.mh') dnl
|
|
|
|
extern DWORD g_DebugFlags; // defined in pvvid.mcp
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Bits describing a vertex
|
|
|
|
// Set if vertex participates in a completely inside triangle
|
|
const __VERTEX_IN_INSIDETRIANGLE = 1;
|
|
const __2_VERTEX_IN_INSIDETRIANGLE = 1 << 5;
|
|
const __3_VERTEX_IN_INSIDETRIANGLE = 1 << 10;
|
|
// Set if vertex participates in a triangle, which requires clipping
|
|
const __VERTEX_IN_CLIPTRIANGLE = 2;
|
|
const __2_VERTEX_IN_CLIPTRIANGLE = 2 << 5;
|
|
const __3_VERTEX_IN_CLIPTRIANGLE = 2 << 10;
|
|
// The following two bits are set for the first vertex of every triangle
|
|
// in the triangle strip.
|
|
const __TRIANGLE_OUTSIDE = 4;
|
|
const __TRIANGLE_CLIP = 8;
|
|
const __TRIANGLE_INSIDE = 16;
|
|
|
|
const __3_TRIANGLE_OUTSIDE = 4 << 10;
|
|
const __3_TRIANGLE_CLIP = 8 << 10;
|
|
const __2_TRIANGLE_OUTSIDE = 4 << 5;
|
|
const __2_TRIANGLE_CLIP = 8 << 5;
|
|
const __3_TRIANGLE_INSIDE = 16 << 10;
|
|
const __2_TRIANGLE_INSIDE = 16 << 5;
|
|
//--------------------------------------------------------------------------
|
|
// TransformVertexMakeClipCode
|
|
//
|
|
// Transforms vertex coordinates to the clipping space and computes clip code
|
|
//
|
|
// Arguments:
|
|
// pInp - input pointer (D3DVERTEX)
|
|
// pOut - output pointer (ClipVertex)
|
|
// Output:
|
|
// Returns clip code
|
|
// Transformed coordinates are written to the clip vertex (hx, hy, hz, hw, clip)
|
|
//
|
|
DWORD TransformVertexMakeClipCode(D3DFE_PROCESSVERTICES *pv, D3DVERTEX* pInp,
|
|
D3DVALUE* pWeights, BYTE* pMatrixIndices, ClipVertex* pOut)
|
|
{
|
|
D3DVALUE x,y,z,w;
|
|
DWORD clip;
|
|
d_TransformVertex(1, pInp, (&pv->mCTM[0]), x,y,z,w, pWeights, pMatrixIndices)
|
|
d_ComputeClipCode(1)
|
|
if (pv->dwDeviceFlags & D3DDEV_GUARDBAND)
|
|
{
|
|
d_ComputeClipCodeGB(2)
|
|
}
|
|
pOut->hx = x;
|
|
pOut->hy = y;
|
|
pOut->hz = z;
|
|
pOut->hw = w;
|
|
pOut->clip = clip;
|
|
return clip;
|
|
}
|
|
//--------------------------------------------------------------------------
|
|
// ComputeScreenCoordinates
|
|
//
|
|
// Computes screen coordinates for the ClipVertex
|
|
// Arguments:
|
|
// pOut - pointer to the ClipVertex
|
|
//
|
|
inline void ComputeScreenCoordinates(D3DFE_PROCESSVERTICES *pv, ClipVertex* pInp, D3DVECTORH *pOut)
|
|
{
|
|
D3DVALUE xs, ys, zs, w;
|
|
w = D3DVAL(1)/pInp->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(1,pInp->hx,pInp->hy,pInp->hz,w,xs,ys,zs)
|
|
pOut->x = xs;
|
|
pOut->y = ys;
|
|
pOut->z = zs;
|
|
pOut->w = w;
|
|
}
|
|
//--------------------------------------------------------------------------
|
|
// Update input and output pointers to start from dwStartVertexIndex
|
|
//
|
|
inline void SetInputAndOutputPointers(D3DFE_PROCESSVERTICES *pv, DWORD dwStartVertexIndex)
|
|
{
|
|
// Update output pointer
|
|
pv->lpvOut = (char*)pv->lpvOut + dwStartVertexIndex * pv->dwOutputSize;
|
|
// Update input pointers
|
|
pv->position.lpvData = (char*)pv->position.lpvData + dwStartVertexIndex * pv->position.dwStride;
|
|
if (pv->dwDeviceFlags & D3DDEV_STRIDE)
|
|
{
|
|
pv->normal.lpvData = (char*)pv->normal.lpvData + dwStartVertexIndex * pv->normal.dwStride;
|
|
pv->diffuse.lpvData = (char*)pv->diffuse.lpvData + dwStartVertexIndex * pv->diffuse.dwStride;
|
|
pv->specular.lpvData = (char*)pv->specular.lpvData + dwStartVertexIndex * pv->specular.dwStride;
|
|
|
|
for (DWORD i=0; i < pv->nTexCoord; i++)
|
|
pv->textures[i].lpvData = (char*)pv->textures[i].lpvData +
|
|
dwStartVertexIndex * pv->textures[i].dwStride;
|
|
}
|
|
}
|
|
dnl//--------------------------------------------------------------------------
|
|
dnl// d_TransformVertexMakeClipCode
|
|
dnl//
|
|
dnl// Transforms vertex coordinates to the clipping space and computes clip code
|
|
dnl//
|
|
dnl// Arguments:
|
|
dnl// $1 - margin count
|
|
dnl// $2 - input pointer (D3DVERTEX)
|
|
dnl// $3 - output pointer (ClipVertex)
|
|
dnl// $4 - pointer to weights
|
|
dnl// $5 - pointer to matrix indices
|
|
dnl// Output:
|
|
dnl// x,y,z,w should be defined as float
|
|
dnl// clip should be defined as DWORD
|
|
dnl//
|
|
define(`d_TransformVertexMakeClipCode',`dnl
|
|
d_empty_($1)d_TransformVertex($1, $2, m, x,y,z,w, $4, $5)
|
|
d_margin($1)d_ComputeClipCode($1)
|
|
d_margin($1)if (pv->dwDeviceFlags & D3DDEV_GUARDBAND)
|
|
d_margin($1){
|
|
d_margin($1) d_ComputeClipCodeGB($1+1)
|
|
d_margin($1)}
|
|
d_margin($1)$3->hx = x;
|
|
d_margin($1)$3->hy = y;
|
|
d_margin($1)$3->hz = z;
|
|
d_margin($1)$3->hw = w;
|
|
d_margin($1)$3->clip = clip;') dnl
|
|
dnl//--------------------------------------------------------------------------
|
|
dnl// Copies processed vertex to the output FVF buffer
|
|
dnl//
|
|
dnl// Input:
|
|
dnl// $1 - Margin count
|
|
dnl// $2 - output buffer
|
|
dnl// Notes:
|
|
dnl// Output vertex pointer is moved to the next vertex
|
|
dnl//
|
|
define(`d_CopyToOutputVertex',`dnl
|
|
d_empty_($1)// copy to the output vertex
|
|
d_margin($1)$2->sx = x;
|
|
d_margin($1)$2->sy = y;
|
|
d_margin($1)$2->sz = z;
|
|
d_margin($1)$2->rhw = w;
|
|
|
|
d_margin($1)DWORD *pOut = (DWORD*)((char*)$2 + pv->diffuseOffsetOut);
|
|
d_margin($1)if (pv->dwVIDOut & D3DFVF_DIFFUSE)
|
|
d_margin($1) *pOut++ = pv->lighting.outDiffuse;
|
|
d_margin($1)if (pv->dwVIDOut & D3DFVF_SPECULAR)
|
|
d_margin($1) *pOut++ = pv->lighting.outSpecular;
|
|
d_margin($1)d_CopyTextureCoord($1, pOut)
|
|
d_margin($1)$2 = (D3DTLVERTEX*)((BYTE*)$2 + pv->dwOutputSize);')dnl
|
|
dnl//--------------------------------------------------------------------------
|
|
dnl// Copies processed vertex to the clip vertex
|
|
dnl//
|
|
dnl// Input:
|
|
dnl// $1 - Margin count
|
|
dnl// $2 - clip vertex address
|
|
dnl//
|
|
define(`d_CopyToClipVertex',`dnl
|
|
d_empty_($1)if (($2->clip & pv->dwClipMaskOffScreen) == 0)
|
|
d_margin($1){ // Copy screen coordinates
|
|
d_margin($1) $2->sx = x;
|
|
d_margin($1) $2->sy = y;
|
|
d_margin($1) $2->sz = z;
|
|
d_margin($1) $2->rhw = w;
|
|
d_margin($1)}
|
|
d_margin($1)$2->color = pv->lighting.outDiffuse;
|
|
d_margin($1)$2->specular= pv->lighting.outSpecular;
|
|
d_margin($1)d_CopyTextureCoord($1, $2->tex)') dnl
|
|
dnl//--------------------------------------------------------------------------
|
|
define(`d_ProcessPrimitive',`dnl
|
|
d_empty_($1){
|
|
d_margin($1) pv->pGeometryFuncs->ProcessVertices(pv);
|
|
d_margin($1) if (pv->dwClipIntersection)
|
|
d_margin($1) return D3D_OK;
|
|
d_margin($1) return (DoDrawPrimitive(pv));
|
|
d_margin($1)}')dnl
|
|
//---------------------------------------------------------------------
|
|
// Clipping and lighting a non-indexed triangle list in one pass
|
|
//
|
|
HRESULT D3DFE_PVFUNCSI::ProcessTriangleList(D3DFE_PROCESSVERTICES *pv)
|
|
{
|
|
#ifdef DEBUG_PIPELINE
|
|
if (g_DebugFlags & __DEBUG_ONEPASS)
|
|
d_ProcessPrimitive(1)
|
|
#endif
|
|
if (pv->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
d_ProcessPrimitive(1)
|
|
|
|
pv->dwFirstClippedVertex = 0xFFFFFFFF;
|
|
pv->dwFlags |= D3DPV_ONEPASSCLIPPING;
|
|
pv->pGeometryFuncs->ProcessVertices(pv);
|
|
|
|
int primitiveCount = 0; // Number of triangles in the current unclipped part
|
|
BYTE *startVertex = (BYTE*)pv->lpvOut;
|
|
|
|
if (pv->dwFirstClippedVertex != 0xFFFFFFFF)
|
|
{
|
|
if (pv->dwFirstClippedVertex > 3)
|
|
{
|
|
// Compute number of unclipped primitives
|
|
primitiveCount = pv->dwFirstClippedVertex / 3;
|
|
// Index of the first vertex to process
|
|
DWORD dwStartVertexIndex = primitiveCount * 3;
|
|
DWORD dwVertexCount = dwStartVertexIndex;
|
|
// Compute new number of primitives to process
|
|
pv->dwNumPrimitives = pv->dwNumPrimitives - primitiveCount;
|
|
|
|
SetInputAndOutputPointers(pv, dwStartVertexIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pv->dwFlags &= ~D3DPV_ONEPASSCLIPPING;
|
|
if (pv->dwClipIntersection)
|
|
return D3D_OK;
|
|
return (DoDrawPrimitive(pv));
|
|
}
|
|
|
|
HRESULT ret = D3D_OK;
|
|
D3DTLVERTEX*lpOutVer = (D3DTLVERTEX*)pv->lpvOut;
|
|
ClipVertex cv[3];
|
|
DWORD dwClipMaskOffScreen = pv->dwClipMaskOffScreen;
|
|
LPD3DMATRIXI m = &pv->mCTM[0];
|
|
D3DLIGHTINGELEMENT EyeSpaceData;
|
|
|
|
d_Setup()
|
|
|
|
cv[0].next = &cv[1];
|
|
cv[1].next = &cv[2];
|
|
|
|
for (DWORD i = pv->dwNumPrimitives; i; i--)
|
|
{
|
|
ClipVertex *out = cv;
|
|
D3DVERTEX *lpInpVer = (D3DVERTEX*)in;
|
|
D3DVALUE *pWeights = inWeights;
|
|
BYTE *pMatrixIndices = inMatrixIndices;
|
|
// First we transform three vertices and compute the clip codes
|
|
dwClipUnion = 0;
|
|
dwClipIntersection = ~0;
|
|
for (DWORD i=3; i; i--)
|
|
{
|
|
DWORD clip = TransformVertexMakeClipCode(pv, lpInpVer, pWeights, pMatrixIndices, out);
|
|
out++;
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
lpInpVer = (D3DVERTEX*)((BYTE*)lpInpVer + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
}
|
|
pv->dwClipUnion |= dwClipUnion;
|
|
pv->dwClipIntersection &= dwClipIntersection;
|
|
// Now we can check where the triangle is
|
|
if (!dwClipIntersection)
|
|
{
|
|
if (!(dwClipUnion & dwClipMaskOffScreen))
|
|
{ // The triangle does not require clipping
|
|
ClipVertex *lpXfmVer = cv;
|
|
primitiveCount++;
|
|
for (DWORD i=3; i; i--)
|
|
{
|
|
D3DVALUE x,y,z,w;
|
|
w = D3DVAL(1)/lpXfmVer->hw;
|
|
d_ComputeScreenCoordinates(5, lpXfmVer->hx, lpXfmVer->hy, lpXfmVer->hz, w, lpOutVer)
|
|
|
|
d_DoLightingAndFog(5, in, inNormal, inDiffuse, inSpecular, lpOutVer, inWeights, inMatrixIndices)
|
|
D3DVALUE *pOutTexture = (D3DVALUE*)((BYTE*)lpOutVer + pv->texOffsetOut);
|
|
d_CopyTextureCoordUpdateInputPointers(5, pOutTexture)
|
|
|
|
lpXfmVer = lpXfmVer->next;
|
|
lpOutVer = (D3DTLVERTEX*)((BYTE*)lpOutVer + dwOutVerSize);
|
|
}
|
|
}
|
|
else
|
|
{ // The triangle requires clipping
|
|
if (primitiveCount)
|
|
{ // first draw the ones that didn't need clipping
|
|
DWORD vertexCount = primitiveCount*3;
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLELIST, startVertex,
|
|
vertexCount, primitiveCount);
|
|
startVertex = (BYTE*)lpOutVer;
|
|
if (ret)
|
|
goto l_Exit;
|
|
}
|
|
primitiveCount = 0;
|
|
ClipVertex *lpXfmVer = cv;
|
|
for (DWORD i=3; i; i--)
|
|
{
|
|
if ((lpXfmVer->clip & dwClipMaskOffScreen) == 0)
|
|
{
|
|
float x,y,z;
|
|
float w = D3DVAL(1)/lpXfmVer->hw;
|
|
d_ComputeScreenCoordinates(6, lpXfmVer->hx, lpXfmVer->hy, lpXfmVer->hz, w, lpXfmVer)
|
|
}
|
|
|
|
d_ComputeOutputColors(5, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
|
|
if (pv->dwVIDOut & D3DFVF_DIFFUSE)
|
|
lpXfmVer->color = pv->lighting.outDiffuse;
|
|
if (pv->dwVIDOut & D3DFVF_SPECULAR)
|
|
lpXfmVer->specular = pv->lighting.outSpecular;
|
|
|
|
d_CopyTextureCoordUpdateInputPointers(5, lpXfmVer->tex)
|
|
lpXfmVer++;
|
|
}
|
|
ret = Clip(pv, &cv[0], &cv[1], &cv[2]);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
}
|
|
else
|
|
{ // Triangle is outside
|
|
// Update input pointers
|
|
d_UpdateInputPointers(3)
|
|
d_UpdateInputPointers(3)
|
|
d_UpdateInputPointers(3)
|
|
}
|
|
}
|
|
// draw final batch, if any
|
|
if (primitiveCount)
|
|
{
|
|
DWORD dwVertexCount = primitiveCount*3;
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLELIST, startVertex,
|
|
dwVertexCount, primitiveCount);
|
|
}
|
|
l_Exit:
|
|
return ret;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Clipping and lighting a non-indexed triangle fan in one pass
|
|
//
|
|
HRESULT D3DFE_PVFUNCSI::ProcessTriangleFan(D3DFE_PROCESSVERTICES *pv)
|
|
{
|
|
#ifdef DEBUG_PIPELINE
|
|
if (g_DebugFlags & __DEBUG_ONEPASS)
|
|
d_ProcessPrimitive(1)
|
|
#endif
|
|
if (pv->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
d_ProcessPrimitive(1)
|
|
|
|
// TRUE, if the current triangle is the first in the unclipped part of the primitive
|
|
BOOL bFirstInsideTriangle = TRUE;
|
|
// Index of a vertex to start processing from
|
|
DWORD dwStartVertexIndex = 0;
|
|
int primitiveCount = 0; // Number of triangles in the current unclipped part
|
|
// Start vertex for the current unclipped part of the triangle fan
|
|
BYTE *startVertex = (BYTE*)pv->lpvOut;
|
|
// Current pointer to the output vertex buffer
|
|
D3DTLVERTEX*lpOutVer = (D3DTLVERTEX*)startVertex;
|
|
DWORD dwVertexCount = 1; // Number of processed vertices (first vertex is processed
|
|
// outside of the vretx loop)
|
|
DWORD dwInsideVertexCount= 0; // Number of vertices, put to the lpVout buffer
|
|
|
|
pv->dwFirstClippedVertex = 0xFFFFFFFF;
|
|
pv->dwFlags |= D3DPV_ONEPASSCLIPPING;
|
|
pv->pGeometryFuncs->ProcessVertices(pv);
|
|
if (pv->dwFirstClippedVertex == 0xFFFFFFFF)
|
|
{
|
|
pv->dwFlags &= ~D3DPV_ONEPASSCLIPPING;
|
|
if (pv->dwClipIntersection)
|
|
return D3D_OK;
|
|
return (DoDrawPrimitive(pv));
|
|
}
|
|
else
|
|
{
|
|
if (pv->dwFirstClippedVertex >= 3)
|
|
{
|
|
bFirstInsideTriangle = FALSE; // First vertex is already copied to the output
|
|
// We process again the last unclipped vertex
|
|
dwStartVertexIndex = pv->dwFirstClippedVertex - 1;
|
|
// Update output pointer
|
|
pv->lpvOut = (char*)pv->lpvOut + dwStartVertexIndex * pv->dwOutputSize;
|
|
lpOutVer = (D3DTLVERTEX*)pv->lpvOut;
|
|
primitiveCount = dwStartVertexIndex - 2;
|
|
dwVertexCount += pv->dwFirstClippedVertex;
|
|
dwInsideVertexCount += pv->dwFirstClippedVertex;
|
|
}
|
|
}
|
|
|
|
HRESULT ret;
|
|
ClipVertex cv1;
|
|
ClipVertex *cv2; // Vertex to process
|
|
ClipVertex *cv3; // Vertex to transform
|
|
ClipVertex *cv; // Previous to cv2. Used for clipping
|
|
D3DVERTEX *lpInpStart; // Next input vertex to transform
|
|
D3DVALUE *pWeights; // Vertex weights. Should be in ssync with lpInpStart
|
|
BYTE *pMatrixIndices; // Vertex weights. Should be in ssync with lpInpStart
|
|
DWORD dwClipMaskOffScreen = pv->dwClipMaskOffScreen; // Mask for guard band bits
|
|
LPD3DMATRIXI m = &pv->mCTM[0];
|
|
// Vertex flags has 5 bits per vertex
|
|
// Bits 0 - 4 - vertex cv2
|
|
// Bits 5 - 9 - vertex cv3
|
|
DWORD vf = 0;
|
|
D3DLIGHTINGELEMENT EyeSpaceData;
|
|
|
|
d_Setup()
|
|
|
|
lpInpStart = (D3DVERTEX*)in;
|
|
pWeights = (D3DVALUE*)inWeights;
|
|
pMatrixIndices = (BYTE*)inMatrixIndices;
|
|
|
|
// This is the loop count. We use "+1", because the processed vertex is behind the
|
|
// transformed one.
|
|
DWORD i = pv->dwNumPrimitives + 1;
|
|
{
|
|
// Transform, process and keep the first vertex
|
|
|
|
DWORD clip = TransformVertexMakeClipCode(pv, lpInpStart, pWeights, pMatrixIndices, &cv1);
|
|
// PSGP could transform vertices with different precision. It is possible that
|
|
// PSGP detects that a vertex is inside, but we here mark it as the outside.
|
|
// We force the clipping code to be zero in case when we re-transform vertices
|
|
if (pv->dwFirstClippedVertex >= 3)
|
|
{
|
|
clip = 0;
|
|
}
|
|
if ((clip & dwClipMaskOffScreen) == 0)
|
|
{
|
|
ComputeScreenCoordinates(pv, &cv1, (D3DVECTORH*)&cv1.sx);
|
|
}
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
cv1.color = pv->lighting.outDiffuse;
|
|
cv1.specular = pv->lighting.outSpecular;
|
|
|
|
d_CopyTextureCoordUpdateInputPointers(2, ((DWORD*)(cv1.tex)))
|
|
lpInpStart = (D3DVERTEX*)in;
|
|
pWeights = inWeights;
|
|
pMatrixIndices = inMatrixIndices;
|
|
}
|
|
|
|
cv2 = &pv->clipVer[0]; // Next output vertex for process
|
|
cv3 = &pv->clipVer[1]; // Next output vertex for transform
|
|
|
|
// In case when there were unclipped part, we have to update pointers
|
|
// to start from dwStartVertex
|
|
if (pv->dwFirstClippedVertex >= 3)
|
|
{
|
|
// Update input pointers
|
|
in = (D3DVECTOR*)((char*)pv->position.lpvData + dwStartVertexIndex * pv->position.dwStride);
|
|
if (pv->dwDeviceFlags & D3DDEV_STRIDE)
|
|
{
|
|
inWeights = (D3DVALUE*)((char*)pv->weights.lpvData + dwStartVertexIndex * pv->weights.dwStride);
|
|
inMatrixIndices = (BYTE*)pv->matrixIndices.lpvData + dwStartVertexIndex * pv->matrixIndices.dwStride;
|
|
inNormal = (D3DVECTOR*)((char*)pv->normal.lpvData + dwStartVertexIndex * pv->normal.dwStride);
|
|
inDiffuse = (DWORD*)((char*)pv->diffuse.lpvData + dwStartVertexIndex * pv->diffuse.dwStride);
|
|
inSpecular = (DWORD*)((char*)pv->specular.lpvData + dwStartVertexIndex * pv->specular.dwStride);
|
|
|
|
for (DWORD i=0; i < pv->nTexCoord; i++)
|
|
inTexture[i] = (D3DVALUE*)((char*)pv->textures[i].lpvData +
|
|
dwStartVertexIndex * pv->textures[i].dwStride);
|
|
}
|
|
else
|
|
{
|
|
inWeights = (D3DVALUE*) ((char*)in + 3*sizeof(float));
|
|
inMatrixIndices = (BYTE*)(inWeights + pv->dwNumVerBlends - 1);
|
|
inNormal = (D3DVECTOR*) ((char*)in + pv->normalOffset);
|
|
inDiffuse = (DWORD*) ((char*)in + pv->diffuseOffset);
|
|
inSpecular = (DWORD*) ((char*)in + pv->specularOffset);
|
|
inTexture[0] = (D3DVALUE*) ((char*)in + pv->texOffset);
|
|
}
|
|
// Process the last unclipped vertex and copy it to the cv2
|
|
// This vertex will be copied to the output buffer later
|
|
TransformVertexMakeClipCode(pv, (D3DVERTEX*)in, inWeights, inMatrixIndices, cv2);
|
|
// Transformed vertex is ahead by one vertex
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)in + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)inWeights + pv->weights.dwStride);
|
|
pMatrixIndices = inMatrixIndices + pv->matrixIndices.dwStride;
|
|
i = i-pv->dwFirstClippedVertex + 2; // New loop count
|
|
cv2->clip = 0; // Fixes potential PSGP precision problem
|
|
goto l_InsideLoop;
|
|
}
|
|
|
|
// Transformed vertex will be ahead of the processed one, because we want
|
|
// to know where is the triangle when processing a vertex
|
|
{
|
|
TransformVertexMakeClipCode(pv, lpInpStart, pWeights, pMatrixIndices, cv2);
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
}
|
|
|
|
dwClipUnion |= cv1.clip | cv2->clip;
|
|
dwClipIntersection &= cv1.clip & cv2->clip;
|
|
|
|
float x,y,z,w;
|
|
int clip;
|
|
|
|
l_ClippedLoop:
|
|
|
|
d_TransformVertexMakeClipCode(1, lpInpStart, cv3, pWeights, pMatrixIndices)
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
|
|
l_ClippedLoop2:
|
|
|
|
// Set status for cv1, cv2, cv3 vertices based on their clip codes
|
|
if (!(cv1.clip & cv2->clip & cv3->clip))
|
|
{
|
|
if (!((cv1.clip | cv2->clip | cv3->clip) & dwClipMaskOffScreen))
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE |__2_VERTEX_IN_INSIDETRIANGLE |
|
|
__2_TRIANGLE_INSIDE;
|
|
else
|
|
vf |= __VERTEX_IN_CLIPTRIANGLE | __2_VERTEX_IN_CLIPTRIANGLE |
|
|
__2_TRIANGLE_CLIP;
|
|
}
|
|
else
|
|
vf |= __2_TRIANGLE_OUTSIDE;
|
|
|
|
l_ClippedLoopProcessOnly:
|
|
|
|
if ((cv2->clip & pv->dwClipMaskOffScreen) == 0)
|
|
{ // vertex is inside the guardband or frustum
|
|
// Compute screen coordinates
|
|
w = D3DVAL(1)/cv2->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv2->hx, cv2->hy, cv2->hz, w, x,y,z)
|
|
}
|
|
if (vf & (__VERTEX_IN_INSIDETRIANGLE | __VERTEX_IN_CLIPTRIANGLE))
|
|
{
|
|
// Compute colors
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
}
|
|
if (vf & __VERTEX_IN_INSIDETRIANGLE)
|
|
{
|
|
if (vf & __TRIANGLE_OUTSIDE)
|
|
{
|
|
if (primitiveCount > 0)
|
|
{
|
|
DWORD dwVerCount = primitiveCount+2;
|
|
// Draw batched primitive
|
|
if (dwInsideVertexCount < dwVertexCount)
|
|
{
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLEFAN, startVertex,
|
|
dwVerCount, primitiveCount);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
// Prepare for the next part of the primitive
|
|
startVertex += dwVerCount * pv->dwOutputSize;
|
|
}
|
|
else
|
|
{
|
|
// Suppose we have a fan with 5 vertices. When there is the
|
|
// following sequence of triangles:
|
|
// "inside" - "outside" - "inside", 6 vertices will be put to
|
|
// the vertex buffer, but space was allocated only for 5.
|
|
// To prevent similar cases draw the previous primitive as clipped
|
|
// and re-use vertex buffer space
|
|
pv->dwFlags |= D3DPV_NONCLIPPED;
|
|
ret = DRAW_CLIPPED_PRIM(pv, D3DPT_TRIANGLEFAN, startVertex,
|
|
dwVerCount, primitiveCount);
|
|
pv->dwFlags &= ~D3DPV_NONCLIPPED;
|
|
dwInsideVertexCount -= dwVerCount;
|
|
lpOutVer = (D3DTLVERTEX*)startVertex;
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
primitiveCount = 0;
|
|
bFirstInsideTriangle = TRUE;
|
|
}
|
|
}
|
|
if (bFirstInsideTriangle)
|
|
{
|
|
// For the first completely inside triangle we have to
|
|
// write the first vertex to the output
|
|
MAKE_TL_VERTEX_FVF(pv, (BYTE*)lpOutVer, &cv1);
|
|
lpOutVer = (D3DTLVERTEX*)((BYTE*)lpOutVer + pv->dwOutputSize);
|
|
bFirstInsideTriangle = FALSE;
|
|
dwInsideVertexCount++;
|
|
}
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
dwInsideVertexCount++;
|
|
}
|
|
if (vf & __VERTEX_IN_CLIPTRIANGLE)
|
|
{
|
|
d_CopyToClipVertex(2, cv2)
|
|
}
|
|
if (vf & __TRIANGLE_INSIDE)
|
|
{ // Triangle is inside the frustum
|
|
primitiveCount++;
|
|
}
|
|
else
|
|
{
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
DWORD dwVerCount = primitiveCount+2;
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLEFAN, startVertex,
|
|
dwVerCount, primitiveCount);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
// Prepare for the next part of the primitive
|
|
startVertex += dwVerCount * pv->dwOutputSize;
|
|
primitiveCount = 0;
|
|
bFirstInsideTriangle = TRUE;
|
|
}
|
|
if (vf & __TRIANGLE_CLIP)
|
|
{ // The triangle requires clipping
|
|
// Clip prev triangle - cv1, cv, cv2
|
|
HRESULT ret = Clip(pv, cv, cv2, &cv1);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
}
|
|
d_UpdateInputPointers(1);
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
cv = cv2;
|
|
cv2 = cv3;
|
|
cv3 = cv3->next;
|
|
vf >>= 5;
|
|
dwVertexCount++;
|
|
if (--i > 1)
|
|
{
|
|
if (dwClipUnion == 0 && primitiveCount == 1)
|
|
{
|
|
// If all vertices are inside the frustum we use the optimized loop
|
|
goto l_InsideLoop;
|
|
}
|
|
goto l_ClippedLoop;
|
|
}
|
|
// For the last vertex we have to do processing only
|
|
if (i)
|
|
goto l_ClippedLoopProcessOnly;
|
|
goto l_Exit;
|
|
|
|
l_InsideLoop:
|
|
|
|
d_TransformVertex(1, lpInpStart, m, x,y,z,w, pWeights, pMatrixIndices)
|
|
d_ComputeClipCode(1)
|
|
cv3->hx = x;
|
|
cv3->hy = y;
|
|
cv3->hz = z;
|
|
cv3->hw = w;
|
|
cv3->clip = 0;
|
|
if (clip)
|
|
goto l_ExitInsideLoop;
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
|
|
l_InsideLoopProcessOnly:
|
|
{
|
|
w = D3DVAL(1)/cv2->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv2->hx, cv2->hy, cv2->hz, w, x,y,z)
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
d_UpdateInputPointers(2)
|
|
primitiveCount++;
|
|
cv2 = cv3;
|
|
cv3 = cv3->next;
|
|
dwVertexCount++;
|
|
dwInsideVertexCount++;
|
|
if (--i > 1)
|
|
goto l_InsideLoop;
|
|
if (i)
|
|
goto l_InsideLoopProcessOnly;
|
|
}
|
|
goto l_Exit;
|
|
|
|
l_ExitInsideLoop:
|
|
|
|
{
|
|
// For the last transforem vertex (cv3) we computed only clip code
|
|
// without guardband
|
|
d_ComputeClipCodeGB(2)
|
|
cv3->clip = clip;
|
|
dwClipUnion |= clip;
|
|
// We have to set status for the cv2 vertex
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE | __TRIANGLE_INSIDE;
|
|
goto l_ClippedLoop2;
|
|
}
|
|
|
|
l_Exit:
|
|
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
DWORD dwVertexCount = primitiveCount+2;
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLEFAN, startVertex,
|
|
dwVertexCount, primitiveCount);
|
|
}
|
|
pv->dwClipUnion = dwClipUnion;
|
|
pv->dwClipIntersection = dwClipIntersection;
|
|
return D3D_OK;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Clipping and lighting a non-indexed triangle strip in one pass
|
|
//
|
|
// Vertices cv1 cv2 cv3 cv4 cv5
|
|
// Next to Next to
|
|
// process transform
|
|
// | | | |
|
|
// * * * * * * * *
|
|
//
|
|
HRESULT D3DFE_PVFUNCSI::ProcessTriangleStrip(D3DFE_PROCESSVERTICES *pv)
|
|
{
|
|
#ifdef DEBUG_PIPELINE
|
|
if (g_DebugFlags & __DEBUG_ONEPASS)
|
|
d_ProcessPrimitive(1)
|
|
#endif
|
|
if (pv->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
d_ProcessPrimitive(1)
|
|
|
|
int primitiveCount; // Number of triangles in the current unclipped part
|
|
DWORD dwTriIndex; // Index of the current triangle in the primitive
|
|
// Start vertex for the current unclipped part of the triangle strip
|
|
BYTE *startVertex = (BYTE*)pv->lpvOut;
|
|
// Vertex flags has 5 bits per vertex
|
|
// Bits 0 - 4 - vertex cv3
|
|
// Bits 5 - 9 - vertex cv4
|
|
// Bits 10 - 14 - vertex cv5
|
|
DWORD vf = 0;
|
|
|
|
D3DLIGHTINGELEMENT EyeSpaceData;
|
|
pv->dwFirstClippedVertex = 0xFFFFFFFF;
|
|
pv->dwFlags |= D3DPV_ONEPASSCLIPPING;
|
|
pv->pGeometryFuncs->ProcessVertices(pv);
|
|
if (pv->dwFirstClippedVertex == 0xFFFFFFFF)
|
|
{
|
|
pv->dwFlags &= ~D3DPV_ONEPASSCLIPPING;
|
|
if (pv->dwClipIntersection)
|
|
return D3D_OK;
|
|
return (DoDrawPrimitive(pv));
|
|
}
|
|
else
|
|
{
|
|
primitiveCount = 0;
|
|
dwTriIndex = 0;
|
|
if (pv->dwFirstClippedVertex > 3)
|
|
{
|
|
DWORD dwStartVertexIndex = pv->dwFirstClippedVertex - 2;
|
|
primitiveCount = dwStartVertexIndex - 2;
|
|
dwTriIndex = dwStartVertexIndex;
|
|
SetInputAndOutputPointers(pv, dwStartVertexIndex);
|
|
}
|
|
}
|
|
|
|
HRESULT ret = D3D_OK;
|
|
// Current pointer to the output vertex buffer
|
|
D3DTLVERTEX*lpOutVer = (D3DTLVERTEX*)pv->lpvOut;
|
|
ClipVertex *cv1; // First vertex of delayed clipped triangle
|
|
ClipVertex *cv2; // Second vertex of delayed clipped triangle
|
|
ClipVertex *cv4;
|
|
ClipVertex *cv3; // Vertex to process
|
|
ClipVertex *cv5; // Vertex to transform
|
|
D3DVERTEX *lpInpStart; // Next input vertex to transform
|
|
D3DVALUE *pWeights; // Vertex weights. Should be in ssync with lpInpStart
|
|
BYTE *pMatrixIndices; // Vertex weights. Should be in ssync with lpInpStart
|
|
DWORD dwClipMaskOffScreen = pv->dwClipMaskOffScreen; // Mask for guard band bits
|
|
LPD3DMATRIXI m = &pv->mCTM[0];
|
|
DWORD dwNumPrimitives = pv->dwNumPrimitives;
|
|
DWORD i;
|
|
BOOL bOddStrip = FALSE; // Strip starts from odd vertex index
|
|
// (1, 3, 5 ...). First triangle is drawn as
|
|
// indexed.
|
|
|
|
d_Setup()
|
|
|
|
lpInpStart = (D3DVERTEX*)in;
|
|
pWeights = (D3DVALUE*)inWeights;
|
|
pMatrixIndices = (BYTE*)inMatrixIndices;
|
|
cv1 = NULL;
|
|
cv2 = NULL;
|
|
cv3 = pv->clipVer;
|
|
cv5 = pv->clipVer;
|
|
cv4 = cv3->next;
|
|
// Transform first two vertices and copy them to the clip buffer
|
|
for (DWORD j=2; j; j--)
|
|
{
|
|
DWORD clip = TransformVertexMakeClipCode(pv, lpInpStart, pWeights, pMatrixIndices, cv5);
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
cv5 = cv5->next;
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
}
|
|
|
|
float x,y,z,w;
|
|
int clip;
|
|
|
|
if (pv->dwFirstClippedVertex > 3)
|
|
{
|
|
// Force clip code to be zero to fix potential PSGP precision problem
|
|
cv3->clip = 0;
|
|
cv4->clip = 0;
|
|
dwClipIntersection = 0;
|
|
// We have to set status for the cv3 and cv4 vertices
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE |__2_VERTEX_IN_INSIDETRIANGLE |
|
|
__TRIANGLE_INSIDE | __2_TRIANGLE_INSIDE;
|
|
|
|
goto l_InsideLoop;
|
|
}
|
|
|
|
dwClipUnion |= cv3->clip | cv4->clip;
|
|
dwClipIntersection &= cv3->clip & cv4->clip;
|
|
|
|
l_ClippedLoop:
|
|
|
|
d_TransformVertexMakeClipCode(1, lpInpStart, cv5, pWeights, pMatrixIndices)
|
|
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
|
|
l_ClippedLoop2:
|
|
|
|
// Set status for cv3, cv4, cv5 vertices based on their clip codes
|
|
if (!(cv3->clip & cv4->clip & cv5->clip))
|
|
{
|
|
if (!((cv3->clip | cv4->clip | cv5->clip) & dwClipMaskOffScreen))
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE |__2_VERTEX_IN_INSIDETRIANGLE |
|
|
__3_VERTEX_IN_INSIDETRIANGLE | __3_TRIANGLE_INSIDE;
|
|
else
|
|
vf |= __VERTEX_IN_CLIPTRIANGLE | __2_VERTEX_IN_CLIPTRIANGLE |
|
|
__3_VERTEX_IN_CLIPTRIANGLE | __3_TRIANGLE_CLIP;
|
|
}
|
|
else
|
|
vf |= __3_TRIANGLE_OUTSIDE;
|
|
|
|
l_ProcessOnly:
|
|
|
|
// When we process a vertex, its status is completely defined
|
|
if ((cv3->clip & pv->dwClipMaskOffScreen) == 0)
|
|
{ // vertex is inside the guardband or frustum
|
|
// Compute screen coordinates
|
|
w = D3DVAL(1)/cv3->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv3->hx, cv3->hy, cv3->hz, w, x,y,z)
|
|
}
|
|
if (vf & (__VERTEX_IN_INSIDETRIANGLE | __VERTEX_IN_CLIPTRIANGLE))
|
|
{
|
|
// Compute colors
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
}
|
|
if (vf & __VERTEX_IN_INSIDETRIANGLE)
|
|
{
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
}
|
|
if (vf & __VERTEX_IN_CLIPTRIANGLE)
|
|
{
|
|
d_CopyToClipVertex(2, cv3)
|
|
}
|
|
|
|
if (vf & __TRIANGLE_INSIDE)
|
|
{
|
|
primitiveCount++;
|
|
if (primitiveCount == 1)
|
|
{
|
|
if (dwTriIndex & 1)
|
|
// First triangle has an odd index
|
|
bOddStrip = TRUE;
|
|
else
|
|
bOddStrip = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
if (bOddStrip)
|
|
{
|
|
// Draw first triangle as indexed triangle
|
|
WORD indices[3] = {0, 2, 1};
|
|
pv->lpvOut = startVertex;
|
|
pv->dwIndexSize = 2;
|
|
pv->pDDI->SetIndexedPrimParams(0, 0, 3, pv->pDDI->GetCurrentPrimBase());
|
|
ret = DRAW_INDEX_PRIM(pv, D3DPT_TRIANGLELIST, indices, 3, 1);
|
|
if (ret != D3D_OK)
|
|
goto l_Error;
|
|
primitiveCount--;
|
|
// Move to the next vertex
|
|
startVertex += pv->dwOutputSize;
|
|
pv->pDDI->SkipVertices(1);
|
|
}
|
|
DWORD dwVerCount = primitiveCount+2;
|
|
if (primitiveCount > 0)
|
|
{
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLESTRIP, startVertex,
|
|
dwVerCount, primitiveCount);
|
|
if (ret != D3D_OK)
|
|
goto l_Error;
|
|
}
|
|
else
|
|
{
|
|
// When primitiveCount is 0 we still need to skip 2 vertices
|
|
// of the odd triangle
|
|
pv->pDDI->SkipVertices(2);
|
|
}
|
|
// Prepare for the next part of the primitive
|
|
startVertex += dwVerCount * pv->dwOutputSize;
|
|
primitiveCount = 0;
|
|
// If next triangle is inside, we have to re-use the last vertex
|
|
// of just rendered part of the triangle strip
|
|
if (vf & __2_TRIANGLE_INSIDE)
|
|
{
|
|
pv->pDDI->MovePrimitiveBase(-1);
|
|
startVertex -= pv->dwOutputSize;
|
|
}
|
|
}
|
|
if (vf & __TRIANGLE_CLIP)
|
|
{ // The triangle requires clipping
|
|
// Clip prev triangle - cv1, cv2, cv3
|
|
HRESULT ret;
|
|
if (dwTriIndex & 1)
|
|
ret = Clip(pv, cv1, cv3, cv2);
|
|
else
|
|
ret = Clip(pv, cv1, cv2, cv3);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
}
|
|
d_UpdateInputPointers(1);
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
cv1 = cv2;
|
|
cv2 = cv3;
|
|
cv3 = cv4;
|
|
cv4 = cv5;
|
|
cv5 = cv5->next;
|
|
dwTriIndex++;
|
|
vf >>= 5;
|
|
if (dwTriIndex < dwNumPrimitives)
|
|
{
|
|
if (dwClipUnion == 0 && primitiveCount == 1)
|
|
{
|
|
// If all vertices are inside the frustum we use the optimized loop
|
|
goto l_InsideLoop;
|
|
}
|
|
goto l_ClippedLoop;
|
|
}
|
|
// We still have to process the last two vertices. They are already transformed
|
|
if (dwTriIndex < dwNumPrimitives + 2)
|
|
goto l_ProcessOnly;
|
|
goto l_Exit;
|
|
|
|
l_InsideLoop:
|
|
|
|
d_TransformVertex(1, lpInpStart, m, x,y,z,w, pWeights, pMatrixIndices)
|
|
d_ComputeClipCode(1)
|
|
cv5->hx = x;
|
|
cv5->hy = y;
|
|
cv5->hz = z;
|
|
cv5->hw = w;
|
|
cv5->clip = 0;
|
|
if (clip)
|
|
goto l_ExitInsideLoop;
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
|
|
l_InsideLoopProcessOnly:
|
|
{
|
|
// Because "primitiveCount" is now in sync with the transformed vertex
|
|
// (not processed one as it should be), it is greater by 2 then the actual
|
|
// primitive count
|
|
primitiveCount++;
|
|
w = D3DVAL(1)/cv3->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv3->hx, cv3->hy, cv3->hz, w, x,y,z)
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
dwTriIndex++;
|
|
cv3 = cv3->next;
|
|
cv5 = cv5->next;
|
|
d_UpdateInputPointers(2);
|
|
if (dwTriIndex < dwNumPrimitives)
|
|
{
|
|
goto l_InsideLoop;
|
|
}
|
|
// We still have to process the last two vertices. They are already transformed
|
|
if (dwTriIndex < dwNumPrimitives + 2)
|
|
goto l_InsideLoopProcessOnly;
|
|
}
|
|
goto l_Exit;
|
|
|
|
l_ExitInsideLoop:
|
|
{
|
|
// For the last transforem vertex (cv5) we computed only clip code
|
|
// without guardband
|
|
d_ComputeClipCodeGB(2)
|
|
cv5->clip = clip;
|
|
dwClipUnion |= clip;
|
|
// We have to set status for the cv3 and cv4 vertices
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE |__2_VERTEX_IN_INSIDETRIANGLE |
|
|
__TRIANGLE_INSIDE | __2_TRIANGLE_INSIDE;
|
|
cv4 = cv3->next;
|
|
goto l_ClippedLoop2;
|
|
}
|
|
|
|
l_Exit:
|
|
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
if (bOddStrip)
|
|
{
|
|
// Draw first triangle as indexed triangle
|
|
WORD indices[3] = {0, 2, 1};
|
|
pv->dwIndexSize = 2;
|
|
pv->lpvOut = startVertex;
|
|
pv->pDDI->SetIndexedPrimParams(0, 0, 3, pv->pDDI->GetCurrentPrimBase());
|
|
ret = DRAW_INDEX_PRIM(pv, D3DPT_TRIANGLELIST, indices, 3, 1);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
primitiveCount--;
|
|
// Move to the next vertex
|
|
pv->pDDI->SkipVertices(1);
|
|
startVertex += pv->dwOutputSize;
|
|
}
|
|
DWORD dwVertexCount = primitiveCount+2;
|
|
if (primitiveCount > 0)
|
|
{
|
|
ret = DRAW_PRIM(pv, D3DPT_TRIANGLESTRIP, startVertex,
|
|
dwVertexCount, primitiveCount);
|
|
if (ret != D3D_OK)
|
|
goto l_Error;
|
|
}
|
|
else
|
|
{
|
|
pv->pDDI->SkipVertices(2);
|
|
}
|
|
}
|
|
pv->dwClipUnion = dwClipUnion;
|
|
pv->dwClipIntersection = dwClipIntersection;
|
|
l_Error:
|
|
return ret;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Clipping and lighting a non-indexed line list in one pass
|
|
//
|
|
HRESULT D3DFE_PVFUNCSI::ProcessLineList(D3DFE_PROCESSVERTICES *pv)
|
|
{
|
|
#ifdef DEBUG_PIPELINE
|
|
if (g_DebugFlags & __DEBUG_ONEPASS)
|
|
d_ProcessPrimitive(1)
|
|
#endif
|
|
if (pv->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
d_ProcessPrimitive(1)
|
|
|
|
pv->dwFirstClippedVertex = 0xFFFFFFFF;
|
|
pv->dwFlags |= D3DPV_ONEPASSCLIPPING;
|
|
pv->pGeometryFuncs->ProcessVertices(pv);
|
|
|
|
int primitiveCount = 0; // Number of triangles in the current unclipped part
|
|
BYTE *startVertex = (BYTE*)pv->lpvOut;
|
|
|
|
if (pv->dwFirstClippedVertex != 0xFFFFFFFF)
|
|
{
|
|
if (pv->dwFirstClippedVertex > 2)
|
|
{
|
|
// Compute number of unclipped primitives
|
|
primitiveCount = pv->dwFirstClippedVertex >> 1;
|
|
// Index of the first vertex to process
|
|
DWORD dwStartVertexIndex = primitiveCount << 1;
|
|
DWORD dwVertexCount = dwStartVertexIndex;
|
|
// Compute new number of primitives to process
|
|
pv->dwNumPrimitives = pv->dwNumPrimitives - primitiveCount;
|
|
|
|
SetInputAndOutputPointers(pv, dwStartVertexIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pv->dwFlags &= ~D3DPV_ONEPASSCLIPPING;
|
|
if (pv->dwClipIntersection)
|
|
return D3D_OK;
|
|
return (DoDrawPrimitive(pv));
|
|
}
|
|
|
|
HRESULT ret = D3D_OK;
|
|
D3DTLVERTEX*lpOutVer = (D3DTLVERTEX*)pv->lpvOut;
|
|
ClipVertex cv[2];
|
|
DWORD dwClipMaskOffScreen = pv->dwClipMaskOffScreen;
|
|
LPD3DMATRIXI m = &pv->mCTM[0];
|
|
D3DLIGHTINGELEMENT EyeSpaceData;
|
|
|
|
d_Setup()
|
|
|
|
cv[0].next = &cv[1];
|
|
|
|
for (DWORD n = pv->dwNumPrimitives; n; n--)
|
|
{
|
|
ClipVertex *out = cv;
|
|
D3DVERTEX *lpInpVer = (D3DVERTEX*)in;
|
|
D3DVALUE *pWeights = inWeights;
|
|
BYTE* pMatrixIndices = inMatrixIndices;
|
|
// First we transform three vertices and compute the clip codes
|
|
dwClipUnion = 0;
|
|
dwClipIntersection = ~0;
|
|
for (DWORD i=2; i; i--)
|
|
{
|
|
DWORD clip = TransformVertexMakeClipCode(pv, lpInpVer, pWeights, pMatrixIndices, out);
|
|
out++;
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
lpInpVer = (D3DVERTEX*)((BYTE*)lpInpVer + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
}
|
|
pv->dwClipUnion |= dwClipUnion;
|
|
pv->dwClipIntersection &= dwClipIntersection;
|
|
// Now we can check where the triangle is
|
|
if (!dwClipIntersection)
|
|
{
|
|
if (!(dwClipUnion & dwClipMaskOffScreen))
|
|
{ // The line does not require clipping
|
|
ClipVertex *lpXfmVer = cv;
|
|
primitiveCount++;
|
|
for (DWORD i=2; i; i--)
|
|
{
|
|
ComputeScreenCoordinates(pv, lpXfmVer, (D3DVECTORH*)lpOutVer);
|
|
d_DoLightingAndFog(5, in, inNormal, inDiffuse, inSpecular, lpOutVer, inWeights, inMatrixIndices)
|
|
D3DVALUE *pOutTexture = (D3DVALUE*)((BYTE*)lpOutVer + pv->texOffsetOut);
|
|
d_CopyTextureCoordUpdateInputPointers(5, pOutTexture)
|
|
|
|
lpXfmVer = lpXfmVer->next;
|
|
lpOutVer = (D3DTLVERTEX*)((BYTE*)lpOutVer + dwOutVerSize);
|
|
}
|
|
}
|
|
else
|
|
{ // The line requires clipping
|
|
if (primitiveCount)
|
|
{ // first draw the ones that didn't need clipping
|
|
DWORD vertexCount = primitiveCount << 1;
|
|
ret = DRAW_PRIM(pv, D3DPT_LINELIST, startVertex,
|
|
vertexCount, primitiveCount);
|
|
startVertex = (BYTE*)lpOutVer;
|
|
if (ret)
|
|
goto l_Exit;
|
|
}
|
|
primitiveCount = 0;
|
|
ClipVertex *lpXfmVer = cv;
|
|
for (DWORD i=2; i; i--)
|
|
{
|
|
if ((lpXfmVer->clip & dwClipMaskOffScreen) == 0)
|
|
{
|
|
ComputeScreenCoordinates(pv, lpXfmVer, (D3DVECTORH*)&lpXfmVer->sx);
|
|
}
|
|
|
|
d_ComputeOutputColors(5, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
|
|
if (pv->dwVIDOut & D3DFVF_DIFFUSE)
|
|
lpXfmVer->color = pv->lighting.outDiffuse;
|
|
if (pv->dwVIDOut & D3DFVF_SPECULAR)
|
|
lpXfmVer->specular = pv->lighting.outSpecular;
|
|
|
|
d_CopyTextureCoordUpdateInputPointers(5, lpXfmVer->tex)
|
|
lpXfmVer++;
|
|
}
|
|
ret = ClipLine(pv, &cv[0], &cv[1]);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
}
|
|
else
|
|
{ // Line is outside
|
|
// Update input pointers
|
|
d_UpdateInputPointers(3)
|
|
d_UpdateInputPointers(3)
|
|
}
|
|
}
|
|
// draw final batch, if any
|
|
if (primitiveCount)
|
|
{
|
|
DWORD dwVertexCount = primitiveCount << 1;
|
|
ret = DRAW_PRIM(pv, D3DPT_LINELIST, startVertex, dwVertexCount, primitiveCount);
|
|
}
|
|
l_Exit:
|
|
return ret;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Clipping and lighting a non-indexed triangle line in one pass
|
|
//
|
|
// Vertices cv1 cv2 cv3
|
|
// Next to Next to
|
|
// process transform
|
|
// | | |
|
|
// * * * * * * *
|
|
//
|
|
HRESULT D3DFE_PVFUNCSI::ProcessLineStrip(D3DFE_PROCESSVERTICES *pv)
|
|
{
|
|
#ifdef DEBUG_PIPELINE
|
|
if (g_DebugFlags & __DEBUG_ONEPASS)
|
|
d_ProcessPrimitive(1)
|
|
#endif
|
|
if (pv->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
d_ProcessPrimitive(1)
|
|
|
|
int primitiveCount; // Number of triangles in the current unclipped part
|
|
DWORD dwTriIndex; // Index of the current triangle in the primitive
|
|
// Start vertex for the current unclipped part of the triangle strip
|
|
BYTE *startVertex = (BYTE*)pv->lpvOut;
|
|
// Vertex flags has 5 bits per vertex
|
|
// Bits 0 - 4 - vertex cv2
|
|
// Bits 5 - 9 - vertex cv3
|
|
DWORD vf = 0;
|
|
D3DLIGHTINGELEMENT EyeSpaceData;
|
|
|
|
pv->dwFirstClippedVertex = 0xFFFFFFFF;
|
|
pv->dwFlags |= D3DPV_ONEPASSCLIPPING;
|
|
pv->pGeometryFuncs->ProcessVertices(pv);
|
|
if (pv->dwFirstClippedVertex == 0xFFFFFFFF)
|
|
{
|
|
pv->dwFlags &= ~D3DPV_ONEPASSCLIPPING;
|
|
if (pv->dwClipIntersection)
|
|
return D3D_OK;
|
|
return (DoDrawPrimitive(pv));
|
|
}
|
|
else
|
|
{
|
|
primitiveCount = 0;
|
|
dwTriIndex = 0;
|
|
if (pv->dwFirstClippedVertex > 2)
|
|
{
|
|
DWORD dwStartVertexIndex = pv->dwFirstClippedVertex - 1;
|
|
primitiveCount = dwStartVertexIndex - 1;
|
|
dwTriIndex = dwStartVertexIndex;
|
|
// We have to set status for the cv2 vertex
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE | __TRIANGLE_INSIDE;
|
|
SetInputAndOutputPointers(pv, dwStartVertexIndex);
|
|
}
|
|
}
|
|
|
|
HRESULT ret = D3D_OK;
|
|
// Current pointer to the output vertex buffer
|
|
D3DTLVERTEX*lpOutVer = (D3DTLVERTEX*)pv->lpvOut;
|
|
ClipVertex *cv1; // First vertex of delayed clipped line
|
|
ClipVertex *cv2; // Vertex to process
|
|
ClipVertex *cv3; // Vertex to transform
|
|
D3DVERTEX *lpInpStart; // Next input vertex to transform
|
|
D3DVALUE *pWeights; // Vertex weights. Should be in ssync with lpInpStart
|
|
BYTE *pMatrixIndices; // Vertex weights. Should be in ssync with lpInpStart
|
|
DWORD dwClipMaskOffScreen = pv->dwClipMaskOffScreen; // Mask for guard band bits
|
|
LPD3DMATRIXI m = &pv->mCTM[0];
|
|
DWORD dwNumPrimitives = pv->dwNumPrimitives;
|
|
DWORD i;
|
|
|
|
d_Setup()
|
|
|
|
lpInpStart = (D3DVERTEX*)in;
|
|
pWeights = (D3DVALUE*)inWeights;
|
|
pMatrixIndices = (BYTE*)inMatrixIndices;
|
|
cv1 = NULL;
|
|
cv2 = pv->clipVer;
|
|
cv3 = pv->clipVer;
|
|
{
|
|
// Transform first vertex and copy it to the clip buffer
|
|
DWORD clip = TransformVertexMakeClipCode(pv, lpInpStart, pWeights, pMatrixIndices, cv3);
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
cv3 = cv3->next;
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
}
|
|
|
|
dwClipUnion |= cv2->clip;
|
|
dwClipIntersection &= cv2->clip;
|
|
|
|
float x,y,z,w;
|
|
int clip;
|
|
|
|
if (pv->dwFirstClippedVertex > 2)
|
|
{
|
|
// Force clip code to be zero to fix potential PSGP precision problem
|
|
cv2->clip = 0;
|
|
dwClipIntersection = 0;
|
|
goto l_InsideLoop;
|
|
}
|
|
l_ClippedLoop:
|
|
|
|
clip = TransformVertexMakeClipCode(pv, lpInpStart, pWeights, pMatrixIndices, cv3);
|
|
|
|
dwClipUnion |= clip;
|
|
dwClipIntersection &= clip;
|
|
|
|
l_ClippedLoop2:
|
|
|
|
// Set status for cv2, cv3 vertices based on their clip codes
|
|
if (!(cv2->clip & cv3->clip))
|
|
{
|
|
if (!((cv2->clip | cv3->clip) & dwClipMaskOffScreen))
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE |__2_VERTEX_IN_INSIDETRIANGLE |
|
|
__2_TRIANGLE_INSIDE;
|
|
else
|
|
vf |= __VERTEX_IN_CLIPTRIANGLE | __2_VERTEX_IN_CLIPTRIANGLE |
|
|
__2_TRIANGLE_CLIP;
|
|
}
|
|
else
|
|
vf |= __2_TRIANGLE_OUTSIDE;
|
|
|
|
l_ProcessOnly:
|
|
|
|
// When we process a vertex, its status is completely defined
|
|
if ((cv2->clip & pv->dwClipMaskOffScreen) == 0)
|
|
{ // vertex is inside the guardband or frustum
|
|
// Compute screen coordinates
|
|
w = D3DVAL(1)/cv2->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv2->hx, cv2->hy, cv2->hz, w, x,y,z)
|
|
}
|
|
if (vf & (__VERTEX_IN_INSIDETRIANGLE | __VERTEX_IN_CLIPTRIANGLE))
|
|
{
|
|
// Compute colors
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
}
|
|
if (vf & __VERTEX_IN_INSIDETRIANGLE)
|
|
{
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
}
|
|
if (vf & __VERTEX_IN_CLIPTRIANGLE)
|
|
{
|
|
d_CopyToClipVertex(2, cv2)
|
|
}
|
|
|
|
if (vf & __TRIANGLE_INSIDE)
|
|
{
|
|
primitiveCount++;
|
|
}
|
|
else
|
|
{
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
DWORD dwVerCount = primitiveCount+1;
|
|
ret = DRAW_PRIM(pv, D3DPT_LINESTRIP, startVertex, dwVerCount, primitiveCount);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
// Prepare for the next part of the primitive
|
|
startVertex += dwVerCount * pv->dwOutputSize;
|
|
primitiveCount = 0;
|
|
}
|
|
if (vf & __TRIANGLE_CLIP)
|
|
{ // The line requires clipping
|
|
// Clip prev triangle - cv1, cv2
|
|
HRESULT ret;
|
|
ret = ClipLine(pv, cv1, cv2);
|
|
if (ret != D3D_OK)
|
|
goto l_Exit;
|
|
}
|
|
}
|
|
d_UpdateInputPointers(1);
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
cv1 = cv2;
|
|
cv2 = cv3;
|
|
cv3 = cv3->next;
|
|
dwTriIndex++;
|
|
vf >>= 5;
|
|
if (dwTriIndex < dwNumPrimitives)
|
|
{
|
|
if (dwClipUnion == 0 && primitiveCount == 1)
|
|
{
|
|
// If all vertices are inside the frustum we use the optimized loop
|
|
goto l_InsideLoop;
|
|
}
|
|
goto l_ClippedLoop;
|
|
}
|
|
// We still have to process one last vertex. It is already transformed
|
|
if (dwTriIndex < dwNumPrimitives + 1)
|
|
goto l_ProcessOnly;
|
|
goto l_Exit;
|
|
|
|
l_InsideLoop:
|
|
|
|
d_TransformVertex(1, lpInpStart, m, x,y,z,w, pWeights, pMatrixIndices)
|
|
d_ComputeClipCode(1)
|
|
cv3->hx = x;
|
|
cv3->hy = y;
|
|
cv3->hz = z;
|
|
cv3->hw = w;
|
|
cv3->clip = 0;
|
|
if (clip)
|
|
goto l_ExitInsideLoop;
|
|
lpInpStart = (D3DVERTEX*)((BYTE*)lpInpStart + dwInpVerSize);
|
|
pWeights = (D3DVALUE*)((BYTE*)pWeights + pv->weights.dwStride);
|
|
pMatrixIndices = pMatrixIndices + pv->matrixIndices.dwStride;
|
|
|
|
l_InsideLoopProcessOnly:
|
|
{
|
|
// Because "primitiveCount" is now in sync with the transformed vertex
|
|
// (not processed one as it should be), it is greater by 1 then the actual
|
|
// primitive count
|
|
primitiveCount++;
|
|
w = D3DVAL(1)/cv2->hw;
|
|
d_ComputeScreenCoordinatesNoOutput(2, cv2->hx, cv2->hy, cv2->hz, w, x,y,z)
|
|
d_ComputeOutputColors(2, in, inNormal, inDiffuse, inSpecular, inWeights, inMatrixIndices)
|
|
d_CopyToOutputVertex(2, lpOutVer)
|
|
dwTriIndex++;
|
|
cv2 = cv2->next;
|
|
cv3 = cv3->next;
|
|
d_UpdateInputPointers(2);
|
|
if (dwTriIndex < dwNumPrimitives)
|
|
{
|
|
goto l_InsideLoop;
|
|
}
|
|
// We still have to process one last two vertex. It is already transformed
|
|
if (dwTriIndex < dwNumPrimitives + 1)
|
|
goto l_InsideLoopProcessOnly;
|
|
}
|
|
goto l_Exit;
|
|
|
|
l_ExitInsideLoop:
|
|
{
|
|
// For the last transforem vertex (cv3) we computed only clip code
|
|
// without guardband
|
|
d_ComputeClipCodeGB(2)
|
|
cv3->clip = clip;
|
|
dwClipUnion |= clip;
|
|
// We have to set status for the cv3 and cv4 vertices
|
|
vf |= __VERTEX_IN_INSIDETRIANGLE | __TRIANGLE_INSIDE ;
|
|
goto l_ClippedLoop2;
|
|
}
|
|
|
|
l_Exit:
|
|
|
|
if (primitiveCount > 0)
|
|
{
|
|
// Draw batched primitive
|
|
DWORD dwVertexCount = primitiveCount+1;
|
|
ret = DRAW_PRIM(pv, D3DPT_LINESTRIP, startVertex, dwVertexCount, primitiveCount);
|
|
}
|
|
pv->dwClipUnion = dwClipUnion;
|
|
pv->dwClipIntersection = dwClipIntersection;
|
|
return ret;
|
|
}
|