タグに「JavaScript」を持つ
1〜9件目 / 9件
1

 JSceneKitでobjファイル表示対応した / magicien 

image Polyなるサービスが始まったらしいと聞いて、JSceneKitで作りかけだったobjファイルの表示機能を実装した。例によって必要最低限の機能しか実装していないけれど、Polyからダウンロードできるobjファイルは概ね表示できるはず。


Polyにアップロードされているモデルを眺めていると、明らかに毛色の違うものがちらほら。どうやらVRで作られたものらしい。3Dモデルというと、直方体やカプセルのような形から始めて、ポリゴンを分割したり、凹凸を付けたりしていくのが一般的だと思うのだけれど、VRで作られたモデルは、まるで油絵のように、色の付いたポリゴンをいくつも重ねて作られていた。ポリゴン同士が連結していないので、隙間から向こうが見える。

露骨な三角形や四角形の組み合わせも折り紙のようで嫌いではないけれど、輪郭の曖昧なモデルも味があって良い。もちろん、VRを使わなくても同じ表現はできるのだろうが、VRがあるからこそ生まれた表現だろう。

2017/11/11(Sat) 04:59:09

 GitHubから取得したファイルをindexedDBに保存する / magicien 

ブラウザ(JavaScript)からGitHubのリポジトリのアーカイブを取得・展開するの続き。
展開したファイルをIndexedDBに保存する。


DBのオープンやデータの更新を要求すると、戻り値として IDBRequest のオブジェクトが返ってくる。このオブジェクトの onsuccess と onerror に関数を設定すると、処理が終わった時に呼び出される。ソースはこんな感じ。例によって検証していないので、typoとかバグとかあるかも。

リクエストを発行してからでないとコールバック関数が設定できないのは設計がおかしいんじゃなかろうか。
とりあえず、getResult関数でonsuccess、onerrorをPromiseでラップしておく。transactionの場合は、イベント名がちょっと違って、oncomplete、onerror、onabort になる。ディスクの空き容量が足りないと、QuotaExceededError が起きて onabort が呼び出される。手元のMacBook Airだとこのエラーが頻繁に起きる。
IndexedDBで使える容量はブラウザや状況(ディスクの空き容量)によって変わるようで、ひどい時はChromeで容量上限が10KBだった。おまえ、キャッシュとかswapとかで軽く数GB食いつぶすくせにIndexedDBには厳しすぎない?まぁ無いものは無いのでエラーを出して終了するしかない。Chromeを終了してディスクを解放してから再起動すると上限が1GBに増えていたりする。

2017/10/08(Sun) 07:46:30

 ブラウザ(JavaScript)からGitHubのリポジトリのアーカイブを取得・展開する / magicien 

需要がありそうで全く無い、ブラウザのJavaScriptで、GitHubからリポジトリをzipで落とす方法。

GitHubのAPIドキュメントによると、GET /repos/:owner/:repo/:archive_format/:ref でアーカイブがダウンロードできるらしい。
archive_formatは、zipball か tarball の二択。ref はブランチ名とかタグ名とか。リクエストを送ると、302のリダイレクトでダウンロードURLを教えてくれる(https://codeload.github.com/... とか)。

問題は、codeload.github.comがクロスドメインを許可していないこと。JavaScriptでダウンロードしようとすると、ブラウザ側で自主規制がかかって失敗する。仕方が無いので、サーバを経由してダウンロードさせることにする。サーバがGitHub Appのアクセストークンを持っているなら、プライベートなリポジトリもダウンロードできる(5分間だけ有効なダウンロードURLを教えてもらえる)ので、これが正しい方法なのだろう。

ソースコードはこんな感じ。実際に使っているものを抜粋・編集したものだけれど、未検証なので動かないかもしれない。
ここぞとばかりにasync/awaitを使ってみたが、.then.then.thenでも良いと思う。

続き:GitHubから取得したファイルをindexedDBに保存する

2017/10/06(Fri) 06:34:12

 Undo/RedoできるCanvasを作った / magicien 

まーた言ったそばから脇道に逸れているわけ。
HTMLCanvasElement (CanvasRenderingContext2D) に undo/redo機能が欲しいな、と思ったので作りました。
undo-canvasデモページ1

CanvasRenderingContext2Dのプロパティに片っ端からフックをかますという荒技を使い、変更したプロパティ、呼び出したメソッドを全て記録しています。
副産物として、上書きしたメソッドを prototype で定義されているものに戻す reset-object を別モジュールとして作りました。

使ってみたい人は、scriptタグでundo-canvasを読み込んでください。
<script src="https://cdn.rawgit.com/magicien/undo-canvas/v0.1.2/undo-canvas.js"></script>
ブラウザ向けnpmモジュールを作っている人は、npmコマンドでインストールできます。
npm install --save undo-canvas
jsファイルを読み込むと、グローバル変数として UndoCanvas が定義されるので、
const canvas = document.getElementById('myCanvas')
const context = canvas.getContext('2d')
UndoCanvas.enableUndo(context)
こんな感じで、UndoCanvas.enableUndoにコンテキストを引数として渡してください。contextに undo/redo 関数が追加され、以降、このcanvasに対する操作が記録されていきます。
undo/redoしたくなったら、
context.undo()
context.redo()
で行ったり来たりできます。
undo/redoが必要なくなったら、disableUndoで無効化できます(記録されていた操作履歴は全て消えます)。
UndoCanvas.disableUndo(context)
その他機能はGithubの記載を更新していくので、そちらを参照してください。

undo/redoを実現する仕組みはOracleのようなDBの考え方をぱく参考にしたのですが、思いの外うまくハマったので、気が向いたら解説記事でも書くかもしれません。

さて、今度こそエディタ制作作業に戻ろう...

2017/09/06(Wed) 04:58:25

 React/Reduxでゲームエディタを作る時のメモ / magicien 

色々と脇道に逸れていたけれど、ゲームエディタを作り始めることにした。
フレームワークは勉強も兼ねて React/Redux を使ってみることにする。この選択が正しいかどうかは分からない。間違っていたら作り直せば良いので気楽に行こう。

以前Reactをほんの少し触ったことはあったけれど、存在意義も含めほぼ理解できずに終わったので、今回は1から勉強し直す。以下、困ったことリスト。回答とか感想とかは未来の自分に任せることにする。React/Reduxの正しい設計が知りたくて見に来た人は参考にしてはいけない。

モジュールのバージョンは?

とりあえず最新のものを使おう。React v15.6.1、Redux v3.7.2。それと、React Routerがバージョンによって大きく挙動が変わるみたい。現状最新の React Router v4.2.0 を使う。色々試した結果、react-routerを使わずに、redux-first-routerを使うことにした。モジュールは随時最新化していくことにする。

デザインをどうするか

色々探してみたけれど、結局Bootstrapを使う。というか、他に選択肢が見つからなかった。自由度も良い感じに制限されていて迷わなくて済むし、もうこれで良いんじゃないかな。デフォルトだとボタンの余白が大きいのが少し気になる。スマホ向けには良さそうだけど、PC向けにはもうちょっと小さくしたい。

各国語対応はどうするか

後になってi18n対応するのは大変そうなので、予め設計を考えておく。react-boilerplateがi18n対応していたので、それを参考に実装する。ソースコードに日本語を埋め込むのも微妙だし、デフォルトの文言は英語にしよう。英語用のメッセージ一覧は無くても良いけれど、他の言語に翻訳する時にメッセージ一覧があった方が便利なので用意する。というか自動生成したい。

ルーティングはどうするか

最初、react-routerを使っていたけれど、URLに応じてコンテンツを変更しようと考えたときに、redux-first-routerの方が楽だということに気づき、乗り換えることにした。そのページを表示する直前・直後に実行する処理も書けるし便利。
redux-first-routerを使うと、ページを移動時にURLに応じたactionがdispatchされるようになる。逆に、actionをdispatchすることでページを移動させることも可能。このactionを使ってURLとstoreをうまいこと同期させてあげれば、コンポーネント側でURLを気にしなくても良くなる。いちいちdispatchしなくてもurlのリンクを書いておけばそれに応じたactionが実行される。便利。
この変更によって、redux-sagaは要らなくなってしまった。

データのフェッチはどうするか

react-boilerplateredux-sagaを使っているので、それに倣ってみる。どうやらGeneratorで実現しているようだ。Sagaの定義はStore/Actionと一緒に置いておく感じかな。actionをdispatchする前にsagaをrunしておく必要があるけど、全ページで全sagaをrunするのは無駄かな。全部runするか、ページ毎に対象を指定するか、うまい方法が無いかは実装しながら考えよう。

今のbabelの設定だと、async/awaitが使えるので、そっちの方が良い気がしてきた。Sagaをrunするとactionの受け取り手が無駄に増えちゃったりするし。GeneratorでもPromiseでも使えるみたいなので、async/awaitに全て移行してしまおう。

テストはどうするか

boilerplateだと各jsファイルの横にtestsディレクトリを作ってテストを書いているようだ。モジュールはJestを使っている。この構成も倣ってみよう。使い方はmocha/chaiとほとんど同じかな?

Undo/Redoはどうするか

これも後から対応するの大変そうだから先に考えておきたい。
大部分のデータは、redux-undoで実現できそう。undo/redoしたい粒度でデータをhistory/futureにとっておく。問題はどういう粒度で何のデータを保存しておくか。エディタのメイン画面はマップエディタにしたいと考えていて、マップの地面の高低はheightmapのテクスチャという形で保存したいと考えている。
この場合、画像編集のデータをどう保存すれば良いのか。画像を丸ごと保存してしまう?グレースケールだから行けそうか...でも変更箇所の差分だけ取っておく方が良いよなぁ。それだとredux-undoの使い方にはマッチしない。Photoshopの実装がどうなっているか知りたい。DBみたいに適当なタイミングでチェックポイント(画像丸ごと保存)を作って、そこからredoが妥当かなぁ。よくよく考えたら、動画フォーマットもそんな感じだよね。でも実装がめちゃくちゃ複雑になりそう。→ Undo/RedoできるCanvasを作った

データの保存はどうするか

一時保存は localStorage IndexedDB へ、永続化はGithubへ、という感じにしたい。Github APIはまだ動作確認中。→GitHub Appsでリポジトリにファイル追加・更新
クライアント側で何を使うかも悩みどころ。IndexedDBはそのままでもシンプルかつ高機能なので、特にラッパー無しでもいけそうな予感。まさか仕様としてブラウザ上にDBが実装されるようになるとは。

ファイル構成をどうするか

Action/Reducer/Selector/Storeを1ファイルにまとめる

React/Reduxの実装例とか見ると、ほとんどの例でファイルが粉々に分割されていて非常に読み辛かった。なので、ducks-modular-reduxを参考に一ファイルにまとめてしまうことにした。テストするときに困るだろうか。

ContainerとグローバルのStateを分離する

ContainerとAction/Reducer/Stateが一対一で対応している例が多かったけれど、その構成のままContainerを100、200と増やしていくと地獄と化すのが目に見えているので、分離することにした。(実装例の規模が小さすぎて1組ずつしか無かっただけかもしれない)
Containerとデータ構造・操作をくっつけると再利用性が上がるのってReduxを使っていない場合の話だよね...?同じデータを複数のContainerで使う時にどう実装すべきか分からない。ContainerにStateを管理させると、設計がぐちゃぐちゃになって拡張が難しくなるようにしか思えない。

最上位のContainer(ページ)は別ディレクトリに分離する

Container同士にも親子関係があるので、同一ディレクトリにフラットに並べると分かりづらい。各画面の最上位のContainerは別ディレクトリに分けることにした。後でページ毎にwebpackするjsファイルを分けるのにも便利じゃないかと思う。

jsのファイル名はindex.jsにする

importするときにファイル名まで書かずに済むから楽なんじゃないかと。

ファイル構成(暫定)

app
├index.html HTMLの外枠。
├app.js  Storeの生成、Router、i18nの仕込み。
├pages  最上位のContainer(ページレイアウト)置き場。
│└pageA
│ ├index.js react-helmetでヘッダ上書き。必要に応じてconnectする。
│ ├Component.js そのページ専用のコンポーネントは一緒に置いてしまう。
│ ├messages.js 翻訳対応するメッセージ一覧
│ └tests
│  ├index.test.js index.jsのテスト
│  └Component.test.js Component.jsのテスト
├containers 各ページ共通で使えそうなパーツ置き場
│└containerA i18n用コンテナとか。
│ ├index.js React.Component等。必要に応じてconnectする。
│ ├messages.js 翻訳対応するメッセージ一覧
│ └tests
│  └index.test.js index.jsのテスト
├components 各ページ共通で使えそうなパーツ置き場
│└componentA ボタンとか。
│ ├index.js React.Componentか関数。connectしない。
│ ├messages.js 翻訳対応するメッセージ一覧
│ └tests
│  └index.test.js index.jsのテスト
├redux
│├configureStore.js Storeを生成。Middleware、Reducer取り込み。
│├reducers.js modulesからreducerを一括でロードする。
│├routesMap.js URLとpagesのマッピングを書く。
││       ページ毎のデータフェッチとかアクセス制御(誘導?)とかも書いておく。
│├modules ActionType/Action/Selector/Reducer置き場
││└moduleA
││ ├index.js 全部まとめて一ファイルに。reducerをexport defaultする。
││ └tests
││  └index.test.js index.jsのテスト
│└tests
│ ├configureStore.test.js 
│ └reducers.test.js
├translations
│├en.json 英語用メッセージ。自動生成したい。
│└ja.json 日本語用メッセージ
├config
│└index.js convictを使って説明とか型とか書いておく。
└tests
 └app.test.js app.jsのテスト

Code Splittingはどうするか

webpackするときに、ページ毎のjsと共通のjsをそれぞれ別ファイルにしたい。
React + Reduxを使った大規模商用サービスの開発」だと、require.ensureを使っていたけれど、例によってReactRouterの仕様変更によりそのままでは使えなかった。
でもReactRouterのページにCode Splittingの方法が書いてあったので、それをそのまま使うことにした。bundle loaderがうまいことやってくれるらしい。生成されるjsファイルは、0.[ハッシュ].chunk.js、1.[ハッシュ].chunk.js みたいになっていて、ページ数が増えると数字がずれたりする。上の資料だと、モジュールの並び順を制御してハッシュ値が変わらないよう工夫していたけれど、今回はそこまで神経質にしなくても良いかな。
react-routerを使うのをやめたので、代わりにuniversal-importを使うことにした。大してコード修正せずにそのまま乗り換えられたので一安心。

Stateに溜まったゴミはどうすればいいのか

画面遷移を続けていくと、Stateにゴミデータがどんどん溜まっていく。データの後処理はどうするのが良いのかな?これもReduxを使うが故の悩みだろうか。

ネット上の情報はいろんなバージョンのReactを使っていてよく分からない

バージョン間の差異が大きすぎる。どうしたらいいんでしょうか。とりあえず半年以上前の記事は話半分で読むことにする。

参考になったもの

React + Reduxを使った大規模商用サービスの開発

画面とかStateとか大量にある場合の設計の実例。実サービスとして作っただけあって、実践的。本格的に作り始める前にこの資料を見つけられて本当に良かった。性能測定はどうやっているのだろう。

ducks-modular-redux

action-types/actions/reducersを別々のファイルにしてもあんまり意味がないから一つのファイルにまとめちゃおうという提案。自分も同じ疑問を持っていたので、しっくりきた。

react-boilerplate

実際にReact/Reduxのアプリを作る上で最小限必要なものが一通り揃っている。i18n対応しているのが非常に素晴らしい。Herokuへのデプロイに対応しているのもポイント高い。React Router v3だったので、残念ながらそのまま使うことはできなかったけれど、大いに参考になった。

wp-calypso

WordPressのReact/Redux実装。大規模アプリの実例として参考になった。規模が大きすぎる気がしなくもない。ライセンスがGPLなので要注意。

2017/09/02(Sat) 18:32:24

 JSceneKitでMMDモデル・モーション表示デモ公開 / magicien 

紹介動画をアップしました。

デモページはこちら。
ニコニ立体ちゃん(アリシア・ソリッド)のモデルを初めて表示したとき、パンツ丸見えじゃん...どこがどうバグったらこんなことになるんだ...と思って、公式サイトを確認しに行ったら、元から丸見えだったよ!せっかく縦回転の角度に制限付けたのに台無しだよ!

2017/08/13(Sun) 09:58:25

 TGA画像表示用ライブラリを作った / magicien 

とあるモデルを表示しようとしたら、テクスチャがtgaファイルのためブラウザでうまく読み込めなかった。
仕方ないのでパーサを作り、ついでにnpmのパッケージとして公開した。

Githubページはこちら
調べてみたらTGAは結構古いフォーマットみたいで、今後ブラウザがサポートするのは望み薄。需要はほぼ無い気がするけど、WebGLでtgaのテクスチャを使いたい人が自分以外にもいるかもしれないので、パッケージ化しておくことにした。

サンプルページにはレナさんの画像を使わせていただいた。Gimpでtgaに変換したものを表示させてみたが、うまくいったようだ。画像処理系はサンプルに困ったらレナさんを使えばいいや、というのがあって気が楽で良い。

3Dでサンプルとして自由に使えそうなのだと、ティーポットとかうさぎとかだろうか。でもMMDみたいにボーンがあって、さらにアニメーションも含めて気兼ねなく使えて、自由に共有できるものがなかなか無い。

2017/08/05(Sat) 13:50:10

 Webpack の devServer でファイル毎のContent-Type(mime)を設定する / magicien 

この方法で正しいのかよく分からないけど、とりあえず動いたので忘れないうちにメモしておく。


webpack-dev-serverで、.exr という拡張子のファイルの Content-Type を "image/x-exr" で返したいんだけど、どうすればいいのかな?と思って適当にいじってたらなんか動いた。
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
new WebpackDevServer(webpack(config.webpack), config.webpack.devServer).listen(...)
devServerを初期化するときは、こんな感じで config.webpack.devServer をオプションとして渡しているのだけれど、このオプションをこんな感じで書いた。
{
    staticOptions: {
        setHeaders: (res, path, stat) => {
            if(/.exr$/.test(path)){
                res.setHeader('Content-Type', 'image/x-exr')
            }
        }
    }
}
思いっきり関数埋め込んでますが。まぁ大改造したわけでもないし、これで良しとする。

ブラウザで表示してみて気づいたけど、exrファイル対応してるのSafariだけっぽい?そもそも読み込めたとしてどうやってシェーダに送るかという問題もあるか。データ構造自体はそんなに複雑じゃないっぽいけど、いざパーサを書こうとすると、データが圧縮されてるのがきつい。ぐぬぬ...
2017/06/23(Fri) 14:31:12

 JSceneKit:SceneKitをJavaScriptで動かすライブラリ / magicien 

思うところあって、JavaScriptでSceneKitを動かそうと悪戦苦闘中です。

JSceneKitについての動画を2つほどアップしました。





ソースはこちら、サンプルページはこちら
(SafariはWebGL2未対応なので動きません。WebGL2.0の仕様のドラフトの第一著者がAppleの人なのに...)

実際、Appleの初期のサンプル(Fox)については、ほぼ動くようになりましたが、物理演算が大きな壁として立ちはだかっています。
本家SceneKitでは、BulletPhysicsに少し(かなり?)手を加えたものを使っているようです。
Concaveの剛体の衝突判定が何であんなに速いのかわけがわからん...
ammo.jsを試してみましたが、メモリ不足で落ちてしまい、途方に暮れております。

そこへSceneKitの新たなAPI登場で追い討ちをかけられている状況なわけです。
WWDC2017ではFoxの続編、Fox 2のデモが行われ、Foxと比べてすごくゲームっぽく(クラッシュバンディクーっぽく)なったな、という印象です。ジャンプと攻撃のアクションが増えたことで、UIも前作より良さげですね。
(動画はこちら。Fox 2のデモは1:40〜)

被写界深度、モーションブラー、SSAOは頑張って実装するとして、GLES3.0でテセレーションはハードル高い...時が解決してくれるのを待つべきでしょう。そういえば、GameplayKitも使われていましたね...こちらは何とか実現したいところ。

2017/06/15(Thu) 07:25:02