//******************************************************************************* // 功能:存储地形判断射线相交的必须信息 // --------------- // 作者:马晓霏 //******************************************************************************* #pragma once #include <vector> #include "OgreVector3.h" #include "OgreAxisAlignedBox.h" #include "OgreImage.h" namespace OgreSE { class TerrainData { public: TerrainData(size_t width, size_t height, const Ogre::Vector3& scale = Ogre::Vector3(100,100,100)); ~TerrainData(); //////////////////////////////////////////////////////////////// // 功能: 得到世界坐标中 xz 点的法线值 //////////////////////////////////////////////////////////////// Ogre::Vector3 GetNormalAt(float x, float z) const; //////////////////////////////////////////////////////////////// // 功能: 得到世界坐标中 xz 点的高度图值 //////////////////////////////////////////////////////////////// float GetRawHeightAt(float x, float z) const; //////////////////////////////////////////////////////////////// // 功能: 计算绑定盒,结果保存到 pBox, 其他参数例如 32,64,64,96 //////////////////////////////////////////////////////////////// void ComputerBoundBox(Ogre::AxisAlignedBox* pBox, size_t startX, size_t startZ, size_t endX, size_t endZ) const; //////////////////////////////////////////////////////////////// // 功能: 得到绑定盒, 更新绑定盒 //////////////////////////////////////////////////////////////// Ogre::AxisAlignedBox& GetBoundBox(){return m_BoundBox;}; //////////////////////////////////////////////////////////////// // 功能: 得到高度图宽长 //////////////////////////////////////////////////////////////// size_t GetWidth(){return m_nWidth;}; size_t GetHeight() {return m_nHeight;}; //////////////////////////////////////////////////////////////// // 功能: 得到高度图 //////////////////////////////////////////////////////////////// const Ogre::Vector3& GetPosition(){return m_vPosition;};//返回当前地形的第一个节点 m_vPosition const Ogre::Vector3& GetScale(){return m_vScale;}; //得到地形的缩放率 //-----------2D与3D之间转换--------------- ////////////////////////////////////////////////////////////////// // 功能: 计算给定的索引值在世界坐标中的位置 ////////////////////////////////////////////////////////////////// Ogre::Vector3 ConvertToWorld(int x, float yRaw, int z) const { return Ogre::Vector3( m_vPosition.x + m_vScale.x * x, m_vPosition.y + m_vScale.y * yRaw, m_vPosition.z + m_vScale.z * z); }; float ConvertToWorldX(size_t x) const { return m_vPosition.x + m_vScale.x * x; } float ConvertToWorldZ(size_t z) const { return m_vPosition.z + m_vScale.z * z; } float ConvertToWorldY(float yRaw) const { return m_vPosition.y + yRaw * m_vScale.y; } //////////////////////////////////////////////////////////////// // 功能: 得到结点 xz 点的world坐标 //////////////////////////////////////////////////////////////// Ogre::Vector3 PixelToWorld(size_t x, size_t z) const { return ConvertToWorld(x, GetRawHeight(x,z), z); } //////////////////////////////////////////////////////////////// // 功能: 得到高度图像素点对应的3D点的法线, 切线 // 参数: x,z,栅格点号 // 返回值: 法线,切线 //////////////////////////////////////////////////////////////// Ogre::Vector3 GetPixelNormal(size_t x, size_t z) const; Ogre::Vector3 GetPixelTangent(size_t x, size_t z) const; //////////////////////////////////////////////////////////////// // 功能: 得到xy点在世界坐标中的实际Y值 //////////////////////////////////////////////////////////////// float GetWorldHeight(size_t x, size_t z) const { assert(!IsEmpty() && IsValidPixel(x,z)); return m_vPosition.y + m_vScale.y* GetRawHeight(x,z); } //////////////////////////////////////////////////////////////// // 功能: 得到xy点在高度图中的高度值 //////////////////////////////////////////////////////////////// float GetRawHeight(size_t x, size_t z) const { assert(!IsEmpty() && IsValidPixel(x,z)); return m_Heightmap[z*m_nWidth + x]; } float& GetRawHeight(size_t x, size_t z) { assert(!IsEmpty() && IsValidPixel(x,z)); return m_Heightmap[z*m_nWidth + x]; } void SetRawHeight(size_t x, size_t z, float val) { assert(!IsEmpty() && IsValidPixel(x,z)); m_Heightmap[z*m_nWidth + x] = val; } void LoadHeightmapData(Ogre::DataStreamPtr &stream) { stream->read(m_Heightmap, m_nWidth*m_nHeight*sizeof(float)); } void SaveHeightmapData(std::ofstream &stream) { stream.write((char*)m_Heightmap, m_nWidth*m_nHeight*sizeof(float)); } //////////////////////////////////////////////////////////////// // 功能: 计算世界坐标中的xz在高度图中的位置 //////////////////////////////////////////////////////////////// size_t WorldToPixelX(float x) const { return Ogre::Math::IFloor((x - m_vPosition.x) * m_vInvScale.x + (float)0.5); } size_t WorldToPixelZ(float z) const { return Ogre::Math::IFloor((z - m_vPosition.z) * m_vInvScale.z + (float)0.5); } //////////////////////////////////////////////////////////////// // 功能: 计算世界坐标中的xz的栅格号 m_vPosition.x:第一个栅格放在世界坐标中的位置 //////////////////////////////////////////////////////////////// size_t WorldToGridX(float x) const//从当前世界坐标来得到当前节点在地形栅格中的编号 { return Ogre::Math::IFloor((x - m_vPosition.x) * m_vInvScale.x); } size_t WorldToGridZ(float z) const { return Ogre::Math::IFloor((z - m_vPosition.z) * m_vInvScale.z); } private: //////////////////////////////////////////////////////////////// // 功能: 判断地形是否是空 //////////////////////////////////////////////////////////////// bool IsEmpty() const { return m_Heightmap == NULL; } //////////////////////////////////////////////////////////////// // 功能: 给定xy点是否有效 //////////////////////////////////////////////////////////////// bool IsValidPixel(size_t x, size_t z) const { return 0 <= x && x < && 0 <= z && z < m_nHeight; } private: // 设置数据 size_t m_nWidth; // 高度图宽,(2^n)* m + 1 size_t m_nHeight; // 高度图高,(2^n)* m + 1 Ogre::Vector3 m_vScale; // 缩放率:每个栅格的世界坐标单元尺寸 Ogre::Vector3 m_vPosition; // 第一个栅格放在世界坐标中的位置 float* m_Heightmap; // 保存每一点的高度, 有(m_nWidth * m_nHeight)个元素,保存在 *.Heightmap 中 高度数据存储在其中 mutable Ogre::AxisAlignedBox m_BoundBox; // 边界盒 Ogre::Vector3 m_vInvScale; // 缩放率的倒数 }; } //******************************************************************************* // 功能:存储地形判断射线相交的必须信息 // --------------- // 作者:马晓霏 //******************************************************************************* #include "StdAfx.h" #include "SETerrainData.h" namespace OgreSE { //*************************************************************************************** // 构造 析构 //m_Heightmap用来存储地形的高度信息,设置地图的开始点,设置地图的外接盒 TerrainData::TerrainData(size_t width, size_t height, const Ogre::Vector3& scale) : m_nWidth(width)//高度图宽 , m_nHeight(height) //高度图高 , m_vScale(scale) //缩放比率 , m_vInvScale(1/scale) //缩放比率的倒数 , m_Heightmap(NULL)//保存每一点的高度, 有(m_nWidth * m_nHeight)个元素,保存在 *.Heightmap 中 高度数据存储在其中 { m_Heightmap = new float[m_nWidth * height];//保存每一点的高度,有(m_nWidth * m_nHeight)个元素,保存在 *.Heightmap 中 memset(m_Heightmap, 0, m_nWidth * height * sizeof(float));//初始化所有的高度为0 --width; --height; //m_vPosition 第一个栅格放在世界坐标中的位置 注意一定要中心为 (0,0,0) m_vPosition = Ogre::Vector3(- m_vScale.x * (width>>1), 0, -m_vScale.z * (height>>1)); // 0,0为中点 m_vPosition=Vector3(-128,0,-128); //则 左上为(-128,0,-128) 右上 (128,0,-128) 右下 (-128,0,128) 右下 (128,0,128) center(0,0,0) //边界盒m_BoundBox m_BoundBox.setExtents(ConvertToWorld(0, 0, 0), ConvertToWorld(width, 0, height)); } TerrainData::~TerrainData() { SAFE_DELETE_ARRAY(m_Heightmap); } //用于计算地形的包围盒 void TerrainData::ComputerBoundBox(Ogre::AxisAlignedBox* pBox, size_t startX, size_t startZ, size_t endX, size_t endZ) const { float minHeight = Ogre::Math::POS_INFINITY; // 无穷大 float maxHeight = Ogre::Math::NEG_INFINITY; // 负无穷大 // 找到该地形块 Y 最大和最小值 for (size_t z = startZ; z <= endZ; ++z) { for (size_t x = startX; x <= endX; ++x) { float h = GetRawHeight(x,z); if (minHeight > h) minHeight = h; if (maxHeight < h) maxHeight = h; } } pBox->setExtents(ConvertToWorld(startX, minHeight, startZ), ConvertToWorld(endX, maxHeight, endZ)); } //*************************************************************************************** // 功能: 得到 heightmap xz 点的法线 Ogre::Vector3 TerrainData::GetPixelNormal(size_t x, size_t z) const { /* x-1 x x+1 z-1 +--+--+ | /| /| |/ |/ | z +--+--+ | /| /| |/ |/ | z+1 +--+--+ */ if (!IsValidPixel(x, z)) return Ogre::Vector3::UNIT_Y; float h = GetRawHeight(x, z); Ogre::Vector3 corners[7]; int count = 0; #define V(i,j) ((void)(corners[count++] = Ogre::Vector3((i)*m_vScale.x, (GetRawHeight(x+(i),z+(j)) - h)*m_vScale.y, (j)*m_vScale.z))) if (x == 0) { if (z != m_nHeight - 1) { V( 0,+1); } V(+1, 0); if (z != 0) { V(+1,-1); V( 0,-1); } } else if (x == m_nWidth - 1) { if (z != 0) { V( 0,-1); } V(-1, 0); if (z != m_nHeight - 1) { V(-1,+1); V( 0,+1); } } else { if (z != 0) { V(+1, 0); V(+1,-1); V(0 ,-1); } V(-1, 0); if (z != m_nHeight - 1) { V(-1,+1); V( 0,+1); V(+1, 0); } } #undef V assert(2 <= count && count <= sizeof(corners)/sizeof(*corners)); Ogre::Vector3 sum(0, 0, 0); for (int i = 1; i < count; ++i) { Ogre::Vector3 n = corners[i-1].crossProduct(corners[i]); assert(n.y > 0); n.normalise(); sum += n; } sum.normalise(); return sum; } //*************************************************************************************** // 功能: 得到 高度图 xz 点的切线 Ogre::Vector3 TerrainData::GetPixelTangent(size_t x, size_t z) const { Ogre::Vector3 v3Return; int flip = 1; Ogre::Vector3 here(x*m_vScale.x, GetRawHeight(x,z)*m_vScale.y, z*m_vScale.z);//得到当前节点的坐标here Ogre::Vector3 left;//当前节点左边的那个节点的坐标 left if (x == 0)//如果当前节点的左边没有节点了 即当前节点就是最左边节点 那么需要一个翻转 { flip *= -1; left = Ogre::Vector3((x+1)*m_vScale.x, GetRawHeight(x+1,z)*m_vScale.y, z*m_vScale.z);//实际上是当前节点的右边坐标 } else left = Ogre::Vector3((x-1)*m_vScale.x, GetRawHeight(x-1,z)*m_vScale.y, z*m_vScale.z); left -= here;//得到一个向量,是当前节点左边的坐标减去当前节点的坐标 ,就是切线 v3Return = flip * left; v3Return.normalise(); return v3Return; } //*************************************************************************************** // 功能: 得到世界坐标中 xz点的高度图值 float TerrainData::GetRawHeightAt(float x, float z) const { // scale down x -= m_vPosition.x; z -= m_vPosition.z; x *= m_vInvScale.x;//得到在地形Terrain中的x和z坐标节点 z *= m_vInvScale.z; // retrieve height from heightmap via bilinear interpolation size_t xi = (size_t) x, zi = (size_t) z; if (xi < 0) xi = 0; if (zi< 0) zi = 0; float xpct = x - xi, zpct = z - zi; if (xi >= m_nWidth-1)//如果当前节点在最右边那那一列,那么就要修改成为倒数第二列 { xi = m_nWidth-2; xpct = 1.0f; } if (zi >= m_nHeight-1)//如果当前节点在最后边一行,那么修改成为倒数第二行 { zi = m_nHeight-2; zpct = 1.0f; } // 内插 float w0 = (1.0 - xpct) * (1.0 - zpct); float w1 = (1.0 - xpct) * zpct; float w2 = xpct * (1.0 - zpct); float w3 = xpct * zpct; return w0 * GetRawHeight(xi, zi) + w1 * GetRawHeight(xi, zi+1) + w2 * GetRawHeight(xi+1, zi) + w3 * GetRawHeight(xi+1, zi+1); } //*************************************************************************************** // 功能: 得到世界坐标中 xz 点的法线值,计算两个向量的叉积 Ogre::Vector3 TerrainData::GetNormalAt(float x, float z) const { Ogre::Vector3 here (x, ConvertToWorldY(GetRawHeightAt(x, z)), z); Ogre::Vector3 left (x-1, ConvertToWorldY(GetRawHeightAt(x-1, z)), z); Ogre::Vector3 down (x, ConvertToWorldY(GetRawHeightAt(x, z+1)), z+1); left = left - here; down = down - here; Ogre::Vector3 normal = left.crossProduct(down);// normal.normalise(); return normal; } }