//*******************************************************************************
// 功能:存储地形判断射线相交的必须信息
// ---------------
// 作者:马晓霏
//*******************************************************************************
#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;
}
}
ogrese地形数据信息 SETerrainData.h SETerrainData.cpp 源码以及注释
最新推荐文章于 2022-01-29 16:13:39 发布