Saturday, September 30, 2006

耳聪目明的我

很早就听说年岁越轻的人对高频的声音越敏感,有些人利用这个原理设计出高频的手机铃音用在课堂上。前天在diglog翻出来一个测试站点,列举了很多频率的声音,看测试的人能听到多高的频率。上面说17.7khz以上的只能被20岁以下的人听到。摆明了就是个年龄测试嘛。测试了一下,以我22岁的高龄,居然可以听到21.1khz,只有最后一个听不到,过度很明显。看来我还年轻啊。后来看一个论坛讨论这个,有人抱怨17.7khz以上就听不见了,我心里更是窃喜。真是精神鸦片啊,太舒服了。有时候在公交车上给小屁孩让座,最怕听他管我叫叔叔。我靠,简直像在我耳边放枪一样!这个是网址:http://www.ultrasonic-ringtones.com/

难道,是我的音箱暴音了?

fstream和中文路径

前几天用了一个开源的工具来设计界面(www.wxformbuilder.org)。结果发现有个bug,只要存储路径有中文字符,存储就会失败,而且没有任何提示。一下午的工作就白白丢失了。今天自己写程序也遇到这个情况,是fstream的问题。默认的情况下不支持中文路径。fopen就可以。
查到一篇文章讲这个,一定程度上能解决问题:

问题:
有时候用ifstream或ofstream打开带有中文路径的文件会失败。

解决办法:
1、使用C语言的函数设置为中文运行环境
setlocale(LC_ALL,"Chinese-simplified");

2、使用STL函数设置为系统语言环境
std::locale::global(std::locale(""));

当然选2啦!

但是据说这样之后cout又不能输出中文了。

Tuesday, September 26, 2006

缺耐力,没兴趣

编程序太需要耐力了。我特别佩服那些写程序写好几年的人。我自问做不到。前几天读到一篇博客讲兴趣的重要,说比尔盖茨最开始根本不会料想到自己能创立一个软件帝国,但是兴趣驱使着他。似乎很多成功的人最开始不一定要有什么大志,踏实地实践自己的兴趣,最后就成功了。我大概从第一次上网就在网上自称bill,其实我经常想我是真的想像bill那样对计算机执著呢,还是单纯羡慕他兜里的钱。我真的很有兴趣么?今天看到stardict.org作者的一段自我介绍,他大学挂了好多门,学了五年才毕业,但是他在大学期间写了不少程序。我感慨自己的大学念的糊涂。要是再有一次机会,我也一定要轰轰烈烈地挂上10多门课,念个五六年,完成一些自己的程序。现在觉得念完大学什么都没留下。唉,念的时候不懂,一毕业全都明白了。

现在我的3D建模程序快要写好了,收尾的工作全是体力活,已经编不动了。好程序都不是一日之寒啊,stardict已经做了3年,而我的这个3D建模程序估计要是个life-time project了。刚才用我的程序简单拽出了一个橡皮鸭子:

Monday, September 25, 2006

用Torpark来避开封锁

前天在diglog上面看到新推出了一款改版的firefox,据说可以匿名访问,更加安全。我当时没有留意。后来链接到出处的网站上,发现官方网站的链接根本打不开,我就觉得有诈。再加上这个软件的名字前面有tor三个字母,我预感这是个可以用来绕过封锁的工具。挂代理,老牛拉慢车一样地连上官方网站,下载之后,果然!这个工具把tor和firefox整合在了一起。通过tor代理,路经是随机的,走的是哪个国家,你可以通过访问google.com来观察,因为它会自动跳转到相应国家的google去,我第一次走的是加拿大,然后是荷兰还有德国。
tor本来是一个网络安全工具,可是到了咱们这里纯粹用来避开封锁,也是很无奈的事情。下面把我收集到的关于torpark的介绍集中一下。

首先是来自http://www.firefox.hk/的消息:
安全专家打造出了匿名浏览版Firefox
一些关注隐私的编程人员已经推出了一个修改版的Firefox,使得用户能够匿名浏览网页,它名叫Torpark。其创始人是一帮称为Hactivismo的计算机安全高手和隐私专家。[http://torpark.nfshost.com/]这个官方网站被封杀了,需要代理才能上。
每隔几分钟,Torpark浏览器就会作用于电脑的IP地址,使得它好像会改变一样。IP地址是一个分配给联网电脑的数字标识符。IP地址和其它数据可以用来跟踪某个用户,并且不少网站都会跟踪IP地址。
这个浏览器免费提供下载,它是移动版Firefox的一个修改版,而移动版的Firefox是一个优化版的浏览器,它能运行在USB闪存上面。
Torpark浏览器使用加密方法通过Onion路由器来发送数据,而Onion路由器是一个世界范围的网络服务,昵称是“TOR”,它以随机,模糊的方式互相转发数据。
当然,安全是有代价的。用Torpark冲浪的一个小小缺点是,与其它浏览器相比,浏览同一个网站的速度会稍微慢一点。
Torpark警告,从最后一个TOR服务器发送到网站的数据才会被加密。由于只有用户的连接才是匿名的,所以Torpark建议如用户名和密码等敏感数据在浏览有金色锁标志的网站时才使用,而金色锁标志表示这个网站使用了加密。
Torpark的用户界面与Firefox很接近,只有少数的改变。它在右下角显示了网站所见到的IP地址,此外还有一个“Flush TOR”按钮来重置一个新的随即服务器连接。


然后是官方的简介:
Torpark
torpark 欢迎来到Torpark官方网站。下载Torpark,把它放在USB闪存里,然后把USB闪存插在家庭,学校或公共场所的任何internet终端上,运行Torpark.exe,它就能自动连接到Tor网络,从而创建一条间接连接你的电脑和Tor网络中存在的电脑的秘密通道,这样你就可以匿名在网上冲浪了。Torpark价格怎样?它是免费的。
你可能听说过那些带有很多广告的次等软件,比如Anonymouzer,SafeSharing,InvisibleIP,SecretSurfer等等。他们不仅要求你付费或赞助,还需要你进行安装。那还怎么做到匿名呢?试试Torpark吧,它体积小,便携,干净,不含任何间谍软件和广告,最吸引人的一点是它是免费的。
注意
Torpark 默认是英文版的,但他包含语言文件,你可以选择你想要的语言。要想更改语言,请按照languages.txt文件中的步骤进行设置。在下一个版本里,我打算在其中加入一个工具,来帮你完成这项设置。

本来还要贴一个tor的中文手册的,但是官方网站上的中文翻译实在太差了。先算了吧。

torpark有个移动浏览的概念,我觉得挺好的。torpark应该是个绿色的程序,能安装到u盘上面。然后你可以随时随地地在异地打开它来浏览。而且它自己带有一个数据存储的空间,也就是说,你在别人电脑上运行tor你可以看到所有你收集的书签,你在别人电脑上新建的书签也可以保存在自己的有盘上,而不会对别人电脑上的数据有影响。但是这个东西是否需要电脑上安装了firefox才能运行呢?我就不知道了,不过考虑到这个软件的大小,我想答案应该是不需要。

官方网站上不去的,我来做个电驴的下载链接吧,方便找不到代理的兄弟们。

Sunday, September 24, 2006

测定网站的DNA序列

这里有个网站可以通过你网站的代码来生成一个DNA序列图。挺有意思的。地址:http://www.baekdal.com/web2dna/
不知道是不是哪天网站数据丢失了可以用这个图谱来恢复,呵呵。

试验一下light box 2

很早就发现不少网站显示图像的时候很特别,比如说wordpress.org上面显示博客模版的那个浏览器,以及blogger的一个模版站的图像显示功能。但是我一直不知道这个叫做light box,是一个javascript的图像显示代码。官方网站在这里:http://www.huddletogether.com/projects/lightbox2/
安装的过程是这样的:
首先到官方网站下载代码。
之后是修改文件中图像的路径。
然后上传到你认为比较稳定的空间去。我用的googlepages。
在博客模版中的head标记中添加下面的代码:

<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/scriptaculous.js?load=effects"></script>
<script type="text/javascript" src="js/lightbox.js"></script>
<link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen" />


在调用light box的地方使用这样的代码,增加一个rel="lightbox"的属性:

<a href="images/image-1.jpg" rel="lightbox" title="my caption">image #1</a>


如果是一组图片可以添加这样的代码,其中中括号内是组的名称:

<a href="images/image-1.jpg" rel="lightbox[roadtrip]">image #1</a>
<a href="images/image-2.jpg" rel="lightbox[roadtrip]">image #2</a>
<a href="images/image-3.jpg" rel="lightbox[roadtrip]">image #3</a>


点击这些图片可以查看到效果。

Saturday, September 23, 2006

拗口古文三则

施氏食狮史
作者:赵元任

石室诗士施氏 嗜狮 誓食十狮 氏时时适市视狮 十时 适十狮适市 是时 适施氏适市 氏视是十狮 恃矢势 使是十狮逝世 氏拾是十狮尸 适石室 石室湿 氏使侍拭石室 石室拭 氏始试食是十狮 食时 始识是十狮 实十石狮尸 试释是事

注音:
原文拼音化后如下(汉语拼音正词法建议数字以阿拉伯数字代表,故「十 shí」作「10」):

<< Shī Shì shí shī shǐ >>
Shíshì shīshì Shī Shì, shì shī, shì shí 10 shī.
Shì shíshí shì shì shì shī.
10 shí, shì 10 shī shì shì.
Shì shí, shì Shī Shì shì shì.
Shì shì shì 10 shī, shì shì shì, shī shì 10 shī shìshì.
Shì shí shì 10 shī shī, shì shíshì.
Shíshì shī, Shì shǐ shì shì shíshì.
Shíshì shì, Shì shí shì shí shì 10 shī.
Shí shí, shǐ shì shì 10 shī, shí 10 shī shī.
Shì shì shì shì.

解释:
有一位住在石室里的诗人叫施氏,爱吃狮子,决心要吃十隻狮子。他常常去市场看狮子。十点鐘,刚好有十隻狮子到了市场。那时候,刚好施氏也到了市场。他看见那十隻狮子,便放箭,把那十隻狮子杀死了。他拾起那十隻狮子的尸体,带到石室。石室湿了水,施氏叫侍从把石室擦乾。石室擦乾了,他才试试吃那十隻狮子。吃的时候,才发现那十隻狮子,原来是十隻石头的狮子尸体。试试解释这件事吧。

备注:
施氏食狮史是一篇由赵元任所写的设限文章。全文共九十二字,每字的普通话发音都是shi。这篇文言作品在阅读时并没有问题,但当用普通话朗读或者拉丁化作品时,问题便出现了。这是古文同音字多的缘故。
很多人认為赵元任是希望通过本篇,引证中文拉丁化所带来的荒谬。但是支持拉丁化的人却指出赵元任乃是国语罗马字的主要设计者,他只是举例说明拉丁化只适合於白话文,不适合於文言文。

赵元任什么人?查了一下维基:
赵元任(1892~1982)
  中国语言学家。江苏常州人。1892 年11月3日生于天津,1982年2月25日卒于美国。
  赵元任长期致力于推行国语(普通话)的工作。1922年出版《 国语留声片课本 》,1935 年出版《新国语留声片课本》,在推行国语的工作中起了示范的作用 。这两套唱片分别代表中华人民共和国建立以前推行国语的两个阶段 。他从1927 年起从事方言研究工作 ,调查过吴语( 江苏南部和浙江 )、粤语、徽州话和江西、湖南、湖北3省的方言,发表过很多调查报告和专题论文,如《现代吴语的研究》、《湖北方言调查报告》、《中山方言》、《台山语料》、《汉语称呼用词》等。其中《现代吴语的研究》是中国第一部用现代语言学方法研究方言的著作。这部书包括30处的调查材料,他与 4 个同事合著的《湖北方言调查报告》包括64处的调查材料。
  赵元任在记音方法和记音工具上也有重要的贡献。他关于记音方法的论述主要见于《音位标音法的多能性》和《语言问题》第二讲《音位论》。《音位论》说明音位观念所以存在的理由以及归纳音位应该考虑的一些原则。《音位标音法的多能性》一文阐明从语音材料归纳音位系统时可以有多种选择,答案不是唯一的。这篇论文现在已成为音位理论的经典著作之一。他设计的一套五度制标调字母,为记录和研究汉语(以及其他有字调的语言)的声调提供了准确、方便的工具。在语法方面,赵元任最重要的著作是《中国话的文法》。这部著作以直接成分分析法作为研究语法的主要方法,显然受了结构主义语言学的影响;可是作者持论通达,从来不拿事实迁就理论。总之,无论从立论的深度说,还是从影响的广泛说,《中国话的文法》都是最重要的汉语语法著作之一。

季姬击鸡记

季姬寂 集鸡 鸡即棘鸡 棘鸡饥叽 季姬及箕稷济鸡 鸡既济 跻姬笈 季姬忌 急咭鸡 鸡急 继圾几 季姬急 即籍箕击鸡 箕疾击几伎 伎即齑 鸡叽集几基 季姬急极屐击鸡 鸡既殛 季姬激 即记《季姬击鸡记》(78字)

白话译文:
季姬感到寂寞,罗集了一些鸡来养,是那种出自荆棘丛中的野鸡。野鸡饿了叫叽叽,季姬就拿竹箕中的小米喂它们。鸡吃饱了,跳到季姬的书箱上,季姬怕脏,忙叱赶鸡,鸡吓急了,就接着跳到几桌上,季姬更着急了,就借竹箕为赶鸡的工具,投击野鸡,竹箕的投速很快,却打中了几桌上的陶伎俑,那陶伎俑掉到地下,竟粉碎了。季姬争眼一瞧,鸡躲在几桌下乱叫,季姬一怒之下,脱下木屐鞋来打鸡,把鸡打死了。想着养鸡的经过,季姬激动起来,就写了这篇《季姬击鸡记》。

羿裔熠邑彝

羿裔熠①,邑②彝,义医,艺诣。熠姨遗一裔伊③,伊仪迤,衣旖,异奕矣。熠意④伊矣,易衣以贻伊,伊遗衣,衣异衣以意异熠,熠抑矣。伊驿邑,弋一翳⑤,弈毅⑥。毅仪奕,诣弈,衣异,意逸。毅诣伊,益伊,伊怡,已臆⑦毅矣,毅亦怡伊。翌,伊亦弈毅。毅以蜴贻伊,伊亦贻衣以毅。
伊疫,呓毅,癔异矣,倚椅咿咿,毅亦咿咿。毅诣熠,意以熠,议熠医伊,熠懿⑧毅,意役毅逸。毅以熠宜伊,翼逸。熠驿邑以医伊,疑伊胰痍⑨,以蚁医伊,伊遗异,溢,伊咦。熠移伊,刈薏⑩以医,伊益矣。伊忆毅,亦呓毅矣,熠意伊毅已逸,熠意役伊。伊异,噫,缢。熠癔,亦缢。

注解:
①熠:医生,据说为后羿的后裔.
②邑:以彝为邑,指居住在一个彝族聚居的地方.
③伊:绝世佳丽,仪态万方,神采奕奕.
④意:对伊有意思,指熠爱上了伊.
⑤翳:有遮蔽的地方,指伊游弋到了一个阴凉的地方.
⑥毅:逍遥不羁的浪人,善于下棋,神情坚毅,目光飘逸.
⑦臆:主观的感觉,通“意”,指对毅有好感.
⑧懿:原意为“懿旨”,此处引申为要挟,命令.
⑨胰痍:胰脏出现了疮痍.
⑩刈:割下草或者谷物一类.薏:薏米,白色,可供食用,也可入药.

Thursday, September 21, 2006

自制的电脑

今天泡网看到的,国外一个牛人在家里自己做的一个电脑。
http://www.homebrewcpu.com/
通过上面这个网站就可以看到这个对这个强人和这件事情的介绍。
http://www.magic-1.org
而通过上面这个网址你就可以登陆到架设在这个使用自制CPU运行的机器上的网站。

你也可以通过
telnet www.magic-1.org
来访问这个机器,你可以留言甚至玩玩简单的游戏,不过速度比较慢。
人要是有兴趣是很可怕的一件事。我就是兴趣太多了,和没兴趣一样,至今还一事无成。

Tuesday, September 19, 2006

堆疵及其成因

Heap Corruption应该翻译成什么好呢,翻了一下百度,没有答案,所以这里自作主张地翻译成“堆疵”。
前段时间编程的时候遇到过一次堆疵的情况,崩溃的地方是调用“new”的时候。当时觉得很奇怪,因为似乎只有内存消耗殆尽的时候new才会疵,但是我的程序没有用多少内存,怎么会疵能。后来知道是堆疵造成的。堆疵最麻烦的就是出现错误之后并不马上导致程序崩溃,而是埋下个隐患,等到再次使用出现问题的内存的时候才出现错误。这样经常会让人感到莫名其妙,因为出错的位置怎么看都是对的。好在vs2005里面可以报告出疵内存的地址,然后单步执行观察那片内存区域,这样就可以把疵点找到了。
当时在ogre3d的wiki上找到的文章讲堆疵,挺好的,翻译如下:

啥是堆?
堆是一些内存区域,这些内存区域是操作系统(比如windows)为一个应用程序保留的。堆是存放你程序所使用的数据的地方。
例如:

int main()
{
//下面这一行会分配到 2 * 4 bytes 的内存 (int 占用 4 bytes)
int a, b;
}


啥是堆疵?
出现堆疵就意味着你程序中的某个语句覆盖了堆中的一些数据。如果程序中其他的语句想接触这个数据,会造成坏的数据。
这会造成很多奇怪的错误,甚至无法解释。如果出现了堆疵,它在默认情况下会使得new操作返回NULL。
所以说,如果你出现了一些很奇怪的问题,很可能就是出现了堆疵。这个主要发生在release模式下,虽然在debug模式下程序很可能很正常。出现问题的几个可能的原因如下。

堆疵的常见原因:

悬挂指针
悬挂指针指的是这个指针所指向的实体已经被释放了。如果这时候你要使用这个指针,会产生错误。例如:

class A
{
String mName;

void A(String name) : mName(name)
{
}

void speak()
{
printf("My name is: %s\n", mName.c_str());
}
}

A *pointer1, *pointer2;

pointer1 = new A("Michael");
pointer2 = pointer1;
//现在两个指针同时指向一个object (class A)

pointer1->speak(); // this will work
pointer2->speak(); // this too

delete pointer1;
//现在删除了object, 但是两个指针仍然指向它们原先指向的内存地址

pointer1->speak(); // 这会产生错误
pointer2->speak(); // 这个也会出现错误


重复地释放
如果你释放一个指针所指向区域两次(=如果你释放一个悬挂指针),你同样会遇到奇怪的错误。这里我想引用wumpus的话:"在这种情况下,有趣的事情会更多"。例如:

A* pointer = new A("Dave");
pointer->speak();
delete pointer;

B* pointerB = new B(20);
pointerB->add(50);
pointerB->printResult();
delete pointer; //这个可能只是个笔误

//现在就要发生错误了... 找错误一定很有意思...


数组越界
如果你设定一个数组元素的下标超过了数组的大小,你就会改写在堆中数组之后区域的内存。比如:

int numbers[3];
A* pointer = new A("Hansi");
B* pointerB = new B(4);

numbers[0] = 23;
numbers[1] = 42;
numbers[2] = 5;
numbers[3] = 666; //数组越界

//现在你应经把两个指针所指向的位置给覆盖了
//在32位机上,一个指针变量的大小是 4 bytes (unsigned int) 在64位机上是 8 bytes (unsigned long)
//所以覆盖第一个指针(如果是32位系统)。会产生错误:
pointerB->add(16);
pointerB->printResult(); //这个会显示"20"
delete pointerB; //到这里还没有问题

// 这里会有数以亿计的代码... 都没什么问题,除非你要这样做:
A->speak(); //这个会疵掉,除非下面这个成立:
//第三行(int)pointer == 666;


其它在release模式下出现奇怪错误的解释

没有初始化的指针
在release模式下,变量不会自动地初始化,所以如果你没有主动地指定一个指针变量是空的话,它可能是一个随机数。如果你使用这个指针,就会产生错误:

A* pointer;

if (pointer == NULL)
pointer = new A("John");

pointer->speak(); //这个在release模式下可能不行,但是在debug下没问题
delete pointer;

int x;
printf("%i", ++x); //输出一个随机的数目


为啥写这个文章?
我在网上找了3天。我想知道我的小地图在release模式下出现"assertion failed"的原因。问题出现在一个颜色数组(ColourValue mColors[5]),我只需要5个颜色,但是我往里面放了11个进去...
希望这个能帮你加速找错的过程。好运!

我为啥翻译这个文章?
我的程序堆疵之后我摸不到头脑,索性甩手不干了。后来还是放不下心,玩不进去游戏,看不进去电视。在网上搜索了很多堆疵的资料,大多罗列了上面几种原因,当时我看的时候觉得这些原因都太傻了,我的程序已经反复看了很多遍,不可能因为这么几个弱错误疵掉。后来看了半天内存,发现问题,我在释放了一个指针所指的内存之后又去使用它。

Sunday, September 17, 2006

飞出个未来--just can't get enough futurama


最近非常着迷的一部动画片。讲了一个20世纪的倒霉蛋偶然来到未来经历的一些故事。Futurama这名称来自1939年纽约世界博览会中,通用汽车用作展示未来科技的展馆名称。在这个展馆内,以真空管制成的电视亦首次亮相。这台电视机由Philo Farnsworth发明,而他的姓也成为了这套电视剧的角色名称。
这个剧flyine.net翻译的字幕非常的地道。值得一看。
一些简介:
这部动画剧集荣获了多次艾美奖等殊荣,IMDB评分高达9.1分,该剧的制作班底是《辛普森一家/阿森一族》(The Simpsons)的原班人马。香港曾播出过,港译名为"乃出个未来"。由于播映的电视台不同,所以该剧有4季和5季两个版本,但两个版本内容相同均为 72集,只是分季划分和部分集的排列顺序不同而已。

该剧讲述了 Fry,一位20世纪的年轻人在30世纪的冒险经历。
Phillip Fry是一个纽约市的25岁的比萨饼快递员,他的生活就是每天四处奔波,1999年12月21日他偶然的被冰冻了起来, 当他醒过来时已是1000年以后,他也有了一个开始新生活的机会. 他到行星际递公司找了一份工作, 那个公司主要业务就是为宇宙的各个角落提供现代化的快递包裹服务. 他的同事包括快递运输船船长 Leela, 一个粗暴但可爱的单眼女外星人,还有一个有着很多人类的缺点的机器人Bender。Fry, Leela, Bender, 有时再加上Amy 和Zoidberg 他们冒着生命危险为宇宙的各个地方快递包裹,有时也为了减税从事一些慈善行动. 每一集都是笑料百出,一部非常不错的搞笑科幻动画。

Tor代理+firefox避开封锁


今天想上网查一查关于动画片futurama的介绍,链接指向维基百科。英文的我通过手动添加ip地址倒是可以访问。但是中文的实在上不去,气得要死。不得已装了tor来代理。把方法写一下,看看还有没有internet censorship的受害者可以用的上。
首先要安装一个叫Vidalia的软件,这是一个控制Tor的图形界面。里面也包含了tor。

http://www.vidalia-project.net/

下载之后一路回车就装好了。
然后到firefox的官方去找一个叫tor button的插件,安装。
地址在这里:
https://addons.mozilla.org/firefox/2275/

安装成功之后,firefox右下角出现一个tor enable/tor disable的label,点击可以打开或者关闭代理,非常的方便。这下就可以上中文的维基百科了。速度稍微慢一点,但是可以忍。

Tuesday, September 12, 2006

无符号的整数好危险

最近经常会犯一些比较低级的错误,比如无符号的整数问题。如果一个变量不可能取负值我就习惯声明成无符号类型,比如说数组的下标。这样做主要是为了完全利用32位字长。但是也会构成些隐患。比如这个就不会循环:

for(size_t i=vectorCount;i>-1;--i)
{
...
}

而这个循环了很多遍,如果count为0的话:
for(size_t i=count-1;i>0;--i)
{
...
}

这个要经常注意了,很容易犯。

Sunday, September 10, 2006

最近编OpenGL遇到的几个问题

最近在实现自己程序中的鼠标拾取功能,在选择范围的调整上面颇费了些周折。最后发现是对函数参数的理解有误造成的。
有个函数是

void gluPickMatrix(GLdouble x,GLdouble y,GLdouble width,GLdouble height,GLint viewport[4]);

它是用来根据当前鼠标选择区域生成一个相应的投影矩阵的。这里面头两个参数不是这个选择区域的左上角位置,而是整个区域的中心位置。如果错把它当成左上角,选择的区域就会斜向上有个偏移。OpenGL红宝书上没有区域选择的例子,只有一个通过鼠标点击选择的程序,因而没有说明这个问题。
同样,在颜色编码拾取的时候,要通过glReadPixel这个函数读取当前在back buffer中的颜色信息:
void glReadPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,GLvoid *pixels);

这里面的y实际上是整个区域的左下角位置,通常也很容易被人当成是左上角。另外,这些函数的纵坐标和OpenGL环境中的纵坐标是相反的,需要进行翻折。
另外在得到一个Call List的编号的时候,这个函数:
GLuint glGenLists(GLsizei range);

可能会产生一个错误叫GL_INVALID_OPERATION,返回的值是0。OpenGL的手册上讲,这是因为在glBegin和glEnd中调用这个函数造成的。可刚好我程序里是在一个构造函数里调用的,不可能存在glBegin和glEnd。一开始我不明白,后来google出来一个gamedev的帖子,上面说,这个错误是由于调用函数的时候OpenGL环境的状态不对。它举的例子就是还没有建立OpenGL的Context的时候就读入贴图。而我这个也是在没有Context的时候就要生成Call id,所以出错了。

解决Firefox1.5中表格内图像留白的方法

不知道Firefox是成心还是bug,反正在表格内的图像可能会自动在下面留出一块空白,而同样的代码在ie中显示是正常的。
比如说这样的一段代码:

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<img border="0" src="..."></td>
</tr>
</table>

就会出现显示的问题。
解决的办法就是在<img>标记中加上一个align属性。
比如:
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<img align="..." border="0" src="..."></td>
</tr>
</table>

仙剑奇侠传3模型文件格式分析(1)

前段时间特别喜欢看武林外传, 看到百度贴吧上面有人提议做一个武林外传的游戏。我就跟他们说美工可是大问题,建议他们从现成的游戏里面提取出来。于是我想到了研究仙剑奇侠转3模型文件的格式。把研究的过程在这里计个流水账吧。

仙剑3的资源打包在一个文件中。我对这些打包的东西没什么兴趣,而且网上已经有高手做了拆包的程序,所以就直接拿来用了。

拆包以后会出现很多的贴图文件,同时在模型文件夹下会出现很多.pol文件。估计这个就是模型文件了,因为pol很可能就是polygon的缩写。

模型文件的名称没有什么规律,但是幸运的是有一个叫box.pol的文件(在object目录下),这个很可能说明是一个方盒子的模型。方盒子模型的坐标会比较有特点,因而从这个模型着手分析可能会比较容易。

用16进制的方式打开这个文件进行初步观察,可以观察到如下的特征:

1、文件应该是没有压缩的,理由是文件中重复的字节很多,而且明显能看出很多的字符串。
2、文件的开头有个POLYd字样,应该是文件的合法性标识。打开其他的文件比较了一下,可以证实这个判断。
3、在文件开头有个Box02,可能是模型的名字。
4、紧跟模型名字有很多扩展名,作用不明,结束的位置也不清楚。
5、在这些扩展名之后紧跟了很多数据,含义未知。在这段区域中,每隔固定的长度都会出现 FFFF FFFF 字节。
6、在这段数据之后出现了一些重复的字节,之后是一个图像的文件名xiangzi.tga,应该是该模型的贴图。但是在众多资源中并没有找到这个贴图,现存疑。 这个字符串长度为11,但是在它前面没有指定这个长度的字节存在。其后又是一片空白的区域。
7、 最后存在一些数据区域。

看 到这个文件,我首先发现的就是有很多的 FFFF FFFF 字节存在。这个很可能成为分析这个模型文件的突破口。首先,一般的3D模型的坐标分量是用一个32位的float类型来表示的,而 FFFF FFFF 正好也是32位,这很可能说明这段区域包含了3D模型的顶点坐标。第二,FFFF FFFF 这个数据本身是没有意义的。在文件中它很可能是一个开始或结束的标示符。比如说,在nVidia的Demo中,3D模型就用同样的字节作为开始和结束的符 号。另外,FFFF FFFF 字节的数目刚好是16个,因为一个盒子通常有8个顶点,而16刚好是8的倍数,这也预示着这段数据同顶点的数据之间有一定的关系。但是问什么是顶点的2倍 而不正好是8呢,我感觉可能是前8个是顶点,后8个是贴图坐标。

现在已经假设 FFFF FFFF 是潜在的开始和结束标记,那么它到底是开始还是结束呢。如果假设它是起始标记,那么最后一个顶点的数据将如何结束呢?另外,不难发现,最后一个 FFFF FFFF 后面的数据和上一个 FFFF FFFF 后面的数据区别比较大:

因此我认为这里的 FFFF FFFF 不太可能是起始标记,而应该是结束的标记。

在 顶点数据区之后又很多的重复区域,其间包含了一个贴图文件名,之后又是一些重复的空闲区域。首先这个贴图文件名很可能分割了顶点数据和面的数据。这个判断 没有什么特别严谨的理由,只不过因为obj格式的模型也是这样, 才作出的估计。因为没有在这个数据区域中找到指明了文件名长短(11)的数据,所以我感觉,这个文件应该为贴图文件名分配了一段固定长的空间,而文件名的 长短就限制在这个空间之内,这就是为什么如果文件名没有占满这个空间,后面会出现很多重复空闲的区域。

初步判断在贴图文 件名之后是面的数据,除了上面提到的原因以外,还有几个因素可以肯定这个假设。首先,这些数据明显是以4个字为一个单位。这就说明,这段数据不太可能是浮 点类型的数据。我们知道,在3D模型中,每个面通常包含了组成这个面的3个顶点的编号,因而是整型的数据。虽然这里的数据是16位的,而不是整形通常的 32位,但是说不定它是用的short类型。另外一个重要的特点是,这个数据区域的开始有个明显的递增的趋势,这个也是面数据的一个特征。因为一个面的顶 点通常是连续建立的,因而编号递增变化。最后,这个区域中的数据,如果是short类型的话,刚好都不超过16,因为已知有16个顶点信息了,而这段数据 很可能就是顶点的编号。这也说明那16个点也许不是前8个顶点,后8个贴图坐标,而是一样的顶点信息。那为什么有16个呢

经过初步观察可以得到如下的结论或者说猜测:

1、文件开头有一个固定的标识。
2、之后是模型的名称和不明的数据
3、紧接着是顶点数据,每个顶点由 FFFF FFFF字节结束
4、然后是贴图文件名
5、最后是面数据,short型

到 这一步还有几个问题不太明了,第一,如果最后面是面数据的话,到底是triangle strip还是triangle list呢,不得而知。第二,为什么在第0x27那行会有一个连续的 0000 0000 呢,这个作为面数据没有什么意义。第三,每一个顶点数据包含了20个字节的信息,初步估计是3个坐标(12个字节)和2个贴图坐标(8个字节),但是如果 贴图坐标包含在顶点信息中,为什么会有16个顶点信息而非8个呢?不过看到这一步,只有写个程序试验一下了。

这个是伪代码:

打开文件,将文件指针调整到0x58的位置开始读取一个20个字节的数据
while(最后面是 FFFF FFFF)
{
以浮点的形式输出这个点的所有信息
再读取一个点
}

这个程序运行之后可以观察到,每个顶点输出的5个浮点数中,前两个浮点数是一个在0和1之间的数,而后面的3个数范围比较大。这说明前面的2个数应该是贴图坐标,而后面的是顶点的3个坐标。于是可以假定顶点数据的格式是这样的:

struct vertex
{
float u;
float v;
float x;
float y;
float z;
};

为什么把贴图坐标放在坐标之前呢,这个是比较奇怪的一点。

把 顶点取出之后,还要看一下这些顶点是不是构成一个盒子的造型。我用opengl简单地渲染了一下,确实。有了顶点的数据,需要分析面的数据了。刚才提到, 在面的数据中存在连续的顶点编号,这个是没意义的。但是这些数据只存在于数据区的前面,后面的数据都比较正常。所以我从后往前数,看到什么位置出现了不正 常。因为先前也提到,一般面的数据呈现一种递增的趋势,而且一般都从编号0开始,所以我认为0x280应该是面数据的开始。如果每个面包含3个顶点的话, 每个面应该占有6个字节,而从0x280到文件尾有60个字节。如果这些面是triangle list的话,那么刚好有10个面。但是,一个盒子应该有6个面,每个面应该由2个三角面所组成,这样算下来应该有12个面的数据,但是为什么这里只有 10个呢。另外注意到,紧接着面数据区前面的一个32位整型数据(0x27c)刚好是0xa,也就是10。会不会这个就是面的数目呢。我比对了其他的文 件,应该没错。

于是,面的数据应该是这样的:

int faceCount;
struct face
{
short v1;
short v2;
short v3;
};

按照这样的格式将模型输出成obj格式,然后用3D Max导入。发现模型是一个没有底面的盒子。这解释了为什么最后的面数据只有10个,而不是12个。因为在游戏中,为了加速渲染速度,模型需要尽量精简, 而一个盒子贴近地面的那一个面又通常不可见,因此模型中省略了底面。另外,模型中存在不少位置重复的点,现在看来是因为每个点的贴图坐标只能允许有一组, 所以如果要一个点有不同的贴图坐标,只能定义好几个点,这些点位置相同,但是贴图坐标不同。

初步拆解了这个模型文件之后,要进一步讨论它的适用型。把这个程序在其它的模型身上试验了一下,又成功拆开了j13.pol这个文件。 但是导入到3D Max之后,贴图坐标似乎有些不太正常。但是一开始我没有注意到这个问题。另外又试了几个文件,都出了问题。

如果要这个拆解的程序能够应用到更多的模型身上,必须要假设所有模型的顶点信息的起点都是0x58,并且包含贴图信息的数据区的长度是一致的。

我 打开那些不能拆解的模型文件,发现它们的顶点数据区的起始都不是ox58,也就是说我的第一个假设有问题。但是贴图信息的数据区长度似乎是一致的。另外一 个问题就是,这些问题文件包含的模型不止一个,它们都存在多个顶点数据区,面数据区和贴图数据区。这很可能说明,未知的文件头长度和模型文件所包含的模型 数目是相关的。

另外我注意到在0x8的位置上有个数,最开始我以为是指明模型名字长度的一个数值。但是比较box.pol文件,发现明显不 对。j13.pol的这个数值也是1,但是打不开的那些文件中这个数字就不是1了。这说明,这个数字表示模型中所含模型的个数。打开几个文件数了一下,证 实了这个猜想。