mirror of https://github.com/axmolengine/axmol.git
Merge pull request #12495 from super626/v3
Fix terrain getIntersectionPoint
This commit is contained in:
commit
f9db471026
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue