code wiki / (root) / nx_gltf_render_descriptor_candidate_t181.nx

nx_gltf_render_descriptor_candidate_t181.nx source

↩ module page · 248 lines · 23296 B

1// nx_gltf_render_descriptor_candidate_t181.nx -- Generates a normal map descriptor with texture, image, and encoding details for GLTF rendering. 2import "nx_gltf_pose_candidate_t181.nx" 3import "nx_sha256.nx" 4import "nx_gltf_writer.nx" 5import "nx_normal_encoding_contract.nx" 6// A present but unsupported map is explicit refusal, never an absent/legacy map. 7// Image offsets inherit this descriptor's exact sourceSha256/sourceBytes identity. 8func gpd_normal_binding(w:*i64,cx:*i64,mat:i64)->i64{ 9 let have:i64=gli_member(cx,mat,"normalTexture");if have<0{return -1};if have==0{return 0} 10 let sp:*i64=cx[4] as *i64;let at:i64=sp[0];let tok:*NxJsonTok=cx[2] as *NxJsonTok 11 glp_reset(tok,cx[0] as *u8,cx[1],at) 12 var reason:*u8="";var valid:i64=1;var texture:i64=-1 13 if tok.kind!=NX_JSON_LBRACE{valid=0;reason="normal-texture-not-object"} 14 if valid==1{if gli_member(cx,at,"extensions")!=0{valid=0;reason="normal-texture-extension-unsupported"}} 15 if valid==1{if gld_int(cx,at,"texCoord",0)!=0{valid=0;reason="normal-texture-uv-set-unsupported"}} 16 if valid==1{ 17 texture=gld_int(cx,at,"index",-1) 18 if texture<0{valid=0;reason="normal-texture-index-invalid"} 19 } 20 // Preserve the original numeric token after checking the existing native numeric range. 21 // Negative scale is legal. Values beyond this representation are explicitly unsupported. 22 if valid==1{ 23 let sh:i64=gli_member(cx,at,"scale") 24 if sh<0{valid=0;reason="normal-scale-invalid"}else{if sh==1{ 25 glp_reset(tok,cx[0] as *u8,cx[1],sp[0]);var fixed:i64=0 26 if tok.kind!=NX_JSON_NUMBER{valid=0;reason="normal-scale-invalid"}else{ 27 if gld_number_fixed((tok.src as i64+tok.tok_off) as *u8,tok.tok_len,FBB_Q,&fixed)!=0{valid=0;reason="normal-scale-native-range-unsupported"} 28 } 29 }} 30 } 31 if valid==1{ 32 let bytes:i64=4*__size_of(i64);let check:*i64=sys_mmap_try(bytes) as *i64 33 if (check as i64)==0{return -7};var k:i64=0;while k<4{check[k]=0;k=k+1} 34 let ir:i64=gpd_image(check,cx,texture);let failed:i64=check[3] 35 sys_munmap_direct(check as *u8,bytes) 36 if ir!=0||failed!=0{valid=0;reason="normal-image-binding-unsupported"} 37 } 38 gpd_s(w,",\"normalMap\":{\"state\":") 39 if valid==0{gpd_s(w,"\"refused\",\"reason\":\"");gpd_s(w,reason);return gpd_s(w,"\"}")} 40 let encoding:i64=ntb_normal_encoding_admit(1,NTB_NORMAL_ENCODING_GLTF_UNORM) 41 if encoding!=NTB_NORMAL_ENCODING_GLTF_UNORM{return -2} 42 gpd_s(w,"\"prepared\",\"sourceIdentity\":\"descriptor.sourceSha256+sourceBytes\",\"texture\":");gpd_n(w,texture) 43 gpd_s(w,",\"image\":");if gpd_image(w,cx,texture)!=0{return -2} 44 gpd_s(w,",\"texCoord\":0,\"scale\":");if gpd_raw(w,cx,at,"scale","1")!=0{return -2} 45 gpd_s(w,",\"normalEncoding\":{\"id\":");gpd_n(w,encoding) 46 gpd_s(w,",\"name\":\"gltf-rgb8-unorm-v1\",\"linearTransfer\":true,\"normalizationRequired\":true},\"tangentFrame\":\"gltf-tangent-xyz-and-w-handedness\",\"renderQualified\":false}") 47 return 0 48} 49 50func gpd_source_identity(w:*i64,cx:*i64)->i64{ 51 let digest:*u8=sys_mmap_try(32);if (digest as i64)==0{return -7};sha256_digest(cx[6] as *u8,cx[7],digest);let hex:*u8="0123456789abcdef";gpd_s(w,"\"sourceSha256\":\"");var i:i64=0;while i<32{let b:i64=digest[i] as i64;gpd_put(w,(hex as i64+b/16) as *u8,1);gpd_put(w,(hex as i64+b%16) as *u8,1);i=i+1};gpd_s(w,"\",");sys_munmap_direct(digest,32);return 0 52} 53 54// Native render descriptor over retained GLB bytes. No geometry or image transcoding. 55// Writer state: output, capacity, position, failure. A null output performs sizing. 56func gpd_put(w:*i64,s:*u8,n:i64)->i64{return glw_put(w,s,n)} 57func gpd_s(w:*i64,s:*u8)->i64{return glw_s(w,s)} 58func gpd_n(w:*i64,n:i64)->i64{return glw_n(w,n)} 59func gpd_raw(w:*i64,cx:*i64,parent:i64,key:*u8,fallback:*u8)->i64{ 60 let h:i64=gli_member(cx,parent,key);if h<0{return -1};if h==0{return gpd_s(w,fallback)} 61 let sp:*i64=cx[4] as *i64;return gpd_put(w,(cx[0]+sp[0]) as *u8,sp[1]-sp[0]) 62} 63func gpd_view(w:*i64,cx:*i64,accessor:i64,v:*i64)->i64{ 64 let st:*i64=cx[5] as *i64;if gltf_accessor_view(cx,accessor,st[2],v)!=0{return -1} 65 gpd_s(w,"[");gpd_n(w,accessor);var i:i64=0;while i<GLD_VIEW_WORDS{gpd_s(w,",");gpd_n(w,v[i]);i=i+1};return gpd_s(w,"]") 66} 67func gpd_alpha(cx:*i64,mat:i64)->i64{ 68 let have:i64=gli_member(cx,mat,"alphaMode");if have<0{return -1};if have==0{return 0} 69 let t:*NxJsonTok=cx[2] as *NxJsonTok;let sp:*i64=cx[4] as *i64;glp_reset(t,cx[0] as *u8,cx[1],sp[0]) 70 if glp_eq(t,"OPAQUE")==1{return 0};if glp_eq(t,"MASK")==1{return 1};if glp_eq(t,"BLEND")==1{return 2};return -1 71} 72func gpd_image(w:*i64,cx:*i64,texture:i64)->i64{ 73 let tx:i64=gld_nth(cx,0,"textures",texture);if tx<0||gli_member(cx,tx,"extensions")!=0{return -1} 74 let image:i64=gld_int(cx,tx,"source",-1);let im:i64=gld_nth(cx,0,"images",image);if im<0||gli_member(cx,im,"uri")!=0{return -1} 75 let vi:i64=gld_int(cx,im,"bufferView",-1);let view:i64=gld_nth(cx,0,"bufferViews",vi);if view<0||gld_int(cx,view,"buffer",-1)!=0||gli_member(cx,view,"extensions")!=0{return -1} 76 let off:i64=gld_int(cx,view,"byteOffset",0);let len:i64=gld_int(cx,view,"byteLength",-1);let bf:i64=gld_nth(cx,0,"buffers",0);let declared:i64=gld_int(cx,bf,"byteLength",-1) 77 if off<0||len<=0||off>declared||len>declared-off{return -1} 78 gpd_s(w,"{\"image\":");gpd_n(w,image);gpd_s(w,",\"offset\":");gpd_n(w,cx[8]-cx[6]+off);gpd_s(w,",\"bytes\":");gpd_n(w,len);gpd_s(w,",\"mime\":");if gpd_raw(w,cx,im,"mimeType","null")!=0{return -1} 79 let samplerHave:i64=gli_member(cx,tx,"sampler");if samplerHave<0{return -1} 80 var sampler:i64=-1 81 if samplerHave==1{let si:i64=gld_int(cx,tx,"sampler",-1);if si<0{return -1};sampler=gld_nth(cx,0,"samplers",si);if sampler<0{return -1}} 82 gpd_s(w,",\"sampler\":{");var mag:i64=9729;var min:i64=9987;var ws:i64=10497;var wt:i64=10497 83 if sampler>=0{mag=gld_int(cx,sampler,"magFilter",9729);min=gld_int(cx,sampler,"minFilter",9987);ws=gld_int(cx,sampler,"wrapS",10497);wt=gld_int(cx,sampler,"wrapT",10497)} 84 if mag!=9728&&mag!=9729{return -1};if min!=9728&&min!=9729&&min!=9984&&min!=9985&&min!=9986&&min!=9987{return -1} 85 if ws!=10497&&ws!=33071&&ws!=33648{return -1};if wt!=10497&&wt!=33071&&wt!=33648{return -1} 86 gpd_s(w,"\"mag\":");gpd_n(w,mag);gpd_s(w,",\"min\":");gpd_n(w,min);gpd_s(w,",\"wrapS\":");gpd_n(w,ws);gpd_s(w,",\"wrapT\":");gpd_n(w,wt);return gpd_s(w,"}}") 87} 88// Validate unit-interval decimals exactly; fixed-point truncation alone admits values just outside endpoints. 89func gpd_unit_token(s:*u8,n:i64)->i64{ 90 var value:i64=0;if gld_number_fixed(s,n,1,&value)!=0{return -1} 91 var start:i64=0;if s[0]==(45 as u8){start=1} 92 var i:i64=start;var digits:i64=0;var before:i64=0;var dotted:i64=0;var first:i64=-1;var leading:i64=0;var tail:i64=0 93 while i<n&&s[i]!=(101 as u8)&&s[i]!=(69 as u8){let c:i64=s[i] as i64;if c==46{before=digits;dotted=1}else{if c!=48{if first<0{first=digits;leading=c-48}else{tail=1}};digits=digits+1};i=i+1} 94 if first<0{return 0};if start!=0{return -1};if dotted==0{before=digits} 95 var exponent:i64=0;var sign:i64=1;let bound:i64=n+1 96 if i<n{i=i+1;if s[i]==(45 as u8){sign=0-1;i=i+1}else{if s[i]==(43 as u8){i=i+1}};while i<n{let d:i64=(s[i] as i64)-48;if exponent>(bound-d)/10{exponent=bound}else{exponent=exponent*10+d};i=i+1}} 97 let magnitude:i64=before-first-1+sign*exponent;if magnitude<0{return 0};if magnitude>0||leading!=1||tail!=0{return -1};return 0 98} 99func gpd_unit_field(w:*i64,cx:*i64,parent:i64,key:*u8,fallback:*u8,count:i64)->i64{ 100 if parent<0{return gpd_s(w,fallback)};let have:i64=gli_member(cx,parent,key);if have<0{return -1};if have==0{return gpd_s(w,fallback)} 101 let sp:*i64=cx[4] as *i64;let begin:i64=sp[0];let end:i64=sp[1];let t:*NxJsonTok=cx[2] as *NxJsonTok;glp_reset(t,cx[0] as *u8,cx[1],begin) 102 if count>1{if t.kind!=NX_JSON_LBRACKET{return -1};nx_json_next(t)};var i:i64=0 103 while i<count{if t.kind!=NX_JSON_NUMBER||gpd_unit_token((t.src as i64+t.tok_off) as *u8,t.tok_len)!=0{return -1};i=i+1;nx_json_next(t);if i<count{if t.kind!=NX_JSON_COMMA{return -1};nx_json_next(t)}} 104 if count>1&&t.kind!=NX_JSON_RBRACKET{return -1};return gpd_put(w,(cx[0]+begin) as *u8,end-begin) 105} 106// Retain unsupported map metadata and expose lit admission instead of silently dropping it. 107func gpd_material_inputs(w:*i64,cx:*i64,mat:i64,pbr:i64)->i64{ 108 var unsupported:i64=0;gpd_s(w,",\"mapInputs\":{");var i:i64=0 109 while i<4{var key:*u8="normalTexture";var parent:i64=mat;if i==1{key="occlusionTexture"};if i==2{key="emissiveTexture"};if i==3{key="metallicRoughnessTexture";parent=pbr} 110 if i>0{gpd_s(w,",")};gpd_s(w,"\"");gpd_s(w,key);gpd_s(w,"\":");var have:i64=0;if parent>=0{have=gli_member(cx,parent,key)};if have<0{return -1};if have==0{gpd_s(w,"null")}else{unsupported=unsupported+1;if gpd_raw(w,cx,parent,key,"null")!=0{return -1}};i=i+1 111 };gpd_s(w,"},\"litEligible\":");if unsupported==0{gpd_s(w,"true")}else{gpd_s(w,"false")};gpd_s(w,",\"litUnsupportedInputs\":");gpd_n(w,unsupported);gpd_s(w,",\"litReason\":\"");if unsupported==0{gpd_s(w,"scalar-basecolor-inputs-supported")}else{gpd_s(w,"material-map-inputs-not-rendered")};gpd_s(w,"\"");return 0 112} 113func gpd_material(w:*i64,cx:*i64,index:i64)->i64{ 114 let mat:i64=gld_nth(cx,0,"materials",index);if mat<0||gli_member(cx,mat,"extensions")!=0{return -1} 115 let mode:i64=gpd_alpha(cx,mat);if mode<0{return -1} 116 gpd_s(w,"{\"index\":");gpd_n(w,index);gpd_s(w,",\"alpha\":");gpd_n(w,mode);gpd_s(w,",\"cutoff\":");if gpd_raw(w,cx,mat,"alphaCutoff","0.5")!=0{return -1} 117 gpd_s(w,",\"doubleSided\":");if gpd_raw(w,cx,mat,"doubleSided","false")!=0{return -1} 118 var h:i64=gli_member(cx,mat,"pbrMetallicRoughness");if h<0{return -1};let sp:*i64=cx[4] as *i64;var pbr:i64=-1;if h==1{pbr=sp[0]} 119 gpd_s(w,",\"factor\":");if gpd_unit_field(w,cx,pbr,"baseColorFactor","[1,1,1,1]",4)!=0{return -1} 120 gpd_s(w,",\"metallicFactor\":");if gpd_unit_field(w,cx,pbr,"metallicFactor","1",1)!=0{return -1} 121 gpd_s(w,",\"roughnessFactor\":");if gpd_unit_field(w,cx,pbr,"roughnessFactor","1",1)!=0{return -1} 122 gpd_s(w,",\"emissiveFactor\":");if gpd_unit_field(w,cx,mat,"emissiveFactor","[0,0,0]",3)!=0{return -1} 123 gpd_s(w,",\"baseColor\":");var texture:i64=-1 124 if pbr>=0{h=gli_member(cx,pbr,"baseColorTexture");if h<0{return -1};if h==1{let bt:i64=sp[0];if gli_member(cx,bt,"extensions")!=0||gld_int(cx,bt,"texCoord",0)!=0{return -1};texture=gld_int(cx,bt,"index",-1);if texture<0{return -1}}} 125 if texture<0{gpd_s(w,"null")}else{if gpd_image(w,cx,texture)!=0{return -1}} 126 if gpd_material_inputs(w,cx,mat,pbr)!=0{return -1} 127 if gpd_normal_binding(w,cx,mat)!=0{return -1} 128 return gpd_s(w,"}") 129} 130func gltf_render_descriptor(cx:*i64,mesh:i64,skin:i64,w:*i64)->i64{ 131 if (cx as i64)<=0||(w as i64)<=0{return -1};let st:*i64=cx[5] as *i64 132 let m:i64=gld_nth(cx,0,"meshes",mesh);let sk:i64=gld_nth(cx,0,"skins",skin);if m<0||sk<0{return -1} 133 if gli_member(cx,m,"extensions")!=0||gli_member(cx,m,"weights")!=0{return -2} 134 let n:i64=gli_array(cx,m,"primitives",0);if n<=0{return -1};let joints:i64=gli_array(cx,sk,"joints",0);if joints<=0{return -1} 135 let v:*i64=sys_mmap_try(GLD_VIEW_WORDS*8) as *i64;if (v as i64)==0{return -7};var rc:i64=0 136 gpd_s(w,"{");if gpd_source_identity(w,cx)!=0{sys_munmap_direct(v as *u8,GLD_VIEW_WORDS*8);return -7};gpd_s(w,"\"schema\":\"nishi-authored-render/1\",\"scope\":\"source-albedo-and-discrete-native-pose\",\"basis\":\"gltf-Y-up\",\"physicalScale\":null,\"sourceBytes\":");gpd_n(w,cx[7]);gpd_s(w,",\"binOffset\":");gpd_n(w,cx[8]-cx[6]);gpd_s(w,",\"mesh\":");gpd_n(w,mesh);gpd_s(w,",\"skin\":");gpd_n(w,skin);gpd_s(w,",\"joints\":");gpd_n(w,joints);gpd_s(w,",\"parts\":[") 137 var i:i64=0 138 while i<n{ 139 let p:i64=gld_nth(cx,m,"primitives",i);if p<0||gli_member(cx,p,"extensions")!=0||gli_member(cx,p,"targets")!=0||gld_int(cx,p,"mode",4)!=4{rc=-2;break} 140 let h:i64=gli_member(cx,p,"attributes");let sp:*i64=cx[4] as *i64;if h!=1{rc=-1;break};let a:i64=sp[0] 141 let vertexColor:i64=gli_member(cx,a,"COLOR_0");if vertexColor<0{rc=-2;break} 142 if i>0{gpd_s(w,",")};gpd_s(w,"{\"attributes\":{");var j:i64=0 143 while j<6{var key:*u8="POSITION";if j==1{key="JOINTS_0"};if j==2{key="WEIGHTS_0"};if j==3{key="TEXCOORD_0"};if j==4{key="NORMAL"};if j==5{key="TANGENT"} 144 let ai:i64=gld_int(cx,a,key,-1);if ai<0{rc=-2;break};if j>0{gpd_s(w,",")};gpd_s(w,"\"");gpd_s(w,key);gpd_s(w,"\":");if gpd_view(w,cx,ai,v)!=0{rc=-2;break};j=j+1 145 };if rc!=0{break};gpd_s(w,"},\"vertexColorInput\":");if gpd_raw(w,cx,a,"COLOR_0","null")!=0{rc=-2;break};gpd_s(w,",\"litGeometryEligible\":");if vertexColor==0{gpd_s(w,"true")}else{gpd_s(w,"false")};gpd_s(w,",\"litGeometryReason\":\"");if vertexColor==0{gpd_s(w,"vertex-color-absent")}else{gpd_s(w,"linear-vertex-color-not-rendered")};gpd_s(w,"\",\"indices\":");let ix:i64=gld_int(cx,p,"indices",-1);if ix<0||gpd_view(w,cx,ix,v)!=0{rc=-2;break};if v[5]!=1||v[6]!=0||(v[3]!=5121&&v[3]!=5123&&v[3]!=5125)||v[1]%3!=0{rc=-2;break} 146 gpd_s(w,",\"material\":");let ma:i64=gld_int(cx,p,"material",-1);if ma<0||gpd_material(w,cx,ma)!=0{rc=-2;break};gpd_s(w,"}");i=i+1 147 } 148 gpd_s(w,"]}");sys_munmap_direct(v as *u8,GLD_VIEW_WORDS*8);if w[3]!=0{return -3};return rc 149} 150 151func gpd_i(w:*i64,n:i64)->i64{return glw_i(w,n)} 152// Caller selects exact native sample times; no baked matrix interpolation. 153func gltf_render_poses(cx:*i64,animation:i64,skin:i64,position:i64,jointattr:i64,weightattr:i64,times:*i64,samples:i64,w:*i64)->i64{ 154 if samples<=0||(times as i64)<=0{return -1};let st:*i64=cx[5] as *i64;let count:i64=st[5];let sk:i64=gld_nth(cx,0,"skins",skin);if sk<0{return -1};let joints:i64=gli_array(cx,sk,"joints",0) 155 if count<=0||joints<=0||count>GLD_I64_MAX/368||joints>GLD_I64_MAX/128{return -1} 156 let need:i64=count*368;if joints*128>GLD_I64_MAX-need-GLD_VIEW_WORDS*24-72{return -1};let bytes:i64=need+joints*128+GLD_VIEW_WORDS*24+72 157 let mem:*u8=sys_mmap_try(bytes);if (mem as i64)==0{return -7};let parents:*i64=mem as *i64;let local:*i64=(mem as i64+count*8) as *i64;let world:*i64=(local as i64+count*128) as *i64;let done:*i64=(world as i64+count*128) as *i64;let trs:*i64=(done as i64+count*8) as *i64;let flags:*i64=(trs as i64+count*80) as *i64;let masks:*i64=(flags as i64+count*8) as *i64;let palette:*i64=(masks as i64+count*8) as *i64;let pv:*i64=(palette as i64+joints*128) as *i64;let jv:*i64=(pv as i64+GLD_VIEW_WORDS*8) as *i64;let wv:*i64=(jv as i64+GLD_VIEW_WORDS*8) as *i64;let vertex:*i64=(wv as i64+GLD_VIEW_WORDS*8) as *i64;let bounds:*i64=(vertex as i64+24) as *i64 158 var rc:i64=0 159 if gltf_accessor_view(cx,position,st[2],pv)!=0||gltf_accessor_view(cx,jointattr,st[2],jv)!=0||gltf_accessor_view(cx,weightattr,st[2],wv)!=0||gld_parents(cx,count,parents)!=0{rc=-2} 160 gpd_s(w,"{");if gpd_source_identity(w,cx)!=0{sys_munmap_direct(mem,bytes);return -7};gpd_s(w,"\"schema\":\"nishi-native-discrete-lbs/1\",\"q\":");gpd_n(w,FBB_Q);gpd_s(w,",\"timeQ\":");gpd_n(w,VM_Q30);gpd_s(w,",\"animation\":");gpd_n(w,animation);gpd_s(w,",\"skin\":");gpd_n(w,skin);gpd_s(w,",\"samples\":[") 161 var sample:i64=0 162 while sample<samples&&rc==0{ 163 var i:i64=0;while i<count{flags[i]=gld_node_trs(cx,i,(trs as i64+i*80) as *i64,(local as i64+i*128) as *i64);if flags[i]<0{rc=-2;break};i=i+1};if rc!=0{break} 164 let channels:i64=gld_animate(cx,animation,times[sample],trs,count,masks);if channels<0{rc=-3;break} 165 i=0;while i<count{if flags[i]==0{if gld_trs_matrix((trs as i64+i*80) as *i64,(local as i64+i*128) as *i64)!=0{rc=-4;break}};i=i+1};if rc!=0{break} 166 if gld_world_matrices(count,parents,local,world,done)!=0||gld_skin_palette(cx,skin,world,count,palette,joints)!=joints{rc=-5;break} 167 if sample>0{gpd_s(w,",")};gpd_s(w,"{\"time\":");gpd_i(w,times[sample]);gpd_s(w,",\"channels\":");gpd_n(w,channels);gpd_s(w,",\"palette\":[");i=0 168 while i<joints*16{if i>0{gpd_s(w,",")};gpd_i(w,palette[i]);i=i+1};gpd_s(w,"],\"jointWorld\":[");i=0 169 while i<joints{let at:i64=gld_nth(cx,sk,"joints",i);let tok:*NxJsonTok=cx[2] as *NxJsonTok;glp_reset(tok,cx[0] as *u8,cx[1],at);let sourceNode:i64=gld_uint(tok);if sourceNode<0||sourceNode>=count{rc=-5;break};if i>0{gpd_s(w,",")};gpd_s(w,"[");var axis:i64=0;while axis<3{if axis>0{gpd_s(w,",")};gpd_i(w,world[sourceNode*16+12+axis]);axis=axis+1};gpd_s(w,"]");i=i+1};if rc!=0{break};gpd_s(w,"],\"bounds\":[") 170 i=0;while i<pv[1]{if gld_skin_vertex(cx,pv,jv,wv,i,palette,joints,vertex)!=0{rc=-6;break};var k:i64=0;while k<3{if i==0||vertex[k]<bounds[k]{bounds[k]=vertex[k]};if i==0||vertex[k]>bounds[k+3]{bounds[k+3]=vertex[k]};k=k+1};i=i+1};if rc!=0{break} 171 i=0;while i<6{if i>0{gpd_s(w,",")};gpd_i(w,bounds[i]);i=i+1};gpd_s(w,"]}");sample=sample+1 172 } 173 gpd_s(w,"]}");sys_munmap_direct(mem,bytes);if w[3]!=0{return -3};return rc 174} 175 176 177func gpd_static_material_admit(cx:*i64,index:i64)->i64{ 178 if index<0{return 0};let mat:i64=gld_nth(cx,0,"materials",index);if mat<0{return -2} 179 if gli_member(cx,mat,"normalTexture")!=0||gli_member(cx,mat,"occlusionTexture")!=0||gli_member(cx,mat,"emissiveTexture")!=0{return -2} 180 let have:i64=gli_member(cx,mat,"pbrMetallicRoughness");if have<0{return -2} 181 if have==1{let sp:*i64=cx[4] as *i64;let pbr:i64=sp[0] 182 if gli_member(cx,pbr,"baseColorTexture")!=0||gli_member(cx,pbr,"metallicRoughnessTexture")!=0{return -2}} 183 return 0 184} 185// Additive static variant of the existing authored descriptor. No skin, poses or weights are invented. 186// This first boundary admits a single unskinned node with positive uniform axis scale only. 187// cx must already pass gltf_view_open. Refusal may leave partial writer bytes; callers must not publish them. 188// Scalar materials only: this variant emits no UV view and never admits material texture inputs. 189// Bounded static multipart: every primitive shares the exact position/normal accessors. 190func gpd_static_primitive(cx:*i64,p:i64,positionExpected:i64,normalExpected:i64,arena:*i64)->i64{ 191 if p<0||gld_int(cx,p,"mode",4)!=4||gli_member(cx,p,"targets")!=0||gli_member(cx,p,"extensions")!=0{return -2} 192 let sp:*i64=cx[4] as *i64;if gli_member(cx,p,"attributes")!=1{return -2};let a:i64=sp[0] 193 if gli_member(cx,a,"JOINTS_0")!=0||gli_member(cx,a,"WEIGHTS_0")!=0||gli_member(cx,a,"COLOR_0")!=0{return -2} 194 let position:i64=gld_int(cx,a,"POSITION",-1);let normal:i64=gld_int(cx,a,"NORMAL",-1);let index:i64=gld_int(cx,p,"indices",-1) 195 if position<0||normal<0||index<0{return -2} 196 if positionExpected>=0&&(position!=positionExpected||normal!=normalExpected){return -2} 197 let pv:*i64=arena;let nv:*i64=(arena as i64+GLD_VIEW_WORDS*8) as *i64;let iv:*i64=(arena as i64+GLD_VIEW_WORDS*16) as *i64;let st:*i64=cx[5] as *i64 198 if gltf_accessor_view(cx,position,st[2],pv)!=0||gltf_accessor_view(cx,normal,st[2],nv)!=0||gltf_accessor_view(cx,index,st[2],iv)!=0{return -2} 199 if pv[3]!=5126||nv[3]!=5126||pv[5]!=3||nv[5]!=3||pv[1]!=nv[1]||pv[1]<=0||pv[6]!=0||nv[6]!=0{return -2} 200 if iv[5]!=1||iv[6]!=0||iv[1]%3!=0||(iv[3]!=5121&&iv[3]!=5123&&iv[3]!=5125){return -2} 201 var ix:i64=0;while ix<iv[1]{let off:i64=iv[0]+ix*iv[2];var idx:i64=0 202 if iv[3]==5121{idx=gll_ru8(cx[8] as *u8,off)}else{if iv[3]==5123{idx=gls_ru16(cx[8] as *u8,off)}else{idx=gll_ru32(cx[8] as *u8,off)}} 203 if idx<0||idx>=pv[1]{return -2};ix=ix+1} 204 if gpd_static_material_admit(cx,gld_int(cx,p,"material",-1))!=0{return -2} 205 return 0 206} 207func gpd_static_into(cx:*i64,node:i64,w:*i64,arena:*i64)->i64{ 208 let nodes:i64=gli_array(cx,0,"nodes",0) 209 if nodes!=1||node!=0{return -2} 210 let at:i64=gld_nth(cx,0,"nodes",node) 211 if at<0||gli_member(cx,at,"skin")!=0||gli_member(cx,at,"children")!=0||gli_member(cx,at,"extensions")!=0{return -2} 212 let mesh:i64=gld_int(cx,at,"mesh",-1);let m:i64=gld_nth(cx,0,"meshes",mesh) 213 if m<0||gli_member(cx,m,"weights")!=0||gli_member(cx,m,"extensions")!=0{return -2} 214 let parts:i64=gli_array(cx,m,"primitives",0);if parts<=0{return -2} 215 let p:i64=gld_nth(cx,m,"primitives",0) 216 if gpd_static_primitive(cx,p,-1,-1,arena)!=0{return -2} 217 let sp:*i64=cx[4] as *i64;if gli_member(cx,p,"attributes")!=1{return -2};let a:i64=sp[0] 218 let position:i64=gld_int(cx,a,"POSITION",-1);let normal:i64=gld_int(cx,a,"NORMAL",-1) 219 let pv:*i64=arena;let nv:*i64=(arena as i64+GLD_VIEW_WORDS*8) as *i64;let iv:*i64=(arena as i64+GLD_VIEW_WORDS*16) as *i64 220 var part:i64=1;while part<parts{if gpd_static_primitive(cx,gld_nth(cx,m,"primitives",part),position,normal,arena)!=0{return -2};part=part+1} 221 let trs:*i64=(arena as i64+GLD_VIEW_WORDS*24) as *i64;let matrix:*i64=(trs as i64+10*8) as *i64 222 if gld_node_trs(cx,node,trs,matrix)<0{return -2} 223 if matrix[0]<=0||matrix[0]!=matrix[5]||matrix[0]!=matrix[10]||matrix[15]!=FBB_Q{return -2} 224 var k:i64=0;while k<12{if k!=0&&k!=5&&k!=10{if matrix[k]!=0{return -2}};k=k+1} 225 let bounds:*i64=(matrix as i64+16*8) as *i64;let v:*i64=(bounds as i64+6*8) as *i64;let out:*i64=(v as i64+3*8) as *i64 226 var i:i64=0;while i<pv[1]{k=0;while k<3{if gld_view_float(cx,pv,i,k,FBB_VERTEX_Q,(v as i64+k*8) as *i64)!=0{return -2};k=k+1} 227 k=0;while k<3{if gld_view_float(cx,nv,i,k,FBB_Q,out)!=0{return -2};k=k+1} 228 if fbb_point(matrix,v,out)!=0{return -2} 229 k=0;while k<3{if i==0||out[k]<bounds[k]{bounds[k]=out[k]};if i==0||out[k]>bounds[k+3]{bounds[k+3]=out[k]};k=k+1};i=i+1} 230 gpd_s(w,"{");if gpd_source_identity(w,cx)!=0{return -7} 231 gpd_s(w,"\"schema\":\"nishi-authored-render/1\",\"geometryMode\":\"static\",\"scope\":\"native-static-source-geometry\",\"basis\":\"gltf-Y-up\",\"physicalScale\":null,\"skin\":null,\"joints\":0,\"node\":0,\"q\":1048576,\"transformContract\":\"positive-uniform-axis-scale\",\"sourceBytes\":");gpd_n(w,cx[7]);gpd_s(w,",\"binOffset\":");gpd_n(w,cx[8]-cx[6]);gpd_s(w,",\"mesh\":");gpd_n(w,mesh) 232 gpd_s(w,",\"nodeMatrixQ20\":[");i=0;while i<16{if i>0{gpd_s(w,",")};gpd_i(w,matrix[i]);i=i+1};gpd_s(w,"],\"normalMatrixQ20\":[") 233 i=0;while i<9{if i>0{gpd_s(w,",")};var n:i64=0;if i==0||i==4||i==8{n=FBB_Q*FBB_Q/matrix[0]};gpd_i(w,n);i=i+1};gpd_s(w,"],\"boundsQ20\":[") 234 i=0;while i<6{if bounds[i]>GLD_I64_MAX/(FBB_Q/FBB_VERTEX_Q)||bounds[i]<0-GLD_I64_MAX/(FBB_Q/FBB_VERTEX_Q){return -2};if i>0{gpd_s(w,",")};gpd_i(w,bounds[i]*(FBB_Q/FBB_VERTEX_Q));i=i+1} 235 gpd_s(w,"],\"boundsMethod\":\"native-all-vertices-FX1024-expanded-to-Q20\",\"parts\":[");part=0;while part<parts{if part>0{gpd_s(w,",")};let primitive:i64=gld_nth(cx,m,"primitives",part);let index:i64=gld_int(cx,primitive,"indices",-1);gpd_s(w,"{\"attributes\":{\"POSITION\":");if gpd_view(w,cx,position,pv)!=0{return -2};gpd_s(w,",\"NORMAL\":");if gpd_view(w,cx,normal,nv)!=0{return -2} 236 gpd_s(w,"},\"vertexColorInput\":null,\"litGeometryEligible\":true,\"litGeometryReason\":\"static-source-normals-no-maps\",\"indices\":");if gpd_view(w,cx,index,iv)!=0{return -2};gpd_s(w,",\"material\":") 237 let ma:i64=gld_int(cx,primitive,"material",-1) 238 if gpd_static_material_admit(cx,ma)!=0{return -2} 239 if ma>=0{if gpd_material(w,cx,ma)!=0{return -2}}else{gpd_s(w,"{\"index\":null,\"source\":\"gltf-default-material\",\"alpha\":0,\"cutoff\":0.5,\"doubleSided\":false,\"factor\":[1,1,1,1],\"metallicFactor\":1,\"roughnessFactor\":1,\"emissiveFactor\":[0,0,0],\"baseColor\":null,\"mapInputs\":{\"normalTexture\":null,\"occlusionTexture\":null,\"emissiveTexture\":null,\"metallicRoughnessTexture\":null},\"litEligible\":true,\"litUnsupportedInputs\":0,\"litReason\":\"gltf-default-material\"}")} 240 gpd_s(w,"}");part=part+1};gpd_s(w,"]}");if w[3]!=0{return -3};return 0 241} 242 243func gltf_render_static_descriptor(cx:*i64,node:i64,w:*i64)->i64{ 244 if (cx as i64)<=0||(w as i64)<=0{return -1} 245 let bytes:i64=(GLD_VIEW_WORDS*3+10+16+6+3+3)*8 246 let arena:*i64=sys_mmap_try(bytes) as *i64;if (arena as i64)==0{return -7} 247 let rc:i64=gpd_static_into(cx,node,w,arena);sys_munmap_direct(arena as *u8,bytes);return rc 248}