最近研究天龙八部的地形管理, 由于地形很大, 很显然Ogre自带的地形管理器没法使用参考了别人的研究之后, 不打算使用ETM来管理地形直接写了一个巨大的手工纹理来保存地形所需要的所有图像, 然后根据网格数据一个一个填写纹理坐标, 目前只写了地形第一层的代码, 第二层还没开始读写速度还是很快的
下面是两张截图
参考他人的文章修改 ETM,用Ogre实现《天龙八部》地形与部分场景详解(附源码) 纹理的创建方法和该文一样, 只是我打不算用ETM管理器创建地形自己创建一个mesh来管理地形, 包含顶点数据, 纹理数据
view source print ? 01void TLBBTerrain::createManualMesh() 02{ 03 MeshPtr mesh = MeshManager::getSingleton().createManual("mymesh", 04 ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); 05 SubMesh* submesh = mesh->createSubMesh(); 06 submesh->useSharedVertices = false; 07 // 顶点缓存 08 submesh->vertexData = new VertexData(); 09 submesh->vertexData->vertexStart = 0; 10 unsigned int nCount = m_xSize * m_zSize * 4; 11 submesh->vertexData->vertexCount = nCount; 12 VertexDeclaration* decl = submesh->vertexData->vertexDeclaration; 13 VertexBufferBinding* bind = submesh->vertexData->vertexBufferBinding; 14 size_t offset = 0; 15 decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_POSITION); 16 offset += VertexElement::getTypeSize(VET_FLOAT3); 17 decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_NORMAL); 18 offset += VertexElement::getTypeSize(VET_FLOAT3); 19 decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); 20 offset += VertexElement::getTypeSize(VET_FLOAT2); 21 decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1); 22 offset += VertexElement::getTypeSize(VET_FLOAT2); 23 decl->addElement( MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 2); 24 offset += VertexElement::getTypeSize(VET_FLOAT2); 25 HardwareVertexBufferSharedPtr vertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( 26 decl->getVertexSize(MAIN_BINDING), nCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); 27 bind->setBinding(MAIN_BINDING, vertexBuffer); 28 float* pBase = static_cast<float*>(vertexBuffer->lock(HardwareBuffer::HBL_DISCARD)); 29 TexureInfo info; 30 for (int j = 0; j < m_zSize; ++j) 31 { 32 for (int i = 0; i < m_xSize; ++i) 33 { 34 info = handleTexture(i, j); 35 // 点0 36 fillVertexData(i, j, pBase, info.firstleft, info.firsttop, info.secondleft, info.secondtop); 37 pBase += 12; 38 // 点1 39 fillVertexData(i, j+1, pBase, info.firstleft, info.firstbottom, info.secondleft, info.secondbottom); 40 pBase += 12; 41 // 点2 42 fillVertexData(i+1, j+1, pBase, info.firstright, info.firstbottom, info.secondright, info.secondbottom); 43 pBase += 12; 44 // 点3 45 fillVertexData(i+1, j, pBase, info.firstright, info.firsttop, info.secondright, info.secondtop); 46 pBase += 12; 47 } 48 } 49 vertexBuffer->unlock(); 50 HardwareIndexBufferSharedPtr indexBuffer = 51 HardwareBufferManager::getSingleton().createIndexBuffer( 52 HardwareIndexBuffer::IT_32BIT, 53 m_xSize * m_zSize * 6, 54 HardwareBuffer::HBU_STATIC, true); 55 submesh->indexData->indexBuffer = indexBuffer; 56 submesh->indexData->indexStart = 0; 57 submesh->indexData->indexCount = m_xSize * m_zSize * 6; 58 unsigned int* indexBuff = (unsigned int*)indexBuffer->lock(HardwareBuffer::HBL_NORMAL); 59 int indexFirstNum = 0; 60 for (int j = 0; j < m_zSize; ++j) 61 { 62 for (int i = 0; i < m_xSize; ++i) 63 { 64 *indexBuff++ = indexFirstNum; 65 *indexBuff++ = indexFirstNum + 1; 66 *indexBuff++ = indexFirstNum + 2; 67 *indexBuff++ = indexFirstNum; 68 *indexBuff++ = indexFirstNum + 2; 69 *indexBuff++ = indexFirstNum + 3; 70 indexFirstNum += 4; 71 } 72 } 73 indexBuffer->unlock(); 74 AxisAlignedBox meshBounds(0, m_minHeight * m_scale.y, 0, 75 m_xSize * m_scale.x, m_maxHeight * m_scale.y, m_zSize * m_scale.z); 76 mesh->_setBounds(meshBounds); 77 mesh->load(); 78 mesh->touch(); 79 // 设置相机位置 80 mSceneMgr->getCamera("PlayerCam")->setPosition(0, m_maxHeight * m_scale.y, 0); 81}其实我认为, 可以根据主角在场景的位置创建小范围的mesh, 可以将该mesh分成tile*tile大小, 根据主角位置创建周围四个tile的mesh就可以了. 不需要写这么大的mesh.等以后写入主角怪物的时候在写这段代码加载静态对象的时候出现了很多问题, 经过分析, 发现天龙八部游戏新版本的很多mesh不能够直接使用, 主要是Model目录里面的0灌木, 0树木, 0物品三个文件夹里面的mesh文件直接拷贝老天龙的数据就可以了不过加载的时候读取skeleton数据会出现错误, 查找一些资料后发现需要直接修改Ogre源码, 这些数据主要用于实现骨骼动画的时候会出现对OgreSkeletonSerializer.cpp文件里面的SkeletonSerializer::readAnimationTrack 函数进行修改.然后编译出ogremain.dll, ogremain_d.lib, 覆盖OgreSDK/bin文件里面的同名文件
view source print ? 01void SkeletonSerializer::readAnimationTrack(DataStreamPtr & stream, Animation * anim, 02 Skeleton * pSkel) 03 { 04 // unsigned short boneIndex : Index of bone to apply to 05 unsigned short boneHandle; 06 readShorts(stream, & boneHandle, 1 ); 07 // Find bone 08 Bone * targetBone = pSkel -> getBone(boneHandle); 09 // Create track 10 NodeAnimationTrack * pTrack = anim -> createNodeTrack(boneHandle, targetBone); 11 // Keep looking for nested keyframes 12 if ( ! stream -> eof()) 13 { 14 unsigned short streamID = readChunk(stream); 15 // while(streamID == SKELETON_ANIMATION_TRACK_KEYFRAME && !stream->eof()) 16 while ((streamID == SKELETON_ANIMATION_TRACK_KEYFRAME || streamID == 0x4120 ) && ! stream -> eof()) 17 { 18 if (streamID == 0x4120 ) 19 { 20 unsigned short len; 21 readShorts(stream, & len, 1 ); 22 unsigned short flags; 23 readShorts(stream, & flags, 1 ); 24 int count = (mCurrentstreamLen - 4 - 4 ) / 4 ; 25 if (len != count / 8 ) 26 len = len; 27 float time; 28 for ( int i = 0 ; i < len; i += 1 ) 29 { 30 readFloats(stream, & time, 1 ); 31 TransformKeyFrame * kf = pTrack -> createNodeKeyFrame(time); 32 Quaternion rot = Quaternion::IDENTITY; 33 if (flags & 1 ) 34 { 35 readObject(stream, rot); 36 } 37 kf -> setRotation(rot); 38 Vector3 trans = Vector3::ZERO; 39 if (flags & 2 ) 40 { 41 readObject(stream, trans); 42 } 43 kf -> setTranslate(trans); 44 } 45 } 46 else 47 readKeyFrame(stream, pTrack, pSkel); 48 if ( ! stream -> eof()) 49 { 50 // Get next stream 51 streamID = readChunk(stream); 52 } 53 } 54 if ( ! stream -> eof()) 55 { 56 // Backpedal back to start of this stream if we've found a non-keyframe 57 stream -> skip( - STREAM_OVERHEAD_SIZE); 58 } 59 } 60 }显示场景图如下