关于向量和点变换
3D数学中,向量可以表示点也可以表示向量,当表示点时候进行矩阵变换要用coord需要考虑平移w=1,当表示向量时候进行矩阵变换用normal不需要考虑平移w=0,用齐次向量常数是0表示向量不包括平移,非齐次向量常数是1表示的是空间点包括平移。
关于投影变换
矩阵变换:将屏幕空间捕获到的位置,转换为游戏世界坐标系中的位置
世界坐标系->观察坐标系->投影坐标系->屏幕坐标系。
对于投影坐标系有了更深入的理解:
(1)投影坐标系因为透视投影面被放置到了正轴,避免了处理负数,同时避免了透视投影的上下左右颠倒。(2)
矩阵没有缩放但是设置了m34=1,使得点变换时候会进行w=z变为w=1的除法,使得经过透视投影,物体点被放置到了x属于[-1,1],y属于[-1,1],z属于[0,1](dx中,opengl中是z为[-1,1])。
(3) 3D透视投影矩阵的Z轴是对1/z进行插值,因为要在1/z后插值到[0,1]中,所以得到的常数是矩阵中的z位置,1/z是矩阵中的平移位置。
2D正交投影矩阵只要对z插值在[0,1]中就可以了,常数矩阵中平移位置,z插值是矩阵中的z位置。
屏幕坐标系变换就是将x的[-1,1]转换为[0,width],将y的[1,-1]转换为[0,height]位置,点的z值用于光栅化深度测试,和模板测试使用。
3D数学中,向量可以表示点也可以表示向量,当表示点时候进行矩阵变换要用coord需要考虑平移w=1,当表示向量时候进行矩阵变换用normal不需要考虑平移w=0.
关于世界坐标变换
世界坐标转换SetTransform(D3DTS_WORLD, &M)是将物体坐标系转换到世界坐标系需要进行的转换,基于当前物体坐标系需要进行的变换
就可以了,连续变换世界坐标也不会改变或者重置世界坐标系原点,只是对当前需要绘制的物体产生影响。
因为建立的包围盒是变换以后的数据,所以绘制的时候又会根据世界矩阵变换一遍,所以设置世界矩阵变换为单位矩阵不变换绘制包围盒即可。
开发中遇到的各种问题
// 熟悉各种类型的分类讨论。遇到的问题不熟悉的可以通过对比分析找到一些原因。
// 超过左边和右边
// 有的轴在物体内部
// 要根据射线原点开始大于最大缩放后还是大于,或者开始是小于包围盒最大点最大缩放后还是小于。
// 过程中还发现了,根据最迟到达的射线,其实可以求出最长的变换长度的,这样就可以把不符合条件转换为符合条件的已知变换。
// 网络中搜索了下其实大家用射线和包围盒的检测,大多数使用D3DXIntersectTri的方式进行包围盒检测。
1.把物体放置在了原点,导致每次点击都会命中。
2.记得固定管道渲染是4次坐标系变换过程,世界,视图,投影,屏幕,转换到世界空间需要进行屏幕,投影,视图就可以了。
3.调试算法问题:通过不符合的现象进行调试,分析出原因
4.包围盒的颜色没有,那么是因为启动了光照的原因且没有设置材质,所以禁止光照Device->SetRenderState(D3DRS_LIGHTING, FALSE);。
5.AABB的浮点数有问题,FLT_MIN和-FLT_MAX区别,所以建立的包围盒大小是有问题的。
6.世界坐标转换SetTransform(D3DTS_WORLD, &M)是将物体坐标系转换到世界坐标系需要进行的转换,基于当前物体坐标系需要进行的变换
// 就可以了,连续变换世界坐标也不会改变或者重置世界坐标系原点,只是对当前需要绘制的物体产生影响。
// 因为建立的包围盒是变换以后的数据,所以绘制的时候又会根据世界矩阵变换一遍,所以设置世界矩阵变换为单位矩阵不变换绘制包围盒即可。
正交投影和透视投影的深入理解
2D中的正交投影到屏幕坐标系的理解,和2D中的对象拾取技术和3D中的区别研究
设置屏幕坐标系大小定义,多视图,可以实现屏幕分割
D3DVIEWPORT9 viewport = {0, 0, width, height, 0.0f, 1.0f} ;
(*device)->SetViewport(&viewport);
设备规范坐标系到屏幕坐标系D3D会自动进行变换,3D透视投影中通过:
W/2, 0, 0, 0
0, -H/2, 0,0
0, 0, MaxZ-MinZ, 0
X0+W/2, Y0+H/2, MinZ,1
上述的参数含义就是D3DVIEWPORT9中定义的参数含义。
3D透视投影矩阵的Z轴是对1/z进行插值,因为要在1/z后插值到[0,1]中,所以得到的常数是矩阵中的z位置,1/z是矩阵中的平移位置。
2D正交投影矩阵只要对z插值在[0,1]中就可以了,常数矩阵中平移位置,z插值是矩阵中的z位置。
3D透视投影的设备规范坐标系由透视投影矩阵和顶点除法变换得到,x属于[-1,1],y属于[-1,1],z属于[0,1](但是OGL中z属于[-1,-1]
可见深度缓存中存储的深度也就是z在设备规范坐标系中的值, 但程序员也可以在世界坐标系和视图坐标系中对物体按z值排序渲染。
2D正交投影的设备规范坐标系直接由正交投影变换得到,不会再进行除法,且前面的世界坐标系视图坐标系都是一样的,只不过正交
投影去掉了进大远小的效果(z值不能影响了),直接经过正交投影变换如果x在[-1,1],y在[-1,1]规范坐标内的可以渲染,在外的不会
渲染,z值也被正交投影放置到了[0,1]中放入深度缓存中。
一般设置的正交投影设置下,也就是不缩放,那么物体在世界坐标系中的点和屏幕坐标系中的像素是对应的。
2D和3D的区别仅仅是投影那一刻,他们在世界坐标系中都是一样的世界坐标空间的,摄像机空间也是一样的空间,
设备规范化坐标系x属于[-1,1]y属于[-1,1]z属于[0,1]OGLz属于[-1,1],屏幕坐标系也是一样的。
一般2D中位置用像素和格子来衡量就可以了,3D中位置需要虚拟空间尺寸和格子来衡量。
其实D3D,OGL中的透视投影和正交投影都是没有丢掉z值的,知道z值情况下都是可逆的
但是在不知道z值情况下,比如设置设备规范坐标系中的z值是1或者0.5或者0.0:
那么在透视投影情况下计算得到的摄像机坐标空间下的点会不一样,但是单位射线向量都是一样的,
因为透视投影下摄像机空间下相同视锥夹角的点,是会被投影到相同的设备规范坐标系的(因为除以w=z)。
而在正交投影情况下得到的摄像机空间下的点会不一样,且单位化法向量也不会一样,
只有知道真正的z值计算得到的法向量才是正确的(因为知道z值情况下D3DXMatrixOrthoLH正交投影也是可逆的)
因为正交投影下摄像机空间下相同的视锥夹角的点,会被投影到不同的设备规范坐标系,因为没有除以w=z,正交投影只和x,y本身
相关,和z不相关。在不同设备坐标系z下,所以得到摄像机坐标系中的变换射线向量是不成比例的,导致拾取算法的射线方向计算错误。
射线方向计算错误导致z轴上是最远的相交,但此时计算x,y轴并没有超出包围盒区域;而透视投影钟z轴也是最远的相交,
而此时x,y轴是超出了包围盒的。所以3D拾取算法不能用于2D中,2D需要一个忽略掉z值的鼠标点击点是否在包围盒矩形中就可以了。
AABB.h
#ifndef _AABB_H_
#define _AABB_H_
#include "d3dUtility.h"
// AABB的顶点用于绘制
struct AABBVertex
{
AABBVertex(){}
AABBVertex(float x, float y, float z, D3DCOLOR color)
{
_x = x; _y = y; _z = z;
_color = color;
}
float _x, _y, _z;
D3DCOLOR _color;
static const DWORD FVF;
};
class AABB
{
public:
AABB();
~AABB();
// 1.自己开发的射线和包围盒检测的方法RayIntersetBoxWithoutDirLength
// 射线与AABB的检测,该射线不用考虑射线方向向量的大小,假设方向向量是无限长的,这个判断在用鼠标在3D世界中拾取常用到
// 避免参数方程,代入球面方程判断解,只能用球形包围盒的模式
inline bool CheckNotArrive(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta,int nWhich,float fMaxScale);
bool CheckOneAxis(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta, int nWhich);
bool RayIntersetBoxWithoutDirLength(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta);
// 2D中的选择检测
bool SelectObject2D(const D3DXVECTOR3 worldClickPos);
// 2.变换后使用RayIntersect算法
D3DXVECTOR3 GetRayDir(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta);
float RayIntersect(
const D3DXVECTOR3 &rayOrg, // orgin of the ray
const D3DXVECTOR3 &rayDelta, // length and direction of the ray
D3DXVECTOR3 *returnNormal // optionally, the normal is returned
) const;
// 3.标准的使用D3DXIntersectTri,用射线和AABB的六个面进行检测,射线是不用求出长度的
bool IsIntersect(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta);
// 一些公共的函数
void CalculateBoudingBox(D3DXVECTOR3 *pVec, int n);
void setToTransformedBox(const AABB &box, const D3DXMATRIX &m);
void Reset();
bool IsEmpty();
// 建立包围盒模型并渲染的
void Setup(IDirect3DDevice9 *pDevice);
void Draw( IDirect3DDevice9 *pDevice );
private:
D3DXVECTOR3 min;
D3DXVECTOR3 max;
bool m_bEmpty;
bool m_bSetup;
IDirect3DVertexBuffer9* VB;
IDirect3DIndexBuffer9* IB;
};
#endif
AABB.cpp
#include "AABB.h"
const DWORD AABBVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
AABB::AABB()
{
VB = NULL;
IB = NULL;
Reset();
}
AABB::~AABB()
{
}
void AABB::Reset()
{
min.x = min.y = min.z = FLT_MAX;
// FLT_MIN 是最小正负数,但是我们需要的是最小的负数,所以这里如果用FLT_MIN会导致计算不正确
max.x = max.y = max.z = -FLT_MAX;
m_bEmpty = true;
m_bSetup = false;
d3d::Release<IDirect3DVertexBuffer9*>(VB);
d3d::Release<IDirect3DIndexBuffer9*>(IB);
}
bool AABB::IsEmpty()
{
return m_bEmpty;
}
void AABB::Setup(IDirect3DDevice9 *pDevice)
{
if( pDevice == NULL || m_bEmpty )
{
m_bSetup = false;
return;
}
pDevice->CreateVertexBuffer(
8 * sizeof(AABBVertex),
D3DUSAGE_WRITEONLY,
AABBVertex::FVF,
D3DPOOL_MANAGED,
&VB,
0);
pDevice->CreateIndexBuffer(
36 * sizeof(WORD), // 索引缓存的字节数,个数是三角形的个数*3,索引缓存个数还是没有减少的。
D3DUSAGE_WRITEONLY, // 使用类型为只读类型,获取指针然后写入数据,数据将会批量提交
D3DFMT_INDEX16, // D3DFMT_INDEX16是16bit,D3DFMT_INDEX32是32bit
D3DPOOL_MANAGED, // D3DPOOL类型,创建在SM中,需要时候拷贝到AGP内存/显存中
&IB,
0);//保留
AABBVertex *vertices;
D3DCOLOR blueColor = D3DCOLOR_XRGB(0,0,255);
VB->Lock(0,0,(void**)&vertices, 0/*D3DLOCK_DISCARD*/);
vertices[0] = AABBVertex(min.x, min.y, min.z, blueColor);
vertices[1] = AABBVertex(min.x, max.y,min.z, blueColor);
vertices[2] = AABBVertex( max.x, max.y, min.z, blueColor);
vertices[3] = AABBVertex( max.x, min.y, min.z, blueColor);
vertices[4] = AABBVertex(min.x, min.y, max.z, blueColor);
vertices[5] = AABBVertex(min.x, max.y, max.z, blueColor);
vertices[6] = AABBVertex(max.x, max.y, max.z, blueColor);
vertices[7] = AABBVertex( max.x, min.y, max.z, blueColor);
VB->Unlock();
WORD *indices = NULL;
IB->Lock(0,0,(void**)&indices, 0);
indices[0] = 0; indices[1] = 1; indices[2] = 2;
indices[3] = 0; indices[4] = 2; indices[5] = 3;
// back side
indices[6] = 4; indices[7] = 6; indices[8] = 5;
indices[9] = 4; indices[10] = 7; indices[11] = 6;
// left side
indices[12] = 4; indices[13] = 5; indices[14] = 1;
indices[15] = 4; indices[16] = 1; indices[17] = 0;
// right side
indices[18] = 3; indices[19] = 2; indices[20] = 6;
indices[21] = 3; indices[22] = 6; indices[23] = 7;
// top
indices[24] = 1; indices[25] = 5; indices[26] = 6;
indices[27] = 1; indices[28] = 6; indices[29] = 2;
// bottom
indices[30] = 4; indices[31] = 0; indices[32] = 3;
indices[33] = 4; indices[34] = 3; indices[35] = 7;
IB->Unlock();
m_bSetup = true;
}
void AABB::Draw( IDirect3DDevice9 *pDevice )
{
if( pDevice == NULL || !m_bSetup )
{
return;
}
pDevice->BeginScene();
pDevice->SetStreamSource(0, VB, 0, sizeof(AABBVertex));
pDevice->SetIndices(IB);
pDevice->SetFVF(AABBVertex::FVF);
// Draw cube.
pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
pDevice->EndScene();
}
bool AABB::SelectObject2D(const D3DXVECTOR3 worldClickPos)
{
return worldClickPos.x >= min.x && worldClickPos.x<= max.x && worldClickPos.y >= min.y && worldClickPos.y <= max.y;
}
bool AABB::CheckNotArrive(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta,int nWhich,float fMaxScale )
{
float fCurWhich;
float fRayOrgWhich;
float fRayDeltaWhich;
float fMinWhich;
float fMaxWhich;
if ( nWhich == 1)
{
fRayOrgWhich = rayOrg.x;
fRayDeltaWhich = rayDelta.x;
fMinWhich = min.x;
fMaxWhich = max.x;
}
else if( nWhich == 2)
{
fRayOrgWhich = rayOrg.y;
fRayDeltaWhich = rayDelta.y;
fMinWhich = min.y;
fMaxWhich = max.y;
}
else if( nWhich == 3)
{
fRayOrgWhich = rayOrg.z;
fRayDeltaWhich = rayDelta.z;
fMinWhich = min.z;
fMaxWhich = max.z;
}
else
{
MessageBox(NULL, "CheckNotArrive参数nWhich不对","调试",0);
return false;
}
fCurWhich = fRayOrgWhich + fMaxScale * fRayDeltaWhich;
// 转换为int是为了放宽点精度,避免选中了但是没有判断选中
/*float fCurWhichAdd = 0.7f;
fCurWhich += fCurWhichAdd;*/
if( fRayOrgWhich < fMinWhich && int(fCurWhich) < int(fMinWhich) )
{
return false;
}
else if ( fRayOrgWhich > fMaxWhich && int(fCurWhich) > int(fMaxWhich) )
{
return false;
}
return true;
}
bool AABB::CheckOneAxis(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta, int nWhich )
{
bool bIncrease = true;
float fRayOrgWhich;
float fRayDeltaWhich;
float fMinWhich;
float fMaxWhich;
int nOtherAxis1;
int nOtherAxis2;
if ( nWhich == 1)
{
fRayOrgWhich = rayOrg.x;
fRayDeltaWhich = rayDelta.x;
fMinWhich = min.x;
fMaxWhich = max.x;
nOtherAxis1 = 2;
nOtherAxis2 = 3;
}
else if( nWhich == 2)
{
fRayOrgWhich = rayOrg.y;
fRayDeltaWhich = rayDelta.y;
fMinWhich = min.y;
fMaxWhich = max.y;
nOtherAxis1 = 1;
nOtherAxis2 = 3;
}
else if( nWhich == 3)
{
fRayOrgWhich = rayOrg.z;
fRayDeltaWhich = rayDelta.z;
fMinWhich = min.z;
fMaxWhich = max.z;
nOtherAxis1 = 1;
nOtherAxis2 = 2;
}
else
{
MessageBox(NULL, "CheckNotArrive参数nWhich不对","调试",0);
return false;
}
if( d3d::FloatEquip(fRayDeltaWhich, 0.0f) )
{
return false;
}
if( fRayOrgWhich > fMaxWhich )
{
bIncrease = false;
}
if( bIncrease )
{
// rayDelta.x x正负都是不能判断一定不相交的,因为x可能在包围盒内,只能通过是向右增加还是向右增长来判断
if( fRayDeltaWhich < 0.0f )
{
if( fRayOrgWhich < fMinWhich )
{
return false;
}
float fScaleMaxLimit = ( fMinWhich - fRayOrgWhich ) / fRayDeltaWhich;
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMaxLimit) )
{
return false;
}
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2 ,fScaleMaxLimit) )
{
return false;
}
}
else
{
if( fRayOrgWhich > fMaxWhich )
{
return false;
}
float fScaleMaxLimit = ( fMaxWhich - fRayOrgWhich ) / fRayDeltaWhich;
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMaxLimit) )
{
return false;
}
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2, fScaleMaxLimit) )
{
return false;
}
}
}
else
{
// 射线原点一定在最大值右边了,不会在物体中间情况
if( fRayDeltaWhich >= 0.0f )
{
return false;
}
float fScaleMax = ( fMinWhich - fRayOrgWhich ) / fRayDeltaWhich;
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMax) )
{
return false;
}
if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2, fScaleMax) )
{
return false;
}
}
}
// 还有一个最长的射线方式,转换为固定长度射线和AABB相交的判断
bool AABB::RayIntersetBoxWithoutDirLength(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta)
{
// 如果原点在包围盒内,那么就直接返回了
if ( rayOrg.x >= min.x && rayOrg.x <= max.x &&
rayOrg.y >= min.y && rayOrg.y <= max.y &&
rayOrg.z >= min.z && rayOrg.z <= max.z)
{
return true;
}
// 1.先考虑x方向上,判断 方向
if ( !CheckOneAxis(rayOrg, rayDelta, 1) )
{
return false;
}
// 2.先考虑x方向上,判断 方向
if ( !CheckOneAxis(rayOrg, rayDelta, 2) )
{
return false;
}
// 3.先考虑x方向上,判断 方向
if ( !CheckOneAxis(rayOrg, rayDelta, 3) )
{
return false;
}
// 哪里出现了问题
return true;
}
void AABB::CalculateBoudingBox(D3DXVECTOR3 *pVec, int n)
{
for( int i = 0; i < n; i++ )
{
if(pVec[i].x < min.x )
{
min.x = pVec[i].x;
}
if(pVec[i].y < min.y )
{
min.y = pVec[i].y;
}
if(pVec[i].z < min.z )
{
min.z = pVec[i].z;
}
if(pVec[i].x > max.x )
{
max.x = pVec[i].x;
}
if(pVec[i].y > max.y )
{
max.y = pVec[i].y;
}
if(pVec[i].z > max.z )
{
max.z = pVec[i].z;
}
}
m_bEmpty = false;
}
D3DXVECTOR3 AABB::GetRayDir(const D3DXVECTOR3 &rayOrg, const D3DXVECTOR3 &rayDelta)
{
float fMaxScale = 0.0f;
float fMaxScaleNext = 0.0f;
if( rayOrg.x < max.x && rayDelta.x > 0.0f )
{
fMaxScale = (max.x - rayOrg.x) / rayDelta.x;
}
else if( rayOrg.x > min.x && rayDelta.x < 0.0f )
{
fMaxScale = (min.x - rayOrg.x) / rayDelta.x;
}
if( rayOrg.y < max.y && rayDelta.y > 0.0f )
{
fMaxScaleNext = (max.y - rayOrg.y) / rayDelta.y;
}
else if( rayOrg.y > min.y && rayDelta.y < 0.0f )
{
fMaxScaleNext = (min.y - rayOrg.y) / rayDelta.y;
}
if( fMaxScaleNext > fMaxScale )
{
fMaxScale = fMaxScaleNext;
}
if( rayOrg.z < max.z && rayDelta.z > 0.0f )
{
fMaxScaleNext = (max.z - rayOrg.z) / rayDelta.z;
}
else if( rayOrg.z > min.z && rayDelta.z < 0.0f )
{
fMaxScaleNext = (min.z - rayOrg.z) / rayDelta.z;
}
if( fMaxScaleNext > fMaxScale )
{
fMaxScale = fMaxScaleNext;
}
return (fMaxScale * rayDelta);
}
float AABB::RayIntersect(
const D3DXVECTOR3 &rayOrg, // orgin of the ray
const D3DXVECTOR3 &rayDelta, // length and direction of the ray
D3DXVECTOR3 *returnNormal // optionally, the normal is returned
) const
{ // We'll return this huge number if no intersection
const float kNoIntersection = 1e30f;
// Check for point inside box, trivial reject, and determine parametric
// distance to each front face
bool inside = true;
float xt, xn;
if (rayOrg.x < min.x) {
xt = min.x - rayOrg.x;
if (xt > rayDelta.x) return kNoIntersection;
xt /= rayDelta.x;
inside = false;
xn = -1.0f;
} else if (rayOrg.x > max.x) {
xt = max.x - rayOrg.x;
if (xt < rayDelta.x) return kNoIntersection;
xt /= rayDelta.x;
inside = false;
xn = 1.0f;
} else {
xt = -1.0f;
}
float yt, yn;
if (rayOrg.y < min.y) {
yt = min.y - rayOrg.y;
if (yt > rayDelta.y) return kNoIntersection;
yt /= rayDelta.y;
inside = false;
yn = -1.0f;
} else if (rayOrg.y > max.y) {
yt = max.y - rayOrg.y;
if (yt < rayDelta.y) return kNoIntersection;
yt /= rayDelta.y;
inside = false;
yn = 1.0f;
} else {
yt = -1.0f;
}
float zt, zn;
if (rayOrg.z < min.z) {
zt = min.z - rayOrg.z;
if (zt > rayDelta.z) return kNoIntersection;
zt /= rayDelta.z;
inside = false;
zn = -1.0f;
} else if (rayOrg.z > max.z) {
zt = max.z - rayOrg.z;
if (zt < rayDelta.z) return kNoIntersection;
zt /= rayDelta.z;
inside = false;
zn = 1.0f;
} else {
zt = -1.0f;
}
// Inside box?
if (inside) {
if (returnNormal != NULL) {
*returnNormal = -rayDelta;
//returnNormal->normalize();
D3DXVec3Normalize(returnNormal, returnNormal);
}
return 0.0f;
}
// Select farthest plane - this is
// the plane of intersection.
int which = 0;
float t = xt;
if (yt > t) {
which = 1;
t = yt;
}
if (zt > t) {
which = 2;
t = zt;
}
switch (which) {
case 0: // intersect with yz plane
{
float y = rayOrg.y + rayDelta.y*t;
if (y < min.y || y > max.y) return kNoIntersection;
float z = rayOrg.z + rayDelta.z*t;
if (z < min.z || z > max.z) return kNoIntersection;
if (returnNormal != NULL) {
returnNormal->x = xn;
returnNormal->y = 0.0f;
returnNormal->z = 0.0f;
}
} break;
case 1: // intersect with xz plane
{
float x = rayOrg.x + rayDelta.x*t;
if (x < min.x || x > max.x) return kNoIntersection;
float z = rayOrg.z + rayDelta.z*t;
if (z < min.z || z > max.z) return kNoIntersection;
if (returnNormal != NULL) {
returnNormal->x = 0.0f;
returnNormal->y = yn;
returnNormal->z = 0.0f;
}
} break;
case 2: // intersect with xy plane
{
float x = rayOrg.x + rayDelta.x*t;
if (x < min.x || x > max.x) return kNoIntersection;
float y = rayOrg.y + rayDelta.y*t;
if (y < min.y || y > max.y) return kNoIntersection;
if (returnNormal != NULL) {
returnNormal->x = 0.0f;
returnNormal->y = 0.0f;
returnNormal->z = zn;
}
} break;
}
// Return parametric point of intersection
return t;
}
bool AABB::IsIntersect(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta)
{
if( m_bEmpty )
{
return false;
}
if( !m_bSetup )
{
return false;
}
D3DXVECTOR3 v0(min.x, min.y, min.z);
D3DXVECTOR3 v1(min.x, max.y, min.z);
D3DXVECTOR3 v2(max.x, max.y, min.z);
D3DXVECTOR3 v3(max.x, min.y, min.z);
D3DXVECTOR3 v4(min.x, min.y, max.z);
D3DXVECTOR3 v5(max.x, min.y, max.z);
D3DXVECTOR3 v6(max.x, max.y, max.z);
D3DXVECTOR3 v7(min.x, max.y, max.z);
// front
if( D3DXIntersectTri(&v0, &v1, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v0, &v2, &v3, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
// back
if( D3DXIntersectTri(&v4, &v5, &v6, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v4, &v6, &v7, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
// left
if( D3DXIntersectTri(&v4, &v7, &v1, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v4, &v1, &v0, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
// right
if( D3DXIntersectTri(&v5, &v3, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v5, &v2, &v6, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
// top
if( D3DXIntersectTri(&v7, &v6, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v7, &v2, &v1, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
// bottom
if( D3DXIntersectTri(&v0, &v3, &v5, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
if( D3DXIntersectTri(&v0, &v5, &v4, &rayOrg,&rayDelta, NULL, NULL, NULL) )
{
return true;
}
}
void AABB::setToTransformedBox(const AABB &box, const D3DXMATRIX &m)
{
// If we're empty, then bail
if ( box.m_bEmpty ) {
return;
}
// Start with the translation portion
min = max = D3DXVECTOR3(m._41, m._42, m._43);
// Examine each of the 9 matrix elements
// and compute the new AABB
if (m._11 > 0.0f) {
min.x += m._11 * box.min.x; max.x += m._11 * box.max.x;
} else {
min.x += m._11 * box.max.x; max.x += m._11 * box.min.x;
}
if (m._12 > 0.0f) {
min.y += m._12 * box.min.x; max.y += m._12 * box.max.x;
} else {
min.y += m._12 * box.max.x; max.y += m._12 * box.min.x;
}
if (m._13 > 0.0f) {
min.z += m._13 * box.min.x; max.z += m._13 * box.max.x;
} else {
min.z += m._13 * box.max.x; max.z += m._13 * box.min.x;
}
if (m._21 > 0.0f) {
min.x += m._21 * box.min.y; max.x += m._21 * box.max.y;
} else {
min.x += m._21 * box.max.y; max.x += m._21 * box.min.y;
}
if (m._22 > 0.0f) {
min.y += m._22 * box.min.y; max.y += m._22 * box.max.y;
} else {
min.y += m._22 * box.max.y; max.y += m._22 * box.min.y;
}
if (m._23 > 0.0f) {
min.z += m._23 * box.min.y; max.z += m._23 * box.max.y;
} else {
min.z += m._23 * box.max.y; max.z += m._23 * box.min.y;
}
if (m._31 > 0.0f) {
min.x += m._31 * box.min.z; max.x += m._31 * box.max.z;
} else {
min.x += m._31 * box.max.z; max.x += m._31 * box.min.z;
}
if (m._32 > 0.0f) {
min.y += m._32 * box.min.z; max.y += m._32 * box.max.z;
} else {
min.y += m._32* box.max.z; max.y += m._32 * box.min.z;
}
if (m._33 > 0.0f) {
min.z += m._33 * box.min.z; max.z += m._33 * box.max.z;
} else {
min.z += m._33 * box.max.z; max.z += m._33 * box.min.z;
}
m_bEmpty = false;
}
PickingTechnology.h
struct Ray
{
D3DXVECTOR3 _origin;
D3DXVECTOR3 _direction;
};
struct BoudingSphere
{
D3DXVECTOR3 _center;
float _radius;
};
class CPickingTechnology
{
public:
CPickingTechnology(int nWidth, int nHeight, IDirect3DDevice9 *pDevice);
~CPickingTechnology();
// 构建包围盒
void BuildObjectBox(D3DXVECTOR3 *pVec, int n, const string strObjectMarkNmae);
bool IsPickingOK(const POINT &cursorPoint);
void SetupAABB();
void DrawAABB();
void setToTransformedBox(const AABB &box, const D3DXMATRIX &m);
// 和包围球的相交检测
Ray CalcPickingRay(int x, int y);
void TransFormRay(Ray *ray, D3DXMATRIX *T);
bool PickingTest(int x, int y);
bool RaySphereIntTest( Ray *ray, BoudingSphere *sphere);
private:
// 自己写的转换函数验证过是正确的,而且非常清晰
void CalculateRayDirFromScreenToWorld(const POINT &cursorPoint);
private:
// 包围盒物体的名字,用于调试
string m_strObjectName;
// 当前的包围盒对象
AABB m_AABB;
// 鼠标点击世界坐标系中原点
D3DXVECTOR3 m_cursorWorldOrigin;
// 鼠标点击的世界坐标系中射线方向向量
D3DXVECTOR3 m_cursorWorldDir;
// 设备对象
IDirect3DDevice9 *m_pDevice;
// 宽高
int m_nWidth;
int m_nHeight;
// 包围球
BoudingSphere m_Shere;
};
#endif
PickingTechnology.cpp
#include "PickingTechnology.h"
CPickingTechnology::CPickingTechnology(int nWidth, int nHeight, IDirect3DDevice9 *pDevice)
{
m_nWidth = nWidth;
m_nHeight = nHeight;
m_pDevice = pDevice;
}
CPickingTechnology::~CPickingTechnology()
{
}
void CPickingTechnology::BuildObjectBox(D3DXVECTOR3 *pVec, int n, const string strObjectMarkNmae)
{
m_AABB.CalculateBoudingBox(pVec, n);
m_strObjectName = strObjectMarkNmae;
}
void CPickingTechnology::CalculateRayDirFromScreenToWorld(const POINT &cursorPoint)
{
if(m_pDevice == NULL)
{
return;
}
// 由屏幕变换到x,y属于[-1,1], z属于1的裁剪除以w = z后的空间为
float x = 2.0f * cursorPoint.x / m_nWidth - 1;
float y = 1 - 2.0f * cursorPoint.y / m_nHeight;
D3DXVECTOR3 vecPointInCube(x, y, 0.0f);
// 由cube空间通过透视投影求逆,得到鼠标点击点在透视投影空间中的位置
D3DXMATRIX perspectiveMatrix;
m_pDevice->GetTransform(D3DTS_PROJECTION,&perspectiveMatrix);
D3DXMATRIX perspectiveMatrixInverse;
D3DXMatrixInverse(&perspectiveMatrixInverse, NULL, &perspectiveMatrix);
D3DXVECTOR3 vecPointInPerspective;
// 视图空间的透视投影变换内部仅仅是放缩,尽管没有进行4D 到3D的除法运算,就是除以w = z的运算, 但是因为需要的是单位向量.
// 其逆运算cube空间内乘以w = z,后计算得到的方向向量和没有乘以w = z的运算得到的单位向量都是一样的。
// 所以直接透视逆矩阵乘法,就得到视图空间中所求的方向向量
// Transforms a 3D vector by a given matrix, projecting the result back into w = 1.
// 当投影变换时候虽然没有4D 到3D的除以了w = z 等于1,但是向量点进行变换时候,却会进行除以w = z的变换,使得透视投影变换
// 向量点,会使得向量点被投影到xy属于[-1,1],z属于[0,1]的小立方体中;
// 同时投影矩阵求逆时也是没有进行乘以w=z的乘法,但是 D3DXVec3TransformCoord变换时候,会使得w=z,变为w = 1,从而进行乘法。
D3DXVec3TransformCoord(&vecPointInPerspective, &vecPointInCube, &perspectiveMatrixInverse);
// 测试
//D3DXMATRIX projMatrix(1.33f, 0.0f, 0.0f, 0.0f,
// 0.0f, 1.0f, 0.0f, 0.0f,
// 0.0f, 0.0f, 0.0f, -0.999f,// 一定要-0.999因为是D3DTS_PROJECTION
// 0.0f, 0.0f, 1.0f, 1.0f
// );
//D3DXVECTOR3 vecTest(-0.0094f, 0.054, 1);
////D3DXVec3Normalize(&vecTest, &vecTest);
//D3DXVECTOR3 vecOut;
//// 归一化处理如果w=0.001,那么乘以1000就得到投影坐标系中的坐标;这里其实逆向运算是需要真正的z的值,才能进行更准确的变换
//// 对于拾取技术,是不需要z的值的,因为用一个点击的原点和射线单位向量就可以了,检测单位向量穿过的最前面的物体
//// 置于屏幕中斜线前面和后面的物体并不是同一个射线的,正对z轴里面的物体才是同一个射线选中的
//D3DXVec3TransformCoord(&vecOut, &vecTest, &projMatrix);
//// 测试结束
// 鼠标点击的原点是(0,0,0)和方向向量进行规范化
D3DXVECTOR3 cursorPersOrigin(0.0f, 0.0f, 0.0f);
/*D3DXVECTOR3 cursorPersDir;
D3DXVec3Normalize(&cursorPersDir, &vecPointInPerspective);*/
// 视图空间到世界坐标系的变换
D3DXMATRIX viewMatrix;
D3DXMATRIX viewInverseMatrix;
m_pDevice->GetTransform(D3DTS_VIEW, &viewMatrix);
D3DXMatrixInverse(&viewInverseMatrix, NULL, &viewMatrix);
// 原点进行点变换,需要考虑平移,w = 1
D3DXVec3TransformCoord(&m_cursorWorldOrigin, &cursorPersOrigin, &viewInverseMatrix);
// 鼠标射线进行点变换,需要考虑平移,w = 0
D3DXVec3TransformNormal(&m_cursorWorldDir, &vecPointInPerspective, &viewInverseMatrix);
// 单位化,因为拾取技术,只需要知道射线方向选择第一个相交的物体旧可以了
// 2D中可以不用单位化,通过世界坐标系中的x,y进行判断即可
D3DXVec3Normalize(&m_cursorWorldDir, &m_cursorWorldDir);
}
bool CPickingTechnology::IsPickingOK(const POINT &cursorPoint)
{
if( m_AABB.IsEmpty() )
{
MessageBox(NULL, "没有设置需要判断的物体", "鼠标拾取判断", 0);
return false;
}
/*POINT point;
point.x = 576;
point.y = 425;*/
CalculateRayDirFromScreenToWorld(cursorPoint);
// m_cursorWorldDir是世界坐标系中的点
// 2D的自己写的
//bool bSelect = m_AABB.SelectObject2D(m_cursorWorldDir);
// 3D通用的标准做法
//bool bSelect = m_AABB.IsIntersect(m_cursorWorldOrigin, m_cursorWorldDir);
// 3D的改用的标准算法
D3DXVECTOR3 rayDir = m_AABB.GetRayDir(m_cursorWorldOrigin, m_cursorWorldDir);
float ft = m_AABB.RayIntersect(m_cursorWorldOrigin, rayDir/*m_cursorWorldDir*/, NULL);
// 3D的自己写的算法
//bool bSelect = m_AABB.RayIntersetBoxWithoutDirLength(m_cursorWorldOrigin, m_cursorWorldDir);
string strMsg = "拾取对象:";
if ( ft >= 0.0f && ft <=1.0f/*bSelect*/ )
{
strMsg += m_strObjectName;
}
else
{
strMsg += "没有选择对象";
}
MessageBox(NULL, strMsg.c_str(), "鼠标拾取判断", 0);
}
void CPickingTechnology::SetupAABB()
{
m_AABB.Setup(m_pDevice);
}
void CPickingTechnology::DrawAABB()
{
m_AABB.Draw(m_pDevice);
}
void CPickingTechnology::setToTransformedBox(const AABB &box, const D3DXMATRIX &m)
{
m_AABB.setToTransformedBox(box, m);
}
Ray CPickingTechnology::CalcPickingRay(int x, int y)
{
float px = 0.0f;
float py = 0.0f;
D3DVIEWPORT9 vp;
m_pDevice->GetViewport(&vp);
D3DXMATRIX proj;
m_pDevice->GetTransform(D3DTS_PROJECTION, &proj);
px = ( ((2.0f * x) / vp.Width) - 1.0f) / proj(0,0);
py = ( ((-2.0f * y) / vp.Height) + 1.0f) / proj(1,1);
Ray ray;
ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
ray._direction = D3DXVECTOR3(px, py, 1.0f);
return ray;
}
void CPickingTechnology::TransFormRay(Ray *ray, D3DXMATRIX *T)
{
D3DXVec3TransformCoord(&(ray->_origin), &(ray->_origin),T);
// 鼠标射线进行点变换,需要考虑平移,w = 1
D3DXVec3TransformNormal(&(ray->_direction), &(ray->_direction),T);
D3DXVec3Normalize(&(ray->_direction), &(ray->_direction));
}
bool CPickingTechnology::RaySphereIntTest( Ray *ray, BoudingSphere *sphere)
{
D3DXVECTOR3 v = ray->_origin - sphere->_center;
float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v);
float c = D3DXVec3Dot(&v, &v) - sphere->_radius * sphere->_radius;
float discriminant = b * b - 4.0f * c;
if( discriminant < 0.0f)
{
return false;
}
discriminant = sqrt(discriminant);
float s0 = (-b + discriminant) / 2.0f;
float s1 = (-b -discriminant) / 2.0f;
if(s0 >= 0.0f || s1 >= 0.0f)
{
return true;
}
return false;
}
bool CPickingTechnology::PickingTest(int x, int y)
{
Ray ray = CalcPickingRay(x, y);
D3DXMATRIX viewMatrix;
D3DXMATRIX viewInverseMatrix;
m_pDevice->GetTransform(D3DTS_VIEW, &viewMatrix);
D3DXMatrixInverse(&viewInverseMatrix, NULL, &viewMatrix);
//float ft = m_AABB.RayIntersect(ray._origin, ray._direction, NULL);
m_Shere._center = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
m_Shere._radius = 1.732f;
//bool bSelect = m_AABB.RayIntersectLimitless(m_cursorWorldOrigin, m_cursorWorldDir);
string strMsg = "拾取对象:";
bool bOK = RaySphereIntTest(&ray, &m_Shere);
if ( bOK/*ft >= 0.0f && ft <=1.0f*/ )
{
strMsg += m_strObjectName;
bOK = true;
}
else
{
strMsg += "没有选择对象";
}
MessageBox(NULL, strMsg.c_str(), "鼠标拾取判断", 0);
return bOK;
}
cube.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// File: cube.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Renders a spinning cube in wireframe mode. Demonstrates vertex and
// index buffers, world and view transformations, render states and
// drawing commands.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
#include "d3dUtility.h"
#include "PickingTechnology.h"
// 拾取对象
CPickingTechnology *g_pPickingObj = NULL;
//
// Globals
//
IDirect3DDevice9* Device = 0;
const int Width = 640;
const int Height = 480;
IDirect3DVertexBuffer9* VB = 0;
IDirect3DIndexBuffer9* IB = 0;
//
// Classes and Structures
//
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z, D3DCOLOR color)
{
_x = x; _y = y; _z = z;
_color = color;
}
float _x, _y, _z;
D3DCOLOR _color;
static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
//
// Framework Functions
//
bool Setup()
{
//
// Create vertex and index buffers.
//
Device->CreateVertexBuffer(
8 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&VB,
0);
Device->CreateIndexBuffer(
36 * sizeof(WORD), // 索引缓存的字节数,个数是三角形的个数*3,索引缓存个数还是没有减少的。
D3DUSAGE_WRITEONLY, // 使用类型为只读类型,获取指针然后写入数据,数据将会批量提交
D3DFMT_INDEX16, // D3DFMT_INDEX16是16bit,D3DFMT_INDEX32是32bit
D3DPOOL_MANAGED, // D3DPOOL类型,创建在SM中,需要时候拷贝到AGP内存/显存中
&IB,
0);//保留
//
// Fill the buffers with the cube data.
//
// define unique vertices:
Vertex* vertices;
// Locks a range of vertex data and obtains a pointer to the vertex buffer memory.
/* nOffset: 0 whole
nSize: 0 Whole
void **ppbData
nFlags: Combination of zero or more locking flags that describe the type of lock to perform. For this method, the valid flags are:
D3DLOCK_DISCARD */
//D3DLOCK_DISCARD
//char szDebugInfo[256];
VB->Lock(0, 0, (void**)&vertices, 0/*D3DLOCK_DISCARD*/);
// vertices of a unit cube
/*vertices[0] = Vertex(-1.0f, -1.0f, -1.0f, D3DCOLOR_XRGB(255,0,0));
vertices[1] = Vertex(-1.0f, 1.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[2] = Vertex( 1.0f, 1.0f, -1.0f, D3DCOLOR_XRGB(0,255,0));
vertices[3] = Vertex( 1.0f, -1.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[4] = Vertex(-1.0f, -1.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[5] = Vertex(-1.0f, 1.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[6] = Vertex( 1.0f, 1.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[7] = Vertex( 1.0f, -1.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));*/
vertices[0] = Vertex(-100.0f, -100.0f, -1.0f, D3DCOLOR_XRGB(255,0,0));
vertices[1] = Vertex(-100.0f, 100.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[2] = Vertex( 100.0f, 100.0f, -1.0f, D3DCOLOR_XRGB(0,255,0));
vertices[3] = Vertex( 100.0f, -100.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[4] = Vertex(-100.0f, -100.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[5] = Vertex(-100.0f, 100.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[6] = Vertex( 100.0f, 100.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
vertices[7] = Vertex( 100.0f, -100.0f, 1.0f, D3DCOLOR_XRGB(255,255,0));
VB->Unlock();
// define the triangles of the cube:
WORD* indices = 0;
IB->Lock(0, 0, (void**)&indices, 0);
// DX中三角形顺时针(左手定则)为正面, 逆时针为背面会被消隐,OGL则相反。
// 索引缓存点的个数是三角形片面数 * 3得到的个数。
// 所有索引缓存表面从外往物体里看都是顺时针给出的(例如六面体背面的索引缓存顺序),索引缓存每连续三个为一个三角形即可。
// 索引缓存的好处:
// 1.减少了顶点缓存的顶点个数(不需重复顶点)
// 2.灵活修改图形(通过修改索引而不是顶点)
// 3.提高渲染runtime将多次用到的数据放置在相近的地方Cache中(local reference)
// front side
indices[0] = 0; indices[1] = 1; indices[2] = 2;
indices[3] = 0; indices[4] = 2; indices[5] = 3;
// back side
indices[6] = 4; indices[7] = 6; indices[8] = 5;
indices[9] = 4; indices[10] = 7; indices[11] = 6;
// left side
indices[12] = 4; indices[13] = 5; indices[14] = 1;
indices[15] = 4; indices[16] = 1; indices[17] = 0;
// right side
indices[18] = 3; indices[19] = 2; indices[20] = 6;
indices[21] = 3; indices[22] = 6; indices[23] = 7;
// top
indices[24] = 1; indices[25] = 5; indices[26] = 6;
indices[27] = 1; indices[28] = 6; indices[29] = 2;
// bottom
indices[30] = 4; indices[31] = 0; indices[32] = 3;
indices[33] = 4; indices[34] = 3; indices[35] = 7;
IB->Unlock();
//
// Position and aim the camera.
//
// 视图坐标系和局部坐标系是一样的,都是世界坐标系转换为指定的局部坐标系,从局部点到世界点需要先旋转后平移Mx*My*Mz*P的变换矩阵,
//而从世界点到局部点要进行先平移后旋转的逆过程P-1Mz-1My-1Mx-1的变换矩矩阵(PMzMyMx是重用局部点到世界的变换).
//矩阵是重新分解和组合空间位置和方位的空间变换过程;
//矩阵行是新坐标系的基向量或表示平移,用变换后的向量在原坐标系各轴分解得的向量表示;
//矩阵列是对应原坐标系的各轴,是各新基向量在列对应原坐标系的轴上的累积(组合)向量。
D3DXVECTOR3 position(0.0f, 0.0f, -100.0f);//camera在世界坐标系中的位置向量
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);//target是camera需要平移到的且旋转所在的点,可以是原点,也 可以是其它观察点
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);//定义向上的方向,一般是[0,1,0]
D3DXMATRIX V;
// V是world-to-view空间的变换矩阵,内部会计算世界坐标系到观察坐标系的变换(物体假设不动),
// 其实新的坐标系下所有物体的位置时重新计算了的。
D3DXMatrixLookAtLH(&V, &position, &target, &up);
Device->SetTransform(D3DTS_VIEW, &V);////设置变换的状态,待最后综合各种变换得到最终实际变换时才变换(可暂时理解为世界坐标系中的物体被变换到观察坐标系中)
//
// Set the projection matrix.
//
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
// 假设物体不动,观察坐标系到投影坐标系的转换矩阵
// 待最后综合各种变换得到最终实际变换时才变换(可暂时理解为观察坐标系中的物体被变换到投影坐标系内)
&proj,
D3DX_PI * 0.5f, // 90 - degree 观察坐标系中的上下夹角
(float)Width / (float)Height, // 宽高比例
1.0f, // 观察坐标系视锥体的近截面
1000.0f);// 观察坐标系视锥体的远截面
D3DXMATRIX proj2D;
D3DXMatrixOrthoLH(&proj2D, float(Width), float(Height), 1.0f, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
//
// Switch to wireframe mode.
//
Device->SetRenderState(D3DRS_LIGHTING, FALSE);
Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID/*D3DFILL_WIREFRAME*/);
Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
//Device->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, true);
//D3DXMATRIX Rx, Ry;
static float y = 2.0f;
D3DXMATRIX T;
D3DXMatrixTranslation(&T, -3.0f, 2.0f, 6.0f);
//D3DXMatrixRotationY(&Ry, y);
//D3DXMatrixIdentity(&Ry);
// 缩放或旋转会导致包围盒拉伸变形,而不正确
D3DXMATRIX M =T /*T * Ry*/;
AABB originBox;
D3DXVECTOR3 pVertex[8];
for ( int i = 0; i < 8; i++)
{
pVertex[i] = D3DXVECTOR3(vertices[i]._x, vertices[i]._y, vertices[i]._z);
D3DXVec3TransformCoord(&pVertex[i], &pVertex[i], &M);
}
//originBox.CalculateBoudingBox(pVertex, 8);
//g_pPickingObj->setToTransformedBox(originBox, M);
g_pPickingObj->BuildObjectBox(pVertex, 8, "茶壶");
// 设置包围盒
/*VB->Lock(0, 0, (void**)&vertices, 0);
if( g_pPickingObj != NULL )
{
g_pPickingObj->BuildObjectBox( pVertex, 8, "茶壶");
}
VB->Unlock();*/
if( g_pPickingObj != NULL )
{
g_pPickingObj->SetupAABB();
}
return true;
}
void Cleanup()
{
d3d::Release<IDirect3DVertexBuffer9*>(VB);
d3d::Release<IDirect3DIndexBuffer9*>(IB);
}
bool Display(float timeDelta)
{
if( Device )
{
//
// spin the cube:
//
D3DXMATRIX Rx, Ry;
// rotate 45 degrees on x-axis
D3DXMatrixRotationX(&Rx, 3.14f / 4.0f);
// incremement y-rotation angle each frame
static float y = 2.0f;
D3DXMatrixRotationY(&Ry, y);
y += timeDelta;
// reset angle to zero when angle reaches 2*PI
if( y >= 6.28f )
y = 0.0f;
// combine x- and y-axis rotation transformations.
//D3DXMATRIX p = Rx * Ry;
// 世界坐标系的转换命令会被runtime commmand buffer调度,因为前面的D3DTS_VIEW,D3DTS_PROJECTION会被真正渲染管道中的时候排序到后面去了
/* D3DXMATRIX T = D3DXMATRIX( 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f,5.0f,1.0f);
D3DXMATRIX M = T * Ry;
Device->SetTransform(D3DTS_WORLD, &M);*/
D3DXMATRIX T;
D3DXMatrixTranslation(&T, -3.0f, 2.0f, 6.0f);
Device->SetTransform(D3DTS_WORLD, &T);
// draw the scene:
//
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetIndices(IB);
Device->SetFVF(Vertex::FVF);
// Draw cube.
Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
Device->EndScene();
D3DXMATRIX T2;
D3DXMatrixIdentity(&T2);
Device->SetTransform(D3DTS_WORLD, &T2);
if( g_pPickingObj != NULL )
{
g_pPickingObj->DrawAABB();
}
Device->Present(0, 0, 0, 0);
}
return true;
}
//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
::DestroyWindow(hwnd);
break;
case WM_LBUTTONDOWN:
{
POINT p;
GetCursorPos(&p);
if( g_pPickingObj != NULL )
{
g_pPickingObj->IsPickingOK(p);
//g_pPickingObj->PickingTest/*IsPickingOK*/(p.x, p.y);
}
}
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE prevInstance,
PSTR cmdLine,
int showCmd)
{
if(!d3d::InitD3D(hinstance,
Width, Height, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, "InitD3D() - FAILED", 0, 0);
return 0;
}
g_pPickingObj = new CPickingTechnology(Width, Height, Device);
if(!Setup())
{
::MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
d3d::EnterMsgLoop( Display );
Cleanup();
Device->Release();
return 0;
}