@rezless/vm 0.4.0 → 0.5.0

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.
Files changed (131) hide show
  1. package/dist/builtins/base64.d.ts.map +1 -1
  2. package/dist/builtins/base64.js +29 -2
  3. package/dist/builtins/base64.js.map +1 -1
  4. package/dist/builtins/chat.d.ts +7 -1
  5. package/dist/builtins/chat.d.ts.map +1 -1
  6. package/dist/builtins/chat.js +26 -29
  7. package/dist/builtins/chat.js.map +1 -1
  8. package/dist/builtins/dataserver.d.ts +2 -1
  9. package/dist/builtins/dataserver.d.ts.map +1 -1
  10. package/dist/builtins/dataserver.js +5 -4
  11. package/dist/builtins/dataserver.js.map +1 -1
  12. package/dist/builtins/debug.d.ts +4 -0
  13. package/dist/builtins/debug.d.ts.map +1 -0
  14. package/dist/builtins/debug.js +7 -0
  15. package/dist/builtins/debug.js.map +1 -0
  16. package/dist/builtins/http.d.ts +8 -1
  17. package/dist/builtins/http.d.ts.map +1 -1
  18. package/dist/builtins/http.js +87 -9
  19. package/dist/builtins/http.js.map +1 -1
  20. package/dist/builtins/identity.d.ts +2 -0
  21. package/dist/builtins/identity.d.ts.map +1 -1
  22. package/dist/builtins/identity.js +9 -1
  23. package/dist/builtins/identity.js.map +1 -1
  24. package/dist/builtins/index.d.ts.map +1 -1
  25. package/dist/builtins/index.js +29 -7
  26. package/dist/builtins/index.js.map +1 -1
  27. package/dist/builtins/inventory.d.ts +13 -2
  28. package/dist/builtins/inventory.d.ts.map +1 -1
  29. package/dist/builtins/inventory.js +82 -43
  30. package/dist/builtins/inventory.js.map +1 -1
  31. package/dist/builtins/json.d.ts.map +1 -1
  32. package/dist/builtins/json.js +11 -36
  33. package/dist/builtins/json.js.map +1 -1
  34. package/dist/builtins/link-info.d.ts +1 -1
  35. package/dist/builtins/link-info.d.ts.map +1 -1
  36. package/dist/builtins/link-info.js +2 -2
  37. package/dist/builtins/link-info.js.map +1 -1
  38. package/dist/builtins/linksetdata.d.ts.map +1 -1
  39. package/dist/builtins/linksetdata.js +298 -31
  40. package/dist/builtins/linksetdata.js.map +1 -1
  41. package/dist/builtins/listen.d.ts +3 -1
  42. package/dist/builtins/listen.d.ts.map +1 -1
  43. package/dist/builtins/listen.js +9 -1
  44. package/dist/builtins/listen.js.map +1 -1
  45. package/dist/builtins/lists.d.ts +21 -0
  46. package/dist/builtins/lists.d.ts.map +1 -1
  47. package/dist/builtins/lists.js +82 -38
  48. package/dist/builtins/lists.js.map +1 -1
  49. package/dist/builtins/math.d.ts +27 -4
  50. package/dist/builtins/math.d.ts.map +1 -1
  51. package/dist/builtins/math.js +118 -41
  52. package/dist/builtins/math.js.map +1 -1
  53. package/dist/builtins/permissions.d.ts +18 -0
  54. package/dist/builtins/permissions.d.ts.map +1 -0
  55. package/dist/builtins/permissions.js +17 -0
  56. package/dist/builtins/permissions.js.map +1 -0
  57. package/dist/builtins/prim-accessors.d.ts +1 -0
  58. package/dist/builtins/prim-accessors.d.ts.map +1 -1
  59. package/dist/builtins/prim-accessors.js +44 -42
  60. package/dist/builtins/prim-accessors.js.map +1 -1
  61. package/dist/builtins/prim-params.d.ts +1 -1
  62. package/dist/builtins/prim-params.d.ts.map +1 -1
  63. package/dist/builtins/prim-params.js +19 -11
  64. package/dist/builtins/prim-params.js.map +1 -1
  65. package/dist/builtins/strings.d.ts.map +1 -1
  66. package/dist/builtins/strings.js +37 -33
  67. package/dist/builtins/strings.js.map +1 -1
  68. package/dist/builtins/time.d.ts +2 -0
  69. package/dist/builtins/time.d.ts.map +1 -1
  70. package/dist/builtins/time.js +2 -0
  71. package/dist/builtins/time.js.map +1 -1
  72. package/dist/clock.d.ts +23 -9
  73. package/dist/clock.d.ts.map +1 -1
  74. package/dist/clock.js +40 -13
  75. package/dist/clock.js.map +1 -1
  76. package/dist/dispatch.d.ts.map +1 -1
  77. package/dist/dispatch.js +10 -3
  78. package/dist/dispatch.js.map +1 -1
  79. package/dist/index.d.ts +2 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +1 -0
  82. package/dist/index.js.map +1 -1
  83. package/dist/interpreter.js +2 -2
  84. package/dist/interpreter.js.map +1 -1
  85. package/dist/inventory.d.ts +2 -0
  86. package/dist/inventory.d.ts.map +1 -1
  87. package/dist/inventory.js +4 -0
  88. package/dist/inventory.js.map +1 -1
  89. package/dist/keys.d.ts +6 -0
  90. package/dist/keys.d.ts.map +1 -0
  91. package/dist/keys.js +20 -0
  92. package/dist/keys.js.map +1 -0
  93. package/dist/linkset.d.ts +28 -3
  94. package/dist/linkset.d.ts.map +1 -1
  95. package/dist/linkset.js +66 -7
  96. package/dist/linkset.js.map +1 -1
  97. package/dist/prim-params.d.ts +5 -0
  98. package/dist/prim-params.d.ts.map +1 -1
  99. package/dist/prim-params.js +187 -107
  100. package/dist/prim-params.js.map +1 -1
  101. package/dist/prim.d.ts +6 -1
  102. package/dist/prim.d.ts.map +1 -1
  103. package/dist/prim.js +23 -3
  104. package/dist/prim.js.map +1 -1
  105. package/dist/runtime.d.ts +11 -1
  106. package/dist/runtime.d.ts.map +1 -1
  107. package/dist/script.d.ts +27 -2
  108. package/dist/script.d.ts.map +1 -1
  109. package/dist/script.js +89 -27
  110. package/dist/script.js.map +1 -1
  111. package/dist/values/coerce.d.ts +11 -0
  112. package/dist/values/coerce.d.ts.map +1 -1
  113. package/dist/values/coerce.js +65 -38
  114. package/dist/values/coerce.js.map +1 -1
  115. package/dist/values/format.d.ts +24 -16
  116. package/dist/values/format.d.ts.map +1 -1
  117. package/dist/values/format.js +42 -25
  118. package/dist/values/format.js.map +1 -1
  119. package/dist/values/ops.d.ts +2 -1
  120. package/dist/values/ops.d.ts.map +1 -1
  121. package/dist/values/ops.js +13 -5
  122. package/dist/values/ops.js.map +1 -1
  123. package/dist/values/utf8.d.ts +3 -0
  124. package/dist/values/utf8.d.ts.map +1 -0
  125. package/dist/values/utf8.js +14 -0
  126. package/dist/values/utf8.js.map +1 -0
  127. package/dist/world.d.ts +29 -0
  128. package/dist/world.d.ts.map +1 -0
  129. package/dist/world.js +29 -0
  130. package/dist/world.js.map +1 -0
  131. package/package.json +2 -2
@@ -1,5 +1,7 @@
1
- import { ALL_SIDES, CLICK_ACTION_TOUCH, PRIM_ALPHA_MODE, PRIM_ALPHA_MODE_NONE, PRIM_ALLOW_UNSIT, PRIM_BUMP_NONE, PRIM_BUMP_SHINY, PRIM_CAST_SHADOWS, PRIM_CLICK_ACTION, PRIM_COLOR, PRIM_DAMAGE, PRIM_DESC, PRIM_FLEXIBLE, PRIM_FULLBRIGHT, PRIM_GLOW, PRIM_GLTF_ALPHA_MODE_OPAQUE, PRIM_GLTF_BASE_COLOR, PRIM_GLTF_EMISSIVE, PRIM_GLTF_METALLIC_ROUGHNESS, PRIM_GLTF_NORMAL, PRIM_HEALTH, PRIM_HOLE_DEFAULT, PRIM_LINK_TARGET, PRIM_MATERIAL, PRIM_MATERIAL_STONE, PRIM_NAME, PRIM_NORMAL, PRIM_OMEGA, PRIM_PHANTOM, PRIM_PHYSICS, PRIM_PHYSICS_SHAPE_PRIM, PRIM_PHYSICS_SHAPE_TYPE, PRIM_POINT_LIGHT, PRIM_POSITION, PRIM_POS_LOCAL, PRIM_PROJECTOR, PRIM_REFLECTION_PROBE, PRIM_RENDER_MATERIAL, PRIM_ROTATION, PRIM_ROT_LOCAL, PRIM_SCRIPTED_SIT_ONLY, PRIM_SCULPT_TYPE_SPHERE, PRIM_SHINY_NONE, PRIM_SIT_FLAGS, PRIM_SIT_TARGET, PRIM_SIZE, PRIM_SLICE, PRIM_SPECULAR, PRIM_TEMP_ON_REZ, PRIM_TEXGEN, PRIM_TEXGEN_DEFAULT, PRIM_TEXT, PRIM_TEXTURE, PRIM_TYPE, PRIM_TYPE_BOX, PRIM_TYPE_CYLINDER, PRIM_TYPE_PRISM, PRIM_TYPE_RING, PRIM_TYPE_SCULPT, PRIM_TYPE_SPHERE, PRIM_TYPE_TORUS, PRIM_TYPE_TUBE, STATUS_PHANTOM, STATUS_PHYSICS, TEXTURE_DEFAULT, } from './generated/constants.js';
2
- import { NULL_KEY } from './values/types.js';
1
+ import { ALL_SIDES, CLICK_ACTION_TOUCH, PRIM_ALPHA_MODE, PRIM_ALPHA_MODE_NONE, PRIM_ALLOW_UNSIT, PRIM_BUMP_NONE, PRIM_BUMP_SHINY, PRIM_CAST_SHADOWS, PRIM_CLICK_ACTION, PRIM_COLOR, PRIM_DAMAGE, PRIM_DESC, PRIM_FLEXIBLE, PRIM_FULLBRIGHT, PRIM_GLOW, PRIM_GLTF_ALPHA_MODE_OPAQUE, PRIM_GLTF_BASE_COLOR, PRIM_GLTF_EMISSIVE, PRIM_GLTF_METALLIC_ROUGHNESS, PRIM_GLTF_NORMAL, PRIM_HEALTH, PRIM_HOLE_DEFAULT, PRIM_LINK_TARGET, PRIM_MATERIAL, PRIM_MATERIAL_WOOD, PRIM_NAME, PRIM_NORMAL, PRIM_OMEGA, PRIM_PHANTOM, PRIM_PHYSICS, PRIM_PHYSICS_SHAPE_PRIM, PRIM_PHYSICS_SHAPE_TYPE, PRIM_POINT_LIGHT, PRIM_POSITION, PRIM_POS_LOCAL, PRIM_PROJECTOR, PRIM_REFLECTION_PROBE, PRIM_RENDER_MATERIAL, PRIM_ROTATION, PRIM_ROT_LOCAL, PRIM_SCRIPTED_SIT_ONLY, PRIM_SCULPT_TYPE_SPHERE, PRIM_SHINY_NONE, PRIM_SIT_FLAGS, PRIM_SIT_TARGET, PRIM_SIZE, PRIM_SLICE, PRIM_SPECULAR, PRIM_TEMP_ON_REZ, PRIM_TEXGEN, PRIM_TEXGEN_DEFAULT, PRIM_TEXT, PRIM_TEXTURE, PRIM_TYPE, PRIM_TYPE_BOX, PRIM_TYPE_CYLINDER, PRIM_TYPE_PRISM, PRIM_TYPE_RING, PRIM_TYPE_SCULPT, PRIM_TYPE_SPHERE, PRIM_TYPE_TORUS, PRIM_TYPE_TUBE, STATUS_PHANTOM, STATUS_PHYSICS, TEXTURE_DEFAULT, } from './generated/constants.js';
2
+ import { NULL_KEY, isRotation, isVector } from './values/types.js';
3
+ import { listElementType } from './values/list-types.js';
4
+ import { truncateUtf8 } from './values/utf8.js';
3
5
  /**
4
6
  * Typed-literal aliases for the PRIM_TYPE_* constants. The
5
7
  * generated module exports them as `: number` so TS can't narrow on
@@ -80,7 +82,7 @@ export function defaultPrimParams() {
80
82
  sculpt: { textureKey: '', type: PRIM_SCULPT_TYPE_SPHERE },
81
83
  slice: { x: 0, y: 1, z: 0 },
82
84
  shape: defaultShape(),
83
- material: PRIM_MATERIAL_STONE,
85
+ material: PRIM_MATERIAL_WOOD,
84
86
  tempOnRez: false,
85
87
  castShadows: false,
86
88
  allowUnsit: true,
@@ -92,7 +94,7 @@ export function defaultPrimParams() {
92
94
  damage: { amount: 0, type: 0 },
93
95
  health: 1,
94
96
  reflectionProbe: { enabled: false, ambiance: 0, clipDistance: 0, flags: 0 },
95
- physicsMaterial: { gravityMultiplier: 1, restitution: 0, friction: 0, density: 1000 },
97
+ physicsMaterial: { gravityMultiplier: 1, restitution: 0.5, friction: Math.fround(0.6), density: 1000 },
96
98
  textureAnim: { mode: 0, face: 0, sizex: 0, sizey: 0, start: 0, length: 0, rate: 0 },
97
99
  passCollisions: 0,
98
100
  passTouches: 0,
@@ -118,15 +120,21 @@ const asRot = (v) => {
118
120
  return identityRot();
119
121
  };
120
122
  const toByteChannel = (c) => Math.floor(Math.min(1, Math.max(0, c)) * 255) / 255;
121
- function facesOf(faceArg) {
122
- const f = num(faceArg, 0) | 0;
123
- if (f === ALL_SIDES)
124
- return [0, 1, 2, 3, 4, 5];
125
- return [f];
123
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
124
+ const clampVec = (v, lo, hi) => ({ x: clamp(v.x, lo, hi), y: clamp(v.y, lo, hi), z: clamp(v.z, lo, hi) });
125
+ function normalizeRot(r) {
126
+ const mag = Math.hypot(r.x, r.y, r.z, r.s);
127
+ if (mag === 0)
128
+ return identityRot();
129
+ return { x: r.x / mag, y: r.y / mag, z: r.z / mag, s: r.s / mag };
126
130
  }
127
- function inRange(face) {
128
- return face >= 0 && face <= 5;
131
+ /** Face indexes a face argument names: every face for ALL_SIDES, none past the shape's face count. */
132
+ export function faceIndices(p, face) {
133
+ if (face === ALL_SIDES)
134
+ return p.faces.map((_, i) => i);
135
+ return face >= 0 && face < p.faces.length ? [face] : [];
129
136
  }
137
+ const facesOf = (p, faceArg) => faceIndices(p, num(faceArg, 0) | 0);
130
138
  /**
131
139
  * Slot count for a PRIM_* rule's *write* form, given the rule constant
132
140
  * and (for variable-shape rules like PRIM_TYPE) the rules list + cursor
@@ -210,14 +218,14 @@ export function writePrimParam(prim, param, rules, cursor) {
210
218
  const p = prim.params;
211
219
  switch (param) {
212
220
  case PRIM_NAME:
213
- prim.name = str(rules[cursor]);
221
+ prim.name = truncateUtf8(str(rules[cursor]), 63);
214
222
  return 1;
215
223
  case PRIM_DESC:
216
- prim.description = str(rules[cursor]);
224
+ prim.description = truncateUtf8(str(rules[cursor]), 127);
217
225
  prim.appearance.description = prim.description;
218
226
  return 1;
219
227
  case PRIM_TEXT: {
220
- const text = str(rules[cursor]);
228
+ const text = truncateUtf8(str(rules[cursor]), 254);
221
229
  const color = asVec(rules[cursor + 1]);
222
230
  const alpha = num(rules[cursor + 2], 1);
223
231
  prim.appearance.text = { text, color, alpha };
@@ -229,10 +237,10 @@ export function writePrimParam(prim, param, rules, cursor) {
229
237
  return 1;
230
238
  case PRIM_ROTATION:
231
239
  case PRIM_ROT_LOCAL:
232
- p.rotation = asRot(rules[cursor]);
240
+ p.rotation = normalizeRot(asRot(rules[cursor]));
233
241
  return 1;
234
242
  case PRIM_SIZE:
235
- p.size = asVec(rules[cursor]);
243
+ p.size = clampVec(asVec(rules[cursor]), 0.01, 64);
236
244
  return 1;
237
245
  case PRIM_SLICE:
238
246
  p.slice = asVec(rules[cursor]);
@@ -322,27 +330,22 @@ export function writePrimParam(prim, param, rules, cursor) {
322
330
  case PRIM_TYPE:
323
331
  return writePrimType(p, rules, cursor);
324
332
  case PRIM_COLOR: {
325
- const faces = facesOf(rules[cursor]);
333
+ const faces = facesOf(p, rules[cursor]);
326
334
  const color = asVec(rules[cursor + 1]);
327
- const alpha = num(rules[cursor + 2], 1);
335
+ const alpha = clamp(num(rules[cursor + 2], 1), 0, 1);
328
336
  for (const f of faces) {
329
- if (!inRange(f))
330
- continue;
331
- // Clone so faces don't alias the same vector instance.
332
- p.faces[f].color = { x: color.x, y: color.y, z: color.z };
337
+ p.faces[f].color = clampVec(color, 0, 1);
333
338
  p.faces[f].alpha = alpha;
334
339
  }
335
340
  return 3;
336
341
  }
337
342
  case PRIM_TEXTURE: {
338
- const faces = facesOf(rules[cursor]);
343
+ const faces = facesOf(p, rules[cursor]);
339
344
  const tex = str(rules[cursor + 1]);
340
345
  const repeats = asVec(rules[cursor + 2]);
341
346
  const offsets = asVec(rules[cursor + 3]);
342
347
  const rot = num(rules[cursor + 4]);
343
348
  for (const f of faces) {
344
- if (!inRange(f))
345
- continue;
346
349
  p.faces[f].texture = tex;
347
350
  p.faces[f].textureRepeats = { x: repeats.x, y: repeats.y, z: repeats.z };
348
351
  p.faces[f].textureOffsets = { x: offsets.x, y: offsets.y, z: offsets.z };
@@ -351,66 +354,54 @@ export function writePrimParam(prim, param, rules, cursor) {
351
354
  return 5;
352
355
  }
353
356
  case PRIM_RENDER_MATERIAL: {
354
- const faces = facesOf(rules[cursor]);
357
+ const faces = facesOf(p, rules[cursor]);
355
358
  const m = str(rules[cursor + 1]);
356
359
  for (const f of faces) {
357
- if (!inRange(f))
358
- continue;
359
360
  p.faces[f].renderMaterial = m;
360
361
  }
361
362
  return 2;
362
363
  }
363
364
  case PRIM_FULLBRIGHT: {
364
- const faces = facesOf(rules[cursor]);
365
+ const faces = facesOf(p, rules[cursor]);
365
366
  const v = num(rules[cursor + 1]) !== 0;
366
367
  for (const f of faces) {
367
- if (!inRange(f))
368
- continue;
369
368
  p.faces[f].fullBright = v;
370
369
  }
371
370
  return 2;
372
371
  }
373
372
  case PRIM_GLOW: {
374
- const faces = facesOf(rules[cursor]);
373
+ const faces = facesOf(p, rules[cursor]);
375
374
  const v = num(rules[cursor + 1]);
376
375
  for (const f of faces) {
377
- if (!inRange(f))
378
- continue;
379
376
  p.faces[f].glow = v;
380
377
  p.glow[f] = v;
381
378
  }
382
379
  return 2;
383
380
  }
384
381
  case PRIM_BUMP_SHINY: {
385
- const faces = facesOf(rules[cursor]);
382
+ const faces = facesOf(p, rules[cursor]);
386
383
  const shiny = num(rules[cursor + 1]) | 0;
387
384
  const bump = num(rules[cursor + 2]) | 0;
388
385
  for (const f of faces) {
389
- if (!inRange(f))
390
- continue;
391
386
  p.faces[f].bumpShiny = { shiny, bump };
392
387
  }
393
388
  return 3;
394
389
  }
395
390
  case PRIM_TEXGEN: {
396
- const faces = facesOf(rules[cursor]);
391
+ const faces = facesOf(p, rules[cursor]);
397
392
  const mode = num(rules[cursor + 1]) | 0;
398
393
  for (const f of faces) {
399
- if (!inRange(f))
400
- continue;
401
394
  p.faces[f].texgen = mode;
402
395
  }
403
396
  return 2;
404
397
  }
405
398
  case PRIM_NORMAL: {
406
- const faces = facesOf(rules[cursor]);
399
+ const faces = facesOf(p, rules[cursor]);
407
400
  const tex = str(rules[cursor + 1]);
408
401
  const repeats = asVec(rules[cursor + 2]);
409
402
  const offsets = asVec(rules[cursor + 3]);
410
403
  const rot = num(rules[cursor + 4]);
411
404
  for (const f of faces) {
412
- if (!inRange(f))
413
- continue;
414
405
  p.faces[f].normal = {
415
406
  texture: tex,
416
407
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -421,7 +412,7 @@ export function writePrimParam(prim, param, rules, cursor) {
421
412
  return 5;
422
413
  }
423
414
  case PRIM_SPECULAR: {
424
- const faces = facesOf(rules[cursor]);
415
+ const faces = facesOf(p, rules[cursor]);
425
416
  const tex = str(rules[cursor + 1]);
426
417
  const repeats = asVec(rules[cursor + 2]);
427
418
  const offsets = asVec(rules[cursor + 3]);
@@ -430,8 +421,6 @@ export function writePrimParam(prim, param, rules, cursor) {
430
421
  const gloss = num(rules[cursor + 6]) | 0;
431
422
  const env = num(rules[cursor + 7]) | 0;
432
423
  for (const f of faces) {
433
- if (!inRange(f))
434
- continue;
435
424
  p.faces[f].specular = {
436
425
  texture: tex,
437
426
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -446,18 +435,16 @@ export function writePrimParam(prim, param, rules, cursor) {
446
435
  return 8;
447
436
  }
448
437
  case PRIM_ALPHA_MODE: {
449
- const faces = facesOf(rules[cursor]);
438
+ const faces = facesOf(p, rules[cursor]);
450
439
  const mode = num(rules[cursor + 1]) | 0;
451
440
  const cutoff = num(rules[cursor + 2]) | 0;
452
441
  for (const f of faces) {
453
- if (!inRange(f))
454
- continue;
455
442
  p.faces[f].alphaMode = { mode, cutoff };
456
443
  }
457
444
  return 3;
458
445
  }
459
446
  case PRIM_GLTF_BASE_COLOR: {
460
- const faces = facesOf(rules[cursor]);
447
+ const faces = facesOf(p, rules[cursor]);
461
448
  const tex = str(rules[cursor + 1]);
462
449
  const repeats = asVec(rules[cursor + 2]);
463
450
  const offsets = asVec(rules[cursor + 3]);
@@ -468,8 +455,6 @@ export function writePrimParam(prim, param, rules, cursor) {
468
455
  const aCut = num(rules[cursor + 8]);
469
456
  const dbl = num(rules[cursor + 9]) | 0;
470
457
  for (const f of faces) {
471
- if (!inRange(f))
472
- continue;
473
458
  p.faces[f].gltf.baseColor = {
474
459
  texture: tex,
475
460
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -485,14 +470,12 @@ export function writePrimParam(prim, param, rules, cursor) {
485
470
  return 10;
486
471
  }
487
472
  case PRIM_GLTF_NORMAL: {
488
- const faces = facesOf(rules[cursor]);
473
+ const faces = facesOf(p, rules[cursor]);
489
474
  const tex = str(rules[cursor + 1]);
490
475
  const repeats = asVec(rules[cursor + 2]);
491
476
  const offsets = asVec(rules[cursor + 3]);
492
477
  const rot = num(rules[cursor + 4]);
493
478
  for (const f of faces) {
494
- if (!inRange(f))
495
- continue;
496
479
  p.faces[f].gltf.normal = {
497
480
  texture: tex,
498
481
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -503,7 +486,7 @@ export function writePrimParam(prim, param, rules, cursor) {
503
486
  return 5;
504
487
  }
505
488
  case PRIM_GLTF_METALLIC_ROUGHNESS: {
506
- const faces = facesOf(rules[cursor]);
489
+ const faces = facesOf(p, rules[cursor]);
507
490
  const tex = str(rules[cursor + 1]);
508
491
  const repeats = asVec(rules[cursor + 2]);
509
492
  const offsets = asVec(rules[cursor + 3]);
@@ -511,8 +494,6 @@ export function writePrimParam(prim, param, rules, cursor) {
511
494
  const metallic = num(rules[cursor + 5]);
512
495
  const rough = num(rules[cursor + 6]);
513
496
  for (const f of faces) {
514
- if (!inRange(f))
515
- continue;
516
497
  p.faces[f].gltf.metallicRoughness = {
517
498
  texture: tex,
518
499
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -525,15 +506,13 @@ export function writePrimParam(prim, param, rules, cursor) {
525
506
  return 7;
526
507
  }
527
508
  case PRIM_GLTF_EMISSIVE: {
528
- const faces = facesOf(rules[cursor]);
509
+ const faces = facesOf(p, rules[cursor]);
529
510
  const tex = str(rules[cursor + 1]);
530
511
  const repeats = asVec(rules[cursor + 2]);
531
512
  const offsets = asVec(rules[cursor + 3]);
532
513
  const rot = num(rules[cursor + 4]);
533
514
  const tint = asVec(rules[cursor + 5]);
534
515
  for (const f of faces) {
535
- if (!inRange(f))
536
- continue;
537
516
  p.faces[f].gltf.emissive = {
538
517
  texture: tex,
539
518
  textureRepeats: { x: repeats.x, y: repeats.y, z: repeats.z },
@@ -552,6 +531,7 @@ export function writePrimParam(prim, param, rules, cursor) {
552
531
  }
553
532
  function writePrimType(p, rules, cursor) {
554
533
  const kind = (num(rules[cursor]) | 0);
534
+ const old = p.shape;
555
535
  switch (kind) {
556
536
  case SHAPE_BOX:
557
537
  case SHAPE_CYLINDER:
@@ -565,7 +545,7 @@ function writePrimType(p, rules, cursor) {
565
545
  topSize: asVec(rules[cursor + 5]),
566
546
  topShear: asVec(rules[cursor + 6]),
567
547
  };
568
- return 7;
548
+ break;
569
549
  case SHAPE_SPHERE:
570
550
  p.shape = {
571
551
  kind: SHAPE_SPHERE,
@@ -575,7 +555,7 @@ function writePrimType(p, rules, cursor) {
575
555
  twist: asVec(rules[cursor + 4]),
576
556
  dimple: asVec(rules[cursor + 5]),
577
557
  };
578
- return 6;
558
+ break;
579
559
  case SHAPE_TORUS:
580
560
  case SHAPE_TUBE:
581
561
  case SHAPE_RING:
@@ -593,14 +573,14 @@ function writePrimType(p, rules, cursor) {
593
573
  radiusOffset: num(rules[cursor + 10]),
594
574
  skew: num(rules[cursor + 11]),
595
575
  };
596
- return 12;
576
+ break;
597
577
  case SHAPE_SCULPT:
598
578
  p.shape = {
599
579
  kind: SHAPE_SCULPT,
600
580
  map: str(rules[cursor + 1]),
601
581
  type: num(rules[cursor + 2]) | 0,
602
582
  };
603
- return 3;
583
+ break;
604
584
  default:
605
585
  // Unknown PRIM_TYPE_* kind: terminate the walk rather than guessing
606
586
  // a slot count. Matches writePrimParamSlots' default behavior so
@@ -608,6 +588,8 @@ function writePrimType(p, rules, cursor) {
608
588
  // same way.
609
589
  return null;
610
590
  }
591
+ remapFaces(p, old);
592
+ return writePrimParamSlots(PRIM_TYPE, rules, cursor);
611
593
  }
612
594
  /**
613
595
  * Read a single PRIM_* rule, returning the flat values it expands to and
@@ -627,6 +609,7 @@ export function readPrimParam(prim, param, rules, cursor) {
627
609
  return { values: t ? [t.text, t.color, t.alpha] : ['', zero(), 0], consumed: 0 };
628
610
  }
629
611
  case PRIM_POSITION:
612
+ return { values: [prim.regionPosition()], consumed: 0 };
630
613
  case PRIM_POS_LOCAL:
631
614
  return { values: [p.position], consumed: 0 };
632
615
  case PRIM_ROTATION:
@@ -688,147 +671,119 @@ export function readPrimParam(prim, param, rules, cursor) {
688
671
  case PRIM_TYPE:
689
672
  return { values: readPrimType(p), consumed: 0 };
690
673
  case PRIM_COLOR: {
691
- const faces = facesOf(rules[cursor]);
674
+ const faces = facesOf(p, rules[cursor]);
692
675
  const out = [];
693
676
  for (const f of faces) {
694
- if (!inRange(f))
695
- continue;
696
677
  out.push(p.faces[f].color, p.faces[f].alpha);
697
678
  }
698
679
  return { values: out, consumed: 1 };
699
680
  }
700
681
  case PRIM_TEXTURE: {
701
- const faces = facesOf(rules[cursor]);
682
+ const faces = facesOf(p, rules[cursor]);
702
683
  const out = [];
703
684
  for (const f of faces) {
704
- if (!inRange(f))
705
- continue;
706
685
  const fa = p.faces[f];
707
686
  out.push(fa.texture, fa.textureRepeats, fa.textureOffsets, fa.textureRotation);
708
687
  }
709
688
  return { values: out, consumed: 1 };
710
689
  }
711
690
  case PRIM_RENDER_MATERIAL: {
712
- const faces = facesOf(rules[cursor]);
691
+ const faces = facesOf(p, rules[cursor]);
713
692
  const out = [];
714
693
  for (const f of faces) {
715
- if (!inRange(f))
716
- continue;
717
694
  out.push(p.faces[f].renderMaterial);
718
695
  }
719
696
  return { values: out, consumed: 1 };
720
697
  }
721
698
  case PRIM_FULLBRIGHT: {
722
- const faces = facesOf(rules[cursor]);
699
+ const faces = facesOf(p, rules[cursor]);
723
700
  const out = [];
724
701
  for (const f of faces) {
725
- if (!inRange(f))
726
- continue;
727
702
  out.push(p.faces[f].fullBright ? 1 : 0);
728
703
  }
729
704
  return { values: out, consumed: 1 };
730
705
  }
731
706
  case PRIM_GLOW: {
732
- const faces = facesOf(rules[cursor]);
707
+ const faces = facesOf(p, rules[cursor]);
733
708
  const out = [];
734
709
  for (const f of faces) {
735
- if (!inRange(f))
736
- continue;
737
710
  out.push(p.faces[f].glow);
738
711
  }
739
712
  return { values: out, consumed: 1 };
740
713
  }
741
714
  case PRIM_BUMP_SHINY: {
742
- const faces = facesOf(rules[cursor]);
715
+ const faces = facesOf(p, rules[cursor]);
743
716
  const out = [];
744
717
  for (const f of faces) {
745
- if (!inRange(f))
746
- continue;
747
718
  out.push(p.faces[f].bumpShiny.shiny, p.faces[f].bumpShiny.bump);
748
719
  }
749
720
  return { values: out, consumed: 1 };
750
721
  }
751
722
  case PRIM_TEXGEN: {
752
- const faces = facesOf(rules[cursor]);
723
+ const faces = facesOf(p, rules[cursor]);
753
724
  const out = [];
754
725
  for (const f of faces) {
755
- if (!inRange(f))
756
- continue;
757
726
  out.push(p.faces[f].texgen);
758
727
  }
759
728
  return { values: out, consumed: 1 };
760
729
  }
761
730
  case PRIM_NORMAL: {
762
- const faces = facesOf(rules[cursor]);
731
+ const faces = facesOf(p, rules[cursor]);
763
732
  const out = [];
764
733
  for (const f of faces) {
765
- if (!inRange(f))
766
- continue;
767
734
  const n = p.faces[f].normal;
768
735
  out.push(n.texture, n.textureRepeats, n.textureOffsets, n.textureRotation);
769
736
  }
770
737
  return { values: out, consumed: 1 };
771
738
  }
772
739
  case PRIM_SPECULAR: {
773
- const faces = facesOf(rules[cursor]);
740
+ const faces = facesOf(p, rules[cursor]);
774
741
  const out = [];
775
742
  for (const f of faces) {
776
- if (!inRange(f))
777
- continue;
778
743
  const s = p.faces[f].specular;
779
744
  out.push(s.texture, s.textureRepeats, s.textureOffsets, s.textureRotation, s.color, s.glossiness, s.environment);
780
745
  }
781
746
  return { values: out, consumed: 1 };
782
747
  }
783
748
  case PRIM_ALPHA_MODE: {
784
- const faces = facesOf(rules[cursor]);
749
+ const faces = facesOf(p, rules[cursor]);
785
750
  const out = [];
786
751
  for (const f of faces) {
787
- if (!inRange(f))
788
- continue;
789
752
  out.push(p.faces[f].alphaMode.mode, p.faces[f].alphaMode.cutoff);
790
753
  }
791
754
  return { values: out, consumed: 1 };
792
755
  }
793
756
  case PRIM_GLTF_BASE_COLOR: {
794
- const faces = facesOf(rules[cursor]);
757
+ const faces = facesOf(p, rules[cursor]);
795
758
  const out = [];
796
759
  for (const f of faces) {
797
- if (!inRange(f))
798
- continue;
799
760
  const g = p.faces[f].gltf.baseColor;
800
761
  out.push(g.texture, g.textureRepeats, g.textureOffsets, g.textureRotation, g.color, g.alpha, g.alphaMode, g.alphaCutoff, g.doubleSided);
801
762
  }
802
763
  return { values: out, consumed: 1 };
803
764
  }
804
765
  case PRIM_GLTF_NORMAL: {
805
- const faces = facesOf(rules[cursor]);
766
+ const faces = facesOf(p, rules[cursor]);
806
767
  const out = [];
807
768
  for (const f of faces) {
808
- if (!inRange(f))
809
- continue;
810
769
  const g = p.faces[f].gltf.normal;
811
770
  out.push(g.texture, g.textureRepeats, g.textureOffsets, g.textureRotation);
812
771
  }
813
772
  return { values: out, consumed: 1 };
814
773
  }
815
774
  case PRIM_GLTF_METALLIC_ROUGHNESS: {
816
- const faces = facesOf(rules[cursor]);
775
+ const faces = facesOf(p, rules[cursor]);
817
776
  const out = [];
818
777
  for (const f of faces) {
819
- if (!inRange(f))
820
- continue;
821
778
  const g = p.faces[f].gltf.metallicRoughness;
822
779
  out.push(g.texture, g.textureRepeats, g.textureOffsets, g.textureRotation, g.metallic, g.roughness);
823
780
  }
824
781
  return { values: out, consumed: 1 };
825
782
  }
826
783
  case PRIM_GLTF_EMISSIVE: {
827
- const faces = facesOf(rules[cursor]);
784
+ const faces = facesOf(p, rules[cursor]);
828
785
  const out = [];
829
786
  for (const f of faces) {
830
- if (!inRange(f))
831
- continue;
832
787
  const g = p.faces[f].gltf.emissive;
833
788
  out.push(g.texture, g.textureRepeats, g.textureOffsets, g.textureRotation, g.tint);
834
789
  }
@@ -921,4 +876,129 @@ export function primParamTypes(param, values) {
921
876
  const pattern = param === PRIM_TYPE ? primTypeTypes(values[0]) : (PRIM_PARAM_TYPES[param] ?? []);
922
877
  return values.map((_, i) => pattern[i % pattern.length]);
923
878
  }
879
+ const PER_FACE_RULES = new Set([
880
+ PRIM_COLOR, PRIM_TEXTURE, PRIM_RENDER_MATERIAL, PRIM_FULLBRIGHT, PRIM_GLOW, PRIM_BUMP_SHINY,
881
+ PRIM_TEXGEN, PRIM_NORMAL, PRIM_SPECULAR, PRIM_ALPHA_MODE, PRIM_GLTF_BASE_COLOR,
882
+ PRIM_GLTF_NORMAL, PRIM_GLTF_METALLIC_ROUGHNESS, PRIM_GLTF_EMISSIVE,
883
+ ]);
884
+ const RULE_CONSTANTS = {
885
+ PRIM_NAME, PRIM_DESC, PRIM_TEXT, PRIM_POSITION, PRIM_POS_LOCAL, PRIM_ROTATION, PRIM_ROT_LOCAL,
886
+ PRIM_SIZE, PRIM_SLICE, PRIM_MATERIAL, PRIM_PHYSICS, PRIM_PHANTOM, PRIM_TEMP_ON_REZ,
887
+ PRIM_PHYSICS_SHAPE_TYPE, PRIM_CAST_SHADOWS, PRIM_ALLOW_UNSIT, PRIM_SCRIPTED_SIT_ONLY,
888
+ PRIM_SIT_FLAGS, PRIM_CLICK_ACTION, PRIM_HEALTH, PRIM_DAMAGE, PRIM_OMEGA, PRIM_POINT_LIGHT,
889
+ PRIM_REFLECTION_PROBE, PRIM_PROJECTOR, PRIM_FLEXIBLE, PRIM_SIT_TARGET, PRIM_TYPE, PRIM_COLOR,
890
+ PRIM_TEXTURE, PRIM_RENDER_MATERIAL, PRIM_FULLBRIGHT, PRIM_GLOW, PRIM_BUMP_SHINY, PRIM_TEXGEN,
891
+ PRIM_NORMAL, PRIM_SPECULAR, PRIM_ALPHA_MODE, PRIM_GLTF_BASE_COLOR, PRIM_GLTF_NORMAL,
892
+ PRIM_GLTF_METALLIC_ROUGHNESS, PRIM_GLTF_EMISSIVE,
893
+ };
894
+ const RULE_NAMES = new Map(Object.entries(RULE_CONSTANTS).map(([name, value]) => [value, name]));
895
+ function givenType(rules, index) {
896
+ const known = listElementType(rules, index);
897
+ if (known)
898
+ return known;
899
+ const v = rules[index];
900
+ if (typeof v === 'string')
901
+ return 'string';
902
+ if (typeof v === 'number')
903
+ return Number.isInteger(v) ? 'integer' : 'float';
904
+ if (isRotation(v))
905
+ return 'rotation';
906
+ if (isVector(v))
907
+ return 'vector';
908
+ return 'list';
909
+ }
910
+ function typeAccepts(expected, given) {
911
+ if (expected === given)
912
+ return true;
913
+ const numeric = (t) => t === 'integer' || t === 'float';
914
+ const text = (t) => t === 'string' || t === 'key';
915
+ return (numeric(expected) && numeric(given)) || (text(expected) && text(given));
916
+ }
917
+ /** `%g`-style number text, as the simulator prints it in error messages. */
918
+ const formatG = (v) => String(Number(v.toPrecision(6)));
919
+ /**
920
+ * Check one PRIM_* rule of a set call before it is applied. Returns null if
921
+ * the rule can run, otherwise the error text that follows "rule #N" in the
922
+ * simulator's message (e.g. " (PRIM_SIZE): arg #1 vector expected but string given").
923
+ */
924
+ /** GLTF override rules take "" in any slot after the face to mean "no override". */
925
+ const GLTF_RULES = new Set([PRIM_GLTF_BASE_COLOR, PRIM_GLTF_NORMAL, PRIM_GLTF_METALLIC_ROUGHNESS, PRIM_GLTF_EMISSIVE]);
926
+ export function checkPrimRule(param, rules, cursor) {
927
+ const name = RULE_NAMES.get(param);
928
+ if (name === undefined || writePrimParamSlots(param, rules, cursor) === null)
929
+ return ': unknown rule';
930
+ const pattern = param === PRIM_TYPE ? primTypeTypes(rules[cursor]) : PRIM_PARAM_TYPES[param];
931
+ const expected = PER_FACE_RULES.has(param) ? ['integer', ...pattern] : pattern;
932
+ for (let i = 0; i < expected.length; i++) {
933
+ if (cursor + i >= rules.length)
934
+ break;
935
+ if (i > 0 && GLTF_RULES.has(param) && rules[cursor + i] === '')
936
+ continue;
937
+ const given = givenType(rules, cursor + i);
938
+ if (!typeAccepts(expected[i], given)) {
939
+ return ` (${name}): arg #${i + 1} ${expected[i]} expected but ${given} given`;
940
+ }
941
+ }
942
+ if (param === PRIM_GLOW) {
943
+ const glow = num(rules[cursor + 1]);
944
+ if (glow < 0 || glow > 1)
945
+ return ` (${name}): bounds error; ${formatG(glow)} is not in (0, 1)`;
946
+ }
947
+ return null;
948
+ }
949
+ const PROFILE_SIDES = {
950
+ [SHAPE_BOX]: 4, [SHAPE_CYLINDER]: 1, [SHAPE_PRISM]: 3, [SHAPE_SPHERE]: 1,
951
+ [SHAPE_TORUS]: 1, [SHAPE_TUBE]: 4, [SHAPE_RING]: 3,
952
+ };
953
+ const isCut = (range) => range.x > 0 || range.y < 1;
954
+ function faceRoles(shape) {
955
+ if (shape.kind === SHAPE_SCULPT)
956
+ return [{ from: 0, to: 1 }];
957
+ const looped = shape.kind === SHAPE_TORUS || shape.kind === SHAPE_TUBE || shape.kind === SHAPE_RING;
958
+ const profileCut = looped ? shape.advancedCut : shape.cut;
959
+ const pathEnds = shape.kind === SHAPE_SPHERE
960
+ ? isCut(shape.dimple) || shape.hollow > 0
961
+ : !looped || isCut(shape.cut);
962
+ const sides = PROFILE_SIDES[shape.kind];
963
+ const roles = pathEnds ? ['top'] : [];
964
+ for (let i = 0; i < sides; i++) {
965
+ const from = i / sides, to = (i + 1) / sides;
966
+ if (from < profileCut.y && to > profileCut.x)
967
+ roles.push({ from, to });
968
+ }
969
+ if (shape.hollow > 0)
970
+ roles.push('inner');
971
+ if (pathEnds)
972
+ roles.push('bottom');
973
+ if (isCut(profileCut))
974
+ roles.push('cutStart', 'cutEnd');
975
+ return roles;
976
+ }
977
+ /**
978
+ * Rebuild the face slots for a new shape. A face keeps the values of the old
979
+ * face with the same role; an outer side takes the first old outer side it
980
+ * overlaps, else the nearest one; any other new face copies the old last face.
981
+ */
982
+ function remapFaces(p, oldShape) {
983
+ const oldRoles = faceRoles(oldShape);
984
+ const old = p.faces;
985
+ const last = old[old.length - 1];
986
+ const oldOuter = oldRoles.flatMap((r, i) => (typeof r === 'string' ? [] : [{ ...r, face: old[i] }]));
987
+ const source = (role) => {
988
+ if (typeof role === 'string')
989
+ return old[oldRoles.indexOf(role)] ?? last;
990
+ const gap = (o) => Math.max(o.from - role.to, role.from - o.to);
991
+ const overlap = oldOuter.find((o) => gap(o) < 0);
992
+ if (overlap)
993
+ return overlap.face;
994
+ const nearest = oldOuter.reduce((a, o) => (!a || gap(o) < gap(a) ? o : a), undefined);
995
+ return nearest?.face ?? last;
996
+ };
997
+ p.faces = faceRoles(p.shape).map((role) => structuredClone(source(role)));
998
+ p.glow = p.faces.map((f) => f.glow);
999
+ }
1000
+ /** Face count of a prim shape, as llGetNumberOfSides reports it. */
1001
+ export function numberOfSides(shape) {
1002
+ return faceRoles(shape).length;
1003
+ }
924
1004
  //# sourceMappingURL=prim-params.js.map