建设网站有哪些术语,做网站网站要找谁,房价网查询官网,企业网站建设大概多少钱我们的目标是从houdini输出生成的四面体#xff0c;希望是tetgen格式的。
众所周知#xff0c;houdini是不能直接输出四面体的。
有三方案去解决#xff1a;
输出点云ply文件#xff0c;然后利用tetgen生成网格。输出Hounidi内置的.geo格式文件#xff0c;然后写个脚本…我们的目标是从houdini输出生成的四面体希望是tetgen格式的。
众所周知houdini是不能直接输出四面体的。
有三方案去解决
输出点云ply文件然后利用tetgen生成网格。输出Hounidi内置的.geo格式文件然后写个脚本去解析json因为这个文件就是个json。直接从Houdini中利用Python节点输出tetgen格式。
我探索并试验了以上所有三种方案。优缺点如下
第一种方案的缺点是四面体是不可控的因为是tetgen现生成的。
第二种方案的缺点是你要保证geo里面没有多余的数据。例如还存储的颜色或uv等信息或者houdini的prim上还存了其他信息就会导致解析失败。当然我们可以使用clean节点清除掉这些多余信息。我会把脚本放到文末。
第三种方案是最好的。可控性最好。因此后面我们会说这种方法。
tetgen的数据格式
我们首先要介绍一下tetgen的数据格式
我们需要其中三种 .node结尾代表顶点位置 .ele结尾代表四面体编号 .face结尾代表三角形编号 如图其中第一个数代表点/面/单元的数量
ele和face中的顶点编号对应的都是node中点前面那个编号。
例如ele中每一行为 当前单元的编号 四面体第1个顶点的编号 四面体第2个顶点的编号 四面体第3个顶点的编号 四面体第4个顶点的编号
Houdini中使用python节点写出 各个节点的作用如下所示
file: 读入objclean: 清理多余信息非必要polyreduce: 简化模型缩小顶点数非必要tetconform: 生成四面体split: 分出表面三角形和四面体null无作用的节点只是为了占位python node(write_faces): 写出表面三角形编号python node(write_nodes):写出所有顶点位置python node(write_eles)写出四面体编号
脚本的内容在附录。 在这里我们要讲解一下houdini中的数据。
分为四种
points 几何点的信息包括点的位置vertices: 拓扑顶点信息。例如可以存顶点的编号primitives: 图元信息。可以存储例如该四面体的体积大小等detail: 整个几何体的信息。
一个常见的误区是混淆points和vertices。points完全是空间中真实存在的一个点。具有位置速度等信息。但是vertices可以认为是对顶点编号的reference。例如一个正方体的角点可以被三个面同时共享。他都是同一个几何点但是却有三个不同的vertices归属于不同的面。这样的好处是保证了唯一性一个point只对应一个vertex一个vertex只被一个primitive所包含。
这里要注意的是vertices中存的是什么完全取决于图元是什么。假如是个四面体就可以是四个顶点编号。假如是三角形就是三角形三个点编号。 正是由于三角形与四面体都被存在primitive中所以我们才用split将其分开。方面后面输出。
注意在tetconform中勾选add surface triangles才会输出三角面。 最后稍微讲解下python节点中的脚本。
请注意houdini中的python是完全面向对象的因此万物皆为对象。
我们仅以write_nodes为例。API请查阅Houdini的官方文档。
import hou # houdini包
geo hou.pwd().geometry() #当前python节点的第一个输入端口所对应的对象是个SOP对象
print(geo)import os
path hou.hipFile.path() #hip文件所在的位置
path os.path.dirname(path) /models/bunny2000_try.node
print(fpath is {path})pts geo.points() # 获取SOP对象上的point对象列表是个list
numpts len(pts)
print(numpts:, numpts)f open(path, w)
f.write(str(numpts) 3 0 0\n)
for i in range(numpts):pt pts[i] pos pt.position() #获取point对象的position属性就是位置f.write( str(i) str(pos[0]) str(pos[1]) str(pos[2])\n)
f.close()完毕。
非必要内容在taichi中ggui显示
请见 https://github.com/chunleili/learn-meshtaichi 中的tut03
其中主要就是多写了directly_import_surf这个函数而已。
网格文件也请见这里。
结果如图 附录1Houdini中python节点的脚本内容
write_faces
import hou
geo hou.pwd().geometry()
print(geo)import os
path hou.hipFile.path()
path os.path.dirname(path) /models/bunny2000_try.face
print(fpath is {path})# write surface triangles
tris geo.prims()
num_tris len(tris)
f open(path[:-4]face, w)
f.write(str(num_tris) 0\n)
for i in range(num_tris):tri tris[i].points()f.write( str(i) str(tri[0].number()) str(tri[1].number()) str(tri[2].number()) -1 \n)
f.close()write_nodes
import hou
geo hou.pwd().geometry()
print(geo)import os
path hou.hipFile.path()
path os.path.dirname(path) /models/bunny2000_try.node
print(fpath is {path})pts geo.points()
numpts len(pts)
print(numpts:, numpts)f open(path, w)
f.write(str(numpts) 3 0 0\n)
for i in range(numpts):pt pts[i]pos pt.position()f.write( str(i) str(pos[0]) str(pos[1]) str(pos[2])\n)
f.close()write_eles
import hou
geo hou.pwd().geometry()
print(geo)import os
path hou.hipFile.path()
path os.path.dirname(path) /models/bunny2000_try.ele
print(fpath is {path})eles geo.prims()
num_eles len(eles)
print(num_eles:, num_eles)
f1 open(path[:-4].ele, w)
f1.write(str(num_eles) 4 0\n)
for i in range(num_eles):ele eles[i].points()f1.write( str(i) str(ele[0].number()) str(ele[1].number()) str(ele[2].number()) str(ele[3].number())\n)
f1.close()附录2 houdni的geo文件解析转换为tetgen格式四面体的脚本
import os
import jsondef read_geo(from_path):with open(from_path,r) as f:datajson.load(f)# 读取顶点个数等信息pointcountdata[5] # 点个数vertexcountdata[7] primitivecountdata[9] # 四面体个数# 读取四面体的索引topology data[13]pointref topology[1]tet_indices pointref[1]# 四面体的索引是一个一维数组# 读取顶点的位置attributes data[15]pointattributes attributes[1]positions pointattributes[0][1][7][5]return tet_indices,positions, pointcount,vertexcount,primitivecountdef write_tetgen(tet_indices,positions, pointcount, primitivecount,to_path, gen_faceFalse):# 写入tetgen的node文件(也就是顶点的位置)node_file to_path.nodeif(os.path.exists(node_file)):print(remove file: node_file)os.remove(node_file)with open(node_file,w) as f:f.write(str(pointcount) 3 0 0\n)for i in range(pointcount):f.write( str(i) str(positions[i][0]) str(positions[i][1]) str(positions[i][2])\n)# 写入tetgen的ele文件(也就是四面体的索引)ele_file to_path.eleif(os.path.exists(ele_file)):print(remove file: ele_file)os.remove(ele_file)with open(ele_file,w) as f:f.write(str(primitivecount) 4 0\n)for i in range(primitivecount):f.write( str(i) str(tet_indices[i*4]) str(tet_indices[i*41]) str(tet_indices[i*42]) str(tet_indices[i*43])\n)# 写入tetgen的face文件(也就是三角面的索引)face_file to_path.faceif(os.path.exists(face_file)):print(remove file: face_file)os.remove(face_file)if(gen_face):# 由于本身没有三角面所以如果想生成face就自己遍历一遍facecount 0for i in range(primitivecount):facecount 4with open(face_file,w) as f:f.write(str(facecount) 0\n)face_i 0for i in range(primitivecount):f.write( str(face_i) str(tet_indices[i*4]) str(tet_indices[i*42]) str(tet_indices[i*41]) -1\n)face_i 1f.write( str(face_i) str(tet_indices[i*4]) str(tet_indices[i*43]) str(tet_indices[i*42]) -1\n)face_i 1f.write( str(face_i) str(tet_indices[i*4]) str(tet_indices[i*41]) str(tet_indices[i*43]) -1\n)face_i 1f.write( str(face_i) str(tet_indices[i*41]) str(tet_indices[i*42]) str(tet_indices[i*43]) -1\n)face_i 1print(\n\nwrite tetgen file success! \nnode file: node_file\nele file: ele_file)if __name__ __main__:from_pathmodels/bunny1000_dilate/bunny1000_dilate.geoto_pathfrom_path[:-4]tet_indices,positions, pointcount,vertexcount,primitivecount read_geo(from_path)write_tetgen(tet_indices,positions, pointcount,primitivecount,to_path, gen_faceTrue)附录3在太极ggui中显示
learn-meshtaichi tut03
import taichi as ti
import meshtaichi_patcher as Patcherti.init()# CAUTION: 我们只加了这一个函数, 其他的基本不变。这个就是用来读取face文件的
def directly_import_surf():import numpy as npimport ospwd os.getcwd().replace(\\, /)face_file_name pwd /models/bunny_tet/bunny_tet.face# print(face_file_name: , face_file_name)with open(face_file_name, r) as f:lines f.readlines()NF int(lines[0].split()[0])face_indices np.zeros((NF, 3), dtypenp.int32)for i in range(NF):face_indices[i] np.array(lines[i 1].split()[1:-1], dtypenp.int32)return face_indices.flatten()
armadillo_surf_indices directly_import_surf()# 读入四面体网格
def init_tet_mesh(model_name):#基本与上面一样只是多了一个CV关系表示通过一个cell可以找到它的四个顶点theMesh Patcher.load_mesh(model_name, relations[CV])theMesh.verts.place({x : ti.math.vec3})theMesh.verts.x.from_numpy(theMesh.get_position_as_numpy())display_indices ti.field(ti.u32, shape len(armadillo_surf_indices))display_indices.from_numpy(armadillo_surf_indices) #这里直接读入了face文件return theMesh, display_indicesmodel_name models/bunny_tet/bunny_tet.node
armadillo, armadillo_indices init_tet_mesh(model_name)
armadillo_indices.to_numpy()window ti.ui.Window(taichimesh, (1024, 1024))
canvas window.get_canvas()
scene ti.ui.Scene()
camera ti.ui.Camera()
camera.up(0, 1, 0)
camera.fov(75)
camera.position(4.5,4.5,0.6)
camera.lookat(3.8, 3.8, 0.5)
camera.fov(75)frame 0
paused ti.field(int, shape())
paused[None] 1
while window.running:# 用下面这段代码通过提前设置一个paused变量我们就可以在运行的时候按空格暂停和继续了for e in window.get_events(ti.ui.PRESS):if e.key ti.ui.SPACE:paused[None] not paused[None]print(paused:, paused[None])if not paused[None]:# substep()print(fframe: {frame})frame 1# 我们可以通过下面的代码来查看相机的位置和lookat这样我们就能知道怎么调整相机的位置了# print(camera.curr_position,camera.curr_position)# print(camera.curr_lookat,camera.curr_lookat)# movement_speed0.05表示移动速度hold_keyti.ui.RMB表示按住右键可以移动视角# wasdqe可以移动相机camera.track_user_inputs(window, movement_speed0.05, hold_keyti.ui.RMB)scene.set_camera(camera)scene.mesh(armadillo.verts.x, armadillo_indices, color (0.5,0.5,0.5))scene.point_light(pos(0.5, 1.5, 0.5), color(1, 1, 1))scene.ambient_light((0.5,0.5,0.5))canvas.scene(scene)window.show()