#include "CCTerrain.h" #include USING_NS_CC; #include "renderer/CCGLProgram.h" #include "renderer/CCGLProgramCache.h" #include "renderer/CCGLProgramState.h" #include "renderer/CCRenderer.h" #include "renderer/CCGLProgramStateCache.h" #include "renderer/ccGLStateCache.h" #include "base/CCDirector.h" #include "2d/CCCamera.h" #include NS_CC_BEGIN Terrain * Terrain::create(TerrainData ¶meter, CrackFixedType fixedType) { Terrain * terrain = new (std::nothrow)Terrain(); terrain->setSkirtHeightRatio(1.0f); terrain->_terrainData = parameter; terrain->_crackFixedType = fixedType; terrain->_isCameraViewChanged = true; //chunksize terrain->_chunkSize = parameter.chunkSize; //heightmap terrain->initHeightMap(parameter.heightMapSrc.c_str()); Texture2D::TexParams texParam; texParam.wrapS = GL_REPEAT; texParam.wrapT = GL_REPEAT; if(!parameter.alphaMapSrc) { auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(parameter.detailMaps[0].detailMapSrc); auto texture = new (std::nothrow)Texture2D(); texture->initWithImage(textImage); texture->generateMipmap(); terrain->_detailMapTextures[0] = texture; texParam.minFilter = GL_LINEAR_MIPMAP_LINEAR; texParam.magFilter = GL_LINEAR_MIPMAP_LINEAR; texture->setTexParameters(texParam); delete textImage; }else { //alpha map auto image = new (std::nothrow)Image(); image->initWithImageFile(parameter.alphaMapSrc); terrain->_alphaMap = new (std::nothrow)Texture2D(); terrain->_alphaMap->initWithImage(image); texParam.wrapS = GL_CLAMP_TO_EDGE; texParam.wrapT = GL_CLAMP_TO_EDGE; texParam.minFilter = GL_LINEAR; texParam.magFilter = GL_LINEAR; terrain->_alphaMap->setTexParameters(texParam); delete image; for(int i =0;i<4;i++) { auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(parameter.detailMaps[i].detailMapSrc); auto texture = new (std::nothrow)Texture2D(); texture->initWithImage(textImage); delete textImage; texture->generateMipmap(); terrain->_detailMapTextures[i] = texture; texParam.wrapS = GL_REPEAT; texParam.wrapT = GL_REPEAT; texParam.minFilter = GL_LINEAR_MIPMAP_LINEAR; texParam.magFilter = GL_LINEAR_MIPMAP_LINEAR; texture->setTexParameters(texParam); } } terrain->init(); terrain->setAnchorPoint(Vec2(0,0)); terrain->autorelease(); return terrain; } bool Terrain::init() { _lodDistance[0]=64; _lodDistance[1]=128; _lodDistance[2]=196; auto shader = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_3D_TERRAIN); auto state = GLProgramState::create(shader); setGLProgramState(state); _normalLocation = glGetAttribLocation(this->getGLProgram()->getProgram(),"a_normal"); setDrawWire(false); setIsEnableFrustumCull(true); _alphaMapLocation = -1; for(int i =0;i<4;i++) { _detailMapLocation[i] = -1; _detailMapSizeLocation[i] = -1; } return true; } void Terrain::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) { _customCommand.func = CC_CALLBACK_0(Terrain::onDraw, this, transform, flags); renderer->addCommand(&_customCommand); } void Terrain::onDraw(const Mat4 &transform, uint32_t flags) { auto glProgram = getGLProgram(); glProgram->use(); glProgram->setUniformsForBuiltins(transform); glDepthMask(GL_TRUE); GLboolean depthTestCheck; glGetBooleanv(GL_DEPTH_TEST,&depthTestCheck); if(!depthTestCheck) { glEnable(GL_DEPTH_TEST); } GLboolean blendCheck; glGetBooleanv(GL_BLEND,&blendCheck); if(blendCheck) { glDisable(GL_BLEND); } if(!_alphaMap) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,_detailMapTextures[0]->getName()); glUniform1i(_detailMapLocation[0],0); glUniform1i(_alphaIsHasAlphaMapLocation,0); }else { for(int i =0;i<_maxDetailMapValue;i++) { glActiveTexture(GL_TEXTURE0+i); glBindTexture(GL_TEXTURE_2D,_detailMapTextures[i]->getName()); glUniform1i(_detailMapLocation[i],i); glUniform1f(_detailMapSizeLocation[i],_terrainData.detailMaps[i].detailMapSize); } glUniform1i(_alphaIsHasAlphaMapLocation,1); glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D,_alphaMap->getName()); glUniform1i(_alphaMapLocation,4); } auto camera = Camera::getVisitingCamera(); if(memcmp(&_CameraMatrix,&camera->getViewMatrix(),sizeof(Mat4))!=0) { _isCameraViewChanged = true; _CameraMatrix = camera->getViewMatrix(); } if(_isCameraViewChanged ) { auto camPos = camera->getPosition3D(); //set lod setChunksLOD(camPos); } if(_isCameraViewChanged ) { _quadRoot->resetNeedDraw(true);//reset it //camera frustum culling _quadRoot->cullByCamera(camera,_terrainModelMatrix); } _quadRoot->draw(); if(_isCameraViewChanged) { _isCameraViewChanged = false; } glActiveTexture(GL_TEXTURE0); if(depthTestCheck) { glEnable(GL_DEPTH_TEST); }else { glDisable(GL_DEPTH_TEST); } if(blendCheck) { glEnable(GL_BLEND); } } void Terrain::initHeightMap(const char * heightMap) { _heightMapImage = new Image(); _heightMapImage->initWithImageFile(heightMap); _data = _heightMapImage->getData(); _imageWidth =_heightMapImage->getWidth(); _imageHeight =_heightMapImage->getHeight(); int chunk_amount_y = _imageHeight/_chunkSize.height; int chunk_amount_x = _imageWidth/_chunkSize.width; loadVertices(); calculateNormal(); memset(_chunkesArray, 0, sizeof(_chunkesArray)); for(int m =0;m_terrain = this; _chunkesArray[m][n]->_size = _chunkSize; _chunkesArray[m][n]->generate(_imageWidth,_imageHeight,m,n,_data); } } //calculate the neighbor for(int m =0;m=0) _chunkesArray[m][n]->left = _chunkesArray[m][n-1]; if(n+1right = _chunkesArray[m][n+1]; if(m-1>=0) _chunkesArray[m][n]->back = _chunkesArray[m-1][n]; if(m+1front = _chunkesArray[m+1][n]; } } _quadRoot = new QuadTree(0,0,_imageWidth,_imageHeight,this); } Terrain::Terrain() { _alphaMap = nullptr; } void Terrain::setChunksLOD(Vec3 cameraPos) { int chunk_amount_y = _imageHeight/_chunkSize.height; int chunk_amount_x = _imageWidth/_chunkSize.width; for(int m=0;m_parent->_worldSpaceAABB; auto center = aabb.getCenter(); float dist = Vec3(center.x,0,center.z).distance(Vec3(cameraPos.x,0,cameraPos.z)); _chunkesArray[m][n]->_currentLod = 3; for(int i =0;i<3;i++) { if(dist<=_lodDistance[i]) { _chunkesArray[m][n]->_currentLod = i; break; } } } } float Terrain::getHeight(float x, float z, Vec3 * normal) { Vec2 pos = Vec2(x,z); //top-left Vec2 tl = Vec2(-1*_terrainData.mapScale*_imageWidth/2,-1*_terrainData.mapScale*_imageHeight/2); auto result = getNodeToWorldTransform()*Vec4(tl.x,0.0f,tl.y,1.0f); tl = Vec2(result.x,result.z); Vec2 to_tl = pos - tl; //real size Vec2 size = Vec2(_imageWidth*_terrainData.mapScale,_imageHeight*_terrainData.mapScale); result = getNodeToWorldTransform()*Vec4(size.x,0.0f,size.y,0.0f); size = Vec2(result.x,result.z); float width_ratio = to_tl.x/size.x; float height_ratio = to_tl.y/size.y; float image_x = width_ratio * _imageWidth; float image_y = height_ratio * _imageHeight; float u =image_x - (int)image_x; float v =image_y - (int)image_y; float i = (int)image_x; float j = (int)image_y; if(image_x>=_imageWidth-1 || image_y >=_imageHeight-1 || image_x<0 || image_y<0) { return 0; }else { float a = getImageHeight(i,j)*getScaleY(); float b = getImageHeight(i,j+1)*getScaleY(); float c = getImageHeight(i+1,j)*getScaleY(); float d = getImageHeight(i+1,j+1)*getScaleY(); if(normal) { normal->x = c - b; normal->y = 2; normal->z = d - a; normal->normalize(); //(*normal) = (1-u)*(1-v)*getNormal(i,j)+ (1-u)*v*getNormal(i,j+1) + u*(1-v)*getNormal(i+1,j)+ u*v*getNormal(i+1,j+1); } float reuslt = (1-u)*(1-v)*getImageHeight(i,j)*getScaleY() + (1-u)*v*getImageHeight(i,j+1)*getScaleY() + u*(1-v)*getImageHeight(i+1,j)*getScaleY() + u*v*getImageHeight(i+1,j+1)*getScaleY(); return reuslt; } } float Terrain::getHeight(Vec2 pos, Vec3*Normal) { return getHeight(pos.x,pos.y,Normal); } float Terrain::getImageHeight(int pixel_x,int pixel_y) { int byte_stride =1; switch (_heightMapImage->getRenderFormat()) { case Texture2D::PixelFormat::BGRA8888: byte_stride = 4; break; case Texture2D::PixelFormat::RGB888: byte_stride =3; break; case Texture2D::PixelFormat::I8: byte_stride =1; break; default: break; } return _data[(pixel_y*_imageWidth+pixel_x)*byte_stride]*1.0/255*_terrainData.mapHeight -0.5*_terrainData.mapHeight; } void Terrain::loadVertices() { _maxHeight = -99999; _minHeight = 99999; for(int i =0;i<_imageHeight;i++) { for(int j =0;j<_imageWidth;j++) { float height = getImageHeight(j,i); TerrainVertexData v; v.position = Vec3(j*_terrainData.mapScale- _imageWidth/2*_terrainData.mapScale, //x height, //y i*_terrainData.mapScale - _imageHeight/2*_terrainData.mapScale);//z v.texcoord = Tex2F(j*1.0/_imageWidth,i*1.0/_imageHeight); _vertices.push_back (v); //update the min & max height; if(height>_maxHeight) _maxHeight = height; if(height<_minHeight) _minHeight = height; } } } void Terrain::calculateNormal() { _indices.clear(); //we generate whole terrain indices(global indices) for correct normal calculate for(int i =0;i<_imageHeight-1;i+=1) { for(int j = 0;j<_imageWidth-1;j+=1) { int nLocIndex = i * _imageWidth + j; _indices.push_back (nLocIndex); _indices.push_back (nLocIndex + _imageWidth); _indices.push_back (nLocIndex + 1); _indices.push_back (nLocIndex + 1); _indices.push_back (nLocIndex + _imageWidth); _indices.push_back (nLocIndex + _imageWidth+1); } } for (unsigned int i = 0 ; i < _indices.size() ; i += 3) { unsigned int Index0 = _indices[i]; unsigned int Index1 = _indices[i + 1]; unsigned int Index2 = _indices[i + 2]; Vec3 v1 = _vertices[Index1].position - _vertices[Index0].position; Vec3 v2 = _vertices[Index2].position - _vertices[Index0].position; Vec3 Normal; Vec3::cross(v1,v2,&Normal); Normal.normalize(); _vertices[Index0].normal += Normal; _vertices[Index1].normal += Normal; _vertices[Index2].normal += Normal; } for (unsigned int i = 0 ; i < _vertices.size() ; i++) { _vertices[i].normal.normalize(); } //global indices no need at all _indices.clear(); } void Terrain::setDrawWire(bool bool_value) { _isDrawWire = bool_value; } void Terrain::setLODDistance(float lod_1, float lod_2, float lod_3) { _lodDistance[0] = lod_1; _lodDistance[1] = lod_2; _lodDistance[2] = lod_3; } void Terrain::setIsEnableFrustumCull(bool bool_value) { _isEnableFrustumCull = bool_value; } Terrain::~Terrain() { _alphaMap->release(); _heightMapImage->release(); delete _quadRoot; for(int i=0;i<4;i++) { if(_detailMapTextures[i]) { _detailMapTextures[i]->release(); } } for(int i = 0;i height) { lastRayPosition = rayPos; rayPos += rayStep; 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; } void Terrain::setMaxDetailMapAmount(int max_value) { _maxDetailMapValue = max_value; } cocos2d::Vec2 Terrain::convertToTerrainSpace(Vec2 worldSpaceXZ) { Vec2 pos = Vec2(worldSpaceXZ.x,worldSpaceXZ.y); //top-left Vec2 tl = Vec2(-1*_terrainData.mapScale*_imageWidth/2,-1*_terrainData.mapScale*_imageHeight/2); auto result = getNodeToWorldTransform()*Vec4(tl.x,0.0f,tl.y,1.0f); tl = Vec2(result.x,result.z); Vec2 to_tl = pos - tl; //real size Vec2 size = Vec2(_imageWidth*_terrainData.mapScale,_imageHeight*_terrainData.mapScale); result = getNodeToWorldTransform()*Vec4(size.x,0.0f,size.y,0.0f); size = Vec2(result.x,result.z); float width_ratio = to_tl.x/size.x; float height_ratio = to_tl.y/size.y; float image_x = width_ratio * _imageWidth; float image_y = height_ratio * _imageHeight; return Vec2(image_x,image_y); } void Terrain::resetHeightMap(const char * heightMap) { _heightMapImage->release(); _vertices.clear(); free(_data); for(int i = 0;i_worldSpaceAABB; } Terrain::QuadTree * Terrain::getQuadTree() { return _quadRoot; } void Terrain::setAlphaMap(cocos2d::Texture2D * newAlphaMapTexture) { _alphaMap->release(); _alphaMap = newAlphaMapTexture; } void Terrain::setDetailMap(unsigned int index, DetailMap detailMap) { if(index>4) { CCLOG("invalid DetailMap index %d\n",index); } _terrainData.detailMaps[index] = detailMap; if(_detailMapTextures[index]) { _detailMapTextures[index]->release(); } _detailMapTextures[index] = new (std::nothrow)Texture2D(); auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(detailMap.detailMapSrc); _detailMapTextures[index]->initWithImage(textImage); } Terrain::ChunkIndices Terrain::lookForIndicesLOD(int neighborLod[4], int selfLod, bool * result) { (* result) =false; ChunkIndices tmp; tmp.indices = 0; tmp.size = 0; if(_chunkLodIndicesSet.empty()) { (* result) =false; return tmp; }else { int test[5]; memcpy(test,neighborLod,sizeof(int [4])); test[4] = selfLod; for(int i =0;i<_chunkLodIndicesSet.size();i++) { if(memcmp(test,_chunkLodIndicesSet[i].relativeLod,sizeof(test))==0) { (*result) = true; return _chunkLodIndicesSet[i]._chunkIndices; } } } (* result) =false; return tmp; } Terrain::ChunkIndices Terrain::insertIndicesLOD(int neighborLod[4], int selfLod, GLushort * indices,int size) { ChunkLODIndices lodIndices; memcpy(lodIndices.relativeLod,neighborLod,sizeof(int [4])); lodIndices.relativeLod[4] = selfLod; lodIndices._chunkIndices.size = size; glGenBuffers(1,&(lodIndices._chunkIndices.indices)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lodIndices._chunkIndices.indices); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*size,indices,GL_STATIC_DRAW); this->_chunkLodIndicesSet.push_back(lodIndices); return lodIndices._chunkIndices; } Terrain::ChunkIndices Terrain::lookForIndicesLODSkrit(int selfLod, bool * result) { ChunkIndices badResult; badResult.indices = 0; badResult.size = 0; if(this->_chunkLodIndicesSkirtSet.empty()) { (*result) = false; return badResult; } for(int i =0;i<_chunkLodIndicesSkirtSet.size();i++) { if(_chunkLodIndicesSkirtSet[i].selfLod == selfLod) { (*result) = true; return _chunkLodIndicesSkirtSet[i]._chunkIndices; } } (*result) = false; return badResult; } Terrain::ChunkIndices Terrain::insertIndicesLODSkirt(int selfLod, GLushort * indices, int size) { ChunkLODIndicesSkirt skirtIndices; skirtIndices.selfLod = selfLod; skirtIndices._chunkIndices.size = size; glGenBuffers(1,&(skirtIndices._chunkIndices.indices)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, skirtIndices._chunkIndices.indices); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*size,indices,GL_STATIC_DRAW); this->_chunkLodIndicesSkirtSet.push_back(skirtIndices); return skirtIndices._chunkIndices; } void Terrain::setSkirtHeightRatio(float ratio) { _skirtRatio = ratio; } void Terrain::onEnter() { Node::onEnter(); _terrainModelMatrix = getNodeToWorldTransform(); _quadRoot->preCalculateAABB(_terrainModelMatrix); cacheUniformLocation(); } void Terrain::cacheUniformLocation() { auto glProgram = getGLProgram(); _alphaIsHasAlphaMapLocation = glGetUniformLocation(glProgram->getProgram(),"u_has_alpha"); if(!_alphaMap) { _detailMapLocation[0] = glGetUniformLocation(glProgram->getProgram(),"u_texture0"); }else { for(int i =0;i<_maxDetailMapValue;i++) { char str[20]; sprintf(str,"u_texture%d",i); _detailMapLocation[i] = glGetUniformLocation(glProgram->getProgram(),str); sprintf(str,"u_detailSize[%d]",i); _detailMapSizeLocation[i] = glGetUniformLocation(glProgram->getProgram(),str); } _alphaMapLocation = glGetUniformLocation(glProgram->getProgram(),"u_alphaMap"); } } void Terrain::Chunk::finish() { //genearate two VBO ,the first for vertices, we just setup datas once ,won't changed at all //the second vbo for the indices, because we use level of detail technique to each chunk, so we will modified frequently glGenBuffers(2,vbo); //only set for vertices vbo glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(TerrainVertexData)*vertices.size(), &vertices[0], GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER,0); calculateSlope(); for(int i =0;i<4;i++) { int step = int(powf(2.0f, float(_currentLod))); int indicesAmount =(_terrain->_chunkSize.width/step+1)*(_terrain->_chunkSize.height/step+1)*6+(_terrain->_chunkSize.height/step)*3*2 +(_terrain->_chunkSize.width/step)*3*2; _lod[i].indices.reserve(indicesAmount); } } void Terrain::Chunk::bindAndDraw() { #if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) if(_terrain->_isDrawWire) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); }else { glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } #endif glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); if(_terrain->_isCameraViewChanged) { switch (_terrain->_crackFixedType) { case CrackFixedType::SKIRT: updateIndicesLODSkirt(); break; case CrackFixedType::INCREASE_LOWER: updateVerticesForLOD(); updateIndicesLOD(); break; default: break; } } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_chunkIndices.indices); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_TEX_COORD| GL::VERTEX_ATTRIB_FLAG_NORMAL); unsigned long offset = 0; //position glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(TerrainVertexData), (GLvoid *)offset); offset +=sizeof(Vec3); //texcoord glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD,2,GL_FLOAT,GL_FALSE,sizeof(TerrainVertexData),(GLvoid *)offset); offset +=sizeof(Tex2F); glEnableVertexAttribArray(_terrain->_normalLocation); //normal glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_NORMAL,3,GL_FLOAT,GL_FALSE,sizeof(TerrainVertexData),(GLvoid *)offset); glDrawElements(GL_TRIANGLES, (GLsizei)_chunkIndices.size, GL_UNSIGNED_SHORT, 0); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _chunkIndices.size); #if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); #endif } void Terrain::Chunk::generate(int imgWidth, int imageHei, int m, int n, const unsigned char * data) { pos_y = m; pos_x = n; switch (_terrain->_crackFixedType) { case CrackFixedType::SKIRT: { for(int i=_size.height*m;i<=_size.height*(m+1);i++) { if(i>=imageHei) break; for(int j=_size.width*n;j<=_size.width*(n+1);j++) { if(j>=imgWidth)break; auto v =_terrain->_vertices[i*imgWidth + j]; vertices.push_back (v); } } // add four skirts float skirtHeight = _terrain->_skirtRatio *_terrain->_terrainData.mapScale*int(powf(2.0f, float(3))); //#1 _terrain->_skirtVerticesOffset[0] = (int)vertices.size(); for(int i =_size.height*m;i<=_size.height*(m+1);i++) { auto v = _terrain->_vertices[i*imgWidth +_size.width*(n+1)]; v.position.y -= skirtHeight; vertices.push_back (v); } //#2 _terrain->_skirtVerticesOffset[1] = (int)vertices.size(); for(int j =_size.width*n;j<=_size.width*(n+1);j++) { auto v = _terrain->_vertices[_size.height*(m+1)*imgWidth + j]; v.position.y -=skirtHeight; vertices.push_back (v); } //#3 _terrain->_skirtVerticesOffset[2] = (int)vertices.size(); for(int i =_size.height*m;i<=_size.height*(m+1);i++) { auto v = _terrain->_vertices[i*imgWidth + _size.width*n]; v.position.y -= skirtHeight; vertices.push_back (v); } //#4 _terrain->_skirtVerticesOffset[3] = (int)vertices.size(); for(int j =_size.width*n;j<=_size.width*(n+1);j++) { auto v = _terrain->_vertices[_size.height*m*imgWidth+j]; v.position.y -= skirtHeight; //v.position.y = -5; vertices.push_back (v); } } break; case CrackFixedType::INCREASE_LOWER: { for(int i=_size.height*m;i<=_size.height*(m+1);i++) { if(i>=imageHei) break; for(int j=_size.width*n;j<=_size.width*(n+1);j++) { if(j>=imgWidth)break; auto v =_terrain->_vertices[i*imgWidth + j]; vertices.push_back (v); } } } break; } calculateAABB(); finish(); } Terrain::Chunk::Chunk() { _currentLod = 0; left = nullptr; right = nullptr; back = nullptr; front = nullptr; _oldLod = -1; for(int i =0;i<4;i++) { _neighborOldLOD[i] = -1; } } void Terrain::Chunk::updateIndicesLOD() { int currentNeighborLOD[4]; if(left) { currentNeighborLOD[0] = left->_currentLod; }else{currentNeighborLOD[0] = -1;} if(right) { currentNeighborLOD[1] = right->_currentLod; }else{currentNeighborLOD[1] = -1;} if(back) { currentNeighborLOD[2] = back->_currentLod; }else{currentNeighborLOD[2] = -1;} if(front) { currentNeighborLOD[3] = front->_currentLod; }else{currentNeighborLOD[3] = -1;} if(_oldLod == _currentLod &&(memcmp(currentNeighborLOD,_neighborOldLOD,sizeof(currentNeighborLOD))==0) ) { return;// no need to update } bool isOk; _chunkIndices = _terrain->lookForIndicesLOD(currentNeighborLOD,_currentLod,&isOk); if(isOk) { return; } memcpy(_neighborOldLOD,currentNeighborLOD,sizeof(currentNeighborLOD)); _oldLod = _currentLod; int gridY = _size.height; int gridX = _size.width; int step = int(powf(2.0f, float(_currentLod))); if((left&&left->_currentLod > _currentLod) ||(right&&right->_currentLod > _currentLod) ||(back&&back->_currentLod > _currentLod) || (front && front->_currentLod > _currentLod)) //need update indices. { //t-junction inner _lod[_currentLod].indices.clear(); for(int i =step;i_currentLod > _currentLod)//left { for(int i =0;i_currentLod > _currentLod) end -=step; if(back&&back->_currentLod > _currentLod) start +=step; for(int i =start;i_currentLod > _currentLod)//LEFT { for(int i =0;i_currentLod > _currentLod) end -=step; if(back&&back->_currentLod > _currentLod) start +=step; for(int i =start;i_currentLod > _currentLod)//front { for(int i =0;i_currentLod > _currentLod)//back { for(int i =0;iinsertIndicesLOD(currentNeighborLOD,_currentLod,&_lod[_currentLod].indices[0],(int)_lod[_currentLod].indices.size()); }else{ //No lod difference, use simple method _lod[_currentLod].indices.clear(); for(int i =0;iinsertIndicesLOD(currentNeighborLOD,_currentLod,&_lod[_currentLod].indices[0],(int)_lod[_currentLod].indices.size()); } } void Terrain::Chunk::calculateAABB() { std::vectorpos; for(int i =0;i highest.y) { highest = vertices[i].position; } } auto a = Vec2(lowest.x,lowest.z); auto b = Vec2(highest.x,highest.z); float dist = a.distance(b); slope = (highest.y - lowest.y)/dist; } void Terrain::Chunk::updateVerticesForLOD() { if(_oldLod == _currentLod){ return;} // no need to update vertices vertices_tmp = vertices; int gridY = _size.height; int gridX = _size.width; if(_currentLod>=2 && abs(slope)>1.2) { int step = int(powf(2.0f, float(_currentLod))); for(int i =step;ilookForIndicesLODSkrit(_currentLod,&isOk); if(isOk) return; int gridY = _size.height; int gridX = _size.width; int step = int(powf(2.0f, float(_currentLod))); int k =0; for(int i =0;i_skirtVerticesOffset[1] +j); _lod[_currentLod].indices.push_back (nLocIndex + step); _lod[_currentLod].indices.push_back (nLocIndex + step); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[1] +j); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[1] +j + step); } //#3 for(int i =0;i_skirtVerticesOffset[2]+i); _lod[_currentLod].indices.push_back ((i+step)*(gridX+1)); _lod[_currentLod].indices.push_back ((i+step)*(gridX+1)); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[2]+i); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[2]+i +step); } //#4 for(int j =0;j_skirtVerticesOffset[3]+j); _lod[_currentLod].indices.push_back (nLocIndex); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[3] + j + step); _lod[_currentLod].indices.push_back (_terrain->_skirtVerticesOffset[3] +j); _lod[_currentLod].indices.push_back (nLocIndex + step); } _chunkIndices = _terrain->insertIndicesLODSkirt(_currentLod,&_lod[_currentLod].indices[0], (int)_lod[_currentLod].indices.size()); } Terrain::QuadTree::QuadTree(int x, int y, int w, int h, Terrain * terrain) { _terrain = terrain; _needDraw = true; parent = nullptr; tl =nullptr; tr =nullptr; bl =nullptr; br =nullptr; pos_x = x; pos_y = y; this->height = h; this->width = w; if(width> terrain->_chunkSize.width &&height >terrain->_chunkSize.height) //subdivision { _isTerminal = false; this->tl = new QuadTree(x,y,width/2,height/2,terrain); this->tl->parent = this; this->tr = new QuadTree(x+width/2,y,width/2,height/2,terrain); this->tr->parent = this; this->bl = new QuadTree(x,y+height/2,width/2,height/2,terrain); this->bl->parent = this; this->br = new QuadTree(x+width/2,y+height/2,width/2,height/2,terrain); this->br->parent = this; _aabb.merge(tl->_aabb); _aabb.merge(tr->_aabb); _aabb.merge(bl->_aabb); _aabb.merge(br->_aabb); }else // is terminal Node { int m = pos_y/terrain->_chunkSize.height; int n = pos_x/terrain->_chunkSize.width; _chunk = terrain->_chunkesArray[m][n]; _isTerminal = true; _aabb = _chunk->_aabb; _chunk->_parent = this; } _worldSpaceAABB = _aabb; _worldSpaceAABB.transform(_terrain->getNodeToWorldTransform()); } void Terrain::QuadTree::draw() { if(!_needDraw)return; if(_isTerminal){ this->_chunk->bindAndDraw(); }else { this->tl->draw(); this->tr->draw(); this->br->draw(); this->bl->draw(); } } void Terrain::QuadTree::resetNeedDraw(bool value) { this->_needDraw = value; if(!_isTerminal) { tl->resetNeedDraw(value); tr->resetNeedDraw(value); bl->resetNeedDraw(value); br->resetNeedDraw(value); } } void Terrain::QuadTree::cullByCamera(const Camera * camera, const Mat4 & worldTransform) { if(!camera->isVisibleInFrustum(&_worldSpaceAABB)) { this->resetNeedDraw(false); }else { if(!_isTerminal){ tl->cullByCamera(camera,worldTransform); tr->cullByCamera(camera,worldTransform); bl->cullByCamera(camera,worldTransform); br->cullByCamera(camera,worldTransform); } } } void Terrain::QuadTree::preCalculateAABB(const Mat4 & worldTransform) { _worldSpaceAABB = _aabb; _worldSpaceAABB.transform(worldTransform); if(!_isTerminal){ tl->preCalculateAABB(worldTransform); tr->preCalculateAABB(worldTransform); bl->preCalculateAABB(worldTransform); br->preCalculateAABB(worldTransform); } } Terrain::QuadTree::~QuadTree() { if(tl) delete tl; if(tr) delete tr; if(bl) delete bl; if(br) delete br; } Terrain::TerrainData::TerrainData(const char * heightMapsrc , const char * textureSrc, const Size & chunksize, float height, float scale) { this->heightMapSrc = heightMapsrc; this->detailMaps[0].detailMapSrc = textureSrc; this->alphaMapSrc = nullptr; this->chunkSize = chunksize; this->mapHeight = height; this->mapScale = scale; } Terrain::TerrainData::TerrainData(const char * heightMapsrc, const char * alphamap, const DetailMap& detail1, const DetailMap& detail2, const DetailMap& detail3, const DetailMap& detail4, const Size & chunksize, float height, float scale) { this->heightMapSrc = heightMapsrc; this->alphaMapSrc = const_cast(alphamap); this->detailMaps[0] = detail1; this->detailMaps[1] = detail2; this->detailMaps[2] = detail3; this->detailMaps[3] = detail4; this->chunkSize = chunksize; this->mapHeight = height; this->mapScale = scale; _detailMapAmount = 4; } Terrain::TerrainData::TerrainData(const char* heightMapsrc, const char * alphamap, const DetailMap& detail1, const DetailMap& detail2, const DetailMap& detail3, const Size & chunksize /*= Size(32,32)*/, float height /*= 2*/, float scale /*= 0.1*/) { this->heightMapSrc = heightMapsrc; this->alphaMapSrc = const_cast(alphamap); this->detailMaps[0] = detail1; this->detailMaps[1] = detail2; this->detailMaps[2] = detail3; this->detailMaps[3] = nullptr; this->chunkSize = chunksize; this->mapHeight = height; this->mapScale = scale; _detailMapAmount = 3; } Terrain::TerrainData::TerrainData() { } Terrain::DetailMap::DetailMap(const char * detailMapPath, float size /*= 35*/) { this->detailMapSrc = detailMapPath; this->detailMapSize = size; } Terrain::DetailMap::DetailMap() { detailMapSrc = ""; detailMapSize = 35; } NS_CC_END