/* ============ idSurface_Patch::LerpVert ============ */ void idSurface_Patch::LerpVert( const idDrawVert& a, const idDrawVert& b, idDrawVert& out ) const { out.xyz[0] = 0.5f * ( a.xyz[0] + b.xyz[0] ); out.xyz[1] = 0.5f * ( a.xyz[1] + b.xyz[1] ); out.xyz[2] = 0.5f * ( a.xyz[2] + b.xyz[2] ); out.SetNormal( ( a.GetNormal() + b.GetNormal() ) * 0.5f ); out.SetTexCoord( ( a.GetTexCoord() + b.GetTexCoord() ) * 0.5f ); }
/* ==================== sdTraceSurface::RayIntersection ==================== */ bool sdTraceSurface::RayIntersection( const idVec3& start, const idVec3& end, idDrawVert& dv ) const { idVec3 p; int linkNum; unsigned int i; unsigned int maxTraceBins; int hashBin[3]; int separatorAxis; float faceDist, traceDist; float separatorDist; idVec3 normal, invNormal; // clear the result in case nothing is hit dv.Clear(); if ( numLinkBlocks == 0 ) { return false; } // get trace normal and normalize normal = end - start; faceDist = traceDist = normal.Normalize(); // inverse normal invNormal[0] = ( normal[0] != 0.0f ) ? 1.0f / normal[0] : 1.0f; invNormal[1] = ( normal[1] != 0.0f ) ? 1.0f / normal[1] : 1.0f; invNormal[2] = ( normal[2] != 0.0f ) ? 1.0f / normal[2] : 1.0f; // maximum number of hash bins the trace can go through maxTraceBins = 1 + idMath::Ftoi( traceDist * ( fabs( normal[0] ) * invBinSize[0] + fabs( normal[1] ) * invBinSize[1] + fabs( normal[2] ) * invBinSize[2] ) ); // get the first hash bin for the trace p = start - _bounds[0]; hashBin[0] = idMath::Ftoi( idMath::Floor( p[0] * invBinSize[0] ) ); hashBin[1] = idMath::Ftoi( idMath::Floor( p[1] * invBinSize[1] ) ); hashBin[2] = idMath::Ftoi( idMath::Floor( p[2] * invBinSize[2] ) ); separatorAxis = 0; separatorDist = 0.0f; bool insideHash = false; for ( i = 0; i < maxTraceBins; i++, GetNextHashBin( start, normal, hashBin, separatorAxis, separatorDist ) ) { // if this bin is outside the valid hash space if ( ( hashBin[0] | hashBin[1] | hashBin[2] ) & ~( binsPerAxis - 1 ) ) { if ( insideHash ) { break; // left valid hash space } continue; // not yet in valid hash space } insideHash = true; // if a triangle was hit before the plane that separates this hash bin from the previous hash bin if ( faceDist < traceDist && faceDist < separatorDist * invNormal[separatorAxis] ) { break; } const triLink_t *link; // test all the triangles in this hash bin for ( linkNum = binLinks[hashBin[0]][hashBin[1]][hashBin[2]]; linkNum != -1; linkNum = link->nextLink ) { link = &linkBlocks[ linkNum / MAX_LINKS_PER_BLOCK ][ linkNum % MAX_LINKS_PER_BLOCK ]; faceDist = TraceToMeshFace( link->faceNum, start, normal, faceDist, traceDist, dv ); } } // FIXME: SD_USE_DRAWVERT_SIZE_32 #if defined( SD_USE_DRAWVERT_SIZE_32 ) idVec3 n = dv.GetNormal(); n.Normalize(); dv.SetNormal( n ); #else dv._normal.Normalize(); #endif return ( faceDist < traceDist ); }
ID_INLINE float sdTraceSurface::TraceToMeshFace( const int faceNum, const idVec3& point, const idVec3 &normal, const float faceDist, const float traceDist, idDrawVert& dv ) const { int i; float dist; const idPlane* plane; idVec3 edge; float d, t; idVec3 dir[3]; float baseArea; float bary[3]; idVec3 testVert; plane = facePlanes + faceNum; // only test against planes facing the opposite direction as our trace normal d = -( plane->Normal() * normal ); if ( d <= TRIANGLE_NORMAL_EPSILON ) { return faceDist; } // get the distance to the plane dist = plane->Distance( point ); if ( dist >= traceDist * d ) { return faceDist; } const vertIndex_t* index = indexes + faceNum * 3; const idDrawVert &v0 = verts[ index[ 0 ] ]; const idDrawVert &v1 = verts[ index[ 1 ] ]; const idDrawVert &v2 = verts[ index[ 2 ] ]; // if normal is inside all edge planes, this face is hit dir[0] = v0.xyz - point; dir[1] = v1.xyz - point; edge = dir[0].Cross( dir[1] ); t = normal * edge; if ( t < -TRIANGLE_EDGE_EPSILON ) { return faceDist; } dir[2] = v2.xyz - point; edge = dir[1].Cross( dir[2] ); t = normal * edge; if ( t < -TRIANGLE_EDGE_EPSILON ) { return faceDist; } edge = dir[2].Cross( dir[0] ); t = normal * edge; if ( t < -TRIANGLE_EDGE_EPSILON ) { return faceDist; } // find the point of impact on the triangle plane dist /= d; testVert = point + dist * normal; // calculate barycentric coordinates of the impact point on the high poly triangle baseArea = 1.0f / idWinding::TriangleArea( v0.xyz, v1.xyz, v2.xyz ); bary[0] = idWinding::TriangleArea( testVert, v1.xyz, v2.xyz ) * baseArea; bary[1] = idWinding::TriangleArea( v0.xyz, testVert, v2.xyz ) * baseArea; bary[2] = idWinding::TriangleArea( v0.xyz, v1.xyz, testVert ) * baseArea; if ( bary[0] + bary[1] + bary[2] > 1.1f ) { return faceDist; } dv.xyz = testVert; // triangularly interpolate the texture coordinates, normals and colors to the sample point // FIXME: SD_USE_DRAWVERT_SIZE_32 for ( i = 0; i < 2; i++ ) { dv._st[ i ] = bary[0] * v0._st[i] + bary[1] * v1._st[i] + bary[2] * v2._st[i]; } #if defined( SD_USE_DRAWVERT_SIZE_32 ) idVec3 n; idVec3 n0 = v0.GetNormal(); idVec3 n1 = v1.GetNormal(); idVec3 n2 = v2.GetNormal(); for ( i = 0; i < 3; i++ ) { n[ i ] = bary[0] * n0[i] + bary[1] * n1[i] + bary[2] * n2[i]; } n.Normalize(); dv.SetNormal( n ); idVec3 tgnt; idVec3 tgnt0 = v0.GetTangent(); idVec3 tgnt1 = v1.GetTangent(); idVec3 tgnt2 = v2.GetTangent(); for ( i = 0; i < 3; i++ ) { tgnt[ i ] = bary[0] * tgnt0[i] + bary[1] * tgnt1[i] + bary[2] * tgnt2[i]; } tgnt.Normalize(); dv.SetTangent( tgnt ); float s0 = v0.GetBiTangentSign(); float s1 = v1.GetBiTangentSign(); float s2 = v2.GetBiTangentSign(); dv.SetBiTangentSign( bary[0] * s0 + bary[1] * s1 + bary[2] * s2 ); #else for ( i = 0; i < 3; i++ ) { dv._normal[ i ] = bary[0] * v0._normal[i] + bary[1] * v1._normal[i] + bary[2] * v2._normal[i]; } dv._normal.Normalize(); for ( i = 0; i < 4; i++ ) { dv._tangent[i] = bary[0] * v0._tangent[i] + bary[1] * v1._tangent[i] + bary[2] * v2._tangent[i]; } #endif for ( i = 0; i < 4; i++ ) { dv.color[i] = idMath::ClampInt( 0, 255, idMath::FtoiFast( bary[0] * v0.color[i] + bary[1] * v1.color[i] + bary[2] * v2.color[i] ) ); } return dist; }