﻿<?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++博客-清源游民的网络笔记本-随笔分类-OGRE</title><link>http://www.cppblog.com/yuanyajie/category/3706.html</link><description>记录所思所想，收藏所见所闻�?
</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:01:23 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:01:23 GMT</pubDate><ttl>60</ttl><item><title>Ogre3D嵌入Qt框架 之　小结</title><link>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24528.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Mon, 21 May 2007 03:57:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24528.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/24528.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24528.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/24528.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/24528.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/yuanyajie/archive/2007/05/21/24528.html'>阅读全文</a><img src ="http://www.cppblog.com/yuanyajie/aggbug/24528.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-05-21 11:57 <a href="http://www.cppblog.com/yuanyajie/archive/2007/05/21/24528.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ogre3D嵌入Qt框架 之　秀图</title><link>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24506.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Sun, 20 May 2007 17:26:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24506.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/24506.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/05/21/24506.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/24506.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/24506.html</trackback:ping><description><![CDATA[<p>环境：WindowsXp Pro SP2, VS2003.NET, Ogre1.4.1(Eihort), Qt 4.2.2 开源版<br>参考：ShowMesh,MAGE,两款工具源码<br>先把图秀出来，有时间文字总结一下，难度虽然不高，但也折腾不少时间,也算近来学习Qt与Ogre的一个小结。<br><br><img height=480 alt="" src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_ShowOgreEmbedInQt.jpg" width=612 border=0><br></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/24506.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-05-21 01:26 <a href="http://www.cppblog.com/yuanyajie/archive/2007/05/21/24506.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》读书笔记 之 第十一章 动态阴影</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/21/20302.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Wed, 21 Mar 2007 09:07:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/21/20302.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/20302.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/21/20302.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/20302.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/20302.html</trackback:ping><description><![CDATA[
		<p>清源游民　　gameogre@gmail.com<br /><br /><font color="#0000ff">阴影技术</font><br />Ogre支持两种主流的动态阴影技术，模板(stencil)阴影与纹理(texture)阴影,每一种都有两个变体：modulative 与additive。这四种技术完全兼容固定函数图形流水线，因此不需要可编程GPU支持。然而，可利用GPU程序进行加速。在场景中只有使用一种阴影技术，应该在场景渲染这前进行阴影技术相关设置（最好是在创建场景管理器之间）。通过调用SceneManager::setShadowTechnique()来设置技术，参数指定技术的具体类型。阴影技术缺省情况下被关闭。对于物体，投射与接收阴影可以在材质中控制，也可以控制物体自己对自己投射。由于模板阴影算法的本质特征，透明半透明的物体要么全部投射实心阴影要么根本不投影影，不可能得到半透明的阴影。而使用纹理阴影技术则可以。灯不能用来投射阴影。<br /><font color="#0000ff">模板阴影算法<br /></font>模板阴影算法原理上来讲比较简单：从光源的角度看一个可以投射阴影的物体，可以看到物体最外轮廓的<br />形状，把这个轮廓沿光的方向延伸，形成一个空间体，就是所谓的阴影锥(shadow volume),很显然，如果一<br />个物体被这个阴影锥包围，那么这个物体就处于前面那个物体所投射的阴影中了。既然阴影锥是通过延伸的方式来增长的，那么可以在延伸方向对锥体的延伸范围作出控制。在延伸方向上，有一端不能延伸，另外一端的延伸范围控制按照下面两个规则。第一，如果使用可编程图形硬件，顶点程序可以对顶点进行无限延伸。第二，如果使用固定函数图形流水线，延伸的范围由光线衰减设置(点光源和聚光灯）或使用<br />SceneManager::setShadowDirectionalLightExtrusionDistance()来控制。在不使用加速图形硬件的情况下，应该尽量避免物体离光源过近，这样会产生过于宽的阴影锥，导致不正确的阴影效果。根据算法原理，产生的阴影边界非常明显：一个像素要么在阴影中，要么不在。这带来的好处是，即使阴影锥的延伸距离很大，也不会影响精度。可以讲这种技术产生的阴影是一种“硬阴影"。纹理阴影技术可以”软化“阴影。使用模板阴影技术需要边列表(edge lists),标准的转换工具在产生二进制mesh文件时会创建边列表，如果没有，在程序中也可以用Mesh::buildEdgeList()来产生。因为有时候，我们会代码的方式来生成mesh,如果这时我们想使用模板阴影技术，那么就要确保产生出边列表数据。如果边列表数据不存在，那么ogre会认为你不希望这个物体投射阴影。<br /><font color="#0000ff">模板阴影优化<br /></font>Ogre能进行一些一般的优化。例如可使用硬件加速，可能通过光的方向与范围来检测它是否会对平截头体<br />(frustum)产生影响，从面避免了计算那些不必要的阴影几何体。ogre支持双面模板，stencil-wrapping扩展，<br />这些都能阻止不必要的原型安装(primitive setup)与驱动过载(driver overhead)。ogre使用相对廉价的Z-pass算法代替Z-Fail算法（当Z－Fail算法不必要时，假如相机处于一个阴影锥中，使用Z-pass算法会有问题，这种情况下应该使用Z-fail算法）。<br />阴影锥不受封闭几何的影响。在下图中，<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_shadowVolumeJPG.JPG" /><br />卫兵投射到地板上，投射的范围内有个封闭的箱子（此例中设定箱子不投射阴影）。根据现实世界的经验，箱子上会有卫兵投射的阴影，如图中所示。但光线经过箱子阻挡后应该不再前进，也就是说阴影锥经过箱子阻挡后不应该延伸了，于是箱子后面的地板上不应该再出现卫兵的阴影（真实世界中出现箱子的阴影，但是此例设定箱子不产生阴影)。图中的地板上出现了卫兵投射的阴影，这说明了阴影不会受到延伸方向上物体的影响。<br />在屏幕上看不到的物体也可以把阴影投影到可视的平截头体(view frustum)中。虽然看不到，但是这些物体<br />也必须得被渲染，渲染他们纯粹是为了得到产生阴影的相关数据。在不使用硬件加速的情况下，尽量避免物体离光源过近。<br /><font color="#0000ff">纹理阴影</font><br />纹理阴影算法的基本原理是：首先从光源的角度观察场景，把观察到结果渲染到一个纹理，纹理中只保存<br />深度值（深度缓冲中的值）。这样做的意义是，在纹理中保存了场景中的物体与光源之间的最短距离。然后从相机的角度进行正常渲染场景，计算每个像素与光源的距离,并与纹理对应的值进行比较，如果大于，那么说明它不是光线方向上离光源的最近的，会有阴影产生<font color="#008000">。/*可以有点疑惑，每个需要测试的像素如何与纹理中的像素一一对应呢？还是按照生成纹理图的原理，需要把测试顶点的位置坐标，转换到光源为视点的空间，也就是说从光源的角度，看看顶点应该在哪里。这样就可以一一对应比较了。当然这仅仅是为了比较的目的，真正渲染时顶点还是要先转换到视空间中(从相机角度看)*/</font>。<br />纹理阴影速度上要比模板阴影算法快，但因为阴影被渲染到纹理中，而纹理具有确定的解析度，因此当阴影被扩展，拉伸时，效果会较差，如产生锯齿边等。<br />因为使用了纹理内存，因此它的尺寸决定了纹理阴影数的上限。ogre中可以管理一帧中可以使用的阴影纹理数：SceneManager::setShadowTextureCount()。每个光源都需要一个纹理。假如指定的纹理数少于光源数，ogre采用”先来先服务“的原则分配纹理。假如不能增加纹理尺寸来提高视觉效果，最好减小投射阴影的距离。阴影不会突然终结，ogre会淡化纹理阴影的边缘阻止有阴影与无阴影之间剧烈过渡。可以控制淡化半径。<br /><font color="#0000ff">Modulative阴影混合<br /></font>正常场景渲染的颜色与阴影的颜色相乘来创建暗的、表示阴影的颜色。这种方法有从已经渲染的场景中”减去“光照影响的效果。阴影区域一律变暗，与进入阴影区的光线数无关。当有多个光源产生的阴影区<br />重叠时，阴影会非常暗，视觉效果很差。<br /><font color="#0000ff">Additive Shadow Masking<br /></font>Modulative的方法是通过影响阴影区实现的。而Additive方法是只对处于光照中的区域有影响，对阴影区没<br />有影响。这样阴影区会受到别的光源的影响而变得稍亮，而相互重叠的阴影区也不会更暗。<br />ogre会把一个通道分为三个通道.<br />Ambietn,应用环境光到场景中，在这个通道没有纹理被渲染。<br /> Diffuse and specular, 这个通道对每个灯都渲染一次，当前灯产生的阴影区域不受影响。其余的区域与场景进行混合。同样也不使用任何贴花纹理。<br />Decal。纹理被应用到前面累积的颜色上。</p>
		<p>
				<br /> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/20302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-21 17:07 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/21/20302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》读书笔记 之 第十章 布告板与粒子　第二部分 </title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20227.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 20 Mar 2007 10:04:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20227.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/20227.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/20227.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/20227.html</trackback:ping><description><![CDATA[清源游民　　<a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a><br /><br /><font color="#0000ff">粒子系统<br /></font>ogre中的粒子系统既可用脚本描述，也可用代码完成。用粒子脚本定义的粒子系统实际上是个模板，因此它定义的粒子系统可在程序中方便地重用。<br /><font color="#0000ff">粒子系统与场景</font><br />粒子系统会被挂到场景结点上，因此，结点的平移，缩放，旋转会关联到粒子系统，影响粒子反射的向。<br />粒子会被发射到世界空间中，这意味着当场景移动时，它会牵连到发射器，但已经发射出去的粒子不受影响。假如需要这些粒子受结点的影响，可以把粒子发射到本地空间(local space)中。粒子系统不能无限制的发射粒子，它有一个限额（quota)。一旦到达限额，粒子系统不会再发射粒子，直到已经存在的粒子消亡。缺省的限额是10。<br /><font color="#0000ff">粒子系统约束(Bounds)</font><br />粒子系统的动态特征意味着它们的绑定盒必须规律地被重新计算。缺省情况下，ogre会在10秒之后，停止<br />更新绑定盒。可以用ParticleSystem::setBoundsAutoUpdated(  bool  autoUpdate,  Real  stopIn = 0.0f )来改变缺省行为。stopIn这个参数告诉ogre多长时间后停止更新。假如有个粒子系统，可以知道它的增长不会超过某个范围，可以事先用ParticleSystem::setBounds()设置绑定盒尺寸。这样做效率很高，如果无法事先确定粒子系统的增长情况，可先用setBounds()设定一个初始尺寸，而用setBoundsAutoUpdated()让它在一段时间之后更新。<br /><font color="#0000ff">粒子系统更新<br /></font>对于粒子系统的更新，ogre采用适度启发式的策略。举例来说，当粒子系统不在视锥体之内，ogre仍会对粒子系统进行更新，这是考虑到不久之后，粒子系统有可能会重新进入视锥体之内。这就避免了这种情况下的视觉上的不和谐。但是出于性能上的考虑，当粒子系统退出视锥体一段时间之后，粒子确实会停止更新。这又会碰到上面的问题：被冻结的粒子系统突然重新进入视锥体的情况。ogre提供了一个所谓的<br />"fast-forward"机制，允许粒子系统在被冻结之后，快速超越当前状态。这种特性也可以用在刚创建的粒子系统，使它们状态提前一段时间，使用的方法是ParticleSystem::fastForward ();<br /><font color="#0000ff">粒子系统排序<br /></font>可以指示ogre根据与相机之间的距离，对粒子进行排序。虽然这会影响性能，但有时不得不用。在烟的<br />例子中，不使用排序的结果是，从烟的顶部依然清晰地看到火苗，而使用了排序之后火苗被烟雾模糊掉了。显然后者更加真实。<br /><font color="#0000ff">发射器<br /></font>点发射器从空间中单一点发射粒子。box发射器可以从规则四方体的任何位置发射粒子。cylinder定义了一个圆柱体。ellipsoid定义了一个椭球体。hollow elipsoid只是椭球体的外壳。ring只出平面圆环的边发射。被发射粒子的速度，方向也可以配置。粒子系统会被挂到场景结点上，因此发射器与父结点有一个相对的位置。粒子通常不会沿直线发射，它们在发射器方向的一个锥内发射，有一个angle参数来定义。假如希望沿直线发射，angle应该设为0,假如希望全方向发射，angle设为180, 90表示会在方向向量的半球内随机发射。<br />发射率用　粒子数/秒　表示。发射器可以按某个发射率发射，也可以使用一个范围内的随机值。粒子可<br />以有固定的生命期，也可以从一个范围随机指定。颜色也一样，固定值，随机值都可以。也可以在运动时，以插件的形式提供定制发射器，这是扩展ogre粒子系统最简单的方法。<br /><font color="#0000ff">影响器<br /></font>粒子一旦被发射到世界中，影响器可被用来影响粒子的运动路径与生命期。<br /><font color="#ff1493">LinearForce</font>:加一个力到粒子上，力是一个向量，有方向与大小（模）。<br /><font color="#ff1493">ColourFader</font>:用于改变粒子颜色。表示每秒减少的值，是一个绝对值。<br /><font color="#ff1493">ColorFader2</font>:与ColourFader相似，但是粒子生命到到达其生命期的某个阶段时，可以转换到另一个褪色函数。<br /><font color="#ff1493">ColourInterpolator</font>:与上面两个相似，但是它最多可以有6个阶段，分别对每个阶段的时间与颜色进行定义，然后不同阶段之间进行插值，这与关键帧动画的思想有点像。<br /><font color="#ff1493">Scaler</font>:用来缩放粒子尺寸，以时间为函数来定义一个缩放因子。<br /><font color="#ff1493">Rotator</font>:通过一组随机数量，或是随机速率来旋转粒子的纹理，这些被定义在某个范围之内。<br /><font color="#ff1493">colourImage</font>:从图像中攻取颜色,方向从左到右，适合一维纹理 。<br /><img src ="http://www.cppblog.com/yuanyajie/aggbug/20227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-20 18:04 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/20/20227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》读书笔记 之 第十章 布告板与粒子　第一部分 </title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20216.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 20 Mar 2007 07:31:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20216.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/20216.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/20/20216.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/20216.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/20216.html</trackback:ping><description><![CDATA[
		<p>清源游民　gameogre@gmail.com<br /><br /><font color="#0000ff">布告板(Billboard)</font><br />Ogre中的布告板简单的来讲就是场景中的一个四边形，但它的朝向与相机有关。通常布告板随着相机的视方向旋转以便与相机的视线方向对齐。把布告板放在场景的任何地方，它将会朝向相机。为了效率考虑，布告板与相机的视线方向对齐，而不是与从布告板到相机的向量对齐。大部分情况下两者没有明显的区别。如果想使用后一种的对齐形式，也可以办到，但在性能会有所降低。<br /><font color="#0000ff">布告板集(Billboard Sets)</font><br />布告板不能独立存在，他们也不能自我渲染。他们必须属于某个布告板集。布告板集可认为是一组布告板<br />的管理器，布告板集内的所有布告板有相同的尺寸，使用相同的材质。相同的尺寸，相同的纹理会带来计算与渲染效率的提升。可以在一个布告板集内单独改变某个公告板的尺寸，但这样做会导致性能下降ogre<br />认为一个布告板集是一个操作的单位，它们要么都不渲染，要么全部进行渲染，换句话说，它们以同样的<br />方式进行剔除处理，也可以对集合的个体进行单独剔除，但最好不要这么做，同样这也是出于效率的虑。在大范围的应用布告板集来模拟草的例子中，就有可能出现在布告板集内的许多布告板落在视角之外的情况，在这种情况下也许会想到用单独剔除的方式，但这样做同样不可避免地会影响性能。更好的做法是使用多个布告板集而不是在一个巨大的布告板集进行单独剔除。布告板集内的布告板是相对于布告板集attached到的场景结点进行定位的。既然，布告板集内的布告板使用相同的材质，那么它们会在同一batch进行渲染。<br /><font color="#0000ff">布告板创建<br /></font>布告板可以用与本布告板集的中心的偏移与尺寸（宽，高）来描述。它的方向是它到相机向量的函数。缺省，创建的布告板是所谓的point布告板。这种布告板总是完全地面对相机而且垂直。布告板的原点缺省位置是它的中心，可以在九个规范的位置范围内改变原点位置。第二种类型的布告板是oriented布告板，它即可以沿各自的Y轴旋转，也可沿共同的轴（通常是Y轴）旋转。第三种是perpendicular布告板，它与方向向量（布告板与相机之间的向量）垂直。这个方向向量可以是共享的向量（通常是Z轴），也可以是各自的Z轴。这种类型的布告板也需要一个上方向辅助定位。<br />point布告板相对于其他类型的布告板有一些优点。一般，ogre为每个布告板产生一个quad(四个顶点，每个<br />顶点都有位置，纹理坐标），并把生成的几何体送入GPU进行渲染。对于point 布告板，可以废除其中的三个，让GPU挑选，以及决定如何进行基于点绘制纹理属性的渲染。进行硬件点渲染有一些限制：只支持<br />point布告板，其中只限于以中心为原点的那一类。尺寸与外观在材质中定义，不在布告板集，尺寸是有限<br />的，不可能与软件创建的一样大。不支持单独的布告板旋转与改变大小，但可以对纹理单元旋转。<br /><font color="#0000ff">布告板池</font><br />创建布告板时需要告诉布告板集中布告板的数量。布告板被分别放在active与free列表中，这使得定位非常快。当创建的布告板的数量超过了布告板池的尺寸，池的尺寸会增加一倍。最好事先确定池的大小，避免在使用中使池的大小不合理地增长。<br /><font color="#0000ff">布告板纹理坐标</font><br />支持非全范围的纹理坐标分配。举个例子，假如你有个纹理，它包含几个子纹理。每个纹理都包含一个<br />英文字母。这时，你想创建几个公告板，每个公告板上显示不同的字母，当然它们都来自同一个纹理。<br />可以先创建一个包含纹理坐标的数组，然后把这个数组提供给布告板集。当利用布告板集创建布告板时，<br />可以给这个布告板一个存放纹理坐标的数组的索引。图片与代码如下：<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_abcd.JPG" /><br /><font color="#008000">// create a new billboard set with a pool of 4 billboards<br /></font>BillboardSet* bbset = sceneMgr-&gt;createBillboardSet("BBSet", 4);<br /><font color="#008000">// create a texture coordinate array to address the texture</font><br />FloatRect texCoordArray[] = {<br />FloatRect(0.0, 0.0, 0.5, 0.5), // address the "A"<br />FloatRect(0.5, 0.0, 1.0, 0.5), // address the "B"<br />FloatRect(0.0, 0.5, 0.5, 1.0), // address the "C"<br />FloatRect(0.5, 0.5, 1.0, 1.0), // address the "D"<br />};<br /><font color="#008000">// provide this array to the billboard set<br /></font>bbset-&gt;setTextureCoords(texCoordArray, 4);<br />// now create a billboard to display the "D"; this<br /><font color="#008000">// is the fourth entry in the array, index=3<br /></font>Billboard* bb = bbset-&gt;createBillboard(Vector3(0, 0, 0));<br />bb-&gt;setTexcoordIndex(3);<br />也可以直接对纹理坐标进行赋值<br />FloatRect coords(0.5, 0.5, 1.0, 1.0);<br />bb-&gt;setTexcoordRect(coords);<br />布告板使用的材质脚本与可以包括正常材质脚本中的所有东西，而且可以使用布告板与点纹理特有的一些<br />指令与参数。<br /><font color="#0000ff">布告板链与丝带跟踪（Ribbon Trails）<br /></font>布告板链一组前后相联结的一组布告板（可以联想“老鹰抓小鸡”的游戏中小鸡们的组织结构）。ogre<br />提供了一个ribbon-trail引用类，它使用了布告板链，可以追逐场景中的结点，并留下一条尾巴。布告板链可以分多个节，因此可以很好来模拟灯的效果　。布告板链的缺点是必须手动更新。可以为布告板链的每个节的尾部添加项，每个节有最大的长度，假如超过了那个长度，尾部项被移除，重新做为节的头。<br />ribbon trail有类似的行为，从尾部移除项作为头来增长，从头到尾颜色逐渐褪化。图片与代码如下：<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_Snap5.jpg" /><br /><br />void setupTrailLights(void)<br />{<br />NameValuePairList pairList;<br />pairList["numberOfChains"] = "2";<br />pairList["maxElements"] = "80";<br />RibbonTrail* trail = static_cast&lt;RibbonTrail*&gt;(<br />mSceneMgr-&gt;createMovableObject("1", "RibbonTrail", &amp;pairList));<br />trail-&gt;setMaterialName("Examples/LightRibbonTrail");<br />trail-&gt;setTrailLength(400);<br />mSceneMgr-&gt;getRootSceneNode()-&gt;<br />createChildSceneNode()-&gt;attachObject(trail);<br /><font color="#008000">// Create nodes for trail to follow</font><br />SceneNode* animNode = mSceneMgr-&gt;getRootSceneNode()-&gt;<br />createChildSceneNode();<br />animNode-&gt;setPosition(50,30,0);<br />Animation* anim = mSceneMgr-&gt;createAnimation("an1", 14);<br />anim-&gt;setInterpolationMode(Animation::IM_SPLINE);<br />NodeAnimationTrack* track = anim-&gt;createNodeTrack(1, animNode);<br />TransformKeyFrame* kf = track-&gt;createNodeKeyFrame(0);<br />kf-&gt;setTranslate(Vector3(50,30,0));<br />kf = track-&gt;createNodeKeyFrame(2);<br />kf-&gt;setTranslate(Vector3(100, -30, 0));<br />kf = track-&gt;createNodeKeyFrame(4);<br />kf-&gt;setTranslate(Vector3(120, -100, 150));<br />kf = track-&gt;createNodeKeyFrame(6);<br />kf-&gt;setTranslate(Vector3(30, -100, 50));<br />kf = track-&gt;createNodeKeyFrame(8);<br />kf-&gt;setTranslate(Vector3(-50, 30, -50));<br />kf = track-&gt;createNodeKeyFrame(10);<br />kf-&gt;setTranslate(Vector3(-150, -20, -100));<br />kf = track-&gt;createNodeKeyFrame(12);<br />kf-&gt;setTranslate(Vector3(-50, -30, 0));<br />kf = track-&gt;createNodeKeyFrame(14);<br />kf-&gt;setTranslate(Vector3(50,30,0));<br />AnimationState* animState = mSceneMgr-&gt;createAnimationState("an1");<br />animState-&gt;setEnabled(true);<br />mAnimStateList.push_back(animState);<br />trail-&gt;setInitialColour(0, 1.0, 0.8, 0);<br />trail-&gt;setColourChange(0, 0.5, 0.5, 0.5, 0.5);<br />trail-&gt;setInitialWidth(0, 5);<br />trail-&gt;addNode(animNode);<br /><font color="#008000">// Add light<br /></font>Light* l2 = mSceneMgr-&gt;createLight("l2");<br />l2-&gt;setDiffuseColour(trail-&gt;getInitialColour(0));<br />animNode-&gt;attachObject(l2);<br /><font color="#008000">// Add billboard</font><br />BillboardSet* bbs = mSceneMgr-&gt;createBillboardSet("bb", 1);<br />bbs-&gt;createBillboard(Vector3::ZERO, trail-&gt;getInitialColour(0));<br />bbs-&gt;setMaterialName("Examples/Flare");<br />animNode-&gt;attachObject(bbs);</p>
		<p>}<br />首先，RibbonTrail创建了两个链，每个最多包括80个元素，尾巴长400个世界单位。创建了一个结点animNoade。为这个结点创建了一个动画,使得这个结点可以根据关键帧的设计在场景中不断变化位置。并把一个RibbonTrail与这个结点联结起来，于是RibbonTrail可与animNode一起运动。又创建了一个灯，并联结到这个结点，灯随结点也一起运动，于是怪物头会有明暗变化。最后用一个布告板来形象表示刚创建的灯，因为灯在场景中是不可见的。因为灯被挂到animNode结点上，所以布告板bbs也挂到同样的结点上。当然别忘了使用了动画状态来推进动画。</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/20216.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-20 15:31 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/20/20216.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第九章 动画 第一部分 </title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/16/19974.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 16 Mar 2007 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/16/19974.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19974.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/16/19974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19974.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19974.html</trackback:ping><description><![CDATA[清源游民　<a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a><br /><font color="#0000ff">概述</font><br />动画，总的来讲，在计算机发展的各个阶段，与艺术家们快速浏览一系列彼此稍微不同的图片从而产生运动的幻觉的行为没有什么不同。1872年，Eadweard Muybridge给奔跑的马拍了一组连续的照片，从而证明了马在奔跑时会四脚同时离地。这是一系列的图片流可以产生运动幻觉的最早的文档化的记录。<br />现在在Ogre中，不必小题大作地装配一些设备来创造这种运动幻觉了。只需要每帧少量地移动或变形场景中的数据，幻觉就产生了。它确实只是幻觉。ogre不跟踪对象的速度，加速度，或是角色的胳膊是在抬起<br />或是叉着腰。ogre逐帧来处理位置与朝向。ogre能为你做的是帮你重新播放这些动画。这些动画可以是用离线工具制作的，或是运行时产生的，或仅是为了实现相机漫游而填充了一系列位置的曲线。<br />场景的每帧都会被重画，两帧之间没有什么会被保存下来。ogre中的动画仅仅是根据关于任意变量（通常<br />是时间）的函数，来定位与定向角色。每一帧，在ogre动画系统的控制之下，场景中的实体与动画被稍稍<br />移动或产生变形，然后场景被重画。这种产生动画的方式，与华纳兄弟的动画团队做卡通动画没什么同。<br />一行画24个卡通兔，彼此稍微不同，然后在1秒钟的时间连续播放。<br /><font color="#0000ff">Ogre中的动画类型</font><br />根据控制物体的不同方式分为两种：基于关键帧的动画，控制器。<br />抽象地来看，ogre中的动画是相关tracks的集合。track是作为时间函数存储的一组数据值。一对“track值”与"时间点“，构成了关键帧(keyframe)，更好的一种说法是，在时间轴线上对对象全部或部分的位置，朝向，缩放，进行采样。根据动画的类型，ogre支持好几种关键帧。"关键帧”这个名称来源于手工制作动画的时代（像上面提到的制作卡通兔动画），首席艺术家会给他的下级提供一组关键的帧。这些帧给出了场景中角色的位置，然后下级艺术家们根据这些画出帧之间角色的位置。在ogre中，你就是首席艺术家，需要提供这些关键帧，ogre是你的下级，它帮你完成帧之间的过渡。<br />ogre中支持的track有下面这几种类型（一个track中的所有关键帧必须有一致的类型）:<br /><font color="#ff1493">NumericAnimaitonTrack(NumericKeyFrame):<br /></font>关键帧包含一个AnyNumeric类型的标量值。Any是ogre中定义的结构，和COM中的variant　类型一样，允许数据类型的多态化。AnyNumeric限定了可能的数据类型只能是数字的(实数，整数)。<br /><font color="#ff1493">NodeAnimationTrack(TransformKeyFrame):<br /></font>关键帧包含两个3维向量，一个四元数，分别表示结点的位置，缩放与朝向。<br /><font color="#ff1493">VertexAnimationTrack(VertexMorphKeyFrame,VertexPoseKeyFrame):<br /></font>关键帧包含或是引用在一个特定时间点上（在pose 动画中）顶点位置和混合权重(VertexPoseKeyFrame)。<br /><font color="#0000ff">动画状态<br /></font>程序与ogre中的动画主要的交互是通过动画状态来完成的(animation state)。在建模工具中，可以为骨骼或顶点网格沿时间轴线定义多个动画，导出时每个动画都可以定义一个名字，通过这个名字来定位Entity中同的动画。通过动画状态可以访问动画的许多属性：长度，当前位置,动画名，是否循环，有否有效，权重（用于混合动画）。权重根据混合动画的类型进行不同的处理。<br /><font color="#ff1493">混合权平均：</font><br />告诉ogre做这种混合时，所有权重之和为1。如果不是1，ogre会进行正规化处理，使得它们的总和为1。这种方式只对骨骼动画有效，它也是默认的混合方式。<br /><font color="#ff1493">混合权累积：</font><br />ogre只是简单地把所有引用权重的效果进行累加，不会再平衡。这种方式只对顶点动画有效（pose, morph),用到骨骼动画时也可以。<br />动画状态也是也给动画“加时间”推动它向前变化的地方。基本上总是给一个时间增量，意味着从上次调用到现在增长了的时间。本质上，加一个时间增量与直接设定时间位置没有什么不同。<br /><font color="#0000ff">骨骼动画<br /></font>骨骼动画是最常用的动画类型。网格(mesh)中的顶点被绑定到骨骼上，如同我们身体的肌肉绑定到骨上。<br />不同的是，计算机动画中的“骨头”不是真实存在的。它们用一些（无对应实体）变换来表示骨头的位置，朝向与缩放，经常是用矩阵来表示。骨骼动画的关键帧，仅是相对于骨骼恢复位置的偏移。它们被组合起来形成了沿着时间轴线上某一给定点的骨骼。<br />Ogre中的骨骼是有层次关系的，如脚骨与胫骨相连，胫骨又与膝盖骨相连。在这种层次关系下，除了root外，每个骨头都有父亲。但并不限于只有一个root,这种层次关系仅仅是用来传递转换(自上而下)。改变一根骨头会影响它所有孩子的位置。反向的运动ogre不支持：当一个骨头运动时，它不会影响父辈。<br /><font color="#0000ff">顶点绑定<br /></font>顶点被绑定到骨骼时会指定一个权重，权重说明了骨头运动对顶点的影响效果。典型的是把一个顶点绑<br />定到多个骨头，赋于不同的权重。ogre仅支持每顶点四个骨头，但每个骨头可以与任意多个顶点关联。<br /><font color="#0000ff">顶点动画<br /></font>与通过计算动画骨骼，再根据骨骼位置计算顶点位置的动画方式不同。顶点动画直接对顶点进行动画操作。它是资源密集型的解决方案，对于每个动画位置都要转送完整的顶点数据的拷贝。有时这种方式是维一的办法，例如实现逼真的面部表情动画。<br />顶点动画在动画track这个层次上被管理、操纵。这意味着我们可以对角色的头，脸部分使用复杂的pose动画，而角色的剩余部分使用相对简单的morph动画。但是不能对相同的顶点使用不同的动画类型。顶点动画在引用一组顶点集时，要么全部引用，要么一个也不引用。这意味着，在网格级别上来看，所有的共享几何(shared geometry)构成了顶点动画的顶点数据。在子网格级别上来看，所有子网格的顶点构成了顶点动画的顶点数据。举例来讲，一个子网格的某些顶点参与morph动画，而剩余顶点参与pose动画，这种情况是不允许的。<br /><font color="#ff1493">变形动画(Morph Animation)<br /></font>变形动画是两种顶点动画技术中最简单最直观的一种。实际的顶点位置做为"快照"被保存。这与前面说到<br />的卡通兔动画的过程是一样的。这种方法比较消耗资源，但是计算效率最高。关键之间数据通过插值得。<br />主要的缺点是不能混合多个变形动画，因为他们保存的是顶点的绝对位置。当然，如果有两个动画，它们<br />影响的顶点没有交集，那么确实可以对这两个动画进行混合。<br /><font color="#0000ff"><font color="#ff1493">姿势动画(Pose Animation)</font><br /></font>模型的各种姿势被保存在动画tracks中。这些姿势可以被混合来创建更复杂的动画。顶点的位置是相对于<br />复原位置的偏移量，而不是顶点的绝对位置。而且只有变化的数据才被保存。因此，它不仅更灵活，而且<br />对资源使用也更高效。<br />pose动画的关键帧引用一个或多个pose tracks，每个引用对姿势有一个影响权重。权重决定了当pose被混合时每个顶点偏移对最终的顶点位置产生多大的影响。不必在每一帧都引用一个pose。在实践中，pose混合会碰到一些限制：每个附加的pose需要更多的处理时间。<br /><font color="#0000ff">混合动画</font><br />不能混合pose与morph动画到一起，也不能混合多个morph动画。<br />当混合骨骼动画与顶点动画时，ogre将首先计算/混合顶点动画，然后应用骨骼变形到混合顶点上。<br />规范的pose与骨骼动画混合的程序,会混合全身体的骨骼动画与复杂的面部表情。例如，可以对角色的面部顶点使用pose动画，而角色的剩余部分(包括头)使用相对简单的骨骼动画。<br />混合两个骨骼动画对于动画之间的切换是最有用的。例如角色可能在循环地执行"跑步"这个动画，一会儿<br />他可能要停下来（执行"idle"动画），那么这时需要对两个动画进行混合。<br />Morph与骨骼动画的混合，对于给骨骼动画的角色增加"外皮"网格变形是很合适的。例如,将一个衣服动画与角色实际的骨骼运动动画混合，这样就避免了对衣服进行实时模拟的高额代价。<br /><font color="#0000ff">硬件vs软件<br /></font>应用硬件加速时最应该记住的是：硬件动画网格数据不能被CPU使用。这相当重要！假如你依赖网格数据<br />进行别的计算(体积阴影计算，物理模拟），一旦顶点变形计算被移到GPU中，顶点就永远消失了，不在代码中使用了，假如需要变形之后的顶点数据，那么你还得用CPU进行与GPU同样的计算！这确实也可以做到,告诉ogre做吧：　Entity::addSoftwareAnimationRequest()。<br />硬件加速的骨骼动画与顶点动画混合，会在顶点程序中执行混合。假如你刚才还没有注意到上面曾经提到<br />过的执行顺序的话，现在又有机会让你意识到这点：先顶点，然后骨骼。这同样也意味着，所有的动画技术都应该是基于硬件加速的，不能让硬件加速的骨骼动画与基于软件的顶点动画混合。<br />使用GPU加速<font color="#ff1493">骨骼动画</font>时，应该考虑到：骨骼动画算法的本质上要求，在计算每个顶点时，骨架中所有骨头的数据必须都被提供。老旧的硬件仅仅支持24　bones或更少的skinning通道。这时应把mesh分为几段，在多个通道中skin它们。ogre只会把submesh引用到的bones发送到skinning shader,而不是骨骼中所有的bones,这使得多个skinnig pass成为可能。<br />在CPU中对<font color="#ff1493">morph动画</font>顶点进行插值的主要优点是利用了GPU并行计算的本质。morph计算起来也不复杂，<br />主要的输入是两个顶点位置与一个插值因子，顶点程序产生插值后的顶点位置做为输出。<br /><font color="#ff1493">pose动画</font>与morph类似，而且有与骨骼动画类似的限制：它提供给程序的pose数目如何进行传递。<br /><font color="#0000ff">标签点<br /></font>标签本身不是动画的一部分，但它们常被用于联结任意对象（例如武器）到存在的动画模型上。标签点<br />的概念比较简单：它是骨架中一块特殊的骨头，它能在运行时，控制绑定对象的位置与朝向。不必事先<br />创建标签点，可以在运动期attched一个可移动对象到骨架中一个命名的骨头上。Entity有这样一个方法：<br />attachObjectToBone();一旦物体被绑到骨头上，它就会随着标签点移动。需要知道的是，对象并不是真正<br />地被attched到标签点上。它被attched到用那个骨骼的Entity上。<br /><font color="#0000ff">动画与控制器</font><br />两者都是基于一个一维的变量来控制对象的状态，把变量输入一个函数产生输出。对于动画来说，输入变量是时间，而对于控制器来说，输入变量是任何你想要的东西。<br /><font color="#ff1493">动画的特点：</font><br />通过插值函数计算给定时间点上track值来产生输出，从而改变顶点位置。<br />动画被认为是个封闭的系统：你设置输入值，ogre 使用它来更新相关对象的属性。<br />插值函数较为固定：要么线值，要么三次样条插值。<br />动画有ogre提供的许多工具的支持。<br /><font color="#ff1493">控制器的特点</font>：<br />对于输出的计算更为灵活，工作函数需要提供，因此有更多选择。<br />控制器可以形成一个链，一个输出可作为另一个输入。<br />控制器自己不移动任何东西，对于输出可以不做任何事情。ogre用控制器来驱动纹理动画。<br />控制器自动逐帧更新，然而必须手工添加代码使每帧的动画向前变化。<img src ="http://www.cppblog.com/yuanyajie/aggbug/19974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-16 17:33 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/16/19974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第八章 渲染对象</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/15/19910.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Thu, 15 Mar 2007 09:25:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/15/19910.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19910.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/15/19910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19910.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19910.html</trackback:ping><description><![CDATA[
		<p>清源游民　gameogre@gmail.com<br /><br /><font color="#0000ff">OGRE渲染对象</font><br />Ogre中的render target 只是共享AGP内存或显存的某个区域的抽象，这个区域中保存着全部或部分场景2D渲染结果。最普通的render target是主渲染窗口，这是应用程序的主窗口。使用这个render target不需要做太多另外的努力，ogre可以帮我们创建它。还可以把场景中的全部或部分（甚至是场景中不可见的部分）渲染到一个纹理，这个纹理可以被场景中的其他多边形使用。目标硬件缓冲在ogre中抽象为render target,它的物理显示可以为主渲染窗口，也可以是副窗口，也可以是非可视的：纹理。<br />渲染对象也是事件源，假如程序注册了这事件，ogre会在pre- 和 postrender时通知程序，这给程序改变设置，做出响应的机会。甚至在视口(viewport)级别上pre-,post- 渲染时也有上述通知机制。ogre提供了计算渲染状态信息的功能,如多长时间渲染一帧，渲染对象上有多少三角形等。<br /><font color="#0000ff">渲染窗口</font><br />窗口是由渲染系统的具体实现创建与维护的（D3D9下是个标准的Win32 Window)。Ogre允许对渲染窗口进行很少的配置，限于尺寸，标题栏文字等。假如希望开发的程序有菜单，工具栏等，把ogre的渲染输出到<br />窗口的客户区，这是可能的。ogre可以提供你系统相关的窗口句柄给渲染窗口，也允许你提供句柄给父窗口。第一个创建的ogre渲染窗口称为主窗口。另外创建的是副窗口。窗口不像主窗口那么重要。假如在程序中有三个窗口，那么为了正确的进行资源清理，必须在销毁主窗口前把另外两个副窗口销毁。<br /><font color="#0000ff">视口<br /></font>渲染窗口包含一个或多个视口。视口是一个长方形区域，场景管理器把从一个相机中看到的场景可见内容<br />的透视图渲染到这个区域中。视口创建时会引用一个相机，但这不是视口的静态属性，可以随时改变用于<br />渲染视口的相机。每个视口拥有一个z序数，z序高的位于Z序低之上。z序0，总是被一个能覆盖整个渲染对象的视口所拥有。缺省，ogre会清理视口的颜色与深度缓冲。然而，可以关闭这些缓冲清理。视口是渲染对象（也就是特定的渲染系统，如OpenGL)与相机（也就是特定场景管理器与它的内容）之间的唯一交互点。视口不必占用整个渲染对象的表面。3D渲染相机不是真正的相机，所有的几何LoD计算是以相机的位置算的，因此不能简单的改变相机的焦点属性来达到“更近”的渲染效果。<br /><font color="#0000ff">渲染到纹理<br /></font>使用步骤是：创建一个纹理渲染对象，配置它的渲染属性，加纹理到渲染对象列表，设置纹理到被使用的材质。纹理对象先于其他渲染对象类型更新，这就保证了使用了渲染纹理的对象正确地被渲染。渲染纹理可以像其他普通纹理一样被处理。对于那些不需要每帧都更新的渲染纹理，可以关闭自动逐帧更新而采用手动更新。从底层看，纹理对象就是一块硬件缓冲。为了性能考虑，认为它们是只写与静态的。渲染到纹理会渲染场景中的几何，需要一些时间执行，会降低应用程序帧率。但是很多有用的技术现在离不开渲染到纹理的使用。实时阴影，实时反射等。ogre支持渲染到多个纹理，唯一的限制是它们就具有相同的寸。<br /><font color="#0000ff">渲染对象类<br /></font>RenderTarget是RenderWindow,MultiRenderTarget,与RenderTexture的基类。<br />RenderWindow通过子类化来实现：D3D9RenderWindow,GLXWindow等。在windows操作系统中，由于可以使用Direct3D 9或是OpenGL,于是可以分别使用D3D9RenderWindow，Win32Window。<br />RenderSystem::createRenderWindow()来创建窗口。<br />virtual RenderWindow* Ogre::RenderSystem::createRenderWindow  (  const String &amp;  name,  <br />  unsigned int  width,  <br />  unsigned int  height,  <br />  bool  fullScreen,  <br />  const NameValuePairList *  miscParams = 0 <br /> )  <br />通过最后一个参数，可以设置窗口的一些属性。<br />将渲染窗口嵌入外部窗口的代码<br />NameValuePairList params; <font color="#008000">// is just a typedef std::map&lt;std::string, std::string&gt;<br /></font><font color="#008000">// set external window handle -- assume that you have<br />// already created a window to embed the Ogre render window, and its handle is<br />// stored in an integer variable called "mParent"<br /></font>params["externalWindowHandle"] = StringConverter::toString(mParent);<br /><font color="#008000">// window can be resized later to fit within parent client area if needed<br /></font>RenderWindow* window = createRenderWindow("MyWindow", 800, 600, false, &amp;params);<br /><font color="#0000ff">渲染到纹理Demo</font><br />demo在原点创建了一个倾斜的平面，设置相机，把场景（场景由一个魔鬼脑袋与几个环面纽结组成）<br />相对于平面的倒影渲染到纹理中。渲染纹理与平面已经应用的静态纹理混合，这样就实现了反射效果。<br /><font color="#ff1493">创建渲染纹理:<br /></font>TexturePtr texture = TextureManager::getSingleton().createManual( "RttTex", <br />                               ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, <br />                              512, 512, 0, PF_R8G8B8, TU_RENDERTARGET );<br /><font color="#ff1493">创建相机与视口用于渲染到纹理:</font><br />             mReflectCam = mSceneMgr-&gt;createCamera("ReflectCam");<br />            mReflectCam-&gt;setNearClipDistance(mCamera-&gt;getNearClipDistance());<br />            mReflectCam-&gt;setFarClipDistance(mCamera-&gt;getFarClipDistance());<br />            mReflectCam-&gt;setAspectRatio(<br />                (Real)mWindow-&gt;getViewport(0)-&gt;getActualWidth() / <br />                (Real)mWindow-&gt;getViewport(0)-&gt;getActualHeight());<br />            Viewport *v = rttTex-&gt;addViewport( mReflectCam );<br />            v-&gt;setClearEveryFrame( true );<br />            v-&gt;setBackgroundColour( ColourValue::Black );<br /><font color="#ff1493">创建使用渲染纹理的材质</font><font color="#ff1493">：</font><br />MaterialPtr mat = MaterialManager::getSingleton().create("RttMat",<br />                ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);<br />            TextureUnitState* t = mat-&gt;getTechnique(0)-&gt;getPass(0)-&gt;createTextureUnitState("RustedMetal.jpg");<br />            t = mat-&gt;getTechnique(0)-&gt;getPass(0)-&gt;createTextureUnitState("RttTex");<br />           <font color="#008000"> // Blend with base texture<br /></font>            t-&gt;setColourOperationEx(LBX_BLEND_MANUAL, LBS_TEXTURE, LBS_CURRENT, ColourValue::White, <br />                ColourValue::White, 0.25);<br />            t-&gt;setTextureAddressingMode(TextureUnitState::TAM_CLAMP);<br />            t-&gt;setProjectiveTexturing(true, mReflectCam);<br />            rttTex-&gt;addListener(this);<br />材质名为RttMat,包含一个技术，后者有一个通道。通道有两个纹理单元,一个为静态纹理(RustedMetal.jpg)<br />,另一个纹理单元为渲染纹理,两个纹理按比例混合。设置了合适的纹理寻址模式，开启了透视纹理支持。<br />并为渲染纹理（它是渲染对象）注册了侦听器。<br /><font color="#ff1493">相机倒置使用材质:</font><br />            <font color="#008000">// set up linked reflection<br /></font>            mReflectCam-&gt;enableReflection(mPlane);<br />            <font color="#008000">// Also clip</font><br />            mReflectCam-&gt;enableCustomNearClipPlane(mPlane);<br />        }<br />          <font color="#008000"> // Give the plane a texture</font><br />           mPlaneEnt-&gt;setMaterialName("RttMat");<br />相机这样设置后，渲染出的将是场景的倒影，这是以指定平面为参照的。定制最近裁剪平面的作用是：<br />那些低于反射面的对象将不会再被渲染。渲染纹理时，并不想把平面也渲染进去，可以在上边已经注册<br /><font color="#ff1493">侦听器做点手脚</font>：<br />void preRenderTargetUpdate(const RenderTargetEvent&amp; evt)<br />    {<br />       <font color="#008000"> // Hide plane <br /></font>        mPlaneEnt-&gt;setVisible(false);</p>
		<p>    }<br />    void postRenderTargetUpdate(const RenderTargetEvent&amp; evt)<br />    {<br />        <font color="#008000">// Show plane <br /></font>        mPlaneEnt-&gt;setVisible(true);<br />    }<br />最后要提的是，在每一帧，都要使两个相机的位置，朝向保持一致:<br /><font color="#ff1493">frameStarted:<br /></font> mReflectCam-&gt;setOrientation(mCamera-&gt;getOrientation());<br />  mReflectCam-&gt;setPosition(mCamera-&gt;getPosition());</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-15 17:25 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/15/19910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第七章 资源管理  第一部分</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/14/19827.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Wed, 14 Mar 2007 09:09:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/14/19827.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19827.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/14/19827.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19827.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19827.html</trackback:ping><description><![CDATA[
		<p>清源游民　　gameogre@gmail.com<br /><br /><font color="#0000ff">资源组</font><br />命名组可以作为一个整体加载与卸载。在加载，卸载，初始化时把把组中的所有资源作为一个执行单位来<br />看待，而不是逐个进行处理。资源组管理纯粹是了为了管理上的方便，是否使用组的方式与性能无关。假<br />如向资源组管理器中加入了资源位置而没有指定组名，那么这些资源位置被放入"General"组中。<br /><font color="#0000ff">资源组与世界几何<br /></font>缺省情况下，ogre把加载的世界几何放入"General"组。也可以覆盖(override)这种方式，使得对世界几何的管理像其他资源的管理方式一样。统一的场景管理器可以提供关于装载世界总步骤的线索，这样就可以在关卡加载时，提供精确的以进度条显示的反馈信息。<br /><font color="#0000ff">资源位置(location)</font><br />资源位置是ogre去查找资源进行索引的地方。索引指的是在某个位置的所有资源通过它们的名字被映射，这样可以方便进行更快的查找资源。可以在程序的任何时候添加或删除资源位置,不必事先对所以可能用到的进行定义。资源位置在ogre中实际上是个archive,它的意思就是“文件的集合”。磁盘上的文件系统是<br />archive的一种类型。另外一种是zip archive。可以自定义archive格式。这通过改写archive类实现来完成,必须支持对命名的leaf文件进行枚举操作，必须支持通配符，支持结点递归，给ogre提供一个流访问archive中文件的数据。ogre中的archive是只读的。ogre资源管理器利用archive枚举特性来索引archive的内容，当ogre对archive进行索引时，不会实际加载任何资源。<br /><font color="#0000ff">资源生命周期</font><br />资源有四种状态,各状态之间的转换关系如下图：<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_Resource.JPG" /><br /><font color="#ff1493">Undefined:</font>这是程序开始时，所有资源的缺省状态。除非它们被声明，ogre对程序用到的资源一无所知，手工调用代码 ResourceGroupManager::declareResource()或在脚本中被解析之后，资源的状态变为Declared<br /><font color="#ff1493">Declared:</font>声明就是告诉ogre想要加载某些资源。ResourceGroupManager::declareResource()总是有效的，包括在渲染系统初始之前，这与ResourceManager::create()不同，因为后者依赖于渲染系统。<br /><font color="#ff1493">Unloaded</font>:通过调用ResourceGroupManager::initialiseAllResourceGroup(), ResourceGroupManager::<br />initialiseResourceGroup(),或Root::initialise()(它会初始化在此调用之前所有声明的资源)，资源状态进入到Unloaded,资源会占用一点内存来保存它的定义的实例，但是资源本身还没有加载到内存。从另一角度看(从Loaded到Unloaded),引起状态变化的调用有：ResourceManager::unload(),ResourceManager::unloadAll(), ResourceManager::unloadAllUnreferencedResources(),ResourceGroupManager::unloadResourceGroup(), or Resource::unload().所有这些调用仍会保存资源实例，但真正的资源数据会从内存中卸载。<br /><font color="#ff1493">Loaded:</font> 这种状态下，所有数据都变得有效。与此状态有关的调用有Resource::load(), Resource::reload(),<br />ResourceManager::load(), ResourceManager::reload(), ResourceManager::reloadAll(),<br />ResourceManager::reloadAllUnreferencedResources(), and ResourceGroupManager::<br />loadResourceGroup().<br /><font color="#0000ff">逻辑资源管理<br /></font>资源以命名组的形式组织，每组可以包括任何类型的资源，每种资源都有自己的资源管理器，后者负责加载与卸载特定类型的资源。ogre对它的资源没有实现特定的内存管理方案，如果你需要对某种资源实“最近最少使用算法”方案来进行管理，那么需要自己的代码来实现。值的一提的是，现在的大多数显卡驱动，对于大多数重要的资源已经实现了这种LRU算法管理。<br /><font color="#0000ff">资源加载<br /></font>假如没有预加载，当资源被访问时会进行加载。实际的加载，卸载是资源自己的责任。<br /><font color="#0000ff">手动加载资源</font><br />资源管理层不负责实际的加载与卸载。通常，不必担心资源是否存在于易失性媒介。然而，手工方式时需要考虑。手工资源加载器必须在任何时候准备好重新加载资源。假如某个资源是通过程序生成的，那么手工资源加载器必须内存中缓冲这些资源，或者是当资源管理加载时重新创建它。ogre认为手工加载与自动加载没有区别。<br /><font color="#0000ff">后台资源管理<br /></font>缺省，ogre不是线程安全的。假如在OgreConfig.h中 #define OGRE_THREAD_SUPPORT 1 ,那么资源管理<br />代码的线程同步功能变得有效，我们就可以在包含Root实例的线程之外，开启新的线程对资源管理类与方法进行操作,从而实现灵活的资源加载方案。　<br /><font color="#0000ff">非后台资源管理<br /></font>Ogre中大量使用了Observer模式,资源管理系统也不例外。ResourceGroupListene 回调接口包含了几个方法<br />允许对资源加载过程进行细粒度的监听。<br /><font color="#0000ff">资源卸载</font><br />资源被加载后总存在于内存中，直到被应用程序强行卸载(通过资源组管理器或是被资源直接释放)。资源管理组管理会把组中所有的资源都卸载掉。在资源被引用时不能强行卸载。<br /><font color="#0000ff">Resource Locations<br /></font><font color="#008000">// 配置文件方式<br /></font>ConfigFile cf;<br />cf.load("resources.cfg");<br /><font color="#008000">// Go through all sections &amp; settings in the file<br /></font>ConfigFile::SectionIterator seci = cf.getSectionIterator();<br />String secName, typeName, archName;<br />while (seci.hasMoreElements())<br />{<br />secName = seci.peekNextKey();<br />ConfigFile::SettingsMultiMap *settings = seci.getNext();<br />ConfigFile::SettingsMultiMap::iterator i;<br />for (i = settings-&gt;begin(); i != settings-&gt;end(); ++i)<br />{<br />typeName = i-&gt;first;<br />archName = i-&gt;second;<br />ResourceGroupManager::getSingleton().addResourceLocation(<br />archName, typeName, secName);<br />}<br />}<br /><font color="#008000">//硬编码方式<br /></font>ResourceGroupManager *rgm = ResourceGroupManager::getSingletonPtr();<br />rgm-&gt;addResourceLocation("../../media/packs/OgreCore.zip", "Zip", "Bootstrap");<br />rgm-&gt;addResourceLocation("../../media", "FileSystem", "General");<br />rgm-&gt;addResourceLocation("../../media/fonts", "FileSystem", "General");</p>
		<p>
				<font color="#0000ff">初始化</font>
				<br />在初始化之前，必须创建至少一个渲染窗口。因为在分析脚本时可能会创建GPU资源，而后者需要渲染<br />上下文。<br /><font color="#008000">// initialize all of the previously defined resource groups</font><br />ResourceGroupManager::getSingleton().initialiseAllResourceGroups();<br /><font color="#008000">// or, alternately, initialize the defined resource groups one at a time<br /></font>ResourceGroupManager::getSingleton().initialiseResourceGroup("General");<br />ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");<br /><font color="#0000ff">卸载</font><br />可以在任何时候卸载资源（以组或单个的方式）,正在使用的资源不会被卸载。<br /><font color="#0000ff">以组的方式卸载</font><br />ResourceGroupManager::getSingleton().unloadResourceGroup("Bootstrap", true);<br />ResourceGroupManager::getSingleton().<br />unloadUnreferencedResourcesInGroup("Bootstrap", true);<br />true表示只卸载资源数据，不删除资源实例，它可以被reloaded。有些资源在创建时被标为<br />"nonreloadable",这种类型的资源不能使用上述方法卸载。<br /><font color="#0000ff">清理或销毁资源组</font><br />清理仅是卸载资源与分离资源索引，销毁不仅做清理的工作，还包括从资源组中把自己移除。<br />ResourceGroupManager::geSingleton().clearResourceGroup("Bootstrap");<br />ResourceGroupManager::geSingleton().destroyResourceGroup("Bootstrap");<br /><font color="#0000ff">以个体方式卸载</font><br /><font color="#008000">// assume that pEntity is a valid pointer to an instance of Entity</font><br />MeshPtr meshPtr = pEntity-&gt;getMesh();<br />meshPtr-&gt;unload();<br /><font color="#0000ff">加载/重载资源组</font><br />ResourceGroupManager::getSingleton().loadResourceGroup("Bootstrap", false, true)<br />二个布尔变量不同资源类型资源加载开关，一针对"Normal"资源，二针对"World geometry"<br /><font color="#0000ff">资源组加载通知<br /><font color="#000000"><font color="#0000ff">class</font> LoadingProgressListener : <font color="#0000ff">public</font> ResourceGroupListener<br /></font>{<br />   public: <br />       </font><font color="#000000"><font color="#008000"> // fired when a group begins parsing scripts</font><br />                void resourceGroupScriptingStarted(const String&amp; groupName,<br />                                                                        size_t scriptCount) {}<br />       <font color="#008000">// fired when a script is about to be parsed<br /></font>               void scriptParseStarted(const String&amp; scriptName) {}<br />       <font color="#008000">// fired when the script has been parsed<br /></font>               void scriptParseEnded() {}<br />       <font color="#008000">// fired when all scripts in the group have been parsed</font><br />              void resourceGroupScriptingEnded(const String&amp; groupName) {}<br />      <font color="#008000">//还有一些接口<br /></font>}<br /><font color="#008000">//实现之后，进行注册<br /></font>LoadingProgressListener listener(m_progressMeter);<br />ResourceGroupManager::getSingleton().addResourceGroupListener(&amp;listener);</font></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-14 17:09 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/14/19827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第六章 材质　材质复制 </title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19754.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 13 Mar 2007 07:57:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19754.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19754.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19754.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19754.html</trackback:ping><description><![CDATA[
		<p>清源游民　gameogre@gmail.com<br /><br /><font color="#0000ff">材质复制</font><br />如果新的脚本只是对某个已经存在的脚本进行小部分的变化，那么可进行从旧脚本复制，而不是C＆P。新材质可以修改特定technique,pass,texture,或是添加新项。<br />格式: material &lt;NewUniqueChildName&gt; : &lt;ReferanceParentMaterial&gt;<br />当材质脚本被加载到ogre中，这种继承关系不再维系，假如父材质在运行时被修改，不会影响到子材质。如果在一个technique中有5个pass,而我们只想修改第5个pass,我们可以用pass指定或是它的索引(从0计）<br />meterial test2: test1<br />{<br />  technique 0<br />    {<br />      pass 4<br />        {<br />          ambient 0.5 0.7 0.3 1.0<br />        }<br />    }<br />}<br />加入一个新的technique或pass到material中，可以通过赋于它们新的名称来实现，新名称必须在父材质中没有出现过。指定新索引也一样。新technique或pass会被加载到父材质相应technique或pass末尾.<br />material BumpMap2 : BumpMap1<br />{<br />  technique ati8500<br />  {<br />    pass 0<br />    {<br />      texture_unit NormalMap<br />      {<br />        texture BumpyMetalNM.png<br />      }<br />    }<br />  }<br />}<br />在上面的例子中，如果texture_unit的名称NormalMap在父材质中指定过，那么现在的行为表示override,如果它是一个新的名字，那么表示在pass的最后新加一个texture unit。　</p>
		<p>
				<font color="#0000ff">纹理别名</font>
				<br />
				<br />在源材质被clone时，每个texture unit可以被赋于一个Texture alias(别名）。可以用这个别名来指定使用什么纹理。格式： texture_alias &lt;name&gt; 缺省： texutre_unit &lt;name&gt;( 纹理单元的名字)<br />texture_unit DiffuseTex<br />{<br />  texture diffuse.jpg<br />}<br />在这种情况下，texture_alias为DiffuseTex.<br />material TSNormalSpecMapping<br />{ <br />  <font color="#0000ff">technique </font><font color="#000000">GLSL<br /></font>  { <br />    pass <br />    { <br />      texture_unit <font color="#ff1493">NormalMap<br /></font>      {<br />        texture defaultNM.png<br />        tex_coord_set 0<br />        filtering trilinear<br />      }</p>
		<p>       texture_unit <font color="#ff1493">DiffuseMap</font><br />      {<br />        texture defaultDiff.png<br />        filtering trilinear<br />        tex_coord_set 1<br />      }</p>
		<p>        texture_unit <font color="#ff1493">SpecMap</font><br />      {<br />        texture defaultSpec.png<br />        filtering trilinear<br />        tex_coord_set 2<br />      }<br />    } <br />  } </p>
		<p> <font color="#0000ff"> technique </font><font color="#000000">HLSL_DX9</font><br />  { <br />    pass <br />    { <br />        texture_unit <br />      {<br />        texture_alias <font color="#ff1493">NormalMap</font><br />        texture defaultNM.png<br />        tex_coord_set 0<br />        filtering trilinear<br />      }</p>
		<p>      texture_unit <br />      {<br />        texture_alias <font color="#ff1493">DiffuseMap</font><br />        texture defaultDiff.png<br />        filtering trilinear<br />        tex_coord_set 1<br />      }</p>
		<p>      texture_unit<br />      {<br />        texture_alias <font color="#ff1493">SpecMap</font><br />        texture defaultSpec.png<br />        filtering trilinear<br />        tex_coord_set 2<br />      }<br />    } <br />  } <br />}<br />例子中有两个技术，都使用相同的纹理,第一个技术使用缺省的方式定义了纹理别名，第二个技术显式地指定纹理别名。两者都有相对应的同样的纹理别名。如果想定义一个材质只是想使用不同的纹理,那么copy父材质时可以使用set_texture_alias来重新指定新纹理。<br />material fxTest : TSNormalSpecMapping<br />{<br />  set_texture_alias <font color="#ff1493">NormalMap</font> fxTestNMap.png<br />  set_texture_alias <font color="#ff1493">DiffuseMap</font> fxTestDiff.png<br />  set_texture_alias <font color="#ff1493">SpecMap</font> fxTestMap.png<br />}<br />上面代码就对拥有指定别名的texture unit所使用的纹理图片进行了重新指定。<br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-13 15:57 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/13/19754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第六章 材质　概念</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19703.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 13 Mar 2007 03:31:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19703.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19703.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/13/19703.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19703.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19703.html</trackback:ping><description><![CDATA[
		<font color="#0000ff">基本对象着色<br /></font>材质，简单的来讲就是定义了被赋于此材质的对象如何反射光。ogre中的光照采用局部光照模型，这意味着，对计算有影响的因素有：光照的角度与光的颜色，相机的视角，对象的材质。ogre支持四种类型用来描述材质的颜色,表述了光照对它的影响：环境光，漫反射，发散，镜面反射。环境光是对全局光照的近似，漫反射是指当物体被光照射后在物体各方向上反射的光的颜色。发散指物体本身自发光的颜色。镜面反射，指的是在某个视角看到的被光反射出的“高光”。镜面高光有两个参数可以控power,shininess.power用来控制“加亮点”的尺寸(影响的范围越大)。而shininess值越大的表面，高光越容易聚集。<br /><font color="#0000ff">纹理映射<br /></font>通过纹理坐标，用一张2D图片给3D对象着色的技术。<br /><font color="#0000ff">可编程着色</font><br />通过写高级或低级的顶点程序，片段程序代替固定管线的功能。支持可编程图形硬件也可以当做固定函数流水线硬件来用。可编程硬件允许更灵活的计算顶点的的颜色，位置等。纹理也可用于可编程硬件。纹理中包含的数据，不仅仅可以表示颜色，也可以表示任何其他程序员知道如何处理的数据。<br /><font color="#0000ff">Batching<br /></font>材质与mesh之间的关系所带来的最大影响是渲染状态的改变。ogre中的渲染单元大多数情况下指的是renderable,它是SubEntity的父类。渲染单元这个概念的重要性在于，正是在这里，ogre将调用显卡驱动开始并且会结束“画操作”。一次画操作（batch),指的是清除颜料（color,texture,etc)与材料（vertexof display lists)，从头开始一组新的操作的过程。就像一个画家每次在画板上做画时会洗净他的调色板一样。对于3D硬件来讲，这个过程包括发送新的顶点列表与数据索引到GPU(或者引用还存在的），用纹理，颜色，以及光栅化这些顶点组成的面需要的一些元数据来设置GPU。因此，最有效率的做法是一次尽可能多的把<br />使用同样渲染数据的顶点发送到GPU进行处理。ogre会尽量减小状态改变带来的影响，但它遵重你如何构成一个renderable的决定。假如你有一个模型它有20个块组成，它们都使用相同的材质，这时应该把他们整合到相同的renderable中去，而不是分20次batch发送到显卡。每帧的batch数会有个上限。<br /><font color="#0000ff">材质克隆<br /></font>ogre中的材质是共享的。当你从材质管理器获得一个指针，它与从使用这种材质的其他对象所拥有的材质<br />指针是相同的。因此改变一处会影响其他，为了避免影响其他应该克隆材质。<br /><font color="#000080"><font color="#0000ff">Technique &amp; Scheme</font><br /></font>Technique允许针对不同的硬件平台定制不同的材质。Scheme允许对特定一组技术进行更一般化的描述。Ogre选择技术时有明确的顺序。首先，它寻找属于某个命名Scheme中的技术。然后在选好的scheme中查找那些应用了特定材质lod的技术。最后在这些技术列表中，选择最能适应当前硬件设置的技术。缺省，所有的技术属于细节级别0,它对应最高细节级别。<br /><font color="#0000ff">Material LoD</font><br />层次细节这个术语常用来讨论随场景中的物体的几何复杂性是相机与物体之间距离的函数。类似的概念被应用到材质定义上。ogre提供了在哪个层次细节上使用特定技术的手段。可以在材质中定义层次细节发生变化时的距离，赋于材质中的每个技术一个细节索引。每个细节索引也可以有多个技术，用来支持scheme或是硬件能力fallback。<br /><font color="#0000ff">材质组成<br /></font>一个materail由一个或多个technique组成，后者又由一个或多个pass组成，一个时刻只有一个technique是活动的。pass对于在GPU上执行画操作的renderable来讲，是完整的原子渲染状态。假如选择的技术有三pass，那么每帧会对renderable进行三次画操作。pass可以引用纹理单元定义，而不必包含任何纹理单元定义。<br /><font color="#0000ff">纹理单元<br /></font>ogre materail中的纹理单元实际引用GPU上的texture sampler.多数现代图形硬件有多个texture sampler,<br />ogre支持的硬件至少有一个有效的texture sampler。纹理单元包括一个纹理的引用，可以来自一个磁盘文件，或是运行时渲染，或是来自外部视频流。可以在每个pass中指定多个纹理单元。ogre会检测硬件支持的能力，必要时把一个pass折分成多个pass。举例来说，假设pass中指定6个纹理单元，而硬件只支持4个，那么ogre会把这个pass拆分成两个，对这两个pass执行纹理混合以达到相同的设计功能。纹理通常保存在video memory中，直到它们不再需要。ogre不会每次需要时都通过总线传输纹理，除非纹理经常地从<br />video memory中剔除。（这时发生了纹理震荡，原因是有太多或太大的纹理同时存在于vedio memory中）。<br /><font color="#0000ff">纹理压缩<br /></font>现代图形硬件支持压缩纹理。ogre只是简单地把纹理按原样传送他们到图形硬件中去，它不会压缩纹理。<br />对于那些压缩过的纹理，也不必事先处理它，如果硬件不支持压缩纹理，ogre会在运行时解压缩。<br /><font color="#0000ff">实体<br /></font>在渲染期间，Entity主要作为subentities的容器来用，subentities是实际的渲染单元（renderable）。<br />subentity与submesh有一一对应的关系，后者提供原始的材质引用。概念上来讲，entity(subentity)提供了对象的渲染属性，而mesh(submesh)提供了对象的结构属性。<img src ="http://www.cppblog.com/yuanyajie/aggbug/19703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-13 11:31 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/13/19703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第五章 场景管理　场景查询</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/11/19573.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Sun, 11 Mar 2007 09:24:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/11/19573.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19573.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/11/19573.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19573.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19573.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#0000ff">场景查询<br /></font>创建查询的代价比较大，而执行不是。SceneQueryResualt只定义了两种成员：movables与worldFragments.<br />掩码也需要自己定义，自己解释。在一个轴对齐盒子中查询灯光的例子如下：<br />const unsigned int LIGHT_QUERY_MASK = 0x00000001;　//掩码定义<br />Light* light1 = mSceneMgr-&gt;createLight("Light1");<br />Light* light2 = mSceneMgr-&gt;createLight("Light2");<br />light1-&gt;setPosition(12, 12, 12);<br />light2-&gt;setPosition(5, 5, 5);<br />light1-&gt;setQueryFlags(LIGHT_QUERY_MASK);<br />light2-&gt;setQueryFlags(LIGHT_QUERY_MASK);<br />AxisAlignedBoxSceneQuery* lightQuery =<br />mSceneMgr-&gt;createAABBQuery(<br />AxisAlignedBox(0, 0, 0, 10, 10, 10), LIGHT_QUERY_MASK);<br /><font color="#008000">// sometime later in the application's code, find out what lights are in the box<br /></font>SceneQueryResult&amp; results = lightQuery-&gt;execute(); //查询<br /><font color="#008000">// iterate through the list of items returned; there should only be one, and it<br />// should be light2 created above. The list iterator is MovableObject type.<br /></font>SceneQueryResultMovableList::iterator it = results.movables.begin();<br />for (; it != results.movables.end(); it++)<br />{<br /><font color="#008000">// act only on the lights, which should be all we have <br /></font>assert ((*it)-&gt;getQueryFlags() &amp; LIGHT_QUERY_MASK) != 0);<br /><font color="#008000">// do whatever it was we needed to do with the lights<br /></font>}<br /><font color="#008000">// destroy the query when we are done with it<br /></font>mSceneMgr-&gt;destroyQuery(lightQuery);</p>
		<p>我们知道地形总是起伏不平的，当主角在上面行走时需要根据地形的高度调整，可以光线查询来实现。<br />原理比较简单：向主角脚下执行光线查询，与地形有一个交点，根据交点的高度调整主角位置。<br /><font color="#0000ff">Terrain Clamping<br /></font>void Entity::clampToTerrain() {<br />static Ogre::Ray updateRay;<br />updateRay.setOrigin(m_controlledNode-&gt;getPosition() + Ogre::Vector3(0, 15, 0));<br />updateRay.setDirection(Ogre::Vector3::NEGATIVE_UNIT_Y);<br />m_raySceneQuery-&gt;setRay(updateRay);<br />Ogre::RaySceneQueryResult&amp; qryResult = m_raySceneQuery-&gt;execute();<br />if (qryResult.size() == 0) {<br /><font color="#008000">// then we are under the terrain and need to pop above it<br /></font>updateRay.setOrigin(m_controlledNode-&gt;getPosition());<br />updateRay.setDirection(Ogre::Vector3::UNIT_Y);<br />m_raySceneQuery-&gt;setRay(updateRay);<br />}<br />qryResult = m_raySceneQuery-&gt;execute();<br />Ogre::RaySceneQueryResult::iterator i = qryResult.begin();<br />if (i != qryResult.end() &amp;&amp; i-&gt;worldFragment)<br />{<br />Ogre::SceneQuery::WorldFragment* wf = i-&gt;worldFragment;<br />m_controlledNode-&gt;setPosition(m_controlledNode-&gt;getPosition().x,<br />i-&gt;worldFragment-&gt;singleIntersection.y,<br />m_controlledNode-&gt;getPosition().z);<br />}<br />}<br />void Entity::init()<br />{<br /><font color="#008000">// lots of other irrelevant entity init stuff goes here</font><br />m_raySceneQuery = sm-&gt;createRayQuery(<br />Ogre::Ray(m_controlledNode-&gt;getPosition(),<br />Ogre::Vector3::NEGATIVE_UNIT_Y));<br /><font color="#008000">// move this node is such a way that it is above the terrain</font><br />clampToTerrain();<br />}</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-11 17:24 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/11/19573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第五章 场景管理　地形</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19504.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 09 Mar 2007 09:25:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19504.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19504.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19504.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19504.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19504.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#0000ff" size="5">Ogre中的地形<br /></font>
				<br />
				<font color="#0000ff">使用terrain.cfg</font>
				<br />WorldTexture=terrain_texture.jpg  <font color="#008000">//地形纹理<br /></font>DetailTexture=terrain_detail.jpg  <font color="#008000">//细节纹理<br /></font>DetailTile=3                     <font color="#008000"> //细节纹理在一个地形小块中的平铺数<br /></font>PageSource=Heightmap             <font color="#008000"> //高度图数据源<br /></font>Heightmap.image=terrain.png      <font color="#008000"> //高度图名称,符合2^n+1<br /></font>PageSize=513                     <font color="#008000"> //高度图大小<br /></font>TileSize=65                      <font color="#008000"> //地形小块大小<br /></font>MaxPixelError=3                  <font color="#008000"> //决定使用层次细节时充许误差<br /></font>PageWorldX=1500                   <font color="#008000">//地形在世界中的范围x方向<br /></font>PageWorldZ=1500                 <font color="#008000">  //z方向<br /></font>MaxHeight=100                   <font color="#008000">  //世界中地形最大映射高度<br /></font>MaxMipMapLevel=5                <font color="#008000">  //层次细节上限<br /></font>#VertexNormals=yes                <font color="#008000">//在缓冲中计算顶点法线，计算机光照或GPU程序用到时打开<br /></font>#VertexColors=yes　　　　　　　　 <font color="#008000">//在缓冲中设置顶点颜色，假如有GPU程序需要时打开</font><br />#UseTriStrips=yes                <font color="#008000"> //对于现在的硬件，建议关掉<br /></font>VertexProgramMorph=yes　　　     <font color="#008000"> //使用顶点程序进行LOD融合处理<br /></font>LODMorphStart=0.2                <font color="#008000"> //LOD融合开始点：高，低LOD之间距离之比</font></p>
		<p>下列参数用于提供自己的着色程序时使用，这会提供自己定义的material，那么先前定义的<br />WorldTexture 与 DetailTexture的设置不再用到，多余的了。</p>
		<p>MorphLODFactorParamName=morphFactor　<br /><font color="#008000">//假设VertexProgramMorph被设为yes,定制的material中包括一个高级顶点程序。它指定了一个顶点<br />//程序的参数名，这个参数用于融合LOD,参数值从0－1，0表示不调整，1表示完全调整到下一级LOD</font><br />MorphLODFactorParamIndex        <font color="#008000"> //用于materail中包含低级顶点程序的情况，意义同上<br /></font>CustomMaterialName              <font color="#008000"> //指定的materail名字</font></p>
		<p>上述配置文件定义了基于高度图的地形。这些参数定义可概括为两类：Ogre使用第一类从高度图产生地形<br />mesh与材质。第二类是定制材质与GPU顶点程序，这可以代替ogre自动产生的着色程序。<br /><font color="#ff1493">另外的说明：</font><br />TerrainScenceManager会把高度图分为向个pages,每个page由几个tiles组成.而它们也不过是个方便的名字，<br />它们都定义了在产生的mesh中一组构成正方形的顶点集。<br />WorldTexture定义的纹理不必与目标地形一样大。<br />PageWorldX，PageWorldZ可以缩放世界中的地形。<br />MaxHeight 在Y方向缩放地形。<br />DetailTexture 只使用一个纹理，如使用多层纹理，应该使用自定义materail。<br /><br /><font color="#0000ff">从程序加载地形</font><br />setWorldGeometry()有重载形式，一种用于加载配置文件，另一种我们可在程序中使用，以<br />达到手工加载的功能。这里，SceneData被 typedef 为std:map,它存储了如我们在terrain.cfg<br />中看到那些值对。假设我们已经从某个二进制文件读入我们想要的内容到SceneData中。我们要做<br />的就是把读入的内容转换成setWorldGeometry()需要的类型。先看一下函数原型：<br />Ogre::SceneManager::setWorldGeometry (  DataStreamPtr &amp;  stream,  <br />  const String &amp;  typeName = StringUtil::BLANK <br /> )  <br />DataStreamPtr是一个智能指针,因此从局部变量中返回是安全的,程序相当直观，不再多说了。</p>
		<p>Ogre::DataStreamPtr Process_Loader::getSceneDataStream(SceneData &amp;data) {<br /><font color="#008000">// create what looks like a config file for the edification of Ogre</font><br />std::string mem;<br />SceneData::iterator it;<br />for (it=data.begin(); it!=data.end(); it++) {<br />mem += it-&gt;first;<br />mem += "=";<br />mem += it-&gt;second;<br />mem += "\n";<br />}<br />void *pMem = (void *)new unsigned char[mem.length()+1];<br />memset(pMem, 0, mem.length()+1);<br />memcpy(pMem, mem.c_str(), mem.length() + 1);<br /><font color="#008000">// stuff this into a MemoryDataStream<br /></font>Ogre::DataStreamPtr pStr(new Ogre::MemoryDataStream(pMem, mem.length() + 1));<br />return pStr;<br />}<br /><font color="#008000">// and then elsewhere in the world loader:</font><br />Ogre::DataStreamPtr pStr = getSceneDataStream(terrainDef);<br />m_sceneMgr-&gt;setWorldGeometry(pStr);<br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-09 17:25 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/09/19504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第五章 场景管理 第二部分 </title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19496.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 09 Mar 2007 06:25:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19496.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19496.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/09/19496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19496.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19496.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#0000ff">场景对象创建<br /></font>场景中的所有对象，包括可移动与不可移动的：lights, cameras, entities,particle system,<br />billboards, skyboxes, static geometry , world geometry.都由场景管理器来创建。场景中的<br />任何东西都由场景管理器来管理。任何通过场景管理器得到的东西，都必须由场景管理器来销毁。<br />用户不能delete通过由场景管理器得到的指针。<br />场景结点只有一个父结点，可能有多个子结点。可以随意的attach 和 detach 这些场景中的结点。<br />在明确告诉场景管理器销毁这些结点前，它们总是存在。如果不想渲染场景中的某些结点上的内容，<br />我们可以很方便地对她们进行detach.场景中总是存在一个Root结点。可以把多个内容对象attach到<br />同一个结点上。不能同时把一个实例对象挂到两个场景结点上，一个场景结点也不能有两个父结点。<br />总是场景结点在执行空间操作（平移，旋转，缩放）而不是实体对象。<br /><font color="#0000ff">场景查询</font><br />场景管理器的第二个最常用的功能是进行场景查询。包括射线查询，球查询，绑定盒查询，绑定平面<br />查询，相交查询。Terrain clamping：在崎岖不平的路上，勇敢地向下发射一束光，不管你告诉我你有<br />多高，我永远把你踩在脚下..（是不是不太像笔记？^_^)。所有这些查询都是 maskble的，这表明可<br />以在查询时过滤掉不关心的对象类型。如球查询时，只想看看它包含了多少lights,其他的对象即使包含<br />在球里也不必返回，实际上根本不用计算。<br /><font color="#0000ff">空间关系与3D变换<br /></font>world , parent , local世界空间中的变换是相对于全局坐标系的源点(0,0,0),这也是root scene node的位置。因此可以认为世界空间中的变换是相对于Root scene node的。父空间中的变换是相对于场点结点的父结点的，本地空间的变换是相对于物体所挂接的结点的。大多数情况下，我们会在父空间中做平移，在本地空间中做旋转，ogre中的这些操作，都是在上述的这种方式下。<br />object space<br />从模型导出的顶点数据与它的源点之间的关系是不变的，当它被挂到场景结点上，这个结点就是认为是<br />它的源点。建模时与导出时不假设测量单位，以世界单位导出（也就是无单位）。<br />ogre平移与设置位置不同：平移可以有多个参照点（world,local,parent space),而用setPosition()<br />时，总是相对于parent-space　坐标系的。<br /><font color="#0000ff">可移动的场景对象</font><br />基于资源的对象：最普通的是mesh(陪伴着skeleton)，这种类型的对象被资源管理系统管理。<br />场景管理器不负责实际的装载，它调用Ogreuq资源管理器来完成。<br />基于四边形的对象：粒子系统，公告板，ribbon trail,overlay,天空盒。它们通常是面向相机的，<br />使用动态材质。它们的主要资源是脚本，这些脚本定义了如何映射材质，以及它们的生命期（对于粒子系统与ribbon trail来说)。天空盒直接用场景管理器来定义。<br /><font color="#0000ff">skyplane,skydome,skybox</font><br />主要的相似点是它们与相机保持一个常量的距离。它们可以在场景中其它对象<br />之前或是之后渲染。它们使用普通的ogre material,因此纹理动画与其他纹理没有什么不同。它们可以<br />被场景管理器打开或关闭，与相机的距离也可以设置。<br />skyplane 是一个平面。用距离和法线定义它与相机的位置关系。可以弯曲，可以分多个段，可对纹理进行多次平铺。skydome由五个平面组成，底部空。使用改变纹理坐标的方式来达到外观上的曲率变化。有一个值用来调节，值越低，曲率越柔和，值越高，越陡峭。skybox 像skydome,但他不能“弯曲”材质坐标。它可以使用立方材质。可使用硬件加速功能达到很好渲染效率。<br /><font color="#0000ff">光</font><br />使用光的限制：单个通道通常最大支持8个灯。使用更多的灯，通过多通道。<br />光与物体之间的距离，决定光对物体实际的影响。Ogre支持点光源，平等光，聚光灯。<br /><font color="#0000ff">世界几何</font><br />当创建基于mesh的景物或关卡，应该分成较小的部分，以便于裁减。Paging Scene Manager提供了这样的工具。<br /><font color="#0000ff">空间分割方案<br /></font>Ogre是基于硬件加速渲染引擎，因此以最大化几何体batching的方式会比基于实际多边形进行空间分割<br />效果要好的多<font color="#ff1493">。Modern GPUs prefer to render a few large batches　of geometry instead of <br />many small batches.<br /></font><font color="#ff1493">The more visible geometry you can render in a single batch, the better your application’s<br />performance is likely to be (within reason, of course; other classic issues such as fillrate and<br />overdraw can still take over if you just blindly blast polygons at the GPU, batched or not).</font><br /><font color="#0000ff">静态几何使用注意</font>：<br />静态几何在使用之间必须被建立。<br />使用相同材质的几何体将被放到同一渲染操作中(batch):<font color="#ff1493">different materials still require a separate batch.<br /></font>努力的方向是，最大化一个调用的三角形数，最小化调用的次数。<br />不能移动被包含在static geometry中的对象。只有一个世界转换应用到完整的static geometry对象上.<br />比同样规模的movable geometry占用更多的内存。<br />假如组中的任何东西在视锥之内，那么组中的所有对象都将被渲染。</p>
		<p> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-09 14:25 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/09/19496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第五章 场景管理 第一部分</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/08/19427.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Thu, 08 Mar 2007 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/08/19427.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19427.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/08/19427.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19427.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19427.html</trackback:ping><description><![CDATA[
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" color="#0000ff">
						<font size="5">Ogre 场景管理</font>
						<br />
				</font>
				<br />每个3D引擎都会用scene graph 来组织它的可渲染对象。scene graph　总是会为了更快地搜索与查询做<br />优化，提供给用户查找目标对象附近特定对象的功能，允许查找，排序，剔除多边形，以实现更高效的渲染。偶尔，scene graph也用于碰撞检测。有时，一个单独的scene graph可被用于程序中的所有子系统，包括<br />音效与物理。<br />Ogre使用插件机制来实现场景管理功能。ScenceManager只是接口，他可以有很多具体的实现。Ogre允许<br />在同一时刻同一场景中使用多个Scene Manager,这样在不同的场景类型切换时带来好处。<br /><br /><font color="#ff1493">场景管理器的责任<br /></font>1，创建，放置场景中的可移动对象，light,camera，并可以在图形遍历中有效地访问它们。<br />2，加载，装配world geometry(它通常很大，向四处延伸，不可移动）<br />3，完成场景查询，例如可以回答这样的问题：在世界空间的特定点画一个球体，它会包含哪些对象？<br />4，剔除不可见对象，把可见对象放入渲染队列进行渲染<br />5，从当前可渲染的透视图中组织，拣选各方向光照<br />6，设置，渲染场景中的所有阴影<br />7　设置，渲染场景中的其他对象（如背景，天空盒）<br />8　传递组织良好的内容到渲染系统进行渲染</p>
		<p>
				<font color="#0000ff">场景管理器类型<br /></font>
				<br />以分析源码的方式讨论一下插件的加载机制与特定场景管理器是如何进行运用的。<br />上一章提到了以手工的方式初始化ogre,包括手工加载场景管理器：<br />root-&gt;loadPlugin("Plugin_OctreeSceneManager");<br />其实所谓的自动方式下，Root:Root()中也会间接调用到loadPlugin()方法，这已在前面的笔记<br />(配置文件Plugins.cfg )中提到过。既然特定管理器以插件的形式（dll文件）给出，下面先看如何<br />加载dll. 进入源码：标号表明执行顺序。<br />void Root::loadPlugin(const String&amp; pluginName)<br /> {<br /><font color="#9acd32"><font color="#006400"> </font><font color="#008000">// Load plugin library</font><br /></font>　　　 DynLib* lib = DynLibManager::getSingleton().load( pluginName ); <font color="#ff0000">//(1)<br /></font><font color="#008000">  // Store for later unload<br /></font> mPluginLibs.push_back(lib); <font color="#ff0000">//(4)<br /></font> <font color="#008000">// Call startup function<br /></font>　　　　DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib-&gt;getSymbol("dllStartPlugin"); <font color="#ff0000">//(5)<br /></font><font color="#008000"> // This must call installPlugin<br /></font> pFunc(); <font color="#ff0000">//(6)</font></p>
		<p> }<br />DynLib* DynLibManager::load( const String&amp; filename)  <font color="#ff0000">//(2)<br /></font>{<br />        DynLib* pLib = new DynLib(filename);<br /> pLib-&gt;load();        <br />        mLibList[filename] = pLib;<br /> return pLib;<br />}<br />void DynLib::load() <font color="#ff0000">//(3)<br /></font>{<br />m_hInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() );<br />}<br />第<font color="#ff0000">(3)</font>中的宏定义如下：<br />#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32<br />#    define DYNLIB_HANDLE hInstance<br />#    define DYNLIB_LOAD( a ) LoadLibrary( a )<br />到此，DLL被加载到内存,第<font color="#ff0000">(4)</font>步，mPluginLibs是个STL容器，它存放动态库指针。<br />第<font color="#ff0000">(5)</font>步，进入源码可以看到<br />void* DynLib::getSymbol( const String&amp; strName ) const throw()<br />    {<br />        return (void*)DYNLIB_GETSYM( m_hInst, strName.c_str() );<br />    }<br />其中宏定义：define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b )，很显然，它取得一个名为<br />dllStartPlugin的函数指针：不防再看看宏定义： typedef void (*DLL_START_PLUGIN)(void);<br />说明DLL_START_PLUGIN为参数为空，返回值为空的函数指针。<br />每个注册到ogre的dll都实现了这个约定函数。对于我们当前讨论的场景管理器Plugin_OctreeSceneManager<br />来讲，我们可以找到其相应的定义:它在第<font color="#ff0000">(6)</font>步中执行</p>
		<p>OctreePlugin* octreePlugin;<br />extern "C" void _OgreOctreePluginExport dllStartPlugin( void )<br />{<br /> <font color="#008000">   // Create new scene manager<br /></font>    octreePlugin = new OctreePlugin();　<font color="#ff0000">//(6-1)</font></p>
		<p> <font color="#008000">   // Register<br /></font>    Root::getSingleton().installPlugin(octreePlugin); <font color="#ff0000">//(6-2)</font></p>
		<p>}<br />程序执行到<font color="#ff0000">(6-1)</font>步，new 来一个OctreePlugin,我们看一下它的定义：<br />class OctreePlugin : public Plugin<br />{<br /> public:<br />  OctreePlugin();<br />  const String&amp; getName() const;<br />  void install();<br />   void initialise();<br />   void shutdown();<br />   void uninstall();<br /> protected:<br />  OctreeSceneManagerFactory* mOctreeSMFactory;<br />  TerrainSceneManagerFactory* mTerrainSMFactory;<br />  TerrainPageSourceListenerManager* mTerrainPSListenerManager;</p>
		<p>};</p>
		<p>我们会注意到它包含两个工厂类指针:OctreeSceneManagerFactory,TerrainSceneManagerFactory<br />他们就是用来生产特定ScenManager的，稍后讨论。先看<font color="#ff0000">(6-2)</font>步：<br />void Root::installPlugin(Plugin* plugin)<br />{<br /> mPlugins.push_back(plugin);　　<font color="#ff0000">//(6-2-1)<br /></font> plugin-&gt;install();             <font color="#ff0000">//(6-2-2)<br /></font> // if rendersystem is already initialised, call rendersystem init too<br /> if (mIsInitialised)<br /> {<br /> plugin-&gt;initialise();         <font color="#ff0000">//(6-2-3)<br /></font> }<br />}<br />//<font color="#ff0000">(6-2-1)</font>步把插件放到容器中。看看<font color="#ff0000">(6-2-2)</font>做了什么：<br />void OctreePlugin::install()<br /> {<br />  // Create objects<br />  mOctreeSMFactory = new OctreeSceneManagerFactory();<br />  mTerrainSMFactory = new TerrainSceneManagerFactory();<br />  mTerrainPSListenerManager = new TerrainPageSourceListenerManager();</p>
		<p> }<br />呵，刚才还说两个工厂类指针，现在把两个工厂建起来，以后可以用来生产东西了。<br />继续看看<font color="#ff0000">(6-2-3):<br /></font>void OctreePlugin::initialise()<br /> {<br />  // Register<br />  Root::getSingleton().addSceneManagerFactory(mOctreeSMFactory);<br />  Root::getSingleton().addSceneManagerFactory(mTerrainSMFactory);<br /> }<br />哦，把这两个工厂注册到Root中，让Root可以使用它们。啊这句话有点问题，Root只是间接的用到，<br />直接雇主是 SceneManagerEnumerator* mSceneManagerEnum;它被包含在Root中。于是我们可以看到<br />void Root::addSceneManagerFactory(SceneManagerFactory* fact) <font color="#ff0000">//(6-2-3-1)</font><br /> {<br />  mSceneManagerEnum-&gt;addFactory(fact);<br /> }</p>
		<p>工厂已经有了，我们如何利用这个工厂生产出我们想到的东西(SceneManager)呢？<br />回忆上一章的手工初始化过程中，我们一般用以下语句来创建SceneManager:<br />  SceneManager *sceneMgr = root-&gt;createSceneManager(ST_GENERIC); <font color="#ff0000">//(A)<br /></font>也可以这样用<br />  sceneMgr = ogre-&gt;createSceneManager("OctreeSceneManager"); <font color="#ff0000">//(B)<br /></font>每个工厂类都用一个字符串表示其类型。上面说的两个工厂分别使用的字符串为：“TerrainSceneManager”，"OctreeSceneManager"<br />(A)语句肯定会调用工厂类的方法来产生实际的SceneManager,下面看源码验证一下：<br />SceneManager* Root::createSceneManager(const String&amp; typeName, <br />  const String&amp; instanceName)<br /> {<br />  return mSceneManagerEnum-&gt;createSceneManager(typeName, instanceName);<br /> }<br />继续挖：<br />SceneManager* SceneManagerEnumerator::createSceneManager(<br />  const String&amp; typeName, const String&amp; instanceName)<br /> {<br />  SceneManager* inst = 0;<br />  for(Factories::iterator i = mFactories.begin(); i != mFactories.end(); ++i)<br />  {<br />   if ((*i)-&gt;getMetaData().typeName == typeName)<br />   {<br />    if (instanceName.empty())<br />    {<br />     // generate a name<br />     StringUtil::StrStreamType s;<br />     s &lt;&lt; "SceneManagerInstance" &lt;&lt; ++mInstanceCreateCount;<br />     inst = (*i)-&gt;createInstance(s.str());<br />    }<br />    else<br />    {<br />     inst = (*i)-&gt;createInstance(instanceName);<br />    }<br />    break;<br />   }<br />  }<br /> }<br />上述代码很简单：因为执行<font color="#ff0000">(6-2-3-1)</font>已经过实际工厂类实例进行了注册。于是遍历每个工厂实例，<br />找到类型相符的。找到之后，如果没有传场景管理器实例的名字，就起个名字。然后用这个名字<br />生产出一个实例来。CreateInstance没干什么，new　呗。<br />SceneManager* OctreeSceneManagerFactory::createInstance(<br /> const String&amp; instanceName)<br />{<br /> return new OctreeSceneManager(instanceName);<br />}<br />就是这样。OctreeSceneManager and TerrainSceneManager 的功能都是由一个DLL提供的。它们的关系是：class _OgreOctreePluginExport TerrainSceneManager : public OctreeSceneManager，按照一般的类关系逻辑，<br />我们知道派生类一般功能都比父类强大，父类应用于一般场景，子类针对特定场景。这个逻辑在这里是对的。本来是写读书笔记，跑题了，打住。</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19427.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-08 15:08 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/08/19427.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第四章 开始使用OGRE</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/07/19361.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Wed, 07 Mar 2007 06:14:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/07/19361.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19361.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/07/19361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19361.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19361.html</trackback:ping><description><![CDATA[
		<p>清源游民　<a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a><br /><font color="#0000ff">日志系统</font>：<br />日志记录了基于ogre的程序每次运行时的所有事件，系统初始化，状态，性能信息。输出的内容被放在磁盘文件上，文件缺省名是ogre.log。也可以手动显示创建日志系统,这需要在创建Root对象之前实施。<br /><font color="#008000">// create an instance of LogManager prior to using LogManager::getSingleton()<br /></font>LogManager* logMgr = new LogManager;<br />Log *log = LogManager::getSingleton().createLog("mylog.log", true, true, false);<br /><font color="#008000">// third param is not used since we already created a log in the previous step<br /></font>Root *root = new Root("", "");<br />可以用Ogre LogManager注册一个Log Listener, 以任何方式重定向log data。可以用这种方式来屏蔽任何日志信息。然后还一个更简单的方法达到上述目的：在实例化Root之前，当实例化一个LogManager后，不调用createLog()方法。<br />以下是实现日志信息截流的代码片断：<br />class MyLogListener : public LogListener<br />{<br />public:<br />void write (const String&amp; name, const String&amp; message,<br />LogMessageLevel level, bool maskDebug)<br />{<br /><font color="#008000">// redirect log output here as needed<br /></font>};<br />MyLogListener *myListener = new MyLogListener;<br /><font color="#008000">// this is the same as calling LogManager::getSingletonPtr() after the<br />// LogManager has first been instanced; the same pointer value is returned<br /></font>LogManager *logMgr = new LogManager;<br />LogMgr-&gt;addListener(myListener);<br />logMgr-&gt;createLog("mylog.log", true, false, true);<br />logMgr-&gt;setLogDetail(LL_NORMAL);<br />Root *root = new Root("", "", "mylog.log");<br /><font color="#0000ff">Ogre手动初始化</font><br />int main(int argc, char *argv[]) <br />{</p>
		<p>
				<font color="#008000"> // tell Root not to load from any plugins or settings file<br /></font> Root *root = new Root("", "");</p>
		<p>
				<font color="#008000"> // Load feature plugins. Scene managers will register <br /> // themselves for all scene types they support<br /></font> root-&gt;loadPlugin("Plugin_CgProgramManager");<br /> root-&gt;loadPlugin("Plugin_OctreeSceneManager");</p>
		<p>
				<font color="#008000"> // load rendersystem plugin(s). The order is important in that GL<br /> // should be available on on platforms, while D3D9 would be available <br /> // only on Windows -- the try/catch will intercept the exception in this<br /> // case where D3D9 is not available and continue gracefully</font>.<br /> try {<br />  root-&gt;loadPlugin("RenderSystem_GL");<br />  root-&gt;loadPlugin("RenderSystem_Direct3D9");<br /> }<br /> catch (...) {}</p>
		<p> try {<br /><font color="#008000">  // We'll simulate the selection of a rendersystem on an arbirtary basis; normally<br />  // you would have your own code to present the user with options and select the<br />  // rendersystem on that basis. Since a GUI is beyond the scope of this example, we'll<br />  // just assume the user selected OpenGL.<br /></font>  RenderSystemList *rList = root-&gt;getAvailableRenderers();<br />  RenderSystemList::iterator it = rList-&gt;begin();<br />  RenderSystem *rSys = 0;</p>
		<p>  while (it != rList-&gt;end()) {<br />   <br />   rSys = *(it++);<br />   if (rSys-&gt;getName().find("OpenGL")) {<br />   <br />    root-&gt;setRenderSystem(rSys);<br />    break;<br />   }<br />  }</p>
		<p>
				<font color="#008000">  // check to see if a render system was selected; if we reached the end of the list<br />  // without selecting a render system then none was found</font>.<br />  if (rSys == 0) {<br />   delete root;<br />   std::cerr &lt;&lt; "No RenderSystem available, exiting..." &lt;&lt; std::endl;<br />   return -1;<br />  }</p>
		<p>  <font color="#008000">// We can initialize Root here if we want. "false" tells Root NOT to create<br />  // a render window for us<br /></font>  root-&gt;initialise(false);</p>
		<p>
				<font color="#008000">  // set up the render window with all default params<br /></font>  RenderWindow *window = rSys-&gt;createRenderWindow(<br />   "Manual Ogre Window", // window title<br />   800,     // window width, in pixels<br />   600,     // window height, in pixels<br />   false,     // fullscreen or not <br />   0);      // use defaults for all other values</p>
		<p>  <font color="#008000">// from here you can set up your camera and viewports as normal<br />  // get a pointer to the default base scene manager -- sufficient for our purposes</font><br />  SceneManager *sceneMgr = root-&gt;createSceneManager(ST_GENERIC);</p>
		<p>
				<font color="#008000">  // create a single camera, and a viewport that takes up the whole window (default behavior)<br /></font>  Camera *camera = sceneMgr-&gt;createCamera("MainCam");<br />  Viewport *vp = window-&gt;addViewport(camera);<br />  vp-&gt;setDimensions(0.0f, 0.0f, 1.0f, 1.0f);<br />  camera-&gt;setAspectRatio((float)vp-&gt;getActualWidth() / (float) vp-&gt;getActualHeight());<br />  camera-&gt;setFarClipDistance(1000.0f);<br />  camera-&gt;setNearClipDistance(5.0f);</p>
		<p>
				<font color="#008000">  // Run the manual render loop. Since we are not using a frame listener in this case, we<br />  // will count to 15 seconds and then instead of exiting, we'll change the render window settings <br />  // and re-initialize it.<br /></font>  bool renderLoop = true;<br />  Timer *timer = Ogre::PlatformManager::getSingleton().createTimer();<br />  timer-&gt;reset();<br />  float s = 0.0f;</p>
		<p>  while (renderLoop &amp;&amp; window-&gt;isActive()) {</p>
		<p>   renderLoop = root-&gt;renderOneFrame();</p>
		<p>   // accumulate total elapsed time<br />   s += (float)timer-&gt;getMilliseconds() / 1000.0f;</p>
		<p>   // if greater than 15 seconds, break out of the loop<br />   if (s &gt;= 15.0f)<br />    renderLoop = false;</p>
		<p>
				<font color="#008000">   // we must call the windowing system's message pump each frame to <br />   // allow Ogre to process messages <br /></font>   //PlatformManager::getSingleton().messagePump();<br />  }<br /> }<br /> catch (Exception &amp;e) {<br />  std::cerr &lt;&lt; e.getFullDescription() &lt;&lt; std::endl;<br /> }</p>
		<p> delete root;<br /> return 0;<br />}<br /></p>
		<p>
				<font color="#0000ff">视口<br /></font>通过视口上的一点与相机的原点产生世界空间中的一条光线<br />// x and y are in "normalized" (0.0 to 1.0) screen coordinates<br />Ray getCameraToViewportRay(Real x, Real y) const;</p>
		<p>视口,创建多个视口，通过Z序（越高越在上）　确定覆盖效果，每个视口可以有不同的背景。<br /><font color="#008000">// assume window is a valid pointer to an existing render window, and<br />// a valid pointer to an existing camera instance<br /></font>Viewport *vpTop, *vpBottom;<br /><font color="#008000">// second parameter is z-order, remaining params are position and size,<br /></font>vpBottom = window-&gt;addViewport(camera, 0);<br /><font color="#008000">// create a smaller viewport on top, in the center, 25% of main vp size<br /></font>vpTop = window-&gt;addViewport(camera, 1,<br />0.375f, 0.375f,<br />0.25, 0.25);<br /><font color="#008000">// set the background of the top window to blue (the default is black<br />// need to set the bottom window explicitly)<br /></font>vpTop-&gt;setBackgroundColour(ColourValue(0.0f, 0.0f, 1.0f));<br /><font color="#008000">// an alternate way to set the color is to use the manifest constant<br />// vpTop-&gt;setBackgroundColour(ColourValue::Blue);</font><br />在多视口情况下，overlay缺省在每个视口中渲染。可以关掉。Skybox, Shadow也是如此。<br />vpTop-&gt;setOverlaysEnabled(false);<br />vpTop-&gt;setSkiesEnabled(false);<br />vpTop-&gt;setShadowsEnabled(true);<br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19361.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-07 14:14 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/07/19361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第三章 设计概要 第二部分</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/06/19280.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 06 Mar 2007 03:18:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/06/19280.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19280.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/06/19280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19280.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19280.html</trackback:ping><description><![CDATA[
		<p>清源游民　 <a href="mailto:gameogre@gmail.com">mailto:gameogre@gmail.com</a></p>
		<p>
				<font color="#0000ff">子系统概览</font>
				<br />
				<font color="#000000">Root Object <br />Root 是程序进入点，它是一个façade 类，提供了访问子系统的方便的方法。通过它可以开启ogre,也可能通过它关闭ogre。</font>
				<br />
				<font color="#ff1493">资源管理</font>
				<br />在渲染场景中使用的任何东西都被视为资源。所有的资源最终都被一个单个类对象管理：ResourceGroupManager，它负责定位资源，初始化资源（不真正装载）。 <br />在缺省情况下，Ogre认识以下类型的资源： <br />Mesh: 二进制格式，它也可包含 morph 和　pose 动画数据 <br />Skeleton: 　可以被Mesh文件引用，也可单独使用，包含骨骼层次结构信息，关键帧信息。 <br />Material: 　定义了渲染一组几何体时的渲染状态，可以被mesh文件引用，也可以手工使用。 <br />GPU 程序：.program 支持HLSL, GLSL,Cg 与低级的 .asm ，这些文件会在任何.material之间被剖析，因此在一个material之中被引用的Gpu程序总是有效的。 <br />Compositor: 与Material很相似，扩展名不同. <br />Font: 用字体定义文件去定义在overlay中使用的字体，扩展名 .fontdef <br />每种资源都有自己的特定的ResourceManager,如，MaterialManager, FontManager。ResourceGroupManager负责通过名字查找资源，它不负责实际的内存管理任务。这些任务是由ResourceManager基类来完成的。ResourceGroupManager允许通过组名来加载，释放整组资源。 <br />默认情况下，Ogre认为资源以一个磁盘文件的形式存在。然而，有些类型的资源可以手工管理，当前只有mesh, font 有手工资源加载的实现。其他类型的可以自己实现，Ogre已经在它的框架中保留了这种功能。 <br /><font color="#ff1493">场景管理</font><br />所有的具体实现都从SceneManager派生而来，程序中常与此类交互。，可以有多个活动的SceneManager。它们用来管理SceneNode, SceneNode可以在场景中被移动。SceneNode也可以有层次结构。 <br />场景实际内容经常也Entity实例的形式存在。它们被Scene Manager创建，MovableObject实现。一个有效的Entity可以被attach 到SceneNode上面。Entity 经常从磁盘上以 mesh文件加载。然后也可以手工创建内容对象，如plane.当Entity被attach到SceneNode上时，进行移动等操作是针对SceneNode的，而不是内容对象。 <br />也可以把非内容对象(灯，相机等)attach 到SceneNode上面。 <br />渲染系统与渲染对象 <br />RenderSystem 是Ogre与底层API的接口, RenderTarget 是　渲染窗口与渲染纹理的概括。 <br />Ogre 扶持多个渲染窗口。窗口可以通过Root对象自动、手动地创建，也可能通过RenderSystem创建。 </p>
		<p>
				<font color="#0000ff">Ogre 管理器</font>
				<br />Manager 是一个可以访问相关类型对象，管理其生命周期的类。例如，ArchiveManager管理Archive实现的创建与注册，访问注册的Archive实现实例。Root对象创建的副作用之一就是初始化所有ogre Manager对象。 <br />LogManager : 发送日志信息到输出流。 <br />ControllerManager: 管理 controllers,后者是基于各种输入，为别的类产生状态值以供使用。 <br />DynLibManager: 　管理动态链接库 <br />PlatformManager: 把抽象的访问转换成底层硬件与操作系统相关的细节。 <br />CompositorManager: 访问管理　Compoitor framework. <br />ArchiveManager: 　文件系统目录，ZIP文件 <br />ParticleSystemManager: 粒子系统，发射器，影响器（affector） <br />SkeletonManager:  允许同名的skeleton对象重用 <br />MeshManager :  管理mesh, 允许同名mesh重用 <br />HighLevelGpuProgramManager: 维护，加载，编译高级GPU程序 <br />GpuProgramManager: 维护低级GPU程序，把编译的高级GPU程序转为汇编 <br />ExternalTextureSourceManager:  管理外部纹理源类实例，如视频流 <br />FontManager:  管理Overlay中使用的字体 <br />ResourceGroupManager: 加载，生命期管理，所有注册的程序资源 <br />OverlayManager: 加载，创建，2D Overlay 类实例 <br />HardwareBufferManager: 管理共享硬件缓冲，　顶点缓冲，像素缓冲，索引缓冲等。 <br />TextureManager: 管理纹理 <br />总结： 这一章无意覆盖关于Ogre的所有东西，只说明了一些普遍需要的Ogre对象，和一些不太常打交道的ogre 对象。 </p>
		<p> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-06 11:18 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/06/19280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Pro Ogre 3D Programming》 读书笔记 之 第三章 设计概要　　第一部分</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19264.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Mon, 05 Mar 2007 15:22:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19264.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19264.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19264.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19264.html</trackback:ping><description><![CDATA[
		<p>清源游民　 <a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a></p>
		<p>
				<font color="#0000ff">设计哲学</font>
				<br />传统上，使用 Direct3D 或 OpenGL 来渲染场景和对象，需要遵循一系列程序处理流的步骤：调用 API 设置渲染状态，调用 API 发送几何体信息，通知 API 或 GPU 去渲染几何体。对每个几何体都是如此返复，直到当前帧被完全渲染。在一下帧同样如此。 <br />使用面向对象的方法来渲染几何体简化了上述过程。通过处理组成场景的各种对象而不是原始的几何体。这些对象包括：可运动对象，静态对象（构成世界布局），光，相机等。那些 3D API 则不再需要：只是把这些对象放到场景中， Ogre 负责处理琐碎的细节。而且，可以用更加直观的方法来操纵对象，而不是使用矩阵。总的说来，我们可以处理对象，它的属性，调用更直观的方法，而不再用顶点列表，三角形列表，旋转矩阵等手段来管理了。 <br />Ogre 提供了面向对象的框架，包括了对象模型中的渲染处理的所有部分。渲染系统抽象了底层 3D 　 API 的复杂性。场景图形功能被抽象成单独的接口，这样一来，各种不同的场景图形管理算法可以即插即用。在场景中的所有可渲染对象，无论是可移动的，还是静态的，都被一组公共接口抽象，这些接口提供了实际的渲染操作（例如 techniques 和它所包含的 passes ） . <br />设计亮点 <br /><font color="#ff1493">一“设计模式”的合理使用 <br /></font>Ogre 中大量使用了经典的设计模式。例如，使用“ Observer ”模式来通知应用程序特定事件的发生，在 demo 中 FrameListener 的使用使得程序可以接收　 frame-started and frame-ended 　事件通知。“ Singleton ”模式强制实现类的单实例。“ Iterator ”模式用来遍历数据结构的内容。 <br />“Visitor” 模式用来在对象上执行某种操作。 Façade 模式用来抽象底层，提供一个单一类接口。 <br />“ Factory” 　用来创建实例。 <br /><font color="#ff1493">二 场景图与内容分离 <br /></font>传统上，场景图与内容同处于同一继承体系下。它要求从场景结点子类化内容类，也就是说内容类从场景结点继承。实践证明这种设计非常糟糕。 首先， Ogre 操作场景图形在接口层 , 它不假设特定图形算法的实现。实际上， Ogre 操作场景图形仅仅通过它的签名（方法），完全忽略底层的算法实现。 <br />第二， Ogre 的图形接口只关心图形结构，不包含任何内在的访问或管理功能。后者被推到 Renderable 中，场景中的所有几何（ movable or otherwise ）都从它继承。这些 Renderalbes 的渲染属性（材质， materials ）被包含到 Entity 对象中去。 Entity 可以包含一个或多个 SubEntity 。这些子实体是实际的可渲染对象。下面是各对象的一个关系图：<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_ScenceGraph.jpg" /><br />通过这样的设计，场景管理与场景内容充分解藕。过于场景图来说，通过 Movable Object 几何，渲染属性变得有效。注意到 Movable 不是从 Scene Node 继承而来的。 Movable Object 是 attached 到 Scene Node 上去的 . 当扩展，改变，重构场景图形实现时对场景内容对象的设计与实现没有任何影响。 <br />从另一方面来说，由于场景图形不需要知道内容类的任何变化。只要实现一些简单的场景图形“确实“需要知道的一些接口。因此，我们可以任意的 attached 一些自定义的东西到场景结点上，比如声音信息等。（听说有个 OgreAL, 它包装了 OepnAL ，猜想就是这样做的吧）。 <br /><font color="#ff1493">三　插件结构</font><br />OGRe 被设计成可扩展的。 Ogre 通过“基于契约的设计 ” 来完成。 Ogre 可以被设计成一组共同工作的组件，它们通过一些已知接口来相互交流。这带来极大的灵活性，某种功能的不同实现与改变非常容易。举例来讲，由于 ogre 处理场景图元管理是在接口级，用户可以随意的选择特定算法的实现。而且，当用户创建一个新的实现，可以很容易的以插件的形式提供给 Ogre, 　实现时只要遵循 ogre 定义的一些接口 . File archives , render systems 也以插件的形式提供，粒子系统也是。 插件形式的吸引人之处是，为了加入插件，不需要重新编译 Ogre 库 . 插件可以在运行时加载。 <br /><font color="#ff1493">四　硬件加速的渲染支持</font><br />Ogre 被设计成只支持硬件加速图形渲染，直接软件渲染不是它的选项。 Ogre 使用完整的硬件加速能力，包括可编程 shaders 。 Unreal 引擎能做什么， Ogre 就能做什么！！（ ^_^, 作者说的）。引擎直接支持的全局光照 (precomputed Radiance Transfer 等 ) 还没有实现，因为这些多数用于非实时计算场合，因此不是什么问题 .Ogre 现在和未来将可能只使用 Direct3D 与 OpenGL 。 <br /><font color="#ff1493">五，灵活的渲染队列结构 <br /></font>Ogre 　采取一个新方法来解决场景中各部分渲染次序问题。粗略地看，标准过程通常如下工作：渲染地形或世界几何，渲染可移动对象，渲染各种特效，渲染 OverLay, 渲染背景或天空盒 . 然而，正如典型的实现一样（像集成电路处理模块），这个过程很难改变 . 在许多例子中， 很难改变渲染的顺序，导致难以维护与不灵活的设计的。 渲染队列的概念是： Ogre 以一次一个的方式渲染一组可以排序的队列，也已同样的方式渲染每个队列中的内容。也就是队列本身有优先级，同样队列中的对象也有自己的优先级。<br /><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_RenderQueue.jpg" /><br />上图中，队列由后向前渲染（ Background à OveryLay ） , 在最前的队列中从左至右渲染。 <br />在这种机制下重新组织渲染顺序非常简单，只要重新分配优先级就可以了。队列可以定制，队列中的对象也可以。整个队列可以“ turned on” and “turned off” ，队列中的对象也可以。 每个队列提供了事件通知（如 prerender and postrender ） , 应用程序有机会改变队列中的对象渲染。这个机制对于开发和维护复杂程序都相当有用。 <br /><font color="#ff1493">六　健壮的材质系统 <br /></font>Ogre 的材质由一个或多个 technique 组成， technique 是 pass 的集合。 Pass 是材质级别的渲染单元，导致一次图形硬件的 ”draw” 调用。可以有多个 pass, 每个 pass 是一次全新的渲染操作，包括硬件渲染状态改变。 <br />最吸引人的特性是 automatic fallback 。 Ogre 自上向下地（ technique 列出的顺序）寻找最适当的 technique.Ogre 还能尽力地重新组织 technique 中的 pass, 以迎合最小的技术需求。例如，当前硬件只支持单纹理单元，而你程序中最小复杂度的技术，需要至少两个纹理单元，那么 ogre 会把一个 pass 拆分成两个，通过混合两个单元纹理的方式达到同样的效果。 <br />Ogre 材质系统也支持 schemes 的概念。 Schemes 可以理解作为普遍的“非常高，高，中，低“不同等级的图形设置。在这种情况下，可以定义四个 schemes , 并为每一个分配 techniques( 复数 ) 。这样就可以 ogre 的所谓 technique fallback 查找那些属于特定 schemes 的 techniques 。使得材质管理更容易点。 <br />用材质脚本完成的事，同样也可以用代码的方式完成。代码的方式也支持 schemes 与 technique fallback 。 <br /><font color="#ff1493">七　本地优化的几何与骨骼格式</font><br />Ogre 使用它单独的 mesh 与骨骼格式。不能直接加载第三软件包生成的数据。社区中有转换第三文件格式的工具，但它不是 Ogre 库的一部分。 Ogre 使用自己的格式是为了效率。 <br /><font color="#ff1493">八　　多种类型的动画 <br /></font>Ogre 支持三种类型的动画：骨骼动画，变形动画， Pose 动画 <br />骨骼动画，把顶点绑定到骨骼上。对象的每个顶点可以受到四个独立的骨头影响。每种影响被赋于一个用数量表示的权重。骨骼动画只在关键帧上执行前向运动学模式。 Ogre 当前不支持逆向动画 . <br />Morph 动画是一种顶点动画技术，他储存关键帧上的绝对的顶点位置信息，在运行时在两个关键帧之间插值。这与 Pose 动画不同， Pose 动画存储的是偏移数据而不是绝对位置。 Pose 动画可以混合以形成复杂动画。 Morph 动画比 Pose 动画功能有限，因为他存储的是绝对值，因此难以与其他 Morph 动画混合。 Morph 动画与 Pose 动画之间也不能混合 . <br />所有的动画类型都可以与骨骼动画混合使用 . <br />所有动画类型都可使用软件执行或是利用 GPU 硬件执行。 <br />Ogre 动画是基于关键帧的，内插方式有线性与立方样条这两种选择。 <br /><font color="#ff1493">九　 Compositor Postprocessing</font> ( 怎么翻译 ~?) <br />用来在一个视口中创建二维的，全屏的后处理效果 . 例如模糊，黑白电视，等等效果（ Ogre 有个很好的示例） Compositor 也有类似于材质系统的 technique 与 pass 的概念。在视口最终被输出前，多个计算与渲染可被执行。像材质的 fallbacks,Compositor framework 提供了 fallback 处理，例如，当输出的像素格式无效时。理解 compositor 最容易的方法是把它当成片段程序（像素渲染）的扩展。不同之处在于：传统的图形管线每个材质 pass 只充许一个片段程序，而 Compositor framework 则可以像打乒乓球一样来来回回好多次。 Compositor 脚本扫行在 ViewPort 上，因此它可以面向任何的 render target, 可以是可渲染纹理，主，副渲染窗口。与材质系统一样，它除了利用脚本实现，也可以使用代码实现。 <br /><font color="#ff1493">十   扩展资源管理</font><br />在 ogre 中，资源定义为：渲染几何体到可渲染目标上用到任何东西。很明显包括： mesh, 　 skeleton, material , overlay script , font , Compositor script, GPU program , texture 。 <br />每种资源都有自己的 manager, 它主要负责控制特定类型资源在内存中的占用数量。换句话说，它控制着资源实例的生命期。不过仅仅在一点上控制：首先，它只能储存同样多的实例，取决于这种类型的资源所分配的内存。第二， ogre 不会删除正被其他部分引用的实例。 <br />资源本身实际负责加载自己。这是资源系统的一个设计特性：手工资源加载。手工加载意味着资源加载，处理，是利用一个方法调用实现的而不是从文件系统中隐式地加载。 <br />资源在 ogre 中有四种状态：未定义，　声明，未加载，　加载。 <br />未定义，说明 ogre 对它一无所知。 <br />声明，它已经在档案中进行了索引。 <br />未加载，已经进行了初始化（脚本，已经被解析过了），引用已经被创建 <br />加载，已经在它的资源 manager 所管理的内存池中占用空间了。 <br />可以用“组”来管理管理各类资源。组中资源之间的关系是任意的，完全取决于程序员：创建 GUI 的所有资源可以分为一组，以 A 字母开头的资源也可以分为一组，等等。有个 缺省的组：General <br />在 ogre 中查找某个资源实例时，不会考虑，资源所在的组。有时，我们可以将资源组命当成某种的 ” 命名空间来用“。 <br />Archives 中的资源是非手动加载的。 Archives 是普通文件容器的简单抽象。它包括文件系统与 ZIP 档案。用户可以实现自定义类型的 archive 。 <br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-05 23:22 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/05/19264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORGE(Eihort)学习笔记之GUI Demo 第三部分 CEGUI初步</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19227.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Mon, 05 Mar 2007 04:43:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19227.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19227.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/05/19227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19227.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19227.html</trackback:ping><description><![CDATA[
		<p>清源游民  <a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a></p>
		<p>在以前的笔记中已经对 CEGUI 的使用做了简单的介绍，这里为了完整性还是把它 C ＆ P 一下： <br />使用 cegui 来制作界面 , 不论在何种平台下 , 有基本的三大步骤要做 : <br />1, 创建一个 CEGUI::Render 实例 <br />2, 创建 CEGUI::System 对象 <br />3, 调用各种方法来渲染用户界面 <br />第一步 , 在我使用的 ogre 环境下使用以下代码来创建 CEGUI::Render 实例 <br />Ogre3D <br />CEGUI::OgreCEGUIRenderer* myRenderer = <br />       new CEGUI::OgreCEGUIRenderer(myRenderWindow); <br />第二步相当简单 , 可使用 new CEGUI::System(myRenderer); <br />第三步，基本上来讲，大部分平台下，如 direct3D, OpenGL, 我们在渲染循环的尾部调用 CEGUI::System::renderGUI 来开始界面的渲染。如果我们使用 ogre3d 引擎，这一步不需要 我们显示的执行。 创建 CEGUI 窗口，我们可以使用两种形式，一是 C ＋＋代码，二是编辑 XML layout 文件。 <br />CEGUI 本身侦测用户输入，这些不是 CEGUI 的责任，而是程序的员的责任。当有用户外部输入时，我 们可以选择将这些消息告知 CEGUI ，这样 CEGUI 才会响应。 <br />CEGUI 使用回调机制来进行消息处理。可以为某个窗体的特定事件注册一个函数，当窗体事件发生时， CEGUI 会自动调用所注册的函数。 <br />DEMO 中需要注意的是 Render To Texture 的实现。 <br />先创建在 ogre 中的 RTT: <br />RenderTexture * rttTex = mRoot-&gt;getRenderSystem()-&gt;createRenderTexture( "RttTex", 512, 512, TEX_ <br />TYPE_2D , PF_R8G8B8 ); <br />再转换成CEGUI可识别的: <br />// Retrieve CEGUI texture for the RTT <br />        CEGUI::Texture* rttTexture = mGUIRenderer-&gt;createTexture((CEGUI::utf8*)"RttTex"); <br />根据这个Texture生成了一个ImageSet; <br />在ImageSet中定义了一个Image（名为RttImage）;其大小与上边的Texutre同样大小。 <br />当在窗口中增加一个static Image时，把其image属性设为 ” RttImage ” 即可。 <br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-05 12:43 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/05/19227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ogre 学习笔记之　ＯＧＲＥ轨迹动画</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19132.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 02 Mar 2007 09:36:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19132.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19132.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19132.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19132.html</trackback:ping><description><![CDATA[轨迹动画(Animation Track)，是 OGRE 所支持的几种动画模式(包括骨骼动画)中的一种。通过让节点(node)沿着一条预设了关键帧(Key Frame)的轨迹移动来实现节点的动画。 
<p>　　OGRE 中创建和实施轨迹动画的步骤大致如下： </p><p>　　1. 在主程序类的 Application :: createScene ( ) 场景创建方法中进行以下工作： </p><p>　　Animation * Ogre::SceneManager::createAnimation (<br />　　 const String &amp; name, // 动画名称<br />　　 Real length // 动画长度(秒)<br />　　 ) [virtual] </p><p>　　(2) 使用 createTrack 方法为动画创建轨迹动画：<br />　　 AnimationTrack * Ogre::Animation::createTrack (<br />　　 unsigned short handle, // 分配给轨迹动画的索引句柄，用于以后可能的调用<br />　　 Node * node // 指定要沿着这条轨迹运动的节点<br />　　 )</p><p>　　(3) 使用 createKeyFrame 方法为轨迹动画创建一系列关键帧：<br />　　 KeyFrame * Ogre::AnimationTrack::createKeyFrame (<br />　　 Real timePos // 时间位置<br />　　 ) <br />　　 <br />　　 (4) 使用 setTranslate 、 setScale 、 setRotation 三个方法来设置关键帧每个时间位置上节点的位置、缩放、旋转属性：<br />　　 void Ogre::KeyFrame::setTranslate ( const Vector3 &amp; trans ) <br />　　 void Ogre::KeyFrame::setScale ( const Vector3 &amp; scale ) <br />　　 void Ogre::KeyFrame::setRotation ( const Quaternion &amp; rot ) <br />　　 <br />　　 (5) 使用 createAnimationState 方法创建一个动画状态来追踪这个轨迹：<br />　　 AnimationState * Ogre::SceneManager::createAnimationState ( const String &amp; animName ) [virtual] <br />　　 <br />　　 (6) 使用 setEnabled 方法来激活动画状态：<br />　　 void Ogre::AnimationState::setEnabled ( bool enabled ) </p><p>　　2. 在接收器类的 Listener::frameStarted(const FrameEvent&amp; evt) 方法中刷新动画状态：<br />　　 void Ogre::AnimationState::addTime ( Real offset ) </p><img src ="http://www.cppblog.com/yuanyajie/aggbug/19132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-02 17:36 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/02/19132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE学习笔记之 配置文件 Resources.cfg</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19131.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 02 Mar 2007 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19131.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19131.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19131.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19131.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b style="mso-bidi-font-weight: normal">
						<i style="mso-bidi-font-style: normal">
								<span lang="en" style="FONT-SIZE: 15pt; COLOR: blue">resources.cfg 
<p></p></span>
						</i>
				</b>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>资源是 OGRE 应用程序渲染过程中需要用到的纹理图片、网格模型文件、骨骼动画文件的总称。 OGRE 应用程序需要在渲染前将这些资源载入内存，那就需要让 OGRE 引擎知道资源的搜索路径。特别的是 OGRE 引擎支持直接读取 Zip 压缩文件中的内容，所以 Zip 文件也必须被当成搜索路径来指定。在 OGRE 引擎中具有虚拟文件系统的概念，引擎内部载入资源文件都是通过虚拟文件系统来进行的，引擎并不关心资源文件来自一个普通文件夹、 zip 压缩包甚至网络映射。真正的文件读取功能是通过插件来实现的，所以大家在运行环境里可以发现 Plugin_FileSystem.dll ，早期的 OGRE 版本还有 Plugin_Zip.dll ，在新的版本里被实现到引擎内部了。目前还没有实现对网络文件的直接访问。 </p>
		<p>为了方便 OGRE 程序在运行期间查找资源，使用了资源配置文件 resources.cfg 。这是一个文本文件，我们可以在 OGRE 程序的可执行文件的同一文件夹下找到它。它的内容就是对资源路径的指定，示例如下： </p>
		<p>Zip=../../../Media/dragon.zip </p>
		<p>Zip=../../../Media/knot.zip </p>
		<p>Zip=../../../Media/skybox.zip </p>
		<p>FileSystem=../../../Media/ </p>
		<p>如果资源在一个 Zip 文件中，就写 Zip=****** ，如果资源在一个普通的硬盘文件夹里就写 FileSystem=****** ，通常这两种情况都有。例如在 OGRE 自带的 Demo 中，就将大部分资源放在一个文件夹里，特殊的资源该文件夹中的 Zip 文件里。 </p>
		<p>在 OGRE 自带的例子框架 setupResources() 展示了 Resources.cfg 文件的使用：先利用 ConfigFile 类对文件内容进行了解析，将资源目录用ResourceGroupManager:: addResourceLocation()向OGRE进行提交; <br /></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-02 17:33 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/02/19131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE学习笔记之 配置文件Plugins.cfg</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19130.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 02 Mar 2007 09:31:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19130.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19130.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19130.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19130.html</trackback:ping><description><![CDATA[
		<span lang="en">  
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><b style="mso-bidi-font-weight: normal"><i style="mso-bidi-font-style: normal"><span lang="en" style="FONT-SIZE: 15pt; COLOR: blue">Plugins.cfg 
<p></p></span></i></b></p><p></p><p></p><p></p><p>Ogre的许多功能是以插件的形式提供的.Ogre提供的以Plugin_开头的许多.DLL文件都是所谓的插件。<br />Plugins.cfg指定了插件的路径和插件文件名, 它们可以放在其它文件夹里，但必须在本文件里指定路径。</p><p>在 windows 平台插件的装入过程如下： <br />Root::() <br />{ <br />   if(!pluginFileName.empty()) <br />   loadPlugins(pluginFileName); <br />｝ <br />――――――＞ <br />void Root::loadPlugins(const String&amp; pluginsfile) <br />{ <br />   ConfigFile cfg; <br />   cfg.load(pluginsfile); <br />    ........................... <br />   // 解析文件，处理后将目录与文件名联接 <br />  for(;;) <br />   loadPlugin(plugindir + (*it)) </p><p>} ――――――＞ </p><p>Root::loadplugin(const string&amp;  pluginName) <br />{ <br />   DyLibmanager::getsinleton.load(pluginName); <br />} <br />――――――＞ </p><p>DynLibManager::load(const string&amp; filename) </p><p>{ <br />        DynLib* pLib=new DynLib(filename); <br />        pLib-&gt;load(); <br />} </p><p>――――――＞ </p><p>void DynLib::load() </p><p>{ <br />      m_hInst=(DYNLIB_HANDLE)DYNLIB_LOAD(name.cstr()); <br />} </p><p>在 windows 平台下有如下定义： </p><p>#define  DYNLIB_LOAD(a)     LoadLibrary(a) </p><p>到此， x.dll 插件被加载到内存中，可以使用插件的功能了^_^<br />以下为一个典型的 Plugins.cfg 文件的内容： </p><p># Defines plugins to load <br /># Define plugin folder <br />PluginFolder=. <br /># Define plugins </p><p>Plugin=RenderSystem_Direct3D9 <br />Plugin=RenderSystem_GL <br />Plugin=Plugin_ParticleFX <br />Plugin=Plugin_BSPSceneManager <br />Plugin=Plugin_OctreeSceneManager <br />Plugin=Plugin_CgProgramManager <br />Plugins.cfg 文件内容相当直观，不再赘述。 <br /></p></span>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-02 17:31 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/02/19130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE学习笔记之 配置文件 Ogre.cfg</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19128.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 02 Mar 2007 09:28:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19128.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19128.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19128.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19128.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#0000ff" size="5">Ogre.cfg</font>
				<br />OGRE 引擎将会在可执行文件所在的文件夹中生成一个 ogre.cfg 文件该文件保存了本次对话框的设置结果，下次显示对话框时会将上次的配置结果读入并直接显示。例子框架用 mRoot-&gt;showConfigDialog() 来调出配置对话框。如果你认为在程序运行之前显示一个对话框很难看，或者希望采用另外的形式（比如专门的配置界面，就象在大部分游戏中那样）来配置渲染系统，只要你确保在可执行文件的同一文件夹下存在有效的 Ogre.cfg 配置文件，就可以直接调用 Root 对象的 restoreConfig() 函数来直接读入渲染系统配置信息，而不显示配置对话框。 </p>
		<p>Ogre::Root::showConfigDialog() 会有下述行为：如果程序运行之间已存在一个有效的 ogre.cfg ，那么它会在显示对话框之前将配置文件的内容载入，如果用户对对话框进行了操行，改变的配置参数， showConfigDialog() 会根据用户的新选择依次调用 Root::setRenderSystem, RenderSystem::setConfigOption and Root::saveConfig （在 ogreWin32ConfigDialog.cpp 中实现，写自己的配置对话框时可以参考），需要注意的是配置好的参数，只是在 RenderSystem::initialise or RenderSystem::reinitialise 调用之后才被激活。 以下为典型的 ogre.cfg 文件的内容 <br />Render System=Direct3D9 Rendering Subsystem <br />[Direct3D9 Rendering Subsystem] <br />Allow NVPerfHUD=No <br />Anti aliasing=None <br />Floating-point mode=Fastest <br />Full Screen=No <br />Rendering Device=ATI MOBILITY RADEON X300<br />VSync=No <br />Video Mode=800 x 600 @ 32-bit colour </p>
		<p>[OpenGL Rendering Subsystem] <br />Colour Depth=32<br />Display Frequency=N/A <br />FSAA=0 <br />Full Screen=No<br />RTT Preferred Mode=FBO <br />VSync=No <br />Video Mode=1024 x 768 </p>
		<p>方括号中为可以选择的渲染子系统，而第一行指出了当前的选择是哪个子系统，方括号下面为各子系统的可选参数，在上的例子中分别列出了 Direct3D 9 与 OpenGL 子系统的可选配置参数。<br />Root(const String&amp; pluginFileName = "plugins.cfg", const String&amp; configFileName = "ogre.cfg", const String&amp; logFileName = "Ogre.log");         ~Root(); <br />Root 的构造函数传递了ogre.cfg,只是简单的将文件名保存下来，供其它方法使用，如上面曾经提到过的 Root:saveConfig(void); </p>
		<p> </p>
		<p> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-02 17:28 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/02/19128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORGE(Eihort)学习笔记之GUI Demo 第二部分 OIS输入</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19121.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Fri, 02 Mar 2007 07:27:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19121.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19121.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/02/19121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19121.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19121.html</trackback:ping><description><![CDATA[
		<p>清源游民　 <a href="mailto:gameogre@gmail.com">gameogre@gmail.com</a><br />在第一部分里分析了 ExampleFrameListener 大部内容，不过没有穷尽，它其实也包含了输入部分。因为 CEGUI 本身没输入侦测功能，他需要外部力量的帮助。在 ogre1.4 之间的 demo 中，输入部分是 ogre 自带的，功能有限（对于我这个菜鸟，暂时还用不到特别的功能 ^_^ ）。新版本里，干脆把这部分去掉了，输入部分采用了新的类库 OIS － Object-oriented Input Library . 他的作者本身就是 OGRE team 的成员，我们可以相信，他可以与 Ogre 一起工作得很好 .OIS 支持键盘，鼠标，游戏杆，最后一项暂时不讨论 . 在 windows 平台下， OIS 提供了对 DirectInput 的封装，基本的输入功能是由后者来实现的。既然是学习，我们不防先大概理解一下 DirectInput 基本功能与使用流程以及 OIS 是如何将这些功能封装进去的。我的目的是要讨论 CEGUI 的，现在转到了 OIS, 又转到了 DirectxInput, 似乎是离目标越来越远了。呵，在菜鸟的眼中，什么东西都是菜，都有营养，不防咀嚼个一下下。 CEGUI 固然是好东西，但是如果我们菜鸟整天只会学一堆堆没完没了的类库，那么我们可能永远就只是个菜鸟了，一句话，知道他做了些什么，比光知道他怎么用那可是相当有效！费话少说了，先看一下 DirectInput, 从 MSDN 上直接抄了一段， 懒得翻译了: 下面第一段话，说了理解directInput需要了解的一些基本概念与术语，因为以前看过一点，大概知道是些什么。而我现在主要是做学习笔记，不是教程，所以不解释了。<br />To understand DirectInput, it is essential to understand the following terms. <br />DirectInput object: The root DirectInput interface. · Device: A keyboard, mouse, joystick, or other input device. ·  DirectInputDevice object: Code representing a keyboard, mouse, joystick, or other input device.·  <br />Device object: Code representing a key, button, trigger, and so on found on a DirectInput device object. Also called device object instance. <br />第二段话说明了使用 DirectInput 的基本步骤， OIS 把这些东西封装起来了 . <br />The following steps represent a simple implementation of DirectInput in which the application takes responsibility for ascertaining what device object (button, axis, and so on) generated each item of data. </p>
		<p>1.  Create the DirectInput object. You use methods of this object to enumerate devices and create DirectInput device objects. </p>
		<p>2.  Enumerate devices. This is not an essential step if you intend to use only the system mouse or keyboard. To ascertain what other input devices are available on the user's system, have DirectInput enumerate them. Each time DirectInput finds a device that matches the criteria you set, it gives you the opportunity to examine the device's capabilities. It also retrieves a unique identifier that you can use to create a DirectInput device object representing the device. </p>
		<p>3.  Create a DirectInputDevice object for each device you want to use. To do this, you need the unique identifier retrieved during enumeration. For the system mouse or keyboard, you can use a standard GUID. </p>
		<p>4.  Set up the device. For each device, first set the cooperative level, which determines the way the device is shared with other applications or the system. You must also set the data format used for identifying device objects, such as buttons and axes, within data packets. If you intend to retrieve buffered data—that is, events rather than states—you also need to set a buffer size. Optionally, at this stage you can retrieve information about the device and tailor the application's behavior accordingly. You can also set properties such as the range of values returned by joystick axes. </p>
		<p>5.  Acquire the device. At this stage you tell DirectInput that you are ready to receive data from the device. </p>
		<p>6.  Retrieve data. At regular intervals, typically on each pass through the message loop or rendering loop, get either the current state of each device or a record of events that have taken place since the last retrieval. If you prefer, you can have DirectInput notify you whenever an event occurs. </p>
		<p>7.  Act on the data. The application can respond either to the state of buttons and axes or to events such as a key being pressed or released. </p>
		<p>8.  Close DirectInput. Before exiting, your application should unacquire all devices and release them, then release the DirectInput object. </p>
		<p>了解了这些基本步骤，下面我们打开OIS的源码，看看上面这8步骤封装到了哪里。然后我们返回 ExampleFrameListener, 看看 demo 中是如何使用 OIS 的 . <br />第一个要看的是InputManager类。他提供了平台无关的接口，负责输入系统的创建。没啥好说的，看源码吧(只列出核心代码)：<br />InputManager * InputManager::createInputSystem( ParamList &amp;paramList ) <br />{    <br />      InputManager* im = 0; <br />      #elif defined OIS_WIN32_PLATFORM <br />           im = newWin32InputManager(); <br />    #endif <br />       im-&gt;_initialize(paramList); <br />    return im; <br />} <br />自然，我们只关心windows平台下的。于是找到源码继续看: <br />void Win32InputManager::_initialize( ParamList &amp;paramList ) <br />{ <br />  //Create the device <br />   hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&amp;mDirectInput, NULL ); <br />   /Ok, now we have DirectInput, parse whatever extra settings were sent to us <br />    _parseConfigSettings( paramList ); <br />    _enumerateDevices(); <br />} </p>
		<p>首先，完成了8步骤中的第1步， Create the DirectInput object 。 在_parseConfigSettings ( paramList ); 中对以后将要创建的设备（键盘，鼠标等）的属性，例如独占模式，前台模式等。这些属性列表paramList   经过处理后，之后在创建设备的时候传入。 <br />void Win32InputManager::_enumerateDevices() <br />{     //Enumerate all attached devices <br />    mDirectInput-&gt;EnumDevices(NULL, _DIEnumKbdCallback, this, DIEDFL_ATTACHEDONLY); <br />} </p>
		<p>完成了8步骤中的第2　部分 Enumerate devices ，它是针对游戏杆这类设计的，对于键盘，鼠标并不需要。InputManager 创建之后，就可以用它来创建键盘，鼠标了。它提供了如下的方法： <br />Object * Win32InputManager::createInputObject( TypeiType, boolbufferMode ) <br />{   <br />  Object* obj = 0;   <br />    switch( iType ) <br />    { <br />    case OISKeyboard: obj = newWin32Keyboard( this, mDirectInput, bufferMode, kbSettings ); break; <br />    case OISMouse: obj = newWin32Mouse( this, mDirectInput, bufferMode, mouseSettings ); break; <br />    obj-&gt;_initialize(); <br />    return obj; <br />} </p>
		<p>鼠标，键盘都有各自的类封装。下面以键盘为例，看看它的源码中都做了些什么：<br />void Win32Keyboard::_initialize() <br />{ </p>
		<p>1      mDirectInput-&gt;CreateDevice(GUID_SysKeyboard, &amp;mKeyboard, NULL); </p>
		<p>2      mKeyboard-&gt;SetDataFormat(&amp;c_dfDIKeyboard) </p>
		<p>3      HWNDhwin = ((Win32InputManager*)mCreator)-&gt;getWindowHandle(); </p>
		<p>4      mKeyboard-&gt;SetCooperativeLevel( hwin, coopSetting))) </p>
		<p>5      mKeyboard -&gt;SetProperty( DIPROP_BUFFERSIZE, &amp;dipdw.diph ))) </p>
		<p>6      HRESULThr = mKeyboard-&gt;Acquire();  </p>
		<p>} </p>
		<p>这里可是做了不少的工作,看看方法名就可以明白。为说明方便加上了标号。<br />1， 它做了8步骤中的第3步， Create a DirectInputDevice object for each device you want to use 。 <br />2, 3 4,5它们共同做了8步骤中的第4步－ Set up the device 。这里面包括：设置键盘的数据格式，协作模式，其他属性。很明显，6做了8　步步骤中的第5步－ Acquire the device 。 意思是告诉DirectInput,我准备从设备上获取数据了，给我做好生伺侯着。准备工作都做好了，可以获取数据了，通过设备提供的下面这个方法来做这件事。这就是8　步骤中的第6件事- Retrieve data <br />void Win32Keyboard::capture() <br />{     if( mBuffered ) <br />       _readBuffered(); <br />       else <br />       _read(); <br />} </p>
		<p>从源码中我们可以看到，根据数据的不同，进行了不同的处理。呵，什么不同呢，再从MSDN上抄一段吧，一看就明白： Buffered and Immediate Data <br />DirectInput supplies two types of data: buffered and immediate. Buffered data is a record of events that are stored until an application retrieves them. Immediate data is a snapshot of the current state of a device. You might use immediate data in an application that is concerned only with the current state of a device - for example, a flight combat simulation that responds to the current position of the joystick and the state of one or more buttons. Buffered data might be the better choice where events are more important than states - for example, in an application that responds to movement of the mouse and button clicks. You can also use both types of data, as you might, for example, if you wanted to get immediate data for joystick axes but buffered data for the buttons. </p>
		<p>呵，清楚了吧。继续看代码, capture()有两个分枝，分别处理缓冲数据与立即数据。<br />首先是缓冲数据，主干代码如下： <br />void Win32Keyboard::_readBuffered() <br />{ <br />  DIDEVICEOBJECTDATA diBuff[KEYBOARD_DX_BUFFERSIZE]; <br />    DWORD entries = KEYBOARD_DX_BUFFERSIZE;  <br />   mKeyboard-&gt;GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &amp;entries, 0 ); <br />    //Update keyboard and modifier states.. And, if listener, fire events <br />    for(unsignedinti = 0; i &lt; entries; ++i ) <br />    { <br />       KeyCode kc = (KeyCode)diBuff[ i ].dwOfs; <br />                     if( diBuff[ i ].dwData &amp; 0x80 ) <br />       { <br />          if( listener ) <br />            istener-&gt;keyPressed( KeyEvent( this,kc,_translateText(kc) ) ); <br />       } <br />       else<br />       { <br />           //Fire off event <br />           if( listener ) <br />            listener-&gt;keyReleased( KeyEvent( this, kc, 0 ) ); <br />       } <br />    } <br />} </p>
		<p>这段代码中，GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &amp;entries, 0 );取得了缓冲数据,添入了一个数组：尺寸是EYBOARD_DX_BUFFERSIZE,可以看到entries也等于此值。Entries的作用除了开始调用时说明数组的大小，同时在函数调用返回时说明数组里有多少个数据填入（没有足够多的缓冲数据，当然填不满啦，假如entries返回6，说明还有6个按键信息在缓冲里等待处理）。很明显，for循环就是对这些未处理的信息进行处理。KeyCode kc = (KeyCode)diBuff[ i ].dwOfs; 这是看看，这个事件是由哪个键引走.KeyCode是个枚举型：<br />enum KeyCode <br />    { <br />    　  C_1           = 0x02, <br />       KC_2            = 0x03, <br />       KC_3            = 0x04, <br />       KC_A            = 0x1E, <br />       KC_S            = 0x1F, <br />       KC_D            = 0x20, <br />    } </p>
		<p>确定了是哪个键，接下来就要问了，是按下还是释放？<br /> if ( diBuff[ i ].dwData &amp; 0x80 ) 　回答了这个问题,下面做的是 <br />if ( listener ) <br />            istener-&gt;keyPressed( KeyEvent( this,kc,_translateText(kc) ) ); </p>
		<p>它把相关的信息内容包装成KeyEvent，作为参数发给了，已经注册了的侦听者.可能不太明白，解释一下：当我们按下了某个键，可以认为是发生了个KeyEvent,程序里会响应这个事件，方法就是把某个类实例注册为侦听者(listener),说白了就是告诉OIS,当键盘按下释放的时候你告诉我啊，我要响应他。这里OIS检测到按键事件发生了，根据侦听者的请求，调用侦听者的方法(keyPressed)。也许有疑问，OIS 怎么知道侦听者实现了keypressed（）方法呢？不急先看以下代码，在GUI demo里： <br />class GuiFrameListener : publicExampleFrameListener, publicOIS::KeyListener, publicOIS::MouseListener <br />看到了吧，它继承自  OIS:KeyListener ,从OIS的源码中找到它： <br />class _OISExport KeyListener <br />    { <br />    public: <br />       virtual ~KeyListener() {} <br />       virtual bool keyPressed( constKeyEvent &amp;arg ) = 0; <br />       virtual bool keyReleased( constKeyEvent &amp;arg ) = 0;    <br />    }; <br />哈哈，他正是定义了这两个接口，既然继承自他，当然也就拥有了这两个接口，于是GuiFrameListener成了合理合法的Listener了。 <br />哦，它在哪里注册的呢？很简单.在GuiFrameListener的构造函数里我们看到： <br />{ <br />       mMouse -&gt;setEventCallback(this); <br />       mKeyboard-&gt;setEventCallback(this); <br />} <br />继续挖源码： <br />virtual void Keyboard : ：setEventCallback ( KeyListener *keyListener ) {listener=keyListener;} <br />这下应该就明白了吧。很明显GUI Demo里使用了缓冲模式. <br />剩下来就是立即模式的数据了，他很好理解： <br />void Win32Keyboard::_read() <br />{ <br />  mKeyboard-&gt;GetDeviceState( sizeof(KeyBuffer), &amp;KeyBuffer );  <br />} <br />它把键盘当下的状态保存到缓冲中去，要想知道哪个键是否按下，只要对照缓冲“按图索骥”就可以了。<br /> bool Win32Keyboard::isKeyDown( KeyCodekey ) <br />{ <br />    return (KeyBuffer[key] &amp; 0x80) != 0; <br />} <br />这个键按下了吗？那个键按下了吗？那那个呢？呵，真啰嗦，得一个个的问。 <br />于是我们 GUI Demo 中可以看到下列的代码： <br />virtual bool processUnbufferedKeyInput(const FrameEvent&amp; evt) <br />       { <br /><br />            if(mKeyboard-&gt;isKeyDown(KC_A)) <br />                      mTranslateVector.x = -mMoveScale;    // Move camera left <br />            if(mKeyboard-&gt;isKeyDown(KC_D)) <br />                    mTranslateVector.x = mMoveScale;      // Move camera RIGHT <br />              if(mKeyboard-&gt;isKeyDown(KC_UP) || mKeyboard-&gt;isKeyDown(KC_W) ) <br />                     mTranslateVector.z = -mMoveScale;     // Move camera forward <br />              if(mKeyboard-&gt;isKeyDown(KC_DOWN) || mKeyboard-&gt;isKeyDown(KC_S) )  <br />                    mTranslateVector.z = mMoveScale;      // Move camera backward <br />｝ </p>
		<p>我们用键盘要不是缓冲模式，要么立即模式，不可能一起上，这所有在 demo 中都能看到， demo 的作者懒得多写代码 ( 嘿嘿 ) ，它继承了 ExampleFrameListener 的许多功能，而 ExampleFrameListener 也实现了些立即模式的按键处理。 Demo 没有用到。写了这么多了，还得 8 　大步骤？？ 该第 7 步了吧： ―― Act on the data ， 这有什么好说的呢？爱咋咋地！最后一步，第 8 步――天龙八部 ! 呵错了，是 Close DirectInput      其实很简单， OIS 把他们封装在各个类的析构函数里了，想当然，源码也不用贴了 . 说了 OIS 　如何把 DirectInput 封装起来，参考 8 大步骤，如何使用 OIS 也基本很明白了。OIS ，就学到这里，接下来该主角上场 CEGUI 得休息了，打字，翻资料，看代码，很累了。 </p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
				<br />
		</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-02 15:27 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/02/19121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORGE(Eihort)学习笔记之GUI Demo  第一部分 例子框架</title><link>http://www.cppblog.com/yuanyajie/archive/2007/03/01/19092.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Thu, 01 Mar 2007 13:47:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/03/01/19092.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19092.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/03/01/19092.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19092.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19092.html</trackback:ping><description><![CDATA[<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">清源游民　</span> <span lang=EN-US>gameogre@gmail.com</span> </p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下面直接涉及到相关类<br></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><v:shapetype id=_x0000_t75 coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_cegui1.JPG"> <br>首先看一下</span> <span lang=EN-US>ExampleFrameListener,</span> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下面是类间关系图</span> </p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><img src="http://www.cppblog.com/images/cppblog_com/yuanyajie/2865/r_cegui2.JPG">&nbsp;<br>ExampleFrameListener 继承了 FrameListener, 因此拥有了它的两个方法 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual bool frameStarted(constFrameEvent&amp; evt) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual bool frameEnded(constFrameEvent&amp; evt) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>在 ExampleApplication 中将它们注册到 ogre 中，在适当的时候， ogre 会调用这两个方法 . 下面是注册地点与时机 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual void ExampleApplication::createFrameListener(void) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; { </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mFrameListener= newExampleFrameListener(mWindow, mCamera); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mFrameListener-&gt;showDebugOverlay(true); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mRoot-&gt;addFrameListener(mFrameListener); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>createFrameListener 在 ExampleApplication::setup() 中被调用. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>ExampleFrameListener 又继承了 WindowEventListener. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>WindowEventListerner 在ogre 1.4中是新加的。按照手册：它是一个 Callback class used to send out window events to client app. 它定义以下四个接口,而显然例子程序中只用到了两个<br></span><span lang=EN-US>virtual void windowMoved(RenderWindow* rw)&nbsp;&nbsp; {} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual void windowResized(RenderWindow* rw) {} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual void windowClosed(RenderWindow* rw)&nbsp; {} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>virtual void windowFocusChange(RenderWindow* rw) {} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>这种所谓的回调类如何实现？下面只说明windows操作系统的情况。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>我们知道，windows操作系统监视系统中发生的一切，通过消息的方式通知相应的窗口。每个窗口类注册的时候，都指明一个回调过程，在那里处理传来的消息。应用程序又有各自的消息队列，从消息队列中取得消息，然后分开给各窗口. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>不防从ogre的源码中看看上述windows基本过程如何实现，这有助理解回调类的实现过程： </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>首先，ogre可以为我们创建一个窗口： </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>mWindow = mRoot-&gt;initialise(true);// </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>于是我们进入到initialise()中看看吧，它倒底做了些什么事情。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>RenderWindow * Root::initialise(boolautoCreateWindow, constString&amp; windowTitle) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>mAutoWindow =&nbsp; mActiveRenderer-&gt;initialise(autoCreateWindow, windowTitle); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// 这里：RenderSystem* mActiveRenderer </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>mActivaRenderer 只是接口，不用实际的事情，实际的工作由它的子类完成，现只看DirectX实现: </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>RenderWindow * D3D9RenderSystem::initialise( boolautoCreateWindow, constString&amp; windowTitle ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>autoWindow = this-&gt;createRenderWindow( windowTitle, width, height, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fullScreen, &amp;miscParams ); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230; .. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>好，继续下去,看看createRenderWindow做了些什么 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;</span> <span lang=EN-US>&nbsp; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>RenderWindow * D3D9RenderSystem::createRenderWindow(constString &amp;name, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int width, unsignedintheight, boolfullScreen, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const NameValuePairList *miscParams) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230;&#8230;&#8230;&#8230; .. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>RenderWindow * win = newD3D9RenderWindow(mhInstance, mActiveD3DDriver, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPrimaryWindow ? mpD3DDevice : 0); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>win -&gt;create( name, width, height, fullScreen, miscParams); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// &#8230;&#8230;&#8230;&#8230;&#8230;&#8230; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>继续 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>void D3D9RenderWindow::create(constString&amp; name, unsignedintwidth, unsignedintheight, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool fullScreen, constNameValuePairList *miscParams) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// Register the window class </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// NB allow 4 bytes of window data for D3D9RenderWindow pointer </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; WNDCLASS wc = { 0, WindowEventUtilities::_WndProc, 0, 0, hInst, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LoadIcon(0, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (HBRUSH)GetStockObject(BLACK_BRUSH), 0, "OgreD3D9Wnd" }; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RegisterClass(&amp;wc); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// 定义了窗口类，并且进行注册，需要注意的是，你看看它把窗口类的回调函数的值设为了什么？先记住，一会儿讨论它吧。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Create our main window </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Pass pointer to self </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mIsExternal = false; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mHWnd = CreateWindow("OgreD3D9Wnd", title.c_str(), dwStyle, </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mLeft, mTop, mWidth, mHeight, parentHWnd, 0, hInst, this); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// 调用win32API把窗口真正Create出来了 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>WindowEventUtilities ::_addRenderWindow(this); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// 这一步也要注意到 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>继续 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>void WindowEventUtilities::_addRenderWindow(RenderWindow* window) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; _msWindows.push_back(window); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>_msWindows 定义为： </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>typedef std::vector&lt;RenderWindow*&gt; Windows; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;static Windows _msWindows; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>，没什么，只是个stl容器，把生成的窗口放进去了. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>到现在，容器类注册好了，回调函数也指定了，窗口也创建出来了。现在我们继续思考我们最最初的问题： WindowEventListerner 这个回调类是如何实现所谓的回调机制的，也就是说windowEventListerner不是定义了四个接口，它响应特定的windows事件，我们要讨论的就是这四个接口方法如何被调用起来，实现所谓的回调机制。 </span><span lang=EN-US>还记得注册窗口类的回调函数是什么吧， </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>WindowEventUtilities::_WndProc ， 呵呵，只要在那里调用上述四个接口函数就好了。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>哪问题又来了，_WndProc到哪里找这四个接口函数呢？ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>在 ExampleFrameListener 类的构造函数里我们可以看到如下代码 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>//Register as a Window listener </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>WindowEventUtilities::addWindowEventListener(mWindow, this); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>啊，这就是奥秘所在!到源码中看看吧。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>void WindowEventUtilities::addWindowEventListener( RenderWindow* window, WindowEventListener* listener ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; _msListeners.inser t(std::make_pair(window, listener)); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>-msListeners 被定义成这样子： </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>typedef std::multimap&lt;RenderWindow*, WindowEventListener*&gt; WindowEventListeners; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>static WindowEventListeners _msListeners; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>没有什么，就是STL容器，它使得窗口(RenderWindow)与(WindowEventListener) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>组成了亲密派对，窗口有了什么消息，就可以用对应的回调类响应。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>只剩下一点秘密了，到底怎么调用起来的呢，某某某说过，源码之下，了无秘密，那就看源码吧： </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>LRESULT CALLBACK WindowEventUtilities::_WndProc(HWNDhWnd, UINTuMsg, WPARAMwParam, LPARAMlParam) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>WindowEventListeners ::iteratorstart = _msListeners.lower_bound(win), </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end = _msListeners.upper_bound(win); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>// 为遍历做准备 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; switch( uMsg )&nbsp;&nbsp; // 消息真的来了 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp; { </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>case WM_MOVE: </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //log-&gt;logMessage("WM_MOVE"); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; win-&gt;windowMovedOrResized(); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for( ; start != end; ++start ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (start-&gt;second)-&gt;windowMoved(win); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; case WM_SIZE: </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //log-&gt;logMessage("WM_SIZE"); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; win-&gt;windowMovedOrResized(); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for( ; start != end; ++start ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (start-&gt;second)-&gt;windowResized(win); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>就这样，四个接口函数被调用起来了！ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>也许，也许，关于消息还有些要说明的。消息如何泵出来的？ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>当看到了如下代码，突然就想找个朋友，会心一笑. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>void WindowEventUtilities::messagePump() </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; MSG&nbsp;&nbsp; msg; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; while( PeekMessage( &amp;msg, NULL, 0U, 0U, PM_REMOVE ) ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; { </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TranslateMessage( &amp;msg ); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DispatchMessage( &amp;msg ); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; } </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>打破铁锅问到底吧，它又如何被调用起来？ </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>void Root::startRendering(void) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp; { </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while( !mQueuedEnd ) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Pump messages in all registered RenderWindow windows </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WindowEventUtilities::messagePump(); </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!renderOneFrame()) </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>} </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>就是这里了。写的乱七八糟，脑子也乱七八糟，梳理一下吧。 </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>首先，mWindow = mRoot-&gt;initialise(true);初始化，随便通过一系统连锁反应create出来一个窗口，并把它的回调函数设定为 WindowEventUtilities::_WndProc. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>ExampleFrameListener 继承了 WindowEventListener 的四个约定接口函数。并且在它的构造函数里调用 WindowEventUtilities::addWindowEventListener(mWindow, this); 进行了注册，以便特定窗口消息发生时进行响应. Root ::startRendering （）进入了渲染循环，渲染每一帧前，检测widnows消息，并进行分发。于是消息进入到窗口类的回调函数_WndProc,在函数内部根据消息的不同，分别调用已注册的侦听器的约定接口函数。<br>先写到这里吧，原来只是想写写CEGUI的使用，谁知已经写了这么多了，CEGUI还一字未提。脑子乱了，先休息吧。下面还得说说OIS,CEGUI又得靠后了. </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><br></span></p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19092.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-03-01 21:47 <a href="http://www.cppblog.com/yuanyajie/archive/2007/03/01/19092.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）CEGUI 中文输入</title><link>http://www.cppblog.com/yuanyajie/archive/2007/02/28/19037.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Wed, 28 Feb 2007 02:25:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2007/02/28/19037.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/19037.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2007/02/28/19037.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/19037.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/19037.html</trackback:ping><description><![CDATA[
		<p>刚刚搞CEGUI的时候大体看了一下CEGUI。原以为其不支持中文。<br />而且网上很多人都说不支持。。我也就没有细细看了。<br />当用到Editbox的时候。又不得不用中文。 这时候问题就来了。<br />先是用utf8方式转换达到中文显示的目的。可是中文输入却不行。网上有一种说法是将两个字节链接起来即可。看了一下编译器设置，当前使用的是多字节模式。就不得不这样做了。不过估计我对C++还不算深。搞了一会，还是失败了。不过后来在网上有一个朋友说CEGUI本来就支持中文的。但又看到其它人说的，不解其言啊。再后来有一个叫老妖精的网友给了我一份代码。结果真的成功了。可显示可输入中文。对于中文的字表由一个font文件创建。方法如下：<br />1:找一个中文字体文件(.ttf)<br />2:创建一个文本文件，将其存为.font文件.<br />3:在.font文件里写如下代码(我个假设一个chinese.ttf文件):<br />&lt;?xml version="1.0" ?&gt;<br />&lt;Font Name="chinese" Filename="chinese.TTF" Type="FreeType" Size="10" NativeHorzRes="1024" NativeVertRes="768" AutoScaled="true"/&gt;<br />// 说明:Name就是在程序中要用到的名字, Filename就是文件名,FreeType可以是自定义的,具体到官方网有说明.Size就是字体的大小.程序中是不能调大小的.因为字体生成纹理后就是固定的.<br />4:在你写的程序的键盘和事件上处理上面正常情况下是这样子的:CEGUI::System-&gt;injectChar((CEGUI::utf32)Key); // Key值就是你程序捉到的按键值,我们需要将其改为:</p>
		<p>    if (ImmIsIME(GetKeyboardLayout(0)))<br />    {<br />     CEGUI::DbcsSupport::injectChar(Key);<br />    }<br />    else<br />    {<br />     CEGUI::System-&gt;injectChar((CEGUI::utf32)Key);<br />    }</p>
		<p>    ImmIsIME(GetKeyboardLayout(0))目的是用于检则现在的输入法是否打开的。如果是在输英文状态我想你不会画蛇添足的。使用这个需要imm32.lib的支持。MSDN上面也有说明。<br />5:最后就是添加一份代码到你的CEGUI使用项目里，如下：<br />namespace CEGUI{<br />bool DbcsSupport::injectChar(utf32 code_point )<br /> {<br />#ifndef UNICODE<br />  static char     s_tempChar[3]  = "";<br />  static wchar_t  s_tempWchar[2] = L"";<br />  static bool s_flag = false;<br />  unsigned char  uch  = (unsigned char)code_point;<br />  if( uch &gt;= 0xA1 )<br />  {<br />   if( !s_flag )<br />   {<br />    s_tempChar[0] = (char)uch; //第一个字节<br />    s_flag = true;<br />    return true;<br />   }<br />   else if( uch &gt;= 0xA1 )<br />   {<br />    s_tempChar[1] = (char)uch; //第二个字节<br />    s_flag = false;<br />    MultiByteToWideChar( 0, 0, s_tempChar, 2, s_tempWchar, 1); //转成宽字节<br />    s_tempWchar[1] = L'\0';<br />    utf32 code = (utf32)s_tempWchar[0];<br />    //Font* fnt = System::getSingleton().getDefaultFont();<br />    return CEGUI::System::getSingleton().injectChar( code );<br />   }<br />   else<br />   {<br />    return CEGUI::System::getSingleton().injectChar(code_point);<br />   }<br />  }<br />  else<br />  {<br />   s_flag = false;<br />   return CEGUI::System::getSingleton().injectChar(code_point);<br />  }<br />#else<br />  return CEGUI::System::getSingleton().injectChar(code_point );<br />#endif<br /> }<br />}</p>
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1509217</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/19037.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2007-02-28 10:25 <a href="http://www.cppblog.com/yuanyajie/archive/2007/02/28/19037.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在OGRE中使用CEGUI的笔记</title><link>http://www.cppblog.com/yuanyajie/archive/2006/11/01/14485.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Wed, 01 Nov 2006 07:32:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2006/11/01/14485.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/14485.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2006/11/01/14485.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/14485.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/14485.html</trackback:ping><description><![CDATA[
		<p>使用 cegui 来制作界面 , 不论在何种平台下 , 有基本的三大步骤要做 : <br />1, 创建一个 CEGUI::Render 实例 <br />2, 创建 CEGUI::System 对象 <br />3, 调用各种方法来渲染用户界面 <br />第一步 , 在我使用的 ogre 环境下使用以下代码来创建 CEGUI::Render 实例 <br />Ogre3D <br />CEGUI::OgreCEGUIRenderer* myRenderer = <br />       new CEGUI::OgreCEGUIRenderer(myRenderWindow); <br />第二步相当简单 , 可使用 <br />new CEGUI::System(myRenderer); <br />第三步，基本上来讲，大部分平台下，如 direct3D, OpenGL, 我们在渲染循环的尾部调用 <br />CEGUI::System::renderGUI 来开始界面的渲染。如果我们使用 ogre3d 引擎，这一步不需要 <br />我们显示的执行。因为 ogre 引擎已经考虑了。 <br />除了这三大步之外，我们还有一些工作要做，首先，我们需要加载数据文件，并且完成初始化工作。 <br />CEGUI 使用了许多类型的文件。 CEGUI 使用一个称为 ResourceProvider 的帮助对象，用它来做为核心 CEGUI 库与外部文件加载系统的接口。通过实现一个特定的 ResourceProvider 对象， cegui 的渲染模块就可以与外部系统的资源管理器、文件加载子系统无缝地集成。 CEGUI 需要的数据文件便可以通过外部系统提供的功能来进行加载。 CEGUI 中的大部分文件是以 XML 的文件格式来保存的。缺省情况下， CEGUI 在内部使用 Xerces-C++ 库来分析 XML 文件，使用 schema 来对 xml 进行校验， schema 文件以标准的 .xsd 扩展名来保存 . 许多文件本质上都是 XML 格式的文件，但是根据其意义不同，分别使用了不同的文件扩展名。 <br />.Imageset 文件可将一幅图像看做若干小图像的集合。换名话说，就是将一幅图像的某个区域看做一幅独立的图片来使用。 <br />.Font 定义了在 CEGUI 中所使用的字体类型。 <br />.scheme 　可以将许多数据组合在一起使用，它也是装载与注册 widget 最方便的方法。在一个 .scheme 文件中可以包括下列几种数据： <br />Imagest,font, window Set, window Alias <br />window set 指定了装载模块（ .dll 等）的名称，和一组可以注册到系统中的 widget. <br />window alias 提供了别名的功能，实现已经注册的 window/widget 的形式上的隐藏。 <br />.Layout 　包含了窗口布局的 xml 表示。每个嵌套的 'window' 元素定义了被创建的 window 或是 widget."Property" 元素定义了窗口的设置与属性值。 <br />.config 是可供选择的配置文件，他可指定一些缺省的属性。 <br />我们使用 CEGUI 来制作图形界面，至少要使用以下三种文件： <br />.imageset, .Font . Scheme; <br />当然，我们知道在一个 Scheme 中是可以包括 imageset 与 Font 的。 <br />CEGUI::SchemeManager::getSingleton().loadScheme(“../datafiles/schemes/TaharezLook.scheme”); <br />// load in a font.  The first font loaded automatically becomes the default font. <br />CEGUI::FontManager::getSingleton().createFont(“../datafiles/fonts/Commonwealth-10.font”); <br />对于 ogre 的使用者来讲，应该在 resources.cfg 将这些资源所在的目录加进去。 <br />我们可用下面的语句来指定 cegui 所需要的缺省的 font 与 cursor 资源。 <br />在 cegui 的概念里，每个 widget 都是一个 window, 从编程的角度来讲，每个 widget 类都是从同一个相同的 window 基类继承而来的 . 因此， widget 与 window 有着基本相同的行为。 <br />窗体的许多属性与设置在窗口层树上是可以遗传的。高一级窗口的属性与行为会影响到下一级窗口的属性与行为。例如附着在某个 window 上的 window 与 widget 会被父 window 影响。当父窗口被 destroy 时，它所附属的所有的子窗口与 widget 也都将被 destroy. <br />创建 CEGUI 窗口，我们可以使用两种形式，一是 C ＋＋代码，二是编辑 XML layout 文件。 <br />注意：在 CEGUI 中，所有的 window 都是由 WindowManager singleton object 来统一创建的。我们首先得到这个对象： <br />using namespace CEGUI; <br />WindowManager&amp; wmgr = WindowManager::getSingleton(); <br />一般来讲，我们总是创建一个 DefaultWindow 来做为我们将使用的窗口的 Root, 在这种方式下，我们会有比较好的灵活性。 <br />Window* myRoot = wmgr.createWindow(“DefaultWindow”, “root”); <br />System::getSingleton().setGUISheet(myRoot); <br />createwindows() 函数所使用的第一个参数，指明了将要创建的窗口类型，它一般是在我们使用的 .scheme 文件中所注册过的，当然还有一些系统定义的，总是有效的窗口类型如上面所提到的 DefaultWindow.DefualtWindow 是不可见的，它只是做为所有窗口的 root 来使用。 <br />一般我们总是要创建一个 Framewindow, 它可以包括其他窗口与 widget ，本身也是可视的。 <br />FrameWindow* fWnd = (FrameWindow*)wmgr.createWindow(“TaharezLook/FrameWindow”, “testWindow”); <br />之后，我们将创建的窗口挂到 root 上，形成一个层次关系。 <br />myRoot-&gt;addChildWindow(fWnd); <br />编辑 xml layout 文件，可使用专门提供的制作工具。具体的使用方法可以参阅相关文档，这里说一下如何使用制作好的 xml layout 文件。 <br />using namespace CEGUI; <br />Window* myRoot = WindowManager::getSingleton().loadWindowLayout(“test.layout”); <br />System::getSingleton().setGUISheet(myRoot); <br />首先，将 layout 文件装载进来，再指定根结点。 <br />CEGUI 本身侦测用户输入，这些不是 CEGUI 的责任，而是程序的员的责任。当有用户外部输入时，我 <br />们可以选择将这些消息告知 CEGUI ，这样 CEGUI 才会响应。 <br />在 ceguir::System 类中定义了一组函数，它作为我们进行消息传递的接口。 <br />bool injectMouseMove(float delta_x, float delta_y); <br />bool injectMousePosition(float x_pos, float y_pos); <br />bool injectMouseLeaves(void); <br />bool injectMouseButtonDown(MouseButton button); <br />bool injectMouseButtonUp(MouseButton button); <br />bool injectKeyDown(uint key_code); <br />bool injectKeyUp(uint key_code); <br />bool injectChar(utf32 code_point); <br />bool injectMouseWheelChange(float delta); <br />bool injectTimePulse(float timeElapsed); <br />这些函数的返回值说明了 CEGUI 是否已经将传入的消息 consume 掉了。 <br />CEGUI 使用回调机制来进行消息处理。可以为某个窗体的特定事件注册一个函数，当窗体事件发生时， CEGUI 会自动调用所注册的函数。 <br />bool TutorialApplication::handlePopMenu(constCEGUI::EventArgs&amp; e) <br />｛ <br />// 。。。。。进行事件响应 <br />｝ <br />void setupEventHandlers(void) <br />    { <br />       CEGUI::WindowManager&amp; wmgr = CEGUI::WindowManager::getSingleton(); <br />       wmgr .getWindow((CEGUI::utf8*)"MyButton")-&gt;subscribeEvent( <br />       CEGUI ::PushButton::EventClicked, CEGUI::Event::Subscriber(&amp;TutorialApplication::handlePopMenu, this)); <br />   } </p>
		<p>EventClicked :: 系统预定义的事件 . subscribeEvent:: 注册函数，它事件与响应函数联接在一起。 <br />在 ogre 程序中，当侦听器收到键盘，鼠标消息时，首先经过适当的转换（ CEGUI 可以识别）再传递给 CEGUI 。下面这个函数执行鼠标键标识转换。 <br />CEGUI::MouseButton convertOgreButtonToCegui(int buttonID) <br />{ <br />       switch (buttonID) <br />       { <br />       case MouseEvent::BUTTON0_MASK: <br />              return CEGUI::LeftButton; <br />       case MouseEvent::BUTTON1_MASK: <br />              return CEGUI::RightButton; <br />       case MouseEvent::BUTTON2_MASK: <br />              return CEGUI::MiddleButton; <br />       case MouseEvent::BUTTON3_MASK: <br />              return CEGUI::X1Button; <br />       default: <br />              return CEGUI::LeftButton; <br />       } <br />} </p>
		<p>将 CEGUI 需要知道的键盘，鼠标消息告知它。即在 OGRE 处理这些消息时通知 CEGUI 。以下这函数说明了用法。 <br />       void mouseMoved (MouseEvent *e)// 鼠标移动 <br />       { <br />              CEGUI::System::getSingleton().injectMouseMove( <br />                   e-&gt;getRelX() * mGUIRenderer-&gt;getWidth(), <br />                   e-&gt;getRelY() * mGUIRenderer-&gt;getHeight()); <br />                   e-&gt;consume(); <br />       } </p>
		<p>       void mousePressed (MouseEvent *e)// 鼠标按下 <br />       { <br />              CEGUI::System::getSingleton().injectMouseButtonDown( <br />               convertOgreButtonToCegui(e-&gt;getButtonID())); <br />             e-&gt;consume(); <br />       } <br />       void mouseReleased (MouseEvent *e)// 鼠标弹起 <br />       { <br />              CEGUI::System::getSingleton().injectMouseButtonUp( <br />              convertOgreButtonToCegui(e-&gt;getButtonID())); <br />              e-&gt;consume(); <br />       } </p>
		<p>       void keyPressed(KeyEvent* e)// 键按下 <br />       { <br />              CEGUI::System::getSingleton().injectKeyDown(e-&gt;getKey()); <br />              CEGUI::System::getSingleton().injectChar(e-&gt;getKeyChar()); <br />              e-&gt;consume(); <br />       } <br />       void keyReleased(KeyEvent* e)// 键弹起 <br />       { <br />             CEGUI::System::getSingleton().injectKeyUp(e-&gt;getKey()); <br />             e-&gt;consume(); </p>
		<p>       } <br />在 CEGUI 中使用中文的问题：   现在总结了一下在 CEGUI 中显示中文需要注意的事项 :<br />    1 、将 simhei.ttf copy to \ogrenew\Samples\Media\gui<br />    2 、将 simhei-12.font  拷到上目录内容为 <br />&lt;?xml version="1.0"  ?&gt;<br />&lt;Font Name="SimHei-12" Filename="simhei.ttf" Type="Dynamic" Size="12" NativeHorzRes="800" NativeVertRes="600"  <br />AutoScaled="true"&gt;<br />&lt;GlyphSet Glyphs=" 你好世界退出演示渲染到新材质建编辑窗口 " /&gt; （ &lt;---- 自己要用到的汉字） <br />&lt;/Font&gt; <br />注意大小写！！ GlyphSet Glyphs 是在程序中要用到的汉字，它是让 cegui 预生成一个字符图像集用的（想当然的 :-P ）如果修改了这个文件，注意要用 Unicode （ UTF-8 ）的编码来保存，在 vc7.1 中：文件 -&gt; 高级保存选项   的   编码   栏中选择。 <br />    3 、在 TaharezLook.scheme 中 <br />     &lt;Font Name="Tahoma-12" Filename="tahoma-12.font" /&gt; 后加入 <br />     &lt;Font Name="SimHei-12" Filename="simhei-12.font" /&gt;  注意大小写 <br />     以上是一些准备工作 <br />    4 、在自己的应用中设置默认字体 <br />    mGUISystem-&gt;setDefaultFont((CEGUI::utf8*)"Tahoma-12"); 改为 <br />    mGUISystem-&gt;setDefaultFont((CEGUI::utf8*)"SimHei-12"); <br />    5 、在自己的应用程序中就可以把相关的 Text 属性该为中文了，如： <br />    item = new CEGUI::ListboxTextItem((CEGUI::utf8*)" 退出 ", 6); <br />    同样要注意的是要保存为 Unicode （ UTF-8 ）的编码。同时这些字要是在 simhei-12.font 中定义过的字，当然也可以象那个 CEGUIChieseDemo <br />那样用动态生成如： <br />    gfont-&gt;defineFontGlyphs(gfont-&gt;getAvailableGlyphs() + (utf8*)" 当前最佳坏平均的框架率三角 "); <br />    编译自己的程序，应该就可以看到中文了，罗嗦一下，记住只要有汉字出现的文件就保存为 Unicode （ UTF-8 ）编码的！！！ </p>
		<p> </p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/14485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2006-11-01 15:32 <a href="http://www.cppblog.com/yuanyajie/archive/2006/11/01/14485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>人物动画与绑定盒</title><link>http://www.cppblog.com/yuanyajie/archive/2006/10/26/14241.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Thu, 26 Oct 2006 08:30:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2006/10/26/14241.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/14241.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2006/10/26/14241.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/14241.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/14241.html</trackback:ping><description><![CDATA[
		<p>在ogre中,当mesh被动画的时候,它的BoundingBox(AABB)是不更新的.由此不能用它她来做为碰撞检测的代理.这个问题麻烦了好几天.最后是在论坛上找到了答案.不能更新的原因有二。一是代价太高。二是有可<br />能硬件做skinng（蒙皮），不能用软件的方法访问硬件的数据。</p>
<img src ="http://www.cppblog.com/yuanyajie/aggbug/14241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2006-10-26 16:30 <a href="http://www.cppblog.com/yuanyajie/archive/2006/10/26/14241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OGRE学习记事本</title><link>http://www.cppblog.com/yuanyajie/archive/2006/10/24/14087.html</link><dc:creator>清源游民</dc:creator><author>清源游民</author><pubDate>Tue, 24 Oct 2006 06:13:00 GMT</pubDate><guid>http://www.cppblog.com/yuanyajie/archive/2006/10/24/14087.html</guid><wfw:comment>http://www.cppblog.com/yuanyajie/comments/14087.html</wfw:comment><comments>http://www.cppblog.com/yuanyajie/archive/2006/10/24/14087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yuanyajie/comments/commentRss/14087.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yuanyajie/services/trackbacks/14087.html</trackback:ping><description><![CDATA[如果一个人物有几个动画状态,而在程序中需要对动画状态作切换时,一定要将先前的那个状态关看,然后再作切换.<br />    //假设先前的动画状态是Wait,在切换到新的动画状态时,应该先将Wait关掉.就是第一句话:以下为可能的代码片断.<br />    <u>mAnimationState-&gt;setEnabled(false);<br /></u>    mAnimationState = mEntity-&gt;getAnimationState( "Walk" );<br />    mAnimationState-&gt;setLoop( true );<br />    mAnimationState-&gt;setEnabled( true);<br />   //2006.10.24<img src ="http://www.cppblog.com/yuanyajie/aggbug/14087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yuanyajie/" target="_blank">清源游民</a> 2006-10-24 14:13 <a href="http://www.cppblog.com/yuanyajie/archive/2006/10/24/14087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>