游戏中鼠标拾取方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/himilong/article/details/51740498

在游戏中,鼠标拾取是不可或缺的。

一. 使用 D3DIntersect()选中模型Mesh

在D3D sample 中有一种方法,对鼠标轨迹线进行MVP 的逆变换,得到线在模型空间位置,然后使用 函数 D3DIntersect , 

和物体的位置进行比较,进而判断出是否是拾取到某个mesh

1. 获取当前的转换矩阵,包括 projection, view, 和 world

void detect_picking()
{
    // get the current transform matrices
    D3DXMATRIX matProjection, matView, matWorld;
    d3ddev->GetTransform(D3DTS_PROJECTION, &matProjection);
    d3ddev->GetTransform(D3DTS_VIEW, &matView);
    d3ddev->GetTransform(D3DTS_WORLD, &matWorld);
}

2. 把鼠标的坐标转换为鼠标的角度

使用一条模型空间的线表示鼠标的移动拾取, 起点为原点,线为空间方向向量


在视锥内计算x, y 方向的角度


GetCursorPos(&MousePos);

float xAngle = (((2.0f * MousePos.x) / SCREEN_WIDTH) - 1.0f) / matProjection(0, 0);
float yAngle = (((-2.0f * MousePos.y) / SCREEN_HEIGHT) + 1.0f) / matProjection(1, 1);

3. 找到逆转换矩阵

void detect_picking(){    // get the current transform matrices    D3DXMATRIX matProjection, matView, matWorld, matInverse;    d3ddev->GetTransform(D3DTS_PROJECTION, &matProjection);    d3ddev->GetTransform(D3DTS_VIEW, &matView);    d3ddev->GetTransform(D3DTS_WORLD, &matWorld);    // use the mouse coordinates to get the mouse angle    GetCursorPos(&MousePos);    float xAngle = (((2.0f * MousePos.x) / SCREEN_WIDTH) - 1.0f) / matProjection(0, 0);    float yAngle = (((-2.0f * MousePos.y) / SCREEN_HEIGHT) + 1.0f) / matProjection(1, 1);    D3DXVECTOR3 origin, direction;    origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);    direction = D3DXVECTOR3(xAngle, yAngle, 1.0f);    // find the inverse matrix    D3DXMatrixInverse(&matInverse, NULL, &(matWorld * matView));}
}

4. 使用逆矩阵把鼠标的方向位置转化为模型空间位置

void detect_picking()
{
    // get the current transform matrices
    D3DXMATRIX matProjection, matView, matWorld, matInverse;
    d3ddev->GetTransform(D3DTS_PROJECTION, &matProjection);
    d3ddev->GetTransform(D3DTS_VIEW, &matView);
    d3ddev->GetTransform(D3DTS_WORLD, &matWorld);

    // use the mouse coordinates to get the mouse angle
    GetCursorPos(&MousePos);
    float xAngle = (((2.0f * MousePos.x) / SCREEN_WIDTH) - 1.0f) / matProjection(0, 0);
    float yAngle = (((-2.0f * MousePos.y) / SCREEN_HEIGHT) + 1.0f) / matProjection(1, 1);

    D3DXVECTOR3 origin, direction;
    origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    direction = D3DXVECTOR3(xAngle, yAngle, 1.0f);

    // find the inverse matrix
    D3DXMatrixInverse(&matInverse, NULL, &(matWorld * matView));

    // convert origin and direction into model space
    D3DXVec3TransformCoord(&origin, &origin, &matInverse);
    D3DXVec3TransformNormal(&direction, &direction, &matInverse);
    D3DXVec3Normalize(&direction, &direction);
}

最后一步,判断交汇

HRESULT D3DXIntersect(LPD3DXBASEMESH pMesh,    // pointer to the mesh to check
                      D3DXVECTOR3* pRayPos,    // pointer to the origin
                      D3DXVECTOR3* pRayDir,    // pointer to the direction
                      BOOL* pHit,    // a boolean telling if the mesh was selected
                      DWORD* pFaceIndex,    // tells which selected triangle was closest
                      FLOAT* pU,    // tells the U coordinate of that triangle
                      FLOAT* pV,    // tells the V coordinate of that triangle
                      FLOAT* pDist,    // a value telling how far from the camera the face is
                      LPD3DXBUFFER* ppAllHits,    // a buffer telling all picked faces
                      DWORD* pCountOfHits);    // tells how many faces were picked

整个函数的完整版即如下:

void detect_picking()
{
    // get the current transform matrices
    D3DXMATRIX matProjection, matView, matWorld, matInverse;
    d3ddev->GetTransform(D3DTS_PROJECTION, &matProjection);
    d3ddev->GetTransform(D3DTS_VIEW, &matView);
    d3ddev->GetTransform(D3DTS_WORLD, &matWorld);

    // use the mouse coordinates to get the mouse angle
    GetCursorPos(&MousePos);
    float xAngle = (((2.0f * MousePos.x) / SCREEN_WIDTH) - 1.0f) / matProjection(0, 0);
    float yAngle = (((-2.0f * MousePos.y) / SCREEN_HEIGHT) + 1.0f) / matProjection(1, 1);

    D3DXVECTOR3 origin, direction;
    origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    direction = D3DXVECTOR3(xAngle, yAngle, 1.0f);

    // find the inverse matrix
    D3DXMatrixInverse(&matInverse, NULL, &(matWorld * matView));

    // convert origin and direction into model space
    D3DXVec3TransformCoord(&origin, &origin, &matInverse);
    D3DXVec3TransformNormal(&direction, &direction, &matInverse);
    D3DXVec3Normalize(&direction, &direction);

    // detect picking
    BOOL hit;
    D3DXIntersect(meshTeapot, &origin, &direction, &hit, NULL, NULL, NULL, NULL, NULL, NULL);
    if(hit)
        d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
    else
        d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);
}



二. 根据RTT制作查询表选中游戏中实体模型

实际游戏中,只需要选中某个实体即可,不必要精细到某个mesh,对活动可拾取目标渲染到一张屏幕大小的RTT,然后对该RTT 生成查询信息。

		const ITexture* tex = pRenderSys->getRenderTargetTexture(renderTargetID);
		PixelBox dstBox(w,h,1,PF_A8R8G8B8, mpData);
		//tex->blitToMemory(Box(0,0,0,w,h,1), dstBox);
		if( !tex->blitToMemory(Box(0,0,0,w,h,1), dstBox) )
		{
			memset(mpData, 0, sizeof(uint) * w * h );
		}

鼠标移动到可拾取地方,即会有判断出拾取到某个可拾取物体, hittest 返回的是鼠标当前的renderable,即活动实体。

	int Selector::rectHitTest( const Vector2& leftTop, const Vector2& rightBottom, std::vector<Result>& outList )
	{
		if(leftTop.x < 0 || leftTop.x > 1.0f || leftTop.y < 0 || leftTop.y > 1.0f)
			return 0;

		if(rightBottom.x < 0 || rightBottom.x > 1.0f || rightBottom.y < 0 || rightBottom.y > 1.0f)
			return 0;

		int w = sResoluctions[mLevel].x;
		int h = sResoluctions[mLevel].y;

		int lx = (int)(leftTop.x * w);
		int ty = (int)(leftTop.y * h);
		int rx = (int)(rightBottom.x * w);
		int by = (int)(rightBottom.y * h);

		lx = Clamp(lx, 0, w - 1);
		ty = Clamp(ty, 0, h - 1);
		rx = Clamp(rx, 0, w - 1);
		by = Clamp(by, 0, h - 1);

		std::set<uint> selSet;
		for(int i = lx; i < rx; ++i)
		{
			for(int j = ty; j < by; ++j)
			{
				uint v = mpData[w * j + i];
				if (v != 0 && selSet.find(v) == selSet.end())
				{
					Result rt;
					fillResult(v, rt);
					outList.push_back(rt);
					selSet.insert(v);
				}
			}
		}

		return (int)outList.size();
	}


Reference:

http://www.directxtutorial.com/Lesson.aspx?lessonid=9-8-4



阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页