nx_morf_draw_core.nx source
↩ module page · 115 lines · 5813 B
1// ORIGINAL. One caller-owned pre-skinning draw view; no asset parser or mood policy.
2// Inputs are admitted immutable arrays. Outputs are DISPOSABLE scratch, valid only
3// on nonnegative return; publish/upload their generation only after success.
4import "nx_morf_eval_core.nx"
5import "nx_vnormals_lib.nx"
6
7const MRD_E_SHAPE: i64 = 0-61
8const MRD_E_ALIAS: i64 = 0-62
9const MRD_E_TRIANGLE: i64 = 0-63
10const MRD_E_NORMAL_RANGE: i64 = 0-64
11
12struct MrdAsset {
13 morph: *MvcView
14 triangles: *i64
15 triangle_words: i64
16 bind_normals: *i64
17 normal_words: i64
18}
19
20// Nonnegative representable absolute difference, or named refusal. No abs(MIN).
21func mrd_abs_difference(a: i64, b: i64) -> i64 {
22 var high: i64 = a; var low: i64 = b
23 if a < b { high = b; low = a }
24 if low < 0 { if high > MVC_I64_MAX+low { return MRD_E_NORMAL_RANGE } }
25 return high-low
26}
27
28// Conservative proof for the unchanged shared normal owner:
29// each edge component <= E, face cross component <= 2 E^2,
30// accumulated component <= 2 E^2 D, where D counts incident triangle entries.
31// This bounds every intermediate, length-square and VN_SCALE multiplication.
32// A refusal means this representation is unproven; it is never a quality verdict.
33func mrd_normal_envelope(positions: *i64, tri: *i64, nt: i64, degree: i64) -> i64 {
34 var edge: i64 = 0; var t: i64 = 0
35 while t < nt {
36 let first: i64 = tri[t*VN_TRI_I]
37 var other: i64 = 1
38 while other < VN_TRI_I {
39 let vertex: i64 = tri[t*VN_TRI_I+other]; var axis: i64 = 0
40 while axis < VN_V3 {
41 let d: i64 = mrd_abs_difference(positions[vertex*VN_V3+axis],positions[first*VN_V3+axis])
42 if d < 0 { return MRD_E_NORMAL_RANGE }
43 if d > edge { edge = d }; axis = axis+1
44 }
45 other = other+1
46 }
47 t = t+1
48 }
49 if edge == 0 { return 0 }; if degree == 0 { return 0 }
50 if mvc_product_ok(edge,edge) == 0 { return MRD_E_NORMAL_RANGE }
51 let square: i64 = edge*edge
52 if mvc_product_ok(square,2) == 0 { return MRD_E_NORMAL_RANGE }
53 let cross: i64 = square*2
54 if mvc_product_ok(cross,degree) == 0 { return MRD_E_NORMAL_RANGE }
55 let component: i64 = cross*degree
56 if component > (MVC_I64_MAX/VN_V3)/component { return MRD_E_NORMAL_RANGE }
57 if mvc_product_ok(component,VN_SCALE) == 0 { return MRD_E_NORMAL_RANGE }
58 return 0
59}
60
61func mrd_prepare(a: *MrdAsset, weights: *i64, weight_words: i64, positions: *i64, position_words: i64, normals: *i64, normal_words: i64) -> i64 {
62 if (a as i64) <= 0 { return MRD_E_SHAPE }
63 let v: *MvcView = a.morph
64 if (v as i64) <= 0 { return MRD_E_SHAPE }
65 if normal_words != position_words { return MRD_E_SHAPE }
66 if normal_words != a.normal_words { return MRD_E_SHAPE }
67 if position_words != v.bind_words { return MRD_E_SHAPE }
68 if a.triangle_words < 0 { return MRD_E_SHAPE }
69 if a.triangle_words % VN_TRI_I != 0 { return MRD_E_SHAPE }
70 if mvc_span(a.triangles,a.triangle_words) == 0 { return MRD_E_SHAPE }
71 if mvc_span(a.bind_normals,a.normal_words) == 0 { return MRD_E_SHAPE }
72 if mvc_span(normals,normal_words) == 0 { return MRD_E_SHAPE }
73 if mvc_span(positions,position_words) == 0 { return MRD_E_SHAPE }
74 if mvc_span(v.bind,v.bind_words) == 0 { return MRD_E_SHAPE }
75 if mvc_span(v.face,v.face_words) == 0 { return MRD_E_SHAPE }
76 if mvc_span(v.delta,v.delta_words) == 0 { return MRD_E_SHAPE }
77 if mvc_span(weights,weight_words) == 0 { return MRD_E_SHAPE }
78 if mvc_overlap(normals,normal_words,positions,position_words) == 1 { return MRD_E_ALIAS }
79 if mvc_overlap(normals,normal_words,v.bind,v.bind_words) == 1 { return MRD_E_ALIAS }
80 if mvc_overlap(normals,normal_words,v.face,v.face_words) == 1 { return MRD_E_ALIAS }
81 if mvc_overlap(normals,normal_words,v.delta,v.delta_words) == 1 { return MRD_E_ALIAS }
82 if mvc_overlap(normals,normal_words,weights,weight_words) == 1 { return MRD_E_ALIAS }
83 if mvc_overlap(normals,normal_words,a.triangles,a.triangle_words) == 1 { return MRD_E_ALIAS }
84 if mvc_overlap(normals,normal_words,a.bind_normals,a.normal_words) == 1 { return MRD_E_ALIAS }
85 if mvc_overlap(positions,position_words,a.triangles,a.triangle_words) == 1 { return MRD_E_ALIAS }
86 if mvc_overlap(positions,position_words,a.bind_normals,a.normal_words) == 1 { return MRD_E_ALIAS }
87 if mvc_overlap(normals,normal_words,v as *i64,__size_of(MvcView)/MVC_WORD) == 1 { return MRD_E_ALIAS }
88 if mvc_overlap(normals,normal_words,a as *i64,__size_of(MrdAsset)/MVC_WORD) == 1 { return MRD_E_ALIAS }
89 if mvc_overlap(positions,position_words,a as *i64,__size_of(MrdAsset)/MVC_WORD) == 1 { return MRD_E_ALIAS }
90 // Core admission and arithmetic must succeed before topology scratch is used.
91 let moved: i64 = mvc_apply(v,weights,weight_words,positions,position_words)
92 if moved < 0 { return moved }
93 if moved == 0 {
94 var copy: i64 = 0
95 while copy < normal_words { normals[copy] = a.bind_normals[copy]; copy = copy+1 }
96 return 0
97 }
98 // Reuse normal output as degree scratch; no hidden per-entity allocation.
99 var i: i64 = 0
100 while i < v.vertices { normals[i] = 0; i = i+1 }
101 var degree: i64 = 0; i = 0
102 while i < a.triangle_words {
103 let vertex: i64 = a.triangles[i]
104 if vertex < 0 { return MRD_E_TRIANGLE }
105 if vertex >= v.vertices { return MRD_E_TRIANGLE }
106 normals[vertex] = normals[vertex]+1
107 if normals[vertex] > degree { degree = normals[vertex] }
108 i = i+1
109 }
110 let nt: i64 = a.triangle_words/VN_TRI_I
111 if mrd_normal_envelope(positions,a.triangles,nt,degree) < 0 { return MRD_E_NORMAL_RANGE }
112 let rc: i64 = vn_smooth_area(positions,a.triangles,v.vertices,nt,normals)
113 if rc < 0 { return rc }
114 return moved
115}