分享
 
 
 

Looking 2002-12-2

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

2002-12-2

好像很久没有写Looking日记了,好像有一个月了吧。过去的一个月里发生的事情,我想我一生都不会忘记。我的一个朋友惹上的刑事官司,在他的父母再三恳求之下,我居然成了他的代理人。从那天开始,我的生活就进入了社会的令一个层面。我以前是一个不太接触社会的人,我周围的人基本上都是大学毕业,有1/4的人是硕士及以上学历,我一直认为这个社会是文明、礼貌的社会。但,当我来到公检法这个圈子里后,我发现我好像到了火星。当我第一次来到看守所为朋友办手续时,接待我的是个老公安。几句话后,他发现我没有一点经验后,劈头盖脸的就是一句:你是不是不接触社会,是不是念书念傻了。当时,我一脸窘迫,点点头说,确实是念傻了。后来,可能是觉得我确实傻的可爱,老公安开始为我指点了一些门道。就这样,我踏上了一段奇怪的旅程。后来,我每天往返于刑警队和检查院才知道,那天的遭遇,真是小菜一碟。不过,司法部门的工作人员的态度虽然差了一些,但还是比较公正的,检察院驳回了对我的朋友的指控。在这20多天里,我还是有很多收获的,至少我的法律意识提高了很多,我感觉以前的我简直就是个法盲。

还是继续说我的Looking吧。自从进而了D3D的世界,我就处于发现和回忆状态。以前虽然写过一些OPENGL的代码,但那毕竟是两年前的事情了,重新建立我的3D编程概念是当前最重要的。总的感觉D3D在功能和概念上与OPENGL是非常类似的,在程序界面和调试功能上好像要比OPENGL优胜一些。由于我看D3D的文档比较吃力,于是我在网上寻找了很多资料,不过有价值的很少,特别是中文资料。有一个很有趣的现象,国内的技术网站的内容重复率真是惊人的高。我在Google上查找资料的时候,最害怕的就是某个生僻的单词被某篇中文资料收集,如果发生这种情况,就不用干别的了,先翻20页再说。绕了一大圈,最后还是苦读M$文档,说来也怪,这次看M$文档就亲切多了,可能是没有后路了的原因吧。

到目前为止,我对3D世界里的空间管理的兴趣要比渲染的兴趣大的多。因为,我认为空间管理才是3D引擎的核心和关键任务。在3D引擎里,像ZBuffer这样普通、通用的技术都是多余的,越高级的功能速度越慢。因此,我没有太多地把注意力放在光照和材质这类事情上,我只是在,Camera的位置上放了一个同向的Spotlight,它的范围和camera space相当,对于我来说,只要能看清楚就行。我首选要解决的问题是camera位置的调整和world space的旋转,我必须能够看到3d space里的各个细节才能进行其它工作。在写Looking编辑器的时候,我的首选参照对象是3DMax,因此每增加什么功能我都会去先研究研究3DMax。在研究3DMax的world space旋转功能时,我发现了我以前在概念上的一个误区。以前在写Looking的时候,显示实际效果的view是D3D view(使用D3D技术渲染的view,呵呵纯粹的俚语),而3个投影view是普通的windows窗体。但3DMax显然不是这样处理的,他的4个view都是得D3D view。这样作有很多显而易见的好处,其中最重要的是在编程界面上所有的view得到了统一。这个技术的关键在于效果view使用的是普通的projection,而投影view使用的是orthogonal projection,也就是投影view的view平截头是方的。这样在效果view里的操作和投影view里的操作就几乎是一样的了,唯一的不同可能只是FVF的差异和投影view里不需要光照并且投影view只需要画出framework。这也意味着,我以前作的很多工作完蛋了。

我一直认为程序员在编写程序的时候,最应该花费精力的是概念和结构,至少不是编写代码。这就像开车一样,如果你的注意力始终在挂档上,那你肯定是个新手。教我开车的师傅说过的一句话令我印象非常深刻:如果你在时速80脉的情况下,还能知道路边的女孩是否漂亮,你就是个成手的。由于我的工作关系,我经常要培训一些新兵。我经常会问他们,在编程的时候你在想什么?这不是在责问他们,而是我确实想知道他们在想什么。在我看来,编程时在想什么是评价一个程序员技术能力的最基本的指标之一。在编写一个项目的过程,有时就是寻找某个概念的过程。当项目完成后,这个概念也就丰满了,项目顺应这概念的曲线而存在。一个项目是什么样子的?这就是项目的样子。如果,一个项目完成了,还总结不出点东西,那这个项目肯定是失败的项目。

解决了view的问题后,我需要作一些辅助性的基础工作。由于我是D3D的新手,我必须要编写大量的试验性代码。但是,D3D的FVF令我非常厌烦。每个函数都要增加新的struct,尽管我把这些struct定义在函数体内,并使用相同的名字,但还是太多了。这时我需要一个通用的D3D渲染函数,我不需要追求速度,只要通用就行。要实现它,我必须提出一个数据源的概念,我把它称为Solid。有了Solid,就需要一个Face的概念;有了Face,就需要一个Polygon的概念;有的Polygon,就需要一个Vertex Pool的概念。这时候,我想我已经越界了。这些概念都是3D Engine的概念,而不是编辑器的概念。如果任由其发展下去,以后会产生概念冲突。于是我建立了一个新的DLL工程,叫LJet。它将是Looking编辑器、Looking编译器和Looking解释器的通用API。写到这里,我开始越来越喜欢D3D了。D3D不禁提供了丰富的渲染功能,而且它还提供的丰富的3D数学支持。像Vector、Matrix、Plane、Quaternion支持函数,应有尽有。如果使用OPENGL这些恐怕都要自己写,虽然难度不大,但代码量却不少,而且还需要大量的调试。现在就简单多了,我可以直接进入主题。当然也不能原样照抄,否则离开了D3D,LJet就玩不转了。在LJet中我使用J作为系统前缀,这样在所有的D3D结构里我把D3D或D3DX字样替换成J后typedef一下,在相关的函数里,我把D3D或D3DX字样替换成j后#define一下,以后要摆脱D3D恐怕就得#if #else #endif了。

现在,我要重新考虑component的问题了。既然定义了通用的data source格式,组件的接口需要进行一些调整。由于组件本身其实就是一个Solid,因此它只需导出Solid实例就可以了。好在我现在只有一个component,一个cube component。我认为,在研究3d engine时,一个cube就足够了。它足可以组成相当复杂的场景。但是,可能是因为好玩,我一口气又作了球体、圆柱、台锥、圆环、棱锥、管子和大茶壶等7个component。只要是3DMax的标准组件,我都完成了一个,就像下面那个图片所显示的,很有趣吧。当然这些组件还有不少瑕疵,不过以后在修改吧。

越研究3DMax越觉得它是个好东西。该软件在交互上的创意真是神来之笔,我真想把这些都搬到我的系统里。这时候,Looking编辑器又来到了一个十字路口。Looking编辑器现在越来越具备了商业级3D图形编辑软件的特征。这时候,我很矛盾,是在编辑系统里继续发展下去,还是向3d engine转移注意力?最后,我选择了后者,毕竟这是我长久以来的一个愿望。但是压制继续发展Looking编辑器的强烈愿望,令我很遗憾,我想我以后会再去发展它的。

再次开发Looking已经有一个半月了。在此期间,我一直在躲避某个东西,我深深的对它有敬畏感。其实它就是LJet。再此之前,我小范围的发展了一下LJet,没有敢轻举妄动。原因有两个,一方面Looking编辑器还没有足够的功能来支持LJet的调试;另一方面,我感觉我的状态还不够好,不足以完成它。在长年累月的编程工作中,状态的起伏是不可避免的。在状态不会的时候去编写核心代码,会产生大量的废码,甚至导致在思维上的崩溃从而放弃项目。但是,我们又不能不工作,因此如何调整状态在程序生涯中是非常重要的。如何在状态不好的时候完成机械性、辅助性工作,如何在状态好的时候完成核心工作?我给这种调整起了个名字,我套用一个围棋术语“腾挪”,或者叫“洗衣机”,呵呵下围棋的朋友一定知道什么意思。但遗憾的是国内公司的管理实在是成问题,至少我呆过的公司都这样。当我腾挪的时候,往往会同领导产生分歧。解释一般是徒劳的。领导一般认为程序设计是可以定量、定时的。这真是很可笑,也很可叹。我曾经给某个领导说了个比喻,我问他:这有一堆砖头,你让我把它砌成一面墙;你可以很清楚的定量、定时,我可以作,但我不能保证它不坍塌;你认为我的工作性质和砌墙类似么?我不知道他听懂了没有?往往,为了保质量的完成项目,我只有用强硬态度。我想我的领导都会认为我是一个能干活的人,但态度实在不好。每每,我超出预计的完成任务,最后拿到的却是缩了水的奖金,呵呵,其中的委屈有谁知道。

为朋友办事,耽误了我大量的时间。那些天,我每每想起Looking都非常着急。事情总算尘埃落地了,一回到电脑前,我就产生了要开发LJet的强烈愿望。我给自己定的第一个任务是CSG(实体构造学)。为什么是csg?原因只有一个,我喜欢。第一次看到csg的boolean效果是在2年前,当时我被这种效果震住了,当时我想我一定要自己实现一个。在深入开发LJet之前,我必须要作一件事情,那就是给自己定个规矩,程序风格。LJet的代码量大约要有1万行,在我作过的系统里它不算大,但它的调试难度却肯定是最大的。举个简单的例子,如果一个球面和一个立方体相互切割,会产生800多个polygon,如果其中的几个polygon出现问题,该如何定位?我首先想到的问题是程序代码的清晰度问题。在培训新兵的时候,我经常会问他们:你编写的代码是给谁看的?我给他们的答案是:你编写的代码是给其他程序员看的;其他程序员看不懂的代码就是废码;如果一段时间后自己都看不懂的代码可真就是乱码了。呵呵,我想不会有人有兴趣看LJet的代码,我提高程序的清晰度只是为了降低调试强度。我给自己定的目标是:程序的清晰度要达到伪代码级。为了实现这个目标,我使用了大量的宏。很多C程序员可能都犯过下面这个错误:

if ( a == b )

...

写成

if ( a = b )

...

这东西就像癌症一样。在头脑不清楚的时候,非常难于发现。往往调试了几个小时后,才发现自己犯了这么个愚蠢的错误。如果在LJet里有一、两个这样的错误,我看我就别干了。

#define JEQUAL( a, b ) ( ( a ) == ( b ) )

上面的这个宏可以解决这种问题。这是个小事情,确实是小事情;这会增加键盘的敲击量,确实非常费手指头。但我不敢冒险,当所有的小事情累积在一起,就会变成大事情。当感觉到代码不可调试、不可控制时,能作的恐怕只有放弃了。

我看过很多C程序员的代码,我发现他们对指针操作情有独钟。我不是说不应该使用指针,这也是不可能的。我只是说代码的书写方式上没完没了的->,恐怕也是灾难。在LJet里的指针操作是非常巨大的,可以肯定100%的函数入口参数需要指针,95%的返回值是指针。在这里存在着一个很重要问题,代码的语意问题。一个变量或函数的名字,就应该说明它的功能,特别是函数名。不恰当的函数名甚至会把意思完全弄反了。在我编写代码的时候,经常为了使名称语意清晰、一目了然而且完全具备单向性,花费大量精力。在LJet中所有的指针操作都是用宏替代的。现在LJet已经有3000多行代码了,但在C源文件中,没有一个->操作。

在这里我举一个例子。在LJet中有一个JBSPNODE结构,它是一个二叉树节点。同时它描述了两个方向,右节点是front,左节点是back。在二叉树的维护上,我们可能更喜欢使用left和right这样的术语,而在空间分割上我们可能更喜欢front和back这样的描述。

#define JBSPNODE_LEFT( p ) ( ( p )->left )

#define JBSPNODE_RIGHT( p ) ( ( p )->right )

#define JSBNODE_FRONT JBSPNODE_RIGHT

#define JSBNODE_BACK JBSPNODE_LEFT

这样代码的语意就非常清晰了。另外宏提供了一个一动而动全局的机会。例如LJET的polygon结构是JPOLYGON,它描述顶点的方式是使用一个顶点索引数组。

#define JPOLYGON_VERTEX( p, i ) ( ( p )->indices[ i ] )

在历遍polygon的所有edge时, 经常会是下面这样:

for ( UINT i = 0; i < JPOLYGON_COUNT( pPoly ); i++ )

{

JPOLYGON_INDEX_TYPE idx1, idx2;

idx1 = JPOLYGON_VERTEX( pPoly, i );

if ( JEQUAL( i, JPOLYGON_COUNT( pPoly ) - 1 ) )

idx2 = JPOLYGON_VERTEX( pPoly, 0 );

else

idx2 = JPOLYGON_VERTEX( pPoly, i + 1 );

}

但如果把JPOLYGON_VERTEX修改一下,代码就清晰多了:

#define JPOLYGON_VERTEX( p, i ) ( ( p )->indices[ i ] % JPOLYGON_COUNT( ( p ) ) )

for ( UINT i = 0; i < JPOLYGON_COUNT( pPoly ); i++ )

{

JPOLYGON_INDEX_TYPE idx1, idx2;

idx1 = JPOLYGON_VERTEX( pPoly, i );

idx2 = JPOLYGON_VERTEX( pPoly, i + 1 );

}

当然,为了提高效率可以声明一个新的顶点索引宏。

#define JPOLYGON_VERTEX( p, i ) ( ( p )->indices[ i ] )

#define JPOLYGON_VERTEX2( p, i ) ( ( p )->indices[ i ] % JPOLYGON_COUNT( ( p ) ) )

有时候把一个函数放到什么地方也是非常伤脑筋的事情。例如一个plane分割polygon的函数,是放在plane.c里还是放在polygon.c里?这可不是个小事情,当系统有几百个函数的时候,我希望从名字上就能定位它在哪个文件里。我一般使用主动动词法。例如,刚才的那个函数名是jPlaneSplitPolygon,那它肯定在plane.c里。

尽管在代码清晰度上煞费苦心,但LJet的调试强度还是令我意外。这也难怪,在调试BSP树的时候,整个文件都充满了递归、变相递归、连表操作、树操作,而且吞吐的数据量大的惊人。另外一个原因是,LJet在开发初期几乎不可能调试。调试基本上是在有数据的情况下进行的,但LJet是个扁平的系统构建,这些元素互相纠缠,缺一不可。在没有相当的代码的情况下,根本就运行不起来,更不用说看到结果了。LJet是在写了2000多行代码后,才开始调试的。在没有调试的情况下凭空构造系统,我称之为“盲写”(呵呵,又是俚语)。我认为,一个程序员特别是C程序员的“盲写”能力,是评价其技术水准的基本指标之一,当然,这不是在评价初级程序员。

经过和LJet的一番角斗,基于bsp的csg系统终于运行起来了。但令我费解的是,在极度切割的情况下(例如,一个cub和一个sphere进行boolean操作),会出现很多斑点。尽管我进行了polygon优化和polygon合并。但情况依然没有改善。数据量实在太大了,尽管没有使用最优polygon合并算法,一次合并也会合并120多个polygon。为了这些斑点,我和LJet整整僵持了2天。最后,当我看到一篇关于T-junctions的文章后,才知道问题所在。真是谢天谢地,我还以为这次又完蛋了。下面的图片是csg的boolean sub的结果,由于csg还在收缩调整阶段,因此结果还没有达到100%的满意。

我们家的宝宝两个月后就要出生了。昨天,我把耳朵贴在我爱人的肚子上对宝宝说:等你出来后,我会在电脑里给你建一个大毫斯(big house)。这时候他,砰,踢了我一脚,看来他还是个急性子,嫌我工作太慢。呵呵。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有