Three.js: モジュラーアーキテクチャへの移行

作成日 2014年05月06日  ·  153コメント  ·  ソース: mrdoob/three.js

Browserify
このアーキテクチャへの移行には、長所と短所があります。 あなたの考えを追加してください。

注:これには、three.jsコンシューマーがbrowserifyを使用する必要はありません。

Suggestion

最も参考になるコメント

そうです、少しリファクタリングが必要になります...

見つけた! このスレッドがここ数日で活発になったので、私はthree-jsnextにもう少し取り組んできました。 これは、既存のThree.jsコードベースを取得し、それをESモジュールに自動的に変換するプロジェクトです。 いくつかのトリッキーな周期的依存関係(特にKeyframeTrack前後)と格闘しているだけですが、すぐに共有できるものがあるはずです。 私の知る限り、すべての例は引き続き機能し、縮小されたビルドは現在のビルドよりも小さいため(Rollupを使用してUMDファイルを生成)、すべて朗報です。

全てのコメント153件

1つの利点は、これにより、three.jsの進行中の開発にモジュラーアーキテクチャが適用されることです。

node / browserifyの一般的なスタイルでは、各ファイルの上部に依存関係が宣言されており、グローバル変数はアンチパターンと見なされます。

スニペットの例を次に示します。

// src/geometry/BoxGeometry.js
var Geometry = require('./Geometry.js');
var Vector3 = require('../core/Vector3.js');
module.exports = BoxGeometry;

function BoxGeometry() {
  // ...
}

BoxGeometry.prototype = Geometry.prototype;

もう1つの利点は、browserifyを使用するthree.js消費者が、必要なパーツを選択できることです。 SceneBoxGeometryPerspectiveCamera 、およびWebGLRendererインポートするだけで、これらすべての依存関係を自動的に取得できます( Object3Dなど)。必要な機能セットだけをサポートするJavaScriptの小さなバンドルがあります。

これは、重大な変更を課さない方法で行うことができます。 トップレベルでは、標準パッケージの一部であると見なされるすべてのクラスをエクスポートします

// src/three.js
var THREE = { rev: 101 }
module.exports = THREE

THREE.Geometry = require('./geometry/Geometry.js')
THREE.BoxGeometry = require('./geometry/BoxGeometry.js')
// ...

注:このファイルはほぼ排他的にrequireステートメントであるため、この例の上部にある依存関係を正確に要求しているわけではありません。

最後に、モジュールシステム(ノード/ブラウザー化、AMD)が使用されているかどうかを検出し、使用されている場合はエクスポートするか、グローバルオブジェクト( window )に追加するユニバーサルモジュール定義でラップします。

確認してみましょう:

  • 優れたモジュラースタイルを実施
  • three.js消費者がbrowserifyを使用して機能を選択できるようにします
  • 重大な変更はありません

これにはビルドシステムの交換が必要になりますが、新しいものは非常に簡単です。

その他の利点:

  • コードを構造化できます
  • グローバル名前空間を汚染することなくモジュールを作成/再利用できます
  • あなたは生産のために構築することができます
  • すべてのモジュールには独自のファイルがあるため、大きなthree.jsファイルで対応するモジュールを検索する必要がないため、より簡単にデバッグできます。

@ shi-314少し混乱していると思いますが、 You can structure your codeYou can build for productionは、アーキテクチャを変更せずにできることですか? three.jsソースまたはthree.jsを使用して構築されたものについて話しているのですか?

commonjs環境での使用を難しくするthree.jsが使用する1つの方法は、 instanceofです: https

これは、アプリケーションでは、ソースツリーに同じライブラリの異なるバージョンが含まれることが多いため、instanceofのチェックは同じライブラリの異なるバージョン間では機能しないためです。 commonjsモジュールシステムへの移行の準備として、これらのinstanceofチェックをGeometry.isGeometry(geom)スタイルのインターフェイスの背後にある機能チェックに置き換えるとよいでしょう。

@kumavis私は

THREE.MeshMyCoolMaterial = function (...) { ... }

しかし、Browserifyがあった場合、あなたができるよりも:

var MeshLambertMaterial = require('./../MeshLambertMaterial');
var MeshMyCoolMaterial = function (...) {...}

したがって、名前空間は一貫性を保ち、コードでTHREE.MeshLambertMaterialMeshMyCoolMaterialを使用する必要はありません。

そしてYou can build for production私は基本的にあなたが言ったのと同じことを意味しました: allows three.js consumers using browserify to pick and choose functionality

@ shi-314ありがとう、それはもっとはっきりしています。 これは、コンシューマー定義クラスの逆シリアル化に対して提案された一般的なソリューションに影響を与えます。

// given that `data` is a hash of a serialized object
var ObjectClass = THREE[ data.type ]
new ObjectClass.fromJSON( data )

これは私の提案したシリアル化/逆シリアル化リファクタリングからのものです
https://github.com/mrdoob/three.js/pull/4621

このような変更によってパフォーマンスが影響を受けることはありません。

これはかなり大きな変更ですが、私も賛成です。

その他の主な利点:

  • browserifyのstandaloneオプションを使用して、UMDビルドを生成できます。 UMDラッパーを手動でいじくり回す必要はありません。
  • パッケージはbrowserify / NPMのユーザーが簡単に利用できます
  • threejsの依存関係(poly2tri、 color-stringなど)の取得がはるかに簡単になります
  • レンダリングライブラリ(ベクトル/数学ライブラリなど)に「実際には属していない」モジュールは、個別のNPMモジュールとして引き出して、他の多くのタイプのプロジェクトで再利用できます。 これの主な利点の1つは、個々のモジュールにバグ/問題、PRなど(ThreeJSの問題のクリーンアップ)用の独自のリポジトリがあることです。
  • NPMがセマンティックバージョニングを処理します。 たとえば、全員のコードの解読を心配することなく、 threejs-vecmathに重大な変更を加えることができます。 反対に、特定のモジュールでパッチまたはマイナーリリースを作成すると、それらのモジュールを使用するユーザーは自動的に変更を取得できるようになります。
  • EffectComposerやさまざまなシェーダーのような「エクストラ」を簡単にパッケージ化して使用できるようにします( npm install threejs-shader-bloom想像してください)。
  • モジュールが引き出されると、最終的な配布サイズは小さくなり、アプリケーション固有になり始めます。 アプリが実際に使用しているモジュールをrequire()するだけなので、最終的には異なる「ビルドタイプ」は必要ありません。

@mrdoobと他の作者に; NPM / Browserifyの経験があまりない場合は、NPM / Browserifyを使用していくつかの小さなプロジェクトを作成し、その「哲学」を感じ取ってみることをお勧めします。 ThreeJSアーキテクチャとは大きく異なります。 大きなフレームワークではなく、多くの小さなことを奨励し

このアプローチのもう1つの利点は、オープンソースのサードパーティのThree.JSモジュール、特にシェーダー、ジオメトリ、モデルローダーなどのエコシステムが存在する可能性があることです。NPMまたはGithub / Componentを通じて公開され、人々は簡単に参照して使用できます。 現時点では、人々が「ソースを表示」するデモをホストすることで、コンテンツが共有されています。 Three.JSはもっと価値があります!

私がThree.JSで抱えている問題の1つは、コードが現在のバージョンのThree.JSとどれだけ早く互換性がなくなるかということだと思います。 このようなものに切り替えることのもう1つの利点は、Three.JSの_bits_の特定のバージョンを指定できることです。これは非常に強力で便利です。

+1

CommonJS / browserifyアーキテクチャの+1を使用すると、コアがより軽量になり、サードパーティ製であっても拡張機能が適合します。

three.jsを小さなモジュールに断片化することにも多くのコストがかかります。 現在のシステムでは、非常に単純なサードパーティのアドオン(例:jetienneのTHREExモジュール)を使用できます。 JSモジュールシステムがビルドシステムの単なるラッパーである限り、現在のセットアップの単純さについては多くのことが言われています。

ビルドサイズを最小化する別の方法は、ClojureScriptが行うことです。 これらは、GoogleのClosureコンパイラがプログラム全体の分析とデッドコードの除去を実行できるようにするためのいくつかの規則に従います。

シンプルさの評価されていない、そしてしばしば見過ごされている優雅さのための+1

+1

three.jsを小さなモジュールに断片化することにも多くのコストがかかります。 現在のシステムでは、非常に単純なサードパーティのアドオン(例:jetienneのTHREExモジュール)を使用できます。

ここでの考え方は、UMDビルドが非ノード環境に引き続き提供されるということです。 THREExのようなプラグインは、単純な<script>タグを持つThreeJSに依存するプラグインでも同じように機能します。

トリッキーなことは、CommonJS環境にいる場合、特定のプラグインをどのようにrequire()するかということです。 たぶんbrowserify-shimが役立つかもしれません。

JSモジュールシステムがビルドシステムの単なるラッパーである限り、現在のセットアップの単純さについては多くのことが言われています。

ThreeJSの現在のプラグイン/拡張システムは、操作するのがかなりひどく、「単純」または簡単にはほど遠いです。 ほとんどのThreeJSプロジェクトは、EffectComposer、FirstPersonControls、モデルローダー、またはexamplesフォルダーに浮かんでいる他の多くのJSファイルの1つなど、何らかの形式のプラグインまたは拡張機能を使用する傾向があります。 現在、これらのプラグインに依存する唯一の方法は次のとおりです。

  • ThreeJSの現在のビルドをダウンロードする
  • 必要なファイルをコピーしてvendorフォルダに貼り付けます
  • gulp / gruntタスクを接続して、必要なすべてのプラグインを連結して縮小します。 それらを_正しい順序で_連結するようにしてください。そうしないと、問題が発生します。 プラグインを追加するときに、このリストを手動で管理します。
  • ThreeJSが更新されるたびに、手順1と2を繰り返します。 新しいコードに下位互換性がないことに気付いたら、髪の毛を抜いてください

さて、browserifyを使用すると、次のようなことができると想像してください。

var FirstPersonControls = require('threejs-controls').FirstPersonControls;

//more granular, only requiring necessary files
var FirstPersonControls = require('threejs-controls/lib/FirstPersonControls');

これらのプラグインはrequire('threejs')と、それらが必要とする可能性のあるその他のもの( GLSLスニペットテキストの三角測量など)になります。 依存関係/バージョン管理はすべてユーザーに隠されており、手動で保守するgrunt / gulpconcatタスクは必要ありません。

トリッキーなことは、CommonJS環境にいる場合、特定のプラグインをどのようにrequire()するかということです。

CommonJS forTHREE.jsプロジェクトを少し使用しています。 これは少し手動のプロセスであり、他の人のコードのチャンクをモジュールに変換します。作成者や寄稿者によって変換されていないレガシーコードの場合、それを回避する簡単な方法はないと思います。

重要な点は、「標準」のTHREEオブジェクト全体をエクスポートするモジュールがあることです。これは、拡張したいすべてのオブジェクトで必要になる可能性があります。

var THREE = require('three');

THREE.EffectComposer = // ... etc, remembering to include copyright notices :)

これは私にとって非常にうまく機能しました。特にプロジェクトが成長し、独自のシェーダーとジオメトリを独自のモジュールなどに追加し始めたときです。

'threejs-full'または 'threejs-classic' npmパッケージがある限り、これはCommonJS環境で古いThree.jsのものを操作するためのかなり実行可能な方法になりますが、これはかなりニッチだと思います!

+1
断片化されたthreejsモジュールがnpm、プラグインで利用可能になったら、私は信じています
開発者はCommonJSenvに移行するのが大好きです。
2014年6月5日午後9時19分、「CharlotteGore」 [email protected]は次のように書いています。

トリッキーなことは、特定のプラグインをどのようにrequire()するかということです。
CommonJS環境にありますか?

CommonJS forTHREE.jsプロジェクトを少し使用しています。 少しです
他の人のコードのチャンクをモジュールに変換する手動プロセスの
レガシーコードではそれを回避する簡単な方法はないと思います
それは作者や寄稿者によって変換されていません。

重要な点は、「標準」全体をエクスポートするモジュールがあるということです。
3つのオブジェクト。これは、拡張したいものすべてが必要とする可能性があります。
それ。

var THREE = require( 'three');
THREE.EffectComposer = // ...など、著作権表示を含めることを忘れないでください:)

これは、特にプロジェクトが成長し、私が成長するにつれて、私にとってはかなりうまくいきました
独自のシェーダーとジオメトリを独自のモジュールなどに追加し始めます。

'threejs-full'または 'threejs-classic'npmパッケージがある限り
これは、古いThree.jsのものを操作するためのかなり実行可能な方法になります
CommonJS環境ですが、これはかなりニッチだと思います。


このメールに直接返信するか、GitHubで表示してください
https://github.com/mrdoob/three.js/issues/4776#issuecomment-45236911

また、たとえばglslifyを使用して、シェーダーをモジュール化することもできます。 そうすれば、オンデマンドでシェーダーを生成するExpressミドルウェアを作成するようなことでも簡単になります。

数ヶ月前、 frame.jsをrequire.jsに移動し、このAMDのものがどのように機能するかをようやく理解しました。

しかし、これを「コンパイル」する方法を学ぶ必要があります。 モジュールのリストからthree.min.jsを生成するためのツール/ワークフローは何ですか?

私はgulp -browserifyプラグインを使用したビルドシステムとしてhttp ://travismaynard.com/writing/no-need-to-grunt-take-a-gulp-of-fresh-air:wink:

いくつかの考え:(もちろん、node、npm、browserifyに関する私の限られた経験に基づく)

  1. node.jsモジュールは素晴らしいと思います(それはnpm、modules、require()sです)
  2. browserifyも素晴らしいと思います

とは言うものの、このスレッドでの議論に続いて、誰もがbrowserifyについて同じ理解を持っているかどうかはわかりません(browserify、commonjs、requirejs、amd、umdは、同じものである必要はないかもしれませんが、いくらか関連しています)。

さて、私の考えの連鎖を少したどっていただければと思います。

  1. JSは素晴らしく、ブラウザ間で高速に実行されます。
  2. うわー、今JSはサーバー側でも実行されます。
  3. それはnode.jsです、それはかっこいいので、node.jsにコードを書いてみましょう
  4. しかし、私は書きたくない/すべてを書くことができない/使用するものを見つける。
  5. 心配ありません。npminstallmodulesを実行してください
  6. これらのクールなモジュールが必要になり、使用できるようになりました。
  7. よく働く!
  8. 待ってください。node.jsで実行されるJSでたくさんのものを書いたところです。
  9. jsはブラウザで想定されていませんか? これらのコードをそこで再度実行するにはどうすればよいですか?

Browserifyが登場するのはここです。 技術的には、ブラウザでrequireJSを使用できます。 ただし、ネットワーク呼び出しをあまり行わずにjsファイルをバンドルしたい(高速なファイルシステムrequire()とは異なります)。 Browserifyが静的分析などの優れた機能を実行して、インポートする必要のあるモジュールを確認し、アプリケーションに最適化されたビルドを作成する場所があります。 (もちろん制限があります。おそらくrequire( 'bla' + variable)を解析できません)node.jsに依存するもののためにエミュレーションレイヤーを必要とする部分を交換することさえできます。 ええ、それは私が今私のブラウザに含めることができるjsビルドを生成します。

これがbrowserifyができることのいくつかですhttps://github.com/substack/node-browserify#usage

これまでのところすべてが素晴らしいように聞こえます...しかし、「browserifyarchitecture」に移行することを検討する価値があると私が考えたいくつかのポイントがあります

  • three.js開発者の考え方を変える必要があります(もちろん、requireモジュールシステムを使用する必要があります)
  • 互換性レイヤーを構築できるため、three.jsユーザーは、モジュラーのメリットを享受することなく、以前の方法でthree.jsを使用できます。
  • 最適化されたビルドを作成できるようにするには、three.jsユーザーはrequireシステムに移行する必要があります
  • 新しいビルドプロセスには、browserifyツールチェーン(現在、python、node.js、または単純なコピーアンドペーストなどを使用できます)またはいくつかのrequireJSツールが含まれる可能性があります。
  • たとえばTrackballControlsのように、各コンポーネントでバージョン管理を行い、three.jsを本当にモジュール化したい場合は、それらを分割する必要があり、それが断片化につながる可能性があります
  • それも多様性につながる可能性がありますが、three.jsの1つの強みは、現在、多くの拡張機能の集中ポイントであるように思われます

したがって、この多様性と便利なモジュールの読み込み(主にnpmエコシステムに乗っている)とカスタマイズされたビルドが素晴らしいものであることがわかった場合は、パラダイムを変更し、コードをリファクタリングし、現在のビルドシステムを変更することをお勧めします。

@mrdoob browserifyに関するいくつかのツールがここにリストされています: https

three.min.jsに関しては、プロジェクトで縮小されたコードを使用しません。 あなたがすべてであるvar three = require('three')あなたにproject.js 、次に実行browserify project.js > bundle.js && uglifyjs bundle.js > bundle.min.js 。 注:縮小したコードは<script src="min.js">発送できます。

私は現在three.jsをでラップしています

if ('undefined' === typeof(window))
  var window = global && global.window ? global.window : this
var self = window

module.exports = THREE

次に、拡張機能を次のようにラップします

module.exports = function(THREE) { /* extension-code here */ }

だから私はそれをそのように要求することができます:

var three = require('./wrapped-three.js')
require('./three-extension')(three)

したがって、これは最適ではありませんが、私は個人的に実際にそれと一緒に暮らすことができ、それほど悪くはないと思います- @ kumavisの提案は_巨大な_利点ですが。

しかし、3つをフォークして、すべてを別々のモジュールに入れて、それがどのように機能するかを確認するのは理にかなっているかもしれません。

また、browserifyに大きく基づいているhttp://modules.gl/もチェックアウトして

@mrdoob @ shi-314 gulp-browserifyは、browserifyを直接(つまり、Vinyl-source-stream経由で)使用することを優先してブラックリストに登録されました。

grunt / gulp / etcのようなツールは常に流動的であり、さまざまな意見がたくさんあります。 結局、どちらを選択するか、またはカスタムスクリプトを使用するかどうかは関係ありません。 より重要な質問は、ユーザーがThreeJSをどのように消費するか、そしてどの程度の下位互換性を維持したいかということです。

もう少し考えてみると、フレームワークとそのアーキテクチャを完全にリファクタリングせずにすべてをモジュール化するのは_本当に_難しいと思います。 ここにいくつかの問題があります:

  • すべての名前空間コードをCommonJSexports / requireに変更する必要があります。 これはかなり大規模な作業であり、醜い../../../math/Vector2などがたくさんあります。
  • 理想的な世界では、ライブラリは断片化されるため、 three-scenethree-lightsなどから切り離されます。その後、各パッケージを個別にバージョン管理できます。 この種の断片化は、ThreeJSのような大きなフレームワークでは非現実的であり、維持するのが面倒です。
  • フレームワークを小さなコンポーネントに断片化していない場合、セマンティックバージョニングは悪夢になります。 フレームワークのどこかにある小さな重大な変更には、全体としてメジャーバージョンのバンプが必要になります。 そして、APIの消費はかなり醜いでしょう: require('three/src/math/Vector2')

私のおすすめ? 私たちは前進する2つのことを考えます:

  1. 小さく始めます。 Vector / Quaternion、色変換、三角測量など、いくつかの重要で再利用可能な機能を引き出します。これらは、ThreeJSの範囲外で役立つため、NPMの候補として適しています。 また、独自のテストスイート、バージョン管理、および問題追跡を行うこともできます。
  2. 新しい機能や依存関係(poly2tri / Tess2など)など、新しいコードをThreeJSに追加する必要がある場合は、別のモジュールとして引き出し、NPMを介して依存させることを検討してください。

すべてがモジュール化されているのを見たいのですが、ThreeJSにとって現実的なアプローチがわかりません。 たぶん誰かがフォークでいくつかの実験をして、どれほど実行可能かを確認する必要があります。

説明してくれてありがとう!

私が恐れているのは、始めたばかりの人々にとって物事を複雑にしていることです。 彼らにこのbrowserify / modulesのものを学ぶように強制することは良い考えではないかもしれません...

ここで@mrdoobに同意する

リポジトリにプリコンパイルされたUMD( browserify --umd )が組み込まれているため、既存の開発者のワークフローに変更はありません。

@mrdoob依存関係管理システムの考え方は単純です。 オプションとビルドシステムに関する数十の投稿を読むのは大変かもしれませんが、最終的には現在のシステムは持続可能ではありません。 あるファイルが別のファイルに依存しているときはいつでも、それは参照を見つけるために新しい開発者が実行しなければならないハントアンドサーチです。 browserifyを使用すると、依存関係が明示的になり、ファイルへのパスがあります。

@repsac依存関係システムは、グローバルスコープを回避し、順序の悪夢ため、他の言語のユーザーがThreeにアクセスしやすくする必要があります。 var foo = require('./foo');は、(大まかに)C#のusing foo;またはJavaのimport foo;似ています。

すべてがモジュール化されているのを見たいのですが、ThreeJSにとって現実的なアプローチがわかりません。 たぶん誰かがフォークで実験して、物事がどれほど実行可能かを確認する必要があります

本当にこれが道だと思います。 仕事を終わらせ、それがどのように機能するかを示してください。

そして、APIの消費はかなりugly: require('three/src/math/Vector2')

実験として、「はじめに」を3つのドキュメントからこの新しいモジュラーアプローチに変換しました。 コードを小さなモジュールに分割することにかなり厳格でない限り、多くの参照があることを想像できます。

これを行う主な利点は、ここで具体的に参照されているものと、それらが依存しているものだけを含めるため、結果のビルドサイズが完全なThree.jsのサイズのごく一部になることです。

必要なすべての依存関係を参照する(そしてそれらをすべて個別にインストールする)ことは、実際には少しひどいことになるかもしれません。

モバイルデバイスを明示的にターゲットにしている場合、この非常にきめ細かいアプローチは完璧ですが、実際には、通常どおりに機能する3つのAPI全体をエクスポートするパッケージが必要であり、次にすべてのボーナスジオメトリをカプセル化する小さなパッケージが必要になると思います。レンダラー、すべての数学、すべてのマテリアルなど、次に個々のモジュールレベルまで下げて、開発者が自分で決定できるようにします。

そして、はい、ウェブのコーディングは苦痛です。

とにかく、実験を続けて...

依存関係をインストールします。

npm install three-scene three-perspective-camera three-webgl-renderer three-cube-geometry three-mesh-basic-material three-mesh three-raf

コードを書いてください...

// import our dependencies..
var Scene = require('three-scene'),
  Camera = require('three-perspective-camera'),
  Renderer = require('three-webgl-renderer'),
  CubeGeometry = require('three-cube-geometry'),
  MeshBasicMaterial = require('three-mesh-basic-material'),
  Mesh = require('three-mesh'),
  requestAnimationFrame = require('three-raf');

// set up our scene...
var scene = new Scene();
var camera = new Camera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new Renderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// create the cube...
var geometry = new CubeGeometry(1, 1, 1);
var material = new MeshBasicMaterial({color: 0x00ff00});
var cube = new Mesh(geometry, material);
scene.add(cube);
// position the camera...
camera.position.z = 5;
// animate the cube..
var render = function () {
  requestAnimationFrame(render);
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  renderer.render(scene, camera);
};
// begin!
render();

次に、ファイルをビルドします

browserify entry.js -o scripts/hello-world.js

それから私たちのページにそれを含めてください

<script src="/scripts/hello-world.js" type="text/javascript"></script>

必要なすべての依存関係を参照する(そしてそれらをすべて個別にインストールする)ことは、実際には少しひどいことになるかもしれません。

Threeがbrowserifyを使用してコードベースを管理するために、エンドユーザーは必ずしもプロジェクトでbrowserifyを使用する必要はありません。 3つは現在のようにグローバルTHREEとして公開できます...ビルドファイルを含めて実行します。

@repsac @mrdoob変更には下位互換性があるため、現在のユーザーは必要がなければ何も変更する必要はありません。 これらの提案は、ThreeJSの広大でモノリシックなコードベースの長期的な保守性と寿命を改善することです。 依存関係やバージョン管理のようなものは、初心者にとっては頭痛の種のように聞こえるかもしれませんが、ThreeJSに加えてツール、フレームワーク、プラグイン、および大規模なWebサイトを開発する人にとっては素晴らしいものです。

つまり、エンドユーザーコードは同じように見える可能性があり、 examplesを変更する必要はまったくありません。

<script src="three.min.js"></script>

<script>
var renderer = new THREE.WebGLRenderer();
</script>

モジュラービルドを探しているより野心的な開発者にとって、またはThreeJSの上に長期的なソリューションを開発しようとしている(つまり、バージョン/依存関係管理を利用する)開発者にとっては、次のようになります。
npm install three-vecmath --save

次に、コードで:

var Vector2 = require('three-vecmath').Vector2;

//.. do something with Vector2

さらに、これにより、ThreeJSのベクトル計算、色変換、三角測量などをThreeJSの範囲外で使用できるようになります。

require()の混乱は悪い考えであり、悪いトレードオフだと思いますが、ユーザーに2つの異なる種類のthree.jsコードを公開し、1つはファンシーモジュールシステムフレーバーで、もう1つはよりシンプルな(しかしセカンドクラスの)モジュールシステムフレーバー。

@erno要点を見逃したと思いますが、 three.jsは内部的にモジュール構造によって編成されますが、これは現在のセットアップと同じビルドファイルを生成するために使用されます。

主な利点は、 three.jsの開発と保守のエクスペリエンスを向上させることです。

@ kumavis-いいえ@ernoは実際にはそれを見逃していませんthree.jsがrequireを介して使用される場合と、そうでない場合は混乱する可能性があることを彼が指摘していることを理解しました(*)。 たとえば、誰かが3つのソースといくつかのサードパーティの例の両方を見て、すべてがどのように機能するかという違いに遭遇します。

(*)これについては、今日の初めにircで話しました。

それは一種の有効なポイントだと思いますが、最終的にどのように機能するか、モジュールとビルドの使用法に問題があるかどうかはわかりません。 しかし、確かに考える価値があるように思われ、全体的な問題がここで慎重に検討されていることは私には良さそうです。これまでの私の部分からの情報と見解に感謝します。

@antontなるほど。 人々はここでさまざまな異なるアプローチを提案しました。私は主にトップレベルの使用法( THREEからすべてを引き出す)のドキュメントを提供すると想定していましたが、他の人はこれに従わない例を作成する可能性があります。混乱を招きます。 そして、それは有効な懸念事項です。

言語に少し戸惑っていたと思います。

もう1つは、より単純な(ただし、2番目のクラスの)モジュールシステムフレーバーです。

これはビルドファイルを参照しているだけですよね?

私の理解では、そうです。 他に何を想像することはできませんが、何かが欠けている可能性があります。

antont、kumavis:ここでの提案は、require()スタイルのコードをエンドユーザーにも公開することについて話しました。たとえば、を参照してください。 mattdeslの最新のコメント。

「モジュラービルドを探している、またはThreeJS上で長期的なソリューションを開発しようとしている(つまり、バージョン/依存関係管理を利用する)意欲的な開発者向け[...]」

より最適化されたビルドを作成する1つの方法は、実際には、依存関係を自動的に把握し、必要なモジュールを解約するスクリプトを作成することです。

現在、bower&browserifyはrequireを使用していませんが、解決策はこれだけではありません。 それを行う既製のオープンソースプロジェクトが他にあるかどうかはわかりませんが(おそらくng-dependenciesのように)、その前にそのようなツールを作成したことがあり、これらの問題を解決する他のアプローチがあると思います。

グーグルのクロージャーコンパイラはそのようなツールかもしれませんか?

ユーザー側では、これは何らかの助けになるでしょうか?
http://marcinwieprzkowicz.github.io/three.js-builder/

それはかなり興味深い@erichlof :) @ marcinwieprzkowiczがこれを手作業で生成したのだろうか... https://github.com/marcinwieprzkowicz/three.js-builder/blob/gh-pages/threejs-src/r66/modules.json

commonjs環境での使用を難しくするthree.jsが使用する1つのプラクティスは、instanceofの使用です: https

これは、アプリケーションでは、ソースツリーに同じライブラリの異なるバージョンが含まれることが多いため、instanceofのチェックは同じライブラリの異なるバージョン間では機能しないためです。 commonjsモジュールシステムへの移行の準備として、これらのinstanceofチェックをGeometry.isGeometry(geom)スタイルのインターフェイスの背後にある機能チェックに置き換えるとよいでしょう。

git / three.js / src内:

grep -r instanceof . | wc -l 
164

git / three.js / examples:

grep -r instanceof . | wc -l 
216

したがって、three.jsでは合計380回のinstanceofがあります。 代替として最適な実装は何でしょうか?

最近、それらのほとんどを置き換えるために使用できるtypeプロパティを追加しました。

最近、それらのほとんどを置き換えるために使用できるtypeプロパティを追加しました。

良い! PRをご用意いたします。

これが別の人気のある大規模なJSライブラリでどのように処理されるかの例については、 https://github.com/facebook/reactを参照して

  1. バニラJSを作成するThree.jsの純粋なコンシューマーは、通常どおりビルドファイルを使用できます。 このユースケースに変更はありません。
  2. browserifyを使用するThree.jsのコンシューマーは、Three.jsをプロジェクトの依存関係として宣言でき、 require固有の依存関係のみを宣言できます。 適切な依存関係管理の利点は十分に文書化されています。
  3. コンポーネント間の依存関係が明示的に文書化されているため、Three.jsへの貢献がより簡単になるはずです。

私はいくつかの研究をしました...

昨日、ファイル間の依存関係を宣言するためにCommonJS require()ステートメントを使用するようにThree.jsソースコードを変換する(かなりばかげた)スクリプトを一緒にハッキングしまし

  1. (WebGLRendererからの)次のようなかなりばかげたrequireステートメントを持つことになります。

var THREE = require('../Three.js'); require('../math/Color.js'); require('../math/Frustum.js'); require('../math/Matrix4.js'); require('../math/Vector3.js'); require('./webgl/WebGLExtensions.js'); require('./webgl/plugins/ShadowMapPlugin.js'); require('./webgl/plugins/SpritePlugin.js'); require('./webgl/plugins/LensFlarePlugin.js'); require('../core/BufferGeometry.js'); require('./WebGLRenderTargetCube.js'); require('../materials/MeshFaceMaterial.js'); require('../objects/Mesh.js'); require('../objects/PointCloud.js'); require('../objects/Line.js'); require('../cameras/Camera.js'); require('../objects/SkinnedMesh.js'); require('../scenes/Scene.js'); require('../objects/Group.js'); require('../lights/Light.js'); require('../objects/Sprite.js'); require('../objects/LensFlare.js'); require('../math/Matrix3.js'); require('../core/Geometry.js'); require('../extras/objects/ImmediateRenderObject.js'); require('../materials/MeshDepthMaterial.js'); require('../materials/MeshNormalMaterial.js'); require('../materials/MeshBasicMaterial.js'); require('../materials/MeshLambertMaterial.js'); require('../materials/MeshPhongMaterial.js'); require('../materials/LineBasicMaterial.js'); require('../materials/LineDashedMaterial.js'); require('../materials/PointCloudMaterial.js'); require('./shaders/ShaderLib.js'); require('./shaders/UniformsUtils.js'); require('../scenes/FogExp2.js'); require('./webgl/WebGLProgram.js'); require('../materials/ShaderMaterial.js'); require('../scenes/Fog.js'); require('../lights/SpotLight.js'); require('../lights/DirectionalLight.js'); require('../textures/CubeTexture.js'); require('../lights/AmbientLight.js'); require('../lights/PointLight.js'); require('../lights/HemisphereLight.js'); require('../math/Math.js'); require('../textures/DataTexture.js'); require('../textures/CompressedTexture.js');

WebGLRenderer(など)を複数のモジュールに分割するなど、いくつかの主要なリファクタリングが必要になります(atmファイルの長さは6000行を超えます)。

  1. GLSLチャンクの解決策を見つける必要があります。 これらのファイルは、コンパイル時にTHREE.ShaderChunkにコンパイルされ、実行時にTHREE.ShaderLib THREE.ShaderChunkにコンパイルされます( THREE.ShaderChunkの配列を結合します)。これは、browserifyだけで行うのはかなり難しいです。 同じことをするbrowserify変換が必要になると思います。

React.jsは、 commonerを使用して、ファイルパスでモジュールを参照せずにモジュールを検索します。 たぶん、同じことをして、 require GLSLファイルをJS構文に変換できるカスタムルールを定義することもできます。

@rasteiner https://github.com/stackgl/glslifyについて学ぶのはとてもうれしいかもしれません、それは成長しているhttp://stack.glファミリーから来ています

私は過去数か月でモジュールと「unixy」アプローチについてかなりの経験を積んでいますが、今では遅すぎると思います。モジュール性またはnpmモジュール用にthreejsをリファクタリングすることは非現実的な目標です。

モジュール性/再利用性の問題に取り組むために私が現在行っていることは次のとおりです。

  • NPMにblur / fxaa / etc用の再利用可能なシェーダーをいくつか配置しています。 これを参照してください:
    https://www.npmjs.org/package/three-shader-fxaa (エンジンに依存しないglsl-fxaaを使用)
  • OrbitControllerやEffectComposerなどの再利用可能なコンポーネントも必要に応じて公開されています。 例えば:
    https://www.npmjs.org/package/three-orbit-controls
  • これらのモジュールは、「3」に依存する代わりに、THREEを取り込んで、ユーティリティクラスを返す関数をエクスポートします。 このようにして、グローバルthreejsまたはcommonjsで使用できます。
  • バージョニングは苦痛です。 メジャーバージョンをthreejsのリリース番号に合わせようとしています。 threejsのすべての新しいバージョンでは、多くの重大な変更が導入されているため、慎重に処理する必要があります。
  • 数学を扱うモジュールは、配列を操作し、モジュラーgl-vec3、gl-mat4などを使用する必要があります。このように、これらは一般的で、threejsの外部で役立ちます。 次に、threejsユーザーは配列へのラッピング/アンラッピングを処理する必要があります。 verlet-system、simplify-pathなどを参照してください。
  • 真にモジュール式または小さなwebGL機能が必要な場合は、今後、stackgl / glslifyを使用します。 これらは、GL状態をリセットする限り、ThreeJS内でも機能します。 例: https

私の新しいプロジェクトは、立ち上げて実行するためだけにnpmで「3」を使用する傾向があります。 ThreeJSがリリース番号と一致するバージョンタグを使用してビルドをnpmに公式に公開した場合、それはかなり素晴らしいことです。

PS:ワークフローに再利用可能/モジュラーシェーダーを導入することに関心のある方へ:
https://gist.github.com/mattdesl/b04c90306ee0d2a412ab

私のiPhoneから送信された

2014年11月20日には、7:42で、アーロン[email protected]書きました:

@rasteiner https://github.com/stackgl/glslifyについて学ぶのはとてもうれしいかもしれません、それは成長しているhttp://stack.glファミリーから来ています


このメールに直接返信するか、GitHubで表示してください。

これがbrowserifyでThree.jsを使用する方法を探していて、このスレッドにつまずく可能性がある他の人を助ける場合、私が自分で設定した方法は、 browserify-shimを使用することです。

_「時々a)グローバルを介してグローバル変数を公開する」_のREADMEセクションに続いて、Three.js用の個別のスクリプトタグを含め、グローバルTHREE変数を公開するように構成しました。

そして、私が自分で解決しなければならなかったのは、ColladaLoader、OrbitControlsなどのエクストラを含める方法でした。私は次のようにしました。

package.jsonから:

    "browser": {
        "three": "bower_components/threejs/build/three.js"
    },
    "browserify-shim": "browserify-shim-config.js",
    "browserify": {
        "transform": [ "browserify-shim" ]
    }

browserify-shim-config.js:

    module.exports = {
        "three": { exports: "global:THREE" },
        "./vendor/threejs-extras/ColladaLoader.js": { depends: {"three": null}, exports: "global:THREE.ColladaLoader" },
        "./vendor/threejs-extras/OrbitControls.js": { depends: {"three": null}, exports: "global:THREE.OrbitControls" }
    };

次に、私自身のスクリプトで、main.js:

    require('../vendor/threejs-extras/ColladaLoader.js');
    require('../vendor/threejs-extras/OrbitControls.js');

    var loader = new THREE.ColladaLoader(),
        controls = new THREE.OrbitControls(camera);
...

Browserifyでは、バイトを変更する場合でも、スクリプト全体を再構築する必要があります。 私はかつてbrowserifyを使用してTHREE.jsを必要とするプロジェクトをパックしましたが、変更を加えるたびにバウンドルを構築してlivereloadをブロックするのに2秒以上かかります。 それはあまりにもイライラします。

通常、 livereloadを使用した開発中に

watchifyは私には機能しません。 ファイルを変更して保存すると、watchifyとbeefyのlivereloadが古い/キャッシュされたバージョンを提供します。 なぜこれが起こるのか私にはわかりません。 ありがたいことに、browserifyはすでにかなりうまく機能しています。

@ChiChou--noparse=threeを渡してbrowserifyします。 これにより、browserifyバンドルのステップが私のマシンで1000ミリ秒から500ミリ秒になります。これは、即座にフィードバックを感じるのに十分な程度です。

@ rasteinerthree.jsの相互依存関係についての非公式な調査に改めて感謝します。 その膨大なdepsのリストは見苦しいコードですが、実際にはその醜さはそのまま存在し、目に見えません。 Browserifyの強みは、汚れた洗濯物を干し、絡まりの少ないシステムを追求する必要があることです。

Three.jsには、オブジェクトを取り込んでそのタイプを認識し、そのタイプに基づいてさまざまなタスクを実行する場所がたくさんあります。 ほとんどの場合、その型依存コードは型自体に移動される可能性があり、操作している可能性のあるすべての型を理解する必要はありません。

以下は、 WebGLRendererの要約例

if ( texture instanceof THREE.DataTexture ) {
  // ...
} else if ( texture instanceof THREE.CompressedTexture ) {
  // ...
} else { // regular Texture (image, video, canvas)
  // ...
}

もっと形にする必要があります

texture.processTexImage( _gl, mipmaps, otherData )

タイプにそれ自体の処理方法を決定させます。 これにより、ライブラリの利用者は、私たちが考えもしなかった新しいテクスチャタイプを使用することもできます。 この構造は相互依存性を減らすはずです。

browserifyアーキテクチャへの移行は間違いなく進むべき道だと思います。 UMDビルドにより、THREE.jsの使用が容易になります。 また、WebGLRendererを複数のファイルに分割することもできます。これは、現在、かなりモノリシックで恐ろしいように見えるためです。

私は現在ここに移動することに取り組んでいるブランチを開始しました: https

ご意見をお聞かせください。

これが@coballast変更点です。

browserifyify.jsファイルで自動変換アプローチを採用しているようです。これが正しい方法だと思います。

私たち全員がまだあまり議論していないことの1つは、この大きくて絶えず変化するライブラリをbrowserifyに移行する最善の方法です。 変更を加えてからPRを開くことはできますが、すぐに古くなります。 それが自動化されたアプローチの魅力です。

できれば:

  • three.js src変換スクリプトを提供します( browserifyify.js
  • 変換プロセスがどのように機能するかを説明するドキュメントを提供する
  • 新しいビルドシステムがどのように機能するかを説明するドキュメントを提供する
  • 変換後にテストを実行する
  • マージの競合につながる可能性のある既存のファイルへの変更を含めない

...次に、これをプッシュボタン変換に変換できます。これは、予見可能な将来にわたって機能します。 その単純さは、イデオロギーの議論が勝ったときに、そのような大規模なプロジェクトへの基本的なアーキテクチャのシフトのこの夢のような概念を達成することを可能にします。

そのために/Three.jsへの変更を削除します。

注:元に戻すだけでなく、新しいブランチまたは強制プッシュを使用して、ブランチの履歴からこれらの変更を削除します

@coballast変換ユーティリティがthree.jsフォークではなく、 three.js開発ディレクトリを指す外部ユーティリティであり、ソースを変換する方が理にかなっているのではないかと思います。ファイルを作成し、ビルドスクリプトを追加して、テストを実行します。

@kumavissrcディレクトリをそのままにして

コードベース全体で一貫したスタイルを自動的にチェックして適用する静的分析を作成する興味深い機会もここにあります。

@coballastは素晴らしいです
escodegenなど、自動コード書き換え用のツールは豊富にあります。 コメントなどを維持していることを確認する必要があります。
threejs-conversion-utilityリポジトリを開始したいですか?

と言った@coballastは、このユーティリティの鋭い焦点を維持することが重要です

@kumavis完了したと考えてください。 私は本当にこれを実現したいと思っています。 これは私が現在取り組んでいる2つのプロジェクトのうちの1つにすぎません。

@kumavis @mrdoobここでの議論のいくつかは、おそらくnpmを介してインストールし、browserifyでコンパイルできる複数の個別のモジュールに3つを分割するという考えに関するもののようです。 私はその考えに完全に反対しているわけではありませんが、それは別の問題であるべきだと思います。 この時点で私が提唱しているのは、browserifyを使用してTHREEのコードベースを内部で管理することだけです。 それを移動して、ユーザーにとっていつもと同じように機能させてから、何が理にかなっているのかを評価します。

そのユーティリティの出力が何であるかを知りたいと思います^^

@coballastは、この時点で空の場合でも、追跡できるレポをリンクします。 そこから構築できます。

https://github.com/coballast/threejs-browserify-conversion-utility

コードはめちゃくちゃです、すぐにクリーンアップされます。

さあ行こう! :ロケット:

これで、browserify srcを生成し、問題なくビルドできる状態のユーティリティができました。 これを自分で行う方法の説明でリポジトリを更新します。 この時点では、例は機能しません。 それを修正するために対処する必要があるいくつかの問題があります。 誰かが袖をまくり上げて手伝いたい場合は、レポに追加します。

@coballastええ、TODOとして問題を提出してください。そうすれば、私や他の人ができる限り参加します。

深刻な問題が発生しています。 #6241を参照

これが機能するために何が起こる必要があるかについての私の分析は次のとおりです: https

browserifyは、その設計により、少なくともトランスポートの冗長性(概念的)です。 これにより、使用コストが高くなり(データプランは誰か?)、遅くなります。

これに対する簡単な解決策は、1つではなく2つのクライアントファイルを必要とするライブラリコードからドキュメントを分離することです。 これは、クライアント側のjsの一般的な方法です。

最初にbrowserifyに欠陥があり、それ自体を修正する必要がある場合、threejsのようなものはもちろん、何かを改善することを検討する必要がある理由がほとんどわかりません。

@spaesaniとにかく、threejsのデータをダウンロードする必要があるためです。 threejsをより小さなモジュールに分割し、自動ビルドシステムに単一のアプリに必要なものを選択させると、実際には、そこにあるほとんどのthreejsアプリはより軽量になります。

何らかの理由で「ドキュメントをライブラリコードから」分離したい場合でも、これを実行して、現在のようにビルド済みバージョンを使用できます。 --standaloneフラグbrowserifyで構築されたアプリを個別のモジュールに分割することもできます。

Browserifyは、ブラウザーで実績のあるモジュール定義API(CommonJS)を使用する方法にすぎません。 これにより、threejsプラグインの開発が大幅に簡素化され、コードの明快さが向上し、生産性が向上します。これにより、バージョン管理システムを通じて整合性を維持しながら、コードが本質的により多くの人々によって維持される、より大きなエコシステム(npm)に統合できるようになります( stackglファミリー)、そして彼らがそれを望まないのであれば、それは人々をCommonJSに強制することさえしません。

もちろん欠点もありますが、それはあなたが言及したものではありません。

three.jsとthree.min.jsをキャッシュして、プロキシ、一般的なモバイルソリューション、またはキャッシングブラウザーによるトランスポート(データ)を節約できます。
threejsコードを選択してドキュメント固有のコードと集約した瞬間、キャッシュは実行できません。
browserifyで許可されている場合