murow 0.0.72 → 0.1.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.
Files changed (115) hide show
  1. package/README.md +15 -1
  2. package/dist/cjs/core/binary-codec/binary-codec.js +1 -1
  3. package/dist/cjs/core/driver/driver.js +1 -1
  4. package/dist/cjs/core/driver/drivers/immediate.js +1 -1
  5. package/dist/cjs/core/driver/drivers/raf.js +1 -1
  6. package/dist/cjs/core/driver/drivers/timeout.js +1 -1
  7. package/dist/cjs/core/input/index.js +1 -1
  8. package/dist/cjs/core/input/mouse-look/index.js +1 -0
  9. package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
  10. package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
  11. package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
  12. package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -1
  13. package/dist/cjs/ecs/component.js +1 -1
  14. package/dist/cjs/ecs/system-builder.js +1 -1
  15. package/dist/cjs/ecs/world.js +1 -1
  16. package/dist/cjs/game/loop/loop.js +1 -1
  17. package/dist/cjs/net/adapters/bun-websocket.js +1 -1
  18. package/dist/cjs/renderer/base/renderer-3d.js +1 -1
  19. package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
  20. package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
  21. package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
  22. package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
  23. package/dist/esm/core/binary-codec/binary-codec.js +1 -1
  24. package/dist/esm/core/driver/drivers/immediate.js +1 -1
  25. package/dist/esm/core/driver/drivers/raf.js +1 -1
  26. package/dist/esm/core/driver/drivers/timeout.js +1 -1
  27. package/dist/esm/core/input/index.js +1 -1
  28. package/dist/esm/core/input/mouse-look/index.js +1 -0
  29. package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
  30. package/dist/esm/core/input/scroll-zoom/index.js +1 -0
  31. package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
  32. package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -1
  33. package/dist/esm/ecs/component.js +1 -1
  34. package/dist/esm/ecs/system-builder.js +1 -1
  35. package/dist/esm/ecs/world.js +1 -1
  36. package/dist/esm/game/loop/loop.js +1 -1
  37. package/dist/esm/net/adapters/bun-websocket.js +1 -1
  38. package/dist/esm/renderer/base/renderer-3d.js +1 -1
  39. package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
  40. package/dist/esm/renderer/prefab-bucket/index.js +1 -1
  41. package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
  42. package/dist/netcode/cjs/index.js +1552 -0
  43. package/dist/netcode/esm/index.js +1530 -0
  44. package/dist/netcode/types/client/game-client.d.ts +125 -0
  45. package/dist/netcode/types/client/index.d.ts +1 -0
  46. package/dist/netcode/types/client/interpolation-buffer.d.ts +37 -0
  47. package/dist/netcode/types/client/interpolation-buffer.test.d.ts +1 -0
  48. package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
  49. package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
  50. package/dist/netcode/types/codec/index.d.ts +1 -0
  51. package/dist/netcode/types/components/index.d.ts +1 -0
  52. package/dist/netcode/types/components/sync-spec.d.ts +43 -0
  53. package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
  54. package/dist/netcode/types/ctx.d.ts +105 -0
  55. package/dist/netcode/types/ctx.test.d.ts +1 -0
  56. package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
  57. package/dist/netcode/types/handlers/index.d.ts +1 -0
  58. package/dist/netcode/types/index.d.ts +11 -0
  59. package/dist/netcode/types/integration.test.d.ts +1 -0
  60. package/dist/netcode/types/intents/define-intents.d.ts +53 -0
  61. package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
  62. package/dist/netcode/types/intents/index.d.ts +1 -0
  63. package/dist/netcode/types/network/base.d.ts +120 -0
  64. package/dist/netcode/types/network/index.d.ts +2 -0
  65. package/dist/netcode/types/network/transport.d.ts +1 -0
  66. package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
  67. package/dist/netcode/types/packets/harness.d.ts +103 -0
  68. package/dist/netcode/types/packets/index.d.ts +2 -0
  69. package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
  70. package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
  71. package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
  72. package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
  73. package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
  74. package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
  75. package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
  76. package/dist/netcode/types/predictions/index.d.ts +1 -0
  77. package/dist/netcode/types/reconciliation.test.d.ts +1 -0
  78. package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
  79. package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
  80. package/dist/netcode/types/rpcs/index.d.ts +1 -0
  81. package/dist/netcode/types/server/game-server.d.ts +77 -0
  82. package/dist/netcode/types/server/index.d.ts +2 -0
  83. package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
  84. package/dist/netcode/types/server/plugins/index.d.ts +3 -0
  85. package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
  86. package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
  87. package/dist/netcode/types/tick-rate.test.d.ts +1 -0
  88. package/dist/netcode/types/transports/index.d.ts +1 -0
  89. package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
  90. package/dist/netcode/types/types.test.d.ts +1 -0
  91. package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
  92. package/dist/types/core/driver/driver.d.ts +8 -8
  93. package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
  94. package/dist/types/core/driver/drivers/raf.d.ts +17 -6
  95. package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
  96. package/dist/types/core/input/index.d.ts +2 -0
  97. package/dist/types/core/input/mouse-look/index.d.ts +1 -0
  98. package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
  99. package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
  100. package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
  101. package/dist/types/ecs/component.d.ts +67 -11
  102. package/dist/types/ecs/entity-handle.d.ts +5 -5
  103. package/dist/types/ecs/system-builder.d.ts +13 -0
  104. package/dist/types/ecs/world.d.ts +72 -4
  105. package/dist/types/game/loop/loop.d.ts +21 -2
  106. package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
  107. package/dist/types/renderer/base/renderer-3d.d.ts +1 -1
  108. package/dist/types/renderer/prefab-bucket/concrete.d.ts +42 -2
  109. package/dist/types/renderer/prefab-bucket/index.d.ts +12 -2
  110. package/dist/types/renderer/prefab-bucket/specs.d.ts +46 -3
  111. package/dist/types/renderer/types.d.ts +5 -3
  112. package/dist/webgpu/cjs/index.js +591 -40
  113. package/dist/webgpu/esm/index.js +591 -40
  114. package/dist/webgpu/types/3d/renderer.d.ts +111 -5
  115. package/package.json +6 -1
@@ -3768,7 +3768,8 @@ var SSTAT_CB = 5;
3768
3768
  var SSTAT_BONE_OFFSET = 6;
3769
3769
  var prefabHandles = /* @__PURE__ */ new WeakMap();
3770
3770
  function isPrefab3D(value) {
3771
- return value.type === "gltf" || value.type === "grid";
3771
+ const t = value.type;
3772
+ return t === "gltf" || t === "grid" || t === "cube" || t === "composite";
3772
3773
  }
3773
3774
  function resolveTransform(opts) {
3774
3775
  const [px, py, pz] = opts.position ?? [0, 0, 0];
@@ -3802,8 +3803,8 @@ function computeBucketStats(bucket) {
3802
3803
  }
3803
3804
  var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3804
3805
  constructor(canvas, options) {
3805
- const resolvedMaxModels = options.maxModels ?? (options.prefabs ? options.prefabs.size + 16 : 32);
3806
- super(canvas, { ...options, maxModels: resolvedMaxModels });
3806
+ const resolvedMaxInstances = options.maxInstances ?? (options.prefabs ? options.prefabs.size + 16 : 32);
3807
+ super(canvas, { ...options, maxInstances: resolvedMaxInstances });
3807
3808
  this.resizeObserver = null;
3808
3809
  this.resizeCallbacks = [];
3809
3810
  // layer=0, sheetId=modelId
@@ -3829,6 +3830,8 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3829
3830
  this.skinnedStaticDirty = false;
3830
3831
  this.skinnedAnimStates = [];
3831
3832
  this.nextBoneOffset = 0;
3833
+ /** Reusable bone-offset blocks per skinIndex. Pushed on remove, popped on add. Indexed by skinIndex (low cardinality), so a Map of lists is fine. */
3834
+ this.freedBoneOffsets = /* @__PURE__ */ new Map();
3832
3835
  // Frustum planes (6 planes × 4 floats each), extracted from VP matrix
3833
3836
  this.frustumPlanes = new Float32Array(24);
3834
3837
  this.uniformData = new Float32Array(24);
@@ -3838,22 +3841,26 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3838
3841
  this._prefabs = options.prefabs ?? null;
3839
3842
  const SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP = 3;
3840
3843
  const bucketStats = this._prefabs ? computeBucketStats(this._prefabs) : null;
3841
- const maxInstances = options.maxInstances ?? resolvedMaxModels;
3842
- this.maxSkinnedInstances = options.maxSkinnedInstances ?? (bucketStats ? maxInstances * Math.max(1, Math.min(bucketStats.maxSkinnedParts, SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP)) : 5e3);
3844
+ this.maxSkinnedInstances = options.maxSkinnedInstances ?? (bucketStats ? resolvedMaxInstances * Math.max(1, Math.min(bucketStats.maxSkinnedParts, SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP)) : 5e3);
3843
3845
  this.maxBonesPerSkin = options.maxBonesPerSkin ?? (bucketStats ? Math.max(1, bucketStats.maxJointCount) : 64);
3846
+ const cullDist = options.animationCullDistance ?? 50;
3847
+ this.animationCullDistanceSq = cullDist * cullDist;
3844
3848
  this.maxTotalBones = this.maxSkinnedInstances * this.maxBonesPerSkin * 2;
3845
3849
  this.updatedBoneOffsets = new Uint8Array(this.maxTotalBones);
3846
- this.freeList = new import_free_list3.FreeList(resolvedMaxModels);
3847
- this.batcher = new import_sparse_batcher2.SparseBatcher(resolvedMaxModels);
3848
- this.dynamicData = new Float32Array(resolvedMaxModels * DYNAMIC_MESH_FLOATS);
3849
- this.staticData = new Float32Array(resolvedMaxModels * STATIC_MESH_FLOATS);
3850
- this.slotIndexData = new Uint32Array(resolvedMaxModels);
3851
- this.instanceModelIds = new Uint8Array(resolvedMaxModels);
3850
+ this.boneOffsetRefcount = new Uint32Array(this.maxTotalBones);
3851
+ this.boneOffsetSkinIndex = new Uint32Array(this.maxTotalBones);
3852
+ this.freeList = new import_free_list3.FreeList(resolvedMaxInstances);
3853
+ this.batcher = new import_sparse_batcher2.SparseBatcher(resolvedMaxInstances);
3854
+ this.dynamicData = new Float32Array(resolvedMaxInstances * DYNAMIC_MESH_FLOATS);
3855
+ this.staticData = new Float32Array(resolvedMaxInstances * STATIC_MESH_FLOATS);
3856
+ this.slotIndexData = new Uint32Array(resolvedMaxInstances);
3857
+ this.instanceModelIds = new Uint8Array(resolvedMaxInstances);
3852
3858
  const msi = this.maxSkinnedInstances;
3853
3859
  this.skinnedFreeList = new import_free_list3.FreeList(msi);
3854
3860
  this.skinnedBatcher = new import_sparse_batcher2.SparseBatcher(msi);
3855
3861
  this.skinnedDynamicData = new Float32Array(msi * DYNAMIC_MESH_FLOATS);
3856
3862
  this.skinnedStaticData = new Float32Array(msi * SKINNED_STATIC_MESH_FLOATS);
3863
+ this.skinnedStaticDV = new DataView(this.skinnedStaticData.buffer);
3857
3864
  this.skinnedSlotIndexData = new Uint32Array(msi);
3858
3865
  this.skinnedInstanceModelIds = new Uint8Array(msi);
3859
3866
  this.skinnedInstanceBoneOffsets = new Uint32Array(msi);
@@ -3864,7 +3871,19 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3864
3871
  this.gpuInstDV = new DataView(this.gpuInstData.buffer);
3865
3872
  }
3866
3873
  async init() {
3867
- this.root = await import_typegpu9.default.init();
3874
+ const adapter = await navigator.gpu.requestAdapter();
3875
+ if (!adapter)
3876
+ throw new Error("WebGPU3DRenderer: no GPU adapter available");
3877
+ const a = adapter.limits;
3878
+ const requiredLimits = {
3879
+ maxBufferSize: a.maxBufferSize,
3880
+ maxStorageBufferBindingSize: a.maxStorageBufferBindingSize,
3881
+ maxStorageBuffersPerShaderStage: a.maxStorageBuffersPerShaderStage,
3882
+ maxComputeWorkgroupStorageSize: a.maxComputeWorkgroupStorageSize,
3883
+ maxComputeInvocationsPerWorkgroup: a.maxComputeInvocationsPerWorkgroup
3884
+ };
3885
+ const device = await adapter.requestDevice({ requiredLimits });
3886
+ this.root = import_typegpu9.default.initFromDevice({ device });
3868
3887
  this.device = this.root.device;
3869
3888
  this.context = this.canvas.getContext("webgpu");
3870
3889
  this.format = navigator.gpu.getPreferredCanvasFormat();
@@ -3881,7 +3900,7 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3881
3900
  format: "depth24plus",
3882
3901
  usage: GPUTextureUsage.RENDER_ATTACHMENT
3883
3902
  });
3884
- this.meshLayout = createMeshLayout(this.maxModels);
3903
+ this.meshLayout = createMeshLayout(this.maxInstances);
3885
3904
  const depthStencil = {
3886
3905
  format: "depth24plus",
3887
3906
  depthWriteEnabled: true,
@@ -3928,10 +3947,10 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
3928
3947
  primitive,
3929
3948
  depthStencil
3930
3949
  });
3931
- this.dynamicBuffer = this.root.createBuffer(d.arrayOf(DynamicMesh, this.maxModels)).$usage("storage");
3932
- this.staticBuffer = this.root.createBuffer(d.arrayOf(StaticMesh, this.maxModels)).$usage("storage");
3950
+ this.dynamicBuffer = this.root.createBuffer(d.arrayOf(DynamicMesh, this.maxInstances)).$usage("storage");
3951
+ this.staticBuffer = this.root.createBuffer(d.arrayOf(StaticMesh, this.maxInstances)).$usage("storage");
3933
3952
  this.uniformBuffer = this.root.createBuffer(MeshUniforms).$usage("uniform");
3934
- this.slotIndexBuffer = this.root.createBuffer(d.arrayOf(d.u32, this.maxModels)).$usage("storage");
3953
+ this.slotIndexBuffer = this.root.createBuffer(d.arrayOf(d.u32, this.maxInstances)).$usage("storage");
3935
3954
  this.rawDynamicBuffer = this.root.unwrap(this.dynamicBuffer);
3936
3955
  this.rawStaticBuffer = this.root.unwrap(this.staticBuffer);
3937
3956
  this.rawUniformBuffer = this.root.unwrap(this.uniformBuffer);
@@ -4033,6 +4052,9 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4033
4052
  lineWidth: prefab.lineWidth
4034
4053
  });
4035
4054
  prefabHandles.set(prefab, model);
4055
+ } else if (prefab.type === "cube") {
4056
+ const model = this.createCube({ size: prefab.size });
4057
+ prefabHandles.set(prefab, model);
4036
4058
  }
4037
4059
  }
4038
4060
  }
@@ -4097,6 +4119,36 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4097
4119
  createCompute(name, options) {
4098
4120
  return new ComputeBuilder(name, options, this.root);
4099
4121
  }
4122
+ /**
4123
+ * Set the maximum distance (in world units) at which the renderer keeps
4124
+ * computing skeletal animation. Skinned instances farther than this are
4125
+ * still drawn, but with their last-computed bone matrices instead of
4126
+ * fresh ones, which saves GPU compute work. Their internal animation
4127
+ * clocks keep ticking on CPU, so when they come back into range they
4128
+ * resume in sync.
4129
+ *
4130
+ * Lower values trade visual smoothness on distant characters for FPS.
4131
+ * Pass `Infinity` to disable culling entirely (always animate).
4132
+ *
4133
+ * Safe to call any time; takes effect on the next frame.
4134
+ *
4135
+ * @param distance Max distance to animate at, in world units.
4136
+ */
4137
+ setAnimationCullDistance(distance) {
4138
+ this.animationCullDistanceSq = distance * distance;
4139
+ }
4140
+ /** Current animation cull distance (in world units). See `setAnimationCullDistance`. */
4141
+ get animationCullDistance() {
4142
+ return Math.sqrt(this.animationCullDistanceSq);
4143
+ }
4144
+ /**
4145
+ * Max skinned instances the renderer was sized for at construction.
4146
+ * Independent budget from `maxInstances` since skinned characters use a
4147
+ * separate set of GPU buffers. Read-only.
4148
+ */
4149
+ get maxSkinned() {
4150
+ return this.maxSkinnedInstances;
4151
+ }
4100
4152
  /**
4101
4153
  * Create a flat grid mesh on the XZ plane at Y=0.
4102
4154
  *
@@ -4128,6 +4180,245 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4128
4180
  indices: new Uint16Array(indices)
4129
4181
  });
4130
4182
  }
4183
+ /**
4184
+ * Create a unit-cube mesh centered at the origin. Pass `size` to scale the
4185
+ * edge length, or keep size = 1 and scale at the instance level.
4186
+ *
4187
+ * ```ts
4188
+ * const cube = renderer.createCube();
4189
+ * renderer.addInstance({ model: cube, color: [1, 0.5, 0.2], scale: 2 });
4190
+ * ```
4191
+ */
4192
+ createCube(opts = {}) {
4193
+ const h = (opts.size ?? 1) / 2;
4194
+ const positions = new Float32Array([
4195
+ // +Z
4196
+ -h,
4197
+ -h,
4198
+ h,
4199
+ h,
4200
+ -h,
4201
+ h,
4202
+ h,
4203
+ h,
4204
+ h,
4205
+ -h,
4206
+ -h,
4207
+ h,
4208
+ h,
4209
+ h,
4210
+ h,
4211
+ -h,
4212
+ h,
4213
+ h,
4214
+ // -Z
4215
+ h,
4216
+ -h,
4217
+ -h,
4218
+ -h,
4219
+ -h,
4220
+ -h,
4221
+ -h,
4222
+ h,
4223
+ -h,
4224
+ h,
4225
+ -h,
4226
+ -h,
4227
+ -h,
4228
+ h,
4229
+ -h,
4230
+ h,
4231
+ h,
4232
+ -h,
4233
+ // +Y
4234
+ -h,
4235
+ h,
4236
+ h,
4237
+ h,
4238
+ h,
4239
+ h,
4240
+ h,
4241
+ h,
4242
+ -h,
4243
+ -h,
4244
+ h,
4245
+ h,
4246
+ h,
4247
+ h,
4248
+ -h,
4249
+ -h,
4250
+ h,
4251
+ -h,
4252
+ // -Y
4253
+ -h,
4254
+ -h,
4255
+ -h,
4256
+ h,
4257
+ -h,
4258
+ -h,
4259
+ h,
4260
+ -h,
4261
+ h,
4262
+ -h,
4263
+ -h,
4264
+ -h,
4265
+ h,
4266
+ -h,
4267
+ h,
4268
+ -h,
4269
+ -h,
4270
+ h,
4271
+ // +X
4272
+ h,
4273
+ -h,
4274
+ h,
4275
+ h,
4276
+ -h,
4277
+ -h,
4278
+ h,
4279
+ h,
4280
+ -h,
4281
+ h,
4282
+ -h,
4283
+ h,
4284
+ h,
4285
+ h,
4286
+ -h,
4287
+ h,
4288
+ h,
4289
+ h,
4290
+ // -X
4291
+ -h,
4292
+ -h,
4293
+ -h,
4294
+ -h,
4295
+ -h,
4296
+ h,
4297
+ -h,
4298
+ h,
4299
+ h,
4300
+ -h,
4301
+ -h,
4302
+ -h,
4303
+ -h,
4304
+ h,
4305
+ h,
4306
+ -h,
4307
+ h,
4308
+ -h
4309
+ ]);
4310
+ const normals = new Float32Array([
4311
+ 0,
4312
+ 0,
4313
+ 1,
4314
+ 0,
4315
+ 0,
4316
+ 1,
4317
+ 0,
4318
+ 0,
4319
+ 1,
4320
+ 0,
4321
+ 0,
4322
+ 1,
4323
+ 0,
4324
+ 0,
4325
+ 1,
4326
+ 0,
4327
+ 0,
4328
+ 1,
4329
+ 0,
4330
+ 0,
4331
+ -1,
4332
+ 0,
4333
+ 0,
4334
+ -1,
4335
+ 0,
4336
+ 0,
4337
+ -1,
4338
+ 0,
4339
+ 0,
4340
+ -1,
4341
+ 0,
4342
+ 0,
4343
+ -1,
4344
+ 0,
4345
+ 0,
4346
+ -1,
4347
+ 0,
4348
+ 1,
4349
+ 0,
4350
+ 0,
4351
+ 1,
4352
+ 0,
4353
+ 0,
4354
+ 1,
4355
+ 0,
4356
+ 0,
4357
+ 1,
4358
+ 0,
4359
+ 0,
4360
+ 1,
4361
+ 0,
4362
+ 0,
4363
+ 1,
4364
+ 0,
4365
+ 0,
4366
+ -1,
4367
+ 0,
4368
+ 0,
4369
+ -1,
4370
+ 0,
4371
+ 0,
4372
+ -1,
4373
+ 0,
4374
+ 0,
4375
+ -1,
4376
+ 0,
4377
+ 0,
4378
+ -1,
4379
+ 0,
4380
+ 0,
4381
+ -1,
4382
+ 0,
4383
+ 1,
4384
+ 0,
4385
+ 0,
4386
+ 1,
4387
+ 0,
4388
+ 0,
4389
+ 1,
4390
+ 0,
4391
+ 0,
4392
+ 1,
4393
+ 0,
4394
+ 0,
4395
+ 1,
4396
+ 0,
4397
+ 0,
4398
+ 1,
4399
+ 0,
4400
+ 0,
4401
+ -1,
4402
+ 0,
4403
+ 0,
4404
+ -1,
4405
+ 0,
4406
+ 0,
4407
+ -1,
4408
+ 0,
4409
+ 0,
4410
+ -1,
4411
+ 0,
4412
+ 0,
4413
+ -1,
4414
+ 0,
4415
+ 0,
4416
+ -1,
4417
+ 0,
4418
+ 0
4419
+ ]);
4420
+ return this.loadModel({ positions, normals });
4421
+ }
4131
4422
  /**
4132
4423
  * Register a model. Returns a handle for addInstance().
4133
4424
  *
@@ -4443,18 +4734,22 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4443
4734
  * with another instance (e.g., when spawning all parts of a character).
4444
4735
  */
4445
4736
  addInstance(opts) {
4737
+ const userPrefabId = isPrefab3D(opts.model) ? opts.model.id : null;
4738
+ if (isPrefab3D(opts.model) && opts.model.type === "composite") {
4739
+ return this.addCompositeInstance(opts, opts.model);
4740
+ }
4446
4741
  const modelOrGltf = isPrefab3D(opts.model) ? resolvePrefabHandle(opts.model) : opts.model;
4447
4742
  if ("parts" in modelOrGltf) {
4448
- return this.addGltfInstance(opts, modelOrGltf);
4743
+ return this.addGltfInstance(opts, modelOrGltf, userPrefabId);
4449
4744
  }
4450
4745
  const modelHandle = modelOrGltf;
4451
4746
  const model = this.models[modelHandle.id];
4452
4747
  if (model?.skinned) {
4453
- return this.addSkinnedInstance(opts, modelHandle, model.skinIndex);
4748
+ return this.addSkinnedInstance(opts, modelHandle, model.skinIndex, void 0, userPrefabId);
4454
4749
  }
4455
4750
  const slot = this.freeList.allocate();
4456
4751
  if (slot === -1)
4457
- throw new Error(`Max instances (${this.maxModels}) reached`);
4752
+ throw new Error(`Max instances (${this.maxInstances}) reached`);
4458
4753
  const dynBase = slot * DYNAMIC_MESH_FLOATS;
4459
4754
  const statBase = slot * STATIC_MESH_FLOATS;
4460
4755
  const t = resolveTransform(opts);
@@ -4481,8 +4776,16 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4481
4776
  this.batcher.add(0, modelHandle.id, slot);
4482
4777
  const dynamicData = this.dynamicData;
4483
4778
  const staticData = this.staticData;
4484
- return {
4779
+ const self = this;
4780
+ let destroyed = false;
4781
+ const posOut = [0, 0, 0];
4782
+ const rotOut = [0, 0, 0];
4783
+ const sclOut = [0, 0, 0];
4784
+ const handle = {
4785
+ slot,
4786
+ modelId: modelHandle.id,
4485
4787
  skinned: false,
4788
+ prefabId: userPrefabId,
4486
4789
  setPosition(nx, ny, nz) {
4487
4790
  dynamicData[dynBase + DYN_CURR_PX] = nx;
4488
4791
  dynamicData[dynBase + DYN_CURR_PY] = ny;
@@ -4497,10 +4800,47 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4497
4800
  staticData[statBase + STAT_SX] = nx;
4498
4801
  staticData[statBase + STAT_SY] = ny;
4499
4802
  staticData[statBase + STAT_SZ] = nz;
4803
+ },
4804
+ teleport(nx, ny, nz) {
4805
+ dynamicData[dynBase + DYN_PREV_PX] = nx;
4806
+ dynamicData[dynBase + DYN_PREV_PY] = ny;
4807
+ dynamicData[dynBase + DYN_PREV_PZ] = nz;
4808
+ dynamicData[dynBase + DYN_CURR_PX] = nx;
4809
+ dynamicData[dynBase + DYN_CURR_PY] = ny;
4810
+ dynamicData[dynBase + DYN_CURR_PZ] = nz;
4811
+ },
4812
+ get position() {
4813
+ posOut[0] = dynamicData[dynBase + DYN_CURR_PX];
4814
+ posOut[1] = dynamicData[dynBase + DYN_CURR_PY];
4815
+ posOut[2] = dynamicData[dynBase + DYN_CURR_PZ];
4816
+ return posOut;
4817
+ },
4818
+ get rotation() {
4819
+ rotOut[0] = dynamicData[dynBase + DYN_CURR_RX];
4820
+ rotOut[1] = dynamicData[dynBase + DYN_CURR_RY];
4821
+ rotOut[2] = dynamicData[dynBase + DYN_CURR_RZ];
4822
+ return rotOut;
4823
+ },
4824
+ get scale() {
4825
+ sclOut[0] = staticData[statBase + STAT_SX];
4826
+ sclOut[1] = staticData[statBase + STAT_SY];
4827
+ sclOut[2] = staticData[statBase + STAT_SZ];
4828
+ return sclOut;
4829
+ },
4830
+ destroy() {
4831
+ if (destroyed)
4832
+ return;
4833
+ destroyed = true;
4834
+ self.batcher.remove(0, modelHandle.id, slot);
4835
+ self.freeList.free(slot);
4836
+ dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
4837
+ staticData.fill(0, statBase, statBase + STATIC_MESH_FLOATS);
4838
+ self.staticDirty = true;
4500
4839
  }
4501
4840
  };
4841
+ return handle;
4502
4842
  }
4503
- addGltfInstance(opts, gltf) {
4843
+ addGltfInstance(opts, gltf, prefabId) {
4504
4844
  const childHandles = [];
4505
4845
  let firstSkinnedSlot;
4506
4846
  for (const part of gltf.parts) {
@@ -4508,7 +4848,7 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4508
4848
  const model = this.models[part.id];
4509
4849
  let handle;
4510
4850
  if (model?.skinned) {
4511
- handle = this.addSkinnedInstance(partOpts, part, model.skinIndex, firstSkinnedSlot);
4851
+ handle = this.addSkinnedInstance(partOpts, part, model.skinIndex, firstSkinnedSlot, prefabId);
4512
4852
  if (firstSkinnedSlot === void 0)
4513
4853
  firstSkinnedSlot = handle.slot;
4514
4854
  } else {
@@ -4517,8 +4857,10 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4517
4857
  childHandles.push(handle);
4518
4858
  }
4519
4859
  const skinnedHandle = childHandles.find((h) => h.skinned);
4860
+ const lead = childHandles[0];
4520
4861
  return {
4521
4862
  skinned: gltf.skinned,
4863
+ prefabId,
4522
4864
  setPosition(x, y, z) {
4523
4865
  for (const h of childHandles)
4524
4866
  h.setPosition(x, y, z);
@@ -4531,15 +4873,131 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4531
4873
  for (const h of childHandles)
4532
4874
  h.setScale(x, y, z);
4533
4875
  },
4876
+ teleport(x, y, z) {
4877
+ for (const h of childHandles)
4878
+ h.teleport(x, y, z);
4879
+ },
4880
+ get position() {
4881
+ return lead.position;
4882
+ },
4883
+ get rotation() {
4884
+ return lead.rotation;
4885
+ },
4886
+ get scale() {
4887
+ return lead.scale;
4888
+ },
4534
4889
  play: skinnedHandle?.play ? (name, opts2) => {
4535
4890
  skinnedHandle.play(name, opts2);
4536
4891
  } : void 0,
4537
4892
  stop: skinnedHandle?.stop ? () => {
4538
4893
  skinnedHandle.stop();
4539
- } : void 0
4894
+ } : void 0,
4895
+ destroy() {
4896
+ for (const h of childHandles)
4897
+ h.destroy();
4898
+ }
4899
+ };
4900
+ }
4901
+ /**
4902
+ * Spawn a composite prefab by spawning each of its parts at the composed
4903
+ * (instance + offset) transform. The returned handle broadcasts subsequent
4904
+ * `setPosition` / `setRotation` to every child, keeping each child's
4905
+ * baked offset applied on top of the new value.
4906
+ */
4907
+ addCompositeInstance(opts, composite) {
4908
+ const bucket = this._prefabs;
4909
+ if (!bucket) {
4910
+ throw new Error(
4911
+ `addInstance: composite '${composite.id}' requires the renderer to be constructed with the bucket (\`prefabs\`).`
4912
+ );
4913
+ }
4914
+ const basePos = opts.position ?? [0, 0, 0];
4915
+ const baseRot = opts.rotation ?? [0, 0, 0];
4916
+ const offsets = composite.parts.map((p) => ({
4917
+ px: p.offset?.position?.[0] ?? 0,
4918
+ py: p.offset?.position?.[1] ?? 0,
4919
+ pz: p.offset?.position?.[2] ?? 0,
4920
+ rx: p.offset?.rotation?.[0] ?? 0,
4921
+ ry: p.offset?.rotation?.[1] ?? 0,
4922
+ rz: p.offset?.rotation?.[2] ?? 0
4923
+ }));
4924
+ const childHandles = [];
4925
+ const posOut = [basePos[0], basePos[1], basePos[2]];
4926
+ const rotOut = [baseRot[0], baseRot[1], baseRot[2]];
4927
+ const sclOut = [1, 1, 1];
4928
+ const initialScale = opts.scale;
4929
+ if (typeof initialScale === "number") {
4930
+ sclOut[0] = sclOut[1] = sclOut[2] = initialScale;
4931
+ } else if (initialScale) {
4932
+ sclOut[0] = initialScale[0];
4933
+ sclOut[1] = initialScale[1];
4934
+ sclOut[2] = initialScale[2];
4935
+ }
4936
+ for (let i = 0; i < composite.parts.length; i++) {
4937
+ const part = composite.parts[i];
4938
+ const off = offsets[i];
4939
+ const partPrefab = bucket.get(part.partId);
4940
+ const partOpts = {
4941
+ ...opts,
4942
+ model: partPrefab,
4943
+ position: [basePos[0] + off.px, basePos[1] + off.py, basePos[2] + off.pz],
4944
+ rotation: [baseRot[0] + off.rx, baseRot[1] + off.ry, baseRot[2] + off.rz]
4945
+ };
4946
+ childHandles.push(this.addInstance(partOpts));
4947
+ }
4948
+ return {
4949
+ skinned: childHandles.some((h) => h.skinned),
4950
+ prefabId: composite.id,
4951
+ setPosition(x, y, z) {
4952
+ posOut[0] = x;
4953
+ posOut[1] = y;
4954
+ posOut[2] = z;
4955
+ for (let i = 0; i < childHandles.length; i++) {
4956
+ const o = offsets[i];
4957
+ childHandles[i].setPosition(x + o.px, y + o.py, z + o.pz);
4958
+ }
4959
+ },
4960
+ setRotation(x, y, z) {
4961
+ rotOut[0] = x;
4962
+ rotOut[1] = y;
4963
+ rotOut[2] = z;
4964
+ for (let i = 0; i < childHandles.length; i++) {
4965
+ const o = offsets[i];
4966
+ childHandles[i].setRotation(x + o.rx, y + o.ry, z + o.rz);
4967
+ }
4968
+ },
4969
+ setScale(x, y, z) {
4970
+ sclOut[0] = x;
4971
+ sclOut[1] = y;
4972
+ sclOut[2] = z;
4973
+ for (const h of childHandles)
4974
+ h.setScale(x, y, z);
4975
+ },
4976
+ teleport(x, y, z) {
4977
+ posOut[0] = x;
4978
+ posOut[1] = y;
4979
+ posOut[2] = z;
4980
+ for (let i = 0; i < childHandles.length; i++) {
4981
+ const o = offsets[i];
4982
+ childHandles[i].teleport(x + o.px, y + o.py, z + o.pz);
4983
+ }
4984
+ },
4985
+ get position() {
4986
+ return posOut;
4987
+ },
4988
+ get rotation() {
4989
+ return rotOut;
4990
+ },
4991
+ get scale() {
4992
+ return sclOut;
4993
+ },
4994
+ destroy() {
4995
+ for (const h of childHandles)
4996
+ h.destroy();
4997
+ }
4540
4998
  };
4541
4999
  }
4542
- addSkinnedInstance(opts, modelHandle, skinIndex, linkedSlot) {
5000
+ addSkinnedInstance(opts, modelHandle, skinIndex, linkedSlot, prefabId = null) {
4543
5001
  const slot = this.skinnedFreeList.allocate();
4544
5002
  if (slot === -1)
4545
5003
  throw new Error(`Max skinned instances (${this.maxSkinnedInstances}) reached`);
@@ -4550,11 +5008,27 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4550
5008
  if (linkedSlot !== void 0) {
4551
5009
  boneOffset = this.skinnedInstanceBoneOffsets[linkedSlot];
4552
5010
  animState = this.skinnedAnimStates[linkedSlot];
5011
+ this.boneOffsetRefcount[boneOffset]++;
4553
5012
  } else {
4554
- boneOffset = this.nextBoneOffset + jointCount;
4555
- this.nextBoneOffset += jointCount * 2;
4556
- skinModel.animation.computeRestPose(this.boneMatrixData, boneOffset * 16);
4557
- this.boneMatrixDirty = true;
5013
+ const pool = this.freedBoneOffsets.get(skinIndex);
5014
+ if (pool && pool.length > 0) {
5015
+ boneOffset = pool.pop();
5016
+ } else {
5017
+ boneOffset = this.nextBoneOffset + jointCount;
5018
+ this.nextBoneOffset += jointCount * 2;
5019
+ }
5020
+ this.boneOffsetRefcount[boneOffset] = 1;
5021
+ this.boneOffsetSkinIndex[boneOffset] = skinIndex;
5022
+ const restOffsetFloats = boneOffset * 16;
5023
+ const restLengthFloats = jointCount * 16;
5024
+ skinModel.animation.computeRestPose(this.boneMatrixData, restOffsetFloats);
5025
+ this.device.queue.writeBuffer(
5026
+ this.rawBoneMatrixBuffer,
5027
+ restOffsetFloats * 4,
5028
+ this.boneMatrixData.buffer,
5029
+ this.boneMatrixData.byteOffset + restOffsetFloats * 4,
5030
+ restLengthFloats * 4
5031
+ );
4558
5032
  animState = skinModel.animation.clipCount > 0 ? skinModel.animation.createState(0, 1, true) : null;
4559
5033
  }
4560
5034
  this.skinnedInstanceBoneOffsets[slot] = boneOffset;
@@ -4580,11 +5054,7 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4580
5054
  this.skinnedStaticData[statBase + SSTAT_CR] = t.cr;
4581
5055
  this.skinnedStaticData[statBase + SSTAT_CG] = t.cg;
4582
5056
  this.skinnedStaticData[statBase + SSTAT_CB] = t.cb;
4583
- new DataView(this.skinnedStaticData.buffer).setUint32(
4584
- (statBase + SSTAT_BONE_OFFSET) * 4,
4585
- boneOffset,
4586
- true
4587
- );
5057
+ this.skinnedStaticDV.setUint32((statBase + SSTAT_BONE_OFFSET) * 4, boneOffset, true);
4588
5058
  this.skinnedStaticDirty = true;
4589
5059
  this.skinnedInstanceModelIds[slot] = modelHandle.id;
4590
5060
  this.skinnedBatcher.add(0, modelHandle.id, slot);
@@ -4592,10 +5062,18 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4592
5062
  const staticData = this.skinnedStaticData;
4593
5063
  const animStates = this.skinnedAnimStates;
4594
5064
  const animation = skinModel.animation;
5065
+ const self = this;
5066
+ const capturedBoneOffset = boneOffset;
5067
+ const capturedSkinIndex = skinIndex;
5068
+ let destroyed = false;
5069
+ const posOut = [0, 0, 0];
5070
+ const rotOut = [0, 0, 0];
5071
+ const sclOut = [0, 0, 0];
4595
5072
  return {
4596
5073
  slot,
4597
5074
  modelId: modelHandle.id,
4598
5075
  skinned: true,
5076
+ prefabId,
4599
5077
  setPosition(nx, ny, nz) {
4600
5078
  dynamicData[dynBase + DYN_CURR_PX] = nx;
4601
5079
  dynamicData[dynBase + DYN_CURR_PY] = ny;
@@ -4611,6 +5089,32 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4611
5089
  staticData[statBase + SSTAT_SY] = ny;
4612
5090
  staticData[statBase + SSTAT_SZ] = nz;
4613
5091
  },
5092
+ teleport(nx, ny, nz) {
5093
+ dynamicData[dynBase + DYN_PREV_PX] = nx;
5094
+ dynamicData[dynBase + DYN_PREV_PY] = ny;
5095
+ dynamicData[dynBase + DYN_PREV_PZ] = nz;
5096
+ dynamicData[dynBase + DYN_CURR_PX] = nx;
5097
+ dynamicData[dynBase + DYN_CURR_PY] = ny;
5098
+ dynamicData[dynBase + DYN_CURR_PZ] = nz;
5099
+ },
5100
+ get position() {
5101
+ posOut[0] = dynamicData[dynBase + DYN_CURR_PX];
5102
+ posOut[1] = dynamicData[dynBase + DYN_CURR_PY];
5103
+ posOut[2] = dynamicData[dynBase + DYN_CURR_PZ];
5104
+ return posOut;
5105
+ },
5106
+ get rotation() {
5107
+ rotOut[0] = dynamicData[dynBase + DYN_CURR_RX];
5108
+ rotOut[1] = dynamicData[dynBase + DYN_CURR_RY];
5109
+ rotOut[2] = dynamicData[dynBase + DYN_CURR_RZ];
5110
+ return rotOut;
5111
+ },
5112
+ get scale() {
5113
+ sclOut[0] = staticData[statBase + SSTAT_SX];
5114
+ sclOut[1] = staticData[statBase + SSTAT_SY];
5115
+ sclOut[2] = staticData[statBase + SSTAT_SZ];
5116
+ return sclOut;
5117
+ },
4614
5118
  play(name, opts2) {
4615
5119
  const state = animStates[slot];
4616
5120
  if (state)
@@ -4620,6 +5124,25 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4620
5124
  const state = animStates[slot];
4621
5125
  if (state)
4622
5126
  animation.stop(state);
5127
+ },
5128
+ destroy() {
5129
+ if (destroyed)
5130
+ return;
5131
+ destroyed = true;
5132
+ self.skinnedBatcher.remove(0, modelHandle.id, slot);
5133
+ self.skinnedFreeList.free(slot);
5134
+ dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
5135
+ staticData.fill(0, statBase, statBase + SKINNED_STATIC_MESH_FLOATS);
5136
+ animStates[slot] = null;
5137
+ self.skinnedStaticDirty = true;
5138
+ if (--self.boneOffsetRefcount[capturedBoneOffset] === 0) {
5139
+ let pool = self.freedBoneOffsets.get(capturedSkinIndex);
5140
+ if (!pool) {
5141
+ pool = [];
5142
+ self.freedBoneOffsets.set(capturedSkinIndex, pool);
5143
+ }
5144
+ pool.push(capturedBoneOffset);
5145
+ }
4623
5146
  }
4624
5147
  };
4625
5148
  }
@@ -4705,6 +5228,31 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4705
5228
  this.animJointLookupOffset = pb.jointLookupOffset;
4706
5229
  return true;
4707
5230
  }
5231
+ /**
5232
+ * Returns true if a skinned instance's bone-matrix compute should be
5233
+ * dispatched this frame. Combines frustum culling (scaled bounding sphere)
5234
+ * and a configurable distance cull from the camera. Callers pass the
5235
+ * camera position + cullDistSq once outside the loop to avoid re-reading.
5236
+ */
5237
+ shouldDispatchSkinning(slot, model, camX, camY, camZ, cullDistSq) {
5238
+ const skinModel = model && model.skinIndex >= 0 ? this.skinnedModels[model.skinIndex] : null;
5239
+ const baseRadius = skinModel?.boundingRadius ?? 10;
5240
+ const base = slot * DYNAMIC_MESH_FLOATS;
5241
+ const sBase = slot * SKINNED_STATIC_MESH_FLOATS;
5242
+ const cx = this.skinnedDynamicData[base + DYN_CURR_PX];
5243
+ const cy = this.skinnedDynamicData[base + DYN_CURR_PY];
5244
+ const cz = this.skinnedDynamicData[base + DYN_CURR_PZ];
5245
+ const sx = Math.abs(this.skinnedStaticData[sBase + SSTAT_SX]);
5246
+ const sy = Math.abs(this.skinnedStaticData[sBase + SSTAT_SY]);
5247
+ const sz = Math.abs(this.skinnedStaticData[sBase + SSTAT_SZ]);
5248
+ const maxScale = sx > sy ? sx > sz ? sx : sz : sy > sz ? sy : sz;
5249
+ if (!this.isInFrustum(cx, cy, cz, baseRadius * maxScale))
5250
+ return false;
5251
+ const dxv = cx - camX, dyv = cy - camY, dzv = cz - camZ;
5252
+ if (dxv * dxv + dyv * dyv + dzv * dzv > cullDistSq)
5253
+ return false;
5254
+ return true;
5255
+ }
4708
5256
  updateAnimations(deltaTime) {
4709
5257
  this.syncLazyAnimationChanges();
4710
5258
  if (this.animComputeNeedsRebuild && this.packedAnimData.clips.length > 0) {
@@ -4748,6 +5296,9 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4748
5296
  this.updatedBoneOffsets.fill(0);
4749
5297
  let count = 0;
4750
5298
  const dv = this.gpuInstDV;
5299
+ const camPos = this.camera.position;
5300
+ const camX = camPos[0], camY = camPos[1], camZ = camPos[2];
5301
+ const cullDistSq = this.animationCullDistanceSq;
4751
5302
  for (let slot = 0; slot < this.maxSkinnedInstances; slot++) {
4752
5303
  const animState = this.skinnedAnimStates[slot];
4753
5304
  if (!animState)
@@ -4786,6 +5337,8 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4786
5337
  const modelId = this.skinnedInstanceModelIds[slot];
4787
5338
  const model = this.models[modelId];
4788
5339
  const skinIdx = model?.skinIndex ?? 0;
5340
+ if (!this.shouldDispatchSkinning(slot, model, camX, camY, camZ, cullDistSq))
5341
+ continue;
4789
5342
  const off = count * 32;
4790
5343
  dv.setInt32(off, animState.clipId, true);
4791
5344
  dv.setFloat32(off + 4, animState.time, true);
@@ -4830,14 +5383,12 @@ var WebGPU3DRenderer = class extends import_renderer4.Base3DRenderer {
4830
5383
  }
4831
5384
  }
4832
5385
  }
5386
+ /**
5387
+ * Free an instance's renderer slot. Equivalent to `handle.destroy()` -
5388
+ * kept as a convenience for direct lookup. Safe to call multiple times.
5389
+ */
4833
5390
  removeInstance(handle) {
4834
- this.batcher.remove(0, handle.modelId, handle.slot);
4835
- this.freeList.free(handle.slot);
4836
- const dynBase = handle.slot * DYNAMIC_MESH_FLOATS;
4837
- const statBase = handle.slot * STATIC_MESH_FLOATS;
4838
- this.dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
4839
- this.staticData.fill(0, statBase, statBase + STATIC_MESH_FLOATS);
4840
- this.staticDirty = true;
5391
+ handle.destroy();
4841
5392
  }
4842
5393
  storePreviousState() {
4843
5394
  this.camera.storePrevious();