第二人生的源码分析(100)雷达地图详细显示
前面已经介绍雷达地图的类声明,现在就来仔细分析一下雷达地图是怎么样显示出来的。雷达地图显示函数LLNetMap::draw代码如下:
#001 void LLNetMap::draw()
#002 {
#003 static LLFrameTimer map_timer;
#004
判断是否显示雷达地图。
#005 if (!getVisible() || !gWorldPointer)
#006 {
#007 return;
#008 }
创建对象图片。
#009 if (mObjectImagep.isNull())
#010 {
#011 createObjectImage();
#012 }
#013
#014 mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
#015 mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
#016
#017 // Prepare a scissor region
#018 F32 rotation = 0;
#019
#020 {
#021 LLGLEnable scissor(GL_SCISSOR_TEST);
#022
#023 {
#024 LLGLSNoTexture no_texture;
取得窗口显示的大小。
#025 LLLocalClipRect clip(getLocalRect());
#026
设置GL显示的模式。
#027 glMatrixMode(GL_MODELVIEW);
#028
显示背景方框。
#029 // Draw background rectangle
#030 gGL.color4fv( mBackgroundColor.mV );
#031 gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
#032 }
#033
计算中心位置。
#034 // region 0,0 is in the middle
#035 S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPanX);
#036 S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPanY);
#037
#038 gGL.pushMatrix();
#039
#040 gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f);
#041
判断是否旋转显示地图。
#042 if( LLNetMap::sRotateMap )
#043 {
#044 // rotate subsequent draws to agent rotation
#045 rotation = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
#046 glRotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f);
#047 }
#048
#049 // figure out where agent is
#050 S32 region_width = llround(gWorldPointer->getRegionWidthInMeters());
#051
#052 for (LLWorld::region_list_t::iterator iter = gWorldp->mActiveRegionList.begin();
#053 iter != gWorldp->mActiveRegionList.end(); ++iter)
#054 {
计算以当前摄像机为中心的地图位置。
#055 LLViewerRegion* regionp = *iter;
#056 // Find x and y position relative to camera's center.
#057 LLVector3 origin_agent = regionp->getOriginAgent();
#058 LLVector3 rel_region_pos = origin_agent - gAgent.getCameraPositionAgent();
#059 F32 relative_x = (rel_region_pos.mV[0] / region_width) * gMiniMapScale;
#060 F32 relative_y = (rel_region_pos.mV[1] / region_width) * gMiniMapScale;
#061
#062 // background region rectangle
#063 F32 bottom = relative_y;
#064 F32 left = relative_x;
#065 F32 top = bottom + gMiniMapScale ;
#066 F32 right = left + gMiniMapScale ;
#067
#068 if (regionp == gAgent.getRegion())
#069 {
#070 gGL.color4f(1.f, 1.f, 1.f, 1.f);
#071 }
#072 else
#073 {
#074 gGL.color4f(0.8f, 0.8f, 0.8f, 1.f);
#075 }
#076
#077 if (!regionp->mAlive)
#078 {
#079 gGL.color4f(1.f, 0.5f, 0.5f, 1.f);
#080 }
#081
#082
显示背景边框。
#083 // Draw using texture.
#084 LLViewerImage::bindTexture(regionp->getLand().getSTexture());
#085 gGL.begin(GL_QUADS);
#086 gGL.texCoord2f(0.f, 1.f);
#087 gGL.vertex2f(left, top);
#088 gGL.texCoord2f(0.f, 0.f);
#089 gGL.vertex2f(left, bottom);
#090 gGL.texCoord2f(1.f, 0.f);
#091 gGL.vertex2f(right, bottom);
#092 gGL.texCoord2f(1.f, 1.f);
#093 gGL.vertex2f(right, top);
#094 gGL.end();
#095
显示水所在的区域。
#096 // Draw water
#097 glAlphaFunc(GL_GREATER, ABOVE_WATERLINE_ALPHA / 255.f );
#098 {
#099 if (regionp->getLand().getWaterTexture())
#100 {
#101 LLViewerImage::bindTexture(regionp->getLand().getWaterTexture());
#102 gGL.begin(GL_QUADS);
#103 gGL.texCoord2f(0.f, 1.f);
#104 gGL.vertex2f(left, top);
#105 gGL.texCoord2f(0.f, 0.f);
#106 gGL.vertex2f(left, bottom);
#107 gGL.texCoord2f(1.f, 0.f);
#108 gGL.vertex2f(right, bottom);
#109 gGL.texCoord2f(1.f, 1.f);
#110 gGL.vertex2f(right, top);
#111 gGL.end();
#112 }
#113 }
#114 glAlphaFunc(GL_GREATER,0.01f);
#115 }
#116
#117
#118 LLVector3d old_center = mObjectImageCenterGlobal;
#119 LLVector3d new_center = gAgent.getCameraPositionGlobal();
#120
#121 new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]);
#122 new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]);
#123 new_center.mdV[2] = 0.f;
#124
#125 if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
#126 {
#127 mUpdateNow = FALSE;
#128 mObjectImageCenterGlobal = new_center;
#129
#130 // Center moved enough.
#131 // Create the base texture.
#132 U8 *default_texture = mObjectRawImagep->getData();
#133 memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );
#134
显示建筑物,根据水平面的高度来显示不同的颜色。
#135 // Draw buildings
#136 gObjectList.renderObjectsForMap(*this);
#137
#138 mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
#139
#140 map_timer.reset();
#141 }
#142
#143 LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
#144 map_center_agent -= gAgent.getCameraPositionAgent();
#145 map_center_agent.mV[VX] *= gMiniMapScale/region_width;
#146 map_center_agent.mV[VY] *= gMiniMapScale/region_width;
#147
#148 LLViewerImage::bindTexture(mObjectImagep);
#149 F32 image_half_width = 0.5f*mObjectMapPixels;
#150 F32 image_half_height = 0.5f*mObjectMapPixels;
#151
#152 gGL.begin(GL_QUADS);
#153 gGL.texCoord2f(0.f, 1.f);
#154 gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]);
#155 gGL.texCoord2f(0.f, 0.f);
#156 gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height);
#157 gGL.texCoord2f(1.f, 0.f);
#158 gGL.vertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height);
#159 gGL.texCoord2f(1.f, 1.f);
#160 gGL.vertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]);
#161 gGL.end();
#162
#163 gGL.popMatrix();
#164
#165 LLVector3d pos_global;
#166 LLVector3 pos_map;
#167
下面开始显示整个地图上所有玩家。
#168 // Draw avatars
#169 for (LLWorld::region_list_t::iterator iter = gWorldp->mActiveRegionList.begin();
#170 iter != gWorldp->mActiveRegionList.end(); ++iter)
#171 {
获取所在区域。
#172 LLViewerRegion* regionp = *iter;
#173 const LLVector3d& origin_global = regionp->getOriginGlobal();
#174
获取当前玩家的人数。
#175 S32 count = regionp->mMapAvatars.count();
#176 S32 i;
#177 LLVector3 pos_local;
#178 U32 compact_local;
#179 U8 bits;
#180 // TODO: it'd be very cool to draw these in sorted order from lowest Z to highest.
#181 // just be careful to sort the avatar IDs along with the positions. –MG
开始在地图上显示所有玩家。
#182 for (i = 0; i < count; i++)
#183 {
#184 compact_local = regionp->mMapAvatars.get(i);
#185
#186 bits = compact_local & 0xFF;
#187 pos_local.mV[VZ] = F32(bits) * 4.f;
#188 compact_local >>= 8;
#189
#190 bits = compact_local & 0xFF;
#191 pos_local.mV[VY] = (F32)bits;
#192 compact_local >>= 8;
#193
#194 bits = compact_local & 0xFF;
#195 pos_local.mV[VX] = (F32)bits;
#196
#197 pos_global.setVec( pos_local );
#198 pos_global += origin_global;
#199
从全局坐标转换为地图坐标。
#200 pos_map = globalPosToView(pos_global);
#201
#202 BOOL show_as_friend = FALSE;
#203 if( i < regionp->mMapAvatarIDs.count())
#204 {
#205 show_as_friend = is_agent_friend(regionp->mMapAvatarIDs.get(i));
#206 }
在地图坐标上显示玩家。
#207 LLWorldMapView::drawAvatar(
#208 pos_map.mV[VX], pos_map.mV[VY],
#209 show_as_friend ? gFriendMapColor : gAvatarMapColor,
#210 pos_map.mV[VZ]);
#211 }
#212 }
#213
#214 // Draw dot for autopilot target
#215 if (gAgent.getAutoPilot())
#216 {
#217 drawTracking( gAgent.getAutoPilotTargetGlobal(), gTrackColor );
#218 }
#219 else
#220 {
#221 LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
#222 if ( LLTracker::TRACKING_AVATAR == tracking_status )
#223 {
#224 drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor );
#225 }
#226 else if ( LLTracker::TRACKING_LANDMARK == tracking_status
#227 || LLTracker::TRACKING_LOCATION == tracking_status )
#228 {
#229 drawTracking( LLTracker::getTrackedPositionGlobal(), gTrackColor );
#230 }
#231 }
#232
#233 // Draw dot for self avatar position
#234 //drawTracking( gAgent.getPosGlobalFromAgent(gAgent.getFrameAgent().getCenter()), gSelfMapColor );
#235 pos_global = gAgent.getPositionGlobal();
#236 pos_map = globalPosToView(pos_global);
#237 gl_draw_image(llround(pos_map.mV[VX]) - 4,
#238 llround(pos_map.mV[VY]) - 4,
#239 LLWorldMapView::sAvatarYouSmallImage,
#240 LLColor4::white);
#241
计算当前摄像机观看的视角。
#242 // Draw frustum
#243 F32 meters_to_pixels = gMiniMapScale/ gWorldPointer->getRegionWidthInMeters();
#244
#245 F32 horiz_fov = gCamera->getView() * gCamera->getAspect();
#246 F32 far_clip_meters = gCamera->getFar();
#247 F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
#248
#249 F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
#250 F32 half_width_pixels = half_width_meters * meters_to_pixels;
#251
#252 F32 ctr_x = (F32)center_sw_left;
#253 F32 ctr_y = (F32)center_sw_bottom;
#254
#255
#256 LLGLSNoTexture no_texture;
#257
旋转地图的显示。
#258 if( LLNetMap::sRotateMap )
#259 {
#260 gGL.color4fv(gFrustumMapColor.mV);
#261
#262 gGL.begin( GL_TRIANGLES );
#263 gGL.vertex2f( ctr_x, ctr_y );
#264 gGL.vertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels );
#265 gGL.vertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels );
#266 gGL.end();
#267 }
#268 else
#269 {
#270 gGL.color4fv(gRotatingFrustumMapColor.mV);
#271
#272 // If we don't rotate the map, we have to rotate the frustum.
#273 gGL.pushMatrix();
#274 gGL.translatef( ctr_x, ctr_y, 0 );
#275 glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
#276 gGL.begin( GL_TRIANGLES );
#277 gGL.vertex2f( 0, 0 );
#278 gGL.vertex2f( -half_width_pixels, far_clip_pixels );
#279 gGL.vertex2f( half_width_pixels, far_clip_pixels );
#280 gGL.end();
#281 gGL.popMatrix();
#282 }
#283 }
#284
下面显示8个方向的文字提示。
#285 // Rotation of 0 means that North is up
#286 setDirectionPos( mTextBoxEast, rotation );
#287 setDirectionPos( mTextBoxNorth, rotation + F_PI_BY_TWO );
#288 setDirectionPos( mTextBoxWest, rotation + F_PI );
#289 setDirectionPos( mTextBoxSouth, rotation + F_PI + F_PI_BY_TWO );
#290
#291 setDirectionPos( mTextBoxNorthEast, rotation + F_PI_BY_TWO / 2);
#292 setDirectionPos( mTextBoxNorthWest, rotation + F_PI_BY_TWO + F_PI_BY_TWO / 2);
#293 setDirectionPos( mTextBoxSouthWest, rotation + F_PI + F_PI_BY_TWO / 2);
#294 setDirectionPos( mTextBoxSouthEast, rotation + F_PI + F_PI_BY_TWO + F_PI_BY_TWO / 2);
#295
#296 LLUICtrl::draw();
#297 }
从上面的函数可以看到,先显示底层的边框,然后在上面显示水区域,接着显示建筑物层,最后在上面显示玩家所在的地方。当然还显示当前玩家所能看到的视角平面,显示地图八个方向的文字显示。
第二人生开发包提供,价格198元/套(含邮费)。
包括内容如下:
1. 《第二人生的源码分析》PDF文档。
2. 第二人生客户端源程序。
3.
提供三个月的技术服务,
提供快速编译指导,比如通过QQ远程编译操作。
提供完整的书参考。
联系方式:
QQ: 9073204
MSN: caimouse1976@sina.com
EMAIL: ccaimouse@gmail.com