この記事はセカンドライフ技術系アドベントカレンダー向けの記事です。
セカンドライフ技術系 Advent Calendar 2018 - Adventar
前回環境を構築したので、今回はいよいよ改造していきます。
で、どういう改造をするかなんですが、セカンドライフはたいていのことは内部スクリプトのLSLで実現できてしまうので
かなり悩みました。最終的にLSLではビューワのメニューの拡張はできないので、右クリックメニューの拡張をすることにしました。
右クリックメニューに「Jsonへ出力」を追加し、オブジェクトの情報をJson形式で出力できるようにします。
メニューの拡張についてはWikiにドキュメントがあるので、ここを参考に進めていきます。
Adding a menu item - Second Life Wiki
XUI設定ファイルの編集
セカンドライフビューワではXUIというUIフレームワークが実装されていて、XMLに設定を書くことでUIを拡張できます。
メニューの拡張もXUIの設定で行います。
その設定XMLがどこにあるかを知るには、ソース全体のフォルダ構成を理解するのが早道です。
ソースコードのトップフォルダ、indraの配下には「llui」「llxml」のようにllで名前が始まるフォルダがたくさんあります。
これらがビューワの基礎ライブラリです。そしてそれらのライブラリを使用するビューワ本体のコードは「newview」配下に
格納されています。
XUIの設定XMLは「newview」フォルダ直下の「skins」フォルダにあります。右クリックメニューを追加するには
「indra\newview\skins\default\xui\en\menu_object.xml」ファイルを編集します。
menu_item_call.on_clickタグは、クリックされた際に呼ばれるイベントハンドラの設定を行っています。
indra\newview\skins\default\xui\en\menu_object.xml
<menu_item_call
enabled="false"
label="Delete"
name="Delete">
<menu_item_call.on_click
function="Object.Delete" />
<menu_item_call.on_enable
function="Object.EnableDelete" />
</menu_item_call>
<!-- Json出力メニューを追加 ↓↓↓ -->
<menu_item_call
enabled="true"
label="ToJson"
name="ToJson">
<menu_item_call.on_click
function="Object.ToJson" />
</menu_item_call>
<!-- Json出力メニューを追加 ↑↑↑ -->
<menu_item_separator
layout="topleft" />
メニューの日本語化をする場合は、jaフォルダの方のmenu_object.xmlを編集します。
indra\newview\skins\default\xui\ja\menu_object.xml
<menu_item_call label="買う" name="Buy..."/>
<menu_item_call label="削除" name="Delete"/>
<!-- Json出力メニューを追加 ↓↓↓ -->
<menu_item_call label="Jsonへ出力" name="ToJson"/>
<!-- Json出力メニューを追加 ↑↑↑ -->
<menu_item_call label="パーティクル所有者をブロック" name="Mute Particle"/>
メニューの拡張はできたので、次はイベントハンドラを書いていきます。
メニューのイベントハンドラは、「newview」フォルダ直下の「llviewermenu.cpp」に書く決まりになっています。
indra\newview\llviewermenu.cpp
#include "llnotificationmanager.h"
...
...
void handle_object_tojson()
{
if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return;
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
LLViewerObject* selected_objectp = selection->getFirstRootObject();
if (selected_objectp)
{
std::ostringstream oss;
oss << "{" << std::endl;
oss << " \"uuid\" : \"" << selected_objectp->getID().asString() << "\"" << std::endl;
oss << " \"position\" : \"" << selected_objectp->getPosition() << "\"" << std::endl;
oss << " \"scale\" : \"" << selected_objectp->getScale() << "\"" << std::endl;
oss << " \"rotation\" : \"" << selected_objectp->getRotation() << "\"" << std::endl;
oss << " \"permmodify\" : \"" << selected_objectp->permModify() << "\"" << std::endl;
oss << " \"permcopy\" : \"" << selected_objectp->permCopy() << "\"" << std::endl;
oss << " \"permmove\" : \"" << selected_objectp->permMove() << "\"" << std::endl;
oss << " \"numfacees\" : \"" << selected_objectp->getNumFaces() << "\"" << std::endl;
oss << " \"ismesh\" : \"" << selected_objectp->isMesh() << "\"" << std::endl;
oss << "}";
LLChat chat;
chat.mSourceType = CHAT_SOURCE_SYSTEM;
chat.mText = oss.str();
LLSD args;
LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args);
}
}
最後にXUIのメニューとイベントハンドラを関連付けるコードを書きます。
「llviewermenu.cpp」の下部に右クリックメニューの関連付け処理が書かれている箇所があるので
そこに追加しておきます。
indra\newview\llviewermenu.cpp
view_listener_t::addMenu(new LLObjectBuild(), "Object.Build");
commit.add("Object.Touch", boost::bind(&handle_object_touch));
commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand));
commit.add("Object.Delete", boost::bind(&handle_object_delete));
view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar");
view_listener_t::addMenu(new LLObjectAttachToAvatar(false), "Object.AttachAddToAvatar");
view_listener_t::addMenu(new LLObjectReturn(), "Object.Return");
commit.add("Object.Duplicate", boost::bind(&LLSelectMgr::duplicate, LLSelectMgr::getInstance()));
view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse");
view_listener_t::addMenu(new LLObjectMute(), "Object.Mute");
enable.add("Object.VisibleTake", boost::bind(&visible_take_object));
enable.add("Object.VisibleBuy", boost::bind(&visible_buy_object));
commit.add("Object.Buy", boost::bind(&handle_buy));
commit.add("Object.Edit", boost::bind(&handle_object_edit));
commit.add("Object.Inspect", boost::bind(&handle_object_inspect));
commit.add("Object.Open", boost::bind(&handle_object_open));
commit.add("Object.Take", boost::bind(&handle_take));
commit.add("Object.ShowInspector", boost::bind(&handle_object_show_inspector));
enable.add("Object.EnableOpen", boost::bind(&enable_object_open));
enable.add("Object.EnableTouch", boost::bind(&enable_object_touch, _1));
enable.add("Object.EnableDelete", boost::bind(&enable_object_delete));
enable.add("Object.EnableWear", boost::bind(&object_selected_and_point_valid));
enable.add("Object.EnableStandUp", boost::bind(&enable_object_stand_up));
enable.add("Object.EnableSit", boost::bind(&enable_object_sit, _1));
view_listener_t::addMenu(new LLObjectEnableReturn(), "Object.EnableReturn");
enable.add("Object.EnableDuplicate", boost::bind(&LLSelectMgr::canDuplicate, LLSelectMgr::getInstance()));
view_listener_t::addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse");
enable.add("Avatar.EnableMute", boost::bind(&enable_object_mute));
enable.add("Object.EnableMute", boost::bind(&enable_object_mute));
enable.add("Object.EnableUnmute", boost::bind(&enable_object_unmute));
enable.add("Object.EnableBuy", boost::bind(&enable_buy_object));
commit.add("Object.ZoomIn", boost::bind(&handle_look_at_selection, "zoom"));
commit.add("Object.ToJson", boost::bind(&handle_object_tojson));
あとはVisualStudio上で「secondlife-bin」プロジェクトをビルドするとビルドフォルダ配下に
セカンドライフの実行ファイルができます。
実際、起動してみるとこんな感じで出力されました。
今回、C++をほぼ初めて実践的に使ってみましたがなんとかビューワの改造ができてほっとしました。
アドベントカレンダーのネタということでオレオレ改造みたいな感じになりましたが、
本来はビューワをいじるならJiraを参照して挙がっているissueに対応するのがいいと思います。
腕に覚えのある方は挑戦してみてください( ̄∇  ̄ )