From 201ae6f321d401f95d58b35f3c116e2f796411de Mon Sep 17 00:00:00 2001 From: tangziwen Date: Thu, 15 Jan 2015 11:45:06 +0800 Subject: [PATCH 1/3] add terrain --- cocos/3d/CCTerrain.cpp | 801 +++++++++++++++++++++++++++++++++++++++++ cocos/3d/CCTerrain.h | 140 +++++++ 2 files changed, 941 insertions(+) create mode 100644 cocos/3d/CCTerrain.cpp create mode 100644 cocos/3d/CCTerrain.h diff --git a/cocos/3d/CCTerrain.cpp b/cocos/3d/CCTerrain.cpp new file mode 100644 index 0000000000..6947269878 --- /dev/null +++ b/cocos/3d/CCTerrain.cpp @@ -0,0 +1,801 @@ +#include "CCTerrain.h" +#include +USING_NS_CC; + +#include "renderer/CCGLProgram.h" +#include "renderer/CCGLProgramState.h" +#include "renderer/CCRenderer.h" +#include "renderer/CCGLProgramStateCache.h" +#include "renderer/ccGLStateCache.h" +#include "2d/CCCamera.h" +#include +static const char * vertex_shader = "\ +attribute vec4 a_position;\ +attribute vec2 a_texCoord;\ +attribute vec3 a_normal;\ +\n#ifdef GL_ES\n\ +varying mediump vec2 v_texCoord;\ +varying mediump vec3 v_normal;\ +\n#else\n\ +varying vec2 v_texCoord;\ +varying vec3 v_normal;\ +\n#endif\n\ +void main()\ +{\ +gl_Position = CC_MVPMatrix * a_position;\ +v_texCoord = a_texCoord;\ +v_normal = a_normal;\ +}\ +"; + +static const char * fragment_shader ="\n#ifdef GL_ES\n\ +precision lowp float;\ +\n#endif\n\ +uniform vec3 u_color;\ +varying vec2 v_texCoord;\ +varying vec3 v_normal;\ +uniform int u_has_alpha;\ +uniform sampler2D u_alphaMap;\ +uniform sampler2D u_texture0;\ +uniform sampler2D u_texture1;\ +uniform sampler2D u_texture2;\ +uniform sampler2D u_texture3;\ +uniform float u_detailSize[4];\ +void main()\ + {\ + vec3 light_direction = vec3(-1,-1,0);\ + float lightFactor = dot(-light_direction,v_normal);\ + if(u_has_alpha<=0)\ + {\ + gl_FragColor = texture2D(u_texture0, v_texCoord)*lightFactor;\ + }else\ + {\ + vec4 blendFactor =texture2D(u_alphaMap,v_texCoord);\ + vec4 color = vec4(0,0,0,0);\ + color = texture2D(u_texture0, v_texCoord*u_detailSize[0])*blendFactor.r +\ + texture2D(u_texture1, v_texCoord*u_detailSize[1])*blendFactor.g + texture2D(u_texture2, v_texCoord*u_detailSize[2])*blendFactor.b;\n\ + float grayFactor =dot(blendFactor.rgb, vec3(1, 1, 1));\ + color +=texture2D(u_texture3, v_texCoord*u_detailSize[3])*(1.0-grayFactor);\ + gl_FragColor = color*lightFactor;\ + }\ +}"; +Terrain * cocos2d::Terrain::create(TerrainData ¶meter) +{ + Terrain * obj = new (std::nothrow) Terrain(); + obj->_terrainData = parameter; + //chunksize + obj->_chunkSize = parameter.chunkSize; + //heightmap + obj->initHeightMap(parameter.heightMapSrc.c_str()); + if(!parameter.alphaMapSrc) + { + auto textImage = new Image(); + textImage->initWithImageFile(parameter.detailMaps[0].detailMapSrc); + auto texture = new Texture2D(); + texture->initWithImage(textImage); + obj->textures.push_back(texture); + obj->init(); + obj->setAnchorPoint(Vec2(0,0)); + }else + { + //alpha map + auto textImage = new Image(); + textImage->initWithImageFile(parameter.alphaMapSrc); + obj->_alphaMap = new Texture2D(); + obj->_alphaMap->initWithImage(textImage); + for(int i =0;i<4;i++) + { + auto textImage = new Image(); + textImage->initWithImageFile(parameter.detailMaps[i].detailMapSrc); + auto texture = new Texture2D(); + texture->initWithImage(textImage); + obj->textures.push_back(texture); + obj->detailSize[i] = parameter.detailMaps[i].detailMapSize; + } + obj->init(); + obj->setAnchorPoint(Vec2(0,0)); + } + return obj; +} + +bool cocos2d::Terrain::init() +{ + _lodDistance[0]=96; + _lodDistance[1]=288; + _lodDistance[2]=480; + auto shader = GLProgram::createWithByteArrays(vertex_shader,fragment_shader); + auto state = GLProgramState::create(shader); + setGLProgramState(state); + setDrawWire(false); + setIsEnableFrustumCull(true); + return true; +} + +void cocos2d::Terrain::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) +{ + _customCommand.init(getGlobalZOrder()); + _customCommand.func = CC_CALLBACK_0(Terrain::onDraw, this, transform, flags); + _customCommand.setTransparent(true); + renderer->addCommand(&_customCommand); +} + +void cocos2d::Terrain::onDraw(const Mat4 &transform, uint32_t flags) +{ + auto glProgram = getGLProgram(); + glProgram->use(); + glProgram->setUniformsForBuiltins(transform); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + if(!_alphaMap) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,textures[0]->getName()); + auto texture_location = glGetUniformLocation(glProgram->getProgram(),"u_texture0"); + glUniform1i(texture_location,0); + auto alpha_location = glGetUniformLocation(glProgram->getProgram(),"u_has_alpha"); + glUniform1i(alpha_location,0); + }else + { + for(int i =0;i<4;i++) + { + glActiveTexture(GL_TEXTURE0+i); + glBindTexture(GL_TEXTURE_2D,textures[i]->getName()); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + char str[20]; + sprintf(str,"u_texture%d",i); + auto texture_location = glGetUniformLocation(glProgram->getProgram(),str); + glUniform1i(texture_location,i); + + sprintf(str,"u_detailSize[%d]",i); + auto detailSizeLocation = glGetUniformLocation(glProgram->getProgram(),str); + glUniform1f(detailSizeLocation,detailSize[i]); + } + auto alpha_location = glGetUniformLocation(glProgram->getProgram(),"u_has_alpha"); + glUniform1i(alpha_location,1); + + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D,_alphaMap->getName()); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + auto alpha_map_location = glGetUniformLocation(glProgram->getProgram(),"u_alphaMap"); + glUniform1i(alpha_map_location,4); + } + //set lod + setChunksLOD(Camera::getVisitingCamera()->getPosition3D()); + //camera frustum culling + auto camera = Camera::getVisitingCamera(); + quad->cullByCamera(camera,getNodeToWorldTransform()); + quad->draw(); + quad->resetNeedDraw(true);//reset it + glActiveTexture(GL_TEXTURE0); +} + +void cocos2d::Terrain::initHeightMap(const char * heightMap) +{ + auto image = new Image(); + image->initWithImageFile(heightMap); + _data = image->getData(); + imageWidth =image->getWidth(); + imageHeight =image->getHeight(); + auto format = image->getRenderFormat(); + int chunk_amount_y = imageHeight/_chunkSize.height; + int chunk_amount_x = imageWidth/_chunkSize.width; + loadVertices(); + calculateNormal(); + + for(int m =0;m_terrain = this; + if(n-1>=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]; + _chunkesArray[m][n]->_size = _chunkSize; + _chunkesArray[m][n]->generate(imageWidth,imageHeight,m,n,_data); + } + } + quad = new QuadTree(0,0,imageWidth,imageHeight,this); +} + +cocos2d::Terrain::Terrain() +{ + _alphaMap = nullptr; +} + +void cocos2d::Terrain::setChunksLOD(Vec3 cameraPos) +{ + int chunk_amount_y = imageHeight/_chunkSize.height; + int chunk_amount_x = imageWidth/_chunkSize.width; + for(int m=0;m_aabb; + aabb.transform(this->getNodeToWorldTransform()); + 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 cocos2d::Terrain::getHeight(float x, float y ,float z) +{ + Vec2 pos = Vec2(x,z); + + //top-left + Vec2 tl = Vec2(-1*_terrainData.mapScale*imageWidth/2,-1*_terrainData.mapScale*imageWidth/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 y; + }else + { + 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 cocos2d::Terrain::getHeight(Vec3 pos) +{ + return getHeight(pos.x,pos.y,pos.z); +} + +float cocos2d::Terrain::getImageHeight(int pixel_x,int pixel_y) +{ + return _data[(pixel_y*imageWidth+pixel_x)*3]*1.0/255*_terrainData.mapHeight -0.5*_terrainData.mapHeight; +} + +void cocos2d::Terrain::loadVertices() +{ + for(int i =0;i_isDrawWire) + { + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + }else + { + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + } +#endif + + glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); + updateVerticesForLOD(); + updateIndices(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vbo[1]); + GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_TEX_COORD| GL::VERTEX_ATTRIB_FLAG_NORMAL); + unsigned int 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); + auto normal_location = glGetAttribLocation(_terrain->getGLProgram()->getProgram(),"a_normal"); + glEnableVertexAttribArray(normal_location); + //normal + glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_NORMAL,3,GL_FLOAT,GL_FALSE,sizeof(TerrainVertexData),(GLvoid *)offset); + glDrawElements(GL_TRIANGLES, _lod[_currentLod].indices.size(), GL_UNSIGNED_SHORT, 0); + +#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 cocos2d::Terrain::Chunk::generate(int imageWidth,int imageHeight,int m,int n,const unsigned char * data) +{ + pos_y = m; + pos_x = n; + for(int i=_size.height*m;i<=_size.height*(m+1);i++) + { + if(i>=imageHeight) break; + for(int j=_size.width*n;j<=_size.width*(n+1);j++) + { + if(j>=imageWidth)break; + auto v =_terrain->vertices[i*imageWidth + j]; + vertices.push_back (v); + } + } + calculateAABB(); + finish(); +} + +cocos2d::Terrain::Chunk::Chunk() +{ + _currentLod = 0; + left =NULL; + right =NULL; + back = NULL; + front =NULL; +} + +void cocos2d::Terrain::Chunk::updateIndices() +{ + 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;ipos; + 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 cocos2d::Terrain::Chunk::updateVerticesForLOD() +{ +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;iheight = height; + this->width = width; + 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; + } +} + +void cocos2d::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 cocos2d::Terrain::QuadTree::resetNeedDraw(bool value) +{ + this->_needDraw = value; + if(!_isTerminal) + { + tl->resetNeedDraw(value); + tr->resetNeedDraw(value); + bl->resetNeedDraw(value); + br->resetNeedDraw(value); + } +} + +void cocos2d::Terrain::QuadTree::cullByCamera(const Camera * camera,const Mat4 & worldTransform) +{ + auto aabb = _aabb; + aabb.transform(worldTransform); + if(!camera->isVisibleInFrustum(&aabb)) + { + this->resetNeedDraw(false); + }else + { + if(!_isTerminal){ + tl->cullByCamera(camera,worldTransform); + tr->cullByCamera(camera,worldTransform); + bl->cullByCamera(camera,worldTransform); + br->cullByCamera(camera,worldTransform); + } + } +} + +cocos2d::Terrain::TerrainData::TerrainData(const char * heightMapsrc ,const char * textureSrc,const Size & chunksize,float mapHeight,float mapScale) +{ + this->heightMapSrc = heightMapsrc; + this->detailMaps[0].detailMapSrc = textureSrc; + this->alphaMapSrc = NULL; + this->chunkSize = chunksize; + this->mapHeight = mapHeight; + this->mapScale = mapScale; +} + +cocos2d::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 mapHeight,float mapScale) +{ + 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 = mapHeight; + this->mapScale = mapScale; +} + +cocos2d::Terrain::TerrainData::TerrainData() +{ + +} + +cocos2d::Terrain::DetailMap::DetailMap(const char * detailMapSrc , float size /*= 35*/) +{ + this->detailMapSrc = detailMapSrc; + this->detailMapSize = size; +} + +cocos2d::Terrain::DetailMap::DetailMap() +{ + detailMapSrc = ""; + detailMapSize = 35; +} diff --git a/cocos/3d/CCTerrain.h b/cocos/3d/CCTerrain.h new file mode 100644 index 0000000000..ccef035931 --- /dev/null +++ b/cocos/3d/CCTerrain.h @@ -0,0 +1,140 @@ +#ifndef CC_TERRAIN_H +#define CC_TERRAIN_H +#include "2d/CCNode.h" +#include "renderer/CCTexture2D.h" +#include "renderer/CCCustomCommand.h" +#include "3d/CCAABB.h" +#include "2d/CCCamera.h" +#include +NS_CC_BEGIN + +#define MAX_CHUNKES 256 + +/* + *Terrain + * + **/ +class CC_DLL Terrain :public Node{ +public: + struct CC_DLL DetailMap{ + DetailMap(); + DetailMap(const char * detailMapSrc , float size = 35); + std::string detailMapSrc; + float detailMapSize; + }; + struct CC_DLL TerrainData + { + TerrainData(); + TerrainData(const char* heightMapsrc ,const char * textureSrc,const Size & chunksize = Size(32,32),float mapHeight = 2,float mapScale = 0.1); + TerrainData(const char* heightMapsrc ,const char * alphamap,const DetailMap& detail1,const DetailMap& detail2,const DetailMap& detail3,const DetailMap& detail4,const Size & chunksize = Size(32,32),float mapHeight = 2,float mapScale = 0.1); + Size chunkSize; + std::string heightMapSrc; + char* alphaMapSrc; + DetailMap detailMaps[4]; + float mapHeight; + float mapScale; + }; +private: + struct TerrainVertexData + { + TerrainVertexData(){}; + TerrainVertexData(Vec3 v1 ,Tex2F v2) + { + position = v1; + texcoord = v2; + }; + cocos2d::Vec3 position; + cocos2d::Tex2F texcoord; + cocos2d::Vec3 normal; + }; + struct Chunk + { + Chunk(); + std::vector vertices; + + struct LOD{ + std::vector indices; + }; + GLuint vbo[2]; + LOD _lod[4]; + AABB _aabb; + void generate(int map_width,int map_height,int m,int n,const unsigned char * data); + void calculateAABB(); + void bindAndDraw(); + void finish(); + void updateVerticesForLOD(); + void calculateSlope(); + int _currentLod; + void updateIndices(); + Chunk * left; + Chunk * right; + Chunk * front; + Chunk * back; + int pos_x; + int pos_y; + Terrain * _terrain; + Size _size; + std::vector vertices_tmp; + float slope; + }; + struct QuadTree + { + QuadTree(int x,int y,int width,int height,Terrain * terrain); + void draw(); + void resetNeedDraw(bool value); + void cullByCamera(const Camera * camera,const Mat4 & worldTransform); + QuadTree * tl; + QuadTree * tr; + QuadTree * bl; + QuadTree * br; + bool _isTerminal; + Chunk * _chunk; + int pos_x; + int pos_y; + int height; + int width; + QuadTree * parent; + AABB _aabb; + bool _needDraw; + }; + friend QuadTree; + friend Chunk; +public: + bool init(); + void initHeightMap(const char* heightMap); + static Terrain * create(TerrainData ¶meter); + // Overrides, internal use only + virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4 &transform, uint32_t flags) override; + float getHeight(float x,float y, float z); + float getHeight(Vec3 pos); + float getImageHeight(int pixel_x,int pixel_y); + void setDrawWire(bool bool_value); + void setLODDistance(float lod_1,float lod_2,float lod_3); + void setIsEnableFrustumCull(bool bool_value); +private: + Terrain(); + void onDraw(const Mat4 &transform, uint32_t flags); + void setChunksLOD(Vec3 cameraPos); + void loadVertices(); + void calculateNormal(); +private: + TerrainData _terrainData; + bool _isDrawWire; + unsigned char * _data; + float _lodDistance[3]; + std::vectortextures; + Texture2D * _alphaMap; + CustomCommand _customCommand; + GLuint vbo[2]; + QuadTree * quad; + int detailSize[4]; + Chunk * _chunkesArray[256][256]; + std::vector vertices; + std::vector indices; + int imageWidth; + int imageHeight; + Size _chunkSize; + bool _isEnableFrustumCull; +}; +NS_CC_END +#endif From 804739b38aa79257706e55bf8ce2775fd06b95db Mon Sep 17 00:00:00 2001 From: tangziwen Date: Thu, 15 Jan 2015 11:45:51 +0800 Subject: [PATCH 2/3] add cmake --- cocos/3d/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocos/3d/Android.mk b/cocos/3d/Android.mk index 9710c56be0..9dd07d8479 100644 --- a/cocos/3d/Android.mk +++ b/cocos/3d/Android.mk @@ -21,8 +21,8 @@ CCMeshVertexIndexData.cpp \ CCSprite3DMaterial.cpp \ CCObjLoader.cpp \ CCSkeleton3D.cpp \ -CCSprite3D.cpp - +CCSprite3D.cpp \ +CCTerrain.cpp LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. From 130c18bde01b4a91e4e848e02f5b6b8ace670e5a Mon Sep 17 00:00:00 2001 From: tangziwen Date: Thu, 15 Jan 2015 15:54:12 +0800 Subject: [PATCH 3/3] fix LOD indices capacity,refactor the code ,add some comments --- cocos/3d/CCTerrain.cpp | 42 +++++++++------ cocos/3d/CCTerrain.h | 115 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 131 insertions(+), 26 deletions(-) diff --git a/cocos/3d/CCTerrain.cpp b/cocos/3d/CCTerrain.cpp index 6947269878..ec22196202 100644 --- a/cocos/3d/CCTerrain.cpp +++ b/cocos/3d/CCTerrain.cpp @@ -61,7 +61,7 @@ void main()\ }"; Terrain * cocos2d::Terrain::create(TerrainData ¶meter) { - Terrain * obj = new (std::nothrow) Terrain(); + Terrain * obj = new (std::nothrow)Terrain(); obj->_terrainData = parameter; //chunksize obj->_chunkSize = parameter.chunkSize; @@ -69,9 +69,9 @@ Terrain * cocos2d::Terrain::create(TerrainData ¶meter) obj->initHeightMap(parameter.heightMapSrc.c_str()); if(!parameter.alphaMapSrc) { - auto textImage = new Image(); + auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(parameter.detailMaps[0].detailMapSrc); - auto texture = new Texture2D(); + auto texture = new (std::nothrow)Texture2D(); texture->initWithImage(textImage); obj->textures.push_back(texture); obj->init(); @@ -79,15 +79,15 @@ Terrain * cocos2d::Terrain::create(TerrainData ¶meter) }else { //alpha map - auto textImage = new Image(); + auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(parameter.alphaMapSrc); - obj->_alphaMap = new Texture2D(); + obj->_alphaMap = new (std::nothrow)Texture2D(); obj->_alphaMap->initWithImage(textImage); for(int i =0;i<4;i++) { - auto textImage = new Image(); + auto textImage = new (std::nothrow)Image(); textImage->initWithImageFile(parameter.detailMaps[i].detailMapSrc); - auto texture = new Texture2D(); + auto texture = new (std::nothrow)Texture2D(); texture->initWithImage(textImage); obj->textures.push_back(texture); obj->detailSize[i] = parameter.detailMaps[i].detailMapSize; @@ -370,6 +370,14 @@ void cocos2d::Terrain::Chunk::finish() 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 cocos2d::Terrain::Chunk::bindAndDraw() @@ -428,10 +436,10 @@ void cocos2d::Terrain::Chunk::generate(int imageWidth,int imageHeight,int m,int cocos2d::Terrain::Chunk::Chunk() { _currentLod = 0; - left =NULL; - right =NULL; - back = NULL; - front =NULL; + left = nullptr; + right = nullptr; + back = nullptr; + front = nullptr; } void cocos2d::Terrain::Chunk::updateIndices() @@ -681,11 +689,11 @@ glBufferData(GL_ARRAY_BUFFER, sizeof(TerrainVertexData)*vertices_tmp.size(), &ve cocos2d::Terrain::QuadTree::QuadTree(int x,int y,int width,int height,Terrain * terrain) { _needDraw = true; - parent = NULL; - tl =NULL; - tr =NULL; - bl =NULL; - br =NULL; + parent = nullptr; + tl =nullptr; + tr =nullptr; + bl =nullptr; + br =nullptr; pos_x = x; pos_y = y; this->height = height; @@ -764,7 +772,7 @@ cocos2d::Terrain::TerrainData::TerrainData(const char * heightMapsrc ,const char { this->heightMapSrc = heightMapsrc; this->detailMaps[0].detailMapSrc = textureSrc; - this->alphaMapSrc = NULL; + this->alphaMapSrc = nullptr; this->chunkSize = chunksize; this->mapHeight = mapHeight; this->mapScale = mapScale; diff --git a/cocos/3d/CCTerrain.h b/cocos/3d/CCTerrain.h index ccef035931..b287d58652 100644 --- a/cocos/3d/CCTerrain.h +++ b/cocos/3d/CCTerrain.h @@ -1,5 +1,29 @@ +/**************************************************************************** + Copyright (c) 2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ #ifndef CC_TERRAIN_H #define CC_TERRAIN_H + #include "2d/CCNode.h" #include "renderer/CCTexture2D.h" #include "renderer/CCCustomCommand.h" @@ -7,76 +31,136 @@ #include "2d/CCCamera.h" #include NS_CC_BEGIN - +/* + *the maximum amount of the chunkes + **/ #define MAX_CHUNKES 256 /* - *Terrain - * + *Terrain + *use to render outdoor or large scene via heightMap **/ class CC_DLL Terrain :public Node{ public: + +/* + *DetailMap + *this struct maintain a detail map data ,including source file ,detail size. + *the DetailMap can use for terrain splatting + **/ struct CC_DLL DetailMap{ + /*Constructors*/ DetailMap(); DetailMap(const char * detailMapSrc , float size = 35); + /*detail Image source file path*/ std::string detailMapSrc; + /*detailMapSize determine how many tiles that Terrain represent*/ float detailMapSize; }; + +/* + *TerrainData + *This TerrainData struct warp all parameter that Terrain need to create + */ struct CC_DLL TerrainData { + /*Constructors*/ TerrainData(); TerrainData(const char* heightMapsrc ,const char * textureSrc,const Size & chunksize = Size(32,32),float mapHeight = 2,float mapScale = 0.1); TerrainData(const char* heightMapsrc ,const char * alphamap,const DetailMap& detail1,const DetailMap& detail2,const DetailMap& detail3,const DetailMap& detail4,const Size & chunksize = Size(32,32),float mapHeight = 2,float mapScale = 0.1); + + /* + *deterimine the chunk size,chunk is the minimal subdivision of the Terrain + */ Size chunkSize; + /*height Map source path*/ std::string heightMapSrc; + /*the source path of the alpha map*/ char* alphaMapSrc; + /*detail maps*/ DetailMap detailMaps[4]; + /*terrain Maximum height*/ float mapHeight; + /*terrain scale factor*/ float mapScale; }; private: + +/* + *terrain vertices internal data format + **/ struct TerrainVertexData { + /*constructor*/ TerrainVertexData(){}; TerrainVertexData(Vec3 v1 ,Tex2F v2) { position = v1; texcoord = v2; }; + /*the vertex's attributes*/ cocos2d::Vec3 position; cocos2d::Tex2F texcoord; cocos2d::Vec3 normal; }; + +/* + *the terminal node of quad, use to subdivision terrain mesh and LOD + **/ struct Chunk { + /*Constructor*/ Chunk(); + /*vertices*/ std::vector vertices; - + /*LOD indices*/ struct LOD{ - std::vector indices; + std::vector indices; }; GLuint vbo[2]; + /*we now have four levels of detail*/ LOD _lod[4]; + /*AABB in local space*/ AABB _aabb; + /*setup Chunk data*/ void generate(int map_width,int map_height,int m,int n,const unsigned char * data); + /*calculateAABB*/ void calculateAABB(); + /*internal use draw function*/ void bindAndDraw(); + /*finish opengl setup*/ void finish(); + /*use linear-sample vertices for LOD mesh*/ void updateVerticesForLOD(); + /*calculate the average slope of the chunk*/ void calculateSlope(); - int _currentLod; + /*updateIndices for every frame*/ void updateIndices(); + + /*current LOD of the chunk*/ + int _currentLod; + /*the left,right,front,back neighbors*/ Chunk * left; Chunk * right; Chunk * front; Chunk * back; + + //the position int pos_x; int pos_y; + /*parent terrain*/ Terrain * _terrain; + /*chunk size*/ Size _size; - std::vector vertices_tmp; + /*chunk's estimated slope*/ float slope; + std::vector vertices_tmp; }; + +/* + *QuadTree + *use to frustum culling and set LOD + **/ struct QuadTree { QuadTree(int x,int y,int width,int height,Terrain * terrain); @@ -97,25 +181,38 @@ private: AABB _aabb; bool _needDraw; }; + + friend QuadTree; friend Chunk; public: + /*init function*/ bool init(); void initHeightMap(const char* heightMap); + /*create entry*/ static Terrain * create(TerrainData ¶meter); - // Overrides, internal use only - virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4 &transform, uint32_t flags) override; + /*get specified position's height mapping to the terrain*/ float getHeight(float x,float y, float z); float getHeight(Vec3 pos); + + /*get height from the raw height map*/ float getImageHeight(int pixel_x,int pixel_y); + /*Debug Use only, show the wireline instead of the surface. only support desktop platform*/ void setDrawWire(bool bool_value); + /*Set threshold distance of each LOD level,must equal or gereater than the chunk size*/ void setLODDistance(float lod_1,float lod_2,float lod_3); + /*Switch frustumCulling Flag*/ void setIsEnableFrustumCull(bool bool_value); + // Overrides, internal use only + virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4 &transform, uint32_t flags) override; private: Terrain(); void onDraw(const Mat4 &transform, uint32_t flags); + //set each chunk's LOD void setChunksLOD(Vec3 cameraPos); + //load vertices for whole height map void loadVertices(); + //calculate Normal Line for each Vertex void calculateNormal(); private: TerrainData _terrainData;