nx_gm_cloth_lib_t138.nx source
↩ module page · 402 lines · 18505 B
1// nx_gm_cloth_lib.nx -- LIB: the PG29 cloth XPBD solver (gm_xpbd_step and its edge/area
2// rulers). Split out of nx_nxa_garment so the GATE can import it without colliding with the
3// generator's main(); the garment organ imports this lib and carries a REAL call (the
4// xpbdcheck verb), so its PG29 watch-row flip is backed by wiring, not a name in a comment.
5// license_tier: ORIGINAL No hw writes (Rule 26).
6import "nx_softtissue.nx"
7// ---- PG29: THE CLOTH XPBD SOLVER (gm_xpbd_step) -- appended 2026-08-26 ----------------------
8// nx_nxa_garment GENERATED a fitted dress; NOTHING simulated it (the page's own comment:
9// "moves with the skin, zero physics", and GPIN is emitted but unconsumed). This is the solver
10// half of PG29, the organ symbol its watch row names. It is a LIB surface inside this organ:
11// main() above is untouched, so the generator's behaviour is bit-identical by construction.
12//
13// SHAPE: standard XPBD (Mueller et al., Position-Based Simulation of Compliant Constrained
14// Dynamics) -- predict under gravity, then iterate distance-constraint projections whose
15// stiffness enters ONLY through a compliance alpha, converted to alpha-tilde = alpha/dt^2 by
16// st_alpha_tilde -- COMPOSED from nx_softtissue, the estate's one XPBD lane, never re-derived.
17// All positions/velocities are i64 in the caller's mesh units; no float anywhere.
18//
19// NO MAGIC NUMBERS: gravity, dt, iteration count and both compliances are CALLER ARGUMENTS.
20// The solver owns only structure. Absolute fabric parameters (a cited bending stiffness) are a
21// NAMED follow-on -- gm_stiffness_mask, the second half of PG29 -- and until they are banked
22// the gate proves RELATIVE physics: a fold happens, area is conserved, stiffness changes the
23// fold. That is exactly the plan row's pre-declared done-rule.
24
25// ---- edge list of the GU x GV ring-grid, DERIVED from the same topology main() emits ----
26// classes: 0 = structural (ring + vertical), 1 = shear (quad diagonals), 2 = bending (2-hop
27// vertical). Rest lengths are measured from the rest verts handed in -- never assumed.
28// Row format per edge: [a, b, rest, class]. Returns the edge count written.
29const GM_EROW: i64 = 4
30const GM_C_STRUCT: i64 = 0
31const GM_C_SHEAR: i64 = 1
32const GM_C_BEND: i64 = 2
33// contact skin = the integer-truncation bound of the radial push (see gm_capsule_project)
34const GM_SKIN: i64 = 2
35
36func gm_isqrt(n: i64) -> i64 {
37 if n <= 0 { return 0 }
38 var x: i64 = n
39 if x > 4096 { x = n/2 }
40 var it: i64 = 0
41 while it < 40 {
42 if x > 0 { let y: i64 = (x + n/x)/2; if y > 0 { x = y } }
43 it = it + 1
44 }
45 return x
46}
47
48func gm_dist(v: *i64, a: i64, b: i64) -> i64 {
49 let dx: i64 = v[a*3] - v[b*3]
50 let dy: i64 = v[a*3+1] - v[b*3+1]
51 let dz: i64 = v[a*3+2] - v[b*3+2]
52 return gm_isqrt(dx*dx + dy*dy + dz*dz)
53}
54
55// build the edge list over an nu x nv2 ring grid (column-major ring index u + row r, vertex
56// id = r*nu + u, ring wraps u -> (u+1)%nu). Caller supplies rest verts; rests are MEASURED.
57func gm_edges_build(verts: *i64, nu: i64, nv2: i64, edges: *i64) -> i64 {
58 var ne: i64 = 0
59 var r: i64 = 0
60 while r < nv2 {
61 var u: i64 = 0
62 while u < nu {
63 let a: i64 = r*nu + u
64 let u1: i64 = (u + 1) % nu
65 // ring structural
66 let br: i64 = r*nu + u1
67 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = br
68 edges[ne*GM_EROW+2] = gm_dist(verts, a, br); edges[ne*GM_EROW+3] = GM_C_STRUCT
69 ne = ne + 1
70 if r + 1 < nv2 {
71 // vertical structural
72 let bv: i64 = (r+1)*nu + u
73 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bv
74 edges[ne*GM_EROW+2] = gm_dist(verts, a, bv); edges[ne*GM_EROW+3] = GM_C_STRUCT
75 ne = ne + 1
76 // shear diagonals of the quad (a, ring-next, below, below-ring-next)
77 let bd: i64 = (r+1)*nu + u1
78 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bd
79 edges[ne*GM_EROW+2] = gm_dist(verts, a, bd); edges[ne*GM_EROW+3] = GM_C_SHEAR
80 ne = ne + 1
81 edges[ne*GM_EROW] = br; edges[ne*GM_EROW+1] = bv
82 edges[ne*GM_EROW+2] = gm_dist(verts, br, bv); edges[ne*GM_EROW+3] = GM_C_SHEAR
83 ne = ne + 1
84 }
85 if r + 2 < nv2 {
86 // bending: 2-hop vertical resists the fold; its compliance is the stiffness knob
87 let bb: i64 = (r+2)*nu + u
88 edges[ne*GM_EROW] = a; edges[ne*GM_EROW+1] = bb
89 edges[ne*GM_EROW+2] = gm_dist(verts, a, bb); edges[ne*GM_EROW+3] = GM_C_BEND
90 ne = ne + 1
91 }
92 u = u + 1
93 }
94 r = r + 1
95 }
96 return ne
97}
98
99// ---- ONE XPBD STEP ---------------------------------------------------------------------------
100// verts/vel: i64 xyz triples, caller's mesh units. pins: vertex ids whose position is HELD
101// (infinite mass -- the GPIN contract). gy_per_tick2: gravity as velocity-delta per tick in mesh
102// units (sign carries direction; the garment's up-axis is +z, so a falling sheet passes negative).
103// alpha_q12_struct / alpha_q12_bend: XPBD compliance per class in the SAME Q12 the tissue lane
104// banks (st_alpha_tilde's own contract); shear uses the structural value. dt_us: microseconds per
105// tick, handed to st_alpha_tilde so stiffness is TIMESTEP-INDEPENDENT -- the whole point of XPBD.
106// Checked XPBD lane. Arithmetic failure -2, boundary -1, allocation -3, degenerate edge -4.
107// Positions and velocities retain caller mesh-units/per-tick convention. dt only scales compliance.
108const GMC_MAX:i64=9223372036854775807
109func gmc_add(a:i64,b:i64,s:*i64)->i64 {
110 if a<0-GMC_MAX || b<0-GMC_MAX {s[0]=0-2;return 0}
111 if b>0 {if a>GMC_MAX-b {s[0]=0-2;return 0}}
112 if b<0 {if a<(0-GMC_MAX)-b {s[0]=0-2;return 0}}
113 return a+b
114}
115func gmc_mul(a:i64,b:i64,s:*i64)->i64 {
116 if a<0-GMC_MAX || b<0-GMC_MAX {s[0]=0-2;return 0}
117 let aa:i64=st_abs(a);let bb:i64=st_abs(b)
118 if bb>0 {if aa>GMC_MAX/bb {s[0]=0-2;return 0}}
119 return a*b
120}
121func gmc_round(a:i64,b:i64,s:*i64)->i64 {
122 if b<=0 || a<0-GMC_MAX {s[0]=0-2;return 0}
123 if st_abs(a)>GMC_MAX-b/2 {s[0]=0-2;return 0}
124 return st_div_r(a,b)
125}
126func gmc_dot(ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,s:*i64)->i64 {
127 return gmc_add(gmc_add(gmc_mul(ax,bx,s),gmc_mul(ay,by,s),s),gmc_mul(az,bz,s),s)
128}
129func gmc_alpha(a:i64,ds:i64,dt:i64,s:*i64)->i64 {
130 let v0:i64=gmc_round(gmc_mul(a,ds,s),ST_Q12,s)
131 let v1:i64=gmc_round(gmc_mul(v0,ST_DT_REF_US,s),dt,s)
132 gmc_round(gmc_mul(v1,ST_DT_REF_US,s),dt,s)
133 if s[0]!=0 {return 0}
134 return st_alpha_tilde(a,ds,dt)
135}
136func gmc_overlap(a:i64,na:i64,b:i64,nb:i64)->i64 {
137 if na==0 || nb==0 {return 0}
138 if a<=0 || b<=0 || na<0 || nb<0 {return 1}
139 if a>GMC_MAX-na || b>GMC_MAX-nb {return 1}
140 if a<b+nb && b<a+na {return 1}
141 return 0
142}
143func gm_xpbd_workspace_words(nvv:i64,ne:i64)->i64 {
144 if nvv<0 || ne<0 {return 0-1}
145 if ne>GMC_MAX-1 {return 0-1}
146 if nvv>(GMC_MAX-1-ne)/7 {return 0-1}
147 let words:i64=1+nvv*7+ne
148 if words>GMC_MAX/8 {return 0-1}
149 return words
150}
151// Caller supplies disjoint workspace; optional inverse masses use Q12, pinned vertices override to0.
152// Buffer extents are the declared nvv/ne/npins counts, as in the existing pointer API.
153// Commit occurs only after every constraint and velocity succeeds; workspace is scratch on failure.
154func gm_xpbd_step_checked(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64,
155 pins:*i64,npins:i64,gz_per_tick:i64,dt_us:i64,iters:i64,
156 alpha_q12_struct:i64,alpha_q12_bend:i64,mass:*i64,mass_count:i64,
157 work:*i64,work_words:i64)->i64 {
158 let need:i64=gm_xpbd_workspace_words(nvv,ne)
159 if need<0 || work_words<need || (work as i64)<=0 {return 0-1}
160 if nvv<1 || (verts as i64)<=0 || (vel as i64)<=0 {return 0-1}
161 if ne>GMC_MAX/32 || npins<0 || npins>nvv {return 0-1}
162 if ne>0 {if (edges as i64)<=0 {return 0-1}}
163 if npins>0 {if (pins as i64)<=0 {return 0-1}}
164 if dt_us<=0 || iters<0 || alpha_q12_struct<0 || alpha_q12_bend<0 {return 0-1}
165 if (mass as i64)!=0 {if (mass as i64)<0 || mass_count<nvv {return 0-1}}
166 if gmc_overlap(work as i64,need*8,verts as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,vel as i64,nvv*24)==1 {return 0-1}
167 if gmc_overlap(work as i64,need*8,edges as i64,ne*32)==1 || gmc_overlap(work as i64,need*8,pins as i64,npins*8)==1 {return 0-1}
168 if (mass as i64)!=0 {if gmc_overlap(work as i64,need*8,mass as i64,nvv*8)==1 {return 0-1}}
169 if gmc_overlap(verts as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1}
170 work[0]=0
171 let q:*i64=((work as i64)+8) as *i64
172 let w:*i64=((q as i64)+nvv*24) as *i64
173 let iw:*i64=((w as i64)+nvv*24) as *i64
174 let lm:*i64=((iw as i64)+nvv*8) as *i64
175 var i:i64=0
176 while i<nvv {
177 iw[i]=ST_Q12
178 if (mass as i64)!=0 {iw[i]=mass[i];if iw[i]<0 {return 0-1}}
179 var k:i64=0
180 while k<3 {if verts[i*3+k]<0-GMC_MAX || vel[i*3+k]<0-GMC_MAX {return 0-2};q[i*3+k]=verts[i*3+k];w[i*3+k]=vel[i*3+k];k=k+1}
181 i=i+1
182 }
183 i=0
184 while i<npins {if pins[i]<0 || pins[i]>=nvv {return 0-1};iw[pins[i]]=0;i=i+1}
185 i=0
186 while i<ne {
187 if edges[i*4]<0 || edges[i*4]>=nvv || edges[i*4+1]<0 || edges[i*4+1]>=nvv {return 0-1}
188 if edges[i*4+2]<0 || edges[i*4+3]<GM_C_STRUCT || edges[i*4+3]>GM_C_BEND {return 0-1}
189 lm[i]=0;i=i+1
190 }
191 i=0
192 while i<nvv {
193 if iw[i]>0 {
194 w[i*3+2]=gmc_add(w[i*3+2],gz_per_tick,work)
195 var k:i64=0
196 while k<3 {q[i*3+k]=gmc_add(q[i*3+k],w[i*3+k],work);k=k+1}
197 }
198 i=i+1
199 }
200 if work[0]!=0 {return work[0]}
201 var it:i64=0
202 while it<iters {
203 var e:i64=0
204 while e<ne {
205 let a:i64=edges[e*4];let b:i64=edges[e*4+1]
206 let ds:i64=gmc_add(iw[a],iw[b],work)
207 if ds>0 {
208 let dx:i64=gmc_add(q[a*3],0-q[b*3],work)
209 let dy:i64=gmc_add(q[a*3+1],0-q[b*3+1],work)
210 let dz:i64=gmc_add(q[a*3+2],0-q[b*3+2],work)
211 let ll:i64=gmc_dot(dx,dy,dz,dx,dy,dz,work)
212 if work[0]!=0 {return work[0]}
213 let ln:i64=st_isqrt(ll)
214 if ln==0 {if edges[e*4+2]>0 {return 0-4}}
215 if ln>0 {
216 var alpha:i64=alpha_q12_struct
217 if edges[e*4+3]==GM_C_BEND {alpha=alpha_q12_bend}
218 let at:i64=gmc_alpha(alpha,ds,dt_us,work)
219 let den:i64=gmc_add(ds,at,work)
220 let c:i64=gmc_add(ln,0-edges[e*4+2],work)
221 let num:i64=gmc_add(gmc_mul(0-c,ST_Q12,work),0-gmc_round(gmc_mul(at,lm[e],work),ST_Q12,work),work)
222 let dl:i64=gmc_round(gmc_mul(num,ST_Q12,work),den,work)
223 lm[e]=gmc_add(lm[e],dl,work)
224 let sa:i64=gmc_round(gmc_mul(iw[a],dl,work),ST_Q12,work)
225 let sb:i64=gmc_round(gmc_mul(iw[b],dl,work),ST_Q12,work)
226 var k:i64=0
227 while k<3 {
228 let diff:i64=gmc_add(q[a*3+k],0-q[b*3+k],work)
229 let un:i64=gmc_round(gmc_mul(diff,ST_Q12,work),ln,work)
230 let da:i64=gmc_round(gmc_mul(sa,un,work),ST_Q12*ST_Q12,work)
231 let db:i64=gmc_round(gmc_mul(sb,un,work),ST_Q12*ST_Q12,work)
232 q[a*3+k]=gmc_add(q[a*3+k],da,work)
233 q[b*3+k]=gmc_add(q[b*3+k],0-db,work)
234 k=k+1
235 }
236 }
237 }
238 if work[0]!=0 {return work[0]}
239 e=e+1
240 }
241 it=it+1
242 }
243 i=0
244 while i<nvv*3 {w[i]=gmc_add(q[i],0-verts[i],work);i=i+1}
245 if work[0]!=0 {return work[0]}
246 i=0
247 while i<nvv*3 {verts[i]=q[i];vel[i]=w[i];i=i+1}
248 return 0
249}
250func gm_xpbd_step(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64,pins:*i64,npins:i64,
251 gz_per_tick:i64,dt_us:i64,iters:i64,alpha_q12_struct:i64,alpha_q12_bend:i64)->i64 {
252 let words:i64=gm_xpbd_workspace_words(nvv,ne)
253 if words<0 {return 0-1}
254 let mem:*i64=sys_mmap_shared(words*8) as *i64
255 if (mem as i64)<=0 {return 0-3}
256 let rc:i64=gm_xpbd_step_checked(verts,vel,nvv,edges,ne,pins,npins,gz_per_tick,dt_us,iters,
257 alpha_q12_struct,alpha_q12_bend,0 as *i64,0,mem,words)
258 let closed:i64=sys_munmap(mem as *u8,words*8)
259 if rc!=0 {return rc}
260 if closed!=0 {return 0-5}
261 return 0
262}
263func gm_xpbd_step_banked(verts:*i64,vel:*i64,nvv:i64,edges:*i64,ne:i64,pins:*i64,npins:i64,
264 gz_per_tick:i64,dt_us:i64,iters:i64,alpha_q12_struct:i64,alpha_q12_bend:i64,bank:*i64)->i64 {
265 if nvv<1 || nvv>GMC_MAX/24 || (bank as i64)<=0 || (verts as i64)<=0 {return 0-1}
266 if gmc_overlap(bank as i64,nvv*24,verts as i64,nvv*24)==1 || gmc_overlap(bank as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1}
267 var i:i64=0
268 while i<nvv*3 {bank[i]=verts[i];i=i+1}
269 return gm_xpbd_step(verts,vel,nvv,edges,ne,pins,npins,gz_per_tick,dt_us,iters,alpha_q12_struct,alpha_q12_bend)
270}
271
272// total triangle area x2 of the ring grid (cross-product magnitudes), the conservation ruler
273// the gate's area tooth reads. Integer, exact up to isqrt rounding, and the SAME function
274// measures before and after so the comparison cannot drift between two rulers.
275func gm_area2(verts: *i64, nu: i64, nv2: i64) -> i64 {
276 var total: i64 = 0
277 var r: i64 = 0
278 while r < nv2 - 1 {
279 var u: i64 = 0
280 while u < nu {
281 let u1: i64 = (u + 1) % nu
282 let a: i64 = r*nu + u
283 let b: i64 = r*nu + u1
284 let c: i64 = (r+1)*nu + u
285 let d: i64 = (r+1)*nu + u1
286 // quad = two triangles (a,b,c) and (b,d,c)
287 var t9: i64 = 0
288 while t9 < 2 {
289 var p1: i64 = a; var p2: i64 = b; var p3: i64 = c
290 if t9 == 1 { p1 = b; p2 = d; p3 = c }
291 let e1x: i64 = verts[p2*3] - verts[p1*3]
292 let e1y: i64 = verts[p2*3+1] - verts[p1*3+1]
293 let e1z: i64 = verts[p2*3+2] - verts[p1*3+2]
294 let e2x: i64 = verts[p3*3] - verts[p1*3]
295 let e2y: i64 = verts[p3*3+1] - verts[p1*3+1]
296 let e2z: i64 = verts[p3*3+2] - verts[p1*3+2]
297 let cx: i64 = e1y*e2z - e1z*e2y
298 let cy: i64 = e1z*e2x - e1x*e2z
299 let cz: i64 = e1x*e2y - e1y*e2x
300 total = total + gm_isqrt(cx*cx + cy*cy + cz*cz)
301 t9 = t9 + 1
302 }
303 u = u + 1
304 }
305 r = r + 1
306 }
307 return total
308}
309
310// ---- CLOTH-OVER-BODY CONTACT: capsule projection --------------------------------------------
311// The world page's body is a skinned figure whose limbs and torso a CAPSULE approximates -- the
312// same primitive st_set_touch's sphere probe already uses one rung down. Projection, not force:
313// after the constraint iterations, any cloth vert inside the capsule is pushed to its surface
314// along the radial, exactly the PBD contact convention. Called once per tick per capsule by the
315// stepper's caller (capsule count is the CALLER's anatomy, not this lib's constant).
316// Capsule: segment (ax,ay,az)-(bx,by,bz) with radius rad, all in the caller's mesh units.
317// Returns the number of verts projected -- 0 on a clean pass, so a gate can assert BOTH ways.
318// Frictionless static/moving capsule contact. No two-way body response or continuous collision.
319// Pinned penetration is an explicit conflict(-6), not a silently moved attachment.
320func gmc_closest(px:i64,py:i64,pz:i64,ax:i64,ay:i64,az:i64,ux:i64,uy:i64,uz:i64,uu:i64,c:*i64,s:*i64)->i64 {
321 let dot:i64=gmc_dot(gmc_add(px,0-ax,s),gmc_add(py,0-ay,s),gmc_add(pz,0-az,s),ux,uy,uz,s)
322 c[0]=ax;c[1]=ay;c[2]=az
323 if uu>0 && dot>0 {
324 if dot>=uu {c[0]=gmc_add(ax,ux,s);c[1]=gmc_add(ay,uy,s);c[2]=gmc_add(az,uz,s)}
325 else {c[0]=gmc_add(ax,gmc_round(gmc_mul(ux,dot,s),uu,s),s);c[1]=gmc_add(ay,gmc_round(gmc_mul(uy,dot,s),uu,s),s);c[2]=gmc_add(az,gmc_round(gmc_mul(uz,dot,s),uu,s),s)}
326 }
327 return s[0]
328}
329func gm_capsule_project_checked(verts:*i64,vel:*i64,nvv:i64,
330 ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,rad:i64,
331 pins:*i64,npins:i64,cvx:i64,cvy:i64,cvz:i64,work:*i64,work_words:i64)->i64 {
332 if nvv<1 || nvv>(GMC_MAX/8-4)/6 || npins<0 || npins>nvv || rad<0 {return 0-1}
333 let need:i64=4+nvv*6
334 if work_words<need || (work as i64)<=0 || (verts as i64)<=0 || (vel as i64)<=0 {return 0-1}
335 if npins>0 {if (pins as i64)<=0 {return 0-1}}
336 if ax<0-GMC_MAX || ay<0-GMC_MAX || az<0-GMC_MAX || bx<0-GMC_MAX || by<0-GMC_MAX || bz<0-GMC_MAX {return 0-2}
337 if cvx<0-GMC_MAX || cvy<0-GMC_MAX || cvz<0-GMC_MAX {return 0-2}
338 if gmc_overlap(work as i64,need*8,verts as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,vel as i64,nvv*24)==1 || gmc_overlap(work as i64,need*8,pins as i64,npins*8)==1 {return 0-1}
339 if gmc_overlap(verts as i64,nvv*24,vel as i64,nvv*24)==1 {return 0-1}
340 work[0]=0
341 let q:*i64=((work as i64)+8) as *i64
342 let w:*i64=((q as i64)+nvv*24) as *i64
343 let cp:*i64=((w as i64)+nvv*24) as *i64
344 var i:i64=0
345 while i<npins {if pins[i]<0 || pins[i]>=nvv {return 0-1};i=i+1}
346 i=0
347 while i<nvv*3 {if verts[i]<0-GMC_MAX || vel[i]<0-GMC_MAX {return 0-2};q[i]=verts[i];w[i]=vel[i];i=i+1}
348 let ux:i64=gmc_add(bx,0-ax,work);let uy:i64=gmc_add(by,0-ay,work);let uz:i64=gmc_add(bz,0-az,work)
349 let uu:i64=gmc_dot(ux,uy,uz,ux,uy,uz,work)
350 let rr:i64=gmc_mul(rad,rad,work)
351 let rs:i64=gmc_add(rad,GM_SKIN,work)
352 if work[0]!=0 {return work[0]}
353 var hits:i64=0
354 i=0
355 while i<nvv {
356 gmc_closest(q[i*3],q[i*3+1],q[i*3+2],ax,ay,az,ux,uy,uz,uu,cp,work)
357 var dx:i64=gmc_add(q[i*3],0-cp[0],work);var dy:i64=gmc_add(q[i*3+1],0-cp[1],work);var dz:i64=gmc_add(q[i*3+2],0-cp[2],work)
358 var dd:i64=gmc_dot(dx,dy,dz,dx,dy,dz,work)
359 if work[0]!=0 {return work[0]}
360 if dd<rr {
361 var p:i64=0
362 while p<npins {if pins[p]==i {return 0-6};p=p+1}
363 if dd==0 {
364 if uu==0 {dx=1;dy=0;dz=0}
365 else {
366 if st_abs(ux)<=st_abs(uy) && st_abs(ux)<=st_abs(uz) {dx=0;dy=uz;dz=0-uy}
367 else {if st_abs(uy)<=st_abs(uz) {dx=0-uz;dy=0;dz=ux} else {dx=uy;dy=0-ux;dz=0}}
368 }
369 dd=gmc_dot(dx,dy,dz,dx,dy,dz,work)
370 }
371 let d:i64=st_isqrt(dd)
372 if d<=0 {return 0-4}
373 q[i*3]=gmc_add(cp[0],gmc_mul(dx,rs,work)/d,work)
374 q[i*3+1]=gmc_add(cp[1],gmc_mul(dy,rs,work)/d,work)
375 q[i*3+2]=gmc_add(cp[2],gmc_mul(dz,rs,work)/d,work)
376 let rx:i64=gmc_add(w[i*3],0-cvx,work);let ry:i64=gmc_add(w[i*3+1],0-cvy,work);let rz:i64=gmc_add(w[i*3+2],0-cvz,work)
377 let vn:i64=gmc_dot(rx,ry,rz,dx,dy,dz,work)
378 if vn<0 {
379 w[i*3]=gmc_add(w[i*3],0-gmc_round(gmc_mul(dx,vn,work),dd,work),work)
380 w[i*3+1]=gmc_add(w[i*3+1],0-gmc_round(gmc_mul(dy,vn,work),dd,work),work)
381 w[i*3+2]=gmc_add(w[i*3+2],0-gmc_round(gmc_mul(dz,vn,work),dd,work),work)
382 }
383 hits=hits+1
384 }
385 if work[0]!=0 {return work[0]}
386 i=i+1
387 }
388 i=0
389 while i<nvv*3 {verts[i]=q[i];vel[i]=w[i];i=i+1}
390 return hits
391}
392func gm_capsule_project(verts:*i64,vel:*i64,nvv:i64,ax:i64,ay:i64,az:i64,bx:i64,by:i64,bz:i64,rad:i64)->i64 {
393 if nvv<1 || nvv>(GMC_MAX/8-4)/6 {return 0-1}
394 let bytes:i64=(4+nvv*6)*8
395 let work:*i64=sys_mmap_shared(bytes) as *i64
396 if (work as i64)<=0 {return 0-3}
397 let rc:i64=gm_capsule_project_checked(verts,vel,nvv,ax,ay,az,bx,by,bz,rad,0 as *i64,0,0,0,0,work,bytes/8)
398 let closed:i64=sys_munmap(work as *u8,bytes)
399 if rc<0 {return rc}
400 if closed!=0 {return 0-5}
401 return rc
402}