てんちょーの技術日誌

自分がつまづいたこととかメモ

[Houdini] Vellum Constraints を読んでみた その2

概要

こちらの続きです

shop-0761.hatenablog.com

環境

OS: Windows 10
Houdini: 19.0.622
Redshift: redshift_v3.0.62 (たぶん)

読んでいく

Collision Geometry の出力

内部で計算に使っているものの、
入力されたジオメトリはそのまま出力されていました

そのため、Vellum Pack 時には Collision Geometry の Input が不要になります

Tips

neighbours (VEX)

個人的にあまり見たことなかったのでメモ
隣接する point を取得するのに使う

www.sidefx.com

nomoreretake.net

任意文字列のattributeを使うとき

haspointattribを使って判定します
attirbute の指定が文字列なのでできる技ですね

float scale = 1.0;
string attrib = chs("scaleattrib");
if (haspointattrib(0, attrib)){
    scale *= point(0, attrib, @ptnum);
}

パラメータの Disable / Hide

こんな感じで記述することができるようです

書き方はこちら

www.sidefx.com

nomoreretake.net

@opinput1 とは

VEX Expression で @opinput1_pscale を使っているところを見つけました
調べてみると、

Wrangleノードの別のインプットからデータを読み込むのに point(@OpInput2, “P”, @ptnum) 等、 point() や prim() 関数を使ってアクセスできますが、@opinput1_P 等と簡潔に書くことができます。

houdinifx.jp

ExtractTransform ノード

あまり見ないノードだったのでメモ

www.sidefx.com

ドキュメントのサンプルファイルより

これにあるように動かしてからpackより、packしてから動かすほうが効率的っぽい

primitiveSplit ノード

www.sidefx.com

いつもはfacetノード + Unique points でバラバラにすることが多かったです

ただし、このノードは 19.5 から deprecated になっており、
代わりに Point Split ノードが推奨されています

www.sidefx.com

computeOrientRodlengths

Houdiniがインストールされているフォルダ($HFS)内のincludeフォルダを基準に
相対パスでincludeできるようです

素直にインストールしていれば下記あたりです
Windows: C:\Program Files\Side Effects Software\Houdini {バージョン}\houdini\vex\include

computeOrientRodlengthsは pbd_constraints.h 内に定義されています

void
computeOrientRodlengths(const int geo; const int primnum; const string srcgrp;
                        const int outgeo)
{
    // Ignore anything but open polylines.
    if (primintrinsic(geo, "typename", primnum) != "Poly" ||
        primintrinsic(geo, "closed", primnum) == 1)
        return;
    // Only test group if it's not all points.
    int hasgrp = npointsgroup(geo, srcgrp) < npoints(geo);
    // Check if the points have an incoming orient attribute. If os, we assume
    // that it provides a stable basis in which to calculate our rod-aligned orients.
    int hasporient = haspointattrib(geo, "orient");
    vector from = {0, 0, 1};
    // Iterate over pts in vertex order.
    int pts[] = primpoints(geo, primnum);
    int npts = len(pts);
    vector4 orients[];
    float rodlens[];
    resize(orients, npts - 1);
    resize(rodlens, npts - 1);

    int lastpt = pts[npts - 1];
    int loop = 0;
    for(int i=0; i < npts - 1; i++)
    {
        vector d = point(geo, "P", pts[i + 1]) - point(geo, "P", pts[i]);
        vector to = normalize(d);
        if (hasporient)
        {
            // Transform vectors back to rest orientation.
            vector4 porient = point(geo, "orient", pts[i]);
            to = qrotate(qinvert(porient), to);
        }
        vector4 dq = dihedral(from, to);
        if (i == 0)
            orients[i] = dq;
        else
            orients[i] = qmultiply(dq, orients[i-1]);
        rodlens[i] = length(d);
        from = to;
        // Check if this is a loop.
        loop |= (pts[i] == lastpt);
    }

    for(int i=0; i < npts - 1; i++)
    {
        if (hasporient)
        {
            // Rotate new orientation back to current world orientation.
            vector4 porient = point(geo, "orient", pts[i]);
            orients[i] = qmultiply(porient, orients[i]);
        }

        // Make sure to do the above rotation in the orients array
        // before possibly skipping this point based on group membership,
        // otherwise if we then grab the rotation for the last point from
        // the previous vertex, it could be incorrect if the previous
        // vertex wasn't in srcgrp. (Exiting too early caused bug 97655.)
        if (hasgrp && !inpointgroup(geo, srcgrp, pts[i]))
            continue;

        // Set new orientation, possibly overwriting input orientation.
        setpointattrib(geoself(), "orient", pts[i], orients[i]);

        float inertia = point(geo, "inertia", pts[i]);
        // Don't overwrite pinned inertia
        if (inertia == 0.0)
            continue;
        setpointattrib(geoself(), "inertia", pts[i], rodlens[i]);
    }
    // Set inertia and orient for last point if not already seen as part of a loop.
    if (npts > 1 && inpointgroup(geo, srcgrp, lastpt) && !loop)
    {
        float inertia = point(geo, "inertia", lastpt);
        // Don't overwrite pinned inertia
        if (inertia != 0.0)
            setpointattrib(geoself(), "inertia", lastpt, rodlens[npts - 2]);
        // Copy previous orient so final bend/twist rest relative orient is
        // identity, which reduces flipping when final vertex orientation is
        // otherwise unconstrained.
        setpointattrib(geoself(), "orient", lastpt, orients[npts - 2]);
    }
}

ざっくりメインのとこだけ切り出すとこんなかんじ

int pts[] = primpoints(geo, primnum);
for(int i=0; i < npts - 1; i++){
    vector d = point(geo, "P", pts[i + 1]) - point(geo, "P", pts[i]);
    rodlens[i] = length(d);
}
for(int i=0; i < npts - 1; i++){
    setpointattrib(geoself(), "inertia", pts[i], rodlens[i]);
}

剛体の慣性モーメント

球の剛体の慣性モーメントっぽい式をVEX内に見かけたので、
このシリーズを見ておきました

【大学物理】剛体の力学入門①(特徴と魅力)/全6回【力学】 - YouTube

Group の構文

Groupを指定するときに正規表現的なものが使えます
0-nprims(-1):2 のような見慣れない記述方法が出てきて
少し困っていたらドキュメントを見つけました

www.sidefx.com

先述の表記は0を含む偶数のprimを対象としているようです

まとめ

ようやく constraint type の違いによる分岐にさしかかってきました...
ここまではまだアップなので、怯まず読み進めたいと思います

何かあれば Twitter までお気軽にどうぞ~ twitter.com