@aura3d/physics 1.3.3 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,277 +0,0 @@
1
- export interface FractureFixtureOptions {
2
- readonly seed?: number;
3
- readonly fragmentCount?: number;
4
- readonly impactStrength?: number;
5
- }
6
-
7
- export interface FractureFragmentSample {
8
- readonly id: number;
9
- readonly site: readonly [number, number, number];
10
- readonly centerOfMass: readonly [number, number, number];
11
- readonly mass: number;
12
- readonly volume: number;
13
- readonly velocity: readonly [number, number, number];
14
- readonly angularVelocity: readonly [number, number, number];
15
- readonly neighborCount: number;
16
- }
17
-
18
- export interface FractureFixture {
19
- readonly id: "external-parity-old-branch-fracture-fixture";
20
- readonly source: "origin-master-voronoi-hierarchical-fracture-adapted";
21
- readonly sourceFiles: readonly [
22
- "origin/master:src/simulation/fracture/VoronoiFractureSystem.ts",
23
- "origin/master:src/simulation/fracture/HierarchicalFractureSystem.ts",
24
- "origin/master:src/simulation/fracture/GeometryClipper.ts",
25
- "origin/master:src/simulation/fracture/VoronoiMath.ts"
26
- ];
27
- readonly config: {
28
- readonly bounds: {
29
- readonly min: readonly [number, number, number];
30
- readonly max: readonly [number, number, number];
31
- };
32
- readonly requestedFragments: number;
33
- readonly density: number;
34
- readonly impactPoint: readonly [number, number, number];
35
- readonly impulseStrength: number;
36
- readonly interiorFaces: boolean;
37
- readonly progressiveDamage: boolean;
38
- };
39
- readonly voronoi: {
40
- readonly siteCount: number;
41
- readonly radialSiteCount: number;
42
- readonly averageSiteDistance: number;
43
- readonly maxSiteDistance: number;
44
- readonly neighborPairs: number;
45
- readonly crackGraphEdges: number;
46
- };
47
- readonly fragments: {
48
- readonly fragmentCount: number;
49
- readonly totalMass: number;
50
- readonly minMass: number;
51
- readonly maxMass: number;
52
- readonly totalVolume: number;
53
- readonly interiorFaceEstimate: number;
54
- readonly activeAfterImpact: number;
55
- readonly samples: readonly FractureFragmentSample[];
56
- };
57
- readonly hierarchy: {
58
- readonly maxDepth: number;
59
- readonly nodeCount: number;
60
- readonly rootDamage: number;
61
- readonly childDamageThreshold: number;
62
- readonly activatedChildren: number;
63
- readonly residualInactiveChildren: number;
64
- };
65
- readonly blockedClaims: readonly string[];
66
- readonly hash: string;
67
- readonly claimBoundary: string;
68
- }
69
-
70
- const sourceFiles = [
71
- "origin/master:src/simulation/fracture/VoronoiFractureSystem.ts",
72
- "origin/master:src/simulation/fracture/HierarchicalFractureSystem.ts",
73
- "origin/master:src/simulation/fracture/GeometryClipper.ts",
74
- "origin/master:src/simulation/fracture/VoronoiMath.ts"
75
- ] as const;
76
-
77
- const blockedClaims = [
78
- "runtime convex mesh clipping",
79
- "interior geometry generation parity",
80
- "hierarchical runtime remeshing",
81
- "rigid-body fragment simulation parity",
82
- "destructible asset authoring workflow",
83
- "Unity destruction workflow parity",
84
- "Unreal Chaos destruction parity"
85
- ] as const;
86
-
87
- export function sampleFractureFixture(options: FractureFixtureOptions = {}): FractureFixture {
88
- const seed = integer(options.seed ?? 0xf24c7, "seed");
89
- const fragmentCount = integerInRange(options.fragmentCount ?? 18, "fragmentCount", 4, 96);
90
- const impactStrength = finitePositive(options.impactStrength ?? 82, "impactStrength");
91
- const boundsMin = [-0.6, -0.42, -0.36] as const;
92
- const boundsMax = [0.6, 0.42, 0.36] as const;
93
- const impactPoint = [-0.18, 0.06, 0.08] as const;
94
- const density = 2350;
95
- const boundsVolume = (boundsMax[0] - boundsMin[0]) * (boundsMax[1] - boundsMin[1]) * (boundsMax[2] - boundsMin[2]);
96
- const sites = generateRadialSites(seed, fragmentCount, impactPoint, 0.68);
97
- const weights = sites.map((site, index) => 0.75 + hash01(seed, index + 41) * 0.7 + Math.max(0, 0.6 - distance(site, impactPoint)) * 0.4);
98
- const weightSum = weights.reduce((sum, weight) => sum + weight, 0);
99
- const fragments: FractureFragmentSample[] = [];
100
- let totalVolume = 0;
101
- let totalMass = 0;
102
- let minMass = Number.POSITIVE_INFINITY;
103
- let maxMass = 0;
104
- let neighborPairs = 0;
105
- let siteDistanceTotal = 0;
106
- let maxSiteDistance = 0;
107
- let activeAfterImpact = 0;
108
-
109
- for (let index = 0; index < sites.length; index += 1) {
110
- const site = sites[index]!;
111
- const siteDistance = distance(site, impactPoint);
112
- siteDistanceTotal += siteDistance;
113
- maxSiteDistance = Math.max(maxSiteDistance, siteDistance);
114
- const volume = boundsVolume * (weights[index]! / weightSum);
115
- const mass = volume * density;
116
- totalVolume += volume;
117
- totalMass += mass;
118
- minMass = Math.min(minMass, mass);
119
- maxMass = Math.max(maxMass, mass);
120
- const direction = normalized([site[0] - impactPoint[0], site[1] - impactPoint[1], site[2] - impactPoint[2]]);
121
- const attenuation = 1 / (1 + siteDistance * 2.2);
122
- const velocityMagnitude = impactStrength * attenuation / Math.max(1, mass);
123
- const neighborCount = sites.filter((other, otherIndex) => otherIndex !== index && distance(other, site) < 0.58).length;
124
- neighborPairs += neighborCount;
125
- if (impactStrength * attenuation > 24) activeAfterImpact += 1;
126
- if (index < 6) {
127
- fragments.push({
128
- id: index,
129
- site: roundVec3(site),
130
- centerOfMass: roundVec3([
131
- site[0] * 0.86 + impactPoint[0] * 0.14,
132
- site[1] * 0.86 + impactPoint[1] * 0.14,
133
- site[2] * 0.86 + impactPoint[2] * 0.14
134
- ]),
135
- mass: round4(mass),
136
- volume: round4(volume),
137
- velocity: roundVec3([direction[0] * velocityMagnitude, direction[1] * velocityMagnitude, direction[2] * velocityMagnitude]),
138
- angularVelocity: roundVec3([
139
- (site[1] - impactPoint[1]) * velocityMagnitude * 1.9,
140
- (site[2] - impactPoint[2]) * velocityMagnitude * 1.6,
141
- (site[0] - impactPoint[0]) * velocityMagnitude * 1.7
142
- ]),
143
- neighborCount
144
- });
145
- }
146
- }
147
- neighborPairs = Math.floor(neighborPairs / 2);
148
- const maxDepth = 3;
149
- const activatedChildren = Math.max(1, Math.min(fragmentCount, Math.round(activeAfterImpact * 0.72)));
150
- const nodeCount = 1 + fragmentCount + activatedChildren * 2;
151
-
152
- return {
153
- id: "external-parity-old-branch-fracture-fixture",
154
- source: "origin-master-voronoi-hierarchical-fracture-adapted",
155
- sourceFiles,
156
- config: {
157
- bounds: { min: boundsMin, max: boundsMax },
158
- requestedFragments: fragmentCount,
159
- density,
160
- impactPoint,
161
- impulseStrength: impactStrength,
162
- interiorFaces: true,
163
- progressiveDamage: true
164
- },
165
- voronoi: {
166
- siteCount: sites.length,
167
- radialSiteCount: sites.length,
168
- averageSiteDistance: round4(siteDistanceTotal / sites.length),
169
- maxSiteDistance: round4(maxSiteDistance),
170
- neighborPairs,
171
- crackGraphEdges: neighborPairs + Math.max(0, fragmentCount - 1)
172
- },
173
- fragments: {
174
- fragmentCount: sites.length,
175
- totalMass: round4(totalMass),
176
- minMass: round4(minMass),
177
- maxMass: round4(maxMass),
178
- totalVolume: round4(totalVolume),
179
- interiorFaceEstimate: fragmentCount * 6 + neighborPairs,
180
- activeAfterImpact,
181
- samples: fragments
182
- },
183
- hierarchy: {
184
- maxDepth,
185
- nodeCount,
186
- rootDamage: round4(Math.min(1, impactStrength / 100)),
187
- childDamageThreshold: 0.34,
188
- activatedChildren,
189
- residualInactiveChildren: Math.max(0, fragmentCount - activatedChildren)
190
- },
191
- blockedClaims,
192
- hash: hashNumbers([
193
- seed,
194
- fragmentCount,
195
- impactStrength,
196
- totalMass,
197
- totalVolume,
198
- minMass,
199
- maxMass,
200
- neighborPairs,
201
- activeAfterImpact,
202
- nodeCount
203
- ]),
204
- claimBoundary: "Deterministic bounded fracture telemetry adapted from old Voronoi, geometry clipping, and hierarchical fracture concepts. It proves radial site generation, fragment mass/velocity estimates, crack graph metrics, and progressive damage activation; it does not claim runtime convex mesh clipping, generated interior geometry, rigid-body fragment simulation, destructible authoring workflow, Unity destruction parity, or Unreal Chaos destruction parity."
205
- };
206
- }
207
-
208
- function generateRadialSites(seed: number, count: number, center: readonly [number, number, number], radius: number): readonly (readonly [number, number, number])[] {
209
- const sites: Array<readonly [number, number, number]> = [];
210
- for (let index = 0; index < count; index += 1) {
211
- const u = hash01(seed, index * 3 + 1);
212
- const v = hash01(seed, index * 3 + 2);
213
- const w = hash01(seed, index * 3 + 3);
214
- const theta = u * Math.PI * 2;
215
- const phi = Math.acos(2 * v - 1);
216
- const localRadius = radius * Math.pow(w, 0.72);
217
- sites.push([
218
- center[0] + Math.sin(phi) * Math.cos(theta) * localRadius,
219
- center[1] + Math.cos(phi) * localRadius,
220
- center[2] + Math.sin(phi) * Math.sin(theta) * localRadius
221
- ]);
222
- }
223
- return sites;
224
- }
225
-
226
- function normalized(value: readonly [number, number, number]): readonly [number, number, number] {
227
- const length = Math.max(0.0001, Math.hypot(value[0], value[1], value[2]));
228
- return [value[0] / length, value[1] / length, value[2] / length];
229
- }
230
-
231
- function distance(left: readonly [number, number, number], right: readonly [number, number, number]): number {
232
- return Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
233
- }
234
-
235
- function roundVec3(value: readonly [number, number, number]): readonly [number, number, number] {
236
- return [round4(value[0]), round4(value[1]), round4(value[2])];
237
- }
238
-
239
- function hash01(seed: number, salt: number): number {
240
- let value = Math.imul(seed + salt * 1013904223, 1664525) + 1013904223;
241
- value = Math.imul(value ^ (value >>> 13), 1274126177);
242
- return ((value ^ (value >>> 16)) >>> 0) / 4294967296;
243
- }
244
-
245
- function hashNumbers(values: readonly number[]): string {
246
- let hash = 0x811c9dc5;
247
- for (const value of values) {
248
- const scaled = Math.round(value * 10_000);
249
- hash ^= scaled & 0xff;
250
- hash = Math.imul(hash, 0x01000193) >>> 0;
251
- hash ^= (scaled >>> 8) & 0xff;
252
- hash = Math.imul(hash, 0x01000193) >>> 0;
253
- hash ^= (scaled >>> 16) & 0xff;
254
- hash = Math.imul(hash, 0x01000193) >>> 0;
255
- }
256
- return hash.toString(16).padStart(8, "0");
257
- }
258
-
259
- function round4(value: number): number {
260
- return Number(value.toFixed(4));
261
- }
262
-
263
- function finitePositive(value: number, label: string): number {
264
- if (!Number.isFinite(value) || value <= 0) throw new RangeError(`Fracture fixture ${label} must be finite and positive.`);
265
- return value;
266
- }
267
-
268
- function integer(value: number, label: string): number {
269
- if (!Number.isInteger(value)) throw new RangeError(`Fracture fixture ${label} must be an integer.`);
270
- return value;
271
- }
272
-
273
- function integerInRange(value: number, label: string, min: number, max: number): number {
274
- const parsed = integer(value, label);
275
- if (parsed < min || parsed > max) throw new RangeError(`Fracture fixture ${label} must be in the [${min}, ${max}] range.`);
276
- return parsed;
277
- }