之前用Maya建模,打开节点编辑器,能够看到每一步的编辑是一个节点,能够对之前做过的操作再做修改,真的好酷哇——

现在了解到了Houdini,感觉这才是学程序的该用的建模——程序化建模

起因

在边学边做自己的一个小小虚幻项目,其中一部分,我想要生成一座城市。

生成城市使用Houdini边学边做,其中有这么一个需求:

我生成了一个矩形网格,相邻的点都有边相连,我希望删除一些边,使其像是城市的道路。

在UE里用PCG做我看官方的Demo是用了一次随机的最小生成树和一次删除最小积分。

但在Houdini里似乎对图论不那么友好啦。

但,Houdini的扩展性真的超好,对于不好实现的功能,可以用Vex这个简单的脚本语言来做,更复杂一点的,可以用Python来做。

考虑到图论算法这件事情是挺复杂的,我们就用Python吧。

安装算法库

Houdini带了一个Python解释器(python.exe),能够做基础的Python操作,但第三方库嘛,当然就什么都没有啦。

在Houdini的安装目录里,有个python3XX的文件夹(可能有多个这样的文件夹,但只有一个里面有Python解释器)

测试解释器有没有pip,如果没有,此处应当先给Houdini的解释器装个pip:

https://srcblog.ffeng123.win:23443/archives/1c64ec65-34a8-4cac-88e1-c9290dd7f3bc

写算法什么的,我可没那么勤奋。

安装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节点,可以将这些边变成面。

我能想到的,最大的成功就是无愧于自己的心。