Merge pull request #12495 from super626/v3

Fix terrain getIntersectionPoint
This commit is contained in:
pandamicro 2015-06-24 18:36:52 +08:00
commit f9db471026
3 changed files with 219 additions and 99 deletions

View File

@ -27,6 +27,8 @@ THE SOFTWARE.
USING_NS_CC;
#include <stdlib.h>
#include <CCImage.h>
#include <float.h>
#include <set>
#include "renderer/CCGLProgram.h"
#include "renderer/CCGLProgramCache.h"
#include "renderer/CCGLProgramState.h"
@ -35,9 +37,8 @@ USING_NS_CC;
#include "renderer/ccGLStateCache.h"
#include "renderer/CCRenderState.h"
#include "base/CCDirector.h"
#include "2d/CCCamera.h"
#include "base/CCEventType.h"
#include "2d/CCCamera.h"
NS_CC_BEGIN
@ -490,80 +491,71 @@ cocos2d::Vec3 Terrain::getNormal(int pixel_x, int pixel_y) const
cocos2d::Vec3 Terrain::getIntersectionPoint(const Ray & ray) const
{
Vec3 dir = ray._direction;
dir.normalize();
Vec3 rayStep = _terrainData._chunkSize.width*0.25*dir;
Vec3 rayPos = ray._origin;
Vec3 rayStartPosition = ray._origin;
Vec3 lastRayPosition =rayPos;
rayPos += rayStep;
// Linear search - Loop until find a point inside and outside the terrain Vector3
Vec3 normal;
float height = getHeight(rayPos.x, rayPos.z, &normal);
while (rayPos.y > height)
Vec3 collisionPoint;
if (getIntersectionPoint(ray, collisionPoint))
{
lastRayPosition = rayPos;
rayPos += rayStep;
if (normal.isZero())
return Vec3(0, 0, 0);
height = getHeight(rayPos.x,rayPos.z);
}
Vec3 startPosition = lastRayPosition;
Vec3 endPosition = rayPos;
for (int i= 0; i< 32; i++)
{
// Binary search pass
Vec3 middlePoint = (startPosition + endPosition) * 0.5f;
if (middlePoint.y < height)
endPosition = middlePoint;
else
startPosition = middlePoint;
}
Vec3 collisionPoint = (startPosition + endPosition) * 0.5f;
return collisionPoint;
return collisionPoint;
}
else
{
return Vec3(0,0,0);
}
}
bool Terrain::getIntersectionPoint(const Ray & ray, Vec3 & intersectionPoint) const
{
Vec3 dir = ray._direction;
dir.normalize();
Vec3 rayStep = _terrainData._chunkSize.width*0.25*dir;
Vec3 rayPos = ray._origin;
Vec3 rayStartPosition = ray._origin;
Vec3 lastRayPosition = rayPos;
rayPos += rayStep;
// Linear search - Loop until find a point inside and outside the terrain Vector3
Vec3 normal;
float height = getHeight(rayPos.x, rayPos.z, &normal);
while (rayPos.y > height)
std::set<Chunk *> closeList;
Vec2 start = Vec2(ray._origin.x,ray._origin.z);
Vec2 dir = Vec2(ray._direction.x,ray._direction.z);
start = convertToTerrainSpace(start);
start.x /=(_terrainData._chunkSize.width+1);
start.y /=(_terrainData._chunkSize.height+1);
Vec2 delta = dir.getNormalized();
auto width = float(_imageWidth) / (_terrainData._chunkSize.width + 1);
auto height = float(_imageHeight) / (_terrainData._chunkSize.height + 1);
bool hasIntersect = false;
float intersectionDist = FLT_MAX;
Vec3 tmpIntersectionPoint;
for(;;)
{
lastRayPosition = rayPos;
rayPos += rayStep;
if (normal.isZero())
{
intersectionPoint = Vec3(0, 0, 0);
return false;
int x1 = floorf(start.x);
int x2 = ceilf(start.x);
int y1 = floorf(start.y);
int y2 = ceilf(start.y);
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y++) {
auto chunk = getChunkByIndex(x, y);
if (chunk)
{
if (closeList.find(chunk) == closeList.end())
{
if (chunk->getInsterctPointWithRay(ray, tmpIntersectionPoint))
{
float dist = (ray._origin - tmpIntersectionPoint).length();
if (intersectionDist > dist)
{
hasIntersect = true;
intersectionDist = dist;
intersectionPoint = tmpIntersectionPoint;
}
}
closeList.insert(chunk);
}
}
}
}
height = getHeight(rayPos.x, rayPos.z);
if ((delta.x > 0 && start.x > width) || (delta.x <0 && start.x <0))
{
break;
}
if ((delta.y > 0 && start.y > height) || (delta.y < 0 && start.y < 0))
{
break;
}
start.x += delta.x;
start.y += delta.y;
}
Vec3 startPosition = lastRayPosition;
Vec3 endPosition = rayPos;
for (int i = 0; i < 32; i++)
{
// Binary search pass
Vec3 middlePoint = (startPosition + endPosition) * 0.5f;
if (middlePoint.y < height)
endPosition = middlePoint;
else
startPosition = middlePoint;
}
Vec3 collisionPoint = (startPosition + endPosition) * 0.5f;
intersectionPoint = collisionPoint;
return true;
return hasIntersect;
}
void Terrain::setMaxDetailMapAmount(int max_value)
@ -571,7 +563,7 @@ void Terrain::setMaxDetailMapAmount(int max_value)
_maxDetailMapValue = max_value;
}
cocos2d::Vec2 Terrain::convertToTerrainSpace(Vec2 worldSpaceXZ)
cocos2d::Vec2 Terrain::convertToTerrainSpace(Vec2 worldSpaceXZ) const
{
Vec2 pos(worldSpaceXZ.x,worldSpaceXZ.y);
@ -648,6 +640,12 @@ std::vector<float> Terrain::getHeightData() const
return data;
}
Terrain::Chunk * cocos2d::Terrain::getChunkByIndex(int x, int y) const
{
if (x<0 || y<0 || x>= MAX_CHUNKES || y >= MAX_CHUNKES) return nullptr;
return _chunkesArray[y][x];
}
void Terrain::setAlphaMap(cocos2d::Texture2D * newAlphaMapTexture)
{
_alphaMap->release();
@ -997,6 +995,19 @@ void Terrain::Chunk::generate(int imgWidth, int imageHei, int m, int n, const un
}
break;
}
//store triangle:
for (int i = 0; i < _size.height; i++)
{
for (int j = 0; j < _size.width; j++)
{
int nLocIndex = i * (_size.width + 1) + j;
Triangle a(_originalVertices[nLocIndex]._position, _originalVertices[nLocIndex + 1 * (_size.width + 1)]._position, _originalVertices[nLocIndex + 1]._position);
Triangle b(_originalVertices[nLocIndex + 1]._position, _originalVertices[nLocIndex + 1 * (_size.width + 1)]._position, _originalVertices[nLocIndex + 1 * (_size.width + 1) + 1]._position);
_trianglesList.push_back(a);
_trianglesList.push_back(b);
}
}
calculateAABB();
finish();
@ -1255,6 +1266,31 @@ void Terrain::Chunk::calculateSlope()
_slope = (highest.y - lowest.y)/dist;
}
bool Terrain::Chunk::getInsterctPointWithRay(const Ray& ray, Vec3 &interscetPoint)
{
if (!ray.intersects(_aabb))
return false;
float minDist = FLT_MAX;
bool isFind = false;
for (auto triangle : _trianglesList)
{
Vec3 p;
if (triangle.getInsterctPoint(ray, p))
{
float dist = ray._origin.distance(p);
if (dist<minDist)
{
interscetPoint = p;
minDist = dist;
}
isFind =true;
}
}
return isFind;
}
void Terrain::Chunk::updateVerticesForLOD()
{
if(_oldLod == _currentLod){ return;} // no need to update vertices
@ -1413,6 +1449,11 @@ Terrain::QuadTree::QuadTree(int x, int y, int w, int h, Terrain * terrain)
_isTerminal = true;
_localAABB = _chunk->_aabb;
_chunk->_parent = this;
for (auto & triangle : _chunk->_trianglesList)
{
triangle.transform(_terrain->getNodeToWorldTransform());
}
}
_worldSpaceAABB = _localAABB;
_worldSpaceAABB.transform(_terrain->getNodeToWorldTransform());
@ -1539,4 +1580,78 @@ Terrain::DetailMap::DetailMap()
_detailMapSize = 35;
}
Terrain::Triangle::Triangle(Vec3 p1, Vec3 p2, Vec3 p3)
{
_p1 = p1;
_p2 = p2;
_p3 = p3;
}
void Terrain::Triangle::transform(cocos2d::Mat4 matrix)
{
matrix.transformPoint(&_p1);
matrix.transformPoint(&_p2);
matrix.transformPoint(&_p3);
}
//Please refer to 3D Math Primer for Graphics and Game Development
bool Terrain::Triangle::getInsterctPoint(const Ray &ray, Vec3& interScetPoint)const
{
// E1
Vec3 E1 = _p2 - _p1;
// E2
Vec3 E2 = _p3 - _p1;
// P
Vec3 P;
Vec3::cross(ray._direction,E2,&P);
// determinant
float det = E1.dot(P);
// keep det > 0, modify T accordingly
Vec3 T;
if (det > 0)
{
T = ray._origin - _p1;
}
else
{
T = _p1 - ray._origin;
det = -det;
}
// If determinant is near zero, ray lies in plane of triangle
if (det < 0.0001f)
return false;
float t; // ray dist
float u,v;//barycentric coordinate
// Calculate u and make sure u <= 1
u = T.dot(P);
if (u < 0.0f || u > det)
return false;
// Q
Vec3 Q;
Vec3::cross(T,E1,&Q);
// Calculate v and make sure u + v <= 1
v = ray._direction.dot(Q);
if (v < 0.0f || u + v > det)
return false;
// Calculate t, scale parameters, ray intersects triangle
t = E2.dot(Q);
float fInvDet = 1.0f / det;
t *= fInvDet;
u *= fInvDet;
v *= fInvDet;
interScetPoint = ray._origin + ray._direction * t;
return true;
}
NS_CC_END

View File

@ -109,6 +109,18 @@ public:
float _detailMapSize;
};
/**
* Triangle
*/
struct Triangle
{
Triangle(Vec3 p1, Vec3 p2, Vec3 p3);
bool getInsterctPoint(const Ray &ray, Vec3& interScetPoint) const;
void transform(Mat4 matrix);
Vec3 _p1, _p2, _p3;
};
/**
*TerrainData
*This TerrainData struct warp all parameter that Terrain need to create
@ -219,6 +231,9 @@ private:
/**calculate the average slop of chunk*/
void calculateSlope();
bool getInsterctPointWithRay(const Ray& ray, Vec3 &interscetPoint);
/**current LOD of the chunk*/
int _currentLod;
@ -244,6 +259,8 @@ private:
/**chunk's estimated slope*/
float _slope;
std::vector<TerrainVertexData> _currentVertices;
std::vector<Triangle> _trianglesList;
};
/**
@ -362,7 +379,7 @@ public:
/**
* Convert a world Space position (X,Z) to terrain space position (X,Z)
*/
Vec2 convertToTerrainSpace(Vec2 worldSpace);
Vec2 convertToTerrainSpace(Vec2 worldSpace) const;
/**
* reset the heightmap data.
@ -444,6 +461,8 @@ protected:
ChunkIndices insertIndicesLOD(int neighborLod[4], int selfLod, GLushort * indices, int size);
ChunkIndices insertIndicesLODSkirt(int selfLod, GLushort * indices, int size);
Chunk * getChunkByIndex(int x,int y) const;
protected:
std::vector <ChunkLODIndices> _chunkLodIndicesSet;

View File

@ -161,34 +161,13 @@ void TerrainWalkThru::onTouchesEnd(const std::vector<cocos2d::Touch*>& touches,
_camera->unproject(size, &farP, &farP);
Vec3 dir = farP - nearP;
dir.normalize();
Vec3 rayStep = 15*dir;
Vec3 rayPos = nearP;
Vec3 rayStartPosition = nearP;
Vec3 lastRayPosition =rayPos;
rayPos += rayStep;
// Linear search - Loop until find a point inside and outside the terrain Vector3
float height = _terrain->getHeight(rayPos.x,rayPos.z);
while (rayPos.y > height)
Vec3 collisionPoint(-999,-999,-999);
bool isInTerrain = _terrain->getIntersectionPoint(Ray(nearP, dir), collisionPoint);
if (!isInTerrain)
{
lastRayPosition = rayPos;
rayPos += rayStep;
height = _terrain->getHeight(rayPos.x,rayPos.z);
}
Vec3 startPosition = lastRayPosition;
Vec3 endPosition = rayPos;
for (int i= 0; i< 32; i++)
{
// Binary search pass
Vec3 middlePoint = (startPosition + endPosition) * 0.5f;
if (middlePoint.y < height)
endPosition = middlePoint;
else
startPosition = middlePoint;
}
Vec3 collisionPoint = (startPosition + endPosition) * 0.5f;
_player->idle();
return;
}
dir = collisionPoint - _player->getPosition3D();
dir.y = 0;
dir.normalize();
@ -252,8 +231,15 @@ void Player::update(float dt)
playerModelMat.transformPoint(&playerPos);
Vec3 Normal;
float player_h = _terrain->getHeight(playerPos.x, playerPos.z,&Normal);
player->setPositionY(player_h+PLAYER_HEIGHT);
if (Normal.isZero())//check the player whether is out of the terrain
{
player_h = playerPos.y;
}
else
{
player_h += PLAYER_HEIGHT;
}
player->setPositionY(player_h);
Quaternion q2;
q2.createFromAxisAngle(Vec3(0,1,0),(float)-M_PI,&q2);