[maya][hair][nurbs]nurbsLoftでの束感のあるヘアーモデリングの方法

[Maya][Hair][NURBS] Method for modeling hair with a “clumped” look using NURBS Loft

根元からNurbsCircleを置いて行って

ctrl+d複製し拡大縮小しながら次の円も書いていきます。

ctrl+d複製し縮小しながら移動して先端の部分を用意します

nurbsを全部選択し、サーフェス>作成>ロフトで、loftedSurface1ができます。

loftedSurface1を選択して、修正>変換>NURBSをポリゴンに 

でポリゴンに変換できます。

[自作PC]購入したばかりのhdd 認識しない

購入したばかりのHDDが認識しない場合、最も多い原因は「フォーマット(初期化)未完了」です。まずはWindowsの「ディスクの管理」ツールを開き、該当のHDDが認識されているか確認して、初期化とフォーマットを行ってみてください。 [1, 2, 3]

以下の手順で確認・設定を行えます。

1. 「ディスクの管理」での初期化手順(Windowsの場合)

  1. スタートボタンを右クリックし、メニューから「ディスクの管理」を選択します。
  2. ディスクの初期化画面が自動的に表示された場合は、パーティションスタイル(通常は「GPT」)を選択して「OK」をクリックします。
  3. 表示されない場合は、下部の枠の中から「未割り当て」となっている新しいHDDを探します。
  4. 「未割り当て」の部分を右クリックし、「新しいシンプル ボリューム」を選択します。
  5. 画面の指示に従って(基本的に「次へ」をクリック)、フォーマットを完了させます。 [1, 2, 3, 4, 5]

2. その他の確認ポイント

それでも認識されない場合は、以下の原因が考えられます。

  • 物理的な接続不良: ケーブルの挿し直しや、別のUSBポート(またはSATAポート)への接続を試してください。
  • 外付けHDDの電源: ACアダプターが必要なタイプの場合、電源ケーブルがコンセントにしっかり挿さっているか確認してください。
  • 初期不良の可能性: 別のパソコンに接続しても全く反応がない場合は、製品の初期不良も考えられます

[maya][python]接合したメッシュの法線をいい感じに丸く滑らかにするツール furcraeaSmoothNormalTool.pyとfurcraeaSeamNormalTool.py

できます。まずは 選択したメッシュ、フェース、頂点の法線を滑らかにする Maya Python ツールとして作るのが扱いやすいです。

下のコードは、次の機能を持っています。

  • 選択範囲だけ法線をソフト化
  • メッシュ全体をソフト化
  • 角度を指定
  • 法線ロック解除
  • 法線の平均化
  • ハードエッジ化
  • 法線履歴の削除

furcraeaSmoothNormalTool.py

# -*- coding: utf-8 -*-
import maya.cmds as cmds


WINDOW_NAME = "furcraeaSmoothNormalTool"


def get_selected_mesh_components():
    """
    現在の選択を取得します。
    オブジェクト選択の場合は、そのメッシュ全体を返します。
    コンポーネント選択の場合は、そのまま返します。
    """
    selection = cmds.ls(selection=True, long=True, flatten=True) or []

    if not selection:
        cmds.warning("メッシュ、フェース、エッジ、頂点を選択してください。")
        return []

    result = []

    for item in selection:
        # コンポーネント選択
        if "." in item:
            result.append(item)
            continue

        # TransformからShapeを取得
        if cmds.nodeType(item) == "transform":
            shapes = cmds.listRelatives(
                item,
                shapes=True,
                noIntermediate=True,
                fullPath=True
            ) or []

            mesh_shapes = [
                shape for shape in shapes
                if cmds.nodeType(shape) == "mesh"
            ]

            if mesh_shapes:
                result.append(item)

        # Shapeが直接選択されている場合
        elif cmds.nodeType(item) == "mesh":
            parents = cmds.listRelatives(
                item,
                parent=True,
                fullPath=True
            ) or []

            if parents:
                result.append(parents[0])

    if not result:
        cmds.warning("選択内にポリゴンメッシュがありません。")

    return result


def unlock_normals():
    """選択したメッシュまたはコンポーネントの法線ロックを解除します。"""
    targets = get_selected_mesh_components()
    if not targets:
        return

    cmds.undoInfo(openChunk=True)

    try:
        for target in targets:
            try:
                cmds.polyNormalPerVertex(
                    target,
                    unFreezeNormal=True
                )
            except RuntimeError:
                pass

        cmds.inViewMessage(
            amg="<hl>法線ロックを解除しました</hl>",
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def soften_normals(angle=None):
    """
    指定角度以下のエッジをソフト化します。

    angle:
        180の場合、基本的にすべてのエッジを滑らかにします。
        30~60程度なら、鋭い角を残しやすくなります。
    """
    targets = get_selected_mesh_components()
    if not targets:
        return

    if angle is None:
        angle = cmds.floatSliderGrp(
            "smoothNormalAngleSlider",
            query=True,
            value=True
        )

    cmds.undoInfo(openChunk=True)

    try:
        for target in targets:
            # ロックされた法線がある場合に備えて解除
            try:
                cmds.polyNormalPerVertex(
                    target,
                    unFreezeNormal=True
                )
            except RuntimeError:
                pass

            cmds.polySoftEdge(
                target,
                angle=angle,
                constructionHistory=True
            )

        cmds.inViewMessage(
            amg="<hl>法線を滑らかにしました:</hl> {}°".format(angle),
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def soften_all():
    """選択したメッシュまたはコンポーネントを完全にソフト化します。"""
    soften_normals(180.0)


def harden_normals():
    """選択した範囲をハードエッジ化します。"""
    targets = get_selected_mesh_components()
    if not targets:
        return

    cmds.undoInfo(openChunk=True)

    try:
        for target in targets:
            try:
                cmds.polyNormalPerVertex(
                    target,
                    unFreezeNormal=True
                )
            except RuntimeError:
                pass

            cmds.polySoftEdge(
                target,
                angle=0,
                constructionHistory=True
            )

        cmds.inViewMessage(
            amg="<hl>ハードエッジ化しました</hl>",
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def average_vertex_normals():
    """
    選択頂点の法線を平均化します。
    頂点以外を選択した場合は頂点へ変換します。
    """
    selection = cmds.ls(selection=True, long=True, flatten=True) or []

    if not selection:
        cmds.warning("平均化するメッシュまたはコンポーネントを選択してください。")
        return

    vertices = cmds.polyListComponentConversion(
        selection,
        toVertex=True
    )

    vertices = cmds.ls(
        vertices,
        flatten=True,
        long=True
    ) or []

    if not vertices:
        cmds.warning("頂点を取得できませんでした。")
        return

    cmds.undoInfo(openChunk=True)

    try:
        try:
            cmds.polyNormalPerVertex(
                vertices,
                unFreezeNormal=True
            )
        except RuntimeError:
            pass

        cmds.polyAverageNormal(
            vertices,
            distance=0.0,
            replaceNormalXYZ=(0, 0, 0)
        )

        cmds.inViewMessage(
            amg="<hl>頂点法線を平均化しました</hl>",
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def conform_normals():
    """
    法線の向きを統一します。
    裏返ったフェースが混在している場合に使用します。
    """
    targets = get_selected_mesh_components()
    if not targets:
        return

    cmds.undoInfo(openChunk=True)

    try:
        for target in targets:
            cmds.polyNormal(
                target,
                normalMode=2,
                userNormalMode=0,
                constructionHistory=True
            )

        cmds.inViewMessage(
            amg="<hl>法線方向を統一しました</hl>",
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def reverse_normals():
    """選択したフェースまたはメッシュの法線を反転します。"""
    targets = get_selected_mesh_components()
    if not targets:
        return

    cmds.undoInfo(openChunk=True)

    try:
        for target in targets:
            cmds.polyNormal(
                target,
                normalMode=0,
                userNormalMode=0,
                constructionHistory=True
            )

        cmds.inViewMessage(
            amg="<hl>法線を反転しました</hl>",
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def delete_normal_history():
    """
    選択メッシュの履歴を削除します。
    法線処理以外の履歴も削除されるため注意してください。
    """
    targets = get_selected_mesh_components()
    if not targets:
        return

    transforms = set()

    for target in targets:
        object_name = target.split(".")[0]

        if cmds.nodeType(object_name) == "mesh":
            parents = cmds.listRelatives(
                object_name,
                parent=True,
                fullPath=True
            ) or []

            if parents:
                transforms.add(parents[0])
        else:
            transforms.add(object_name)

    if transforms:
        cmds.delete(list(transforms), constructionHistory=True)

        cmds.inViewMessage(
            amg="<hl>ヒストリを削除しました</hl>",
            pos="midCenter",
            fade=True
        )


def show_smooth_normal_tool():
    """UIを表示します。"""
    if cmds.window(WINDOW_NAME, exists=True):
        cmds.deleteUI(WINDOW_NAME)

    window = cmds.window(
        WINDOW_NAME,
        title="Smooth Normal Tool",
        sizeable=False,
        widthHeight=(340, 390)
    )

    cmds.columnLayout(
        adjustableColumn=True,
        rowSpacing=8
    )

    cmds.separator(height=8, style="none")

    cmds.text(
        label="メッシュ・フェース・エッジ・頂点に対応",
        align="center"
    )

    cmds.separator(height=8, style="in")

    cmds.floatSliderGrp(
        "smoothNormalAngleSlider",
        label="Soft Angle",
        field=True,
        minValue=0.0,
        maxValue=180.0,
        fieldMinValue=0.0,
        fieldMaxValue=180.0,
        value=60.0,
        columnWidth3=(80, 60, 180)
    )

    cmds.button(
        label="指定角度で滑らかにする",
        height=36,
        command=lambda *_: soften_normals()
    )

    cmds.button(
        label="すべて滑らかにする  180°",
        height=32,
        command=lambda *_: soften_all()
    )

    cmds.button(
        label="ハードエッジにする  0°",
        height=32,
        command=lambda *_: harden_normals()
    )

    cmds.separator(height=10, style="in")

    cmds.button(
        label="頂点法線を平均化",
        height=32,
        command=lambda *_: average_vertex_normals()
    )

    cmds.button(
        label="法線ロックを解除",
        height=32,
        command=lambda *_: unlock_normals()
    )

    cmds.separator(height=10, style="in")

    cmds.rowLayout(
        numberOfColumns=2,
        adjustableColumn=1,
        columnWidth2=(165, 165)
    )

    cmds.button(
        label="法線方向を統一",
        height=30,
        command=lambda *_: conform_normals()
    )

    cmds.button(
        label="法線を反転",
        height=30,
        command=lambda *_: reverse_normals()
    )

    cmds.setParent("..")

    cmds.separator(height=10, style="in")

    cmds.button(
        label="ヒストリを削除",
        height=30,
        command=lambda *_: delete_normal_history()
    )

    cmds.separator(height=8, style="none")

    cmds.showWindow(window)


show_smooth_normal_tool()

メッシュの接合部で法線がめちゃくちゃになってるパターンをどうにか滑らかにしたい

その場合は polySoftEdge だけでは直らない可能性が高いです。

接合部で法線が乱れる原因は主に次の2種類です。

  1. 頂点は接合されているが、法線がロック・分割されている
  2. 別メッシュ同士で、同じ位置にある頂点の法線が一致していない

特に髪・顔・首・胴体など、別メッシュの境界を滑らかにつなぎたい場合は、接合部の近接頂点を探して、両方の法線を平均化する必要があります。

以下は、選択した複数メッシュ間で、同じ位置にある頂点の法線を平均化するPythonツールです。

furcraeaSeamNormalTool.py

# -*- coding: utf-8 -*-

import maya.cmds as cmds
import maya.api.OpenMaya as om


WINDOW_NAME = "furcraeaSeamNormalTool"


def get_selected_mesh_dag_paths():
    """
    選択されたTransformまたはMesh Shapeから、
    MeshのMDagPathを取得する。
    """
    selection = om.MGlobal.getActiveSelectionList()
    mesh_paths = []

    for index in range(selection.length()):
        try:
            dag_path = selection.getDagPath(index)
        except RuntimeError:
            continue

        if dag_path.node().hasFn(om.MFn.kTransform):
            try:
                dag_path.extendToShape()
            except RuntimeError:
                continue

        if dag_path.node().hasFn(om.MFn.kMesh):
            mesh_paths.append(dag_path)

    return mesh_paths


def unlock_mesh_normals(dag_path):
    """
    メッシュ全体の頂点法線ロックを解除する。
    """
    mesh_name = dag_path.fullPathName()

    try:
        cmds.polyNormalPerVertex(
            mesh_name,
            unFreezeNormal=True
        )
    except RuntimeError:
        pass


def get_vertex_data(dag_path):
    """
    各頂点について以下を取得する。

    - 頂点番号
    - ワールド座標
    - 頂点法線
    """
    mesh_fn = om.MFnMesh(dag_path)

    points = mesh_fn.getPoints(om.MSpace.kWorld)
    normals = mesh_fn.getVertexNormals(
        True,
        om.MSpace.kWorld
    )

    result = []

    for vertex_id, point in enumerate(points):
        normal = normals[vertex_id]

        result.append({
            "dag_path": dag_path,
            "mesh_fn": mesh_fn,
            "vertex_id": vertex_id,
            "position": om.MVector(point.x, point.y, point.z),
            "normal": om.MVector(normal.x, normal.y, normal.z)
        })

    return result


def build_spatial_hash(vertex_data, tolerance):
    """
    頂点座標をグリッド化して、近接頂点を高速に検索する。
    """
    spatial_hash = {}

    for data in vertex_data:
        position = data["position"]

        key = (
            int(round(position.x / tolerance)),
            int(round(position.y / tolerance)),
            int(round(position.z / tolerance))
        )

        spatial_hash.setdefault(key, []).append(data)

    return spatial_hash


def get_neighbor_keys(position, tolerance):
    """
    誤差境界をまたいだ頂点も検索できるように、
    周囲27セルを返す。
    """
    center_x = int(round(position.x / tolerance))
    center_y = int(round(position.y / tolerance))
    center_z = int(round(position.z / tolerance))

    for offset_x in (-1, 0, 1):
        for offset_y in (-1, 0, 1):
            for offset_z in (-1, 0, 1):
                yield (
                    center_x + offset_x,
                    center_y + offset_y,
                    center_z + offset_z
                )


def average_seam_normals(tolerance=0.001, border_only=True):
    """
    選択した複数メッシュ間で、
    同じ位置にある頂点法線を平均化する。

    tolerance:
        同じ位置と判定する距離。

    border_only:
        Trueの場合は境界頂点のみを対象にする。
    """
    dag_paths = get_selected_mesh_dag_paths()

    if len(dag_paths) < 2:
        cmds.warning(
            "接合部を修正する2つ以上のメッシュを選択してください。"
        )
        return

    cmds.undoInfo(openChunk=True)

    try:
        all_vertex_data = []

        for dag_path in dag_paths:
            unlock_mesh_normals(dag_path)

            mesh_vertex_data = get_vertex_data(dag_path)

            if border_only:
                border_ids = get_border_vertex_ids(dag_path)

                mesh_vertex_data = [
                    data
                    for data in mesh_vertex_data
                    if data["vertex_id"] in border_ids
                ]

            all_vertex_data.extend(mesh_vertex_data)

        spatial_hash = build_spatial_hash(
            all_vertex_data,
            tolerance
        )

        processed = set()
        matched_groups = []

        for source_data in all_vertex_data:
            source_key = (
                source_data["dag_path"].fullPathName(),
                source_data["vertex_id"]
            )

            if source_key in processed:
                continue

            matching_vertices = []

            for neighbor_key in get_neighbor_keys(
                source_data["position"],
                tolerance
            ):
                candidates = spatial_hash.get(
                    neighbor_key,
                    []
                )

                for candidate in candidates:
                    candidate_key = (
                        candidate["dag_path"].fullPathName(),
                        candidate["vertex_id"]
                    )

                    if candidate_key in processed:
                        continue

                    # 同一メッシュ内の近接頂点は対象外
                    if (
                        candidate["dag_path"].fullPathName()
                        == source_data["dag_path"].fullPathName()
                    ):
                        continue

                    distance = (
                        candidate["position"]
                        - source_data["position"]
                    ).length()

                    if distance <= tolerance:
                        matching_vertices.append(candidate)

            if not matching_vertices:
                continue

            group = [source_data] + matching_vertices

            # 同一頂点の重複を除去
            unique_group = {}
            for data in group:
                key = (
                    data["dag_path"].fullPathName(),
                    data["vertex_id"]
                )
                unique_group[key] = data

            group = list(unique_group.values())

            if len(group) < 2:
                continue

            matched_groups.append(group)

            for data in group:
                processed.add((
                    data["dag_path"].fullPathName(),
                    data["vertex_id"]
                ))

        if not matched_groups:
            cmds.warning(
                "指定距離内に一致する接合頂点が見つかりませんでした。"
            )
            return

        apply_average_normals(matched_groups)

        cmds.inViewMessage(
            amg=(
                "<hl>接合部法線を平均化しました</hl>"
                "<br/>一致グループ数: {}"
            ).format(len(matched_groups)),
            pos="midCenter",
            fade=True
        )

    finally:
        cmds.undoInfo(closeChunk=True)


def get_border_vertex_ids(dag_path):
    """
    メッシュの境界エッジに含まれる頂点番号を取得する。
    """
    border_vertex_ids = set()

    edge_iterator = om.MItMeshEdge(dag_path)

    while not edge_iterator.isDone():
        if edge_iterator.onBoundary():
            border_vertex_ids.add(
                edge_iterator.vertexId(0)
            )
            border_vertex_ids.add(
                edge_iterator.vertexId(1)
            )

        edge_iterator.next()

    return border_vertex_ids


def apply_average_normals(matched_groups):
    """
    法線グループごとに平均法線を計算し、
    各頂点へ適用する。
    """
    mesh_updates = {}

    for group in matched_groups:
        average_normal = om.MVector()

        for data in group:
            average_normal += data["normal"]

        if average_normal.length() < 0.000001:
            continue

        average_normal.normalize()

        for data in group:
            mesh_name = data["dag_path"].fullPathName()

            if mesh_name not in mesh_updates:
                mesh_updates[mesh_name] = {
                    "mesh_fn": data["mesh_fn"],
                    "vertex_ids": [],
                    "normals": []
                }

            mesh_updates[mesh_name]["vertex_ids"].append(
                data["vertex_id"]
            )

            mesh_updates[mesh_name]["normals"].append(
                om.MVector(
                    average_normal.x,
                    average_normal.y,
                    average_normal.z
                )
            )

    for update_data in mesh_updates.values():
        mesh_fn = update_data["mesh_fn"]

        mesh_fn.setVertexNormals(
            update_data["normals"],
            update_data["vertex_ids"],
            om.MSpace.kWorld
        )


def run_from_ui(*args):
    tolerance = cmds.floatField(
        "seamNormalToleranceField",
        query=True,
        value=True
    )

    border_only = cmds.checkBox(
        "seamNormalBorderOnlyCheckBox",
        query=True,
        value=True
    )

    if tolerance <= 0:
        cmds.warning("許容距離は0より大きくしてください。")
        return

    average_seam_normals(
        tolerance=tolerance,
        border_only=border_only
    )


def show_seam_normal_tool():
    if cmds.window(WINDOW_NAME, exists=True):
        cmds.deleteUI(WINDOW_NAME)

    window = cmds.window(
        WINDOW_NAME,
        title="Seam Normal Smoother",
        sizeable=False,
        widthHeight=(360, 190)
    )

    cmds.columnLayout(
        adjustableColumn=True,
        rowSpacing=8
    )

    cmds.separator(height=8, style="none")

    cmds.text(
        label="接合する2つ以上のメッシュを選択",
        align="center"
    )

    cmds.text(
        label="同じ位置にある境界頂点の法線を平均化します",
        align="center"
    )

    cmds.separator(height=8, style="in")

    cmds.floatFieldGrp(
        "seamNormalToleranceField",
        numberOfFields=1,
        label="許容距離",
        value1=0.001,
        precision=6,
        columnWidth2=(100, 220)
    )

    cmds.checkBox(
        "seamNormalBorderOnlyCheckBox",
        label="境界頂点のみを処理",
        value=True
    )

    cmds.separator(height=8, style="in")

    cmds.button(
        label="接合部の法線を滑らかにする",
        height=38,
        command=run_from_ui
    )

    cmds.separator(height=8, style="none")

    cmds.showWindow(window)


show_seam_normal_tool()

[Open Web UI][Ollama]守秘義務を考慮したAIのWindowsへのPipでのOpen Web UIインストール方法について

Productionでの仕事は守秘義務が付いて回る。そんなゲーム開発プロダクションでもインストールできるAIについて考えながらインストールしていくマニュアルとする。

Open WebUIはローカル環境(PCや自社サーバーなど)で完結するため、外部のクラウドサーバーにデータが送信される心配がありません。チャット履歴や設定データはすべてローカルのサーバー上に安全に保管・保持されます。 [1, 2, 3, 4]

この自己ホスト型の特性には、以下のようなメリットとセキュリティ上の利点があります。

  • データの完全なプライバシー保護: 入力したプロンプトや生成された回答、個人データは外部のAI事業者(OpenAIやAnthropicなど)の学習データとして使用されることはありません。 [1]
  • 永続的なデータ管理: Dockerコンテナ上の/dataディレクトリ等にデータが永続化される仕組みになっており、アプリ本体をアップデートや再作成した場合でも、過去の履歴やシステム設定は消えずに保持されます。 [1, 2, 3]
  • クローズド環境での利用: インターネットに接続されていないオフライン環境でも動作するため、極めて機密性の高いデータを扱う用途に最適です

あとから分かったことだが、

Open Web UI にはpython のどのバージョンが必要?かというと

Open WebUI の現在の対応状況は次のとおりです。

  • Python 3.11(推奨)
  • Python 3.12
  • Python 3.13(未対応)
  • Python 3.10 以下(未対応)

1.まずpython3.11を用意しよう!

この手順でいく

https://www.python.org/downloads

から、OSに合わせたダウンロードページに遷移してください。今回はWindowsで説明します。

今回は

https://www.python.org/downloads/release/python-3119

がZIP版だったので

Windowsのリンク先に行くと、バージョンがいくつかありますが、OSのbitに合わせたembeddableパッケージをダウンロードします。

今回は、執筆時点で安定版の3.11.9バージョン、64-bitを上述のpythonフォルダにダウンロードします。ダウンロードが完了したら、解凍しましょう。解凍後のフォルダ名がpython-3.11.9-embed-amd64と長いので、python-3.11.9に変更します。

3. pipを使えるようにする

先程のpython-3.11.9フォルダ内にpython311._pthがありますので、これをメモ帳で開きます。そして、#import site#を削除してimoprt siteに変更して保存します。

続いて、get-pip.pyのリンクを右クリックして「名前をつけてリンク先を保存」でpython-3.11.9フォルダにダウンロードします。
このファイルをコマンドプロンプトで実行するには、python-3.11.9フォルダをエクスプローラーで開き、アドレスに「cmd」と入力してEnterを押して、コマンドプロンプトを出現させます。そして、python.exe get-pip.pyを実行します。
これでpip installが使用することができるようになりました

使えるようになったpythonディレクトリをシステム環境変数に登録しよう

cmdを起動してpythonを確認しよう

pythonと打って見たら。。。

無事 環境変数にパスを設定できた

やっとOpen Web UIをインストールできる。

https://docs.openwebui.com/getting-started

によるとPIPでインストールできるので

python-3.11.9フォルダをエクスプローラーで開き、アドレスに「cmd」と入力してEnterを押して、コマンドプロンプトを出現させます。そして、

python.exe -m pip install open-webui

インストール後、次のコマンドを実行してpipを最新バージョンに更新します。

python.exe -m pip install --upgrade pip

でインストール完了です。

Open Web UIの起動方法です

C:\python-3.11.9

Open_WebUI_Starter.bat
を作り

cd Scripts
open-webui serve
pause

を入れて実行ました。

起動できました!

Open WebUIの起動に成功すると、ブラウザで次のアドレスにアクセスできます: http://localhost:8080

完了です。

AI modelがないので使えません。

1,なのでまずバックエンドのOllamaを用意します。Ollama のインストール

Ollama はローカルでLLMを実行するためのバックエンドエンジンです。 [1]

  1. Ollama 公式サイト
    https://ollama.com/download
    からインストーラーをダウンロードし、実行します。
  1. インストールが完了すると、自動的にバックグラウンドで起動します。
  2. コマンドプロンプトやターミナルを開き、以下のコマンドで適当なモデル(例: qwen2.5)をダウンロードします。 [1, 2, 3, 4]
ollama pull qwen2.5

もう一度 http://localhost:8080 へアクセスしてみると 

つかえるようになりました!

毎回ollamaとOpen_WebUI_Starter.batが起動するように

Ollamaアプリを新しく追加登録する

一覧にないアプリやファイル、フォルダを自動起動させたい場合は、専用のフォルダに追加します。 [1]

  1. キーボードの [Windows] + [R] キーを押して「ファイル名を指定して実行」を開きます。
  2. 名前の欄に shell:startup と入力し、「OK」をクリックします。
  3. 「スタートアップ」フォルダが開くので、自動起動したいアプリやファイルのショートカットアイコンをドラッグ&ドロップで追加します

Ollamaの設定

Cloud ークラウドモデルとWeb検索を有効にします。を今回はOffにしてみた。

Expose Ollama to the network ーOllamaをネットワークに公開する
他のデバイスやサービスからOllamaへのアクセスを許可する。

無事Wifiがない環境でも使えるようになった

おすすめの高速かつ頭いいモデル

ollama run deepseek-r1:8b

もう一度 http://localhost:8080 へアクセスしてみると

参考URL

https://www.itflashcards.com/blog/ja/ollama-open-webui

https://chatgpt-enterprise.jp/blog/ollama-deepseek-r1

[Blender5.1][Rig][UE5.8] Auto Rig Pro を購入から使用UE5インポートまで

[Blender 5.1][Rig][UE5.8] From purchasing to using Auto Rig Pro and importing into UE5

https://superhivemarket.com/products/auto-rig-pro

Ado To Cartした。

カートに入った CheckOutして

Create Accountした

Confirm My Accountした

もう一回Checkoutしてこの画面きた。PayPal Checkoutした。

Ordersクリックした。

Blender5.1.1にドラッグした

入らないので

8 downloads
auto_rig_pro_3.78.21.zip
proxy_picker_1.1.11.zip
rig_tools_3.78.18.zip
Al_linux_1.20.zip -winにはいらない
Al_macIntel_1.20.zip -winにはいらない
Al_mac_1.20.zip -winにはいらない
Al_win_1.20.zip あとで必要
io_scene_gltf2.zip Blender5.1.1には不要

zipを選択

入った。


Blendr5.1.1に

D:\Download\auto_rig_pro_Set

入れたいけど

AI_win_1.20.zipがMissing Manifestだって。

それはたぶん 入れるzipを間違えているだけ でした。

確認したところ、AI_win_1.20.zip はBlenderアドオン本体ではなく、Auto-Rig ProのAI用データ/実行ファイルでした。なのでBlenderの「Install from Disk」で直接入れると Missing Manifest になります。

つまり入れる順番はこれです。

  1. Blenderで Edit > Preferences > Get Extensions / Add-ons > Install from Disk
  2. まずこれを入れる
    D:\Download\auto_rig_pro_Set\auto_rig_pro_3.78.21.zip

    auto_rig_pro_3.78.21.zip
    proxy_picker_1.1.11.zip – 開発中の機能
    rig_tools_3.78.18.zip ー開発中の機能入れないほうがいい
    Al_win_1.20.zip あとで必要
    io_scene_gltf2.zip Blender5.1.1には不要

Auto-Rig Proの設定内にある Install AI Files… からこれを選ぶ
D:\Download\auto_rig_pro_Set\AI_win_1.20.zip

  1. AI files Installed successfully と言われる。

Auto-Rig Pro公式ドキュメントでも、本体は auto_rig_pro_x.xx.xx.zip をInstall from Disk、AIファイルはAuto-Rig Pro設定内の Install AI Files… から入れる手順になっています: Auto-Rig Pro Installation

補足: Blender 5.1.1なら io_scene_gltf2.zip は入れなくてOKです。公式説明でもBlender 4.2以上はGLTF exporterの追加インストールをスキップするよう書かれています。rig_tools_3.78.18.zip もAuto-Rig Pro本体を入れるなら基本入れない方がいいです。

正常にインストールできると「N」キー押すとARPがでる

素体をインポートして

メッシュ全部選択して

「Auto-Rig Pro:Smart」機能を試してみた

FullBody選択

Guess Markers押す。(10秒ぐらいかかる)

Skelton Setting Presetsから

今回はUE4 Mannequinsを選択した

Guess Fingers 押す。

指にフィットしたようだ。

GO!押す(10秒ぐらいかかる)

できたっぽい。Match To Rig 押す(10秒)

リグができた。

スキニング

Skin押す

Preserved VolumeをOFFでまだBindおさない。

オブジェクトモードにする[Tab]キー

Blender5.1 オブジェクトモード
  1. オブジェクトモードにする[Tab]キー
  2. Shift を押しながらバインドするMeshオブジェクト(複数可)、Armature の(rig)の順に選択する
  3. Bind ボタンを押す

全メッシュではフリーズしたので、Voxelized に切り替え

段階的にBindした

全部Meshの外に行ったので成功がわかった。

スケールが低すぎるため、バインディングが部分的に、または完全に失敗した可能性があります。

スケール修正を有効にして再度バインディングを実行してください。OKした。

ポーズの確認

ポーズモードへの切り替え方法

アーマチュア(ボーン)を選択した状態で、[Ctrl] + [Tab] キーを押すのが最も早いショートカットです。

Blebnder5.1 ポーズモード

屈伸できた

Export

Auto-Rig Pro: ExportのExport FBXでエクスポート

Unreal Engine

Humanoid と

UE4 Legacy をオンにした

書き出し終わったらしい

UE5へインポート

import Texture オン

import Material オン

プロジェクトのUE4マネキンを壊さないようにSkeletonはNoneでインポートした

インポートできたら、skeletonをチェック root、pelvisがあっていればOK

SkeltalMesh > Create > IK Rig

とりあえずはこれくらい設定した

指も設定

IKRig > Create IK Retargeter

①SK_Mannequinも同じようにRoteteOnlyで設定したIK Rigを選ぶ

②Source選択

③Create > Import Animation Seqense

④T_poseを検索してインポートした。

完成

#UE4 #UE4Study Character #HairCloth #HairShadingModel #AdvancedLocomotionSystem ファサっと天使の輪と束感でUEのヘアーの使い方

服と髪をなびかせるHair Clothはこれをやった
https://docs.unrealengine.com/4.27/ja/InteractiveExperiences/Physics/Cloth/Overview/
1, [Section Selection (セクション選択)]
2,左クリックを使って、クロスとして使用したいメッシュの一部を選択します。次に右クリックして、コンテキスト メニューを開いて Cloth アセットを作成します。
3,コンテキスト メニューから、[Create Cloth Asset from Selection (選択したものから Cloth アセットを作成)] を選びます。

 A,Asset Name – 後で簡単に見つけられるようにアセットに名前を付けてください。

 B,Remove from Mesh – クロスとして関連付けたい別個のジオメトリのメッシュの構成要素があれば、このオプションを有効にします。そうでなければ、チェックを入れずにこのままにします。

 C,Physics Asset – Cloth アセットがキャラクター用ならば、その Physics アセットを使ってクロス シミュレーションで適切なコリジョンが生じるようにします。

4,[Create] ボタンをクリックします。
5,セクションを再度右クリックして、コンテキスト メニューを表示させて、[Apply Clothing Asset] にマウスをかざし、利用可能なクロス アセットから適用するものを選択します。これで作成した任意のクロス アセットが選択したセクションに関連付けられます。

・ペイント – マウスの左ボタン
・消去 – Shift キー + マウスの左ボタン
・クロスのプレビュー – H キー

[Window] を選択し、リストから [Clothing] を選択します。
[Clothing Data] リストから選択します。

[Activate Cloth Paint] ボタンをクリックして、選択した Cloth アセットをペイントするために使用可能なプロパティを有効にします。

・ブラシの半径を5にして
・強度を0.2
・フォールオフ0.5
でヘアをクリックすると塗れる。ピンクが塗ってない場所

走ってみると

髪が前に来ちゃう

ので

物理ボディを修正した

全骨コリジョン作ったほうが調整しやすかった。(指は消した)

Spine5がこういった設定 Radius 10以下ぐらい

以下は昔の設定

で設定することが重要

でやっといい感じ

HairShadingModel 基本はこれをやった。

つまりこれで髪の天使の輪みたいなものができる。 (まわしてみないと分からない)

でも髪の束感は出したかったのでOpacityMaskもありにするとこうなる。

束感のために使ってるテクスチャはこれで

https://drive.google.com/file/d/11JcBV0dAtrexQew0fbKUZcLRfbYn7FuZ/view?usp=sharing

こんな感じにスキャッター、スペキュラ、ラフネスにMultyplyしたらいい感じになった。

使っているMIでの値はこんな感じ

最後にまたこれをやって

やっとこうなった

元のモデルはここ

https://www.deviantart.com/xcrofty/art/NieR-Automata-A2-678696387

[Unreal Engine 5.3.2] Create a Cloth Simulation の使い方と流れ

unreal engine 5.3.2 create a cloth simulation How to use and flow

UnrealEngine5.3.2のCloth Toolの使い方と流れ。

0,再生ヘッドは止めておくべき
1,該当のマテリアルを Isolate でシングル表示後に
2,メッシュ右クリックからData >Create 


3,Window>Clothから作られた>Dataを選択
4,「Activate Cloth Paint」押す


5,ピンクになる Cloth WindowのBrushプロパティでる。


6,Cloth WindowのBrush> Radias 15.0程度に
7,Clothで動かしたいところをを白に塗る
8,「Deactivate Cloth Paint」押す 
9、メッシュ右クリック>Apply Clothing Dataでできる。
9,Preview Animation▼でWalkやRunで確認 再生ヘッドを再生する
10,SkeltalMesh保存
11,PhysicsAssetを開く
12,Details>BodySetup>Primitives>Capsules>Index[0]>
13,Radiusを減らす
14,Rotationも合わせる
15,PhysicsAssetを保存
16,Preview Animation▼でWalkやRunで確認
17,SkeltalMesh開きなおしでPhysicsAssetの修正が反映される様子。

別のメッシュからCloth Dataをコピーするには?

1,Add Clothing で追加できる。

2,9、メッシュ右クリック>Apply Clothing Dataでできる。

修正する場合の使い方と流れ


1,該当のマテリアルを Isolate でシングル表示後に
2,メッシュ右クリックからRemove Clothing Data する。>>必要はない。
3,(4,「Activate Cloth Paint」押す )からの流れをやり直す

・マテリアルが透明のTranslusentだとピンクが表示されないことがある。青に塗ると色が混ざって緑とかになる。
・メッシュの割りsubDivisionLevelは多くないと成功するものも成功しない。
・ClothのDeactiveClothPaintはすごく処理が重い。


・処理の早いマシンでならMarvelous Designerで作ったメッシュでもなんとか塗れる。


・Marvelous Designerで作った複雑なプリーツなどは再現されにくい。


・Runで前にあるスカートは太ももで蹴り上げてしまうので、ポリゴン数は少なめのほうがいいかも、割りは多い方がいいのでバランスが重要


・今回成功した前面の割りはこんなもの。

困った場合に成功したスカートだけのポリゴンデータを載せておく

Skirt_Short_Cage2_Middle_Koshi.fbx これだけダウンロードできます。

https://drive.google.com/file/d/18SzHq33gl-FMdSjuai_5FXDudgZiWxQc/view?usp=sharing

揺れのためには

全骨子リジョン作ったほうが調整しやすかった(指は消した)

でキチンと設定することが重要

成功した状態

参考URL

自分用リンク

jennie9_6_v12z_v00145_4JacketMiniSkirt_CageSizeedSW.zip

https://drive.google.com/file/d/1KGYQ4AlubC05BAtDRw5o5UKXlnh_XNwg/view?usp=sharing

jennie9UE532.zip

https://drive.google.com/file/d/156VO3HnXSrQEzEfB9UHOAPO3Q82FoZbl/view?usp=sharing

エラー集

DataでCreateでのエラー

三角形が縮退しているため、衣類シミュレーション メッシュの生成に失敗しました。三角形内に一致する頂点が見つかりました
Failed to generate clothing sim mesh due to degenerate triangle, found conincident vertices in triangle

とか言われたらクリーンアップしてバインドしなおしになっちゃう。。。

https://forums.unrealengine.com/t/failed-to-generate-clothing-sim-mesh-due-to-degenerate-triangle/94634/2

おそらく頂点が別の頂点の上に重なっていることを意味します。Blender では、Remove Doubles を実行してこれを修正します。

Remove Doublesは名前がMerge by Distanceに変わっただけです。

治らない。。。。

こうも書いてあった。

「出力単位をメートルではなくセンチメートルに変更してみてください。」

ミリメートルに変更した!

治った。

でも、できたのは後ろ髪だけ。

ほかはFailedなので作り直しが必要だった。MayaではNurbs CircleをLoftで全部作れた。

自分用 Hair_Furcraea_Plant_NurbsLoftV2.zip

https://drive.google.com/file/d/1ByJfw5QSSNDOQsdk_q9LjZPVS-n5bnEb/view?usp=sharing

Clothでひとふさずつ動かそうとしたらひどかった ボリュームがなくなった。つぶれた。

これならばKawaiiPhysicsのほうがいいかも?

と思ったが全体をやってみた。ボリュームは確かになくなるけど、ある程度形状は保っている感じ。いいのかもこれで。

ということで髪の毛全体テスト

自分用 Hair_Furcraea_Plant_NurbsLoftV3.zip

https://drive.google.com/file/d/1z4aipUFdHk19qCcrBb9bWvsf3KpWvlG9/view?usp=sharing

できたのがこれ ちょっとつぶれるし伸びた印象

でキャラごとmmで出力したけど

Preview Animationするとすごいちっさくなった。キャラBPで拡大すればいいでしょとおもっていたらそれは問題無い様子。

Dataを作り塗ることができた。Falloffは1.0にするともっとなだらかになるはず。

けども。DeactiveClothPaint後の様子がすごいバグ。

わーん

ということでmmにしてもキャラクターだとエンジンのサイズ計算がバグってますので使わないことにしました。

フォーラムに投稿しました。

https://forums.unrealengine.com/t/failed-to-generate-clothing-sim-mesh-due-to-degenerate-triangle-found-conincident-vertices-in-triangle/1678556

mmでは調整したスカートも壊れる

単位はcmにして後ろ髪はCloth 、前髪、左髪、右髪はスキンウェイトかKawaiiPhysicsにしておわりだなこりゃ。

完成2

ゲームでの再生

自分用 jennie9_6_v12z_v00148_HairFurcraea_mmSW.zip

https://drive.google.com/file/d/1FDRjjub_SM___Yb-u_kv5cwspP-1NdVf/view?usp=sharing

自分用 Hair_Furcraea_Plant_NurbsLoftV7_FullLoft.zip

https://drive.google.com/file/d/1pnZHcPUl-wykW3DBE_RcYHBIJ2Ks2N4-/view?usp=sharing

[UE5][BackUp]Cppプロジェクトでいらないフォルダを含めずにバックアップzipを作成するバッチを作った件

gitだと大容量なサーバーとか用意するの大変だから・・・

実行内容はこんなかんじ

D:\Sandbox\BookAnimation_Codex\UE574Book\UE5CppFolder_MakeBackUpZip.bat
にフォルダをドラッグしたら
中身の
.vs
Binaries
Build
DerivedDataCache
Intermediate
Saved
(pluginsのプラグイン名フォルダ内BinariesとIntermidiateも消してます)
を除いた内容のzipを
D:\Sandbox\BookAnimation_Codex\UE574Book\ドラッグしたフォルダ名.zipに作るバッチを作った
zip実行中になんでもいいからプログレスバーとパーセントを表示するようにした

※バックアップからの復旧方法

VisualStudioなどでビルドします。

UE5CppFolder_MakeBackUpZip.bat

@echo off
setlocal EnableExtensions

if "%~1"=="" (
    echo Usage:
    echo   Drag a folder onto this bat file.
    echo.
    pause
    exit /b 1
)

set "BACKUP_SOURCE=%~f1"
if not exist "%BACKUP_SOURCE%\" (
    echo Error: folder not found.
    echo   %BACKUP_SOURCE%
    echo.
    pause
    exit /b 1
)

set "BACKUP_ZIP=%~dp1%~nx1.zip"
set "BACKUP_SCRIPT=%~dp0UE5CppFolder_MakeBackUpZip.ps1"

echo Source:
echo   %BACKUP_SOURCE%
echo.
echo Output:
echo   %BACKUP_ZIP%
echo.
echo Excluding folders:
echo   .vs Binaries Build DerivedDataCache Intermediate Intermidiate Saved
echo.

if not exist "%BACKUP_SCRIPT%" (
    echo Error: helper script not found.
    echo   %BACKUP_SCRIPT%
    echo.
    pause
    exit /b 1
)

powershell -NoProfile -ExecutionPolicy Bypass -File "%BACKUP_SCRIPT%" -Source "%BACKUP_SOURCE%" -ZipPath "%BACKUP_ZIP%"
set "BACKUP_ERROR=%ERRORLEVEL%"

if not "%BACKUP_ERROR%"=="0" (
    echo.
    echo Failed.
    pause
    exit /b %BACKUP_ERROR%
)

echo.
echo Done.
pause

UE5CppFolder_MakeBackUpZip.ps1

param(
    [Parameter(Mandatory = $true)]
    [string]$Source,

    [Parameter(Mandatory = $true)]
    [string]$ZipPath
)

$ErrorActionPreference = 'Stop'

$sourceFullPath = [System.IO.Path]::GetFullPath($Source).TrimEnd('\', '/')
$zipFullPath = [System.IO.Path]::GetFullPath($ZipPath)

$exclude = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
'.vs', 'Binaries', 'Build', 'DerivedDataCache', 'Intermediate', 'Intermidiate', 'Saved' | ForEach-Object {
    [void]$exclude.Add($_)
}

function Test-IsExcludedBackupFile {
    param([Parameter(Mandatory = $true)][string]$FileFullPath)

    if ([System.StringComparer]::OrdinalIgnoreCase.Equals($FileFullPath, $zipFullPath)) {
        return $true
    }

    $relative = $FileFullPath.Substring($sourceFullPath.Length).TrimStart('\', '/')
    foreach ($part in ($relative -split '[\\/]')) {
        if ($exclude.Contains($part)) {
            return $true
        }
    }

    return $false
}

Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem

if (Test-Path -LiteralPath $zipFullPath) {
    Remove-Item -LiteralPath $zipFullPath -Force
}

Write-Host 'Scanning files...'
$files = @(
    Get-ChildItem -LiteralPath $sourceFullPath -Force -Recurse -File | Where-Object {
        $fileFullPath = [System.IO.Path]::GetFullPath($_.FullName)
        -not (Test-IsExcludedBackupFile -FileFullPath $fileFullPath)
    }
)

$total = $files.Count
Write-Host ("Files to zip: {0}" -f $total)

$zip = [System.IO.Compression.ZipFile]::Open($zipFullPath, [System.IO.Compression.ZipArchiveMode]::Create)
$count = 0

try {
    if ($total -eq 0) {
        Write-Progress -Activity 'Creating backup zip' -Status 'No files to zip' -PercentComplete 100
    }
    else {
        foreach ($file in $files) {
            $count++
            $fileFullPath = [System.IO.Path]::GetFullPath($file.FullName)
            $relative = $fileFullPath.Substring($sourceFullPath.Length).TrimStart('\', '/')
            $entryName = $relative -replace '\\', '/'
            $percent = [int][Math]::Floor(($count * 100.0) / $total)

            Write-Progress `
                -Activity 'Creating backup zip' `
                -Status ("{0}%  {1}/{2}  {3}" -f $percent, $count, $total, $entryName) `
                -PercentComplete $percent

            if (($count % 25) -eq 0 -or $count -eq 1 -or $count -eq $total) {
                Write-Host ("{0,3}%  {1}/{2}  {3}" -f $percent, $count, $total, $entryName)
            }

            [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
                $zip,
                $fileFullPath,
                $entryName,
                [System.IO.Compression.CompressionLevel]::Optimal) | Out-Null
        }
    }
}
finally {
    $zip.Dispose()
    Write-Progress -Activity 'Creating backup zip' -Completed
}

Write-Host ("Created: {0}" -f $zipFullPath)
Write-Host ("Files: {0}" -f $count)

[UE5.8.0][Maya][Retarget] TposeのキャラクターモデルをAposeのMannequinのモーションにリターゲットする方法

MM_T_Pose をエクスポート

Export Optionはこう

MayaにFBXインポートした状態で保存する SKM_Quinn_Simple_Tpose.ma

Tposeのキャラクターモデル(ここではfanModelのサイトからダウンロードしたものを用意した)

https://www.deviantart.com/d-presso/art/Nier-Re-in-carnation-Levannia-XPS-DL-880627377

を開いて


先ほどのTposeのFBXをインポートして

Root ( SKM_Quinn_Simple_Tpose:root_Quinn_T_pose ) のトップ のjointをスケールしてキャラクターにあわせる。

複製してフリーズする

以下ツールでスキンウェイトをコピー

SkinWeightCopy_First_Select_MeshGroup_to_Second Select_MeshGroup.mel

// SkinWeightCopy First Select MeshGroup to Second Select MeshGroup 
string $selectedArrFUllPath[] = `ls -long -sl`;
print($selectedArrFUllPath);

string $FirstSelect=$selectedArrFUllPath[0];

string $SecondSelect=$selectedArrFUllPath[1];

string $rejectArr[];

//string $inputNodes[] = `ls -type transform -long -dag $FirstSelect`;
//string $destNodes[] = `ls -type transform -long -dag $SecondSelect`;

string $inputNodes_mesh[] = `ls -type mesh -long -dag $FirstSelect`;
string $destNodes_mesh[] = `ls -type mesh -long -dag $SecondSelect`;

$inputNodes_meshlong=size($inputNodes_mesh);
$destNodes_meshlong=size($destNodes_mesh);

string $inputNodes[];
string $destNodes[];
clear $inputNodes;
clear $destNodes;
string $mesh;
string $parent;
for($d0 = 0; $d0 <$inputNodes_meshlong;$d0++){
    $mesh=$inputNodes_mesh[$d0];
    string $parentS[] = `listRelatives -parent -path -type transform $mesh`;
    $parent=$parentS[0];
    $inputNodes[size($inputNodes)] = $parent;
}
for($d0 = 0; $d0 <$destNodes_meshlong;$d0++){
    $mesh=$destNodes_mesh[$d0];
    string $parentS[] = `listRelatives -parent -path -type transform $mesh`;
    $parent=$parentS[0];
    $destNodes[size($destNodes)] = $parent;
}

print("$inputNodes= ------------------------------------------------ \n");
print($inputNodes);

print("$destNodes= ------------------------------------------------ \n");
print($destNodes);



$inputNodeslong=size($inputNodes);
$destNodeslong=size($destNodes);

string $buffer[];
string $buffer_i[];
$finded = 0;
for($d = 0; $d <$destNodeslong;$d++){
    string $destNode_name=$destNodes[$d];
    
    if($destNode_name==$SecondSelect){
            
    }else{
        print("$destNode_name= "+$destNode_name+"\n");
        
        $numTokens = `tokenize $destNode_name "|" $buffer`;
        $bufferlong=size($buffer);
        string $destNode_name_Short = $buffer[$bufferlong-1]+"";
        print("$destNode_name_Short= "+$destNode_name_Short+"\n");
        $finded = 0;
        for($i = 0; $i <$inputNodeslong;$i++){
            string $inputNode_name=$inputNodes[$i];
            $numTokens_i = `tokenize $inputNode_name "|" $buffer_i`;
            $bufferlong_i=size($buffer_i);
            string $inputNode_name_Short = $buffer_i[$bufferlong_i-1]+"";
            print("$inputNode_name_Short= "+$inputNode_name_Short+"\n");
                
            //$index=`gmatch $inputNode_name $destNode_name_Short`;
            //if($index > 0){
            if($inputNode_name_Short==$destNode_name_Short){
                //$blendShapeName = $blendshapeRet_name;
                print("$destNode_name= "+$destNode_name+" isHit!! "+"\n");
                select -r $inputNode_name;
                select -add $destNode_name;
                copySkinWeights  -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
                //copySkinWeights -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint -sourceSkin $inputNode_name -destinationSkin $destNode_name;
                $finded = 1;
            }//if
        }//for
        if($finded==0){
            $rejectArr[size($rejectArr)] = $destNode_name;
        }//if
    }//if
}//for
print("----rejected_list---start"+"\n");
print($rejectArr);
print("----rejected_list---end"+"\n");
select -r $FirstSelect ;
select -add $SecondSelect ;

適当にスキンウェイトを確認

rootとMesh 選択してFBX出力

UEへインポート

IKRig作成

IKRig設定

マネキンのほうはこう

IK Retargeter 作成

SounceにCreate>Import From Animation Sequence

MM_T_Pose選択

ポーズがそろう

Op Stack >Add Default Ops 押す とそろった

あとは必要なアニメーションを選択してエクスポート

追加で調整した作業 RootMotionを切ってエクスポートしないとだめだった

そのあとは以下を参考にしてください。

[ZBrush]ローポリからハイポリを作成する基本ステップ

ローポリからハイポリを作成する基本ステップ

ローポリモデルの準備外部ツール(Mayaなど)やZBrushで作成したローポリメッシュを読み込みます。この時点でUV展開を済ませておくと後々スムーズです。

◆間違ったインポート手順
ファイルを開くときファイルを開くでOBJ開くと「2.5dモード」がおきる。

◆正しいインポート手順は
ライトボックスからDynaSphreかなんか開いといて ツールからインポート
OBJ選択するとOK

◆右側のメニューから「ジオメトリ(Geometry)」を開きます。

◆トポロジー編集>頂点結合(頂点結合距離0.01)

◆サブディビジョンの適用(細分化)「ディバイド(Divide)」 ボタンを押すたびにポリゴン数が4倍になり、メッシュが滑らかに 1回が妥当

zbrush の2.5dモードの抜け方 

2.5Dセクションに2つありますね。だから、全部消すためにCTRL+Nをして、それからライトボックスからDynaSphreかなんか開いといて Tを押せばいいんだよ。

助けて!2.5Dにハマっちゃった?普通の3D設定に戻す方法がわからないんだよね
https://www.reddit.com/r/ZBrush/comments/19e5wvd/help_stuck_in_2d5_not_sure_how_to_get_back_to_the/?tl=ja