code wiki / (root) / nx_imported_body_registry_t40.nx

nx_imported_body_registry_t40.nx source

↩ module page · 139 lines · 7808 B

1// Private imported-body component registry. Composes existing identity and contact owners. 2// No render handles, model names, AI policy or implicit anatomical dimensions. 3// Persistence integration remains versioned work; this runtime arena is not the legacy v6 save body. 4import "_hdl_build/nx_entity_store.nx" 5import "nx_beach_contact_sweep.nx" 6const IBR_MAGIC:i64=20260910040 7const IBR_VERSION:i64=1 8const IBR_HEADER:i64=4 9const IBR_COMPONENTS:i64=18 10const IBR_DOMAIN:i64=1125899906842624 11const IBR_BAD:i64=0-101 12const IBR_CAPACITY:i64=0-102 13const IBR_CONFLICT:i64=0-103 14const IBR_FUTURE:i64=0-104 15const IBR_SHAPE_AABB:i64=1 16const IBR_ID_HI:i64=0 17const IBR_ID_LO:i64=1 18const IBR_REV:i64=2 19const IBR_SHAPE_REV:i64=3 20const IBR_POSE_REV:i64=4 21const IBR_WORLD_X:i64=5 22const IBR_WORLD_Y:i64=6 23const IBR_WORLD_Z:i64=7 24const IBR_MIN_X:i64=8 25const IBR_MIN_Y:i64=9 26const IBR_MIN_Z:i64=10 27const IBR_MAX_X:i64=11 28const IBR_MAX_Y:i64=12 29const IBR_MAX_Z:i64=13 30const IBR_LAYER:i64=14 31const IBR_MASK:i64=15 32const IBR_ENABLED:i64=16 33const IBR_KIND:i64=17 34struct ImportedBodyHit { 35 delta_q8:i64, 36 initial_overlap:i64, 37 hit_handle:i64, 38 tested:i64, 39 hit_shape_revision:i64, 40 hit_pose_revision:i64, 41} 42func ibr_entities(arena:*i64)->*i64{return ((arena as i64)+IBR_HEADER*8) as *i64} 43func ibr_words(cap:i64)->i64{if cap<1||cap>IBR_DOMAIN/(IBR_COMPONENTS+5){return IBR_BAD};return IBR_HEADER+en_words(cap,IBR_COMPONENTS)} 44func ibr_ready(arena:*i64,words:i64)->i64{ 45 if words<IBR_HEADER{return IBR_BAD} 46 if arena[0]!=IBR_MAGIC{return IBR_BAD} 47 if arena[1]>IBR_VERSION{return IBR_FUTURE} 48 if arena[1]!=IBR_VERSION||arena[2]!=words||arena[3]<1{return IBR_BAD} 49 if ibr_words(arena[3])!=words{return IBR_BAD} 50 let entities:*i64=ibr_entities(arena) 51 if en_cap(entities)!=arena[3]||en_ncomp(entities)!=IBR_COMPONENTS{return IBR_BAD} 52 return 0 53} 54func ibr_init(arena:*i64,words:i64,cap:i64)->i64{ 55 let required:i64=ibr_words(cap);if required<0||words!=required{return IBR_CAPACITY} 56 if arena[0]==IBR_MAGIC{if arena[3]!=cap{return IBR_CONFLICT};return ibr_ready(arena,words)} 57 // An explicit zero arena is required; accidental init never wipes a live or foreign arena. 58 var i:i64=0;while i<words{if arena[i]!=0{return IBR_CONFLICT};i=i+1} 59 arena[0]=IBR_MAGIC;arena[1]=IBR_VERSION;arena[2]=words;arena[3]=cap 60 return en_init(ibr_entities(arena),cap,IBR_COMPONENTS) 61} 62func ibr_domain(v:i64)->i64{if v<=0-IBR_DOMAIN||v>=IBR_DOMAIN{return 0};return 1} 63func ibr_record_valid(record:*i64,words:i64)->i64{ 64 if words!=IBR_COMPONENTS{return IBR_BAD} 65 if record[IBR_ID_HI]==0&&record[IBR_ID_LO]==0{return IBR_BAD} 66 if record[IBR_REV]<1||record[IBR_SHAPE_REV]<1||record[IBR_POSE_REV]<0{return IBR_BAD} 67 var i:i64=IBR_WORLD_X;while i<=IBR_MAX_Z{if ibr_domain(record[i])==0{return IBR_BAD};i=i+1} 68 if record[IBR_MIN_X]>=record[IBR_MAX_X]||record[IBR_MIN_Y]>=record[IBR_MAX_Y]||record[IBR_MIN_Z]>=record[IBR_MAX_Z]{return IBR_BAD} 69 if record[IBR_LAYER]<=0||record[IBR_MASK]<0||record[IBR_ENABLED]<0||record[IBR_ENABLED]>1{return IBR_BAD} 70 if record[IBR_KIND]!=IBR_SHAPE_AABB{return IBR_BAD} 71 return 0 72} 73func ibr_find(arena:*i64,hi:i64,lo:i64)->i64{ 74 let entities:*i64=ibr_entities(arena);var i:i64=0 75 while i<en_count(entities){let handle:i64=en_nth(entities,i) 76 if en_get(entities,handle,IBR_ID_HI)==hi&&en_get(entities,handle,IBR_ID_LO)==lo{return handle};i=i+1} 77 return 0 78} 79// Boundary validation precedes mutation. Same revision is exact-idempotent or a conflict. 80func ibr_upsert(arena:*i64,words:i64,record:*i64,record_words:i64)->i64{ 81 let ready:i64=ibr_ready(arena,words);if ready!=0{return ready} 82 let valid:i64=ibr_record_valid(record,record_words);if valid!=0{return valid} 83 let entities:*i64=ibr_entities(arena) 84 var handle:i64=ibr_find(arena,record[IBR_ID_HI],record[IBR_ID_LO]) 85 if handle!=0{ 86 let revision:i64=en_get(entities,handle,IBR_REV) 87 if record[IBR_REV]<revision{return IBR_CONFLICT} 88 if record[IBR_SHAPE_REV]<en_get(entities,handle,IBR_SHAPE_REV)||record[IBR_POSE_REV]<en_get(entities,handle,IBR_POSE_REV){return IBR_CONFLICT} 89 var shape_changed:i64=0;var component:i64=IBR_MIN_X 90 while component<=IBR_MAX_Z{if record[component]!=en_get(entities,handle,component){shape_changed=1};component=component+1} 91 if record[IBR_KIND]!=en_get(entities,handle,IBR_KIND){shape_changed=1} 92 if shape_changed==1&&record[IBR_SHAPE_REV]==en_get(entities,handle,IBR_SHAPE_REV){return IBR_CONFLICT} 93 var pose_changed:i64=0;component=IBR_WORLD_X 94 while component<=IBR_WORLD_Z{if record[component]!=en_get(entities,handle,component){pose_changed=1};component=component+1} 95 if pose_changed==1&&record[IBR_POSE_REV]==en_get(entities,handle,IBR_POSE_REV){return IBR_CONFLICT} 96 if record[IBR_REV]==revision{var c:i64=0;while c<IBR_COMPONENTS{if en_get(entities,handle,c)!=record[c]{return IBR_CONFLICT};c=c+1};return handle} 97 }else{handle=en_spawn(entities);if handle==0{return IBR_CAPACITY}} 98 var c:i64=0;while c<IBR_COMPONENTS{en_set(entities,handle,c,record[c]);c=c+1} 99 return handle 100} 101func ibr_read(arena:*i64,words:i64,handle:i64,record:*i64,record_words:i64)->i64{ 102 let ready:i64=ibr_ready(arena,words);if ready!=0{return ready} 103 if record_words!=IBR_COMPONENTS{return IBR_CAPACITY} 104 let entities:*i64=ibr_entities(arena);if en_valid(entities,handle)==0{return IBR_BAD} 105 var c:i64=0;while c<IBR_COMPONENTS{record[c]=en_get(entities,handle,c);c=c+1};return 0 106} 107func ibr_local_box(entities:*i64,handle:i64,origin_x_q8:i64,origin_z_q8:i64,out:*BeachContactBox)->i64{ 108 let x:i64=en_get(entities,handle,IBR_WORLD_X)-origin_x_q8 109 let y:i64=en_get(entities,handle,IBR_WORLD_Y) 110 let z:i64=en_get(entities,handle,IBR_WORLD_Z)-origin_z_q8 111 out.min_x=x+en_get(entities,handle,IBR_MIN_X);out.max_x=x+en_get(entities,handle,IBR_MAX_X) 112 out.min_y=y+en_get(entities,handle,IBR_MIN_Y);out.max_y=y+en_get(entities,handle,IBR_MAX_Y) 113 out.min_z=z+en_get(entities,handle,IBR_MIN_Z);out.max_z=z+en_get(entities,handle,IBR_MAX_Z) 114 return 0 115} 116// Caller provides local world-Q8 body and scratch; no allocation, state mutation or implicit depenetration. 117func ibr_sweep(arena:*i64,words:i64,body:*BeachContactBox,origin_x_q8:i64,origin_z_q8:i64,axis:i64,delta_q8:i64,moving_layer:i64,moving_mask:i64,skip_handle:i64,scratch:*BeachContactBox,out:*ImportedBodyHit)->i64{ 118 let ready:i64=ibr_ready(arena,words);if ready!=0{return ready} 119 if axis<0||axis>2||ibr_domain(delta_q8)==0||ibr_domain(origin_x_q8)==0||ibr_domain(origin_z_q8)==0||moving_layer<=0||moving_mask<0{return IBR_BAD} 120 if body.min_x>body.max_x||body.min_y>body.max_y||body.min_z>body.max_z{return IBR_BAD} 121 var axis_check:i64=0;while axis_check<3{if ibr_domain(beach_contact_min(body,axis_check))==0||ibr_domain(beach_contact_max(body,axis_check))==0{return IBR_BAD};axis_check=axis_check+1} 122 if (body as i64)==(scratch as i64)||(body as i64)==(out as i64)||(scratch as i64)==(out as i64){return IBR_BAD} 123 let entities:*i64=ibr_entities(arena) 124 if skip_handle!=0&&en_valid(entities,skip_handle)==0{return IBR_BAD} 125 out.delta_q8=delta_q8;out.initial_overlap=0;out.hit_handle=0;out.tested=0;out.hit_shape_revision=0;out.hit_pose_revision=0 126 var i:i64=0 127 while i<en_count(entities){let handle:i64=en_nth(entities,i) 128 if handle!=skip_handle&&en_get(entities,handle,IBR_ENABLED)==1&&(en_get(entities,handle,IBR_LAYER)&moving_mask)!=0&&(en_get(entities,handle,IBR_MASK)&moving_layer)!=0{ 129 ibr_local_box(entities,handle,origin_x_q8,origin_z_q8,scratch);out.tested=out.tested+1 130 let overlap:i64=beach_contact_overlap(body,scratch) 131 if overlap==1{out.initial_overlap=1} 132 let clipped:i64=beach_contact_clip(body,scratch,axis,out.delta_q8) 133 if clipped!=out.delta_q8||overlap==1{ 134 if overlap==1||out.initial_overlap==0{out.hit_handle=handle;out.hit_shape_revision=en_get(entities,handle,IBR_SHAPE_REV);out.hit_pose_revision=en_get(entities,handle,IBR_POSE_REV)} 135 out.delta_q8=clipped 136 } 137 };i=i+1} 138 return 0 139}