DirectX9 SDK Samples(13) Pick

本例子是关于用鼠标选取物体的。

1OnCreateDevice

    // Load the mesh with D3DX and get back a ID3DXMesh*.  For this
    // sample we'll ignore the X file's embedded materials since we know 
    // exactly the model we're loading.  See the mesh samples such as
    // "OptimizedMesh" for a more generic mesh loading example.
    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, TEXT( "scanner\\scannerarm.x" ) ) );

    V_RETURN( g_Mesh.Create( pd3dDevice, str ) );
    V_RETURN( g_Mesh.SetFVF( pd3dDevice, D3DVERTEX::FVF ) );
载入网格,设置FVF,这里的变量可以直接用FVF描述,可以不用描述数组。

    // Create the vertex buffer
    if( FAILED( pd3dDevice->CreateVertexBuffer( 3 * MAX_INTERSECTIONS * sizeof( D3DVERTEX ),
                                                D3DUSAGE_WRITEONLY, D3DVERTEX::FVF,
                                                D3DPOOL_MANAGED, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }
创建一个三角形的顶点缓冲区,用途就是显示选中的第一个三角形。

这一次跟效果文件没有什么关系,所以就不列出代码了。


2在OnRenderFrame函数中,调用Pick(),计算出选到的三角形。

    // Get the Pick ray from the mouse position
    if( GetCapture() )
    {
        const D3DXMATRIX* pmatProj = g_Camera.GetProjMatrix();

        POINT ptCursor;
        GetCursorPos( &ptCursor );
        ScreenToClient( DXUTGetHWND(), &ptCursor );
用GetCapture函数得到鼠标所在窗口的句柄,然后用GetCursorPos和ScreenToClient得到鼠标在窗口中的坐标。

        // Compute the vector of the Pick ray in screen space
        D3DXVECTOR3 v;
        v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
        v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;
        v.z = 1.0f;
计算出点在投影窗口中的坐标,并加上z坐标,就成了拾取向量。

        // Get the inverse view matrix
        const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
        const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
        D3DXMATRIX mWorldView = matWorld * matView;
        D3DXMATRIX m;
        D3DXMatrixInverse( &m, NULL, &mWorldView );

        // Transform the screen space Pick ray into 3D space
        vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
        vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
        vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
        vPickRayOrig.x = m._41;
        vPickRayOrig.y = m._42;
        vPickRayOrig.z = m._43;
再将拾取向量和拾取向量原点转换到3D空间去。这里的算法貌似跟龙书第一版的有一点不同,不过能用就好了。

    // Get the Picked triangle
    if( GetCapture() )
    {
        LPD3DXMESH pMesh;

        g_Mesh.GetMesh()->CloneMeshFVF( D3DXMESH_MANAGED,
                                        g_Mesh.GetMesh()->GetFVF(), pD3Device, &pMesh );
接下来开始获取拾取到的三角形,首先将网格拷贝一份放在内存中,便于处理。
        LPDIRECT3DVERTEXBUFFER9 pVB;
        LPDIRECT3DINDEXBUFFER9 pIB;

        pMesh->GetVertexBuffer( &pVB );
        pMesh->GetIndexBuffer( &pIB );

        WORD* pIndices;
        D3DVERTEX* pVertices;

        pIB->Lock( 0, 0, ( void** )&pIndices, 0 );
        pVB->Lock( 0, 0, ( void** )&pVertices, 0 );
得到VB和IB,并锁定。
        if( g_bUseD3DXIntersect )
        {
            // When calling D3DXIntersect, one can get just the closest intersection and not
            // need to work with a D3DXBUFFER.  Or, to get all intersections between the ray and 
            // the mesh, one can use a D3DXBUFFER to receive all intersections.  We show both
            // methods.
            if( !g_bAllHits )
            {
                // Collect only the closest intersection
                BOOL bHit;
                DWORD dwFace;
                FLOAT fBary1, fBary2, fDist;
                D3DXIntersect( pMesh, &vPickRayOrig, &vPickRayDir, &bHit, &dwFace, &fBary1, &fBary2, &fDist,
                               NULL, NULL );
                if( bHit )
                {
                    g_dwNumIntersections = 1;
                    g_IntersectionArray[0].dwFace = dwFace;
                    g_IntersectionArray[0].fBary1 = fBary1;
                    g_IntersectionArray[0].fBary2 = fBary2;
                    g_IntersectionArray[0].fDist = fDist;
                }
                else
                {
                    g_dwNumIntersections = 0;
                }
            }
第一段检测拾取的代码,注意这里一共四种情况:用不用D3DXIntersect和检测一个还是检测所有。

            else
            {
                // Collect all intersections
                BOOL bHit;
                LPD3DXBUFFER pBuffer = NULL;
                D3DXINTERSECTINFO* pIntersectInfoArray;
                if( FAILED( hr = D3DXIntersect( pMesh, &vPickRayOrig, &vPickRayDir, &bHit, NULL, NULL, NULL, NULL,
                                                &pBuffer, &g_dwNumIntersections ) ) )
                {
                    SAFE_RELEASE( pMesh );
                    SAFE_RELEASE( pVB );
                    SAFE_RELEASE( pIB );

                    return hr;
                }
                if( g_dwNumIntersections > 0 )
                {
                    pIntersectInfoArray = ( D3DXINTERSECTINFO* )pBuffer->GetBufferPointer();
                    if( g_dwNumIntersections > MAX_INTERSECTIONS )
                        g_dwNumIntersections = MAX_INTERSECTIONS;
                    for( DWORD iIntersection = 0; iIntersection < g_dwNumIntersections; iIntersection++ )
                    {
                        g_IntersectionArray[iIntersection].dwFace = pIntersectInfoArray[iIntersection].FaceIndex;
                        g_IntersectionArray[iIntersection].fBary1 = pIntersectInfoArray[iIntersection].U;
                        g_IntersectionArray[iIntersection].fBary2 = pIntersectInfoArray[iIntersection].V;
                        g_IntersectionArray[iIntersection].fDist = pIntersectInfoArray[iIntersection].Dist;
                    }
                }
                SAFE_RELEASE( pBuffer );
            }

        }
第二段。这一次使用D3DXIntersect检测出所有拾取到的三角形。

        else
        {
            // Not using D3DX
            DWORD dwNumFaces = g_Mesh.GetMesh()->GetNumFaces();
            FLOAT fBary1, fBary2;
            FLOAT fDist;
            for( DWORD i = 0; i < dwNumFaces; i++ )
            {
                D3DXVECTOR3 v0 = pVertices[pIndices[3 * i + 0]].p;
                D3DXVECTOR3 v1 = pVertices[pIndices[3 * i + 1]].p;
                D3DXVECTOR3 v2 = pVertices[pIndices[3 * i + 2]].p;

                // Check if the Pick ray passes through this point
                if( IntersectTriangle( vPickRayOrig, vPickRayDir, v0, v1, v2,
                                       &fDist, &fBary1, &fBary2 ) )
                {
                    if( g_bAllHits || g_dwNumIntersections == 0 || fDist < g_IntersectionArray[0].fDist )
                    {
                        if( !g_bAllHits )
                            g_dwNumIntersections = 0;
                        g_IntersectionArray[g_dwNumIntersections].dwFace = i;
                        g_IntersectionArray[g_dwNumIntersections].fBary1 = fBary1;
                        g_IntersectionArray[g_dwNumIntersections].fBary2 = fBary2;
                        g_IntersectionArray[g_dwNumIntersections].fDist = fDist;
                        g_dwNumIntersections++;
                        if( g_dwNumIntersections == MAX_INTERSECTIONS )
                            break;
                    }
                }
            }
        }
不使用提供的API,自己进行检测,逐个三角形进行检测。包括了是否输出所有拾取三角形两种情况。注意IntersectTriangle是自己编写的函数,检测是否射线是否和三角形相交,代码比较简单,就不单独列出了。

        if( g_dwNumIntersections > 0 )
        {
            D3DVERTEX* v;
            D3DVERTEX* vThisTri;
            WORD* iThisTri;
            D3DVERTEX v1, v2, v3;
            INTERSECTION* pIntersection;

            g_pVB->Lock( 0, 0, ( void** )&v, 0 );

            for( DWORD iIntersection = 0; iIntersection < g_dwNumIntersections; iIntersection++ )
            {
                pIntersection = &g_IntersectionArray[iIntersection];

                vThisTri = &v[iIntersection * 3];
                iThisTri = &pIndices[3 * pIntersection->dwFace];
                // get vertices hit
                vThisTri[0] = pVertices[iThisTri[0]];
                vThisTri[1] = pVertices[iThisTri[1]];
                vThisTri[2] = pVertices[iThisTri[2]];

                // If all you want is the vertices hit, then you are done.  In this sample, we
                // want to show how to infer texture coordinates as well, using the BaryCentric
                // coordinates supplied by D3DXIntersect
                FLOAT dtu1 = vThisTri[1].tu - vThisTri[0].tu;
                FLOAT dtu2 = vThisTri[2].tu - vThisTri[0].tu;
                FLOAT dtv1 = vThisTri[1].tv - vThisTri[0].tv;
                FLOAT dtv2 = vThisTri[2].tv - vThisTri[0].tv;
                pIntersection->tu = vThisTri[0].tu + pIntersection->fBary1 * dtu1 + pIntersection->fBary2 * dtu2;
                pIntersection->tv = vThisTri[0].tv + pIntersection->fBary1 * dtv1 + pIntersection->fBary2 * dtv2;
            }

            g_pVB->Unlock();
        }
接下来对检测到的三角形进行处理。


3回到渲染函数,此时已经得到拾取三角形的信息。

        V( g_pEffect->Begin( &uPasses, 0 ) );

        // Set render mode to lit, solid triangles
        pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
        pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );

        // If a triangle is Picked, draw it
        if( g_dwNumIntersections > 0 )
        {
            for( UINT uPass = 0; uPass < uPasses; ++uPass )
            {
                V( g_pEffect->BeginPass( uPass ) );

                // Draw the Picked triangle
                pd3dDevice->SetFVF( D3DVERTEX::FVF );
                pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( D3DVERTEX ) );
                pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, g_dwNumIntersections );

                V( g_pEffect->EndPass() );
            }

            // Set render mode to unlit, wireframe triangles
            pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
            pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
        }

        V( g_pEffect->End() );

        // Render the mesh
        V( g_Mesh.Render( g_pEffect ) );
注意这里先将FILLMODE设置成SOLID,假如有拾取三角形,则修改成WIREFRAME。还可以看到这里的效果框架只应用到了三角形上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值