lumino trail

ゲームエンジン作ってます。Github:https://github.com/lriki/Lumino Twitter:https://twitter.com/lriki8

長月と作業報告の候

文字列周りの内部的な変更

先月、

  • 内部文字コードを統一にする
  • 例外を積極的に使わないようにする

とか言ってましたが、まだ全体として仕上げるには至ってないです・・・。

String クラスは内部構造を大きく変えました。できるだけ標準ライブラリに依存しないようになっています。 ちなみに SSO(Short String Optimization) も付くようになりました。

編集用のメッシュデータ構造

LOD 作るためのポリ削減とか、法線のより自然な自動計算とか、ちょっと凝ったメッシュ編集にはただのトライアングルリストじゃつらいので、編集用のデータ構造を持ったメッシュクラスを作っています。

実行速度はごめんな。

RenderView

試験的に「RenderView」という機能を作ってみています。

RenderView はこんな人↓

  • ペイントソフトでいうところのレイヤーのようなもの
  • シーンのぞき窓 (オフスクリーンレンダリングでもなんでも)
  • UIウィジェットを配置できる
  • 親子関係を持つことができる
  • ポストエフェクトを適用できる

Unity 知ってる人には、Camera からレンダーターゲット関係の情報を全部独立させたものというと何となくわかるかも。

特にアプリ全体を通して共通な、画面の構成やエフェクトに関係する情報を直感的な感じでまとめたい、というところから作り出しています。 (最近の3Dゲームエンジンは Camera がたくさんの情報を持ちすぎる気がする・・・)

Clustered Shading

作成中のゲームで、大きな1つのメッシュモデル(主に地形)の上にたくさんのライトを置きたくなりました。

というところで、Forward Rendering でもライトをたくさん使えるライティング技法であるところの Clustered Shading を実装中です。 うまく動いたらこれを Lumino 標準のライティングにしたいかな。

ちなみに、Lumino は今のところ Deferred Shading をやるつもりはありません。 そういうことができるレンダリングフレームワークは用意してますが、標準機能としては実装しないです。

(メモ) なぜ Deferred Shading をサポートしないのか?

半透明オブジェクトの描画には Deferred Shading は使えないです。 昨今のゲームエンジンは、不透明には Deferred、半透明には Forward のハイブリッドが一般的です。 Deferred は他にも G-Buffer の肥大化とか MSAA と相性悪いとかいろいろありますが、半透明の問題が一番大変。

きらびやかなエフェクトとか、かっこよく、あるいは切なく、Cool に決めたいんです。 日本とか、アジア圏のゲームはそういう半透明オブジェクトを多用することが多いそうです。 自分の作るゲームもそうだし。

あと、Lumino はシェーダを積極的にサポートしてます。(和製のフリーなゲームエンジン・ライブラリとしては珍しいかも?) ハイブリッドでいく場合、ユーザーはそれぞれの特徴に気を付けてコーディングしなければならないです。 学習の負担とか、実行効率とか、レンダリングのクォリティとか、Luminoがターゲットとする小・中規模ゲームデザインとして必要な範囲は何だとか、 いろいろ考えた結果、そんな方向を向いています。 (ライト1000個使えるよ!とか言われても実際にそんなに使わないよね?)

ただ、それよりもつまらない現実的な問題として、業界の外から技術を追うことに対して時間がとりにくくなりそうだってことがあったりします。

葉月と作業報告の候

今月は Blender ばっかり触ってたのであまり進捗無いです。

ただ、Lumino ベースにちょっと大きな変更を入れようといろいろ検討中です。

  • 内部文字コードを統一にする
  • 例外を積極的に使わないようにする

主にプラットフォーム間の互換性と安全性を上げるための対応です。

この2つについては別に記事を作っています。近いうちに上げると思います。

安全性?

Lumino はずっと、パフォーマンス、特に実行速度とメモリ効率を重視して開発してきました。

それはメインターゲットがゲーム開発(リアルタイムアプリ)であることと、C++ の言語コンセプトを強く受けていたからです。

心理的に大きく変わったのは特にここ1年の間です。

ひとつは仕事。仕事はエンタメ的なところからは大きくはなれて、自動車や医療などが関係するところで働いています。"機能安全" で検索。

もうひとつは最近の言語やフレームワーク。 Kotlin や Swift など(まだそんなに語れるほど勉強してないけど)が効率よりも安全を意識した設計をしているのも影響を受けました。

Lumino は「まずは動くこと」を目指して割と急ぎ足で開発してたこともあって 正しく使えば正しく動くけど、間違ったときに安全な方向に倒して動くとか間違えたことを注意してあげるとか、そのへんがすごく弱いです。

ちゃんとドキュメント化すればいいんじゃないの?それだけじゃだめ。

「無知」に対して考えることは重要だって APIデザインの極意 がゆってた。

文月と作業報告の候

Lumino の公開用ページを作りました

https://lriki.github.io/lumino/

ものすごくプロトタイプです。実用レベルには程遠いです。でも、雰囲気は伝わるかな。

FontAwesomeアイコン描けるようにしました

ネタゲーがはかどりそうなアレです。 f:id:lriki:20170730235954p:plain

アイコンの名前とサイズを指定するだけで、スプライトとして使えます。

auto icon1 = GlyphIcon2D::create("fa-globe", 64);
auto icon2 = GlyphIcon2D::create("fa-play-circle-o", 64);
auto icon3 = GlyphIcon2D::create("fa-android", 64);
icon1->setPosition(32, 32);
icon2->setPosition(96, 32);
icon3->setPosition(160, 32);

アイコンの名前はこちら。 Font Awesome Icons

後々FontAwesome以外のフォントを自由にインポートできるようにしてみたいけど、まずはここまで。

今後について

実際にゲーム作りながらバージョンアップしていきます。

ゲームのためのゲームエンジン。これだけビジュアル面の機能詰め込んだのはそれなりのもの作りたいからだ。 でも、早くても半年くらいはかかるかなぁ・・・。

水無月と作業報告の候

また1ヶ月コースの長いブランチを作ってしまいました。悪い癖です。

オフスクリーンレンダリングの実装中

作成中のゲーム、最初にその作品の中で一番の山場にあたりをつけてざっくりと作ってみています。 そこでどうしてもやりたい演出があるのですが、それに必要なので機能を追加しています。

鏡とかね。

f:id:lriki:20170630211918j:plain

空の作成中

この表現も重要なので作ってます。

f:id:lriki:20170630212106j:plain

プロファイラの実装中

テスト戦略成功の秘訣は日常的な実施である。

ということでパフォーマンステストしやすいように簡易的なプロファイラを作っています。

f:id:lriki:20170630212335j:plain

コーディングスタイルの変更(というか回帰)

関数名をすべて lower camel case に直しました。何年か前に upper に直してから、やっぱり lower に戻した感じです。

勤めている会社にまつわるいくつかの事情から upper にしていましたが、もうそういうのは気にしなくていいかなというのと、

  • WinAPI 関数名(マクロ)とのバッティングがとても、とてもつらい

  • C++11 の range based for とか STL とか、標準が lower 要求してるし

など諸々の理由で lower にしました。

upper にした時はある意味後ろめたい理由だったけど、今回はそうじゃ無いのでまるっきりバカな変更にはならない・・・はず。

今後

思うところあって7/7に一度リリースしようとインストーラやサンプルを準備していたのですが、多分無理そうです。 ちょっと仕事忙しくなっちゃった。

7月中には出したいですね。というか、もうブログ始めて半年だ。ダラダラしないようにしよう。

皐月と作業報告の候

新しくゲームを作り始めました

ゲームエンジンはゲームを作るためのものです。使ってみてはじめてわかる粗がたくさんあるわけです。その修正でまだAPIは安定しません・・・。

今後はゲームを作りながら、その都度 Lumino のほうも更新していきます。

f:id:lriki:20170531205554p:plain

ちなみにキャラ2D、マップ3Dなゲームが好きです。

シーングラフへの Entity Component System の導入

ゲームというのは仕様変更が頻発しがちな、ある種の特殊なシステムです。

変更に対して柔軟に対応するため一般的にはオブジェクト指向で作りますが、 is-a の関係を簡単に破壊するような変更よく起こります。

その解決策のひとつが Entity Component System です。Unity をはじめとして昨今のゲームエンジンはみんな備えているアレです。 機能を部品として分割し、オブジェクトは部品の集合体として定義する、そんな仕組みを入れています。

シリアライズ

Jsonファイルを簡単にシリアライズできる機能を作りました。 boost::serialization と同じようなものですが、Object を継承したオブジェクトをシリアライズする場合、型情報を利用した高度なシリアライズが可能になっています。

class EventData : public Object
{
public:
    String          name;
    float          x;
    float          y;
    float          z;
    String          textureName;
    List<Variant> commands;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(name);
        ar & LN_NVP(x);
        ar & LN_NVP(y);
        ar & LN_NVP(z);
        ar & LN_NVP(textureName);
        ar & LN_NVP(commands);
    }
};

struct MapData
{
    String                  displayName;
    String                  meshName;
    List<RefPtr<EventData>> events;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(displayName);
        ar & LN_NVP(meshName);
        ar & LN_NVP(events);
    }
};
{
    "displayName": "map1",
    "meshName": "MapMesh1.mqo",
    "events": [
        {
            "name": "EV000",
            "x": 1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        },
        {
            "name": "EV001",
            "x": -1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        }
    ]
}
MapData data = JsonSerializer::Load<MapData>(Assets::LoadText("MapData.json"));
data.events[1]->name;   // => "EV001"

とりあえずマップデータとかをツクールMVライクな JSON で用意していく方向で。

ビルド方法

Lumino のビルド方法を簡単にまとめました。 https://github.com/lriki/Lumino/wiki/BuildingWindows

卯月と作業報告の候

もうちょっとレンダリングフローの見直し中

前月に引き続き。

2D/3Dシーンと UI モジュールの関係がかなり変わってきています。

前はシーンのほうが偉くて、それの後描画的な立ち位置で UI の描画が入り込んでました。 今は UI のほうが偉くて、UIFrameWindow クラスをルートとした、ちょうど WPF や HTML のような UI要素の階層構造の一部(Canvas とか)としてシーンが描画されるようになりました。

なんでこんなふうになったの?

  • Lumino で作るゲームのエディタも Lumino で作りたい。
  • 本業ではネイティブアプリの GUI とか作ってるんだけど、そういうフレームワーク作ってみたくなっちゃった。

ウリというかコンセプトもちょっと変わってくると思います。 今までは「ゲームエンジン」だったけど、それプラス、「リアルタイムグラフィックスアプリケーション」。 なんかかっこいい。字面が。

あと、描画はコールバックの外側でもできるようになりました。 コードはこんなイメージ。

void Main()
{
    // コードをコンパイルして Shader オブジェクトを作る
    auto shader = Shader::Create("test.fx");
    
    while (Engine::UpdateFrame())
    {
        // Lumino 自体の描画処理
        Engine::RenderFrame();
        
        // 画面全体への、シェーダを用いた直接描画
        auto* r = Engine::GetSceneGraph3D()->GetRenderer();
        r->SetShader(shader);
        r->DrawScreenRectangle();

        // 描画結果をウィンドウに表示する
        Engine::PresentFrame();
    }
}

ピクセルシェーダをゴリゴリ書くとこんなのが出たりします。 f:id:lriki:20170430220705p:plain

ドキュメントとホームページ

ドキュメント書きたい。でもデザインめんどい。

こんなことがしたい。

  • Markdown で書ける
  • カテゴリやtocが柔軟に管理できる
  • 全文検索できる
  • 拡張しやすい
  • ソース管理しやすい

サイトジェネレータとか今時は星の数ほどあるけど、 がっつりとしたマニュアルやAPIドキュメント作りたいときはイマイチ力不足を感じます。

で、自作とかしてた最中に DocFX を見つけました。 この子はねぇ・・・つよいよ。

自作とかするまえにかなりたくさん調べたつもりだったけど、なんでヒットしなかったのか・・・。無能なだけか。

日本語情報もまだ少ないみたいだからそのうち使い方まとめとこう。

お試し版

ごめんね

弥生と作業報告の候

忙しい仕事が、今日、ようやく終わった。予定。

レンダリングフローの見直し中

今は OnRender コールバックの中とか、決められた範囲でしか描画できないけど それだとデバッグ描画するときにやり辛いので、制限無くどこでも描画できるように調整してます。

ハードウェアでアウトラインフォント書きたい

今の Lumino の文字描画は全部ビットマップベースです。一度テクスチャに書いてからスプライトとして描画しています。 シェーダをサポートしようと決めたときから気になってたのが、シェーダで文字を装飾するときに 普通のメッシュと同じシェーダを再利用しづらいことでした。

もう何年も前からやりたいやりたいで後回しになってましたが、レンダリングアーキテクチャとかパイプラインとかを整理する中で 「やっぱり実現性の検討くらいはやっておいた方がいいよね」と思いました。まる。

f:id:lriki:20170331235108p:plain

そこそこキレイに書けてるんじゃないかな?

テッセレーションは glues を使いました。 後で poly2tri も試してみたいけど、多分 gluTess のほうが FreeType とは相性良さそう?

あと、それだけだとアンチエイリアス無くてとても見れたものじゃなくなるので、 辺の押し出しでアンチエイリアスを表現してます。nanovg と同じやりかた。

で、とりあえず何とか使い物になる品質まで持っていけたと思うんだけど、やっぱり最終的な描画結果はグラボ依存です。 「このピクセルGeForce よりも Radeon の方が濃く見えます」とかすごく細かいところで微妙に差があります。 多分もうこの辺が限界だと思うので、安定して描けるビットマップとは互いに長所短所を補いながら両立していく方向で調整。

最後に

Mac の文字キレイ(羨望)