Maya上でRayを飛ばして衝突点を取得する
Houdiniでやれ
リファレンス
MayaPythonAPI2.0 MFnMesh
MayaPythonAPI2.0 MItMeshVertex
モデルの用意
サンプル用の適当なメッシュを用意している
# -*- coding: utf-8 -*- from __future__ import unicode_literals from __future__ import print_function from __future__ import absolute_import from maya import cmds from maya.api import OpenMaya as om2 def create_bridge(): """ それっぽいモデルの作成 :return :[piers_names], plank_name :rtype: list[list[str],str] """ piers_num = 20 piers_radius = 0.2 piers_height = 2 plank_margin = 1 y_offset = 3 piers = [] for i in [-1, 1]: for j in range(piers_num): z_pos = (float(j) / (piers_num - 1) - 0.5) * piers_num created_cylinder = cmds.polyCylinder(radius=piers_radius, height=piers_height) cmds.xform(created_cylinder, translation=(i, piers_height / 2 + y_offset, z_pos)) piers.append(created_cylinder) plank = cmds.polyCube(height=0.5, depth=piers_num + plank_margin, width=2 + plank_margin) cmds.xform(plank, translation=(0, piers_height + y_offset, 0)) return [piers, plank] def create_terrain(): """ ぼこぼこした地面の作成 :return: terrain_name :rtype: str """ max_height = 3 base_plane = cmds.polyPlane(width=30, height=30, subdivisionsX=10, subdivisionsY=10) cmds.polyMoveVertex(base_plane, translateY=max_height, random=1) cmds.xform(base_plane, translation=(0, -max_height/2, 0)) cmds.polySmooth(base_plane, divisions=2) return base_plane piers, plank = create_bridge() terrain = create_terrain()
衝突点に対して頂点を移動
橋の法線方向が-Y方向のメッシュの頂点を取得し、rayで判定した位置に対して移動している
# -*- coding: utf-8 -*- from __future__ import unicode_literals from __future__ import print_function from __future__ import absolute_import from maya import cmds from maya.api import OpenMaya as om2 def get_face_from_direction_y_minus(mesh_dag): """ 指定のメッシュからYマイナス方向のフェースを構成する頂点番号を取得する :param mesh_dag: dag path from mesh node :type mesh_dag: om2.MDagPath :return : vertex indices :rtype : list[int] """ mit_mesh = om2.MItMeshPolygon(mesh_dag) got_indices = [] while True: if mit_mesh.isDone(): break current_normal = mit_mesh.getNormal() if current_normal[1] < 0: got_indices += mit_mesh.getVertices() # MItMeshPolygon の next は無意味引数を求められる(バグ) mit_mesh.next(0) return got_indices def ray_transform(ray_src, ray_dest, vertex_indices): """ rayを飛ばして接点に頂点を移動 今回はレイの方向は特に指定せず下方向で。 :type ray_src: om2.MDagPath :type ray_dest: om2.MDagPath :type vertex_indices: list[int] :return: """ ray_direction = om2.MFloatVector(0, -1, 0) src_mit_vtx = om2.MItMeshVertex(ray_src) dest_mfn_mesh = om2.MFnMesh(ray_dest) for index in vertex_indices: src_mit_vtx.setIndex(index) src_position = src_mit_vtx.position(om2.MSpace.kWorld) src_position = om2.MFloatPoint(src_position) # 被衝突メッシュ側の関数として用意されている衝突判定関数 # また、似たような関数であるallIntersectionを利用すると貫通先の複数の衝突の取得もできる hit_point, hit_ray_param, hit_face, hit_triangle, hit_bary1, hit_bary2 = dest_mfn_mesh.anyIntersection( src_position, ray_direction, om2.MSpace.kWorld, 1000, True) # APIで移動した場合は戻すが出来ないので今回のようなスクリプトで実行するだけなら大概はxformで移動 # 速度を重視する場合はcmdsプラグインとして移動前の状態を保管してctrl+z用の関数として作成する # src_mit_vtx.setPosition(om2.MPoint(hit_point), om2.MSpace.kWorld) # SPI2.0での頂点移動 cmds.xform("{}.vtx[{}]".format(str(ray_src), index), t=[v for v in hit_point][0:3], worldSpace=True) # 衝突判定の取得情報 """ print("source_point:{}".format(src_position)) # 衝突した座標 print("hit_point:{}".format(hit_point)) # 衝突した座標 print("hit_ray_param:{}".format(hit_ray_param)) # 衝突点までの距離 print("hit_face:{}".format(hit_face)) # 衝突したフェースのindex print("hit_triangle:{}".format(hit_triangle)) # 衝突した三角形フェースの相対index(ポリゴンが保持しているindex) print("hit_bary1:{}".format(hit_bary1)) # 衝突点が三角フェースのどの辺に位置するかどうかの比率 print("hit_bary2:{}".format(hit_bary2)) # 衝突点が三角フェースのどの辺に位置するかどうかの比率 """ terrain_sel = om2.MGlobal.getSelectionListByName(terrain[0]) """:type transform_sel: om2.MSelectionList""" terrain_dag = terrain_sel.getDagPath(0) """:type transform_dag: om2.MDagPath""" for pier in piers: pier_sel = om2.MGlobal.getSelectionListByName(pier[0]) """:type transform_sel: om2.MSelectionList""" pier_dag = pier_sel.getDagPath(0) """:type transform_dag: om2.MDagPath""" y_direction_face_vertices = get_face_from_direction_y_minus(pier_dag) ray_transform(pier_dag, terrain_dag, y_direction_face_vertices)
補足
注意点
anyIntersectionの以下の引数はドキュメント通りの名前だと受け入れてくれない
(arg=~を書かないで指定する)
- raySource
- rayDirection
- space
- maxParam
- testBothDirections
補足
実務ではカメラからの衝突したメッシュの取得に利用したことはある
接地を行うなどの特殊なコンストレイントプラグインなんかを作ると楽しいかもしれない
モデリングに使うならHoudiniでやったほうが他の取り回しも効いていいと思う