Nodi v2 early access版について
for English version
Nodi v2 early access版について
Webブラウザ上で動作する、ノードベースプログラミングによるモデリングツールNodiのv2 early access版を公開しました。 https://v2.nodi3d.com

https://v2.nodi3d.com?example=WaveSpring
この記事では、v2としてNodiをリライトした経緯と技術的背景、今後の展望などについて記述していきます。
Nodiとは?
NodiはGrasshopper、Houdini、Geometry NodesのようなプロシージャルモデリングがWebブラウザ上でできるツールです。 2020年の頭ごろにpublic β版をリリースしたのですが、以前のバージョンはいくつか大きな問題を抱えており、チマチマと時間をかけてリライトすることができました。 (忙しさにかまけてリライトに手をつけられないまま数年過ごしてしまっていたのですが…)
まだv1の機能をすべてリライトできたわけではないのですが、モデリングツールとして最低限使える機能が揃ってきたと判断し、公開するに至りました。
v2の技術的背景と新しくなった点
v1の反省点としては大きく2つあり、
- パフォーマンス
- CADカーネルの弱さ
がありました。
以前のv1では、メインロジックの全てをTypeScript/JavaScriptで開発しており、これによってメモリ管理をガベージコレクションに依存することになってしまい、使っているうちに段々重くなってくる、などの致命的な問題が発生したりしていました。 これはプログラムの見直しによってある程度改善を図ることはできたはずですが、原理的に解決することが難しい面もあり、幾何データが重くなってしまいがちなモデリングツールとしてはかなり悩ましい問題でした。
また、v1では主にthree.jsが持つPolygon Meshを用いたり、曲線・曲面に関してはverbというライブラリに頼る実装になっていたのですが、必要なモデリング操作を実現できずに誤魔化しながらリリースしていた機能もあり、大きな問題になっていました。 1からCADカーネルを実装するにも前述のパフォーマンスの問題があるので、そもそもこのままTypeScript/JavaScriptで開発を続けても良いものかどうか悩んでいました。
RustとWebAssembly
ここで、パフォーマンス面(特にメモリ管理)の懸念点を解決する手段としてWebAssemblyが候補に上がってきます。 WebAssemblyでは線形メモリによって効率的にメモリ管理することが可能なので、(気をつけて実装すれば)不要になったデータは都度破棄されるのでガベージコレクション起因の問題が発生しませんし、またコアな幾何処理に関わる計算においてはJavaScriptよりも速度の向上が期待できるので、WebAssemblyを使う方針でNodiを作り直すことにしました。
作り直しにあたって、言語にはRustを用いることにしました。 WebAssemblyへコンパイル可能な言語としてサポートが充実しているのと、別の幾何処理を伴うプロジェクトで既に自身で使っていた、というのがRustを採用した理由です。
CADカーネル
開発言語にRustを用いることにしたものの、Rustではモデリングソフト開発に使えるような機能豊富なCADカーネルが存在しません。(ただこれはRustに限らず、無償で、という条件になるとそもそも存在しないのかもしれません)
いくつかpure Rust実装のCADカーネルプロジェクトはあり、特にtruckは曲線・曲面の実装を備えた優れたオープンソースライブラリなのですが、Nodiに必要な豊富なモデリング操作を持ったものは見当たらず、モデリングソフトを作る以上好きに機能追加しやすいものがないと開発が難しいと判断し、結局自ら作ることにしました。
そうしてRustで曲線・曲面を扱うためのcurvoというライブラリを開発しました。https://github.com/mattatz/curvo

curvoによる曲面生成
curvoはNodi v2のコアモジュールとして利用しており、曲線・曲面に関する主要なモデリング操作はこのライブラリで実現しています。
このcurvoの開発に伴ってCADカーネルへの理解が深まり、v2ではBRepなどCADとして必須の幾何表現を用意することができました。

capパラメータに応じて出力される幾何表現がBrepに切り替わる
幾何形状の描画
Nodi v2では幾何形状の描画にはthree.jsを用いています。 ただ、v2では幾何データはWebAssembly側にあり、描画に用いるPolygon Meshデータなどを適当にTypeScript側にcloneして渡してしまうと、結局v1で発生していた重い幾何データの解放タイミングがガベージコレクション依存になる問題が残ってしまいます。 なので、v2ではデータのコピーを発生させずにWebGL(またはWebGPU)で描画するために、Typed Arrayを用いたArray Bufferのメモリ共有を用いています。
WebAssembly側で生成された幾何データのメモリアドレスと長さをTypeScript側に渡し、その情報を元にArray Bufferのメモリ共有を実現しています。 以下はWebAssembly側で保持しているPolygon MeshデータをTyped Arrayを介してゼロコピー共有するコードを抜粋して簡易化したものです。
// Rust側
// メモリアドレスと長さの情報のみを持ったPolygon meshのinterop構造体
#[derive(serde::Serialize)]
pub struct MeshInteropHandle {
pub count: usize, // 頂点数
pub vertices: usize, // 頂点バッファのメモリアドレス
pub normals: usize, // 法線バッファのメモリアドレス
}
// Polygon meshの実データを持った型からメモリ情報のみを持った型へ変換
impl<'a> From<&'a MeshInterop> for MeshInteropHandle {
fn from(value: &MeshInterop) -> Self {
Self {
count: value.vertices().len(),
// Vec型のメモリアドレスを設定
vertices: value.vertices().as_ptr() as usize,
normals: value.normals().as_ptr() as usize,
}
}
}
// TypeScript側
// WebAssemblyから送られてきたメモリ情報を元にMesh形状を表すTypedArrayを生成する例
const {
memory, // WebAssembly.Memory
handle // MeshInteropHandle
} = props;
const stride = 3; // 各頂点に含まれる要素数 ([x,y,z]の3つ)
const {
count, // 頂点数
vertices, // 頂点バッファのメモリアドレス
normals // 法線バッファのメモリアドレス
} = handle;
// new Float32Array(WebAssembly.Memory, 参照したいメモリの先頭アドレス, メモリ長さ)
// と指定することで、WebAssembly側のArrayBuffer上のデータをコピーせずに参照できる
const vertexArray = new Float32Array(memory.buffer, vertices, count * stride);
const normalArray = new Float32Array(memory.buffer, normals, count * stride);
// TypedArrayを用いてthree.jsでの描画に用いる形状データを定義
const geometry = new BufferGeometry();
const position = new BufferAttribute(vertexArray, stride);
const normal = new BufferAttribute(normalArray, stride);
geometry.setAttribute("position", position);
geometry.setAttribute("normal", normal);
モジュールとしての使い方
v1を開発していたときに、Nodiをジオメトリエンジンとしてモジュール化し、他のプロジェクトでも使い回しやすくしたいと考えていました。 具体的には、エディターで作成したnode graphを別のプロジェクト上でも転用し、node graphから得られるモデリング結果を再生成する、というような使い方です。
v2ではRust + WebAssemblyを採用したことによってこのようなモジュール的な使い方ができるものを簡単に用意することができました。 https://github.com/Nodi3d/modular このmodularはnpm packageとして提供されていて、前述のような使い方で、別プロジェクトに組み込むことができます。
例えばNakajimaさんによるBento3DのようにNodiをジオメトリ生成部分のみに部分的に利用したツールやサービスを開発しやすくなります。 (Bento3Dではv1のNodiを用いられています。が、v1にはv2のmodularのようなnpm packageを用意していなかったので組み込みが面倒だったようです。)

Bento3D https://bento3d.design/
UI上の変化
UIに関しても1から実装しなおしたことで、全体的なレイアウトをはじめとして様々な部分を更新しています。 特に、画面右にインスペクタ領域を用意したことで、データプレビューや編集がやりやすくなりました。 インスペクタ内には新しくGeometryタブを用意していて、選択中のノードから出力される幾何データのアトリビュートを把握することができます。 (HoudiniやBlenderにあるSpreadsheetを参考にしています)

Geometryタブでノードから出力される幾何データを確認できる
v2で追加したい機能
BRepや曲線曲面系のモデリング機能は優先度高くチマチマ取り組むべき項目として考えているのですが、それ以外だとv1で試験的に実装したFRepモデリングをv2でも実現したいと考えています。
FRepモデリング

v1でのFRepモデリング
FRepでは幾何形状をSigned Distance Functionと呼ばれる計算式で表現するため、形状生成に必要なメモリが少なくすみ、ブラウザアプリでもサクサクモデリングが可能です。 AdobeがリリースしたProject NeoがFRepを使ったモデリングツールの例として挙げられます。
ただ、現状このFRepを例えば3Dプリント可能な形式に変換しようとすると、一旦Mesh化する工程を挟むのがベタな手法となっており、その変換に計算コストが多く取られがちです。 ここで、Mesh化などの工程を経ずにFRepをいきなり3Dプリント可能な形式等に変換する手法をv2では模索したいと考えています。
Webサービスとしての機能
v2 early access版ではデータベースを用意しておらず、クラウド上への保存ができない状態になっています。(ローカルへの書き出しや読み込みは可能) これはモデリング面が充実し、サービスとして提供可能な段階になったら準備しようと考えています。
その他いろいろ
他にも追加したい機能は細かいものを入れると大量にあるのですが、目下考えている大きめの機能は以下のものです。
- プラグイン機能(自作ノードが定義できたり、スクリプティングが可能な環境を用意する)
- HoudiniのBlock Begin/End、Geometry NodesのSimulation Nodesのように、計算結果を使い回せる機能(ノードベースプログラミングの中でループを表現できるようにする)
- CAM / CAE
上記は開発コストが高いものも含んでいるので実現可能性は未知なのですが、機会を見つけて取り組みたいと考えています。
今後の展望
v1では、ありがたいことにいくつかの企業とコラボレーションの提案があったり(ポシャったりもしつつ…)、カスタマイズしたものを実際に納品したりなど、産業との接続を感じつつ高揚する経験を積むことができました。 今後もv2を機能的に充実させていきつつ、実プロジェクトとの連携を通じて実際的なツールにしていきたいと考えています。
ツールに対するフィードバックや機能面でのアイデア、コラボレーションの相談などありましたらお気軽にご連絡ください。
- Nodi v2: https://v2.nodi3d.com
- Feedback: https://github.com/Nodi3d/nodi-v2/discussions
- Contact: contact@nodi3d.com
- X: https://x.com/mattatz
메타데이터
- post_id
- 141966c3d4bb
- slug
- nodi-v2-early-access版について-141966c3d4bb
- url
- https://medium.com/@masatatsu.nakamura/nodi-v2-early-access%E7%89%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-141966c3d4bb
- canonical_url
- https://medium.com/@masatatsu.nakamura/nodi-v2-early-access%E7%89%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-141966c3d4bb
- author_url
- https://medium.com/@masatatsu.nakamura
- status
- ok
- fetched_at
- 2026-06-09 15:37:30