b站UP主七里雪凝的湖边小屋教程——P3/P4【Houdini】萌新的《湖边小屋》教程拆解与实现,殊途同归!_哔哩哔哩_bilibili
目录
内容概括
详细步骤
1 [Facet节点]让每个ceiling面片拥有自己的points
2 [Attribute Wrangle节点]定义一些需要用到的属性
3 [Attribute Wrangle节点]划分branch面
3 [Attribute Wrangle节点]划分trunk面
4 [Attribute Wrangle节点]调整branch的trunk面
5 [Group Delete节点]删除不用的group
6 [Attribute Wrangle节点]划分每个roof面的level高度
用到的节点
1.facet节点
用到的VEX function
1.expandprimgroup
2.primpoints
3.getbbox
2.itoa
3.addpoint
4.set
5.setprimvertex
内容概括把ceiling组的面片正确划分,为拉伸屋顶做准备。
用到了facet节点
主要用到了Attribute Wrangle节点。
教程中形象的用主干trunk和分支branch表示ceiling的面片地位,设定属性,与attribute create节点作用相同,可以直接在attribute wrangle里用 i@...=... 来设定属性并赋予初始值。
初值设置为-1是为了与自动初值0分开来。
关于 Houdini中@定义属性的问题可以看看这篇文章:Houdini定义属性方式-CSDN
这一步 Run Over 选择 Detail(only once) 让代码仅运行一次,以下是完整代码:
int pts[];
vector q0, q1, p1, p2, dir, tan;
vector src_minpos, src_maxpos, dst_minpos, dst_maxpos;
int ptnum1, ptnum2, new_prim, tri_prim1, tri_prim2;
int join_head, join_tail;
float width;
int prims[] = expandprimgroup(0, "ceiling"); //get all the prims
foreach(int main_prim; prims) { //use main_prim to get every prim_number as branch-prim
pts = primpoints(0, main_prim); //get all points of main_prim
getbbox(0, itoa(main_prim), src_minpos, src_maxpos); //get bounding box max&min, returns vectors
foreach(int primnum; prims) {
getbbox(0, itoa(primnum), dst_minpos, dst_maxpos); // get a prim as trunk-prim
if(abs(src_minpos.y - dst_minpos.y) > 0.01) continue; // two prims should be in the same "y"
q0 = point(0, "P", pts[0]);
q1 = point(0, "P", pts[1]); //get two near points
if(abs(src_minpos.z - dst_maxpos.z) < 0.01 || abs(src_maxpos.z - dst_minpos.z) < 0.01) {
if((dst_minpos.x < src_minpos.x + 0.01) && (dst_maxpos.x + 0.01 > src_maxpos.x)){
p1 = set((src_minpos.x + src_maxpos.x)/2.0, src_minpos.y, src_minpos.z);
p2 = set((src_minpos.x + src_maxpos.x)/2.0, src_maxpos.y, src_maxpos.z);
dir = abs(src_minpos.z - dst_maxpos.z) < 0.01 ? {0,0,-1} : {0,0,1};
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2);
p1 = p1.zyx; //change z->x x->z
p2 = p2.zyx;
q0 = q0.zyx;
q1 = q1.zyx;
}else continue; // the primnum-prim can not be a trunk
}else if (abs(src_minpos.x - dst_maxpos.x) < 0.01 || abs(src_maxpos.x - dst_minpos.x) < 0.01){
if(dst_minpos.z - 0.01 < src_minpos.z && dst_maxpos.z + 0.01 > src_maxpos.z){
p1 = set(src_minpos.x, src_minpos.y, (src_minpos.z + src_maxpos.z)/2);
p2 = set(src_maxpos.x, src_maxpos.y, (src_minpos.z + src_maxpos.z)/2);
dir = abs(src_minpos.x - dst_maxpos.x) < 0.01 ? {-1,0,0} : {1,0,0};
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2);
}else continue;
}else continue;
int flag = abs(q0.x - q1.x) < 0.01;//flag =1,lineq0q1 is vertical to linep1p2
if(abs(q0.x - p1.x) < 0.01){ //q0 and p1 are on the same "x"
setprimvertex(0, main_prim, flag, ptnum1);
setprimvertex(0, main_prim, flag + 1, ptnum2);
new_prim = addprim(0, "poly", pts[flag], pts[flag + 1], ptnum2, ptnum1);
tri_prim1 = addprim(0, "poly", pts[flag - 1], pts[flag], ptnum1);
tri_prim2 = addprim(0, "poly", pts[flag + 1], pts[flag + 2], ptnum2);
}else{ //q0 and p2 are on the same "x"
setprimvertex(0, main_prim, flag + 0, ptnum2);
setprimvertex(0, main_prim, flag + 1, ptnum1);
new_prim = addprim(0, "poly", pts[flag], pts[flag + 1], ptnum1, ptnum2);
tri_prim1 = addprim(0, "poly", pts[flag-1], pts[flag], ptnum2);
tri_prim2 = addprim(0, "poly", pts[flag + 1], pts[flag + 2], ptnum1);
}
//tri_prim1 attributes
setprimgroup(0, "triangle", tri_prim1, 1, "set");
setprimattrib(0, "join_head", tri_prim1, -1, "set"); //initialvalue need to be set as "-1"
setprimattrib(0, "join_tail", tri_prim1, -1, "set");
setprimattrib(0, "trunk", tri_prim1, primnum, "set");
setprimattrib(0, "heading", tri_prim1, dir, "set");
setprimattrib(0, "twin", tri_prim1, -1, "set");
//tri_prim2 attributes
setprimgroup(0, "triangle", tri_prim2, 1, "set");
setprimattrib(0, "join_head", tri_prim2, -1, "set");
setprimattrib(0, "join_tail", tri_prim2, -1, "set");
setprimattrib(0, "trunk", tri_prim2, primnum, "set");
setprimattrib(0, "heading", tri_prim2, dir, "set");
setprimattrib(0, "twin", tri_prim2, -1, "set");
//set join_head and join_tail value
join_head = dir.x + dir.z > 0.01 ? ptnum2 : ptnum1;
join_tail = dir.x + dir.z > 0.01 ? ptnum1 : ptnum2;
tan = point(0, "P", pts[flag - 1]) - point(0, "P", pts[flag]);
width = length(tan)/2;
//new_prim attributes
setprimattrib(0, "N", new_prim, {0,1,0}, "set"); //normal
setprimgroup(0, "roof", new_prim, 1, "set");
setprimattrib(0, "twin", new_prim, main_prim, "set");
setprimattrib(0, "trunk", new_prim, primnum, "set");
setprimattrib(0, "dir", new_prim, dir, "set");
setprimattrib(0, "join_head", new_prim, join_head, "set");
setprimattrib(0, "join_tail", new_prim, -1, "set");
setprimattrib(0, "width", new_prim, width, "set");
//old prim main_prim attributes
setprimgroup(0, "ceiling", main_prim, 0, "set"); //remove from group"ceiling"
setprimgroup(0, "roof", main_prim, 1, "set"); // add to the newgroup "roof"
setprimattrib(0, "twin", main_prim, new_prim, "set");
setprimattrib(0, "trunk", main_prim, primnum, "set");
setprimattrib(0, "dir", main_prim, dir, "set");
setprimattrib(0, "join_tail", main_prim, join_tail, "set");
setprimattrib(0, "width", main_prim, width, "set");
setprimattrib(0, "branch", primnum, main_prim, "append");
setprimattrib(0, "branch", primnum, new_prim, "append");
break;
}
}
每一步的详细理解
//定义变量
//其中,以下所有涉及到判断的在>左边和<右边都会有0.01,来使判断更加精确
int pts[];
vector q0, q1, p1, p2, dir, tan;
vector src_minpos, src_maxpos, dst_minpos, dst_maxpos;//编号src在下面代表着branch的面上点,dst代表着trunk的面上点
int ptnum1, ptnum2, new_prim, tri_prim1, tri_prim2;
int join_head, join_tail;
float width;
int prims[] = expandprimgroup(0, "ceiling"); //得到group“ceiling”里所有面的面号
foreach(int main_prim; prims) { //用main_prim取循环提取prims数组中的每个面号
pts = primpoints(0, main_prim); //得到面号为main_prim的面上的所有点号
getbbox(0, itoa(main_prim), src_minpos, src_maxpos); //得到这个面的bounding box的最小点和最大点的坐标,点的类型是vector,这里用到itoa是把整型int转换成string为了拟合getbbox这个函数的用法规则
foreach(int primnum; prims) {
getbbox(0, itoa(primnum), dst_minpos, dst_maxpos);
if(abs(src_minpos.y - dst_minpos.y) > 0.01) continue; //src和dst面至少要在同一个高度,也就是y要相同,如果不同 则跳出这层循环,继续找与src面匹配的dst面
q0 = point(0, "P", pts[0]);
q1 = point(0, "P", pts[1]); //这里的pts就是上面拿到的main_prim面上的点数组,得到点好为0和1的相邻点
//以下是判断部分
if(abs(src_minpos.z - dst_maxpos.z) < 0.01 || abs(src_maxpos.z - dst_minpos.z) < 0.01) {两个面是左右的关系
if((dst_minpos.x < src_minpos.x + 0.01) && (dst_maxpos.x + 0.01 > src_maxpos.x)){
p1 = set((src_minpos.x + src_maxpos.x)/2.0, src_minpos.y, src_minpos.z);
p2 = set((src_minpos.x + src_maxpos.x)/2.0, src_maxpos.y, src_maxpos.z);
dir = abs(src_minpos.z - dst_maxpos.z) < 0.01 ? {0,0,-1} : {0,0,1};
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2); //注意,set只是创建了vector,还需要addpoint来创建点
p1 = p1.zyx; //change z->x x->z
p2 = p2.zyx;
q0 = q0.zyx;
q1 = q1.zyx;
}else continue; //说明trunk面在x轴方向上无法把branch面包含住,因此跳出这次循环,继续找下一个trunk面
}else if (abs(src_minpos.x - dst_maxpos.x) < 0.01 || abs(src_maxpos.x - dst_minpos.x) < 0.01){
if(dst_minpos.z - 0.01 < src_minpos.z && dst_maxpos.z + 0.01 > src_maxpos.z){
p1 = set(src_minpos.x, src_minpos.y, (src_minpos.z + src_maxpos.z)/2);
p2 = set(src_maxpos.x, src_maxpos.y, (src_minpos.z + src_maxpos.z)/2);
dir = abs(src_minpos.x - dst_maxpos.x) < 0.01 ? {-1,0,0} : {1,0,0};
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2);
}else continue;
}else continue;//这个else是两个面的位置无论在左右还是上下方向上都不共边,因此两个面处于独立关系,重新找trunk面
int flag = abs(q0.x - q1.x) < 0.01;//判断成立,则flag=1,说明线q0q1⊥p1p2 反之则q0q1∥p1p2
if(abs(q0.x - p1.x) < 0.01){ //q0和p1在同一边
setprimvertex(0, main_prim, flag, ptnum1);//移动面顶点的位置,但是顶点号的号码是不会变的
setprimvertex(0, main_prim, flag + 1, ptnum2);
new_prim = addprim(0, "poly", pts[flag], pts[flag + 1], ptnum2, ptnum1);
tri_prim1 = addprim(0, "poly", pts[flag - 1], pts[flag], ptnum1);//侧边的面
tri_prim2 = addprim(0, "poly", pts[flag + 1], pts[flag + 2], ptnum2);
}else{ //q0和p2在同一边
setprimvertex(0, main_prim, flag + 0, ptnum2);
setprimvertex(0, main_prim, flag + 1, ptnum1);
new_prim = addprim(0, "poly", pts[flag], pts[flag + 1], ptnum1, ptnum2);
tri_prim1 = addprim(0, "poly", pts[flag-1], pts[flag], ptnum2);
tri_prim2 = addprim(0, "poly", pts[flag + 1], pts[flag + 2], ptnum1);
}
//tri_prim1 attributes
setprimgroup(0, "triangle", tri_prim1, 1, "set");
setprimattrib(0, "join_head", tri_prim1, -1, "set"); //initialvalue need to be set as "-1"
setprimattrib(0, "join_tail", tri_prim1, -1, "set");
setprimattrib(0, "trunk", tri_prim1, primnum, "set");
setprimattrib(0, "heading", tri_prim1, dir, "set");
setprimattrib(0, "twin", tri_prim1, -1, "set");
//tri_prim2 attributes
setprimgroup(0, "triangle", tri_prim2, 1, "set");
setprimattrib(0, "join_head", tri_prim2, -1, "set");
setprimattrib(0, "join_tail", tri_prim2, -1, "set");
setprimattrib(0, "trunk", tri_prim2, primnum, "set");
setprimattrib(0, "heading", tri_prim2, dir, "set");
setprimattrib(0, "twin", tri_prim2, -1, "set");
//set join_head and join_tail value
join_head = dir.x + dir.z > 0.01 ? ptnum2 : ptnum1;
join_tail = dir.x + dir.z > 0.01 ? ptnum1 : ptnum2;
tan = point(0, "P", pts[flag - 1]) - point(0, "P", pts[flag]);
width = length(tan)/2;
//new_prim attributes
setprimattrib(0, "N", new_prim, {0,1,0}, "set"); //normal
setprimgroup(0, "roof", new_prim, 1, "set");
setprimattrib(0, "twin", new_prim, main_prim, "set");
setprimattrib(0, "trunk", new_prim, primnum, "set");
setprimattrib(0, "dir", new_prim, dir, "set");
setprimattrib(0, "join_head", new_prim, join_head, "set");
setprimattrib(0, "join_tail", new_prim, -1, "set");
setprimattrib(0, "width", new_prim, width, "set");
//old prim main_prim attributes
setprimgroup(0, "ceiling", main_prim, 0, "set"); //remove from group"ceiling"
setprimgroup(0, "roof", main_prim, 1, "set"); // add to the newgroup "roof"
setprimattrib(0, "twin", main_prim, new_prim, "set");
setprimattrib(0, "trunk", main_prim, primnum, "set");
setprimattrib(0, "dir", main_prim, dir, "set");
setprimattrib(0, "join_tail", main_prim, join_tail, "set");
setprimattrib(0, "width", main_prim, width, "set");
setprimattrib(0, "branch", primnum, main_prim, "append");
setprimattrib(0, "branch", primnum, new_prim, "append");
break;
}
}
3 [Attribute Wrangle节点]划分trunk面
完整代码,跟之前branch的方法差不多,这里就不过多解释了。
int pts[], ptnum1, ptnum2, new_prim, tri_prim1, tri_prim2, join_head, join_tail;
vector dir, q0, q1, p1, p2, src_minpos, src_maxpos, tmp_dir, tan;
int prims[] = expandprimgroup(0, "ceiling"); //get trunk prims
float width;
foreach(int trunk_prim; prims){
pts = primpoints(0, trunk_prim);
getbbox(0, itoa(trunk_prim), src_minpos, src_maxpos);
int branch_prims[] = prim(0, "branch", trunk_prim);
dir = 0;
q0 = point(0, "P", pts[0]);
q1 = point(0, "P", pts[1]);
foreach(int branch_prim; branch_prims){
dir += abs(prim(0, "dir", branch_prim));
}
if(dir.x > dir.z || abs(dir.z-dir.x) < 0.01 && src_maxpos.x-src_minpos.x < src_maxpos.z-src_maxpos.z+0.01){
p1 = set((src_minpos.x+src_maxpos.x)/2, src_minpos.y, src_minpos.z);
p2 = set((src_minpos.x+src_maxpos.x)/2, src_maxpos.y, src_maxpos.z);
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2);
tmp_dir = {0,0,1}; //p1p2's direction.
p1 = p1.zyx;
p2 = p2.zyx;
q0 = q0.zyx;
q1 = q1.zyx;
}else{
p1 = set(src_minpos.x, src_minpos.y, (src_minpos.z+src_maxpos.z)/2);
p2 = set(src_maxpos.x, src_maxpos.y, (src_minpos.z+src_maxpos.z)/2);
ptnum1 = addpoint(0, p1);
ptnum2 = addpoint(0, p2);
tmp_dir = {1,0,0};
}
int flag = abs(q0.x - q1.x) =0 && level++ > levels[trunk]){
init_level = levels[trunk];
level = level - init_level;
cur = trunk;
while(prim(0, "trunk", cur) != trunk){
//if init_level=0, levels[cur]=levels[cur]+level &&level++;
levels[cur] += init_level ? level : level++;
cur = prim(0, "trunk", cur);
}
levels[cur] += level;
if(!init_level){
twin = prim(0, "twin", cur);
cut_loops(cur, twin);//cut the last prim in the loop
}
}
}
for(int i=0;i
关注
打赏