之前用Maya建模,打开节点编辑器,能够看到每一步的编辑是一个节点,能够对之前做过的操作再做修改,真的好酷哇——
现在了解到了Houdini,感觉这才是学程序的该用的建模——程序化建模
起因
在边学边做自己的一个小小虚幻项目,其中一部分,我想要生成一座城市。
生成城市使用Houdini边学边做,其中有这么一个需求:
我生成了一个矩形网格,相邻的点都有边相连,我希望删除一些边,使其像是城市的道路。
在UE里用PCG做我看官方的Demo是用了一次随机的最小生成树和一次删除最小积分。
但在Houdini里似乎对图论不那么友好啦。
但,Houdini的扩展性真的超好,对于不好实现的功能,可以用Vex这个简单的脚本语言来做,更复杂一点的,可以用Python来做。
考虑到图论算法这件事情是挺复杂的,我们就用Python吧。
安装算法库
Houdini带了一个Python解释器(python.exe),能够做基础的Python操作,但第三方库嘛,当然就什么都没有啦。
在Houdini的安装目录里,有个python3XX的文件夹(可能有多个这样的文件夹,但只有一个里面有Python解释器)
测试解释器有没有pip,如果没有,此处应当先给Houdini的解释器装个pip:
写算法什么的,我可没那么勤奋。
安装networkx库: ./python -m pip install networkx
胶水代码
networkx库里有各种图论算法,我们只需要根据自己的需要调用即可。
但在此之前,需要将Houdini里传进来的模型数据转换成图,以及需要在最后转换回去。
于是有以下代码:
import hou
import networkx as nx
def get_grp(weigthAttr=None,output_id=0):
geo = hou.pwd().geometry(output_id)
pts = geo.points()
grp = nx.Graph()
# 导入点
for pt in pts:
if weigthAttr is None:
grp.add_node(pt.number())
else:
grp.add_node(pt.number(),w=pt.floatAttribValue(weigthAttr))
# 导入片元
for pr in geo.prims():
for ver in pr.vertices():
pta = ver.point().number()
ptb = pr.vertex(ver.number() - 1).point().number()
if not grp.has_edge(pta,ptb):
grp.add_edge(pta,ptb)
# 将点权作为边权
if weigthAttr is not None:
for u,v,data in grp.edges(data=True):
data["weight"] = grp.nodes[u]["w"] + grp.nodes[v]["w"]
return grp
def output_grp(grp,output_id=0):
geo = hou.pwd().geometry(output_id)
# 删除片元
geo.deletePrims(geo.prims(),keep_points=True)
# 添加片元
for u,v in grp.edges():
pol = geo.createPolygon(is_closed=False)
pol.addVertex(geo.point(u))
pol.addVertex(geo.point(v))
# 删除点
pts = geo.points()
removes = []
for p in pts:
if p.number() not in grp:
removes.append(p)
geo.deletePoints(removes)我将胶水代码放到了Houdini内嵌Python的库目录中(python/lib/site-packages/ffutils/hou.py),这样就可以到处复用这段代码啦
效果
一段测试代码:
from networkx.algorithms.tree import minimum_spanning_tree
from ffutils.hou import get_grp,output_grp
grp = get_grp("noise")
mst = minimum_spanning_tree(grp)
output_grp(mst)

Python脚本下面接PolyExpand2D节点,可以将这些边变成面。