/*============================================================================ * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: bspline.cpp * Content: Implementation for B-Splines * ****************************************************************************/ #include "pch.cpp" #pragma hdrstop //----------------------------------------------------------------------------- // RefDev::ProcessBSpline //----------------------------------------------------------------------------- HRESULT RefDev::ProcessBSpline( DWORD dwOffW, DWORD dwOffH, DWORD dwWidth, DWORD dwHeight, DWORD dwStride, DWORD order, FLOAT *pPrimSegments ) { if(order == 0) { order = 2; } else { ++order; } int u_range = dwWidth - (order - 1); int v_range = dwHeight - (order - 1); if(u_range <= 0 || v_range <= 0) { DPFERR("Insufficient control vertices for current order"); return DDERR_INVALIDPARAMS; } RDBSpline bsp(dwWidth, dwHeight, order, order); static const unsigned M[4] = {0, 0, 0, 0}, N[4] = {0, 0, 0, 0}; unsigned u_segs, v_segs, u_start, v_start; if(pPrimSegments != 0) { u_segs = unsigned(double(unsigned(pPrimSegments[0]) + unsigned(pPrimSegments[2])) / 2.0 + 0.5); v_segs = unsigned(double(unsigned(pPrimSegments[1]) + unsigned(pPrimSegments[3])) / 2.0 + 0.5); if(u_segs == 0) { u_segs = 1; } if(v_segs == 0) { v_segs = 1; } if(unsigned(pPrimSegments[0]) != unsigned(pPrimSegments[2]) || unsigned(pPrimSegments[1]) != unsigned(pPrimSegments[3])) { // First, gulp, the irregular outside // To make life easier, we don't want to deal with the case when u_segs or v_segs is one // This ensures that there is at least one inside point if(u_segs == 1) { u_segs = 2; } if(v_segs == 1) { v_segs = 2; } // Start with top edge unsigned segs = unsigned(pPrimSegments[0]); unsigned k_outer = 0; unsigned k_inner = 1; unsigned outer_inc = u_segs - 2; unsigned inner_inc = segs; unsigned outer = 0; unsigned inner = 0; double u0, v0, u1, v1, u2, v2; while(outer_inc != 0 ? (inner != inner_inc * outer_inc || outer != inner_inc * outer_inc) : (k_outer < segs)) { if(inner < outer) { _ASSERT(k_inner < u_segs - 1, "Error in logic"); u0 = double(u_range * k_inner) / double(u_segs) + double(order - 1); v0 = double(v_range) / double(v_segs) + double(order - 1); u1 = double(u_range * k_outer) / double(segs) + double(order - 1); v1 = double(order - 1); u2 = double(u_range * (k_inner + 1)) / double(u_segs) + double(order - 1); v2 = v0; ++k_inner; inner += inner_inc; } else { _ASSERT(k_outer < segs, "Error in logic"); u0 = double(u_range * k_inner) / double(u_segs) + double(order - 1); v0 = double(v_range) / double(v_segs) + double(order - 1); u1 = double(u_range * k_outer) / double(segs) + double(order - 1); v1 = double(order - 1); u2 = double(u_range * (k_outer + 1)) / double(segs) + double(order - 1); v2 = v1; ++k_outer; outer += outer_inc; } HRESULT hr = DrawTessTri(bsp, dwOffW, dwOffH, dwStride, M, N, u0, v0, u1, v1, u2, v2, bsp.TexCoordU(u0), bsp.TexCoordV(v0), bsp.TexCoordU(u1), bsp.TexCoordV(v1), bsp.TexCoordU(u2), bsp.TexCoordV(v2), false, false, false); if(FAILED(hr)) { return hr; } } // bottom edge segs = unsigned(pPrimSegments[2]); k_outer = segs; k_inner = u_segs - 1; inner_inc = segs; outer = 0; inner = 0; while(outer_inc != 0 ? (inner != inner_inc * outer_inc || outer != inner_inc * outer_inc) : (k_outer > 0)) { if(inner < outer) { _ASSERT(k_inner > 1, "Error in logic"); u0 = double(u_range * k_inner) / double(u_segs) + double(order - 1); v0 = double(v_range * (v_segs - 1)) / double(v_segs) + double(order - 1); u1 = double(u_range * k_outer) / double(segs) + double(order - 1); v1 = double(v_range + order - 1); u2 = double(u_range * (k_inner - 1)) / double(u_segs) + double(order - 1); v2 = v0; --k_inner; inner += inner_inc; } else { _ASSERT(k_outer > 0, "Error in logic"); u0 = double(u_range * k_inner) / double(u_segs) + double(order - 1); v0 = double(v_range * (v_segs - 1)) / double(v_segs) + double(order - 1); u1 = double(u_range * k_outer) / double(segs) + double(order - 1); v1 = double(v_range + order - 1); u2 = double(u_range * (k_outer - 1)) / double(segs) + double(order - 1); v2 = v1; --k_outer; outer += outer_inc; } HRESULT hr = DrawTessTri(bsp, dwOffW, dwOffH, dwStride, M, N, u0, v0, u1, v1, u2, v2, bsp.TexCoordU(u0), bsp.TexCoordV(v0), bsp.TexCoordU(u1), bsp.TexCoordV(v1), bsp.TexCoordU(u2), bsp.TexCoordV(v2), false, false, false); if(FAILED(hr)) { return hr; } } // right edge segs = unsigned(pPrimSegments[1]); k_outer = 0; k_inner = 1; outer_inc = v_segs - 2; inner_inc = segs; outer = 0; inner = 0; while(outer_inc != 0 ? (inner != inner_inc * outer_inc || outer != inner_inc * outer_inc) : (k_outer < segs)) { if(inner < outer) { _ASSERT(k_inner < v_segs - 1, "Error in logic"); u0 = double(u_range * (u_segs - 1)) / double(u_segs) + double(order - 1); v0 = double(v_range * k_inner) / double(v_segs) + double(order - 1); u1 = double(u_range + order - 1); v1 = double(v_range * k_outer) / double(segs) + double(order - 1); u2 = u0; v2 = double(v_range * (k_inner + 1)) / double(v_segs) + double(order - 1); ++k_inner; inner += inner_inc; } else { _ASSERT(k_outer < segs, "Error in logic"); u0 = double(u_range * (u_segs - 1)) / double(u_segs) + double(order - 1); v0 = double(v_range * k_inner) / double(v_segs) + double(order - 1); u1 = double(u_range + order - 1); v1 = double(v_range * k_outer) / double(segs) + double(order - 1); u2 = u1; v2 = double(v_range * (k_outer + 1)) / double(segs) + double(order - 1); ++k_outer; outer += outer_inc; } HRESULT hr = DrawTessTri(bsp, dwOffW, dwOffH, dwStride, M, N, u0, v0, u1, v1, u2, v2, bsp.TexCoordU(u0), bsp.TexCoordV(v0), bsp.TexCoordU(u1), bsp.TexCoordV(v1), bsp.TexCoordU(u2), bsp.TexCoordV(v2), false, false, false); if(FAILED(hr)) { return hr; } } // left edge segs = unsigned(pPrimSegments[3]); k_outer = segs; k_inner = v_segs - 1; inner_inc = segs; outer = 0; inner = 0; while(outer_inc != 0 ? (inner != inner_inc * outer_inc || outer != inner_inc * outer_inc) : (k_outer > 0)) { if(inner < outer) { _ASSERT(k_inner > 1, "Error in logic"); u0 = double(u_range) / double(u_segs) + double(order - 1); v0 = double(v_range * k_inner) / double(v_segs) + double(order - 1); u1 = double(order - 1); v1 = double(v_range * k_outer) / double(segs) + double(order - 1); u2 = u0; v2 = double(v_range * (k_inner - 1)) / double(v_segs) + double(order - 1); --k_inner; inner += inner_inc; } else { _ASSERT(k_outer > 0, "Error in logic"); u0 = double(u_range) / double(u_segs) + double(order - 1); v0 = double(v_range * k_inner) / double(v_segs) + double(order - 1); u1 = double(order - 1); v1 = double(v_range * k_outer) / double(segs) + double(order - 1); u2 = u1; v2 = double(v_range * (k_outer - 1)) / double(segs) + double(order - 1); --k_outer; outer += outer_inc; } HRESULT hr = DrawTessTri(bsp, dwOffW, dwOffH, dwStride, M, N, u0, v0, u1, v1, u2, v2, bsp.TexCoordU(u0), bsp.TexCoordV(v0), bsp.TexCoordU(u1), bsp.TexCoordV(v1), bsp.TexCoordU(u2), bsp.TexCoordV(v2), false, false, false); if(FAILED(hr)) { return hr; } } // Now do the regular interior u_start = 1; v_start = 1; } else { // It can be done regularly u_start = 0; v_start = 0; } } else { unsigned segs = unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]); if(segs == 0) { segs = 1; } u_start = 0; v_start = 0; u_segs = segs; v_segs = segs; } for(unsigned i = v_start; i < v_segs - v_start; ++i) { double v0 = double(v_range * i) / double(v_segs) + double(order - 1); double v1 = double(v_range * (i + 1)) / double(v_segs) + double(order - 1); for(unsigned j = u_start; j < u_segs - u_start; ++j) { double u0 = double(u_range * j) / double(u_segs) + double(order - 1); double u1 = double(u_range * (j + 1)) / double(u_segs) + double(order - 1); HRESULT hr = DrawTessQuad(bsp, dwOffW, dwOffH, dwStride, M, N, u0, v0, u1, v1, bsp.TexCoordU(u0), bsp.TexCoordV(v0), bsp.TexCoordU(u1), bsp.TexCoordV(v1), false); if(FAILED(hr)) { return hr; } } } return S_OK; } //----------------------------------------------------------------------------- // RDBSpline::Sample //----------------------------------------------------------------------------- void RDBSpline::Sample(DWORD dwDataType, double u, double v, const BYTE *pRow, DWORD dwStride, DWORD dwPitch, BYTE *Q) const { double Acc[4] = {0.0, 0.0, 0.0, 0.0}; unsigned dwElements = 0; switch(dwDataType) { case D3DVSDT_FLOAT4: ++dwElements; case D3DVSDT_FLOAT3: ++dwElements; case D3DVSDT_FLOAT2: ++dwElements; case D3DVSDT_FLOAT1: ++dwElements; { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NM = N * Basis(j, m_dwOrderU, u); const FLOAT *B = (FLOAT*)pCol; for(unsigned e = 0; e < dwElements; ++e) { Acc[e] += double(B[e]) * NM; } pCol += dwStride; } pRow += dwPitch; } for(unsigned e = 0; e < dwElements; ++e) { ((FLOAT*)Q)[e] = FLOAT(Acc[e]); } } break; case D3DVSDT_D3DCOLOR: case D3DVSDT_UBYTE4: dwElements = 4; { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NM = N * Basis(j, m_dwOrderU, u); const BYTE *B = pCol; for(unsigned e = 0; e < 4; ++e) { Acc[e] += double(B[e]) * NM; } pCol += dwStride; } pRow += dwPitch; } for(unsigned e = 0; e < 4; ++e) { int t = int(Acc[e]); Q[e] = BYTE(t < 0 ? 0 : (t > 255 ? 255 : t)); } } break; case D3DVSDT_SHORT4: dwElements += 2; case D3DVSDT_SHORT2: dwElements += 2; { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NM = N * Basis(j, m_dwOrderU, u); const SHORT *B = (SHORT*)pCol; for(unsigned e = 0; e < dwElements; ++e) { Acc[e] += double(B[e]) * NM; } pCol += dwStride; } pRow += dwPitch; } for(unsigned e = 0; e < dwElements; ++e) { ((SHORT*)Q)[e] = SHORT(Acc[e]); } } break; default: _ASSERT(FALSE, "Ununderstood vertex element data type"); } } //----------------------------------------------------------------------------- // RDBSpline::SampleNormal //----------------------------------------------------------------------------- void RDBSpline::SampleNormal(DWORD dwDataType, double u, double v, const BYTE *pRow, DWORD dwStride, DWORD dwPitch, BYTE *Q) const { double Acc[2][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}; // Fudge u and v if they are on the boundary. This is because the derivative is discontinuous on the boundary // and we really want it to be slightly inside the boundary. if(v == double(m_dwHeight)) { v -= v * DBL_EPSILON; } if(u == double(m_dwWidth)) { u -= u * DBL_EPSILON; } switch(dwDataType) { case D3DVSDT_FLOAT4: case D3DVSDT_FLOAT3: { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); double NPrime = BasisPrime(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NMPrime = N * BasisPrime(j, m_dwOrderU, u); double NPrimeM = NPrime * Basis(j, m_dwOrderU, u); const FLOAT *B = (FLOAT*)pCol; for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B[e]) * NMPrime; Acc[1][e] += double(B[e]) * NPrimeM; } pCol += dwStride; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_D3DCOLOR: case D3DVSDT_UBYTE4: { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); double NPrime = BasisPrime(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NMPrime = N * BasisPrime(j, m_dwOrderU, u); double NPrimeM = NPrime * Basis(j, m_dwOrderU, u); const BYTE *B = pCol; for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B[e]) * NMPrime; Acc[1][e] += double(B[e]) * NPrimeM; } pCol += dwStride; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_SHORT4: { for(unsigned i = 0; i < m_dwHeight; ++i) { double N = Basis(i, m_dwOrderV, v); double NPrime = BasisPrime(i, m_dwOrderV, v); const BYTE *pCol = pRow; for(unsigned j = 0; j < m_dwWidth; ++j) { double NMPrime = N * BasisPrime(j, m_dwOrderU, u); double NPrimeM = NPrime * Basis(j, m_dwOrderU, u); const SHORT *B = (SHORT*)pCol; for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B[e]) * NMPrime; Acc[1][e] += double(B[e]) * NPrimeM; } pCol += dwStride; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_SHORT2: case D3DVSDT_FLOAT2: case D3DVSDT_FLOAT1: default: _ASSERT(FALSE, "Ununderstood vertex element data type"); } } //----------------------------------------------------------------------------- // RDBSpline::SampleDegenerateNormal //----------------------------------------------------------------------------- void RDBSpline::SampleDegenerateNormal(DWORD dwDataType, const BYTE *pRow, DWORD dwStride, DWORD dwPitch, BYTE *Q) const { double Acc[2][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}; switch(dwDataType) { case D3DVSDT_FLOAT4: case D3DVSDT_FLOAT3: { for(unsigned i = 0; i < m_dwHeight; ++i) { double JPrime = BasisPrime(i, m_dwOrderV, 0.0); const FLOAT *B1 = (FLOAT*)pRow; const FLOAT *B2 = (FLOAT*)(pRow + (m_dwWidth - 1) * dwStride); for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B2[e]) * JPrime; Acc[1][e] += double(B1[e]) * JPrime; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_D3DCOLOR: case D3DVSDT_UBYTE4: { for(unsigned i = 0; i < m_dwHeight; ++i) { double JPrime = BasisPrime(i, m_dwOrderV, 0.0); const BYTE *B1 = pRow; const BYTE *B2 = pRow + (m_dwWidth - 1) * dwStride; for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B2[e]) * JPrime; Acc[1][e] += double(B1[e]) * JPrime; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_SHORT4: { for(unsigned i = 0; i < m_dwHeight; ++i) { double JPrime = BasisPrime(i, m_dwOrderV, 0.0); const SHORT *B1 = (SHORT*)pRow; const SHORT *B2 = (SHORT*)(pRow + (m_dwWidth - 1) * dwStride); for(unsigned e = 0; e < 3; ++e) { Acc[0][e] += double(B2[e]) * JPrime; Acc[1][e] += double(B1[e]) * JPrime; } pRow += dwPitch; } ((FLOAT*)Q)[0] = FLOAT(Acc[0][1] * Acc[1][2] - Acc[0][2] * Acc[1][1]); ((FLOAT*)Q)[1] = FLOAT(Acc[0][2] * Acc[1][0] - Acc[0][0] * Acc[1][2]); ((FLOAT*)Q)[2] = FLOAT(Acc[0][0] * Acc[1][1] - Acc[0][1] * Acc[1][0]); } break; case D3DVSDT_FLOAT2: case D3DVSDT_FLOAT1: case D3DVSDT_SHORT2: default: _ASSERT(FALSE, "Ununderstood vertex element data type"); } } //----------------------------------------------------------------------------- // RDBSpline::Basis //----------------------------------------------------------------------------- double RDBSpline::Basis(unsigned i, unsigned k, double s) const { if(k == 1) { if(Knot(i) <= s && s < Knot(i + 1)) { return 1.0; } else { return 0.0; } } else { _ASSERT(k != 0, "Arithmatic error in RDBSpline::Basis"); return ((s - Knot(i)) * Basis(i, k - 1, s)) / (Knot(i + k - 1) - Knot(i)) + ((Knot(i + k) - s) * Basis(i + 1, k - 1, s)) / (Knot(i + k) - Knot(i + 1)); } } //----------------------------------------------------------------------------- // RDBSpline::BasisPrime //----------------------------------------------------------------------------- double RDBSpline::BasisPrime(unsigned i, unsigned k, double s) const { if(k == 1) { return 0.0; } else { _ASSERT(k != 0, "Arithmatic error in RDBSpline::BasisPrime"); return (Basis(i, k - 1, s) + (s - Knot(i)) * BasisPrime(i, k - 1, s)) / (Knot(i + k - 1) - Knot(i)) + ((Knot(i + k) - s) * BasisPrime(i + 1, k - 1, s) - Basis(i + 1, k - 1, s)) / (Knot(i + k) - Knot(i + 1)); } }