﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Render Donkey-随笔分类-Game and Engine</title><link>http://www.cppblog.com/Leaf/category/14535.html</link><description>~~</description><language>zh-cn</language><lastBuildDate>Fri, 14 Jun 2013 21:22:53 GMT</lastBuildDate><pubDate>Fri, 14 Jun 2013 21:22:53 GMT</pubDate><ttl>60</ttl><item><title>镜面反射矩阵推导</title><link>http://www.cppblog.com/Leaf/archive/2013/06/15/201017.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Fri, 14 Jun 2013 16:48:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/06/15/201017.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/201017.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/06/15/201017.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/201017.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/201017.html</trackback:ping><description><![CDATA[<p>最近公司游戏正在准备上线，所以FlasCC也就没有研究了，偶尔有闲功夫，也是玩玩3DMAX和UNITY3D。 感觉不会3DMAX，是一种局限。</p> <p>回到主题，记录一下镜面反射矩阵的推导。</p> <p>在用Irrlicht和RTT做镜面效果的时候，用到了反射矩阵。 就是需要把摄相机镜像，渲染一个RT，贴到镜面模型上。这个其实还纠结了许久，因为之前做水面渲染的时候，水面是平的，很好计算摄相机在水面以下的位置。 但是换成镜面，就不一样了，因为镜面可能是任意面。 于是就需要一个通用的反射矩阵。</p> <p>反射矩阵的计算是基于平面的，因为，任何反射，都需要一个反射面。</p> <p>所以，我们先给出平面表示 Plane(nx,ny,nz,d); 其中(nx,ny,nz)已经单位化。</p> <p>然后，我们假设空间中有任意一点P(x,y,z,1)</p> <p>设这个点P以Plane为反射面的镜像点为P1(x1,y1,z1,w)。</p> <p>&nbsp;</p> <p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p> <p>根据定理，我们知道， 若两个点以某一点为镜像，则两个点的坐标之和除以2，就刚好是中点。 </p> <p>这个理论我们用到这里的话， 那这个中点就刚好是平面上的一个点。 平面上的这个点就是 P(x,y,z,1) - (nx,ny,nz,0)*D .&nbsp; 其中D就是点P到平面的距离</p> <p>而D=Plane dot P = (x*nx+y*ny+z*nz+d);</p> <p>由上面的描述，我们马上想到，那么要求点P1的话，就是这样&nbsp; </p> <p>(P+P1)/2 = P(x,y,z,1) - (nx,ny,nz,0)*D</p> <p>=&gt; P1 = P(x,y,z,1) - 2(nx,ny,nz,0)*D</p> <p>=&gt;P1 = P(x,y,z,1) - 2(nx,ny,nz,0)*(x*nx+y*ny+z*nz+d)</p> <p>&nbsp;</p> <p>换成矩阵形式则为</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ｜1-2*nx*nx&nbsp;&nbsp; -2*nx*ny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -2*nx*nz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; |</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | -2*ny*nx&nbsp;&nbsp;&nbsp;&nbsp; 1 - 2*ny*ny&nbsp;&nbsp;&nbsp;&nbsp; -2*ny*nz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; |</p> <p>P1 = {x,y,z,1}&nbsp;&nbsp; x&nbsp;&nbsp; | -2*nz*nx&nbsp;&nbsp;&nbsp;&nbsp; -2*nz*ny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1-2*nz*nz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp; |</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | -2*d*nx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -2*d*ny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -2*d*nz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; |</p> <p>&nbsp;</p> <p>大功告成</p> <p>btw:这是行主矩阵表示法</p> <img src ="http://www.cppblog.com/Leaf/aggbug/201017.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-06-15 00:48 <a href="http://www.cppblog.com/Leaf/archive/2013/06/15/201017.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一路风雨走过来：那些我亲密接触过的项目</title><link>http://www.cppblog.com/Leaf/archive/2013/04/11/199332.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Wed, 10 Apr 2013 17:51:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/04/11/199332.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/199332.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/04/11/199332.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/199332.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/199332.html</trackback:ping><description><![CDATA[<p>一时间，又不知道说什么了。 贴几张这一路走来，和我有关的项目截图吧。&nbsp; 时间是由新到旧</p> <p>&nbsp;</p> <p>目前在正研发中的项目 《帝国来了》</p> <p><a title="http://v.ku6.com/show/1zMrlsOx0nwpQw3JMoNAjw...html" href="http://v.ku6.com/show/1zMrlsOx0nwpQw3JMoNAjw...html">http://v.ku6.com/show/1zMrlsOx0nwpQw3JMoNAjw...html</a></p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_thumb_2.png" width="577" height="379"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_8.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_thumb_3.png" width="582" height="385"></a> </p> <p>&nbsp;</p> <p>继爹妈总动员之后的第二个产品 《大话修仙》 （天地劫WEB)</p> <p><a title="http://xx.sjgame.cn/" href="http://xx.sjgame.cn/">http://xx.sjgame.cn/</a></p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_thumb.png" width="921" height="474"></a> </p> <p></p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/c47424b98ba1_19B0/image_thumb_1.png" width="925" height="477"></a> </p> <p>&nbsp;</p> <p>处女作 《爹妈总动员》 哎，第一次，给了它。 </p> <p><a title="http://www.87kd.com/game/dmzdy/" href="http://www.87kd.com/game/dmzdy/">http://www.87kd.com/game/dmzdy/</a></p> <p><img alt="" src="http://www.87kd.com/uploadfiles/news/20120828/2012828923532524051.jpg" width="769" height="444"></p> <p>&nbsp;</p> <p>公司让我们学习实战经验，临时参与了两个月的《五虎上将》</p> <p><a title="http://whsj.sjgame.cn/" href="http://whsj.sjgame.cn/">http://whsj.sjgame.cn/</a></p> <p><img src="http://whsj.sjgame.cn/photo/pic/gamepic/20110621193257.jpg" width="980" height="588"></p> <p>&nbsp;</p> <p>第一个FALSH技术DEMO KINGDOM RUSH</p> <p><a title="http://www.cppblog.com/Leaf/archive/2013/02/22/198012.html" href="http://www.cppblog.com/Leaf/archive/2013/02/22/198012.html">http://www.cppblog.com/Leaf/archive/2013/02/22/198012.html</a></p> <p><a href="http://images.cnblogs.com/cnblogs_com/GameRambler/201110/201110122223072336.png"><img title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/GameRambler/201110/201110122223276601.png" width="824" height="704"></a></p> <p>&nbsp;</p> <p>HALO 3D引擎DEMO演示</p> <p><a title="http://www.cppblog.com/Leaf/archive/2013/02/22/198011.html" href="http://www.cppblog.com/Leaf/archive/2013/02/22/198011.html">http://www.cppblog.com/Leaf/archive/2013/02/22/198011.html</a></p> <p><a href="http://images.cnblogs.com/cnblogs_com/GameRambler/201110/201110102113425106.png"><img title="wps_clip_image-26198" border="0" alt="wps_clip_image-26198" src="http://images.cnblogs.com/cnblogs_com/GameRambler/201110/201110102114104086.png" width="800" height="601"></a></p> <p>&nbsp;</p> <p>2部的《天地劫》</p> <p><a title="http://tdj.sjgame.cn/main.html" href="http://tdj.sjgame.cn/main.html">http://tdj.sjgame.cn/main.html</a></p> <p><img src="http://tdj.sjgame.cn/photo/gamepic/20121211212732.jpg" width="769" height="578"></p> <p>&nbsp;</p> <p>在游戏开发的道路上，我想我依然会继续努力、奋斗。 生生不息，不离不弃。。。。</p>  <img src ="http://www.cppblog.com/Leaf/aggbug/199332.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-04-11 01:51 <a href="http://www.cppblog.com/Leaf/archive/2013/04/11/199332.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>irrlicht引擎：镜子效果</title><link>http://www.cppblog.com/Leaf/archive/2013/04/05/199111.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Thu, 04 Apr 2013 16:53:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/04/05/199111.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/199111.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/04/05/199111.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/199111.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/199111.html</trackback:ping><description><![CDATA[<p>&nbsp;</p> <p>最近在用irrlicht做一个3D试衣间的小项目，为了给项目增添点花样，于是想实现一面镜子。</p> <p>我记得D3D龙书上有一个使用模板缓冲区实现的例子。网上也有OPENGL实现的例子。 但这一次，我想用irrlicht的RTT实现一面镜子效果。</p> <p>其实原理和水面反射原理是一样的， 只是没有加扰动而已</p> <p>&nbsp;</p> <p>第一步：渲染反射贴图</p> <blockquote> <p>反射贴图的渲染，其实就是将摄相机通过镜面镜像即可，irrlicht中我找了半天，没有发现镜像矩阵的算法，倒是在网上搜到了一个。 很是不错。</p></blockquote> <p>同时，也翻阅了一下先前公司引擎项目的代码，发现其实就是那个公式。 有兴趣的朋友可以参看这里</p> <p>&nbsp;</p> <p><a title="http://www.cnblogs.com/glshader/archive/2010/11/02/1866971.html" href="http://www.cnblogs.com/glshader/archive/2010/11/02/1866971.html">http://www.cnblogs.com/glshader/archive/2010/11/02/1866971.html</a></p> <p>&nbsp;</p> <p>通过这个镜面反射矩阵，我们可以将摄相机镜像， 相当于是从镜子里向外看，渲染出一个世界。 在渲染的时候，要记得设置裁剪面。 在我的测试中我没有设置。</p> <p>第二步：重新渲染世界</p> <blockquote> <p>重新渲染世界的时候，镜子需要一个特殊的纹理来进行反射贴图。（镜像摄相机空间的投影纹理映射）。 这个贴图方式，就是指忽略镜子的纹理坐标，而通过</p></blockquote> <p>镜像摄相机来计算出投影坐标，然后贴在镜子上。在我的测试中，是用SHADER来实现的。 为镜子做了一个特殊的纹理。</p> <p>&nbsp;</p> <p>下面，我贴一下SHADER，很简单，如果实在不清楚的，可以参考一些投影纹理相关的资料。</p> <p>&nbsp;</p> <p>顶点着色器代码 HLSL</p> <p>float4x4&nbsp;&nbsp;&nbsp; WorldViewProj;<br>float4x4&nbsp;&nbsp;&nbsp; MirrorWorldViewProj;<br>struct VS_OUTPUT<br>{<br>&nbsp;&nbsp;&nbsp; float4 position&nbsp;&nbsp;&nbsp; :POSITION; </p> <p>&nbsp;&nbsp;&nbsp; float3 uv: TEXCOORD0;<br>}; </p> <p>struct VS_INPUT<br>{<br>&nbsp;&nbsp;&nbsp; float4 position&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : POSITION;<br>&nbsp;&nbsp;&nbsp; float4 color&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : COLOR0;<br>&nbsp;&nbsp;&nbsp; float2 texCoord0&nbsp;&nbsp;&nbsp; : TEXCOORD0;<br>};  <p>VS_OUTPUT main(VS_INPUT input)<br>{<br>&nbsp;&nbsp;&nbsp; VS_OUTPUT output; <br>&nbsp;&nbsp;&nbsp; float4 pos = mul(input.position, WorldViewProj);<br>&nbsp;&nbsp;&nbsp; output.position = pos;</p> <p>&nbsp;&nbsp;&nbsp; //计算反射纹理的坐标 </p> <p>&nbsp;&nbsp;&nbsp; pos = mul(input.position,MirrorWorldViewProj);<br>&nbsp;&nbsp;&nbsp; output.uv.x = 0.5 * (pos.w + pos.x);<br>&nbsp;&nbsp;&nbsp; output.uv.y = 0.5 * (pos.w - pos.y);<br>&nbsp;&nbsp;&nbsp; output.uv.z = pos.w;<br>&nbsp;&nbsp;&nbsp; return output;<br>}  <p>&nbsp; <p>像素着色器代码 HLSL  <p>sampler2D colorMap;<br>struct PS_OUTPUT<br>{<br>&nbsp;&nbsp;&nbsp; float4 color : COLOR0;&nbsp;&nbsp; <br>};  <p>struct PS_INPUT<br>{<br>&nbsp;&nbsp;&nbsp; float4 position&nbsp;&nbsp;&nbsp; : POSITION;<br>&nbsp;&nbsp;&nbsp; float3 uv: TEXCOORD0;<br>};<br>PS_OUTPUT main( PS_INPUT input )<br>{ <br>&nbsp;&nbsp;&nbsp; PS_OUTPUT output;<br>&nbsp;&nbsp;&nbsp; float2 uv = saturate(input.uv.xy / input.uv.z);<br>&nbsp;&nbsp;&nbsp; output.color = tex2D(colorMap,uv);<br>&nbsp;&nbsp;&nbsp; return output;<br>}  <p>&nbsp;</p> <p>&nbsp;</p> <p>RTT相关的操作，irrlicht的RenderToTexture已经很明白了，再此不在敷述。</p> <p>&nbsp;</p> <p>上图，收工</p> <p>&nbsp;</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_C6B/2000_2.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="2000" border="0" alt="2000" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_C6B/2000_thumb.jpg" width="539" height="405"></a></p><img src ="http://www.cppblog.com/Leaf/aggbug/199111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-04-05 00:53 <a href="http://www.cppblog.com/Leaf/archive/2013/04/05/199111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>irrlicht引擎：真实的水面渲染</title><link>http://www.cppblog.com/Leaf/archive/2013/04/04/199101.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Thu, 04 Apr 2013 10:16:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/04/04/199101.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/199101.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/04/04/199101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/199101.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/199101.html</trackback:ping><description><![CDATA[<p>先上图</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_10026/image_2.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_10026/image_thumb.png" width="609" height="473"></a> </p> <p>&nbsp;</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_10026/image_4.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_10026/image_thumb_1.png" width="609" height="483"></a> </p> <p></p> <p>本来说是做镜子效果的，结果手工计算的镜面反射矩阵应用在irrlicht相机上的时候，始终无法出现效果，只能去网上搜索</p> <p>在irrlicht official wiki上发现了这个扩展的WaterNode，下载下来，改了点BUG，整合进了Terrain Demo里， 就是上图的效果。</p> <p>在我的机器上,HLSL版本是没有问题的，GL版本貌似RTT有点问题。</p> <p>&nbsp;</p> <p><a href="http://files.cppblog.com/Leaf/RealisticWaterSceneNode020.zip">点击此处下载源代码</a></p>  <img src ="http://www.cppblog.com/Leaf/aggbug/199101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-04-04 18:16 <a href="http://www.cppblog.com/Leaf/archive/2013/04/04/199101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>irrlicht引擎：硬件蒙皮骨骼动画</title><link>http://www.cppblog.com/Leaf/archive/2013/03/26/198823.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 25 Mar 2013 16:08:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/03/26/198823.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/198823.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/03/26/198823.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/198823.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/198823.html</trackback:ping><description><![CDATA[<p>这个东西很顺利，仅用了半小时就找到了方法，最应该感谢的还是Super TuxKart(简称STK，下面就都用这三字母了). 如果不明白STK，同时又对它感兴趣的童鞋，可以访问这里</p> <p><a title="http://supertuxkart.sourceforge.net/" href="http://supertuxkart.sourceforge.net/">http://supertuxkart.sourceforge.net/</a></p> <p>由于墙的原因，需要各位搭梯子。</p> <p>&nbsp;</p> <p>上周末，在弄换装的时候，发现irrlicht引擎本身是不支持硬件蒙皮的，多少令人有些失望。 心里就一直寻思着怎么扩展一下，将它弄出来。</p> <p>值得说明的是STK对irrlicht引擎的用法是很简单的，基本上可以说是裸用，并未在irrlicht接口上做修改。 而是对外进行了一些必要的扩展。</p> <p>当然，STK也对外开放了一个irrlicht.dll，说是修改了其中的BUG。 但直接使用irrlicht是可以的。</p> <p>&nbsp;</p> <p>废话不多说，来说说如何不修改irrlicht一行代码，通过外部扩展来实现硬件骨骼动画吧</p> <p>&nbsp;</p> <p>首先，能够使我们不修改irrlicht代码的原因，是因为ISkinnedMesh提供了一个setHardwareSkinning接口，默认为false.</p> <p>虽然这个接口的说明是"(This feature is not implemented in irrlicht yet)&#8221;，但并不代表，设置与不设置无差别。 </p> <p>查看代码可以发现，当你设置了这个为true以后，irrlicht就完全不管你的动画了。 意思就是，要是你非要让我干我不干不了的事，那就只有您另请高明了。</p> <p>irrlicht连CPU计算都不会参与。 这正好让我们有机可乘，完全用GPU接管。</p> <p>&nbsp;</p> <p>而要让一个顶点参与骨骼计算，那骨骼索引则是少不了的。所以，我们需要想办法让顶点数据能够将骨骼索引代入SHADER中。</p> <p>在STK中用了一种巧妙的方法， 就是使用了顶点的颜色数据， 虽然这样一来，顶点颜色就用不了了。 但在模型渲染时，顶点颜色很少被使用到的。 也就是说，顶点颜色在STK的动画模型中，被用作了骨骼索引。</p> <p>初始化骨骼索引的方法很简单，用下面的代码遍历即可。</p> <p>设：我们有一个骨骼动画模型是 ISkinnedMesh* pSkinnedMesh = &#8230;</p> <p>那么：初始化代码如下</p> <p>for(u32 i = 0;i &lt; pSkinnedMesh -&gt;getMeshBuffers().size();++i)<br>{<br>&nbsp;&nbsp;&nbsp; for(u32 g = 0;g &lt; pSkinnedMesh -&gt;getMeshBuffers()[i]-&gt;getVertexCount();++g)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pSkinnedMesh -&gt;getMeshBuffers()[i]-&gt;getVertex(g)-&gt;Color = video::SColor(0,0,0,0);<br>&nbsp;&nbsp;&nbsp; }<br>}</p> <p>//初始化完毕以后，就是需要真正的索引赋值了，通过以下代码可以完成</p> <p>const core::array&lt;scene::ISkinnedMesh::SJoint*&gt;&amp; joints = pSkinnedMesh -&gt;getAllJoints();<br>for(u32 i = 0;i &lt; joints.size();++i)<br>{<br>&nbsp;&nbsp;&nbsp; const core::array&lt;scene::ISkinnedMesh::SWeight&gt;&amp;&nbsp;&nbsp;&nbsp; weights = joints[i]-&gt;Weights;<br>&nbsp;&nbsp;&nbsp; for(u32 j = 0;j &lt; weights.size();++j)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int buffId = weights[j].buffer_id;  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int vertexId = pSkinedMesh-&gt;getAllJoints()[i]-&gt;Weights[j].vertex_id;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; video::SColor* vColor = &amp;pSkinedMesh-&gt;getMeshBuffers()[buffId]-&gt;getVertex(vertexId)-&gt;Color;  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(vColor-&gt;getRed() == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vColor-&gt;setRed(i + 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(vColor-&gt;getGreen() == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vColor-&gt;setGreen(i + 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(vColor-&gt;getBlue() == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vColor-&gt;setBlue(i + 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(vColor-&gt;getAlpha() == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vColor-&gt;setAlpha(i + 1);<br>&nbsp;&nbsp;&nbsp; }<br>} <p>&nbsp;</p> <p>//经过以上两个步骤，顶点数据改造完成。 值得注意的是， 在这里， 索引 0 是被认为是无效的</p> <p>&nbsp;</p> <p>然后，我们来创建一个SHADER作为渲染。</p> <p>假设 我们将这个pSkinnedMesh绑定了到了一个IAnimatedSceneNode* node 上。</p> <p>那，我们为这个结点创建一个材质 在创建材质前，我们需要准备一个SHADER回调。 SHADER回调就像下面一样就可以了。</p> <p>class HWSkinCallBack:public video::IShaderConstantSetCallBack<br>{<br>&nbsp;&nbsp;&nbsp; scene::IAnimatedMeshSceneNode* m_pNode;<br>public:<br>&nbsp;&nbsp;&nbsp; HWSkinCallBack(scene::IAnimatedMeshSceneNode* node):m_pNode(node)<br>&nbsp;&nbsp;&nbsp; {  <p>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; virtual void OnSetConstants(video::IMaterialRendererServices* services,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s32 userData)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scene::ISkinnedMesh* mesh = (scene::ISkinnedMesh*)m_pNode-&gt;getMesh();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f32 joints_data[55 * 16];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int copyIncrement = 0;  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const core::array&lt;scene::ISkinnedMesh::SJoint*&gt; joints = mesh-&gt;getAllJoints();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(u32 i = 0;i &lt; joints.size();++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; core::matrix4 joint_vertex_pull(core::matrix4::EM4CONST_NOTHING);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; joint_vertex_pull.setbyproduct(joints[i]-&gt;GlobalAnimatedMatrix, joints[i]-&gt;GlobalInversedMatrix);  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f32* pointer = joints_data + copyIncrement;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0;i &lt; 16;++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pointer++ = joint_vertex_pull[i];  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; copyIncrement += 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; services-&gt;setVertexShaderConstant("JointTransform", joints_data, mesh-&gt;getAllJoints().size() * 16);<br>&nbsp;&nbsp;&nbsp; }<br>}; <p>&nbsp;</p> <p>好了，现在我们来创建一个材质</p> <p>s32 hwskm = gpu-&gt;addHighLevelShaderMaterialFromFiles(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "../../skinning.vert","main",video::EVST_VS_2_0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "","main",video::EPST_PS_2_0,&amp;hwc,video::EMT_SOLID);</p> <p>//用新创建出来的材质赋值给这个结点</p> <p>node-&gt;setMaterialType((video::E_MATERIAL_TYPE)hwskm );</p> <p>&nbsp;</p> <p>//到此，设置完毕。</p> <p> //最后，就是skinning.vert本身的内容了。 贴出来即可，没有太多技巧，就是一个普通的蒙皮。</p> <p>// skinning.vert  <p>#define MAX_JOINT_NUM 36<br>#define MAX_LIGHT_NUM 8  <p>uniform mat4 JointTransform[MAX_JOINT_NUM];  <p>void main() <br>{<br>&nbsp;&nbsp;&nbsp; int index;<br>&nbsp;&nbsp;&nbsp; vec4 ecPos;<br>&nbsp;&nbsp;&nbsp; vec3 normal;<br>&nbsp;&nbsp;&nbsp; vec3 light_dir;<br>&nbsp;&nbsp;&nbsp; float n_dot_l;<br>&nbsp;&nbsp;&nbsp; float dist;  <p>&nbsp;&nbsp;&nbsp; mat4 ModelTransform = gl_ModelViewProjectionMatrix;<br>&nbsp;&nbsp;&nbsp; index = int(gl_Color.r * 255.99);<br>&nbsp;&nbsp;&nbsp; mat4 vertTran = JointTransform[index - 1];<br>&nbsp;&nbsp;&nbsp; index = int(gl_Color.g * 255.99);<br>&nbsp;&nbsp;&nbsp; if(index &gt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vertTran += JointTransform[index - 1];  <p>&nbsp;&nbsp;&nbsp; index = int(gl_Color.b * 255.99);<br>&nbsp;&nbsp;&nbsp; if(index &gt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vertTran += JointTransform[index - 1];<br>&nbsp;&nbsp;&nbsp; index = int(gl_Color.a * 255.99);<br>&nbsp;&nbsp;&nbsp; if(index &gt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vertTran += JointTransform[index - 1];<br>&nbsp;&nbsp;&nbsp; ecPos = gl_ModelViewMatrix * vertTran * gl_Vertex;<br>&nbsp;&nbsp;&nbsp; normal = normalize(gl_NormalMatrix * mat3(vertTran) * gl_Normal);<br>&nbsp;&nbsp;&nbsp; gl_FrontColor = vec4(0,0,0,0);<br>&nbsp;&nbsp;&nbsp; for(int i = 0;i &lt; MAX_LIGHT_NUM;i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; light_dir = vec3(gl_LightSource[i].position-ecPos);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n_dot_l = max(dot(normal, normalize(light_dir)), 0.0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dist = length(light_dir);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n_dot_l *= 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gl_FrontColor += gl_LightSource[i].diffuse * n_dot_l;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; gl_FrontColor = clamp(gl_FrontColor,0.3,1.0); <p>&nbsp;&nbsp;&nbsp; ModelTransform *= vertTran;<br>&nbsp;&nbsp;&nbsp; gl_Position = ModelTransform * gl_Vertex;<br>&nbsp;&nbsp;&nbsp; gl_TexCoord[0] = gl_MultiTexCoord0;<br>&nbsp;&nbsp;&nbsp; gl_TexCoord[1] = gl_MultiTexCoord1;<br>&nbsp;&nbsp;&nbsp; /* <br>&nbsp;&nbsp;&nbsp; // Reflections.<br>&nbsp;&nbsp;&nbsp; vec3 r = reflect( ecPos.xyz , normal );<br>&nbsp;&nbsp;&nbsp; float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) );<br>&nbsp;&nbsp;&nbsp; gl_TexCoord[1].s = r.x/m + 0.5;<br>&nbsp;&nbsp;&nbsp; gl_TexCoord[1].t = r.y/m + 0.5;<br>&nbsp;&nbsp;&nbsp; */<br>} <p>&nbsp;</p> <p>&nbsp;</p> <p>//注：这是GLSL 2.0， 在用IRR做测试的时候，要选GL驱动方式。</p> <p>&nbsp;</p> <p>还是上个图吧，不上图感觉没有真像。 虽然图看不出来什么动作</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_14DD5/image_2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_14DD5/image_thumb.png" width="526" height="410"></a> </p> <p>为了说明它真的在动，不得不上第二张。 </p> <p>&nbsp;</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_14DD5/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_14DD5/image_thumb_1.png" width="523" height="417"></a>&nbsp;</p> <p>&nbsp;</p> <p>在此，十分感谢Super Tux Kart. 提供了一个学习和扩展irrlicht的榜样.</p>  <img src ="http://www.cppblog.com/Leaf/aggbug/198823.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-03-26 00:08 <a href="http://www.cppblog.com/Leaf/archive/2013/03/26/198823.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>irrlicht引擎：实现天龙八部的RPG角色换装</title><link>http://www.cppblog.com/Leaf/archive/2013/03/24/198762.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sat, 23 Mar 2013 20:08:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2013/03/24/198762.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/198762.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2013/03/24/198762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/198762.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/198762.html</trackback:ping><description><![CDATA[<p>看了看时间，已经3点过了，突然想写点什么，却又不知从何说起。</p> <p>那就从今天这个用irrlicht做天龙八部的模型换装说起吧。</p> <p>&nbsp;</p> <p>也不知道是为什么，最近又捣鼓起了OGRE和irrlicht. 并且，总想用irrlicht实现一些OGRE中的东西。</p> <p>当然，这不是商业项目，也没有商业目的，纯属蛋疼而已。</p> <p>&nbsp;</p> <p>一切行动的由来，都来自于vczh那天晚上的举动。</p> <p>记得有一天晚上在群里聊天，大伙就称赞各位菊苣是多么的厉害。</p> <p>最后vc发了一个自己的桌面截图说：让你们看看菊苣是如何练成的（这不是原话，和话的字眼有出入，在此不想负任何责任，如果真有想看的，去翻群的聊天记录）</p> <p>那天晚上，我想了很久。想想自己自从转做页游以后，是如何虚渡光阴的。</p> <p>终于忍不住了，翻开了自己的移动硬盘，看看自己曾经做过的小东西。90%是建好工程就没理了。</p> <p>这才明白，我花在思考上的时间远远大于了行动。 于是，我决定改变自己，找回那个真的我。</p> <p>&nbsp;</p> <p>3D游戏是我的真爱， 真爱到就算画面差一点，只要是3D，我也会很喜欢。</p> <p>于是，我觉得自己还是应该接着先前的路走下去。 什么服务器，什么 AS3. 都是浮云， 不喜欢就是不喜欢。</p> <p>私下又开始研究irrlicht了。</p> <p>猛地一发现，自己是多么的搞笑， 从09年到11年，一直在做引擎开发， 也翻过irrlicht和ogre无数遍。 却从来就没有写完过一个完整的DEMO。</p> <p>连功能测试用例都没有写过。突然觉得之前的一些设计似乎有些脱离了实际。没有真正使用过，又怎知如何是好，如何是坏呢？</p> <p>&nbsp;</p> <p>这一次是真的玩irrlicht了， 中间也纠结过是不是OGRE更适合。 但在目前这个时间有限的空间下，我更愿意玩irrlicht.小巧，轻便。 当然，意味着更多东西要自己实现。 不过对于一个代码控来说，也反而更自得其乐。 正好可以在短路的时候，去参考一下其它引擎，用来扩充irrlicht.</p> <p>我要做的不是把irrlicht整得牛B，而是想自己弄弄，加上移动平台的崛起，我觉得irrlicht更加适合吧。 据说gameloft也有使用（仅是据说）。</p> <p>&nbsp;</p> <p>可能很多兄弟会说我这讲的东西，其实就是一坨屎了。 不过，我觉得再坏的评论，也表示一种关注。 批评好过于无视啊~~~~</p> <p>----------------------------------------------------------下面说说我遇上的纠结------------------------------------------------</p> <p><strong>纠结1:换装需要场景节点配合</strong></p> <p>&nbsp; 在irrlicht中，并没有提供普通引擎中的submesh或者bodypart这种东西，用于直接支持换装。 在irrlicht中，如果想要换装，最直接的方法就是依赖于场景结点</p> <p>比如,在我的示例中，可以更换头发，帽子，衣服，护腕，靴子，面容。 那就需要7个场景节点，1个作为根节点，用于控制整个角色的世界坐标，平移，缩放，旋转等属性。另外6个场景节点则分别绑有各个部件的模型</p> <p>贴一下我的角色类的代码，行数不多</p> <p>class CCharactor<br>{<br>&nbsp;&nbsp;&nbsp; IrrlichtDevice* m_pDevice;<br>&nbsp;&nbsp;&nbsp; IAnimatedMeshSceneNode* m_pBodyParts[eCBPT_Num];<br>&nbsp;&nbsp;&nbsp; ISceneNode* m_pRoot;<br>public:<br>&nbsp;&nbsp;&nbsp; CCharactor(IrrlichtDevice* pDevice)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :m_pDevice(pDevice)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(m_pBodyParts,0,sizeof(m_pBodyParts));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pRoot = pDevice-&gt;getSceneManager()-&gt;addEmptySceneNode(NULL,12345);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; void changeBodyPart(ECharactorBodyPartType ePartType,stringw&amp; meshPath,stringw&amp; metrialPath)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ISceneManager* smgr = m_pDevice-&gt;getSceneManager();  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IAnimatedMeshSceneNode* pBpNode = m_pBodyParts[ePartType];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IAnimatedMesh* pMesh = smgr-&gt;getMesh(meshPath.c_str());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pMesh==NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pBpNode==NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pBpNode = smgr-&gt;addAnimatedMeshSceneNode(pMesh,m_pRoot);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pBodyParts[ePartType] = pBpNode;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pBpNode-&gt;setMesh(pMesh);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ITexture* pTexture = m_pDevice-&gt;getVideoDriver()-&gt;getTexture(metrialPath.c_str());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pTexture)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pBpNode-&gt;setMaterialTexture(0,pTexture);<br>&nbsp;&nbsp;&nbsp; }<br>};  <p>&nbsp;</p> <p>//然后，我用了一个结构体来构建部件信息</p> <p>struct SBodyPartInfo<br>{<br>&nbsp;&nbsp;&nbsp; stringw Desc;<br>&nbsp;&nbsp;&nbsp; ECharactorBodyPartType Type;<br>&nbsp;&nbsp;&nbsp; stringw MeshPath;<br>&nbsp;&nbsp;&nbsp; stringw MeterialPath;<br>};  <p>&nbsp;</p> <p><strong>纠结2：共享骨骼</strong></p> <p>首先，irrlicht 1.8中对OGRE模型的格式支持在代码中，最高只看到了1.40版本的解析，更高的就会被无视。 天龙八部的模型有几个是1.30的，而用于换装和主角的，都是1.40的。 可能是解析不全的原因，导致1.40的骨骼动画无法正常播放。 这个问题整了几个小时，没有解决，明天继续</p> <p>其次，多个模型共享骨骼只能通过场景节点的useAnimationFrom来完成，并且传入的是一个Mesh参数。这点让人蛋疼， 天龙八部的角色动作是分开了的，不同的攻击动作是一个skeleton文件。 想要实现共享，有点麻烦。</p> <p>&nbsp;</p> <p><strong>纠结3：模型文件格式</strong></p> <p>irrlicht不像OGRE那样有一个强大且成熟的模型文件格式,虽然提供了.irr格式，但仅是用于irrEdit的场景信息输出。先看一张图</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_8.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_thumb_3.png" width="677" height="582"></a> </p> <p>这张图是irrlicht samples中的MeshViewer的提示框内容。 上面列出了可以支持的模型文件类型。 大家可以看看，又有多少模型格式是可以直接拿来放到项目上用的呢？ mdl和ms3d可以考虑，dae的话，我在开源游戏0 A.D. 中见到使用过。 其它的话，就完全不熟悉了。 OGRE的 .mesh支持也不完全。 难道真要自己整一个。 </p> <p>我能想到的，就是选一个插件完整和模型和动画格式都比较好的作为与美术工具交互的格式。 自己再写一个工具，转换成自己的格式。</p> <p>&nbsp;</p> <p><strong>纠结4：硬件蒙皮</strong></p> <p>我以为像NIKO那样的技术狂，怎么会放掉这一个特性。 很高兴地在场景节点上发现了硬件蒙皮的函数接口。但一看注释，把我咽着了。</p> <p>//! (This feature is not implemented in irrlicht yet)<br>virtual bool setHardwareSkinning(bool on);  <p>&nbsp; <p>其它地方，还没有去整，就先不发表言论了。 继续着这个很傻B,很天真的捣鼓之路。  <p>上个图，纪念一下我的irrlicht产物。</p> <p>&nbsp;</p> <p>布衣</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_2.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_thumb.png" width="586" height="450"></a> </p> <p>&nbsp;</p> <p>换了身盔甲</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_4.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_thumb_1.png" width="588" height="461"></a> </p> <p>&nbsp;</p> <p>换了帽子和靴子</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_6.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/irrlicht_354D/image_thumb_2.png" width="590" height="461"></a> </p> <p>&nbsp;</p> <p>PS:头发没有纹理，所以是白的。</p>  <img src ="http://www.cppblog.com/Leaf/aggbug/198762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2013-03-24 04:08 <a href="http://www.cppblog.com/Leaf/archive/2013/03/24/198762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WOW渲染中的纹理压缩</title><link>http://www.cppblog.com/Leaf/archive/2011/01/23/139183.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 23 Jan 2011 12:00:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2011/01/23/139183.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/139183.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2011/01/23/139183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/139183.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/139183.html</trackback:ping><description><![CDATA[<p>一直没玩台服，国服也停留在72级。　成都的天气一天天严峻，更是不想动了，于是用NVPerfHUD挂了下WOW，看看它到底啥样。</p> <p>用NVPerfHUD挂程序需要被挂的程序自身支持，但是网上有一个老兄写的一个NVPerfHUD Any程序可以帮你完成这个功能。对大多数D9的都有效。除非本身做了破解。</p> <p>给大家这个链接，自己去解决个人问题。</p> <p><a title="http://www.thecodeway.com/blog/?p=433" href="http://www.thecodeway.com/blog/?p=433">http://www.thecodeway.com/blog/?p=433</a></p> <p>&nbsp;</p> <p>完美时空的引擎很不错，依然坚挺地用着D8。其实挺想挂来看看的，可惜市面上主流的工具都不支持D8，只有D9以及以后的版本。</p> <p>&nbsp;</p> <p>WOW在CTM的版本中升级了它的渲染引擎，光从水面和火焰就看得出来。　但WOW一向是以负载和流畅度为主，并且WOW本身卖的不是画面。所以若不能帮助提升游戏性的渲染损耗基本上被无视了。　其实吧，个人觉得，做游戏无非就三种了。　一种是追求画面，这一点棒子很有心得。二是追求可玩性，这一点我想WOW应该遥遥领先了吧。　另外一种追求就显得很单纯了－－－钱。　这一点国内许多游戏做得很好，在此就不列名字了，因为比较多。</p> <p>&nbsp;</p> <p>由于上星期在公司测试新写好的材质和资源管理时，发现ALPHA测试的草丛效果很不理想，于是将草的ALPHA混合开启了，一时间，感觉世界被颠倒了。因为草是按簇画的，排序也只能是按簇排序，当开启了ALPHA混合后，草丛与草丛之间没有太多问题，只是草丛本身出现了像素渲染错误。　不管是Z写和Z测试如何选择开启和关闭。都会有问题。</p> <p>突然发现草这个东西很特别。</p> <p>草丛的建模和GPU GEMS 1上的建模方式一样，　三个矩形交叉的方式。</p> <p>１、它需要用ALPHA混合来实现效果</p> <blockquote> <p><font color="#111111">用ALPHA混合的效果是很不错的。因为可以使边缘柔和。毕竟ALPHA测试会因为图片ALPHA值采样引起的误差导致严重的钜齿。其次就是对美术要求太高了，需要严格控制ALPHA与非ALPHA交界处。</font></p></blockquote> <p>２、但它的行为属性却是一个非透明物体。　</p> <blockquote> <p><font color="#111111">这是因为它必须有深度关系，必需写Z。　因为只有写了Z，才能够正确地与特效等透明物混合，并且也只有写了Z，同一草中的多个面片才能够正常混合。</font></p></blockquote> <p>在忽略其它情况下，我试过了Z测试和Z写的四种组合，也未能达到草丛本身和单棵草都同时达到正确的效果。</p> <p>&nbsp;</p> <p>还是多看看别人怎么做的吧，所以上来就比较关心WOW的植被渲染了。　当看到下面这样的纹理的时候，我笑了。没想到BLZ的美术资源节约到了这样的程度，真是细致入微啊（这不是拍马屁，试问有多少美术资源是这样做的）。　可以看出，若不采用这样的半张，而用整张，那么就是128X128的大小。</p> <p>左边为原图，右边为ALPHA通道查看图</p> <p>&nbsp;</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_1.png" width="229" height="113"></a> <a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_2.png" width="239" height="115"></a> </p> <p>&nbsp;</p> <p>而对于树的渲染，则很普通了。设置好纹理，设置好数据和渲染状态，提交绘制即可。</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_8.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_3.png" width="343" height="200"></a>&nbsp;<a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_10.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_4.png" width="263" height="204"></a> </p> <p>为了看到ALPHA混合的效果，我强忍着把WOW在我7300的显卡上开完了效果。以为会咋的，结果啥也没发生。</p> <p>WOW的植被的确没有用到ALPHA混合。我失望了。</p> <p>可能许多人觉得，把多张纹理整合在一张上更好吧。　当然了，这是自然的。　下面就是WOW中经常一起出现的植被的纹理打包图。</p> <p>大家可以看到，右边的图并没有被裁剪，其原因不言而谕（因为想以非对称营造感觉，并且小件物体纹理本身比较小。）。</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_14.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_6.png" width="340" height="344"></a> <a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_16.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_7.png" width="349" height="339"></a> </p> <p>&nbsp;</p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_18.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_8.png" width="693" height="403"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_20.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/Leaf/WindowsLiveWriter/WOW_11903/image_thumb_9.png" width="424" height="338"></a> </p> <p>发现这草用的是两片，并且大小还不一样。　不知是否真的有此必要。</p> <p>&nbsp;</p> <p>&nbsp;</p> <p>我最后要说明的是，这文章就算完了。没有结论，也没有感言。因为那样太俗套！</p><img src ="http://www.cppblog.com/Leaf/aggbug/139183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2011-01-23 20:00 <a href="http://www.cppblog.com/Leaf/archive/2011/01/23/139183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE3D 渲染系统线程化</title><link>http://www.cppblog.com/Leaf/archive/2010/12/20/137076.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 20 Dec 2010 15:14:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/12/20/137076.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/137076.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/12/20/137076.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/137076.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/137076.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 先前研究渲染系统线程化的时候翻到了这篇文章，于是一边看一边写出了汉语。 文中写多地方翻译得很不通顺，见谅。<br><br> <br><br>译者序：偶然在网上看到这篇文章，自己很想仔细研究一下。但搜寻半天不见中文版。于是自己斗胆翻译了一下。文中不免有漏洞百出，甚至可以说有些地方不及Google翻译得好。但这样总的来说是出了一个中文版，而我自己在翻译过程中也会停下来仔细思考。<br><br>OGRE这个线程化的文章很老了。因为OGRE目前已经支持多线程渲染。 这篇文章貌似是某些人研究出来的三个线程化方案，并给出了测试结果。以向OGRE社区证明线程化方案的可行性。 对于许多想研究渲染线程化的人来说，是一篇值得参考的文章。文中提出了许多在不同情况下线程化时遇到的问题，以及需要注意的问题。值得一读/<br><br>&nbsp;&nbsp;<a href='http://www.cppblog.com/Leaf/archive/2010/12/20/137076.html'>阅读全文</a><img src ="http://www.cppblog.com/Leaf/aggbug/137076.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-12-20 23:14 <a href="http://www.cppblog.com/Leaf/archive/2010/12/20/137076.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用控制台写图形程序－－贪食蛇</title><link>http://www.cppblog.com/Leaf/archive/2010/08/20/124043.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Thu, 19 Aug 2010 17:02:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/08/20/124043.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/124043.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/08/20/124043.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/124043.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/124043.html</trackback:ping><description><![CDATA[<div class=paragraph style="TEXT-ALIGN: left">新版源码下载地址. <a href="http://files.cppblog.com/Leaf/CCGL-SnakeGame.rar"><a style="COLOR: red" href="http://files.cppblog.com/Leaf/CCGL-SnakeGame.rar">点击这里</a> </a>(SnakeGame 0.0.1 ) 链接已修正，请放心使用． <br>--------------------------------------------------------------------------------------------------------------------------------<br>先前的版本会闪屏,于是新建了一个缓冲来解决,并将对控制台的"图形"操作抽象了出来,使我们使用的时候就像在使用普通的图形设备一样<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1 设置清屏色<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2 指定像素位置,设置要绘制的内容<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3 提交缓冲区内容.<br><br>新版源码已完成了游戏的基本功能<br>&nbsp;&nbsp; 输入控制<br>&nbsp;&nbsp; 食物检测<br>&nbsp;&nbsp; 障碍检测<br>另外,还收录了部分用于方便设置控制台属性的函数.如窗口图标,窗口句柄等.<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>Windows下的控制台是可以设置前景，背景色的。这给了我一个很不错的想法，用他写图形。<br><br>说好就开工，用了一个小时写了一个贪食蛇。<br><br>完成功能：<br>有一个简单的绘制框架。<br>一个简单的UI。<br>除了食物没有处理外，其余都OK。<br><br>能自动更新，检测碰撞等。。。<br>二话不说，有图有真相。<br><br>蛋疼的朋友可以试试，挺爽的。。。。</div>
<br><a><img class=galleryImageBorder style="BORDER-TOP-WIDTH: 1px; PADDING-RIGHT: 3px; PADDING-LEFT: 3px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; PADDING-BOTTOM: 3px; MARGIN: 10px; PADDING-TOP: 3px; BORDER-RIGHT-WIDTH: 1px" alt=Picture src="http://cuckoogame.weebly.com/uploads/5/0/3/6/5036479/8269882.jpg"></a><a><img class=galleryImageBorder style="BORDER-TOP-WIDTH: 1px; PADDING-RIGHT: 3px; PADDING-LEFT: 3px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; PADDING-BOTTOM: 3px; MARGIN: 10px; PADDING-TOP: 3px; BORDER-RIGHT-WIDTH: 1px" alt=Picture src="http://cuckoogame.weebly.com/uploads/5/0/3/6/5036479/8842867.jpg"></a><a></a>&nbsp;<a><img class=galleryImageBorder style="BORDER-TOP-WIDTH: 1px; PADDING-RIGHT: 3px; PADDING-LEFT: 3px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; PADDING-BOTTOM: 3px; MARGIN: 10px; PADDING-TOP: 3px; BORDER-RIGHT-WIDTH: 1px" alt=Picture src="http://cuckoogame.weebly.com/uploads/5/0/3/6/5036479/7761147.jpg"></a> <br><br>下载地址：<a href="http://files.cppblog.com/Leaf/SnakeGame.rar">源码加执行程序</a> 
<img src ="http://www.cppblog.com/Leaf/aggbug/124043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-08-20 01:02 <a href="http://www.cppblog.com/Leaf/archive/2010/08/20/124043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原]译：一个游戏引擎所应具有的元素</title><link>http://www.cppblog.com/Leaf/archive/2010/04/26/113660.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 26 Apr 2010 15:34:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/04/26/113660.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/113660.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/04/26/113660.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/113660.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/113660.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 3D游戏引擎设计是一项巨大的软件工程。一个人也能写出一个游戏，但这不只是熬一两个晚上便能搞定的，你很可能会出写出几兆的源代码量。如果你没有持久的信念与激情，你早晚会放弃。 当然，别指望你的第一次尝试就能写出完整的引擎，选择一个对引擎需求较小的项目。努力，你就能成功。&nbsp;&nbsp;<a href='http://www.cppblog.com/Leaf/archive/2010/04/26/113660.html'>阅读全文</a><img src ="http://www.cppblog.com/Leaf/aggbug/113660.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-04-26 23:34 <a href="http://www.cppblog.com/Leaf/archive/2010/04/26/113660.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原]irrlicht v1.6 中文例程18 Splitscreen</title><link>http://www.cppblog.com/Leaf/archive/2010/04/26/113563.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 26 Apr 2010 01:50:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/04/26/113563.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/113563.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/04/26/113563.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/113563.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/113563.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这个例程中我们将学习怎么使用irrlicht中的分屏(比如在赛车类游戏中）{<br>我们将创建一个被分为4个部分的视口，有3个固定摄相机和一个用户可以控制的摄相机好，让们从头文件开始吧（我想没有再多说的必要了）&nbsp;&nbsp;<a href='http://www.cppblog.com/Leaf/archive/2010/04/26/113563.html'>阅读全文</a><img src ="http://www.cppblog.com/Leaf/aggbug/113563.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-04-26 09:50 <a href="http://www.cppblog.com/Leaf/archive/2010/04/26/113563.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏开发历程之材质系统</title><link>http://www.cppblog.com/Leaf/archive/2010/04/24/113443.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sat, 24 Apr 2010 08:13:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/04/24/113443.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/113443.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/04/24/113443.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/113443.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/113443.html</trackback:ping><description><![CDATA[<p>材质，这个词有各行各业都有自己的解释。</p>
<p>美工把物体贴图和物体颜色，高光等统称为材质。D3D和OPENGL这样的图形接口则把物体表面贴图单独叫做纹理，而把漫反射，高光等叫做材质。</p>
<p>&nbsp;</p>
<p>而在游戏引擎或图形引擎中提到的材质，则与此不同。 引擎中提到的材质不仅上面的的内容。 引擎中所谓的材质，是指物体在渲染时一系列的状态控制。 如，ALPHA混合开关以及ALPHA混合因子、纹理过虑方式，纹理通道状态、纹理矩阵、裁剪模式等，在D3D中，就是SetRenderState,SetTextureStageState,SetSamplerStateState等所控制的。在OPENGL中，则大多数由glEnable所控制。</p>
<p>&nbsp;</p>
<p>我们所提到的材质系统，则是以此为基础展开的。 上面提到的这些因子，组成了我们的材质。 也是我们在渲染一个物体的时候，提交到设备的状态控制值。 一个物体的一次渲染，我们称作一个PASS。于是我们顺其自然地将这个渲染时的材质控制的最小单位命名为Pass,则：</p>
<p>&nbsp;</p>
<p>struct TextureState</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; void* Texture;</p>
<p>&nbsp;&nbsp;&nbsp; int ColorOp;</p>
<p>&nbsp;&nbsp;&nbsp; int ColorAgr1;</p>
<p>&nbsp;&nbsp;&nbsp; int ColorAgr2;</p>
<p>&nbsp;&nbsp;&nbsp; int AlphaOp;</p>
<p>&nbsp;&nbsp;&nbsp; int AlphaAgr1;</p>
<p>&nbsp;&nbsp;&nbsp; int AlphaAgr2;</p>
<p>.....//更多内容</p>
<p>};</p>
<p>&nbsp;</p>
<p>class CPass</p>
<p>{</p>
<p>&nbsp;&nbsp; CColor mAmbient;</p>
<p>&nbsp;&nbsp; CColor mDiffuse;</p>
<p>&nbsp;&nbsp; ....更多内容</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; bool mAlphaEnable;</p>
<p>&nbsp;&nbsp;&nbsp; int mScrBlend;</p>
<p>&nbsp;&nbsp;&nbsp; int mDstBlend;</p>
<p>&nbsp;&nbsp;&nbsp; int mCullMode;</p>
<p>&nbsp;&nbsp; TextureState[4] mTextureStates;</p>
<p>&nbsp; ...更多内容</p>
<p>};</p>
<p>&nbsp;</p>
<p>当我们渲染一个物体的时候，只需要将这个类里面的状态应用到设备，即可完成对物体的绘制。</p>
<p>&nbsp;</p>
<p>材质系统的基本内容就是这些，这也是最容易做到的事情。</p>
<p>但是，我们都知道，像D3D或OPENGL这样的图形接口每设置一次GPU状态的时候，都会有一定的开销（通过查看相关文档可以看到某些函数的具体开销值）。而为了保证我们的渲染流畅，我们不得不减少这样的开销。</p>
<p>&nbsp;</p>
<p>很自然地，我们会想到，尽量减少切换。而如何减少切换呢。我们可以记录下自己的硬件状态，在设置下一个的时候，先判断当前硬件状态是否相同，如果相同。则不用再设置。 虽然某些图形接口在其层底做了类似的功能。但我们外部判断一下，也未尝不可。从D3D上来讲，外部判断比让其内部判断效率更佳。需要注意的是，由于我们在自己的程序里做了相同判断。因此，当有另一个程序也修改设备状态的时候，就会产生意想不到的效果。所以，我们应该适当的查询一下设备状态，并更新自己的状态记录表。至于这个查询间隔，就要根据自己的实际情况来测试了。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>通过记录状态的方法来提升的效率是很不明显的，因此，我们需要对材质进行排序，至于怎么排序，这算法上的问题，在此先不作过多解释。 总之，我们将相似的材质排在一起。由于材质很相似，绘完一个再绘下一个的时候，减少了切换，从而大大提升了效率。</p>
<p>&nbsp;</p>
<p>既然已经是涉及到设备了，我们总得考虑一下设备问题。 如果设备不支持我们当前给定的材质状态，怎么办？ 返回FALSE不渲染。还是让程序DOWN掉？&nbsp; 对于一些重要的性能，设备不支持让程序DOWN掉是最好的做法，但是，对于像纹理混合通道不足的情况，让其DOWN掉就显得划不来了。毕竟我们设计的游戏程序是想让更多的玩家能玩不是？ 这样就会涉及到PASS的拆分问题。</p>
<p>&nbsp;</p>
<p>对于PASS的拆分，OGRE已经做得很好了。根据用户的设备性能，如果不满足，就一直拆分，拆到用户满足为止，最后让一个物体渲染多次，来实现多个纹理通道混合的效果。 而多PASS则是在渲染的时候不得不考虑的地方，毕竟有些物殊效果非得用多PASS不可。</p>
<p>&nbsp;</p>
<p>对于多PASS的设计，我们可以参考OGRE的材质方法或是D3DX的效果框架。我们把完成一个最终效果的方案称作一个渲染技术 Technique ，一个技术可由多个Pass来完成。</p>
<p>&nbsp;</p>
<p>class Technique</p>
<p>{</p>
<p>...更多内容</p>
<p>&nbsp;vector&lt;CPass*&gt; mPasses;</p>
<p>};</p>
<p>&nbsp;</p>
<p>这样就满足了我们的需求。&nbsp; 对于同一种效果，我们可以提供多种Technique供程序选择。 而这个选择的条件则可以是根据硬件性能，或是玩家手动选择的配置来实现。</p>
<p>最后结构如下：</p>
<p>class CMaterial</p>
<p>{</p>
<p>public:</p>
<p>...更多内容</p>
<p>vector&lt;CTechnique*&gt; mRenderTechs</p>
<p>};</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>乱七八糟地说了一通，希望没晕死人！！！</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/113443.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-04-24 16:13 <a href="http://www.cppblog.com/Leaf/archive/2010/04/24/113443.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>监听器在游戏开发中的应用----消息回调</title><link>http://www.cppblog.com/Leaf/archive/2010/04/24/113430.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sat, 24 Apr 2010 06:39:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2010/04/24/113430.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/113430.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2010/04/24/113430.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/113430.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/113430.html</trackback:ping><description><![CDATA[<p>我一向是不太喜欢给一些东西强加上个名字。但为了随波逐流，我还是这样做了。</p>
<p>&nbsp;</p>
<p>在我们的游戏开发中，通常会遇到两个模块之间的通信。 回调估计是最常用的方式了。 回调的设计思想很简单，就是两个对象相互注册，然后在需要的时候调用对方的函数。</p>
<p>如下：</p>
<p>&nbsp;</p>
<p><textarea class=cpp rows=15 cols=50 name=code>class B;
class A
{
public:
void RegisterCallback(B* pB)
{
m_pB = pB;
}
void ActiveA()
{
m_pB-&gt;DoB();
}
void DoA(){}
protected:
B* m_pB;
};
class B
{
public:
void RegisterCallback(A* pA)
{
m_pA = pA;
}
void ActiveB()
{
m_pA-&gt;DoA();
}
void DoB(){}
protected:
A*	m_pA;
};
B b;
A a;
b.RegisterCallback(&amp;a);
a.RegisterCallback(&amp;b);
a.ActiveA();
b.ActiveB();</textarea> </p>
<p>&nbsp;</p>
<p>这样，当A执行自己的某些动作的时候，就调用B的函数，这样B就会进行自己的更新或是一些处理。</p>
<p>&nbsp;</p>
<p>但是，由于两个对象的直接回调，导致了许多不方便之处。特别是当A和B的功能需要扩展的时候。例如：现在A在执行过程中，需要调用B中其它的功能函数。这时候就不得不修改A和B的接口。然后大家都重新编译，连接，执行。</p>
<p>&nbsp;</p>
<p>于是，我们就会想会不会有一种更好的方法来解决这一问题。 大家可以想想，WINDOWS中的通信机制：通过解析消息类型来进行处理。是的，消息回调的好处就是方便扩展。 当然我们这里要讲的不是像WINDOWS中那样的消息通信机制，对于我们来说，那种做法过繁琐。</p>
<p>假设现在是想让A通知B一些事情。那么，我们可以把B的void DoB();函数作一点点修改：</p>
<p><textarea class=cpp:nocontrols:collapse rows=15 cols=50 name=code>void DoB(int MsgID)
{
switch(MsgID)
{
case 0:
//做相应的事情
break;
case 1:
//做相应的事情
break;
case 2:
//做相应的事情
break
default:
break;
}
}
</textarea> </p>
<p>&nbsp;</p>
<p>同理，当B要通知A的时候，也这样做就行了。</p>
<p>&nbsp;</p>
<p>但是，这样也很麻烦，关键在于，如果现在写类A的人并不知道类B的人会怎么写，或者说，类B不知道什么时候要写。另外，如果我们强制类B要实现这样的接口，会有点不现实。 </p>
<p>&nbsp;</p>
<p>此时，我们决定使用一个中间对象来连接他们。</p>
<p>&nbsp;</p>
<p><br><textarea class=c-sharp rows=15 cols=50 name=code>class ICallback
{
public:
virtual void Do(int MsgID) = 0;
};
</textarea> </p>
<p>这就是我们传说中的监听器了。 在OGRE或是一些广泛采用面向对象思想的源程序中，随处可见这样的模式。</p>
<p>&nbsp;</p>
<p>还是假设是A需要通知B一些事情。那么，可以在A中注册这个对象，然后调用它的方法就可以了。</p>
<p><textarea class=c-sharp rows=15 cols=50 name=code>class A
{
public:
A()
{
m_pCallback = NULL;
}
void RegisterCallback(ICallback* pCall)
{
m_pCallback = pCall;
}
void ActiveA(int MsgID)
{
if(m_pCallback != NULL)
m_pCallback-&gt;Do(MsgID);
}
protected:
m_pCallback;
};
</textarea> </p>
<p>&nbsp;</p>
<p>而我们在实现B的时候，除了要实现B自己的东西以外，还需要将ICallBack派生并实现 void Do(int Msg)函数；</p>
<p><textarea class=cpp rows=15 cols=50 name=code>class B
{
public:
class CCallback:public ICallback
{
public:
CCallback(B* pB){  m_pB = pB; }
void Do(int Msg)
{
switch(Msg)
{
case 0:
m_pB-&gt;DoB();
break;
case 1:
.....
}
}
private:
B* m_pB;
}
B( )
{
m_pCall = new CCallback(this);
}
void SetA(A* pA){ m_pA = pA;  m_pA-&gt;RegisterCallback(m_pCall);}
protected:
A* m_pA;
}
</textarea> </p>
<p>&nbsp;</p>
<p>这样，双方便很自然地通了信。而写类A的人根本不需要理会类B的人会怎么写，也不用去管类B会是什么样的类名。只要告诉写类B的人，你需要实现这个Callback接口，并且对应的MsgID是干什么用的就OK了。</p>
<p>&nbsp;</p>
<p>也许初初的一看，这是吃力不讨好的工作。毕竟一个写A的人，会去想那么多事情。 而一个写B的人，还要去实现一个Callback类。但是，从可扩展性，和降低耦合上来讲，的确会起不少的作用。 </p>
<p>&nbsp;</p>
<p>而上面的void Do(int MsgID);函数，可以做得更强大一点。</p>
<p>写成void Do(void* pData); 而这个pData怎么使用，就要看A和B通信的具体内容了。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>我正尽力地试着把自己想要说的讲清楚，谢谢！</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/113430.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2010-04-24 14:39 <a href="http://www.cppblog.com/Leaf/archive/2010/04/24/113430.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>改进后的 3D贪食蛇</title><link>http://www.cppblog.com/Leaf/archive/2009/06/04/86732.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Thu, 04 Jun 2009 03:46:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/06/04/86732.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/86732.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/06/04/86732.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/86732.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/86732.html</trackback:ping><description><![CDATA[一开始老是觉得哪里不对劲，后来发现是自己的光照没有设置好。<br>如图的效果就好多了。哈哈<br><br>还是没有看到哪里加附件，于是我把两个小游戏的代码和执行文件发到&#8220;文件&#8221;里面了，可以去那里下载<br>有什么新想法的，大家可以交流。特别是这个贪食蛇的场景。<br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/3DSnake.jpg" width=1024 height=768> 
<img src ="http://www.cppblog.com/Leaf/aggbug/86732.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-06-04 11:46 <a href="http://www.cppblog.com/Leaf/archive/2009/06/04/86732.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自己做的小游戏：墙中公主</title><link>http://www.cppblog.com/Leaf/archive/2009/06/01/86340.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 31 May 2009 18:21:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/06/01/86340.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/86340.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/06/01/86340.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/86340.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/86340.html</trackback:ping><description><![CDATA[<p>手机（联想）上有一个游戏叫墙中公主，就是移动光标，如果光标周围有三个以上颜色相同的方块。则可以消去。<br>玩着玩着就想自己写一个来试试。当然不是手机版。而是PC版的。游戏很简单。代码就不粘上来了。发张效果图。呵呵<br></p>
<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/wall2.jpg" width=636 height=447> <br><br>我想发上来呀。但是传到文件里了，不知道怎么插到这里来。。<br><br>游戏还有一点未完。没有记分系统。还有就是游戏提示那里少了个&nbsp;&nbsp; 空格：消除方块 
<img src ="http://www.cppblog.com/Leaf/aggbug/86340.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-06-01 02:21 <a href="http://www.cppblog.com/Leaf/archive/2009/06/01/86340.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HGE中的游戏GUI代码阅读</title><link>http://www.cppblog.com/Leaf/archive/2009/05/18/83270.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 18 May 2009 04:47:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/18/83270.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83270.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/18/83270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83270.html</trackback:ping><description><![CDATA[<font face=宋体>原</font>作者 : Kevin Lynx<br>BLOG: <a href="http://blog.csdn.net/kevinlynx" target=_blank><u><font color=#0000ff>http://blog.csdn.net/kevinlynx</font></u></a><br><br><br>第一部分：<br><br>HGE helper类中的GUI：<br><br>引擎版本：1.60release。<br><br><br><br>文件：hgegui.h , hgegui.cpp, hgeguictrls.h , hgeguictrls.cpp <br><br>大致类图：<br><br><img border=0 src="http://www.freegames.com.cn/school/UploadPic/2007-4/200745184544144.jpg" onload="return imgzoom(this,550)"><br><br>其中，hgeGUIObject是抽象基类，具体的控件类如按钮，文本标签，都是从它派生而来。HgeGUI属于整个GUI系统的manager，它会保存所有的控件。<br><br>关于hgeGUIObject的8个成员变量，文档里已经有所描述：<br><br>int id; //控件ID<br><br>bool bStatic; //是否可以接受键盘焦点<br><br>bool bVisible; //是否可见<br><br>bool bEnabled; //是否有效<br><br>hgeRect rect; //控件大小<br><br><br><br>hgeGUI *gui; //其属于的 manager ，相当于父对象<br><br>hgeGUIObject *next; //用于双向链表，把所有控件连接在一起<br><br>hgeGUIObject *prev;<br><br>static HGE *hge; //方便使用HGE接口<br><br>关于其部分接口的描述：<br><br>Render: 用于渲染控件到屏幕上<br><br>Update: 用于更新其动画<br><br>Enter: 控件刚显示时的动画<br><br>Leave: 控件要消失时的动画<br><br>IsDone: 控件刚显示和消失时的状态查询函数<br><br><br><br>该类也就是提供了一个抽象而已，利用C++语言的多态机制来方便hgeGUI管理所有的控件。其他具体的控件继承了hgeGUIObject后，必须实现构造函数和Render函数。<br><br><br><br>关于hgeGUI：<br><br>这个类应该属于manager。它负责管理所有的控件。<br><br>其数据成员：<br><br>hgeGUIObject *ctrls; //保存所有控件<br><br>hgeGUIObject *ctrlLock; //可能是用来保存当前被鼠标操作的控件<br><br>hgeGUIObject *ctrlFocus; //保存焦点控件<br><br>hgeGUIObject *ctrlOver; //用来保存鼠标指针所指的控件<br><br><br><br>int navmode;<br><br>int nEnterLeave;<br><br>hgeSprite *sprCursor; //渲染鼠标指针用的<br><br><br><br>float mx,my; //鼠标坐标<br><br>int nWheel; //滚轮偏移量<br><br>bool bLPressed, bLLastPressed;//本帧左键状态，上一帧的左键状态<br><br>bool bRPressed, bRLastPressed;<br><br>其Update方法会负责控件的进入和离开动画，还会负责整体的状态设置---哪些控件拥有焦点，哪些控件被鼠标正在操作，哪些控件正被鼠标指针指着，这些控件它都会保存起来。（也就是说，我们还是可以通过检查控件的状态来设置当鼠标指针在其上时的新动画。）<br><br><br><br>总体而言，该引擎的GUI还是很简单的。一个manager，负责管理所有的控件，然后一个抽象基类，用来协助manager管理---其他具体的控件都必须从那个抽象基类派生。<br><br><br><br>下面具体看一个Button控件：<br><br>Button类的定义如下：<br><br>class hgeGUIButton : public hgeGUIObject<br><br>{<br><br>public:<br><br>hgeGUIButton(int id, float x, float y, float w, float h, HTEXTURE tex, float tx, float ty);<br><br>virtual ~hgeGUIButton();<br><br><br><br>void SetMode(bool _bTrigger) { bTrigger=_bTrigger; }<br><br>void SetState(bool _bPressed) { bPressed=_bPressed; }<br><br>bool GetState() const { return bPressed; }<br><br><br><br>virtual void Render();<br><br>virtual bool MouseLButton(bool bDown);<br><br><br><br>private:<br><br>bool bTrigger;<br><br>bool bPressed;<br><br>bool bOldState;<br><br>hgeSprite *sprUp, *sprDown;<br><br>};<br><br>其中bTrigger表示该按钮的行为是否象一个RadioButton，bPressed表示当前按钮是否被按下，bOldState表示上一次按钮状态，特别用来实现bTrigger的，sprUp,sprDown分别用来绘制弹起和按下时的按钮外观。这两个精灵的创建都是从构造函数的tex上创建而来的，它要求两个状态必须保存在一幅纹理上，且顺序为从左至右。<br><br>按钮的实现代码也很简单：<br><br>void hgeGUIButton::Render()<br><br>{<br><br>if(bPressed) sprDown-&gt;Render(rect.x1, rect.y1);<br><br>else sprUp-&gt;Render(rect.x1, rect.y1);<br><br>}<br><br><br><br>bool hgeGUIButton::MouseLButton(bool bDown)<br><br>{<br><br>if(bDown)<br><br>{<br><br>bOldState=bPressed; bPressed=true;<br><br>return false;<br><br>}<br><br>else<br><br>{<br><br>if(bTrigger) bPressed=!bOldState;<br><br>else bPressed=false;<br><br>return true; <br><br>}<br><br>}<br><br><br><br>联系起来，当hgeGUI::Update里处理ProcessCtrl时，如果鼠标左键按下且其指针在按钮范围内，那么就调用hgeGUIButton::MouseLButton( true )，这个时候button的bPressed=true,那么在渲染的时候，自然就表现出被按下时的状态。<br><br>事实上对于这种类型的按钮---如同windows下的窗体按钮，我们一般不检查其是否被按下，而是检查其是否发生了clicked 这个事件，而这个事件是在先按下在弹起的情况下发生的。因此，判断该事件发生的条件就为：上一帧状态被按下，这一帧没被按下。<br><br><br><br>虽然HGE引擎的GUI很简单，但是其扩展性很好。因为hgeGUI::Update基本上派发了所有控件需要的消息---键盘操作，以及鼠标操作；而hgeGUIObject基类的很多成员函数都会处理这些消息，我们只需要派生hgeGUIObject，然后重载我们需要的消息处理即可。<br><br>第二部分：<br><br>HGE扩展GUI库，从HGE官方论坛下载（作者不明）：<br><br>工程结构：<br><br><img border=0 src="http://www.freegames.com.cn/school/UploadPic/2007-4/200745184544660.jpg" onload="return imgzoom(this,550)"><br><br>其中，guitest.cpp为测试文件。<br><br><br><br>类结构：<br><br><img border=0 src="http://www.freegames.com.cn/school/UploadPic/2007-4/200745184544686.jpg" onload="return imgzoom(this,550)"><br><br>整个系统的工作原理：<br><br>用户继承抽象基类GUIApp，实现具体的OnEvent函数，然后该类会管理所有的GUIAppWindow对象，GUIAppWindow窗口对象会管理其上的所有子控件。<br><br><br><br>相应地，GUIApp派生类会直接得到鼠标和键盘消息，然后派发给所有窗口对象，然后窗口对象再把消息派发到具体的控件对象上。<br><br><br><br>这种Parent-Child关系大致为：<br><img border=0 src="http://www.freegames.com.cn/school/UploadPic/2007-4/200745184544844.jpg" onload="return imgzoom(this,550)"><br><br>GUIAppWindow类保存有其所属的GUIApp对象指针，每个具体的控件又保存有其所属的GUIAppWindow 的对象指针。<br><br><br><br>当一个控件处理了某个事件后，例如按钮处理了鼠标单击事件，它就需要告诉外界用户单击了这个按钮。这里采用的方法是：在基类GUIAppObject里定义了一个虚函数OnEvent( int id)<br><br>，然后在其派生类GUIAppWindow里把这个函数重载为纯虚函数，函数有一个参数，那就是控件ID。当一个控件处理了某个事件后，就通过其内部保存的父窗口指针来调用OnEvent函数，然后GUIAppWindow的派生类---如果该类能产生对象，那么其必然实现了OnEvent的具体代码（这就是为什么在GUIAppWindow里要把OnEvent又重载为纯虚函数的原因），然后在此代码里，窗口根据传进来的控件ID来得知哪个控件发生了事件！<br><br><br><br>GUIAppWindow里有一个容器，它保存了所有该窗口上的控件。<br><br><br><br>所有控件再创建时，都是以其父窗口为参考坐标系的，也就是相对坐标，但是其实际保存的坐标却是绝对坐标—既相对于整个屏幕的坐标（如果是窗口程序，就相对于整个窗口）。大致过程为：在GUIAppWindow派生类中创建子控件时，给子控件指定的坐标为相对坐标，然后当 AddCtrl 时就会重新把子控件的坐标改变为绝对坐标。<br><br><br><br>GUIApp里直接有了BeginScene和EndScene的渲染代码。<br><br><br><br>要使用该扩展库，大致步骤为：<br><br>1． 继承GUIAppWindow类，在这个派生类里重载具体的处理OnEvent的函数，并创建所有该窗口上的子控件。<br><br>2． 继承GUIApp类，在这个派生类中创建窗口对象，并把窗口对象AddCtrl，在这里可以进行其他的初始化工作<br><br>3． 在FrameFunc里调用GUIApp::FrameFunc函数。<br><br><br><br>总体而言，这个扩展GUI主要是扩展了GUI Manager以及GUI Object，并且加入了Parent-Child机制。比较经典的部分在于提供了一个 OnEvent 函数，这样就可以让客户程序员能够得知窗体上的控件发生的事件。 ----其实这种方法的目的就跟Windows中的消息机制，Qt中的signal/slot机制一样。<br>
<img src ="http://www.cppblog.com/Leaf/aggbug/83270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-18 12:47 <a href="http://www.cppblog.com/Leaf/archive/2009/05/18/83270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（OGRE）Overlay中文显示 </title><link>http://www.cppblog.com/Leaf/archive/2009/05/17/83234.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 17 May 2009 15:59:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/17/83234.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83234.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/17/83234.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83234.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83234.html</trackback:ping><description><![CDATA[<div>
<p><span>近日很多朋友咨询<span>Overlay</span>中文显示问题，回答的多了想索性再写个文档算了，放在网上共享，于是就有了本篇。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在<span>Ogre1.2.5</span>版本中，通过与<span>Ogre</span>官方论坛的开发者讨论实现了<span>Overlay</span>的中文显示，当初的实现非常的怪异，具体的实现可以参见<span>Ogre</span>官方论坛。</span></p>
<p><span>随着<span>Ogre</span>的更新，现在<span>Ogre</span>已经发布了<span>1.4.7</span>，<span>1.4</span>系列版本有一个重要的改进，就是加入了<span>UTFString</span>，这为<span>Ogre</span>中文显示予以很大的帮助。为了便于演示，我直接使用<span>Ogre</span>自带的<span>Overlay</span>，也就是大家熟悉的<span>DebugOverlay</span>，测试工程我选择<span>Demo_ParticleFX</span>，选择其他的也没有关系。现在编译它，运行后得到下图：</span></p>
<p align=left></p>
<p align=left><span><img border=0 alt="" src="http://images.cnblogs.com/cnblogs_com/gogoplayer/Ogre/overlay_eng.JPG" width=808 height=634><br>图的最左下角显示的就是英文<span>DebugOverlay</span>，接下来我们的任务就是把它编程中文的，<span>^_^</span>。</span></p>
<p align=left><span>Overlay</span><span>中文化操作步骤如下</span></p>
<p align=left><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>打开<span>OgreSDK\media\packs\</span></span><span> </span><span>OgreCore.zip</span><span>。</span></p>
<p align=left><span><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>打开<span>C:\WINDOWS\Fonts</span>，把<span>simhei.ttf</span>添加到<span>OgreCore.zip</span>，（什么，没有<span>simhei.ttf</span>这个文件，那就还其他的中文<span>ttf</span>字体吧）。</span></p>
<p align=left><span><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>打开<span>OgreCore.zip</span>中的<span>Ogre.fontdef</span>，里面有<span>BlueHighway</span>这个字体定义块，在他的下面添加我们的<span>SimHei</span>，<span>code_points</span>里面的一大堆数字看不明白没关系，随后文章会解释。</span></p>
<p align=left><span>SimHei</span></p>
<p align=left><span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>type <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>truetype</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>source <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>simhei.ttf</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>size <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>16</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>resolution &nbsp;96</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>code_points 33-166 24403-24403 21069-21069 24103-24103 36895-36895 29575-29575 24179-24179 22343-22343 26368-26368 39640-39640 20302-20302 19977-19977 35282-35282 24418-24418 25968-25968 37327-37327 25209-25209 27425-27425 </span></p>
<p align=left><span>}</span></p>
<p align=left><span><span>4.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>打开<span>OgreCore.zip</span>中的<span>OgreDebugPanel.overlay</span>，把<span>BlueHighway</span>全部替换成<span>SimHei</span>，我们要使用中文字体了，嘿嘿。</span></p>
<p align=left><span><span>5.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>修改完成后，确保所做的修改已经保存到<span>OgreCore.zip</span>。</span></p>
<p align=left><span><span>6.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>进入<span>Ogre</span>解决方案，打开文件<span>ExampleFrameListener.h</span>，把<span>54-59</span>行的代码替换如下：</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String currFps = <span>"Current FPS: "</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String avgFps = <span>"Average FPS: "</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String bestFps = <span>"Best FPS: "</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String worstFps = <span>"Worst FPS: "</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String tris = <span>"Triangle Count: "</span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> String batches = <span>"Batch Count: "</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString currFps = L<span>"</span></span><span>当前帧速率<span>: "</span></span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString avgFps = L<span>"</span></span><span>平均帧速率<span>: "</span></span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString bestFps = L<span>"</span></span><span>最高帧速率<span>: "</span></span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString worstFps = L<span>"</span></span><span>最低帧速率<span>: "</span></span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString tris = L<span>"</span></span><span>三角形数量<span>: "</span></span><span>;</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>static</span> DisplayString batches = L<span>"</span></span><span>批次<span>: "</span></span><span>;</span></p>
<p align=left><span><span>7.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>最后重新编译工程，下面是我运行的截图，是不是已经显示中文了，<span>^_^</span>。</span></p>
<p align=left></p>
<p align=left><span><span><img border=0 alt="" src="http://images.cnblogs.com/cnblogs_com/gogoplayer/Ogre/overlay_chinese.JPG" width=808 height=634><br></span></span></p>
<p align=left><span>现在再来看看<span>SimHei</span>中的<span>code_points</span>是如何生成的，这个可以参考我上次写的这篇文章<span><a href="http://www.cnblogs.com/gogoplayer/archive/2008/05/09/1189795.html">http://www.cnblogs.com/gogoplayer/archive/2008/05/09/1189795.html</a></span>，至此，实现<span>Overlay</span>中文显示。</span></p>
<p align=left><span>转载请注明出处：</span></p>
<p align=left><span>作者：<span>gogoplayer</span></span></p>
<p align=left><span>E-mail : gogoplayer@163.com</span></p>
<p align=left><span>QQ : 78939328</span></p>
<span><a href="http://www.gogoplayer.com.cn/"><span>http://www.gogoplayer.com.cn</span></a></span></div>
<SCRIPT type=text/javascript>
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90);
//]]&gt;
</SCRIPT>
<img src ="http://www.cppblog.com/Leaf/aggbug/83234.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-17 23:59 <a href="http://www.cppblog.com/Leaf/archive/2009/05/17/83234.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE主要渲染流程简介</title><link>http://www.cppblog.com/Leaf/archive/2009/05/17/83233.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 17 May 2009 15:56:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/17/83233.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83233.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/17/83233.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83233.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83233.html</trackback:ping><description><![CDATA[<div class=postbody><font size=5>OGRE主要渲染流程简介</font>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 谢伟亮&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; feiyurainy@163.com</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 转载请注明出处</p>
<p>&nbsp;&nbsp;&nbsp; 很早以前就想写一些关于<span>OGRE</span><span>的文章了，一直没机会。</span> </p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;理解一个渲染引擎，我觉得最重要的是先抓住了它的主架构，它的主线，渲染流程，不然的话，一个引擎几万行，甚至几十万行的代码，光是打开</span><span>solution</span><span>就能吓你一跳了，</span><span>OGRE</span><span>也有十几万行的代码量，我一开始看它的时候也是无从下手，感觉代码太多了，不知道从哪开始看好，这个</span><span>class</span><span>看看，那个</span><span>class</span><span>看看，由于对整个引擎没有一个清晰的认识，看过了也印象不深，所以，最后，还是决定先找出它的主线，了解它的渲染流程，这样才能有机地把各个部分联系起来。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;这篇短文也是对</span><span>OGRE</span><span>的主要渲染流程的一个介绍，可能对一些</span><span>class</span><span>不会太多地去介绍具体的实现细节。我所用的代码都是取自于</span><span>OGRE</span><span>的最新的</span><span>CVS</span><span>版本。</span></p>
<p><span>&nbsp;&nbsp;&nbsp; 读者最好对</span><span>OGRE</span><span>有一定的了解，至少得看懂它的</span><span>example</span><span>，不然可能一些东西理解起来比较困难。对</span><span>D3D</span><span>，</span><span>OPENGL</span><span>有一定了解更好。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;如果你看过</span><span>D3D SDK</span><span>中带的例子，你一定知道一个比较简单的</span><span>3D</span><span>程序要运行起来，至少都会涉及以下的几部分：</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;首先是数据的来源，包括顶点数据，纹理数据等，这些数据可以从文件中读取，也可以在程序运行时生成。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;接下来，我们会建立顶点缓冲区把顶点保存起来，建立</span><span>texture</span><span>对象来表示</span><span>texture</span><span>，对顶点组成的物体设置它在世界坐标系下的坐标，设置摄像机的位置，视点，设置</span><span>viewport</span><span>的位置和大小，然后就可以在渲染循环中开始调用渲染操作了，经过了</span><span>front buffer</span><span>和</span><span>back buffer</span><span>的交换，我们就能在屏幕上看到</span><span>3D</span><span>图形了，伪代码如下：</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setupVertexBuffer</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setWorldTransform</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCamera</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setProjectionTransform</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setViewport</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>beginFrame</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setTexture</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawObject</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endFrame</span>&nbsp;</p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp; 以下就是渲染一个物体的主要步骤，在我看来，这就是</span><span>3D</span><span>程序的主线，同样道理，无论你多复杂的渲染引擎，都得实现上述的这些步骤，其他的一些效果如阴影，光照等，都是附着在这条主线上的，所以，如果你能在你所研究的渲染引擎上也清晰地看到这条主线，可能对你深入地研究它会大有帮助，下面，我们就一起来找到</span><span>OGRE</span><span>中的这条主线。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OGRE</span><span>的渲染循环都是起源于</span><span>Root::renderOneFrame</span><span>，这个函数在</span><span>OGRE</span><span>自带的</span><span>example</span><span>中是不会显式调用的，因为</span><span>example</span><span>都调用了</span><span>Root::startRendering</span><span>，由</span><span>startRendering</span><span>来调用</span><span>renderOneFrame</span><span>，如果你用</span><span>OGRE</span><span>来写真正的游戏，或者编辑器，你可能就需要在的消息主循环中调用</span><span>renderOneFrame</span><span>了，顾名思义，这个函数就是对整个</span><span>OGRE</span><span>进行一帧的更新，包括动画，渲染状态的改变，渲染</span><span>api</span><span>的调用等，在这个函数中，会包括了我们上述伪代码的几乎全部内容，所以是本文的重点所在。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;进入</span><span>renderOneFrame</span><span>，可以看到头尾两个</span><span>fire</span><span>函数，这种函数在</span><span>OGRE</span><span>中经常出现，一般都是</span><span>fire&#8230;start</span><span>和</span><span>fire&#8230;end</span><span>一起出现的，在这些函数中，可能会处理一些用户自定义的操作，如</span><span>_fireFrameStarted</span><span>就会对所以的</span><span>frameListener</span><span>进行处理，这些</span><span>fire</span><span>函数可以暂时不用理会，继续看</span><span>_updateAllRenderTargets</span><span>，在这个函数中，会委派当前所用的</span><span>renderer</span><span>对所有创建出来的</span><span>render target</span><span>进行</span><span>update</span><span>，</span><span>render target</span><span>也就是渲染的目的地，一般会有两种，一种是</span><span>render texture</span><span>，一种是</span><span>render buffer</span><span>，接着进入</span><span>RenderSystem::_updateAllRenderTargets</span><span>，可以看到在</span><span>render system</span><span>中，对创建出来的</span><span>render target</span><span>是用</span><span>RenderTargetPriorityMap</span><span>来保存的，以便按照一定的顺序来对</span><span>render target</span><span>进行</span><span>update</span><span>，因为在渲染物体到</span><span>render buffer</span><span>时，一般会用到之前渲染好的</span><span>render texture</span><span>，所以</span><span>render texture</span><span>形式的</span><span>render target</span><span>需要在</span><span>render buffer</span><span>之前进行更新。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;进入</span><span>render target</span><span>的</span><span>update</span><span>，可以看到，它仍然把</span><span>update</span><span>操作继续传递下去，调用所有挂在这个</span><span>render target</span><span>上的</span><span>viewport</span><span>的</span><span>update</span><span>。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Viewport</span><span>其实就是定义了</span><span>render target</span><span>上的一块要进行更新的区域，所以一个</span><span>render target</span><span>是可以挂多个</span><span>viewport</span><span>的，以实现多人对战时分屏，或者是画中画等效果，可以把</span><span>OGRE</span><span>中的</span><span>viewport</span><span>看成是保存</span><span>camera</span><span>和</span><span>rendertarget</span><span>这两者的组合，把</span><span>viewport</span><span>中所定义的</span><span>camera</span><span>所看到的场景内容渲染到</span><span>viewport</span><span>所定义的</span><span>render target</span><span>的区域里。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Viewport</span><span>还有一个重要信息是</span><span>ZOrder</span><span>，可以看到</span><span>RenderTarget</span><span>中的</span><span>ViewportList</span><span>带有一个比较函数，所以在</span><span>RenderTarget::update</span><span>中，</span><span>ZOrder</span><span>越小的，越先被渲染，所以，如果两个</span><span>viewport</span><span>所定义的区域互相重叠了，而且</span><span>ZOrder</span><span>又不一样，最终的效果就是</span><span>ZOrder</span><span>小的</span><span>viewport</span><span>的内容会被</span><span>ZOrder</span><span>大的</span><span>viewport</span><span>的内容所覆盖。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;继续进入</span><span>Viewport::update</span><span>，就像前面所说，它调用它所引用的</span><span>camera</span><span>来渲染整个场景，而在</span><span>Camera::_renderScene</span><span>中，是调用</span><span>SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)</span><span>。</span><span>SceneManager::_renderScene</span><span>里就是具体的渲染流程了。从函数名称还有参数也可以看出来，这个函数的作用就是利用所指定的</span><span>camera</span><span>和</span><span>viewport</span><span>，来把场景中的内容渲染到</span><span>viewport</span><span>所指定的</span><span>render target</span><span>的某块区域中。根据</span><span>camera</span><span>，我们可以定出</span><span>view matrix</span><span>，</span><span>projection matrix</span><span>，还可以进行视锥剔除，只渲染看得见的物体。注意，我们这里只看标准的</span><span>SceneManager</span><span>的方法，不看</span><span>BspSceneManager</span><span>派生类的方法，而且，我们会抛开跟主线无关的内容，如对</span><span>shadow</span><span>的</span><span>setup</span><span>，骨骼动画的播放，</span><span>shader</span><span>参数的传递等，因为我们只注重渲染的主流程。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在</span><span>SceneManager::_renderScene</span><span>中所应看的第一个重要函数是</span><span>_updateSceneGraph</span><span>，</span><span>OGRE</span><span>对场景的组织是通过节点树来组织的，一个节点，你可以看成是空间中的某些变换的组合，如位置，缩放，旋转等，这些变换，会作用到挂接在这些节点上的具体的物体的信息，也就是说，节点保存了</span><span>world transform</span><span>，对具体的物体，如一个人，在空间中的定位，都是通过操作节点来完成的。同时节点还保存了一个世界坐标的</span><span>AABB</span><span>，这个</span><span>AABB</span><span>能容纳所有它所挂接的物体的大小，主要是用于视锥裁减的，如果当前摄像机看不见某个节点的</span><span>AABB</span><span>，那么说明摄像机看不见节点所挂接的所有物体，所以在渲染时可以对这个节点视而不见。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_updateSceneGraph</span><span>的内部处理比较繁琐，我们只需知道，经过了</span><span>_updateSceneGraph</span><span>，场景节点树中的每个节点都经过了更新，包括位置，缩放，和方位，还有节点的包围盒。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;继续回到</span><span>SceneManager::_renderScene</span><span>，接下来要看的是</span><span>setViewport</span><span>，它会调用具体的</span><span>renderer</span><span>的</span><span>setviewport</span><span>的操作，设置</span><span>viewport</span><span>中所挂接的</span><span>render target</span><span>为当前所要渲染的目标，</span><span>viewport</span><span>中的区域为当前所要渲染的目标中的区域。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接下来要碰到</span><span>OGRE</span><span>渲染流程中的一个重要的概念，</span><span>Render Queue</span><span>。这个东西实在内容比较多，还是以后有机会单独提出来说吧，你可以简单把它想成是一个容器，里面的元素就是</span><span>renderable</span><span>，每个</span><span>renderable</span><span>可以看成是每次调用</span><span>drawprimitive</span><span>函数所渲染的物体，可以是一个模型，也可以是模型的一部分。在</span><span>RenderQueue</span><span>中，它会按材质来分组这些</span><span>renderable</span><span>，还会对</span><span>renderable</span><span>进行排序。</span></p>
<p><span>&nbsp;&nbsp;&nbsp; &nbsp;在每一次调用</span><span>SceneManager::_renderScene</span><span>时，都会调用</span><span>SceneManager::prepareRenderQueue</span><span>来清理</span><span>RenderQueue</span><span>，然后再调用</span><span>SceneManager::__findVisibleObjects</span><span>来把当前摄像机所能看见的物体都加入到</span><span>RenderQueue</span><span>中。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SceneManager::__findVisibleObjects</span><span>是一个递归的处理过程，它从场景的根节点开始，先检查摄像机是否能看见这个节点的包围盒（包围盒在</span><span>_updateSceneGraph</span><span>时已经计算好了），如果看不见，那么这个节点，还有它的子节点都不用管了。如果能看见，再检测挂在这个节点上的所有</span><span>MovableObject</span><span>，如果当前所检测的</span><span>MovableObject</span><span>是可见的，就会调用它的</span><span>_updateRenderQueue</span><span>方法，一般在这个方法里就可以把和这个</span><span>MovableObject</span><span>相关的</span><span>renderable</span><span>送入</span><span>RenderQueue</span><span>了。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里要说说</span><span>MovableObject</span><span>，</span><span>MovableObject</span><span>主要是用于表示场景中离散的物体，如</span><span>Entity</span><span>，顾名思义，能移动的物体，不过它的&#8220;能移动&#8221;这个能力是要通过</span><span>SceneNode</span><span>来实现的，所以</span><span>MovableObject</span><span>来能显示出来，首先得先挂接在某个场景节点上，通过场景节点来定位。你可以控制</span><span>MovableObject</span><span>的一些属性，如某个</span><span>MovableObject</span><span>是否要显示，是否要隐藏，都可以通过</span><span>MovableObject::setVisible</span><span>方法来实现。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;检测完该节点上的</span><span>MovableObject</span><span>之后，就继续调用所有子节点的</span><span>_findVisibleObjects</span><span>方法，一直递归下去。这样，就能把场景中所有要渲染的</span><span>renderable</span><span>所加入到</span><span>RenderQueue</span><span>中了。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;至此，我们就拥有了要渲染的物体的信息了，接下来就是对这些物体进行渲染了，你会发现跟</span><span>D3D</span><span>或</span><span>OpenGL</span><span>的代码很类似的调用：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mDestRenderSystem-&gt;clearFrameBuffer</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mDestRenderSystem-&gt;_beginFrame<span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mDestRenderSystem-&gt;_setProjectionMatrix</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mDestRenderSystem-&gt;_setViewMatrix </span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>_renderVisibleObjects();</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mDestRenderSystem-&gt;_endFrame();</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这些</span><span>api</span><span>的作用和</span><span>D3D</span><span>中的类似调用的作用都差不多，这里再说一下</span><span>_renderVisibleObjects()</span><span>，在这个函数中，会对</span><span>RenderQueue</span><span>中的每个</span><span>renderable</span><span>进行渲染，用的是</span><span>visitor</span><span>模式来遍历操作每个</span><span>renderable</span><span>，最终在</span><span>SceneManager::renderSingleObject</span><span>中取出每个</span><span>renderable</span><span>所保存的顶点，索引，世界矩阵等信息，来进行渲染。这其中还包括了查找离该</span><span>renderable</span><span>最近的光源等操作，比较复杂。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;到这里，</span><span>SceneManager::_renderScene</span><span>的流程基本走完了，也就是说，</span><span>OGRE</span><span>一帧中的渲染流程差不多也结束了，你应该也发现，这个流程跟你用</span><span>D3D</span><span>写一个简单程序的流程基本是一样的，在这个流程的基础上，再去看具体的实现，如怎么样设置纹理，怎么样调用你熟悉的</span><span>D3D</span><span>或</span><span>OpenGL</span><span>的</span><span>API</span><span>来渲染物体，应该会简单得多。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对</span><span>OGRE</span><span>的渲染流程的大概介绍到这里也结束了，很多细节都没涉及，以后有机会再写吧。</span></p>
</div>
<script type=text/javascript>
//<![cdata[
Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90);
//]]&gt;
</script>
 <img src ="http://www.cppblog.com/Leaf/aggbug/83233.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-17 23:56 <a href="http://www.cppblog.com/Leaf/archive/2009/05/17/83233.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏中的资源管理――资源高速缓存</title><link>http://www.cppblog.com/Leaf/archive/2009/05/17/83230.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 17 May 2009 15:48:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/17/83230.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83230.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/17/83230.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83230.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83230.html</trackback:ping><description><![CDATA[<div class=postbody>
<p>《游戏中的资源管理――资源高速缓存》<br>转载请注明出处：<a href="http://groups.google.com/group/jianguhan" rel=nofollow target=_blank><u><font color=#800080>http://groups.google.com/group/jianguhan</font></u></a> <br><br><br>1.什么是资源高速缓存 <br>&nbsp;&nbsp;&nbsp; 资源高速缓存的原理与其它内存高速缓存的工作原理是相似的。在游戏的状态转换过程中，有些数据是刚才使用过的，那么直接从资源高速缓存中载入即可。例如，RPG&shy;游戏中主角从大地图进入一个房间，探索一番后主角退出房间，此时只要直接从缓存中载入大地图数据即可，节省了从硬盘载入数据的时间，要知道从硬盘载入数据是非常&shy;慢的。当然，如果你的游戏所使用的数据文件很少，那么你可以在游戏运行过程中把这些数据完全储存在内存中，而不使用资源高速缓存。 </p>
<p><br>2.一个简单的资源高速缓存管理器 <br>&nbsp;&nbsp;&nbsp; 下面我将向你展示一个比较简单的资源高速缓存管理器，源代码来自我上一个游戏，如果你需要知道更多关于资源高速缓存方面的知识，请参考&lt;&lt;Game Coding Complete&gt;&gt;的第八章。 <br>首先，需要一个机制来唯一标识一个资源，我们用下面这个结构来做资源句柄： <br>struct ResHandle <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; ResHandle(std::string &amp;resName, void *buffer, int size) <br>&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_resName = resName; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_size&nbsp;&nbsp; = size; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_buffer = buffer; <br>&nbsp;&nbsp;&nbsp;&nbsp; } </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; ~ResHandle() <br>&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (m_buffer != 0) delete[] m_buffer; <br>&nbsp;&nbsp;&nbsp;&nbsp; } </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; std::string&nbsp;&nbsp; m_resName;&nbsp;&nbsp;&nbsp; //资源名 <br>&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *m_buffer;&nbsp;&nbsp;&nbsp; //资源句柄所标识的资源 <br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_size;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //资源所占内存大小 </p>
<p><br>}; </p>
<p><br>好了，现在我们可以从资源名来找出这个资源了，接下来实现这个资源高速缓存管理器： <br>class CacheManager <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp; CacheManager(); <br>&nbsp;&nbsp;&nbsp;&nbsp; ~CacheManager(); </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //载入资源，resName为资源名，若载入成功size被设为该资源的大小 <br>&nbsp;&nbsp;&nbsp; //注意，管理中的资源不能在管理器外用delete显示的删除它 <br>&nbsp;&nbsp;&nbsp; void*&nbsp;&nbsp;&nbsp; Load(std::string resName, DWORD *size = 0); <br>&nbsp;&nbsp;&nbsp; //设置缓存大小，单位MB <br>&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetCacheSize(int sizeMB)&nbsp;&nbsp;&nbsp; { m_cacheSize = sizeMB * 1024 * 1024; } <br>&nbsp;&nbsp;&nbsp;&nbsp; //得到缓存大小，单位MB <br>&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetCacheSize()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { return m_cacheSize / 1024 /1024; } </p>
<p><br>private: <br>&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp; Free();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放lru链表中最后一个资源 <br>&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp; *Update(ResHandle *res);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //更新lru链表 <br>&nbsp;&nbsp;&nbsp;&nbsp; ResHandle *Find(std::string &amp;resName);&nbsp;&nbsp;&nbsp;&nbsp; //找出该资源名的资源句柄 </p>
<p><br>private: <br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD m_cacheSize;&nbsp;&nbsp;&nbsp;&nbsp; //缓存大小 <br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD m_allocated;&nbsp;&nbsp;&nbsp;&nbsp; //已使用的缓存大小 </p>
<p><br>//lru链表，记录最近被使用过的资源 <br>&nbsp;&nbsp;&nbsp;&nbsp; std::list&lt;ResHandle*&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lru;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; //资源标识映射 <br>&nbsp;&nbsp;&nbsp;&nbsp; std::map&lt;std::string, ResHandle*&gt;&nbsp;&nbsp;&nbsp; m_resources; </p>
<p>&nbsp;</p>
<p>}; </p>
<p><br>CacheManager:: CacheManager () <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; m_cacheSize = 0; <br>&nbsp;&nbsp;&nbsp;&nbsp; m_allocated = 0; </p>
<p><br>} </p>
<p><br>CacheManager::~ CacheManager () <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (!m_lru.empty()) Free();&nbsp;&nbsp; //释放所有管理中的资源 </p>
<p><br>} </p>
<p><br>void * CacheManager::Load(std::string resName, DWORD *size) <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; ResHandle *handle = Find(resName);&nbsp;&nbsp; //查找该资源是否在缓存中 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; if (handle != 0) //如果找到该资源句柄，则返回该资源并更新lru链表 <br>&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (size != 0) *size = handle-&gt;m_size; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Update(handle); <br>&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;&nbsp; else <br>&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //先检测资源大小 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD _size = 资源大小; </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否有足够空间? <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (_size &gt; (m_cacheSize - m_allocated)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (m_lru.empty()) break; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Free(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_allocated += _size; </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer = new char[_size]; <br>//在这里用任何你能想到的办法载入资源文件到buffer <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230; </p>
<p><br>//记录当前资源 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResHandle *handle = new ResHandle(resName, buffer, _size); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lru.push_front(handle); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_resources[resName] = handle; </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (size != 0) *size = _size; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return buffer; <br>&nbsp;&nbsp;&nbsp;&nbsp; } </p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; return 0; </p>
<p>&nbsp;</p>
<p>} </p>
<p><br>void CacheManager::Free() <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; std::list&lt;ResHandle*&gt;::iterator gonner = m_lru.end(); <br>&nbsp;&nbsp;&nbsp;&nbsp; gonner--; <br>&nbsp;&nbsp;&nbsp;&nbsp; ResHandle *handle = *gonner; <br>&nbsp;&nbsp;&nbsp;&nbsp; m_lru.pop_back(); <br>&nbsp;&nbsp;&nbsp;&nbsp; m_resources.erase(handle-&gt;m_resName); <br>&nbsp;&nbsp;&nbsp;&nbsp; m_allocated -= handle-&gt;m_size; <br>&nbsp;&nbsp;&nbsp;&nbsp; delete handle; </p>
<p><br>} </p>
<p><br>void * CacheManager::Update(ResHandle *res) <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; m_lru.remove(res); <br>&nbsp;&nbsp;&nbsp;&nbsp; m_lru.push_front(res); <br>&nbsp;&nbsp;&nbsp;&nbsp; m_size = res-&gt;m_size; <br>&nbsp;&nbsp;&nbsp;&nbsp; return res-&gt;m_buffer; </p>
<p><br>} <br><br>ResHandle * CacheManager::Find(std::string &amp;resName) <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; std::map&lt;std::string, ResHandle*&gt;::iterator it = m_resources.find(resName); <br>&nbsp;&nbsp;&nbsp;&nbsp; if (it == m_resources.end()) return 0; <br>&nbsp;&nbsp;&nbsp;&nbsp; return (*it).second; </p>
<p><br>} <br><br>至此，你已经可以在游戏中缓存任何你想缓存的资源了^_^ </p>
<p>3. 资源管理进阶 <br>&nbsp;&nbsp;&nbsp; 至此你已经可以在游戏中缓存任何你想缓存的资源了，但是你的任务还没完成，当你请求的资源存在于缓存之外时，那个闪耀的硬盘灯可能就是玩家最感兴趣的东西了。 <br>因此你必须根据不同的游戏类型使用不同的载入方式：&nbsp;<br>&nbsp;&nbsp;&nbsp; 一次载入所有东西：适用于任何以界面或关卡切换的游戏&nbsp;<br>&nbsp;&nbsp;&nbsp; 只在关键点载入资源：很多射击游戏都使用这样的设计，如&#8220;半条命&#8221;&nbsp;<br>&nbsp;&nbsp;&nbsp; 持续载入：适用于开放型地图的游戏，如&#8220;侠盗猎车手&#8221; <br>&nbsp;&nbsp;&nbsp; 如果有可能的话，你还可以使用缓存预测机制，当CPU有额外时间的时候可以把未来可能用到的资源载入到资源高速缓存。 <br>&nbsp;&nbsp;&nbsp; 最后，尽管在游戏的资源管理中资源打包不是必须的，但仍然建议大家把资源文件按类型分别打包到单一的文件中，这将为你节省磁盘空间，并加快游戏的载入速度。 </p>
</div>
<script type=text/javascript>
//<![cdata[
Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90);
//]]&gt;
</script>
<img src ="http://www.cppblog.com/Leaf/aggbug/83230.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-17 23:48 <a href="http://www.cppblog.com/Leaf/archive/2009/05/17/83230.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏开发中常用的设计模式</title><link>http://www.cppblog.com/Leaf/archive/2009/05/17/83227.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sun, 17 May 2009 15:36:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/17/83227.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83227.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/17/83227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83227.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83227.html</trackback:ping><description><![CDATA[<div>
<p><font size=3><span>出自http://blog.csdn.net/duzhi5368/archive/2008/04/22/2314232.aspx<br></span></font></p>
<p><font size=3><span>使用设计模式来提高程序库的重复利用性是大型程序项目开发必须的。但是在&#8220;四人帮&#8221;的设计模式概述中提到了</span><span><font face="Times New Roman">23</font></span><span>种标准设计模式，不但难以记住，而且有些设计模式更多的适用于应用程序开发，对游戏项目引擎设计并没有很多的利用价值。根据经验，精挑细选后，笃志在这里记录一些自认为有利用价值的设计模式，以便之后自己设计时使用。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>一：观察者</span><span>Observer</span></h2>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><strong><span>观察者的设计意图和作用是</span></strong><span>：</span><span><span><font face="Times New Roman"> </font></span></span><span>它将对象与对象之间创建一种依赖关系，当其中一个对象发生变化时，它会将这个变化通知给与其创建关系的对象中，实现自动化的通知更新。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>游戏中观察者的适用环境有</span></strong><span>：</span><span><span><font face="Times New Roman"> </font></span></span></font></p>
<p><font size=3><span><font face="Times New Roman">1</font></span><span>：</span><span><font face="Times New Roman">UI</font></span><span>控件管理类。当我们的</span><span><font face="Times New Roman">GUI</font></span><span>控件都使用观察者模式后，那么用户的任何界面相关操作和改变都将会通知其关联对象</span><span><font face="Times New Roman">-----</font></span><span>我们的</span><span><font face="Times New Roman">UI</font></span><span>事件机。</span></font></p>
<p><font size=3><span><font face="Times New Roman">2</font></span><span>：动画管理器。很多时候我们在播放一个动画桢的时候，对其</span><span><font face="Times New Roman">Frame</font></span><span>有很大兴趣，此时我们设置一个</span><span><font face="Times New Roman">FrameLister</font></span><span>对象对其进行监视，获得我们关心的事件进行处理是必须的。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><strong><span>观察者伪代码</span></strong><strong><span>：</span></strong></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>被观察对象目标类</span></font></p>
<p><span><font size=3 face="Times New Roman">Class Subject</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>对本目标绑定一个观察者</span><span><font face="Times New Roman"> Attach( Observer );</font></span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>解除一个观察者的绑定</span><span><font face="Times New Roman"><span>&nbsp;&nbsp; </span>DeleteAttach( Observer );</font></span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>本目标发生改变了，通知所有的观察者，但没有传递改动了什么</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Notity()</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>For ( &#8230;</font></span><span>遍历整个</span><span><font face="Times New Roman">ObserverList &#8230;)</font></span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{ pObserver -&gt;Update(); }</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>对观察者暴露的接口，让观察者可获得本类有什么变动</span><span><font face="Times New Roman">GetState();</font></span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>观察者</span><span><font face="Times New Roman">/</font></span><span>监听者类</span></font></p>
<p><span><font size=3 face="Times New Roman">Class Observer</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>暴露给对象目标类的函数，当监听的对象发生了变动，则它会调用本函数通知观察者</span><span><font face="Times New Roman"> </font></span></font></p>
<p><span><font size=3 face="Times New Roman">Void Update () </font></span></p>
<p><span><font size=3 face="Times New Roman">{ </font></span></p>
<p><font size=3><span><font face="Times New Roman">pSubject -&gt;GetState();&nbsp;// </font></span><span>获取监听对象发生了什么变化</span><font face="Times New Roman"> </font></font></p>
<p><font size=3><span><font face="Times New Roman">TODO</font></span><span>：</span><span><font face="Times New Roman">DisposeFun();&nbsp;// </font></span><span>根据状态不同，给予不同的处理</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><strong><span>非程序语言描述</span></strong><span>：</span></font></p>
<p><font size=3><span><font face="Times New Roman">A</font></span><span>是</span><span><font face="Times New Roman">B</font></span><span>的好朋友，对</span><span><font face="Times New Roman">B</font></span><span>的行为非常关心。</span><span><font face="Times New Roman">B</font></span><span>要出门，此时</span><span><font face="Times New Roman">A</font></span><span>给了</span><span><font face="Times New Roman">B</font></span><span>一个警报器，告诉</span><span><font face="Times New Roman">B</font></span><span>说：&#8220;如果你有事，立刻按这个警报器告诉我。&#8221;。结果</span><span><font face="Times New Roman">B</font></span><span>在外面遇上了麻烦，按下警报器（</span><span><font face="Times New Roman">Update()</font></span><span>），</span><span><font face="Times New Roman">B</font></span><span>就知道</span><span><font face="Times New Roman">A</font></span><span>出了事，于是就调查一下</span><span><font face="Times New Roman">B</font></span><span>到底遇到了什么麻烦</span><span><font face="Times New Roman">(GetState())</font></span><span>，当知道</span><span><font face="Times New Roman">B</font></span><span>原来是因为被人打了，于是立刻进行处理</span><span><font face="Times New Roman">DisposeFun()</font></span><span>，派了一群手下帮</span><span><font face="Times New Roman">B</font></span><span>打架。</span></font></p>
<p><font size=3><span>当然关心</span><span><font face="Times New Roman">A</font></span><span>的人可以不止一个，</span><span><font face="Times New Roman">C</font></span><span>，</span><span><font face="Times New Roman">D</font></span><span>可能也对</span><span><font face="Times New Roman">A</font></span><span>很关心，于是</span><span><font face="Times New Roman">A</font></span><span>这里保存一个所有关心它的人的链表，当遇到麻烦的时候，轮流给每个人一份通知。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>二：单件模式</span><span>Singleton</span></h2>
<p><font size=3><strong><span>单件模式的设计意图和作用是</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>保证一个类仅有一个实例，并且，仅提供一个访问它的全局访问点。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><strong><span>游戏中适用于单件模式的有</span></strong><span>：</span><span><span><font face="Times New Roman"> </font></span></span></font></p>
<p><font size=3><span><font face="Times New Roman">1</font></span><span>：所有的</span><span><font face="Times New Roman">Manger</font></span><span>。在大部分的流行引擎中都存在着它的影子，例如</span><span><font face="Times New Roman">SoundManager, ParticeManager</font></span><span>等。</span></font></p>
<p><font size=3><span><font face="Times New Roman">2</font></span><span>：大部分的工厂基类。这一点在大部分引擎中还是见不到的，实际上，我们的父类工厂采用唯一实例的话，我们子类进行扩展时也会有很大方便。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><strong><span>单件模式伪代码</span></strong><span>：</span></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">Class Singleton</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Static MySingleton;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>单件对象，全局唯一的。</span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Static Instance(){ return MySingleton;}<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>对外暴露接口</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>三：迭代器</span><span>Iterator</span></h2>
<p><span><span><font size=3 face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>迭代器设计意图和作用是</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>提供一个方法，对一个组合聚合对象内各个元素进行访问，同时又不暴露该对象类的内部表示。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>游戏中适用于迭代器模式的有</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>因为</span><span><font face="Times New Roman">STL</font></span><span>的流行，这个设计已经广为人知了，我们对任何形式的资源通一管理时，不免会将其聚合起来，或者</span><span><font face="Times New Roman">List</font></span><span>，或者</span><span><font face="Times New Roman">Vector</font></span><span>，我们都需要一个对其进行访问的工具，迭代器无疑是一个利器。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>迭代器伪代码</span></strong><span>：</span></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>迭代器基类</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Class Iterator</font></font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual First();<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual Next();<span> </span></font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual End();</font></font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual CurrentItem();<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>返回当前</span><span><font face="Times New Roman">Item</font></span><span>信息</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>聚合体的基类</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Class ItemAggregate</font></font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual CreateIterator();&nbsp;// </font></span><span>创建访问自身的一个迭代器</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>实例化的项目聚合体</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Class InstanceItemAggregate : public ItemAggregate</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CreateIterator(){ return new InstanceIterator(this); }</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>四：访问者模式</span><span>Visitor</span><span>：</span></h2>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>访问者设计意图和作用是</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>当我们希望对一个结构对象添加一个功能时，我们能够在不影响结构的前提下，定义一个新的对其元素的操作。（实际上，我们只是把对该元素的操作分割给每个元素自身类中实现了而已）</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>游戏中适用于访问者模式的有</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>任何一个比较静态的复杂结构类中都适合采用一份访问者。这里的&#8220;比较静态的复杂结构类&#8221;意思是，该结构类中元素繁多且种类复杂，且对应的操作较多，但类很少进行变化，我们就能够将，对这个结构类元素的操作独立出来，避免污染这些元素对象。</span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</font></span><span>：例如场景管理器中管理的场景节点，是非常繁多的，而且种类不一，例如有</span><span><font face="Times New Roman">Ogre</font></span><span>中的</span><span><font face="Times New Roman">Root, Irrchit</font></span><span>中就把摄象机，灯光，</span><span><font face="Times New Roman">Mesh</font></span><span>，公告版，声音都做为一种场景节点，每个节点类型是不同的，虽然大家都有共通的</span><span><font face="Times New Roman">Paint(),Hide()</font></span><span>等方法，但方法的实现形式是不同的，当我们外界调用时需要统一接口，那么我们很可能需要需要这样的代码</span><span><font face="Times New Roman"> </font></span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Hide( Object )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{ if (Object == Mesh) HideMesh();&nbsp;if (Object == Light) HideLight();&nbsp;&#8230;&nbsp;}</font></font></span></p>
<p><font size=3><span>此时若我们需要增加一个</span><span><font face="Times New Roman">Object</font></span><span>新的类型对象，我们就不得不对该函数进行修正。而我们可以这样做，让</span><span><font face="Times New Roman">Mesh,Light</font></span><span>他们都继承于</span><span><font face="Times New Roman">Object,</font></span><span>他们都实现一个函数</span><span><font face="Times New Roman">Hide(),</font></span><span>那么就变成</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Mesh::Hide( Visitor ) { Visitor.Hide (Mesh); }</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Light::Hide(Visitor ){ Visitor.Hide (Light); }</font></font></span></p>
<p><font size=3><span>我们在调用时只需要</span><span><font face="Times New Roman">Object.Hide(Visitor){ return Visitor.Hide(Object); }</font></span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span>这样做的<strong><span>好处</span></strong>，我们免去了对重要函数的修正，</span><span><font face="Times New Roman">Object.Hide(Visitor){}</font></span><span>函数我们可以永久不变，但是<strong><span>坏处</span></strong>也是很明显的，因为将方法从对象集合结构中抽离出来，就意味着我们每增加一个元素，它必须继承于一个抽象的被访问者类，实现其全部函数，这个工作量很大。</span></font></p>
<p><font size=3><span>所以，<span>访问者是仅适合于一个装载不同对象的大容器，但同时又要求这个容器的元素节点不应当有大的变动时才使用</span>。另外，废话一句，访问者破坏了</span><span><font face="Times New Roman">OO</font></span><span>思想的。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>访问者伪代码</span></strong><span>：</span></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman">//&nbsp;</font></span><span>访问者基类</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Class Visitor</font></font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual VisitElement( A ){ &#8230; };<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>访问的每个对象都要写这样一个方法</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual VisitElement( B ){ &#8230; };</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>访问者实例</span><span><font face="Times New Roman">A</font></span></font></p>
<p><span><font size=3 face="Times New Roman">Class VisitorA</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>VisitElement( A ){ &#8230; };<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实际的处理函数</span></font></p>
<p><font size=3><span><font face="Times New Roman">VisitElement( B ){ &#8230; };<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实际的处理函数</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>访问者实例</span><span><font face="Times New Roman">B</font></span></font></p>
<p><span><font size=3 face="Times New Roman">Class VisitorB</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>VisitElement( A ){ &#8230; };<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实际的处理函数</span></font></p>
<p><font size=3><span><font face="Times New Roman">VisitElement( B ){ &#8230; };<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实际的处理函数</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>被访问者基类</span></font></p>
<p><span><font size=3 face="Times New Roman">Class Element</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Virtual Accept( Visitor );<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>接受访问者</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>被访问者实例</span><span><font face="Times New Roman">A</font></span></font></p>
<p><span><font size=3 face="Times New Roman">Class ElementA</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Accecpt( Visitor v ){ v-&gt; VisitElement(this); };<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>调用注册到访问者中的处理函数</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>被访问者实例</span><span><font face="Times New Roman">B</font></span></font></p>
<p><span><font size=3 face="Times New Roman">Class ElementB</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Accecpt( Visitor v ){ v-&gt; VisitElement(this); };<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>调用注册到访问者中的处理函数</span></font></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>五：外观模式</span><span>Fa&#231;ade</span></h2>
<p><span><span><font size=3 face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>外观模式的设计意图和作用是</span></strong><span>：</span><span><font face="Times New Roman"> &nbsp;</font></span><span>将用户接触的表层和内部子集的实现分离开发。实际上，这个模式是个纸老虎，之后我们看伪代码立刻就会发现，这个模式实在用的太频繁了。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>游戏中需要使用外观模式的地方是</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp; </font></span></span><span>这个非常多了，举几个比较重要的。</span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</font></span><span>：实现平台无关性。跨平台跨库的函数调用。</span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</font></span><span>：同一个接口去读取不同的资源。</span></font></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</font></span><span>：硬件自动识别处理系统。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>外观模式伪代码</span></strong></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>用户使用的接口类</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Class Interface</font></font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>暴露出来的函数接口函数，有且仅有一个，但内部实现是调用了两个类</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Void InterfaceFun()</font></font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>根据某种条件，底层自主的选择使用</span><span><font face="Times New Roman">A</font></span><span>或</span><span><font face="Times New Roman">B</font></span><span>的方法。用户无须关心底层实现</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>If ( XXX )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ActualA-&gt;Fun();</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">Else</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ActualB-&gt;Fun();</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3><font face="Times New Roman">};<span>&nbsp;&nbsp;&nbsp; </span></font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>实际的实现，不暴露给用户知道</span></font></p>
<p><span><font size=3 face="Times New Roman">Class ActualA</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Void Fun();</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>实际的实现，不暴露给用户知道</span></font></p>
<p><span><font size=3 face="Times New Roman">Class ActualB</font></span></p>
<p><span><font size=3 face="Times New Roman">{</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Void Fun();</font></font></span></p>
<p><span><font size=3 face="Times New Roman">}</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><span><font size=3>怎么样，纸老虎吧，看起来很高深摸测的命名而已。</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<h2><span>六：抽象工厂模式</span><span>AbstractFactory</span></h2>
<p><span><span><font size=3 face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>抽象工厂的设计意图和作用是</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>封装出一个接口，这个接口负责创建一系列互相关联的对象，但用户在使用接口时不需要指定对象所在的具体的类。从中文命名也很容易明白它是进行批量生产的一个生产工厂的作用。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>游戏中使用抽象工厂的地方有</span></strong><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; </font></span></span><span>基本上任何有批量的同类形式的子件地方就会有工厂的存在。（补充一句：下面代码中的</span><span><font face="Times New Roman">ConcreteFactory1</font></span><span>实例工厂就是工厂，而抽象工厂仅仅是工厂的一个抽象层而已。</span><span>）</span></font></p>
<p><font size=3><span><font face="Times New Roman">1</font></span><span>：例如，在音频方面，一个音频的抽象工厂派生出不同的工厂，有音乐工厂，音效工厂。音效工厂中又有一个创建</span><span><font face="Times New Roman">3D</font></span><span>音效节点的方法，一个创建普通音效节点的方法。最终用户只需要</span><span><font face="Times New Roman">SoundFactory-&gt;Create3DNode( pFileName );</font></span><span>就可以创建一个节点了。</span></font></p>
<p><font size=3><span><font face="Times New Roman">2</font></span><span>：场景对象。</span></font></p>
<p><font size=3><span><font face="Times New Roman">3</font></span><span>：渲染对象。</span></font></p>
<p><font size=3><span><font face="Times New Roman">4</font></span><span>：等等&#8230;&#8230;</span></font></p>
<p><font size=3><span>工厂与单件，管理器</span><span><font face="Times New Roman">Manager</font></span><span>关系一定是非常紧密的。</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><strong><span>抽象工厂伪代码</span></strong><span>：</span></font></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><font face="Times New Roman"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>class AbstractProductA {}; // </span></font><span>抽象的产品</span><span><font face="Times New Roman">A</font></span><span>基类</span></font><span><br></span><font size=3><span>　　</span><span><font face="Times New Roman">class AbstractProductB {}; //</font></span><span>抽象的产品</span><span><font face="Times New Roman">B</font></span><span>基类</span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>抽象工厂基类</span></font><span><br></span><span><font size=3>　　</font></span><font size=3><span><font face="Times New Roman">class AbstractFactory<br></font></span><span>　　</span></font><font size=3><span><font face="Times New Roman">{<br></font></span><span>　　</span></font><font size=3><span><font face="Times New Roman">public:<br></font></span><span>　　　</span><span><font face="Times New Roman">virtual AbstractProductA* CreateProductA() = 0 ;// </font></span><span>创建</span></font><font size=3><span><font face="Times New Roman">ProductA<br></font></span><span>　　　</span><span><font face="Times New Roman">virtual AbstractProductB* CreateProductB() = 0 ;// </font></span><span>创建</span></font><span><font size=3><font face="Times New Roman">ProductB<br>&nbsp;&nbsp;&nbsp;<span>&nbsp;&nbsp;&nbsp; </span>} ;</font></font></span></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span>　　</span><span><font face="Times New Roman">class ProductA1 : public AbstractProductA {};<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>产品</span><span><font face="Times New Roman">A</font></span><span>的实例</span></font><font size=3><span><font face="Times New Roman">1<br></font></span><span>　　</span><span><font face="Times New Roman">class ProductA2 : public AbstractProductA {};<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>产品</span><span><font face="Times New Roman">A</font></span><span>的实例</span><span><font face="Times New Roman">2</font></span></font></p>
<p><font size=3><span>　　</span><span><font face="Times New Roman">class ProductB1 : public AbstractProductB {};<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>产品</span><span><font face="Times New Roman">B</font></span><span>的实例</span></font><font size=3><span><font face="Times New Roman">1<br></font></span><span>　　</span><span><font face="Times New Roman">class ProductB2 : public AbstractProductB {};<span>&nbsp;&nbsp;&nbsp; </span>// </font></span><span>产品</span><span><font face="Times New Roman">B</font></span><span>的实例</span><span><font face="Times New Roman">2</font></span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>实例工厂</span><span><font face="Times New Roman">1</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp; &nbsp;class ConcreteFactory1 : public AbstractFactory<br></font></span><span>　　</span></font><font size=3><span><font face="Times New Roman">{<br></font></span><span>　　　　</span></font><font size=3><span><font face="Times New Roman">virtual AbstractProductA* CreateProductA() { return new ProductA1() ; }<br></font></span><span>　　　　</span></font><font size=3><span><font face="Times New Roman">virtual AbstractProductB* CreateProductB() { return new ProductB1() ; }<br></font></span><span>　　　</span><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>static ConcreteFactory1* Instance() { } </font></span><span>　　</span><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实例工厂尽量使用单件模式</span></font><span><br></span><font size=3><span>　　</span><span><font face="Times New Roman">} ;</font></span></font></p>
<p><span><font size=3 face="Times New Roman">&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">// </font></span><span>实例工厂</span><span><font face="Times New Roman">2</font></span></font></p>
<p><span><font size=3>class ConcreteFactory2 : public AbstractFactory<br></font></span><span><font size=3>　　</font><font size=3><span>{<br></span>　　　　</font><font size=3><span>virtual AbstractProductA* CreateProductA() { return new ProductA2() ; }<br></span>　　　　</font><font size=3><span>virtual AbstractProductB* CreateProductB() { return new ProductB2() ; }<br></span>　　　　<span>static ConcreteFactory2* Instance() {}&nbsp;</span></font></span><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>实例工厂尽量使用单件模式</span></font><span><br></span><span><font size=3>　　<span>} ;</span></font></span></p>
<p><span><font size=3>}</font></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
<p><font size=3><strong><span>客户端代码</span></strong><span>：</span></font></p>
<p><span><font size=3>Void main()<br>{<br></font></span><span><font size=3>　　</font><font size=3><span>AbstractFactory *pFactory1 = ConcreteFactory1::Instance() ;<br></span>　　</font><font size=3><span>AbstractProductA *pProductA1 = pFactory1-&gt;CreateProductA() ;<br></span>　　</font><font size=3><span>AbstractProductB *pProductB1 = pFactory1-&gt;CreateProductB() ;<br></span>　　</font><font size=3><span>AbstractFactory *pFactory2 = ConcreteFactory2::Instance() ;<br></span>　　</font><font size=3><span>AbstractProductA *pProductA2 = pFactory2-&gt;CreateProductA() ;<br></span>　　</font><span><font size=3>AbstractProductB *pProductB2 = pFactory2-&gt;CreateProductB() ;<br>}</font></span></span></p>
<p><span><font size=3 face="Times New Roman">//-------------------------------------------------------------------------------------------------------</font></span></p>
</div>
<SCRIPT type=text/javascript>
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90);
//]]&gt;
</SCRIPT>
<img src ="http://www.cppblog.com/Leaf/aggbug/83227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-17 23:36 <a href="http://www.cppblog.com/Leaf/archive/2009/05/17/83227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在VS2005下编译HGE例子 LIBC.lib问题</title><link>http://www.cppblog.com/Leaf/archive/2009/05/14/82985.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Thu, 14 May 2009 12:04:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/14/82985.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/82985.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/14/82985.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/82985.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/82985.html</trackback:ping><description><![CDATA[都知道看引擎的例子是学习一个引擎使用的好方法，但是现在许多人都开始用VS2005了，若又再去安装VC6不免有点麻烦。<br>再说了，不是说是向下兼容的吗？怎么会出现无法编译呢<br>那就是一个很刺眼的问题<br>无法找到 LIBC.lib <br><br>我也查了一下午，终于让我发现了一些东西<br>可以说 libc.lib是一个被人遗弃的家伙，VS2005不再支持它了，于是会提示你找不到。<br>贴子真多呀，翻一N页，也没见一个针对性的。<br>终于一哥们儿的BLOG开门见山就说了解决他的方法<br><br><br>只要忽略这个库即可<br><br><img height=454 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/hgeconfig.jpg" width=672 border=0><br><br>发在这里，只为再次搜索这个东西的兄弟多一个关键字。。<br>
 <img src ="http://www.cppblog.com/Leaf/aggbug/82985.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-14 20:04 <a href="http://www.cppblog.com/Leaf/archive/2009/05/14/82985.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>3DSNAKE-DEMO</title><link>http://www.cppblog.com/Leaf/archive/2009/05/02/81731.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Sat, 02 May 2009 14:27:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/02/81731.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/81731.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/02/81731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/81731.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/81731.html</trackback:ping><description><![CDATA[忙活了一晚上，终于弄出来了，呵呵，虽然蛇的样子和场景不太美观，但是还是摸索出了角色任意方向的控制以及视角跟踪的基本技巧。<br><br>感觉不管怎么学，还是得自己做东西才能有所提升，毕竟书上的问题是死的，自己遇到的问题是活的。<br>截图留念一下！<br><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/snake.jpg" width=637 height=474><br><br>寝室同学说三维的有点晕，哈哈，我调试的时候也玩晕了，不过现在习惯了，呵呵，有助于治疗晕车哦！ 
<img src ="http://www.cppblog.com/Leaf/aggbug/81731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-05-02 22:27 <a href="http://www.cppblog.com/Leaf/archive/2009/05/02/81731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE 中的 Terrian</title><link>http://www.cppblog.com/Leaf/archive/2009/04/27/81271.html</link><dc:creator>Render Donkey</dc:creator><author>Render Donkey</author><pubDate>Mon, 27 Apr 2009 14:34:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/27/81271.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/81271.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/27/81271.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/81271.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/81271.html</trackback:ping><description><![CDATA[很艰难地编译成功了，安了一上午的SP1补丁，截图留念<br><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/Terrain.jpg" width=1024 height=768> 
<img src ="http://www.cppblog.com/Leaf/aggbug/81271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">Render Donkey</a> 2009-04-27 22:34 <a href="http://www.cppblog.com/Leaf/archive/2009/04/27/81271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>