nx_fbx_affine_t32.nx source
↩ module page · 235 lines · 14389 B
1// Private affine qualification in the existing FBX/NXA animation ownership chain.
2// Native full matrices are evaluation output only; no new NXA channel or playback claim.
3import "nx_nxa_anim_t30.nx"
4import "nx_nxa_bind.nx"
5import "nx_vecmath.nx"
6import "nx_f32_sincos.nx"
7const AF_UNSUPPORTED:i64=0-50
8const AF_CHANNEL:i64=0-51
9const AF_TIMING:i64=0-52
10const AF_CAPACITY:i64=0-53
11const AF_COMPONENT_LIMIT:i64=FBB_I64_MAX/(2*FBB_Q)
12const AF_RAD_PER_DEG_F32:i64=1016003125
13const AF_FULL_DEG_Q20:i64=360*FBB_Q
14const AF_HALF_DEG_Q20:i64=180*FBB_Q
15struct FbxAffineModel { id:i64,parent:i64,values:*i64,channels:*i64, }
16struct FbxAffineCurve { times:*u8,values:*u8,count:i64, }
17struct FbxAffineScene { models:*FbxAffineModel,count:i64,curves:*FbxAffineCurve,curve_count:i64,graph:*FbxClipGraph,selection:*FbxClipSelection, }
18func af_model(s:*FbxAffineScene,i:i64)->*FbxAffineModel{return ((s.models as i64)+i*__size_of(FbxAffineModel)) as *FbxAffineModel}
19func af_curve(s:*FbxAffineScene,i:i64)->*FbxAffineCurve{return ((s.curves as i64)+i*__size_of(FbxAffineCurve)) as *FbxAffineCurve}
20func af_find(s:*FbxAffineScene,id:i64)->i64{var i:i64=0;while i<s.count{if af_model(s,i).id==id{return i};i=i+1};return FC_MISSING}
21// Twelve words: T,R,S; rotation order; inheritance; unsupported-transform marker.
22func af_properties(b:*u8,n:i64,start:i64,end:i64,big:i64,v:*i64)->i64{
23 let h:*i64=sys_mmap(FBXC_OUTN*8) as *i64
24 let hit:i64=fbb_find_child(b,n,start,end,big,"Properties70" as *u8,h)
25 if hit<=0{sys_munmap(h as *u8,FBXC_OUTN*8);return hit}
26 var p:i64=h[FBXC_PROPSOFF]+h[FBXC_PLEN];let pe:i64=h[FBXC_END]
27 var result:i64=0
28 while p<pe&&result==0{
29 let rc:i64=fbb_node(b,n,p,pe,big,h)
30 if rc<=0{if rc<0{result=rc};p=pe}
31 else{
32 let ep:i64=h[FBXC_PROPSOFF]+h[FBXC_PLEN];let po:i64=h[FBXC_PROPSOFF]
33 var slot:i64=0-1;var identity:i64=0
34 if fbb_string(b,po,ep,"Lcl Translation" as *u8)==1{slot=0}
35 if fbb_string(b,po,ep,"Lcl Rotation" as *u8)==1{slot=3}
36 if fbb_string(b,po,ep,"Lcl Scaling" as *u8)==1{slot=6}
37 if fbb_string(b,po,ep,"RotationOrder" as *u8)==1{slot=9}
38 if fbb_string(b,po,ep,"InheritType" as *u8)==1{slot=10}
39 if fbb_string(b,po,ep,"PreRotation" as *u8)==1||fbb_string(b,po,ep,"PostRotation" as *u8)==1{slot=11}
40 if fbb_string(b,po,ep,"RotationPivot" as *u8)==1||fbb_string(b,po,ep,"ScalingPivot" as *u8)==1{slot=11}
41 if fbb_string(b,po,ep,"RotationOffset" as *u8)==1||fbb_string(b,po,ep,"ScalingOffset" as *u8)==1{slot=11}
42 if fbb_string(b,po,ep,"GeometricTranslation" as *u8)==1||fbb_string(b,po,ep,"GeometricRotation" as *u8)==1{slot=11}
43 if fbb_string(b,po,ep,"GeometricScaling" as *u8)==1{slot=11;identity=FBB_Q}
44 if slot>=0{
45 var q:i64=po;var k:i64=0
46 while k<4&&result==0{let size:i64=fbb_property_size(b,q,ep);if size<5||b[q]!=(83 as u8){result=FBB_BAD}else{q=q+size};k=k+1}
47 if result==0{
48 if slot==9||slot==10{if fbb_property_size(b,q,ep)!=5||b[q]!=(73 as u8){result=FBB_BAD}else{v[slot]=fbxc_i32(b,q+1)}}
49 else{
50 k=0;while k<3&&result==0{
51 if fbb_property_size(b,q,ep)!=9||b[q]!=(68 as u8){result=FBB_BAD}
52 else{let bits:i64=fbxc_i64le(b,q+1);let exponent:i64=(bits>>52)&2047
53 if exponent>=1065{result=FBB_RANGE}else{let value:i64=fbxc_f64_p20(bits);if value<0-AF_COMPONENT_LIMIT||value>AF_COMPONENT_LIMIT{result=FBB_RANGE}else{if slot==11{if value!=identity{v[11]=1}}else{v[slot+k]=value}}}
54 };q=q+9;k=k+1
55 }
56 }
57 }
58 }
59 p=h[FBXC_END]
60 }
61 }
62 sys_munmap(h as *u8,FBXC_OUTN*8);return result
63}
64func af_template(b:*u8,n:i64,big:i64,v:*i64)->i64{
65 let h:*i64=sys_mmap(FBXC_OUTN*8) as *i64
66 let c:*i64=sys_mmap(FBXC_OUTN*8) as *i64
67 if fbb_find_child(b,n,27,n,big,"Definitions" as *u8,h)!=1{return FBB_MISSING}
68 var p:i64=h[FBXC_PROPSOFF]+h[FBXC_PLEN];let end:i64=h[FBXC_END];var matches:i64=0;var result:i64=0
69 while p<end&&result==0{
70 let rc:i64=fbb_node(b,n,p,end,big,h)
71 if rc<=0{if rc<0{result=rc};p=end}else{
72 if fbxc_name_is(b,h[FBXC_NAMEOFF],h[FBXC_NAMELEN],"ObjectType" as *u8)==1&&fbb_string(b,h[FBXC_PROPSOFF],h[FBXC_PROPSOFF]+h[FBXC_PLEN],"Model" as *u8)==1{
73 matches=matches+1
74 if fbb_find_child(b,n,h[FBXC_PROPSOFF]+h[FBXC_PLEN],h[FBXC_END],big,"PropertyTemplate" as *u8,c)!=1{result=FBB_MISSING}else{
75 if fbb_string(b,c[FBXC_PROPSOFF],c[FBXC_PROPSOFF]+c[FBXC_PLEN],"FbxNode" as *u8)!=1{result=AF_UNSUPPORTED}else{result=af_properties(b,n,c[FBXC_PROPSOFF]+c[FBXC_PLEN],c[FBXC_END],big,v)}
76 }
77 };p=h[FBXC_END]
78 }
79 }
80 sys_munmap(h as *u8,FBXC_OUTN*8);sys_munmap(c as *u8,FBXC_OUTN*8)
81 if result!=0{return result};if matches!=1{return FC_AMBIGUOUS};return 0
82}
83func af_models(b:*u8,n:i64,g:*FbxClipGraph,max_models:i64,s:*FbxAffineScene)->i64{
84 if max_models<=0||max_models>n/13{return AF_CAPACITY}
85 let defaults:*i64=sys_mmap(96) as *i64;defaults[6]=FBB_Q;defaults[7]=FBB_Q;defaults[8]=FBB_Q
86 let big:i64=fbxc_big(fbxc_version(b,n));let dr:i64=af_template(b,n,big,defaults);if dr!=0{return dr}
87 var i:i64=0;var count:i64=0;while i<g.count{if fc_object(g,i).kind==FC_MODEL{count=count+1};i=i+1}
88 if count<=0||count>max_models{return AF_CAPACITY}
89 s.models=sys_mmap(count*__size_of(FbxAffineModel)) as *FbxAffineModel;s.count=count
90 let h:*i64=sys_mmap(FBXC_OUTN*8) as *i64
91 i=0;var m:i64=0
92 while i<g.count{
93 let object:*FbxClipObject=fc_object(g,i)
94 if object.kind==FC_MODEL{
95 let model:*FbxAffineModel=af_model(s,m);model.id=object.id;model.parent=0-1;model.values=sys_mmap(96) as *i64;model.channels=sys_mmap(72) as *i64
96 var j:i64=0;while j<12{model.values[j]=defaults[j];j=j+1};j=0;while j<9{model.channels[j]=0-1;j=j+1}
97 if fbb_node(b,n,object.offset,n,big,h)!=1{return FBB_BAD}
98 let rc:i64=af_properties(b,n,h[FBXC_PROPSOFF]+h[FBXC_PLEN],h[FBXC_END],big,model.values);if rc!=0{return rc}
99 if model.values[9]!=0||model.values[10]!=1||model.values[11]!=0{return AF_UNSUPPORTED}
100 m=m+1
101 };i=i+1
102 }
103 i=0;while i<g.link_count{let link:*FbxClipLink=fc_link(g,i);if link.kind==1{let child:i64=af_find(s,link.src);let parent:i64=af_find(s,link.dst);if child>=0&&parent>=0{let model:*FbxAffineModel=af_model(s,child);if model.parent>=0{return FC_AMBIGUOUS};model.parent=parent}};i=i+1}
104 sys_munmap(defaults as *u8,96);sys_munmap(h as *u8,FBXC_OUTN*8);return 0
105}
106
107func af_linear(b:*u8,n:i64,offset:i64,nk:i64)->i64{
108 let h:*i64=sys_mmap(FBXC_OUTN*8) as *i64;let c:*i64=sys_mmap(FBXC_OUTN*8) as *i64
109 let flags:*i64=sys_mmap(32) as *i64;let refs:*i64=sys_mmap(32) as *i64
110 let big:i64=fbxc_big(fbxc_version(b,n))
111 if fbb_node(b,n,offset,n,big,h)!=1{return FBB_BAD}
112 let start:i64=h[FBXC_PROPSOFF]+h[FBXC_PLEN];let end:i64=h[FBXC_END]
113 if fbb_find_child(b,n,start,end,big,"KeyAttrFlags" as *u8,c)!=1{return AF_UNSUPPORTED}
114 if b[c[FBXC_PROPSOFF]]!=(105 as u8){return FBB_BAD}
115 if fbxc_array(b,c[FBXC_PROPSOFF]+1,4,flags)!=0{return FBB_BAD}
116 if fbb_find_child(b,n,start,end,big,"KeyAttrRefCount" as *u8,c)!=1{return AF_UNSUPPORTED}
117 if b[c[FBXC_PROPSOFF]]!=(105 as u8){return FBB_BAD}
118 if fbxc_array(b,c[FBXC_PROPSOFF]+1,4,refs)!=0{return FBB_BAD}
119 var rc:i64=0;var sum:i64=0;var i:i64=0
120 if flags[2]<=0||flags[2]!=refs[2]||flags[2]>nk||flags[1]!=flags[2]*4||refs[1]!=refs[2]*4{rc=FBB_BAD}
121 while i<flags[2]&&rc==0{
122 if (fbxc_i32(flags[0] as *u8,i*4)&14)!=4{rc=AF_UNSUPPORTED}
123 let repeat:i64=fbxc_i32(refs[0] as *u8,i*4);if repeat<=0||repeat>nk-sum{rc=FBB_BAD}else{sum=sum+repeat};i=i+1
124 }
125 if sum!=nk{rc=FBB_BAD}
126 if flags[3]==1{sys_munmap(flags[0] as *u8,flags[1])};if refs[3]==1{sys_munmap(refs[0] as *u8,refs[1])}
127 sys_munmap(flags as *u8,32);sys_munmap(refs as *u8,32);sys_munmap(h as *u8,FBXC_OUTN*8);sys_munmap(c as *u8,FBXC_OUTN*8);return rc
128}
129func af_curves(b:*u8,n:i64,G:*i64,s:*FbxAffineScene,max_keys:i64)->i64{
130 if max_keys<=0||max_keys>FBB_I64_MAX/8{return AF_CAPACITY}
131 s.curve_count=G[1];s.curves=sys_mmap(G[1]*__size_of(FbxAffineCurve)) as *FbxAffineCurve
132 var i:i64=0;var keys:i64=0
133 while i<G[1]{
134 let nk:i64=G[A_CKT+i*4+1];if nk<=0||nk!=G[A_CKV+i*4+1]||nk>max_keys-keys{return AF_CAPACITY};keys=keys+nk
135 let object_index:i64=fc_find(s.graph,G[A_CID+i]);if object_index<0{return object_index}
136 let rc:i64=af_linear(b,n,fc_object(s.graph,object_index).offset,nk);if rc!=0{return rc}
137 let curve:*FbxAffineCurve=af_curve(s,i)
138 let times:*i64=sys_mmap(32) as *i64;let values:*i64=sys_mmap(32) as *i64
139 if b[G[A_CKT+i*4]-13]!=(108 as u8)||b[G[A_CKV+i*4]-13]!=(102 as u8){return FBB_BAD}
140 if fbxc_array(b,G[A_CKT+i*4]-12,8,times)!=0||fbxc_array(b,G[A_CKV+i*4]-12,4,values)!=0{return FBB_BAD}
141 if times[1]!=nk*8||values[1]!=nk*4{return FBB_BAD}
142 curve.times=times[0] as *u8;curve.values=values[0] as *u8;curve.count=nk
143 sys_munmap(times as *u8,32);sys_munmap(values as *u8,32)
144 var k:i64=0;var previous:i64=0-1
145 while k<nk{
146 let ticks:i64=fbxc_i64le(curve.times,k*8);if ticks<0||ticks>FBB_I64_MAX/FBB_Q||ticks<=previous{return AF_TIMING};previous=ticks
147 let bits:i64=fbxc_u32(curve.values,k*4);if ((bits>>23)&255)>=149{return FBB_RANGE}
148 let value:i64=vm_f32_to_int(bits,FBB_Q);if value<0-AF_COMPONENT_LIMIT||value>AF_COMPONENT_LIMIT{return FBB_RANGE};k=k+1
149 }
150 var node:i64=0;var axis:i64=0-1;var seen:i64=0;k=0
151 while k<G[2]{if G[A_CONN+k*3]==G[A_CID+i]{let tag:i64=G[A_CONN+k*3+2];if tag>=3*256+88&&tag<=3*256+90{node=G[A_CONN+k*3+1];axis=tag-(3*256+88);seen=seen+1}};k=k+1}
152 if seen!=1{return AF_CHANNEL}
153 var model:i64=0-1;var slot:i64=0-1;seen=0;k=0
154 while k<G[2]{if G[A_CONN+k*3]==node{
155 let index:i64=af_find(s,G[A_CONN+k*3+1]);if index>=0{let tag:i64=G[A_CONN+k*3+2]
156 if tag==15*256+110{slot=0};if tag==12*256+110{slot=3};if tag==11*256+103{slot=6}
157 if slot<0{return AF_CHANNEL};model=index;seen=seen+1
158 }
159 };k=k+1}
160 if seen!=1||model<0{return AF_CHANNEL};let target:*FbxAffineModel=af_model(s,model)
161 if target.channels[slot+axis]>=0{return FC_AMBIGUOUS};target.channels[slot+axis]=i
162 i=i+1
163 }
164 return 0
165}
166// Admission is bounded before any decoded array allocation. Buffers are owned by this offline job.
167func af_load(b:*u8,n:i64,stack:i64,array_budget:i64,depth_budget:i64,max_models:i64,max_keys:i64,s:*FbxAffineScene)->i64{
168 if s.count!=0||s.curve_count!=0{return FBB_BAD}
169 let report:*NxaBindReport=sys_mmap(__size_of(NxaBindReport)) as *NxaBindReport
170 let admitted:i64=nb_preflight(b,n,array_budget,depth_budget,report);if admitted!=0{return admitted}
171 let G:*i64=sys_mmap(A_WORDS*8) as *i64
172 s.graph=sys_mmap(__size_of(FbxClipGraph)) as *FbxClipGraph;s.selection=sys_mmap(__size_of(FbxClipSelection)) as *FbxClipSelection
173 let selected:i64=a_load_selected(b,n,stack,G,s.graph,s.selection);if selected!=0{return selected}
174 let models:i64=af_models(b,n,s.graph,max_models,s);if models!=0{return models}
175 let curves:i64=af_curves(b,n,G,s,max_keys)
176 sys_munmap(G as *u8,A_WORDS*8);sys_munmap(report as *u8,__size_of(NxaBindReport));return curves
177}
178// Original authored linear scalar curve. Euler values are not rewritten to shortest-path angles.
179func af_sample(c:*FbxAffineCurve,ticks:i64)->i64{
180 let last:i64=c.count-1
181 if ticks<=fbxc_i64le(c.times,0){return vm_f32_to_int(fbxc_u32(c.values,0),FBB_Q)}
182 if ticks>=fbxc_i64le(c.times,last*8){return vm_f32_to_int(fbxc_u32(c.values,last*4),FBB_Q)}
183 var i:i64=1;while i<c.count{
184 let next:i64=fbxc_i64le(c.times,i*8)
185 if ticks<=next{let previous:i64=fbxc_i64le(c.times,(i-1)*8);let a:i64=vm_f32_to_int(fbxc_u32(c.values,(i-1)*4),FBB_Q);let z:i64=vm_f32_to_int(fbxc_u32(c.values,i*4),FBB_Q)
186 let fraction:i64=(ticks-previous)*FBB_Q/(next-previous);return a+(z-a)*fraction/FBB_Q
187 };i=i+1
188 };return vm_f32_to_int(fbxc_u32(c.values,last*4),FBB_Q)
189}
190
191func af_identity(m:*i64)->i64{var i:i64=0;while i<16{m[i]=0;i=i+1};m[0]=FBB_Q;m[5]=FBB_Q;m[10]=FBB_Q;m[15]=FBB_Q;return 0}
192func af_angle(value:i64)->i64{var angle:i64=value%AF_FULL_DEG_Q20;if angle>AF_HALF_DEG_Q20{angle=angle-AF_FULL_DEG_Q20};if angle<0-AF_HALF_DEG_Q20{angle=angle+AF_FULL_DEG_Q20};return nx_f32_mul(vm_int_to_f32(angle,FBB_Q),AF_RAD_PER_DEG_F32)}
193// Column-major Q20 T*Rz*Ry*Rx*S. Only admitted XYZ/RSrs/no-pivot models reach this owner.
194func af_local(v:*i64,out:*i64)->i64{
195 let m:*i64=sys_mmap(128) as *i64;let rotation:*i64=sys_mmap(128) as *i64
196 af_identity(m);m[0]=v[6];m[5]=v[7];m[10]=v[8]
197 var axis:i64=0;var rc:i64=0
198 while axis<3&&rc==0{
199 af_identity(rotation);let rad:i64=af_angle(v[3+axis]);let sn:i64=vm_f32_to_int(nx_f32_sin(rad),FBB_Q);let cs:i64=vm_f32_to_int(nx_f32_cos(rad),FBB_Q)
200 if axis==0{rotation[5]=cs;rotation[6]=sn;rotation[9]=0-sn;rotation[10]=cs}
201 if axis==1{rotation[0]=cs;rotation[2]=0-sn;rotation[8]=sn;rotation[10]=cs}
202 if axis==2{rotation[0]=cs;rotation[1]=sn;rotation[4]=0-sn;rotation[5]=cs}
203 rc=fbb_compose(rotation,m,m);axis=axis+1
204 }
205 if rc==0{m[12]=v[0];m[13]=v[1];m[14]=v[2];var i:i64=0;while i<16{out[i]=m[i];i=i+1}}
206 sys_munmap(m as *u8,128);sys_munmap(rotation as *u8,128);return rc
207}
208// Caller-owned column-major matrices; staged output stays unchanged on failed hierarchy/evaluation.
209func af_evaluate(s:*FbxAffineScene,ticks:i64,use_curves:i64,out:*i64,out_words:i64)->i64{
210 if s.count<=0||s.count>FBB_I64_MAX/128||out_words<s.count*16{return AF_CAPACITY}
211 if ticks<0||ticks>FBB_I64_MAX/FBB_Q||use_curves<0||use_curves>1{return AF_TIMING}
212 let matrices:*i64=sys_mmap(s.count*128) as *i64;let done:*i64=sys_mmap(s.count*8) as *i64
213 let values:*i64=sys_mmap(72) as *i64;let local:*i64=sys_mmap(128) as *i64
214 var remaining:i64=s.count;var rc:i64=0
215 while remaining>0&&rc==0{
216 var progress:i64=0;var i:i64=0
217 while i<s.count&&rc==0{
218 let model:*FbxAffineModel=af_model(s,i)
219 if model.parent>=s.count{rc=FBB_BAD}
220 else{if done[i]==0{var ready:i64=0;if model.parent<0{ready=1}else{if done[model.parent]==1{ready=1}}
221 if ready==1{
222 var k:i64=0;while k<9{values[k]=model.values[k];if use_curves==1&&model.channels[k]>=0{values[k]=af_sample(af_curve(s,model.channels[k]),ticks)};k=k+1}
223 rc=af_local(values,local)
224 if rc==0{let destination:*i64=((matrices as i64)+i*128) as *i64
225 if model.parent<0{k=0;while k<16{destination[k]=local[k];k=k+1}}else{rc=fbb_compose(((matrices as i64)+model.parent*128) as *i64,local,destination)}
226 if rc==0{done[i]=1;progress=progress+1;remaining=remaining-1}
227 }
228 }
229 }};i=i+1
230 }
231 if progress==0&&remaining>0&&rc==0{rc=FC_AMBIGUOUS}
232 }
233 if rc==0{var i:i64=0;while i<s.count*16{out[i]=matrices[i];i=i+1}}
234 sys_munmap(matrices as *u8,s.count*128);sys_munmap(done as *u8,s.count*8);sys_munmap(values as *u8,72);sys_munmap(local as *u8,128);return rc
235}