intrinsic:packedfulltransform 屬性
對於Pack Gemetry會有一些額外的屬性。創建一個Grid、Sphere,利用Copy節點把小球copy到Grid上,Copy節點中勾選 Pack Geometry Before Copying, 然后用Edit節點移動某個點,會發現信息存儲在 intrinsic:packedfulltransform這個屬性(16位矩陣)里。需要注意的是 intrinsic:packedfulltransform 屬性是只讀的,你不能修改它。但是你可以修改intrinsic:transform (3x3 matrix,控制旋轉,縮放,skew)這個屬性,移動點的位置(P屬性), 這些也會改變intrinsic:packedfulltransform屬性的值。
簡單的一個小案例: http://pan.baidu.com/s/1kVazAmV (houdini_rbd_intrinsic.hip)
再來一個小例子,在Odforce上看到的,利用Copy 節點Instance 一大堆BOX,注意勾選上Copy節點的Pack Geometry Before Copying參數。然后再接一個Wrangle節點,代碼如下:
1 vector scale = fit01(vector(rand(@primnum)), 0.2,1); 2 matrix3 trn = primintrinsic(0, "transform", @primnum); 3 matrix scalem = maketransform(0, 0, {0,0,0}, {0,0,0}, scale, @P); 4 trn *= matrix3(scalem); 5 setprimintrinsic(0, "transform", @primnum, trn);
前后的對比如下:
下面的例子(PackedScale_RBDs.hipnc文件)也用到了上面的技術,不同的是是在Dop的RBD解算中,遇到的問題是Pack物體經過縮放后,物體大小發生變化,它碰撞用到的Guide Geometry實際用的還是結算起始幀的Guide Geometry,雖然在RBD Packed Object中勾選Show Guide Geometry后,看到它是隨着物體大小變化的,但實際不是這樣,看下圖:
可以看到最后物體還是懸浮在空中,加兩行代碼如下:
1 vector scale = chf("scale"); 2 matrix3 trn = primintrinsic(0, "transform", @primnum); 3 matrix scalem = maketransform(0, 0, {0,0,0}, {0,0,0}, scale, @P); 4 trn *= matrix3(scalem); 5 setprimintrinsic(0, "transform", @primnum, trn); 6 7 int pts[] = primpoints(0,i@primnum); 8 setpointattrib(0,"id",pts[0],-1);
最后兩句強制把ID屬性刪除(賦值-1),迫使Dop中重新計算碰撞的Guide Geometry。(據Jeff說,這是目前的唯一方法,Jeff是SideFx開發的)結果如下:
最后還有一個案例也用到了intrinsic:transform屬性(IntrinsicXformsInSim.hip文件)
Constraint Network 節點
每兩個點組成的Polygon構成一個Constraint,每一個點被稱作Anchor。
如果Constraint被破壞(比如力足夠大使之斷掉),那么組成這個Constraint的primitive會被放到一個Primitive的組里,組名:broken。任何在broken組的contraints都會接下來的Constraint Network結算器忽略掉。目前,僅Glue Constraint可以被Bullet結算器打斷。
下面是不同約束具有的不同的Primitive屬性:
線性約束(linear constraints): force、distance
旋轉約束(angular constraints): torque、angle
Glue約束(glue constraints):impact
Anchor 類型
Constraint 幾何體中的每一個點代表一個Anchor,每一個Constraint又兩個Anchor構成,目前有四種不同類型的Anchor,由name、anchor_type、anchor_id屬性指明。
World Space Anchor(世界坐標系Anchor):此類Anchor只有一個點(又P屬性指定),被放在世界坐標系的一個靜止的位置。如果一個Anchor沒有name屬性,或者name屬性指定不是一個有效的RBD Pack物體,那么這個Anchor就被指定為World Space Anchor
Relative Offset Anchor(相對位移Anchor):這種類型的Anchor放在相對於RBD Pack物體的指定位置,這個Anchor的Position跟RBD Pack物體的初始旋轉固定在一起,因此如果物體在結算的時候發生旋轉,那么這個Anchor也跟着旋轉。如果name屬性指定是一個有效的RBD Pack物體,並且anchor_id的屬性設置為-1(或者沒有定義,沒有這個屬性),那么這個Anchor就被當作Relative Offset Anchor。
Point Anchor(點Anchor):這種類型的Anchor的位置,跟某個結算的物體上的一個點綁定在一起,如果name屬性指定是一個有效的RBD Pack物體,並且anchor_id屬性的值也是一個有效的點序號(point index),那么Anchor就會被放在這個指定的點上。另外,如果anchor_type設置為vertex(默認為point),那么Anchor會被放在這樣的一個點上,這個點是由anchor_id指定的vertex所對應的點。(如果結算中物體的拓撲結構發生變化,導致點序號變,可以通過添加anchor_pid、anchor_vid屬性,具體見Constraint Network節點的幫助)
Agent Anchor(代理Anchor):這種Anchor綁定在一個綁定Agent的transform上。具體見幫助,與綁定有關。
Constraint 屬性
condof:點屬性,Integer ; 指定Anchor的約束自由度(0-3)
condir:點屬性,Vector;如果約束自由度是1,這個值定義了一個法向平面,物體可以在其中旋轉,位移。如果約束自由度是2,這個值定義了一個axis(軸向),物體可以繞它位移旋轉。
上面兩個屬性在 Destruction in Houdini 這個教程中粗略的提了一下(constraint_types.hipnc文件)。
Inside the Connectadjacentpieces
adjacent Pieces from Points 方法
// connect_nearby_points ( point wrangle)
/// Creates a new line between the given point numbers. void createline(int pt_a; int pt_b) { int prim = addprim(0, "polyline"); addvertex(0, prim, pt_a); addvertex(0, prim, pt_b); } /// Returns true if the item is contained in the list. int contains(string list[]; string item) { foreach(string str; list) { if (item == str) return 1; } return 0; } int handle = pcopen(0, "P", @P, ch("../searchradius"), chi("../maxsearchpoints")); int max_connections = chi("../maxconnections"); string other_name; string known_pieces[]; int num_connections = 0; string my_name = s@name; while (pciterate(handle) && num_connections < max_connections) { pcimport(handle, "name", other_name); // Don't create connections to multiple points on the same piece, or // to other points on our piece. if ((my_name != other_name) && (num_connections == 0 || !contains(known_pieces, other_name))) { vector other_P; pcimport(handle, "P", other_P); // Only search in the direction of the point normal. if (dot(other_P - @P, @N) >= 0) { int other_ptnum; pcimport(handle, "point:number", other_ptnum); createline(@ptnum, other_ptnum); ++num_connections; if (num_connections < max_connections) push(known_pieces, other_name); } } } pcclose(handle);
// create_ordered_point_pairs (Primitie Wrangle)
// Build a string such as "12-54" for use when identifying duplicate connections. int pt0 = @ptnum; int pt1 = vertexpoint(0, vertexindex(0, @primnum, 1)); s@point_pairs = sprintf("%s-%s", min(pt0, pt1), max(pt0, pt1));
// remove_duplicates(Primitie Wrangle)
string my_point_pair = prim(1, "point_pairs", @primnum); int index = findattribval(1, "primitive", "point_pairs", my_point_pair); // Keep the first match and remove all other duplicates. if (index != @primnum) removeprim(0, @primnum, 0);
流程圖
adjacent Pieces from Surface Points 方法
// consolidate_points(Points wrangle)
// Now, we consolidate all of the duplicates to leave a single point // at the centre of each piece. Using a Fuse SOP doesn't quite work, since // multiple pieces can potentially have the same centroid. int index = findattribval(0, "point", "name", s@name); if (@ptnum != index) { // Rewire any vertices that reference this point to // instead reference the point we're keeping. int v = pointvertex(0, @ptnum); while (v != -1) { setprimvertex(0, -1, v, index); v = vertexnext(0, v); } removepoint(0, @ptnum); } else { // Move the search points back to the centroid of their piece. @P = v@centroid; }
adjacent Points 方法
// create_explicit_lines (point wrangle)
float searchradius = ch("../searchradius"); int haspscale = haspointattrib(0, 'pscale'); int points[]; float @pscale = 1.0; if (haspscale && !chi("../uniformradius")) { points = pcfind_radius(0, "P", "pscale", searchradius, @P, searchradius*@pscale, chi("../maxsearchpoints")); } else { points = pcfind(0, "P", @P, 2*searchradius*@pscale, chi("../maxsearchpoints")); } foreach(int ptj; points) { if(@ptnum == ptj) continue; // Only connect one direction if (ptj < @ptnum) continue; int prim = addprim(geoself(), "polyline"); addvertex(geoself(), prim, @ptnum); addvertex(geoself(), prim, ptj); }
利用vex實現RBD Packed Primitive 在DOP 中轉化
#include "math.h" if(@Frame<15) { float timestep = 1/24.0; // 'packedfulltransform' combines the local transform // and the packed primitive's intrinsic transform. matrix target_xform = primintrinsic(1, "packedfulltransform", @primnum); matrix initial_xform = primintrinsic(0, "packedlocaltransform", @primnum); matrix cur_xform = primintrinsic(0, "packedfulltransform", @primnum); matrix offset = invert(cur_xform) * target_xform; matrix total_offset = invert(initial_xform) * target_xform; // Adjust the transform of the packed primitive so that the display matches // up with the simulation. @P *= offset; setprimintrinsic(0, "transform", @primnum, matrix3(total_offset)); // Update the position/orientation of the RBD object. vector4 q = quaternion(matrix3(offset)); p@orient = qmultiply(q, p@orient); v@w = 2 / timestep * vector(q); vector new_trans = v@trans * offset; v@v = (new_trans - v@trans) / timestep; v@trans = new_trans; } else { i@active = 1; }
這段代碼實在一個破碎教程看到的,作者說這是Sidefx官方的人寫的,感覺很有用。這段代碼貼在Attribute Wrangle中,第一個輸入端輸入是Dop Geometry , 第二個輸入端是利用Transform在Sop對Pack后的同一個物體進行位移。下面是在幫助中搜刮到的一些有助於理解的解釋。
在 hou.PackedPrim 這個Class中,有三個Methods:
transform() → hou.Matrix3
Returns the local 3×3 transform associated with this primitive. The transform doesn’t include the local point transform or any transforms inside the primitive (for example, transforms inside an Alembic file).
fullTransform() → hou.Matrix4
Returns the full 4×4 transform for this primitive’s geometry. This includes translations due to points and any transforms inside the primitive (for example, transforms inside an Alembic file).
setTransform(m4)
Sets this primitive’s local transform. This sets the local 3×3 transform and the translation of the point. This does not affect any transforms inside the primitive (for example, transforms inside an Alembic file).
m4 :A hou.Matrix4 object containing the full transform.