Yes Second Life

セカンドライフに早9年、現役セカンドライファーの考えてること

セカンドライフビューワ改造への道 - 改造編

この記事はセカンドライフ技術系アドベントカレンダー向けの記事です。

セカンドライフ技術系 Advent Calendar 2018 - Adventar

前回環境を構築したので、今回はいよいよ改造していきます。

で、どういう改造をするかなんですが、セカンドライフはたいていのことは内部スクリプトのLSLで実現できてしまうので かなり悩みました。最終的にLSLではビューワのメニューの拡張はできないので、右クリックメニューの拡張をすることにしました。

右クリックメニューに「Jsonへ出力」を追加し、オブジェクトの情報をJson形式で出力できるようにします。 メニューの拡張についてはWikiにドキュメントがあるので、ここを参考に進めていきます。

Adding a menu item - Second Life Wiki

f:id:sabro:20181225032329p:plain

XUI設定ファイルの編集

セカンドライフビューワではXUIというUIフレームワークが実装されていて、XMLに設定を書くことでUIを拡張できます。 メニューの拡張もXUIの設定で行います。 その設定XMLがどこにあるかを知るには、ソース全体のフォルダ構成を理解するのが早道です。

f:id:sabro:20181225032502p:plain

ソースコードのトップフォルダ、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"  // includeを追加
...
...
// Json出力のイベントハンドラ
void handle_object_tojson()
{
    // オブジェクトを選択していない場合はリターン
    if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return;

    // 選択中のオブジェクトを取得
    LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
    
    // 選択中オブジェクトのルートオブジェクトを取得
    LLViewerObject* selected_objectp = selection->getFirstRootObject();

    // 選択しているオブジェクトがある場合はJson出力
    if (selected_objectp)
    {
        // Json文字列を構築
        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クラスを作成し、Json文字列を設定
        LLChat chat;
        chat.mSourceType = CHAT_SOURCE_SYSTEM;  // システムからのチャットに設定
        chat.mText = oss.str();

        // LLSDクラス作成、関数の引数に渡すだけなので空でOK
        LLSD args;

        // チャット出力
        LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args);
    }
}

最後にXUIのメニューとイベントハンドラを関連付けるコードを書きます。 「llviewermenu.cpp」の下部に右クリックメニューの関連付け処理が書かれている箇所があるので そこに追加しておきます。

indra\newview\llviewermenu.cpp

 // Object pie menu
    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」プロジェクトをビルドするとビルドフォルダ配下に セカンドライフの実行ファイルができます。

f:id:sabro:20181225043344p:plain

実際、起動してみるとこんな感じで出力されました。

f:id:sabro:20181225035625p:plain

今回、C++をほぼ初めて実践的に使ってみましたがなんとかビューワの改造ができてほっとしました。 アドベントカレンダーのネタということでオレオレ改造みたいな感じになりましたが、 本来はビューワをいじるならJiraを参照して挙がっているissueに対応するのがいいと思います。 腕に覚えのある方は挑戦してみてください( ̄∇  ̄ )

セカンドライフビューワ改造への道 - 開発環境構築編

この記事はセカンドライフ技術系アドベントカレンダー2018向けの記事です。

セカンドライフ技術系 Advent Calendar 2018 - Adventar

最近C++ができるようになってきたので今回はセカンドライフビューワの改造に挑戦。まずはVisualStudioで開発環境を整えるところまでやっていきます。ちなみに今は64bit版のビューワもありますが、とりあえず32bit版を改造します。

もろもろインストール

少し情報が古いですが、Visual Studio 2013 Viewer Builds - Second Life Wikiを参考にすると、色々インストールする必要があるみたいです。

あまり、環境を汚したくなかったので仮想マシン上で開発していくことにします。以下のリンクから有効期限付きの評価版Windows10のVMがダウンロードできます。

Windows 10 仮想マシンをダウンロードする - Windows アプリ開発

f:id:sabro:20181203004654p:plain

このVMには最初からVisualStudio2017、Windows Subsystem for Linuxがインストールされています。VMを立ち上げたら、まずは色々インストールしていきましょう。

Visual Studio 2013

VMにはVisualStudio2017が入っているのですが、なぜかビルドが通らず・・。ここで2,3日費やした後、2017はアンインストールしWikiの説明にあるVisualStudio2013をインストールしたらあっさりビルドできました。2013は、無料のVisual Studio Dev Essentialsプログラムに参加した後、以下のリンクからダウンロードできました。

以前の Visual Studio ソフトウェアのダウンロード | Visual Studio - Visual Studio

DirectX SDK

以下のリンクからインストーラをダウンロードし、管理者権限で実行。HeaderとLib以外は不要なのでチェックをはずします。

Download DirectX Software Development Kit from Official Microsoft Download Center

f:id:sabro:20181203004424p:plain

CMake

以下のリンクからインストーラをダウンロード。32bit版でないといけないので注意です。インストールオプションで"Add CMake to the system PATH for all users"を選択します。

Download | CMake

f:id:sabro:20181203004502p:plain

Cygwin

Windows Subsystem for Linuxが入っているので不要かと思ったのですが、ビルド時にパスをCygwin向けに変換しているところがあって、やっぱり駄目でした。Cygwinは以下のリンクからダウンロード。こちらは64bit版を選択します。インストーラを管理者権限で実行し、パッケージ選択画面では、「Devel/patch」を追加します。

Cygwin Installation

f:id:sabro:20181203004539p:plain

Python

実はVMには最初からPython3が入っているのですが、ビルドに使用するのは2系のようなのでPython3はアンインストールし、Python2の最新を入れました。こちらは32bit版でないといけないので注意です。インストールオプションでは"Add python.exe to Path"を"Will be installed on local hard drive"に設定します。

Download Python | Python.org

f:id:sabro:20181203004622p:plain

Mercurial

ビューワのソースコードはBitbacketで管理されているのでMercurialで取得します。コマンドラインでhgが使えればいいので、TortoiseHgは不要です。

Download - Mercurial

Autobuild

 AutobuildはPythonで書かれています。Pythonのパッケージ管理ツールpipを使ってインストールします。Windowsコマンドプロンプトで以下を入力すればOKです。

pip install hg+http://bitbucket.org/lindenlab/autobuild-1.1#egg=autobuild

ビルド変数の設定

ここからは、Cygwinコンソール上での作業になります。さて、ビューワのソースコードとは別にビルド変数を設定するツールが用意されています。

lindenlab / viewer-build-variables — Bitbucket

上記リンクからツールをダウンロードして解凍し、その中にある「variables」ファイルのパスを環境変数に設定します。Cygwin形式ではなくWindows形式のパスで指定する必要があることに注意。ここで結構ハマりました・・・。

export AUTOBUILD_VARIABLES_FILE="C:\Users\user\dev\lindenlab-viewer-build-variables\variables"

ビューワのビルド 

作業ディレクトリに移動して、以下のコマンドでビューワのソースコードを取得します。

hg clone https://bitbucket.org/lindenlab/viewer-release

Cloneが終わったらトップフォルダに移動し、autobuildで環境変数の設定をします。

cd viewer-release
eval "$(autobuild source_environment)"

次はautobuildでコンフィグレーションを実行します。これが完了すると、ソリューションファイルが作成されます。「RelWithDebInfoOS」はビルド構成です。Debugビルドはうまく動かないみたいなのでこれにしました。「32」は32bitの指定です。

autobuild configure -c RelWithDebInfoOS -A 32

これでビルドの準備が整いました。作成されたソリューションファイルでプロジェクトを開いてビルドするか、もしくは以下のコマンドでビルドできます。

autobuild build --no-configure  -c  RelWithDebInfo -A 32

これで開発環境の構築は完了です。ハマりまくってまだ環境構築しかできてないので実際の改造はこれからやります( ̄□  ̄ ||

C++、DirectXを勉強しています

長らくVR世界に向けたWebサービスを作るというスタイルでやってきたのですが、そのやり方にも限界を感じてきたのでC++DirectXなどを勉強しています。

色んな人に今勉強するならUnityやUnrealEngineがいいよと進められましが、背後で動いている仕組みを知っておきたかったのと、GPUの使い方を分かっとくことで、AIやビッグデータなんかにも応用できるなと思ってDirectXにしました。とにかく書籍が少ないのが難点ですが・・・。

VRの波が来るのを待ってるうちに、自分もずいぶん歳をとりましたが、せっかくなんで、なにかひとつくらいはリリースできたらなとおもいます。

仮想世界のアバターデザインについて

これは、セカンドライフ非技術系アドベントカレンダー2017向けに書かれた記事です。

adventar.orgセカンドライフアバターといえばリアル調のアバターだと思われがちですが、自分のアバターはこんな感じです。

f:id:sabro:20171219223946p:plain

日本人には、リアルなアバターよりこういうアバターのほうがいいという人も結構いるんじゃないでしょうか。

こういったアニメ調のアバターを基本とするメタバースがあれば、日本人ももっと仮想世界に来るのにと思っていた時期もあったんですが、最近は考え方が変わってきました。

おそらくそういう仮想世界は米国の人はほとんどプレイしないと思います。米国ではアニメは子供のものという認識が強くて大人は一部をのぞいてリアルなアバターを求めています。つまり、アニメ調アバターの仮想世界は日本やアジアなんかに閉じた世界になります。それはもう3D版のインターネットと言えるメタバースではないものになってしまうんじゃないでしょうか。

というわけで、リアルなアバターを基本としつつも、ぼくのアバターみたいにかわいいものも作れる今のセカンドライフは、実は落とし所としてはイイ線いってるんじゃないかと思います。

もちろん技術が進歩すれば、昔から話題にあがっているマルチバースみたいな世界になり、異なった世界観のメタバースを行き来できたりもするかもしれません。将来的に、異なる文化を持つ人々がメタバースでどういう共存、住み分けをしていくのか考えてみるのも面白いと思います。

HighFidelityのTablet UI Frameworkを使う

この記事は、セカンドライフ技術系アドベントカレンダー2017向けの記事です。

adventar.org

前回ちょろっと触れましたが、HightFidelityでHUDみたいなUIを作るには、Tablet UI Frameworkというのを使います。今回はこのフレームワークで出来ることを見ていきます。

Webページを表示する

Tabletオブジェクトの、gotoWebScreen関数を使うと、指定したWebページを表示できます。

(function() {

    // クリックイベント
    this.clickDownOnEntity = function(entityID, mouseEvent) {

        // Tabletオブジェクトを取得
        var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
    
        // このブログを表示
        tablet.gotoWebScreen("http://sabro.hatenablog.com/");
    };
})

モバイル用のページが表示されるみたいです。

f:id:sabro:20171203232010p:plain

上のスクリプトではTabletオブジェクトを唐突に取得しています。このオブジェクトの定義がないかHighFidelityのインストールディレクトリのJSファイルを色々検索して調べてみたのですが、定義されてる箇所が見つかりませんでした。とりあえず、Tablet UI Frameworkを使用するときのおまじないみたいなものと思っておくことにします。

f:id:sabro:20171203235114p:plain

なお、Webページ以外にもQt向けの宣言型UI言語、QMLの表示にも対応しているみたいですが、今回はそこまで試せませんでした( ̄□  ̄ ||

デフォルトのメニューを拡張する

HighFidelityの世界にログインすると、デフォルトでこんなメニューが表示されます。これは公式で用意されているメニューなわけですが、Tablet UI Frameworkを使えばここに自分の機能を追加することが出来ます。

f:id:sabro:20171204000946p:plain

具体的にはTabletオブジェクトのaddButton関数でメニューUIにボタンを追加し、そのボタンのクリックイベントを定義してやればOKです。スクリプト終了時にはボタンを削除する処理も忘れずに入れておきます。ここでは、ボタンを押すとGoogle検索を呼び出すだけのGoogleボタンを作ってみます。

(function() {
    
    // Tabletオブジェクトを取得
    var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");

    // デフォルトのメニューにボタンを追加、ボタンの文言はGoogleを指定
    // iconプロパティを設定すれば独自のアイコンの表示も可能だがここでは省略
    var button = tablet.addButton({
        text: "Google"
    });

    // メニューのボタンを押した際に呼び出す関数、ここではGoogle検索を表示
    function onClicked() {
        tablet.gotoWebScreen("https://www.google.co.jp/");
    }

    // メニューボタンクリックイベントと関数を接続する
    button.clicked.connect(onClicked);

    // スクリプト終了時に呼び出す関数
    function cleanup() {
        tablet.removeButton(button);
    }

    // スクリプト終了イベントと関数を接続する
    Script.scriptEnding.connect(cleanup);
}()); 

作ったスクリプトはインターフェーススクリプトとして読み込みます。メニューの拡張みたいにクライアントから接続している間だけ有効にしたい機能はインターフェーススクリプトとして使用するのがベストです。作ったスクリプトtablet-test.jsと名前をつけてディスクから読み込みます。

f:id:sabro:20171209201332p:plain

メニューにGoogleボタンが追加されました。

f:id:sabro:20171209201404p:plain

押すとGoogle検索画面が表示されます。

f:id:sabro:20171209205401p:plain

終了処理を仕込んであるのでスクリプトを✕ボタンで停止するとボタンもメニューから消えます。

f:id:sabro:20171209215213p:plain

一通り見てきましたが、公式のメニューを自分で拡張できるのが面白いですね。この拡張性の高さがHighFidelityの魅力でもあります( ̄∇  ̄ )

HighFidelityで伝言掲示板をつくる

この記事は、セカンドライフ技術系アドベントカレンダー2017向けの記事です。ウチは例のごとくHigh Fidelityネタを書きます。

adventar.org

今回は実際に何か作ろうということで、伝言掲示板を作ってみようと思います。チャットかなにかで文言を指定すると、それを記録し残してくれるようなツールです。

f:id:sabro:20171123224654p:plain

セカンドライフではオブジェクトに文字列を残すのは結構大変で、すべての文字をテクスチャでアップロードして切り替えたり、プリムにWebページを表示してそこで文字列を表示したりしてましたが、HighFidelityではテキストオブジェクトというテキストを表示するためのオブジェクトが用意されています。これを使えば割りと簡単に実装できそうです。

f:id:sabro:20171123231439p:plain

しかし、ここで重大な事実が判明。なんとHighFidelityにはテキストチャットのインターフェースがデフォルトでは存在しません。テキストチャットから文字列を送信するという手法は取れなくなりました。

f:id:sabro:20171125020834p:plain

今はRiftみたいなHMDでアクセスするのが当たり前なので、マイクのチャットを使うだろうからデフォルトでテキストチャットがあっては邪魔だろうという判断みたいです。一応インターフェース(ビューワ)には、チャットを実装したインターフェーススクリプト、chat.jsが付属しているので、これを有効にしてやればテキストチャットできるようになります。

f:id:sabro:20171125023209p:plain

余談ですが、このchat.jsはすべて公開されているAPIで実装されています。つまり自分でオリジナルのチャットシステムを作ったりもできるということですね。実際、HighFidelityにはオリジナルのチャットシステムを作って公開してる人もいるみたいです。

f:id:sabro:20171125192819p:plain

しかし、デフォルトでOFFになっている機能を前提とするのも微妙なので、今回は伝言掲示板をクリックすると、入力ダイアログを表示するような方針で行こうと思います。

掲示板のタッチイベントで、javascriptのWindow.prompt関数を使って入力値を取得。それをEntities.editEntity関数でテキストエンティティのプロパティに設定します。

(function() {

    // クリックイベント
    this.clickDownOnEntity = function(entityID, mouseEvent) {

        // 入力ダイアログを表示
        var message = Window.prompt("Input Message!", "");

        // 入力値をテキストエンティのtextプロパティに設定
        Entities.editEntity(entityID, { "text" : message });
    };
})

残念ながら、Window.promptの表示メッセージに日本語を使用するとダイアログが出ませんでした。このスクリプトをWeb上に公開しテキストエンティティにScript URLを設定します。

f:id:sabro:20171125225951p:plain

このへんの話がわからない人は、こっちを見てね。

High Fidelityのスクリプト - Yes Second Life

またまた余談ですが、Script URLの下に、Server Script URLというプロパティがありますね。実はEntity Scriptは、Client Entity ScriptとServer Entity Scriptに分かれました。前者はユーザがエンティティの近くにいるときだけ動作し、後者はユーザがいるかどうかに関わらず動作し続けます。

さて、では作った掲示板の動作確認です。まずはタッチでダイアログを出してメッセージを入力。残念ながら日本語は入力無理でした。

f:id:sabro:20171125235420p:plain

入力後、無事メッセージが変わりました。

f:id:sabro:20171125235611p:plain

最初は、テキストの入力をどうするか分からなくて大変そうだなあと思ってたんですが、Window.promptが使えると気づいたら結構簡単に出来てしまいました。

今回のようなダイアログでなくセカンドライフのHUDのようにもっと複雑なUIを作りたい場合は、Tablet UI Frameworkというのを使う必要があります。こちらも機会があればまた記事にしたいと思います( ̄∇  ̄ )

HighFidelityのZoneとはなんぞや?

この記事は、セカンドライフ技術系アドベントカレンダー2016向けの記事です。

www.adventar.org


HighFidelityには、Zoneというオブジェクトがあります。今までのVRにはなかった新概念なので、ちょっと解説してみます。

Zoneとはなにか

Zoneは語感からだいたい分かると思いますが、VR世界をいくつかのエリアに分けて各エリアごとに環境設定できるようにするための「区切り」みたいなものです。具体的には、景観やライティングの設定ができます。

なぜZoneが必要なのか

セカンドライフは、シムが寄り集まることで世界を作っていました。環境はシムごとに変えることができたので特にさらなるエリア分けは必要じゃなかったんです。しかし、HighFidelityは、1つのDomein Serverがとても広大な世界を管理します。なので細かくエリアをわけれる仕組みが必要だったんじゃないでしょうか。

ちなみにこういう広大な世界になったのはシム超え問題がやっぱりきつかったんじゃないかと個人的には思ってます。SANSARも同じくシムという概念がなくなるし、広大な世界というのは次期VRのキーワードですね。

Zone編集の基本操作

f:id:sabro:20161210230451p:plain:w480
編集モードに入ると生成できるオブジェクト一覧にZoneが含まれてます。

f:id:sabro:20161210230824p:plain:w480
Zoneを生成したところです。ここでちょっと注意点があります。生成直後のZoneは選択された状態になってますが、選択状態を解いてしまうと再選択できなくなってしまいます。それどころかZoneのワイヤーフレームも見えなくなります。Zoneはとても大きなオブジェクトなので、普通に選択できるようにしてしまうと、他のオブジェクトを作りたいときに邪魔でしょうがないから、そういう仕様なのかなと思います。

f:id:sabro:20161210230949p:plain
Zoneを見えるようにするには、Editメニューで「Show Zones in Edit Mode」にチェックをつければOKです。

f:id:sabro:20161210231344p:plain
選択状態にしたいときは、Entity Listから探してクリックすればOK。

Zoneの背景を設定する

さてここからちょっと本格的にZoneの設定をしていきます。

f:id:sabro:20161210231819j:plain:w480
↑ 公式サイトより引用

Zoneの背景を作るときはサイコロを作る要領で展開図ぽい画像を作ります。またパノラマ画像みたいなのを背景にすることもできるみたいですが、今回は公式サイトから展開図型のサンプルをダウンロードして設定してみます。

f:id:sabro:20161210232610p:plain

Zoneの編集メニューから、Background Modeを「SkyBox」にすると、SkyBoxメニューが出ます。ここで背景画像のURLと画像の色合いを設定できます。

f:id:sabro:20161210232221p:plain
無事設定した画像が背景になりました。

Zoneのライティングを設定する

Zoneはライトの加減も色々変えることができます。変更できる項目は色々あるんですが、とりあえずライトの強さだけ試してみました。

f:id:sabro:20161210233006p:plain
Light Intensityの数値を上げてみる。

f:id:sabro:20161210233035p:plain
びふぉあ

f:id:sabro:20161210233051p:plain
あふたー

おまけ

f:id:sabro:20161210233247p:plain
Zoneは背景やライティングの設定をすると公式には書いてあったんですが、リファレンスには乗ってない「Flying allowed」「Ghosting allowed」みたいなプロパティもありました。前者は予想通りZone内で飛行禁止する設定。後者はよくわかりませんでした。HighFidelity的には、ライティングみたいな見た目以外にも色々設定できるようにする方針なんでしょうか。せっかくなんで物理法則とかもZoneごとに変えれるようになってくれると面白そうです。

他にもZoneは、ネストさせることができたり、直方体以外の形にしたりと変わったことができるみたいで、まだまだ奥深そうです。SANSARにも似たような機能が入る可能性もあるかもしれないし、次世代VRをやる人はZoneという新概念を理解しておくといいかもですよ( ̄∇  ̄ )