// *************************************************************************** void CVisualCollisionMesh::receiveShadowMap(const NLMISC::CMatrix &instanceMatrix, const CShadowContext &shadowContext) { // empty mesh => no op if(_Vertices.empty()) return; // The VertexBuffer RefPtr has been released? quit if(_VertexBuffer == NULL) return; // **** Select triangles to be rendered with quadGrid // select with quadGrid local in mesh CAABBox localBB; localBB= CAABBox::transformAABBox(instanceMatrix.inverted(), shadowContext.ShadowWorldBB); static std::vector<uint16> triInQuadGrid; uint numTrisInQuadGrid= _QuadGrid.select(localBB, triInQuadGrid); // no intersection at all? quit if(numTrisInQuadGrid==0) return; // **** prepare more precise Clip with shadow pyramid // enlarge temp flag array static std::vector<uint8> vertexFlags; if(vertexFlags.size()<_Vertices.size()) vertexFlags.resize(_Vertices.size()); // reset all to 0 memset(&vertexFlags[0], 0, _Vertices.size()*sizeof(uint8)); // Compute the "LocalToInstance" shadow Clip Volume static std::vector<CPlane> localClipPlanes; /* We want to apply to plane this matrix: IM-1 * MCasterPos, where IM=instanceMatrix and MCasterPos= matrix translation of "shadowContext.CasterPos" BUT, since to transform a plane, we must do plane * M-1, then compute this matrix: localMat= MCasterPos-1 * IM */ CMatrix localMat; localMat.setPos(-shadowContext.CasterPos); localMat*= instanceMatrix; // Allow max bits of planes clip. localClipPlanes.resize(min((uint)shadowContext.ShadowMap->LocalClipPlanes.size(), (uint)NL3D_VCM_SHADOW_NUM_CLIP_PLANE)); // Transform into Mesh local space for(uint i=0;i<localClipPlanes.size();i++) { localClipPlanes[i]= shadowContext.ShadowMap->LocalClipPlanes[i] * localMat; } // **** Clip and fill the triangles uint currentTriIdx= 0; // enlarge the index buffer as max of triangles possibly intersected shadowContext.IndexBuffer.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT); if(shadowContext.IndexBuffer.getNumIndexes()<numTrisInQuadGrid*3) shadowContext.IndexBuffer.setNumIndexes(numTrisInQuadGrid*3); // Start to clip and fill { CIndexBufferReadWrite iba; shadowContext.IndexBuffer.lock(iba); if (iba.getFormat() == CIndexBuffer::Indices32) { uint32 *ibPtr= (uint32 *) iba.getPtr(); // for all triangles selected with the quadgrid for(uint triq=0; triq<numTrisInQuadGrid;triq++) { uint triId[3]; triId[0]= _Triangles[uint(triInQuadGrid[triq])*3+0]; triId[1]= _Triangles[uint(triInQuadGrid[triq])*3+1]; triId[2]= _Triangles[uint(triInQuadGrid[triq])*3+2]; uint triFlag= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK; // for all vertices, clip them for(uint i=0;i<3;i++) { uint vid= triId[i]; uint vf= vertexFlags[vid]; // if this vertex is still not computed if(!vf) { // For all planes of the Clip Volume, clip this vertex. for(uint j=0;j<localClipPlanes.size();j++) { // out if in front bool out= localClipPlanes[j]*_Vertices[vid] > 0; vf|= ((uint)out)<<j; } // add the bit flag to say "computed". vf|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT; // store vertexFlags[vid]= vf; } // And all vertex bits. triFlag&= vf; } // if triangle not clipped, add the triangle if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 ) { // Add the 3 index to the index buffer. ibPtr[currentTriIdx++]= triId[0]; ibPtr[currentTriIdx++]= triId[1]; ibPtr[currentTriIdx++]= triId[2]; } } } else { nlassert(iba.getFormat() == CIndexBuffer::Indices16); uint16 *ibPtr= (uint16 *) iba.getPtr(); // for all triangles selected with the quadgrid for(uint triq=0; triq<numTrisInQuadGrid;triq++) { uint triId[3]; triId[0]= _Triangles[uint(triInQuadGrid[triq])*3+0]; triId[1]= _Triangles[uint(triInQuadGrid[triq])*3+1]; triId[2]= _Triangles[uint(triInQuadGrid[triq])*3+2]; uint triFlag= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK; // for all vertices, clip them for(uint i=0;i<3;i++) { uint vid= triId[i]; uint vf= vertexFlags[vid]; // if this vertex is still not computed if(!vf) { // For all planes of the Clip Volume, clip this vertex. for(uint j=0;j<localClipPlanes.size();j++) { // out if in front bool out= localClipPlanes[j]*_Vertices[vid] > 0; vf|= ((uint)out)<<j; } // add the bit flag to say "computed". vf|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT; // store vertexFlags[vid]= vf; } // And all vertex bits. triFlag&= vf; } // if triangle not clipped, add the triangle // if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 ) if (triFlag == 0) // previous line not useful due to init { // Add the 3 index to the index buffer. ibPtr[currentTriIdx++]= (uint16) triId[0]; ibPtr[currentTriIdx++]= (uint16) triId[1]; ibPtr[currentTriIdx++]= (uint16) triId[2]; } } } } // **** Render // if some triangle to render if(currentTriIdx) { IDriver *drv= shadowContext.Driver; // setup the collision instance matrix drv->setupModelMatrix(instanceMatrix); // update the material projection matrix, cause matrix changed shadowContext.ShadowMapProjector.applyToMaterial(instanceMatrix, shadowContext.ShadowMaterial); // render drv->activeVertexBuffer(*_VertexBuffer); drv->activeIndexBuffer(shadowContext.IndexBuffer); drv->renderTriangles(shadowContext.ShadowMaterial, 0, currentTriIdx/3); // TestYoyo. Show in Red triangles selected /*if(TESTYOYO_VCM_RedShadow) { static CMaterial tam; tam.initUnlit(); tam.setColor(CRGBA(255,0,0,128)); tam.setZFunc(CMaterial::always); tam.setZWrite(false); tam.setBlend(true); tam.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha); tam.setDoubleSided(true); drv->renderTriangles(tam, 0, currentTriIdx/3); }*/ } }