2008年9月7日星期日

3DS 文件格式


於 Mesh Factory 下載模型

完成了 3DS 文件的加載器已有多個星期,到今天才有點時間張貼出來。
其實 3DS 已是一個很古老的格式,它的材質參考路徑僅支援 8.3 格式, 是 Dos 年代的產物。但基於這格式的廣泛流傳和結構簡單,因此儘管 Collada 是大勢所趨,我還是決意使用它作為引擎裡的第一個模型加載器。這樣我就可以快點進行其他方面的進修,待其他模組有了原型以後才加入其他加載器。

我的實作很基本,只支援頂點,索引,標準材質和貼圖;所有的法線得自行計算,雖然我已對 Smoothing Group 加以支援,但有些位置的效果還是顯得不平滑。相信最好的辦法都是直接從 DCC 工具中讀入法線向量,可惜 3DS 沒有這資訊。另外有好一些物體的位置錯了,似乎純粹讀入頂點位置並不足夠,還以為 3DS 格式用不著轉換矩陣。

最後我當然把加載器和之前設計好的資源管理和漸進裝載模組整合在一起,當中的細節留待日後再作詳述。

2008年9月3日星期三

谷歌瀏覽器 Chrome


作為全球資訊網的龍頭大哥谷歌 Google, 推出自家的瀏覽器是遲早的事. 這位新晉的瀏覽器名叫 Chrome, 還是 Beta 階段. 它的宣傳標題是 "一個方塊,無所不包", 正合谷歌一貫使用網上平台的理念; 而一個好的瀏覽器將會是這革命的催化劑.

試用後感覺良好, 介面簡單反應夠快. 而我最喜歡的就是把菜單/按鈕等等介面壓縮成不到80圖素的垂直空間裏, 就連標題棒都省去了.

至於內部構和設計理念, 谷歌用了漫畫形式展示出來. 從中得知谷歌為了避開記憶體洩漏以及安全性的問題, 挑選了一個 Tab, 一個進程 (Process) 的設計. 這有點兒走回頭路的感覺, 但不失為簡單快捷的方案; 何況 Chrome 創建新 Tab 的速度奇快, 沒有半點被創建進程的開銷所拖慢.

目前 Chrome 和 Firefox 相比下還缺少一眾好用的插件, 但相信新一輪瀏覽器之戰又開始了.

2008年8月8日星期五

C 函數的新發現

以往如需要從程式的主迴圈讀取鍵盤的輸入去決定是否退出程式, 我會用另一個執行緒去乎叫 std::cin 或 getchar(), 因為它們都是阻塞 (blocking) 的.

原來我一直忽略了 kbhit() 的存在! 有了它, 以上的問題就可簡化為:

#include

int main() {
while(true) {
// Poll the console without blocing, return true if there is
// a keystroke waiting in the buffer.
if(kbhit()) {
if(getchar() == 'q')
break;
}

// Do something usefull
// ...
}
return 0;
}

可惜 kbhit() 不是標準 C 裡的成員, 在若干平台上我們得自行實踐. 以下編碼出於這裡

#include <sys/select.h>

int kbhit(void)
{
struct timeval tv;
fd_set read_fd;

/* Do not wait at all, not even a microsecond */
tv.tv_sec=0;
tv.tv_usec=0;

/* Must be done first to initialize read_fd */
FD_ZERO(&read_fd);

/* Makes select() ask if input is ready: 0 is the file descriptor for stdin */
FD_SET(0, &read_fd);

/* The first parameter is the number of the largest file descriptor to check + 1. */
if(select(1, &read_fd, NULL/*No writes*/, NULL/*No exceptions*/, &tv) == -1)
return 0; /* An error occured */

/* read_fd now holds a bit map of files that are
* readable. We test the entry for the standard
* input (file 0). */
if(FD_ISSET(0, &read_fd))
/* Character pending on stdin */
return 1;

/* no characters were pending */
return 0;
}

2008年7月22日星期二

共享算術運算子

每當實作一些有關數學的類別 (如 Vector, Matrix, Point, Size 等...) 時,加減乘除等運算子都會時常出現。讓我們試試把共同的地方提煉成基類別:

template<int N>
class Tuple {
public:
Tuple operator+(const Tuple& rhs) const
{
Tuple result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}

float data[N];
};

class Vec3 : public Tuple<3> {
public:
Vec3(float x, float y, float z) {
data[0] = x; data[1] = y; data[2] = z;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
// Compilation error: v1 + v2 is returning Tuple but not Vec3
Vec3 v3 = v1 + v2;
return 0;
}

大家不用擔心那個回圈會為性能帶來負面影響,編譯器懂得把它優化 (我已在VC2008上證實了這一點)。
但由於運算子的返回型態出了問題,我們作出以下嘗試:

template<int N, class R>
class Tuple {
public:
R operator+(const Tuple& rhs) const
{
R result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}

float data[N];
};

class Vec3 : public Tuple<3, Vec3> {
public:
Vec3(float x, float y, float z) {
data[0] = x; data[1] = y; data[2] = z;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
Vec3 v3 = v1 + v2;
return 0;
}

非常好。不過還可以更好哩:

template<int N, class R, class U>
class Tuple : public U {
public:
R operator+(const Tuple& rhs) const
{
R result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}
};

struct _Vec3Union {
union {
struct { float x, y, z; };
float data[3];
};
};

class Vec3 : public Tuple<3, Vec3, _Vec3Union> {
public:
Vec3() {}
Vec3(float x_, float y_, float z_) {
x = x_; y = y_; z = z_;
}
};

struct _SizeUnion {
union {
struct { float width, height; };
float data[2];
};
};

class Size : public Tuple<2, Size, _SizeUnion> {
public:
Size() {}
Size(float w, float h) {
width = w; height = h;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
Vec3 v3 = v1 + v2;

Size s1(1, 2);
Size s2(2, 3);
Size s3 = s1 + s2;

return 0;
}

這可讓 Vec3 組成的 x, y 和 z 用方便的形式去存取。

儘管以上的提示未必有多大用途,還望它能加強大家對 C++ 的了解。

2008年7月14日星期一

尼采的神奇 Functor

尼采給我的題目...不如在此發表答案.


#include <iostream>

typedef int (*Functor)();

// Recursive type template that generate a
// compile-time list of Functor using inheritance
template<size_t N> struct Unit : public Unit<N-1> {
Unit() : mFunctor(&Unit<N>::fun) {}
static int fun() { return N; }
Functor mFunctor;
};

// Partial specialization to end the recursion
template<> struct Unit<0> {
Unit() : mFunctor(&Unit<0>::fun) {}
static int fun() { return 0; }
Functor mFunctor;
};

// Partial specialization to reduce recursive template complexity
template<> struct Unit<256> : public Unit<255> {
Unit() : mFunctor(&Unit::fun) {}
static int fun() { return 256; }
Functor mFunctor;
};

// Partial specialization to reduce recursive template complexity
template<> struct Unit<512> : public Unit<511> {
Unit() : mFunctor(&Unit::fun) {}
static int fun() { return 512; }
Functor mFunctor;
};

// A compile-time maximum count of Functor
static const size_t cMaxN = 768;

typedef Unit<cMaxN> List;
static const List cList;

// A function that return a Functor that return i.
// In other words, it selects from a list of compile-time functors
// base on the run-time parameter i.
Functor getFunctor(int i) {
{ // We have the assumption that Unit<> struct are packed tightly together in cList
typedef const char* byte_ptr;
byte_ptr functorAddres1 = byte_ptr(&(static_cast<const Unit<1>*>(&cList)->mFunctor));
byte_ptr functorAddres2 = byte_ptr(&(static_cast<const Unit<2>*>(&cList)->mFunctor));
(void)functorAddres1; (void)functorAddres2;
assert(functorAddres2 - functorAddres1 == sizeof(Functor));
}
assert(i <= cMaxN);
return *((Functor*)(&cList) + i);
}

int main() {
for(size_t i=0; i<=cMaxN; ++i) {
Functor f = getFunctor(i);
std::cout << f() << std::endl;
assert(f() == int(i));
}

return 0;
}


Win少, 我的答案正確嗎?

我認為最好的 Pdf 閱讀器



不知不覺 Adobe Acrobat Reader 已經出到第九代了.
在每一次更新的時候, 大家有沒有想過其實可能更有好的選擇呢?
我有一個好推薦, 它就是 Foxit Reader.

Foxit 的描繪品質和速度與 Acrobat 不分上下, 但記憶體用量和啟動時間就遠勝 Acrobat; 更不用安裝 , 細小可攜 (僅一個 5MB 的可執行檔).

以下的測試是基於網上的一篇文章:


Adobe Reader 9 Foxit Reader 2.2
啟動須時 (秒) 21 7
記憶體用量 74MB 25MB

縱然 Foxit 缺少某些(許多人都不常用的)功能, 它帶來的眾多好處立即使我把 100 多MB的 Acrobat Reader 從系統中撤除.

2008年7月2日星期三

程式語言效率大比拼

有天正在尋找腳本語言的時候無意間發現這個名為 The Computer Language Benchmarks Game 的網站.
那裡的 benchmark 數據覆蓋多達 76 種語言加編輯器的組合, 19 個試驗程式(大部分還有提供源始碼).
不過, 儘管那裡提供的資料準確無誤, 它所包含的參考價值仍然有限. 問題在於那些試驗程式都過於細小, 以及只有單一的功能. 在一個複雜的應用程式 (如 3D Game Engine), 有大量不同種類的資料需要處理, 因此記憶體的存取很容易成為瓶頸.
一個好例子就是 Java/.Net. 有數據顯示 Java/.Net 可以快過 C/C++, 不過當你走出試驗程式返回現實, 你會感到 Java/.Net 總是慢半拍的.

其實那網站的第一段就表明了:
Benchmarking programming languages?
How can we benchmark a programming language?
We can't - we benchmark programming language implementations.

How can we benchmark language implementations?
We can't - we measure particular programs!