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.
- package/README.md +15 -1
- package/dist/cjs/core/binary-codec/binary-codec.js +1 -1
- package/dist/cjs/core/driver/driver.js +1 -1
- package/dist/cjs/core/driver/drivers/immediate.js +1 -1
- package/dist/cjs/core/driver/drivers/raf.js +1 -1
- package/dist/cjs/core/driver/drivers/timeout.js +1 -1
- package/dist/cjs/core/input/index.js +1 -1
- package/dist/cjs/core/input/mouse-look/index.js +1 -0
- package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
- package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
- package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
- package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -1
- package/dist/cjs/ecs/component.js +1 -1
- package/dist/cjs/ecs/system-builder.js +1 -1
- package/dist/cjs/ecs/world.js +1 -1
- package/dist/cjs/game/loop/loop.js +1 -1
- package/dist/cjs/net/adapters/bun-websocket.js +1 -1
- package/dist/cjs/renderer/base/renderer-3d.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
- package/dist/esm/core/binary-codec/binary-codec.js +1 -1
- package/dist/esm/core/driver/drivers/immediate.js +1 -1
- package/dist/esm/core/driver/drivers/raf.js +1 -1
- package/dist/esm/core/driver/drivers/timeout.js +1 -1
- package/dist/esm/core/input/index.js +1 -1
- package/dist/esm/core/input/mouse-look/index.js +1 -0
- package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
- package/dist/esm/core/input/scroll-zoom/index.js +1 -0
- package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
- package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -1
- package/dist/esm/ecs/component.js +1 -1
- package/dist/esm/ecs/system-builder.js +1 -1
- package/dist/esm/ecs/world.js +1 -1
- package/dist/esm/game/loop/loop.js +1 -1
- package/dist/esm/net/adapters/bun-websocket.js +1 -1
- package/dist/esm/renderer/base/renderer-3d.js +1 -1
- package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
- package/dist/esm/renderer/prefab-bucket/index.js +1 -1
- package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
- package/dist/netcode/cjs/index.js +1552 -0
- package/dist/netcode/esm/index.js +1530 -0
- package/dist/netcode/types/client/game-client.d.ts +125 -0
- package/dist/netcode/types/client/index.d.ts +1 -0
- package/dist/netcode/types/client/interpolation-buffer.d.ts +37 -0
- package/dist/netcode/types/client/interpolation-buffer.test.d.ts +1 -0
- package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
- package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
- package/dist/netcode/types/codec/index.d.ts +1 -0
- package/dist/netcode/types/components/index.d.ts +1 -0
- package/dist/netcode/types/components/sync-spec.d.ts +43 -0
- package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
- package/dist/netcode/types/ctx.d.ts +105 -0
- package/dist/netcode/types/ctx.test.d.ts +1 -0
- package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
- package/dist/netcode/types/handlers/index.d.ts +1 -0
- package/dist/netcode/types/index.d.ts +11 -0
- package/dist/netcode/types/integration.test.d.ts +1 -0
- package/dist/netcode/types/intents/define-intents.d.ts +53 -0
- package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
- package/dist/netcode/types/intents/index.d.ts +1 -0
- package/dist/netcode/types/network/base.d.ts +120 -0
- package/dist/netcode/types/network/index.d.ts +2 -0
- package/dist/netcode/types/network/transport.d.ts +1 -0
- package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
- package/dist/netcode/types/packets/harness.d.ts +103 -0
- package/dist/netcode/types/packets/index.d.ts +2 -0
- package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
- package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
- package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
- package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
- package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
- package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
- package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
- package/dist/netcode/types/predictions/index.d.ts +1 -0
- package/dist/netcode/types/reconciliation.test.d.ts +1 -0
- package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
- package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
- package/dist/netcode/types/rpcs/index.d.ts +1 -0
- package/dist/netcode/types/server/game-server.d.ts +77 -0
- package/dist/netcode/types/server/index.d.ts +2 -0
- package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
- package/dist/netcode/types/server/plugins/index.d.ts +3 -0
- package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
- package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
- package/dist/netcode/types/tick-rate.test.d.ts +1 -0
- package/dist/netcode/types/transports/index.d.ts +1 -0
- package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
- package/dist/netcode/types/types.test.d.ts +1 -0
- package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
- package/dist/types/core/driver/driver.d.ts +8 -8
- package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
- package/dist/types/core/driver/drivers/raf.d.ts +17 -6
- package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
- package/dist/types/core/input/index.d.ts +2 -0
- package/dist/types/core/input/mouse-look/index.d.ts +1 -0
- package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
- package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
- package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
- package/dist/types/ecs/component.d.ts +67 -11
- package/dist/types/ecs/entity-handle.d.ts +5 -5
- package/dist/types/ecs/system-builder.d.ts +13 -0
- package/dist/types/ecs/world.d.ts +72 -4
- package/dist/types/game/loop/loop.d.ts +21 -2
- package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
- package/dist/types/renderer/base/renderer-3d.d.ts +1 -1
- package/dist/types/renderer/prefab-bucket/concrete.d.ts +42 -2
- package/dist/types/renderer/prefab-bucket/index.d.ts +12 -2
- package/dist/types/renderer/prefab-bucket/specs.d.ts +46 -3
- package/dist/types/renderer/types.d.ts +5 -3
- package/dist/webgpu/cjs/index.js +591 -40
- package/dist/webgpu/esm/index.js +591 -40
- package/dist/webgpu/types/3d/renderer.d.ts +111 -5
- package/package.json +6 -1
package/dist/webgpu/cjs/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
3806
|
-
super(canvas, { ...options,
|
|
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
|
-
|
|
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.
|
|
3847
|
-
this.
|
|
3848
|
-
this.
|
|
3849
|
-
this.
|
|
3850
|
-
this.
|
|
3851
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
3932
|
-
this.staticBuffer = this.root.createBuffer(d.arrayOf(StaticMesh, this.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|