2011-11-13

Objective-C 執行緒同步 ( NSCondition )

撰寫多執行緒程式都會碰到資料同步問題,objc 在這邊可使用 NSCondition 來達成,底下是 NSCondition 與 C#,C++用法比較:
Objective-C 的用法如下: 
NSCondition* pLock =[NSCondition new];
[pLock lock];

    //...
}
[pLock unlock];
[pLock release];



C++ 的用法如下:  HANDLE m_Handle =CreateSemaphore( NULL, 1, 1, L"SemaphoreName" );

WaitForSingleObject( m_Handle, nWaitTime );

    //...
}
ReleaseSemaphore( m_Handle, 1, NULL );
CloseHandle( m_Handle );



C# 的用法如下: 
private static AutoResetEvent m_Handle =new AutoResetEvent( true ); 
m_Handle.WaitOne(); 

    //...
}
m_Handle.Set();
其它有關執行緒同步的文章參考這篇

Objective-C 多執行緒 ( NSOperationQueue )

objc 的 NSOperationQueue 跟 Thread Pool 很類似,用起來也非常的容易,這邊把 Operation 看成 Thread Task , Operation Queue 則看成 Thread Pool 。
底下舉一個簡單的例子:主執行緒使用另一個執行緒印出 “i am taskClass” 字串
//taskClass 負責印出 “i am taskClass” 字串 
@interface taskClass : NSOperation {
}
//main 繼承於 NSOperation, 執行緒啟動時將執行這個函數
-(void) main;
@end


 
@implementation taskClass
-(void) main
{
    NSAutoreleasePool* nsap =[NSAutoreleasePool new];
    NSLog(@"i am taskClass");
    [nsap drain];
}
@end



//主程式
int main (int argc, const char * argv[])
{
    NSAutoreleasePool* nsap =[NSAutoreleasePool new];

    //建立 NSOperationQueue
    NSOperationQueue* nsoq =[NSOperationQueue new];

    //設定最多同時執行的執行緒數
    [nsoq setMaxConcurrentOperationCount:10];

    //建立 taskClass
    taskClass* myTaskClass =[taskClass new];

    //將 taskClass 丟給 NSOperationQueue
    //NSOperationQueue 會分配執行緒去執行 taskClass 的 main 函數

    [nsoq addOperation:myTaskClass];

    //等待 Queue 裡所有的 Operation 執行完畢
    [nsoq waitUntilAllOperationsAreFinished];
   
    [myTaskClass release];
   
    [nsoq release];
   
    [nsap drain];
}
此外,假如 Operation 執行的程式屬於無限迴圈,則在主程式要結束時,可用 cancelAllOperations 函數來通知 Queue 裡的 Operation 中斷迴圈並結束程式,舉例如下:
@interface taskClass : NSOperation {
}
-(void) main;
@end


@implementation taskClass
-(void) main
{
    //檢查 Cancelled 是否為 true, 否則繼續執行
    while ([self isCancelled]==false) {
        //迴圈工作... 
    }
}
@end


//主程式
int main (int argc, const char * argv[])
{
    NSAutoreleasePool* nsap =[NSAutoreleasePool new];

    NSOperationQueue* nsoq =[NSOperationQueue new];
    [nsoq setMaxConcurrentOperationCount:10]; 

    taskClass* myTaskClass =[taskClass new]; 
    [nsoq addOperation:myTaskClass];


    //通知所有執行中的 Operation 中斷執行
    [nsoq cancelAllOperations]; 

    [nsoq waitUntilAllOperationsAreFinished];

    [myTaskClass release];

    [nsoq release];

    [nsap drain];
}

2011-11-07

Objective-C dot(.) 與 arrow(->)

objc 在這裡跟 C++ 一樣,可以透過 (.) 及 (->) 來存取類別(class) 的成員 “變數”,但在 objc 的 property 機制影響之下,有些地方可能會搞不清楚。底下做了一些整理。
假如目前有一個 class 如下:
定義:
@interface classA :NSObject{
@private
    int val;
@public
    int pubVal;
   
}

-(void) initVal: (int) newVal;
-(void) printVal;
@end

實作:
@implementation classA
@synthesize val, pubVal;
-(void) initVal:(int)newVal
{
    val =newVal;
}

-(void) printVal
{
    NSLog(@"val=%d", val);
}

@end
dot(.) 與 arrow(->) 的操作:
classA* bbb =[classA new];
//dot notation
//設定值

bbb.pubVal =5;        //dot 透過 property 機制存取, 同等於 [bbb setPubVal:5];
bbb.val =6;              //dot 透過 property 機制存取, 同等於 [bbb setVal:6];
(*bbb).pubVal =7;    //dot 可直接存取 @public member
//(*bbb).val =8;      //Error, 不是 @public member
//取值
int tmpVal;
tmpVal =bbb.pubVal;      //tmpVal =7,同等於tmpVal =[bbb pubVal];
tmpVal =bbb.val;           //tmpVal =6,同等於tmpVal =[bbb val];
tmpVal =(*bbb).pubVal;  //tmpVal =7
//tmpVal =(*bbb).val;  //Error, 不是 @public member
//arrow notation
//設定值

bbb->pubVal =5;     //arrow 可直接存取 @public member 
//bbb->val =5;             //Error, 不是 @public member
//取值
tmpVal =bbb->pubVal;   //tmpVal =7
//tmpVal =bbb->val;     //Error, 不是 @public member

//使用函數操作
//設定值

[bbb setPubVal:5];  //setPubVal 函數由 property 機制自動產生
[bbb setVal:6];        //setVal 函數由 property 機制自動產生
//取值
tmpVal =[bbb pubVal];
tmpVal =[bbb val];

[bbb initVal:10];
[bbb printVal];

[bbb release];
執行的結果為:
val=10

2011-11-04

Objective-C SEL ( Selector )

暫時先“想像” objc 的 SEL 就像是 C 的函數指標(但在 objc 裡另外還有 IMP, 其定義同等於 C 的函數指標),例如現在有一個類別如下:
定義:
@interface classA :NSObject{
@private
    int val;
}

-(void) initVal: (int) newVal;
-(void) printVal;
@end


實作內容:
@implementation classA
-(void) initVal:(int)newVal
{
    val =newVal;
}

-(void) printVal
{
    NSLog(@"val=%d", val);
}

@end
SEL ( Selector ) 的操作範例如下:
id ppp =[classA new];
[ppp initVal:99];
SEL printPP1 =@selector(printVal);
SEL printPP2 =NSSelectorFromString(@"printVal");

if ([ppp respondsToSelector:printPP1])
    [ppp performSelector:printPP1];

if ([ppp respondsToSelector:printPP2])
    [ppp performSelector:printPP2];

[ppp release];
其中的 printPP1, printPP2 皆指向 classAprintVal 函數, 因此 SEL 的產生方式可以有 @selector 或是 NSSelectorFromString 兩種。
執行的結果為:
val=99
val=99

2011-07-23

freeimage + DDS ( nvidia texture tools )

前段時間遇到一個需求是把 bmp, png 轉成 dds 格式,且在過程中自動把 bmp, png 的圖形放大到二的冪次方大小。 

後來使用兩套工具解決這個需求:
  • Freeimage 來做圖檔的讀取工具,
  • nvidia 提供的 texture tools 來做圖檔的格式轉換及壓縮工具,
freeimage library 提供方便快速的圖形讀取和寫入功能,但對(DirectX Surface)DDS 格式的支援只有到讀取的程度,致使無法儲存 DDS 格式檔案。texture tools 則是提供簡單的圖檔讀取及完整的 DDS 檔案格式支援,其 DDS 支援的部分剛好可以補足 freeimage 在支援 DDS 格式不足之處。

底下為把圖片轉成 DDS 格式的簡單實作:

1) 載入圖檔  (src.jpg):
std::string filename("src.jpg");
FREE_IMAGE_FORMAT fif =FreeImage_GetFileType( filename.c_str() );
FIBITMAP* dib =NULL;
dib = FreeImage_Load( fif, filename.c_str() );

2) 將圖檔轉成 RGBA 32bit 格式:
FIBITMAP* dib32 = FreeImage_ConvertTo32Bits(dib);

3) 取得圖形資料:
int nWidth =FreeImage_GetWidth(dib);
int nHeight =FreeImage_GetHeight(dib);
BYTE* pSrcDIB =FreeImage_GetBits(dib32);

4) 設定欲壓縮的圖形資料格式
nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(nvtt::TextureType_2D, nWidth, nHeight);
inputOptions.setMipmapData(pSrcDIB, nWidth, nHeight);
inputOptions.setFormat(nvtt::InputFormat_BGRA_8UB);

inputOptions.setWrapMode(nvtt::WrapMode_Clamp);
inputOptions.setAlphaMode(nvtt::AlphaMode_Transparency);

inputOptions.setNormalMap(false);
inputOptions.setConvertToNormalMap(false);
inputOptions.setGamma(2.2f, 2.2f);
inputOptions.setNormalizeMipmaps(false);

inputOptions.setMipmapGeneration(false);

5) 設定 DDS 檔格式,此處使用的是 DXT1a 格式
nvtt::CompressionOptions compressionOptions;
compressionOptions.setFormat(nvtt::Format_DXT1a);
//compressionOptions.setQuality(nvtt::Quality_Fastest);
//compressionOptions.setQuality(nvtt::Quality_Normal);
//compressionOptions.setQuality(nvtt::Quality_Production);
compressionOptions.setQuality(nvtt::Quality_Highest);

6) 壓縮並儲存成 DDS 檔 (dst.dds):
nvtt::OutputOptions outputOptions;
outputOptions.setFileName("dst.dds");

nvtt::Compressor compressor;
compressor.enableCudaAcceleration(false);
compressor.process(inputOptions, compressionOptions, outputOptions);

7) 最後不忘刪除記憶體:
FreeImage_Unload(dib32);
FreeImage_Unload(dib);

2011-05-15

TextEdit 上字型會自動更換的問題

中文版 OSX SnowLeopard 上 TextEdit 的預設字型是 Heiti TC,它在英文字的呈現上不易閱讀,可以在 TextEdit --> Preferences --> Font 的地方更改成比較容易閱讀的字型,但惡夢來了,文字輸入的過程中若輸入中文之後再輸入英文,字型會被自動改回 Heiti TC,但 Preferences --> Font 裡仍然是自己設定的字型,網路上也 有人 提出一樣的問題這邊 也提到...
Snow Leopard 增加了一項自作聰明的新設計-會偵測目前使用的輸入法切換字體,也就是,就算你切換到英文或其他語系,但是當你在「文字編輯」(TextEdit)等軟體中,只要是使用繁體中文語系輸入法打字,輸出的文字就會自動切換以 Heiti TC 顯示…。

個人認為...

其實是因為設定的字型無法顯示目前輸入法所輸入的字,所以為了顯示該字,才又跳回預設的字型。

發生在我這裡的情況是將字型設定成 Consolas 之後,輸入中文再輸入英文,英文字的字型就變成 Heiti TC,原因是 Consolas 不是 Unicode 字型 的 unicode set 裡不包含中文字,無法顯示中文,後來使用雅黑-Consolas混合字體就不再出現字型被切換回 Heiti TC 的問題了。

2011-05-01

Unity 3D 將程式部署在 iOS 4.3.2 (iphone4) 上執行

最近建立了在 ios 上開發 unity3d 程式的環境,建立過程大概敘述如下:

1) 作業系統 OSX 10.6.7 + Xcode 4.0,手機部分為 iOS4.3.2。
2) 參考這個連結,設定 Xcode (http://www.alexwhittemore.com/?p=398)。
    連結內前兩項的意思是:
        a) 對 iOS4.3.2 進行 JB 動作,完成之後安裝 AppSync for 4.0+ 軟體。
        b) 在 mac 上建立一個 Certificate:
            i) 在功能表執行 Keychain Access --> Certificate Assistant --> Create a Certificate 。
            ii) Name 的部分設定為 "iPhone Developer"。
                Identity Type 為 Self Signed Root。
                Certificate Type 為 Code Signing。
                將 Let me override defaults 打勾。
            iii) Serial Number : 1
                Validity Period (days): 3650
            iv) 一直按下一步到底。

3) 完成 2) 所提供連結之後續步驟(3~7)。
    步驟到這邊就已足夠用 Xcode 開發一般的 iOS Project 。

4) 在 unity3D 下建立 project 並 switch platform 至 ios。
5) Player Settings --> Other Settings --> Bundle Identifier 設定為 my.company.[project name](注意前段 my.company 部分與 3) 中的 script 一致。 

6) Target Platform 設定為 Universal armv6 + armv7
7) SDK Version 設定為 iOS latest
8) Target iOS Version 設定為 4.2
9) 按下 Build And Run 之後將自動生成 Xcode project 且被 Xcode 開啟。
10) 在 Xcode 下重覆 3) 步驟。
11) 在 Build Settings 頁面下的 Code Signing 項目裡的 Code Signing Identity 全設定成 Any iOS SDK : Don't Code Sign。

12) Summary 的 Deployment Target 改成 4.3
13) 按下 Run (在 Stop 右方的下拉式功能表選擇 Unity-iPhone | [device name] (4.3.2)

2011-04-26

NodeJS 在 mac 上的安裝, 刪除

node.js 可以在 http://nodejs.org/ 取得。
解壓縮之後,在 nodejs 目錄下輸入下列指令:
1) ./configure
2) make
3) sudo make install

刪除 nodejs 的指令:
1) make uninstall

update 20110429:
或是透過 mac port 也可以安裝 nodejs。
1) sudo port install nodejs 即可

透過 mac port 刪除 nodejs 的指令
1) sudo port uninstall nodejs

但在安裝前須先安裝 mac port (http://www.macports.org/install.php)。

mac port 一些指令:

1) port selfupdate 檢查更新
2) port installed 檢查已安裝項目
3) port search [item] 搜尋可安裝項目 (例 port search nodejs)
4) port install [item] 安裝項目
5) port uninstall [item] 反安裝項目

2011-03-08

Unity 3D 非對稱投影

透過修改投影矩陣可以達到非對稱的透視投影效果,下圖是正常的透視投影結果:

正常的透視矩陣所投影的結果

若是我們直接移動攝影機位置讓這三個圖柱向左偏移(攝影機向右偏),將得到結果如下圖:

圖柱向左移動但扭曲變形

雖然圓柱向左偏移,但因為透視投影矩陣的關係產生扭曲變形的結果。若要使圓柱偏移且不扭曲變形,以此例在 Unity3D 而言,修改投影矩陣的 m02 元素即可:

修改過的投影矩陣


2011-02-24

Unity 3D 和 CodePage 950

在 Unity 裡使用 System.Text.Encoding 功能時,如果 CodePage 設定成 950 (BIG-5) 的話,各位可能會發現,在編輯器下執行沒有問題(甚至預設的 CodePage 就是 950),但將程式編譯為執行檔之後執行會得到類似的訊息:

System.NotSupportedException: CodePage 950 not supported
  at System.Text.Encoding.GetEncoding (Int32 codepage) [0x00000] in <filename unknown>:0
  ...

而且預設的 CodePage 變成 UTF8 @@

參考這篇討論,原因是少了幾個動態連結檔,將 C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity 目錄下的 I18N.dll 和 I18N.CJK.dll 複製到編譯結果的目錄裡即可排除上述情況(與 System.Data.dll 同目錄)。