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}