Tag Archives

40 Articles

セカンドライフでタッチしたプリムの情報を取得する

はじめに

セカンドライフでプリムをクリックしたときにどこの位置を触ったのか、
といった情報をずらーっと出してみるサンプルです。

サンプル

とにかくいろいろ情報を取得してみます。
全部表示すると訳が分からなくなるのでちょっと表示しています。
ちなみに、テクスチャのUUIDに関しては権限を持っていないと取得できません。

integer	detected_link		= 0;
integer	detected_face		= 0;
vector	detected_touch_pos	= ZERO_VECTOR;
vector	detected_touch_uv	= ZERO_VECTOR;

integer	selected_link		= 0;
integer	selected_face		= 0;
vector	selected_touch_pos	= ZERO_VECTOR;
vector	selected_touch_uv	= ZERO_VECTOR;
string	selected_name;
string	selected_description;
vector	selected_position;
rotation selected_rotation;
vector	selected_size;
string	selected_texture_name;
vector	selected_texture_repeats;
vector	selected_texture_offsets;
float	selected_texture_rotation_in_radians;
vector	selected_color;
float	selected_alpha;
integer	selected_isfullbright;
float	selected_intensity;
string	selected_normal_name;
vector	selected_normal_repeats;
vector	selected_normal_offsets;
float	selected_normal_rotation_in_radians;
string	selected_specular_name;
vector	selected_specular_repeats;
vector	selected_specular_offsets;
float	selected_specular_rotation_in_radians;
vector	selected_specular_color;
integer	selected_specular_glossiness;
integer	selected_specular_environment;
integer	selected_alpha_mode;
integer	selected_mask_cutoff;

setSelected(integer target_link, integer target_face, vector target_touch_pos, vector target_touch_uv) {
	integer num = 0;
	list x = llGetLinkPrimitiveParams(target_link,
		[
			PRIM_NAME,						// string name
			PRIM_DESC,						// string description
			PRIM_POS_LOCAL,					// vector position
			PRIM_ROT_LOCAL,					// rotation rot
			PRIM_SIZE,						// vector size
			PRIM_TEXTURE, target_face,		// string texture, vector repeats, vector offsets, float rotation_in_radians
			PRIM_COLOR, target_face,		// vector color, float alpha
			PRIM_FULLBRIGHT, target_face,	// integer boolean
			PRIM_GLOW, target_face,			// float intensity
			PRIM_NORMAL, target_face,		// string texture, vector repeats, vector offsets, float rotation_in_radians
			PRIM_SPECULAR, target_face,		// string texture, vector repeats, vector offsets, float rotation_in_radians, vector color, integer glossiness integer environment
			PRIM_ALPHA_MODE, target_face	// integer alpha_mode, integer mask_cutoff 
		]
	);
	selected_link			= target_link;
	selected_face			= target_face;
	selected_touch_pos		= target_touch_pos;
	selected_touch_uv		= target_touch_uv;
	selected_name			= llList2String(x, num++);
	selected_description	= llList2String(x, num++);
	selected_position		= llList2Vector(x, num++);
	selected_rotation		= llList2Rot(x, num++);
	selected_size			= llList2Vector(x, num++);
	selected_texture_name					= llList2String(x, num++);
	selected_texture_repeats				= llList2Vector(x, num++);
	selected_texture_offsets				= llList2Vector(x, num++);
	selected_texture_rotation_in_radians	= llList2Float(x, num++);
	selected_color							= llList2Vector(x, num++);
	selected_alpha							= llList2Float(x, num++);
	selected_isfullbright					= llList2Integer(x, num++);
	selected_intensity						= llList2Float(x, num++);
	selected_normal_name					= llList2String(x, num++);
	selected_normal_repeats					= llList2Vector(x, num++);
	selected_normal_offsets					= llList2Vector(x, num++);
	selected_normal_rotation_in_radians		= llList2Float(x, num++);
	selected_specular_name					= llList2String(x, num++);
	selected_specular_repeats				= llList2Vector(x, num++);
	selected_specular_offsets				= llList2Vector(x, num++);
	selected_specular_rotation_in_radians	= llList2Float(x, num++);
	selected_specular_color					= llList2Vector(x, num++);
	selected_specular_glossiness			= llList2Integer(x, num++);
	selected_specular_environment			= llList2Integer(x, num++);
	selected_alpha_mode						= llList2Integer(x, num++);
	selected_mask_cutoff					= llList2Integer(x, num++);
}

showSelectedData() {
	llOwnerSay(
		"linknum: "		+ (string)selected_link + ", " + 
		"facenum: "		+ (string)selected_face + "\n" +
		"linkpos: "		+ (string)selected_position + "\n" +
		"linksize: "	+ (string)selected_size + "\n" +
		"color: "		+ (string)selected_color + "\n" +
		"alpha: "		+ (string)selected_alpha + "\n" +
		"texture: "		+ (string)selected_texture_name + "\n" +
		"normal: "		+ (string)selected_normal_name + "\n" +
		"specular: "	+ (string)selected_specular_name + "\n" + 
		"touchuv: "		+ (string)selected_touch_uv + "\n" +
		"touchpos: "	+ (string)selected_touch_pos + "\n" +
		""
	);
}

touchStart() {
	// 押した場所を確認
	setSelected(detected_link, detected_face, detected_touch_pos, detected_touch_uv);
	showSelectedData();
}

default {
	
	touch_start(integer num_detected){
		detected_link		= llDetectedLinkNumber(0);
		detected_face		= llDetectedTouchFace(0);
		detected_touch_pos	= llDetectedTouchPos(0);
		detected_touch_uv	= llDetectedTouchUV(0);
		touchStart();
	}
	
}

 

セカンドライフでタッチしている時間によって動作を変える

はじめに

セカンドライフでタッチしている時間によって動作をかえます。

タイマーを測って動作を切り替える

フラグとllResetTimeとllGetTimeを組み合わせるサンプルです。
作り方によっては、細かく時間を区切ることができます。

なお、ユーザーが一人の場合を想定しています。
複数の場合は、作りを変えた方がいいかもしれません。

integer	is_touching			= FALSE;

touchStart() {
	llOwnerSay("touchStart");
}

shortTouchEnd() {
	llOwnerSay("shortTouchEnd");
}

longTouchEnd() {
	llOwnerSay("longTouchEnd");
}

longlongTouch() {
	llOwnerSay("longlongTouch");
}

default {
	
	touch_start(integer num_detected){
		is_touching		= TRUE;
		touchStart();
		llResetTime();
	}
	
	// 押し続け
	touch(integer num_detected) {
		float timeSec = llGetTime();
		// 1秒以上の押し続け
		if(is_touching && (timeSec >= 3.0)) {
			longlongTouch();
			is_touching = FALSE;
		}
	}
	 
	touch_end(integer num_detected){
		// 1秒未満の押し終わり
		if(is_touching) {
			float timeSec = llGetTime();
			if(timeSec < 1.0) {
				shortTouchEnd();
			}
			else if(timeSec < 3.0) {
				longTouchEnd();
			}
			is_touching = FALSE;
		}
	}
}

タイマーイベントで押した時間を測る

細かいことはできませんが、タイマーイベントを使用しても作ることができます。

ちなみに、これもまた複数ユーザーのことは考えていません・・・

touchStart() {
	llOwnerSay("touchStart");
}

longlongTouch() {
	llOwnerSay("longlongTouch");
}

default {
	
	touch_start(integer num_detected){
		touchStart();
		llSetTimerEvent(0);
		llSetTimerEvent(3);
	}
	
	// 3秒以上押し続けた場合
	timer() {
		llSetTimerEvent(0);
		longlongTouch();
	}
	
	touch_end(integer num_detected){
		llSetTimerEvent(0);
	}
}

複数ユーザーの対応方法

まずどんな場合でも初期状態に戻す初期化関数を作っておきましょう。

クリックされると引数に値が入ってきます。
1つであれば1人かクリックした場合です。
1人がクリックした場合にllDetectedKeyでユーザーのUUIDを内部メモリに保存しましょう。
2人以上がもしクリックした場合は初期化関数を呼びましょう。

クリックが始まったらtouch_startのイベントを締め切りましょう(誰かがクリックするの防止)
あとは、touch関数の引数は1人のときのみのUUIDを確認して、
最初に保存したUUIDと一致するか調べて処理を行います。

タッチが終了したら、内部メモリのUUIDを解放しましょう。

なお、タイミングにより内部メモリにUUIDが残り続ける場合があるので、
タイムアウトできれいにするようにしておくとよいでしょう。

3DCG用テクスチャ作成ツールのPixPlantがすごい

3DCG用テクスチャ作成ツールのPixPlantがすごい

以前からテクスチャ用のツールが欲しいなーと思っていたのですが、
先日 Bitmap2Material が Steam でセール中(¥ 10,300 → ¥ 6,798)になったので、
これを機会に買おうかなと少し調査しました。

もともと PixPlant か Bitmap2Material で迷っていたのですが、
PixPlant がかなり良さそうなので、
PixPlant の機能について少し紹介したいと思います。
2018/7/2の時点での情報となります。

PixPlantの公式サイト


PixPlantとは?

一言でいうと写真から3DCG用のテクスチャを作れるツールです。
上下左右が自然につながったテクスチャを簡単に作れます。

ライセンス

WindowsとMacで動作します。
有料のライセンスは2種類あります。

通常バージョン

$129 = 約 14800 円(税抜き)
通常バージョンとなります。
会社で買う場合は下記のフリーランスではないので、こちらかと思います。
4つ以上から割引価格になるようです。

フリーランスバージョン

$49 = 約 5600 円(税抜き)
フリーランス用(1人の会社用)となります。
上のライセンスと機能の違いはありません。
このライセンスは、所有者のパソコンでのみ使用できます。

デモバージョン

サイトからライセンスキーなしでダウンロードできるバージョンです。
名前をつけて保存でテクスチャを保存すると透かしが入ります。

購入方法

ここで購入が可能です。
購入する際は、使用許諾などの同意のチェックボックスが非常に小さいので注意
(タブキーとスペースキーを駆使してチェックをいれました!)

購入すると、メールでライセンスキーとダウンロードページが案内されます。
そこでライセンスキーを入力すればフルバージョンをダウンロードできます。

使い方

簡単な使い方を紹介いたします。
全ての機能を紹介はできませんが、イメージはつかめると思います。
とっても簡単なのでDEMOバージョンで是非試してみましょう!

1. テクスチャのサイズ設定
まず起動したらサイズを設定します。

上記はデフォルトの設定です。
「Both」だと上下左右がシームレスに接続されます。

2. 画面の右側の部分に画像をドラッグ&ドロップします。

3. 画像が読み込まれます。

4. 「Seed Wizard」を押すとパースペクティブ補正が行えます。

写真のひずみを消します。不要な部分はバッサリと消して、上下左右が平行になるようにしておきましょう。

5. 「Next」を押すと、1ブロックの設定が行えます。

ここは、最初から自動で認識しているようで楽々です。

6. あとは「<< Generate」を押せば完成!

すごく簡単!

もちろん非幾何学なテクスチャも自然に作れます。

↑写真1枚右側において、特に設定せず「Generate」押しただけですが、かなり自然です。

他にも複数の写真を元にして、1つのテクスチャを作成することも可能です。

さらに書ききれませんが、他に以下のようなことが可能です。

左側のテクスチャの画面で、ドラッグで範囲指定した後に、「<< Generate」を押すと、
範囲指定した中で、改めてテクスチャを自動で生成できるので、不自然な個所を直すことができる!

右側のシードとなるテクスチャでドラッグの範囲指定すると、
部分的に自動生成用のシードとして、使用しないようにマスクさせることができる!

3DCG上での見た目表示機能や、
ノーマルマップ用テクスチャや、AOの焼きこみなど色々できます!


最後は駆け足になりましたが、
上記で興味がわいた方は、Demoバージョンをぜひ試してみるとよいと思います!
あと、Youtubeでも操作している動画があるので見るとよいかも。

セカンドライフのテクスチャの秘密(ノーマルマップ編)

セカンドライフのテクスチャの秘密(ノーマルマップ編)

はじめに

こんにちは!

久しぶりのブログです。

今日は、セカンドライフのテクスチャ、ノーマルマップ  の秘密について話します。
続かないかもしれませんが、とりあえず第1弾です。


ノーマルマップの色成分の基礎

ノーマルマップといえば、平面の凹凸を現した画像です。
通常グレースケールで高さを表した画像から、このノーマルマップを作成します。
おそらく、少し詳しzい方はこの辺までは、ご存知かと思います。

しかし、具体的に
赤色成分は、何をセカンドライフ上の何を示すのか、
青色成分は……?
といったことまで知っている方はそんなにいないのでは?

ということで、この色成分が何を表しているかの解説となります。

ノーマルマップの英語の解説を確認してみましょう。
これを見ると、次のような情報が分かります。
赤成分R = 方向成分X
緑成分G = 方向成分Y
青成分B = 方向成分Z

ただし、方向とは言ってもX+がR+に
対応しているのかといったことはテキストには書いてありません。
なぜ、それが重要かといいますと、ソフトによって色成分との対応が異なるためです。

>・ZBrush:(-X,+Y,+Z)
>・CrazyBump:(+X,+Y,+Z)
>・Blender 2.4x:(-X,-Y,+Z)
>・Blender 2.57以降:(+X,+Y,+Z)
Sandy Virtual City様のブログから引用
※XYZ軸がどちらに対応しているかも重要となるため、ブログ内の軸の解説も参照しましょう。
ブログでは、X軸のプラスをUV座標系のU方向、Y軸のプラスをUV座標系のV方向、Z軸のプラスを凸方向としています。

では、セカンドライフを調べてみましょう。
公式ページにあるノーマルマップの画像から、
凸に対して、画素の色が次のようになっています。
上が RGB(126,178,212)
右が RGB(169,124,210)
下が RGB(109, 75,206)
左が RGB( 69,124,212)

つまり、セカンドライフ(+X, -Y, +Z)
となることが分かります。

これは、NormalMap-Onlineでは、
Invert の R にチェックが入った場合と同様になります。

これらをおさらないすると、たとえば下記のようなバンプマップ/ハイトマップを用意したとすると、

次のような画像がセカンドライフ上でのノーマルマップとなります。

本日は、ここまで!

セカンドライフで自分の行動の色々な情報を取得する

次のような自分の行動の情報を取得してみます。
・自分が喋った回数
・人から聞いた回数(アバターが話したものに限定)
・空を飛んでいる時間(ホバーは含めない)
・海の中を泳いでいる時間(ホバーは含めない)
・歩いている時間
・走った時間
・ジャンプ中の時間
・テレポートした回数
・SIMをまたいだ回数
・人や当たり判定がある物体と衝突した回数

以下、サンプルです。
このコードを適当なオブジェクトに入れた後、HUDとして装備してください。
drawStatusと発言すると、自分の情報が表示されます。

ログインした回数とかも取れるかなと思ったのですが、
装備した回数と見分けがつかないので諦めました。

integer count_attack		= 0;
integer count_speak			= 0;
integer count_listen		= 0;
integer count_flying		= 0;
integer count_swimming		= 0;
integer count_running		= 0;
integer count_walking		= 0;
integer count_teleport		= 0;
integer count_simtraversal	= 0;
integer count_jumping		= 0;

integer TYPE_ATTACK			= 0;
integer TYPE_SPEAK			= 1;
integer TYPE_LISTEN			= 2;
integer TYPE_FLYING			= 3;
integer TYPE_SWIMMING 		= 4;
integer TYPE_RUNNING		= 5;
integer TYPE_WALKING		= 6;
integer TYPE_TELEPORT		= 7;
integer TYPE_SIMTRAVERSAL	= 8;
integer TYPE_JUMPING		= 9;

integer show_dialog		= FALSE;
integer listen_handle	= -1;

addAction(integer action) {
//	llOwnerSay("action : "+ (string)action);
	if(action == TYPE_ATTACK) {
		count_attack	= count_attack + 1;
	}
	else if(action == TYPE_SPEAK) {
		count_speak		= count_speak + 1;
	}
	else if(action == TYPE_LISTEN) {
		count_listen	= count_listen + 1;
	}
	else if(action == TYPE_FLYING) {
		count_flying	= count_flying + 1;
	}
	else if(action == TYPE_SWIMMING) {
		count_swimming	= count_swimming + 1;
	}
	else if(action == TYPE_RUNNING) {
		count_running	= count_running + 1;
	}
	else if(action == TYPE_WALKING) {
		count_walking	= count_walking + 1;
	}
	else if(action == TYPE_TELEPORT) {
		count_teleport	= count_teleport + 1;
	}
	else if(action == TYPE_SIMTRAVERSAL) {
		count_simtraversal	= count_simtraversal + 1;
	}
	else if(action == TYPE_JUMPING) {
		count_jumping	= count_jumping + 1;
	}
}

showStatus() {
	llWhisper(0,
		"attack\t:"			+ (string) count_attack			+ " times\n" +
		"speak\t:"			+ (string) count_speak			+ " times\n" +
		"listen\t:"			+ (string) count_listen			+ " times\n" +
		"teleport\t:"		+ (string) count_teleport		+ " times\n" +
		"simtraversal\t:"	+ (string) count_simtraversal	+ " times\n" +
		"flying\t\t:"		+ (string) count_flying			+ " sec\n" +
		"swimming\t\t:"		+ (string) count_swimming		+ " sec\n" +
		"running\t:"		+ (string) count_running		+ " sec\n" +
		"walking\t:"		+ (string) count_walking		+ " sec\n" +
		"jumping\t:"		+ (string) count_jumping		+ " sec"
	);
}

openDialog() {
	if(listen_handle != -1) {
		llListenRemove(listen_handle);
	}
	listen_handle = llListen(-100, "", llGetOwner(), "");
	llDialog( llGetOwner(), "Which operation?\nYou say \"drawStatus\"... or \"resetStatus\"", ["status", "reset", "cancel"], -100 );
	llSetTimerEvent(0.0);
	llSetTimerEvent(30.0);
	show_dialog = TRUE;
}

closeDialog() {
	if(listen_handle != -1) {
		llListenRemove(listen_handle);
	}
	show_dialog = FALSE;
	listen_handle = llListen(0, "", "", "");
	llSetTimerEvent(0.0);
	llSetTimerEvent(1.0);
}

default {
	state_entry() {
		llOwnerSay("Start Script !\nYou say \"drawStatus\"... or \"resetStatus\"");
		closeDialog();
	}
	
	touch_start(integer total_number) {
		openDialog();
	}
	
	timer() {
		if(show_dialog) {
			closeDialog();
		}
		else {
			string my_animatin = llGetAnimation(llGetOwner());
			if(my_animatin == "Flying") {
				vector v = llGetPos();
				if(llWater(v) > v.z) {
					addAction(TYPE_SWIMMING);
				}
				else {
					addAction(TYPE_FLYING);
				}
			}
			else if(my_animatin == "Running") {
				addAction(TYPE_RUNNING);
			}
			else if(my_animatin == "Walking") {
				addAction(TYPE_WALKING);
			}
			else if(my_animatin == "Jumping") {
				addAction(TYPE_JUMPING);
			}
		}
	}
	
	listen( integer channel, string name, key id, string message ) {
//		llOwnerSay(""+ (string)channel +" " + name + " " + (string)id + " " + message);
		if(show_dialog) {
			if(message == "status") {
				showStatus();
			}
			else if(message == "reset") {
				llResetScript();
			}
			else if(message == "cancel") {
			}
			closeDialog();
		}
		else {
			if(id == llGetOwner()) {
				if(message == "drawStatus") {
					showStatus();
				}
				else if(message == "resetStatus") {
					llResetScript();
				}
				else {
					addAction(TYPE_SPEAK);
				}
			}
			else if(llGetAgentSize(id)){
				addAction(TYPE_LISTEN);
			}
		}
	}
	
	changed(integer mask) {
		if(mask & CHANGED_OWNER) {
			llResetScript();
		}
		if((mask & CHANGED_TELEPORT) && (mask & CHANGED_REGION)) {
			addAction(TYPE_TELEPORT);
		}
		else if(mask & CHANGED_TELEPORT) {
			addAction(TYPE_TELEPORT);
		}
		else if(mask & CHANGED_REGION) {
			addAction(TYPE_SIMTRAVERSAL);
		}
	}
	
	collision_start(integer num) {
		if(llDetectedType(0) & ACTIVE) {
			vector my_velocity = llGetVel();
			vector velocity = llDetectedVel(0);
			float power = llVecMag(my_velocity - velocity);
			if(power >= 1.0) {
				addAction(TYPE_ATTACK);
			}
		}
	}
	
	on_rez(integer param) {
		// 1 ログイン、装備、rez
	}
	
	attach(key attached) {
		if(attached) {
			// 2 ログイン、装備
		}
	}
}
%d人のブロガーが「いいね」をつけました。