2008年11月25日
UNICODE表示プリム高速版

以前のバージョンだと1024 x 1024サイズのテクスチャに1024文字ずつ収納していましたが、あれだとテクスチャの読み込みに時間がかかって、電光掲示板のような用途では読み込まれる前に流れてしまったりしてしまいました。
一つの解決策はテクスチャを縦横半分に縮小してしまうことですが、これをすると文字が圧縮されて読みづらくなってしまうんですね。16x16ドットあればよさそうなものですが、テクスチャとして使用するとアンチエイリアスがかかってぼやけるため、カクカクしたままぼけるということになってしまいます。
というわけで、もう一つの解決策、512 x 512のテクスチャに縦横16文字ずつ、計256文字を収録するという方をやってみます。いままでこれをためらっていたのは、テクスチャの枚数が4倍になってしまうからです。合計163枚、アップロード料金実に1630L$を支払い(間違ったテクスチャを「大量アップロード」してしまい、途中であわててビューアを終了させたりしたので、実際はもっとかかっています><)えいやっと実行。
ついに完成しました。テクスチャ選択関数の行数が163行以上になってしまうので、これを手でUUIDのコピー、ペースト、数値の書き換えなんてやってたらいつまでかかるか分からないし、ちょっとした書き間違いを発見できなくなってしまうので、半自動でここを行うことにしました。
適当なプリムに、アップロードしたテクスチャを全部入れます。テクスチャの名前は
0000-00ff
0100-01ff
という具合に、開始コードポイント、終了コードポイントを16進で書いて、間を"-"でつなげたものです。そして以下のスクリプトを入れます。
string hexc="0123456789ABCDEF";
list textures;
string int2hex(integer x)
{
integer x0 = x & 0xF;
string res = llGetSubString(hexc, x0, x0);
x = (x >> 4) & 0x0FFFFFFF; //otherwise we get infinite loop on negatives.
while( x != 0 )
{
x0 = x & 0xF;
res = llGetSubString(hexc, x0, x0) + res;
x = x >> 4;
}
return "0x"+ res;
}
default
{
touch_start(integer total_number)
{
integer num=llGetInventoryNumber(INVENTORY_TEXTURE);
integer i;
string s;
key k;
string ks;
string st;
string ed;
list tmp;
string output;
textures=[];
llSay(0,"------"+(string)num+"-----");
for (i=0; i<num; i++){
s = llGetInventoryName(INVENTORY_TEXTURE, i);
k= llGetInventoryKey(s);
ks = (string)k;
tmp= llParseString2List(s, ["-"],[]);
st = llList2String(tmp,0);
ed = llList2String(tmp,1);
textures+=[(integer)("0x"+st)];
textures+=[ks];
}
textures=llListSort(textures, 2 ,TRUE);
integer start;
for (i=0; i<num*2; i+=2){
start=llList2Integer(textures,i);
st = int2hex(start);
ed = int2hex(start+255);
ks = llList2String(textures,i+1);
llSay(0,"if (code >= "+ st +" && code <= " + ed +" ) return [ " + st + ",\""+ks+"\" ];");
}
}
}
このプリムをタッチすれば、中に入れたテクスチャを調べてUUIDを抽出し、コードの形で喋ってくれます。チャットウィンドウでこれをコピーして、チャット行の頭の時刻とオブジェクト名をエディタで消してしまえばOK。というわけで、以下、改造版文字表示プリムのスクリプトです。 続きを読む
2008年11月22日
ソースコードを整形して色付けてくれるツール
ええと、ここのところこのブログでLSLのコード公開してたりするのですが、ソラマメってこの辺ぜんぜんうまくできてなくて、<code>タグ使うとインデントの空白が全部見えなくなるし、<pre>タグだと枚行余計な空白がついたり(これ、改行を自動的に<br \>にする設定にしてるせいなんだけどね)、とにかく見づらくなっちゃうんですね。独自のcssでも用意しておけばいいんだろうけど、なんかいい感じのものないかなーと探したら、さぶろクリエイトさんのところで素敵なものを発見。行番号をつけた上に、シンタックスカラーリングまでやってくれるすぐれもののJavaScriptです。ついでにコードをクリップボードにコピーするリンクまでつけてくれる。
サイトにアップロードして設定ってのがめんどくさいかなーと思ってたら(どこまで横着なんだww)、追記からたどった先に、Googleにアップロードした問題のファイルのURLと使い方が載ってました。これ使ってもよさそうなので、試しに採用してみました。なかなかいい感じに表示できます。
でもPHPのコードに対応させるには、PHP用の設定ファイルも必要だし、やっぱしいずれは自分のサーバにアップロードするかなあ。
サイトにアップロードして設定ってのがめんどくさいかなーと思ってたら(どこまで横着なんだww)、追記からたどった先に、Googleにアップロードした問題のファイルのURLと使い方が載ってました。これ使ってもよさそうなので、試しに採用してみました。なかなかいい感じに表示できます。
default
{
state_entry()
{
llSay(0, "こんな感じです^^");
}
touch_start(integer num);
{
llSay(0, "いや~ん");
}
}
でもPHPのコードに対応させるには、PHP用の設定ファイルも必要だし、やっぱしいずれは自分のサーバにアップロードするかなあ。
2008年11月21日
文字コードを取得する方法。
LSLでは、文字コードを得る関数は用意されていないのですが、llStringToBase64()でBase64にエンコードし、llBase64ToInteger()で数値に変換することができます。ところが、llBase64ToInteger()には問題があって、4Byteの数値になる長さ(エンコード状態で6Byte)のBase64文字列以外では正しい結果を返しません。ひらがなの"あ"をエンコードすると"44GC"となり、日本語文字列はうまく6Byteにすることはできません。実際にどういう結果になるか見てみると。
"あ"→"E3818200"
"a"→"6100F612"
といった感じの結果になります。他の文字についても調べてみると、どうやら左詰めで正しい変換結果が入り、余ったバイトには無意味な数値が入っているようです。ということは、余ったバイトは無視して左から必要なバイト数だけ取り出せば問題ないはずです。
SLでの文字コードはUTF-8で、左から順に検査すれば文字コードの長さは求まりますのでこの方法でUTF-8をUCS-2に変換して返す関数を作りました。この関数は一度に一文字しか受け付けませんので、呼び出し元で文字列を一文字ずつ取り出して使用する必要があります。
UTF-8では一文字4byteまで使う可能性があるのですが、この場合3byteまでの変換のみを行っています。4byteUTF-8に対応する場合、もう一行増やす必要があります。現在はUTF-8 3byteを越えるものは32(空白)を返すようになっています。UTF-8 3byteの範囲で、基本的な文字(16bitであらわせる範囲)は含まれていますので、実用上はこれでなんとかなるはずです。JIS第三、第四水準の文字をすべて扱いたいという場合は4byte UTF-8に対応する必要があります。
"あ"→"E3818200"
"a"→"6100F612"
といった感じの結果になります。他の文字についても調べてみると、どうやら左詰めで正しい変換結果が入り、余ったバイトには無意味な数値が入っているようです。ということは、余ったバイトは無視して左から必要なバイト数だけ取り出せば問題ないはずです。
SLでの文字コードはUTF-8で、左から順に検査すれば文字コードの長さは求まりますのでこの方法でUTF-8をUCS-2に変換して返す関数を作りました。この関数は一度に一文字しか受け付けませんので、呼び出し元で文字列を一文字ずつ取り出して使用する必要があります。
integer toUnicode(string c) //Accept one unicode character only.
{
string s= llStringToBase64(c);
integer n= llBase64ToInteger(s);
//
// n contains UTF-8 character code with 'Left to Right order'.
// We accept only 2 byte unicode m so it was only 1 to 3 byte UTF-8.
//
if ((n & 0x80000000) == 0) return ((n & 0x7f000000) >>24);
if ((n & 0xe0000000) == 0xc0000000) return ((n & 0x1f000000) >>18) | ((n & 0x003f0000)>>16);
if ((n & 0xf0000000) == 0xe0000000) return ((n & 0x0f000000) >>12) | ((n & 0x003f0000) >>10) | ((n & 0x00003f00) >>8);
return 32; // else return space code.
}
UTF-8では一文字4byteまで使う可能性があるのですが、この場合3byteまでの変換のみを行っています。4byteUTF-8に対応する場合、もう一行増やす必要があります。現在はUTF-8 3byteを越えるものは32(空白)を返すようになっています。UTF-8 3byteの範囲で、基本的な文字(16bitであらわせる範囲)は含まれていますので、実用上はこれでなんとかなるはずです。JIS第三、第四水準の文字をすべて扱いたいという場合は4byte UTF-8に対応する必要があります。
2008年11月20日
電光掲示板とかホワイトボードとか

上はJ-CASTニュースを拾ってきて右から左に表示する電光掲示板、下は各国語対応のホワイトボードです。
電光掲示板のニュース取得方法は外部サーバに置いたPHPスクリプトを使用していますので、SL内で完結したものにはできていません。もう、ほんとllHTTPRequestの使えなさって><…
ところで、電光掲示板は右から左に文字が流れていくのですが、当初のスクリプトではあちこちの文字がバラバラと切り替わるのが表示されて、結果的に非常に読みにくいものになっていました。実質読めないといってもいい感じ。下のホワイトボードみたいに静的なものならそれでもいいんですけど、動かしちゃうと、一部の文字が先に左に行き、一部は少し遅れってのがあちこち重なると、まじで読めません。表示部分の抜本的な対策が必要でした。 続きを読む
2008年11月17日
UNOCODEなんでも表示プリム
黒板の作者さんが、MITライセンスで配布したのかどうか、LSLコンベンションが終わってしまった今では分からないのですが、とりあえず探しても見つからなかったので…
やってみたらできちゃった
。

日本語

韓国語

中国語
私偉い。天才かもwww 続きを読む
やってみたらできちゃった


日本語

韓国語

中国語
私偉い。天才かもwww 続きを読む
2008年11月16日
黒板のスクリプトって…
すっかりおなじみになった黒板ですが、あれって、どうやってるのか考えると眠れなくなります。
XyzzyTextをもとにして日本語対応してるらしいけど、どうやって??
XyzzyTextの基本を少し。
一つの立方体プリムをぐにぐに変形させて、正面に立方体の6面のうち5面を並べます。この段階で「ぎゃー」っていいたくなるけど、これは序の口。
文字列を分解して頭から一文字ずつ表示するわけだけど、ここに文字を表示するためには、文字のテクスチャが必要です。一文字ずつテクスチャにしてたら、英数記号だけでもアップロード料金が100L$くらいかかっちゃうので、一枚のテクスチャに文字を並べたものを作ってるわけですね。テクスチャの表示位置や倍率を調整して、目的の文字だけを目的の面に表示するわけです。
1プリムに10文字表示可能なバージョンはまたえらいことをしていて、ありうる二文字の組み合わせを全部並べたテクスチャを作っているのですが、それはまた別の話。
英数字だけなら、そこそこ高解像度にしてもテクスチャ一枚とかでいけちゃうんです。実際はテクスチャの効率も考えて、あまり大きくないテクスチャ数枚に分けているようですが。
で、日本語を出すとなると、数千文字は必要なわけです。
SLで使えるテクスチャの最大サイズは1024×1024。
一文字のサイズを32×32ドットにすれば、1024文字一つのテクスチャに入ります。
JIS第一水準は約3000文字ですから、三枚のテクスチャでほぼカバーできます。
これくらいなら許容範囲ですけど、問題はLSLの文字コードがユニコードだってことなんですね。ユニコードって、日本と中国と韓国の漢字を全部並べていて、JIS漢字コードとは全然互換性ないんです。日本で分類されたJIS第一水準とかは適当に統合漢字の中にちらばっています。だからユニコードのままでテクスチャ作ろうとしたら漢字だけで約二万字、テクスチャ20枚位作ってアップロードする必要があります。
じゃあ、JISコードの配列のテクスチャ作ってユニコードからJISコードに変換すればいいんじゃないかと思うでしょうが、ユニコードからJISコードへの変換テーブルのサイズがでっかくなっちゃいます。Integerのリストを作ったとして、32ビットの数字を2万個入れるリストが必要なので、8万バイト、ってことは約72kb必要なわけです。Monoになって拡張されたといえ、スクリプト一個の使えるメモリは最大64kbですから、これじゃ足りません。
いったいどうやってるんだろう…
XyzzyTextをもとにして日本語対応してるらしいけど、どうやって??
XyzzyTextの基本を少し。
一つの立方体プリムをぐにぐに変形させて、正面に立方体の6面のうち5面を並べます。この段階で「ぎゃー」っていいたくなるけど、これは序の口。
文字列を分解して頭から一文字ずつ表示するわけだけど、ここに文字を表示するためには、文字のテクスチャが必要です。一文字ずつテクスチャにしてたら、英数記号だけでもアップロード料金が100L$くらいかかっちゃうので、一枚のテクスチャに文字を並べたものを作ってるわけですね。テクスチャの表示位置や倍率を調整して、目的の文字だけを目的の面に表示するわけです。
1プリムに10文字表示可能なバージョンはまたえらいことをしていて、ありうる二文字の組み合わせを全部並べたテクスチャを作っているのですが、それはまた別の話。
英数字だけなら、そこそこ高解像度にしてもテクスチャ一枚とかでいけちゃうんです。実際はテクスチャの効率も考えて、あまり大きくないテクスチャ数枚に分けているようですが。
で、日本語を出すとなると、数千文字は必要なわけです。
SLで使えるテクスチャの最大サイズは1024×1024。
一文字のサイズを32×32ドットにすれば、1024文字一つのテクスチャに入ります。
JIS第一水準は約3000文字ですから、三枚のテクスチャでほぼカバーできます。
これくらいなら許容範囲ですけど、問題はLSLの文字コードがユニコードだってことなんですね。ユニコードって、日本と中国と韓国の漢字を全部並べていて、JIS漢字コードとは全然互換性ないんです。日本で分類されたJIS第一水準とかは適当に統合漢字の中にちらばっています。だからユニコードのままでテクスチャ作ろうとしたら漢字だけで約二万字、テクスチャ20枚位作ってアップロードする必要があります。
じゃあ、JISコードの配列のテクスチャ作ってユニコードからJISコードに変換すればいいんじゃないかと思うでしょうが、ユニコードからJISコードへの変換テーブルのサイズがでっかくなっちゃいます。Integerのリストを作ったとして、32ビットの数字を2万個入れるリストが必要なので、8万バイト、ってことは約72kb必要なわけです。Monoになって拡張されたといえ、スクリプト一個の使えるメモリは最大64kbですから、これじゃ足りません。
いったいどうやってるんだろう…
2008年11月03日
ラジオ:曲名の日本語表示

成功!!
こちらのPHPコードをほぼそのままパクって、文字コード変換を仕込みました。
<?php
if ( isset ($_GET['url']) ) {
$url=$_GET['url'];
$port=$_GET['port'];
} else {
return;
}
$fp = @fsockopen ($url, $port, $errno, $errstr,1);
header ("Content-type: text/html; charset=utf-8");
if (!$fp)
{
echo "Connection refused"; // Diaplays when sever is offline
}
else
{
fputs($fp, "GET /7.html HTTP/1.0\r\nUser-Agent: Mozilla\r\n\r\n");
while ( !feof($fp) )
{
$info = fgets($fp);
}
$info = str_replace ( '</body></html>' , "" , $info );
$split = explode ( ',' , $info);
if ( empty ( $split [6] ) )
{
echo "The current song is not available"; // Displays when sever is online but no song title
}
else
{
$title = str_replace ( '\'' , '`' , $split[6] );
$title = str_replace ( ',' , ' ' , $title );
$title = mb_convert_encoding( $title , "UTF-8" , "auto" );
echo "$title"; // Diaplays song
}
}
?>
こんな感じ。プレイリストから抽出したshoutcastのストリーミングサーバのURLから、"http://"を削除したものを?url=で投げてやれば曲名を返します。port番号は別個に指定しなくても、urlに含まれる形(xxx.xxx.xxx.xxx:yyyy)とかでいいみたい。
header("Content-type: text/html; charset=utf-8");
でUTF-8で出力する事を明示して、
$title = mb_convert_encoding($title, "UTF-8", "auto");
で変換しただけ。単機能のCGIなので、出力はHTMLとかXMLとかじゃなくていいやと、ベタテキストで出力しています。これをLSLから呼んで、llSetTextで表示しちゃえばOK。
ただ、bodyをそのまま表示すると改行コードだかEOFだか変なゴミが出ちゃうので
llStringTrim(body,STRING_TRIM)でホワイトスペースを削除しています。
しかし、なんだこの曲ww
誰だこのラジオ流してるの。