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/esm/index.js
CHANGED
|
@@ -3656,7 +3656,8 @@ var SSTAT_CB = 5;
|
|
|
3656
3656
|
var SSTAT_BONE_OFFSET = 6;
|
|
3657
3657
|
var prefabHandles = /* @__PURE__ */ new WeakMap();
|
|
3658
3658
|
function isPrefab3D(value) {
|
|
3659
|
-
|
|
3659
|
+
const t = value.type;
|
|
3660
|
+
return t === "gltf" || t === "grid" || t === "cube" || t === "composite";
|
|
3660
3661
|
}
|
|
3661
3662
|
function resolveTransform(opts) {
|
|
3662
3663
|
const [px, py, pz] = opts.position ?? [0, 0, 0];
|
|
@@ -3690,8 +3691,8 @@ function computeBucketStats(bucket) {
|
|
|
3690
3691
|
}
|
|
3691
3692
|
var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
3692
3693
|
constructor(canvas, options) {
|
|
3693
|
-
const
|
|
3694
|
-
super(canvas, { ...options,
|
|
3694
|
+
const resolvedMaxInstances = options.maxInstances ?? (options.prefabs ? options.prefabs.size + 16 : 32);
|
|
3695
|
+
super(canvas, { ...options, maxInstances: resolvedMaxInstances });
|
|
3695
3696
|
this.resizeObserver = null;
|
|
3696
3697
|
this.resizeCallbacks = [];
|
|
3697
3698
|
// layer=0, sheetId=modelId
|
|
@@ -3717,6 +3718,8 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3717
3718
|
this.skinnedStaticDirty = false;
|
|
3718
3719
|
this.skinnedAnimStates = [];
|
|
3719
3720
|
this.nextBoneOffset = 0;
|
|
3721
|
+
/** Reusable bone-offset blocks per skinIndex. Pushed on remove, popped on add. Indexed by skinIndex (low cardinality), so a Map of lists is fine. */
|
|
3722
|
+
this.freedBoneOffsets = /* @__PURE__ */ new Map();
|
|
3720
3723
|
// Frustum planes (6 planes × 4 floats each), extracted from VP matrix
|
|
3721
3724
|
this.frustumPlanes = new Float32Array(24);
|
|
3722
3725
|
this.uniformData = new Float32Array(24);
|
|
@@ -3726,22 +3729,26 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3726
3729
|
this._prefabs = options.prefabs ?? null;
|
|
3727
3730
|
const SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP = 3;
|
|
3728
3731
|
const bucketStats = this._prefabs ? computeBucketStats(this._prefabs) : null;
|
|
3729
|
-
|
|
3730
|
-
this.maxSkinnedInstances = options.maxSkinnedInstances ?? (bucketStats ? maxInstances * Math.max(1, Math.min(bucketStats.maxSkinnedParts, SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP)) : 5e3);
|
|
3732
|
+
this.maxSkinnedInstances = options.maxSkinnedInstances ?? (bucketStats ? resolvedMaxInstances * Math.max(1, Math.min(bucketStats.maxSkinnedParts, SKINNED_PARTS_PER_INSTANCE_DEFAULT_CAP)) : 5e3);
|
|
3731
3733
|
this.maxBonesPerSkin = options.maxBonesPerSkin ?? (bucketStats ? Math.max(1, bucketStats.maxJointCount) : 64);
|
|
3734
|
+
const cullDist = options.animationCullDistance ?? 50;
|
|
3735
|
+
this.animationCullDistanceSq = cullDist * cullDist;
|
|
3732
3736
|
this.maxTotalBones = this.maxSkinnedInstances * this.maxBonesPerSkin * 2;
|
|
3733
3737
|
this.updatedBoneOffsets = new Uint8Array(this.maxTotalBones);
|
|
3734
|
-
this.
|
|
3735
|
-
this.
|
|
3736
|
-
this.
|
|
3737
|
-
this.
|
|
3738
|
-
this.
|
|
3739
|
-
this.
|
|
3738
|
+
this.boneOffsetRefcount = new Uint32Array(this.maxTotalBones);
|
|
3739
|
+
this.boneOffsetSkinIndex = new Uint32Array(this.maxTotalBones);
|
|
3740
|
+
this.freeList = new FreeList3(resolvedMaxInstances);
|
|
3741
|
+
this.batcher = new SparseBatcher2(resolvedMaxInstances);
|
|
3742
|
+
this.dynamicData = new Float32Array(resolvedMaxInstances * DYNAMIC_MESH_FLOATS);
|
|
3743
|
+
this.staticData = new Float32Array(resolvedMaxInstances * STATIC_MESH_FLOATS);
|
|
3744
|
+
this.slotIndexData = new Uint32Array(resolvedMaxInstances);
|
|
3745
|
+
this.instanceModelIds = new Uint8Array(resolvedMaxInstances);
|
|
3740
3746
|
const msi = this.maxSkinnedInstances;
|
|
3741
3747
|
this.skinnedFreeList = new FreeList3(msi);
|
|
3742
3748
|
this.skinnedBatcher = new SparseBatcher2(msi);
|
|
3743
3749
|
this.skinnedDynamicData = new Float32Array(msi * DYNAMIC_MESH_FLOATS);
|
|
3744
3750
|
this.skinnedStaticData = new Float32Array(msi * SKINNED_STATIC_MESH_FLOATS);
|
|
3751
|
+
this.skinnedStaticDV = new DataView(this.skinnedStaticData.buffer);
|
|
3745
3752
|
this.skinnedSlotIndexData = new Uint32Array(msi);
|
|
3746
3753
|
this.skinnedInstanceModelIds = new Uint8Array(msi);
|
|
3747
3754
|
this.skinnedInstanceBoneOffsets = new Uint32Array(msi);
|
|
@@ -3752,7 +3759,19 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3752
3759
|
this.gpuInstDV = new DataView(this.gpuInstData.buffer);
|
|
3753
3760
|
}
|
|
3754
3761
|
async init() {
|
|
3755
|
-
|
|
3762
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
3763
|
+
if (!adapter)
|
|
3764
|
+
throw new Error("WebGPU3DRenderer: no GPU adapter available");
|
|
3765
|
+
const a = adapter.limits;
|
|
3766
|
+
const requiredLimits = {
|
|
3767
|
+
maxBufferSize: a.maxBufferSize,
|
|
3768
|
+
maxStorageBufferBindingSize: a.maxStorageBufferBindingSize,
|
|
3769
|
+
maxStorageBuffersPerShaderStage: a.maxStorageBuffersPerShaderStage,
|
|
3770
|
+
maxComputeWorkgroupStorageSize: a.maxComputeWorkgroupStorageSize,
|
|
3771
|
+
maxComputeInvocationsPerWorkgroup: a.maxComputeInvocationsPerWorkgroup
|
|
3772
|
+
};
|
|
3773
|
+
const device = await adapter.requestDevice({ requiredLimits });
|
|
3774
|
+
this.root = tgpu6.initFromDevice({ device });
|
|
3756
3775
|
this.device = this.root.device;
|
|
3757
3776
|
this.context = this.canvas.getContext("webgpu");
|
|
3758
3777
|
this.format = navigator.gpu.getPreferredCanvasFormat();
|
|
@@ -3769,7 +3788,7 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3769
3788
|
format: "depth24plus",
|
|
3770
3789
|
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
3771
3790
|
});
|
|
3772
|
-
this.meshLayout = createMeshLayout(this.
|
|
3791
|
+
this.meshLayout = createMeshLayout(this.maxInstances);
|
|
3773
3792
|
const depthStencil = {
|
|
3774
3793
|
format: "depth24plus",
|
|
3775
3794
|
depthWriteEnabled: true,
|
|
@@ -3816,10 +3835,10 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3816
3835
|
primitive,
|
|
3817
3836
|
depthStencil
|
|
3818
3837
|
});
|
|
3819
|
-
this.dynamicBuffer = this.root.createBuffer(d.arrayOf(DynamicMesh, this.
|
|
3820
|
-
this.staticBuffer = this.root.createBuffer(d.arrayOf(StaticMesh, this.
|
|
3838
|
+
this.dynamicBuffer = this.root.createBuffer(d.arrayOf(DynamicMesh, this.maxInstances)).$usage("storage");
|
|
3839
|
+
this.staticBuffer = this.root.createBuffer(d.arrayOf(StaticMesh, this.maxInstances)).$usage("storage");
|
|
3821
3840
|
this.uniformBuffer = this.root.createBuffer(MeshUniforms).$usage("uniform");
|
|
3822
|
-
this.slotIndexBuffer = this.root.createBuffer(d.arrayOf(d.u32, this.
|
|
3841
|
+
this.slotIndexBuffer = this.root.createBuffer(d.arrayOf(d.u32, this.maxInstances)).$usage("storage");
|
|
3823
3842
|
this.rawDynamicBuffer = this.root.unwrap(this.dynamicBuffer);
|
|
3824
3843
|
this.rawStaticBuffer = this.root.unwrap(this.staticBuffer);
|
|
3825
3844
|
this.rawUniformBuffer = this.root.unwrap(this.uniformBuffer);
|
|
@@ -3921,6 +3940,9 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3921
3940
|
lineWidth: prefab.lineWidth
|
|
3922
3941
|
});
|
|
3923
3942
|
prefabHandles.set(prefab, model);
|
|
3943
|
+
} else if (prefab.type === "cube") {
|
|
3944
|
+
const model = this.createCube({ size: prefab.size });
|
|
3945
|
+
prefabHandles.set(prefab, model);
|
|
3924
3946
|
}
|
|
3925
3947
|
}
|
|
3926
3948
|
}
|
|
@@ -3985,6 +4007,36 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
3985
4007
|
createCompute(name, options) {
|
|
3986
4008
|
return new ComputeBuilder(name, options, this.root);
|
|
3987
4009
|
}
|
|
4010
|
+
/**
|
|
4011
|
+
* Set the maximum distance (in world units) at which the renderer keeps
|
|
4012
|
+
* computing skeletal animation. Skinned instances farther than this are
|
|
4013
|
+
* still drawn, but with their last-computed bone matrices instead of
|
|
4014
|
+
* fresh ones, which saves GPU compute work. Their internal animation
|
|
4015
|
+
* clocks keep ticking on CPU, so when they come back into range they
|
|
4016
|
+
* resume in sync.
|
|
4017
|
+
*
|
|
4018
|
+
* Lower values trade visual smoothness on distant characters for FPS.
|
|
4019
|
+
* Pass `Infinity` to disable culling entirely (always animate).
|
|
4020
|
+
*
|
|
4021
|
+
* Safe to call any time; takes effect on the next frame.
|
|
4022
|
+
*
|
|
4023
|
+
* @param distance Max distance to animate at, in world units.
|
|
4024
|
+
*/
|
|
4025
|
+
setAnimationCullDistance(distance) {
|
|
4026
|
+
this.animationCullDistanceSq = distance * distance;
|
|
4027
|
+
}
|
|
4028
|
+
/** Current animation cull distance (in world units). See `setAnimationCullDistance`. */
|
|
4029
|
+
get animationCullDistance() {
|
|
4030
|
+
return Math.sqrt(this.animationCullDistanceSq);
|
|
4031
|
+
}
|
|
4032
|
+
/**
|
|
4033
|
+
* Max skinned instances the renderer was sized for at construction.
|
|
4034
|
+
* Independent budget from `maxInstances` since skinned characters use a
|
|
4035
|
+
* separate set of GPU buffers. Read-only.
|
|
4036
|
+
*/
|
|
4037
|
+
get maxSkinned() {
|
|
4038
|
+
return this.maxSkinnedInstances;
|
|
4039
|
+
}
|
|
3988
4040
|
/**
|
|
3989
4041
|
* Create a flat grid mesh on the XZ plane at Y=0.
|
|
3990
4042
|
*
|
|
@@ -4016,6 +4068,245 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4016
4068
|
indices: new Uint16Array(indices)
|
|
4017
4069
|
});
|
|
4018
4070
|
}
|
|
4071
|
+
/**
|
|
4072
|
+
* Create a unit-cube mesh centered at the origin. Pass `size` to scale the
|
|
4073
|
+
* edge length, or keep size = 1 and scale at the instance level.
|
|
4074
|
+
*
|
|
4075
|
+
* ```ts
|
|
4076
|
+
* const cube = renderer.createCube();
|
|
4077
|
+
* renderer.addInstance({ model: cube, color: [1, 0.5, 0.2], scale: 2 });
|
|
4078
|
+
* ```
|
|
4079
|
+
*/
|
|
4080
|
+
createCube(opts = {}) {
|
|
4081
|
+
const h = (opts.size ?? 1) / 2;
|
|
4082
|
+
const positions = new Float32Array([
|
|
4083
|
+
// +Z
|
|
4084
|
+
-h,
|
|
4085
|
+
-h,
|
|
4086
|
+
h,
|
|
4087
|
+
h,
|
|
4088
|
+
-h,
|
|
4089
|
+
h,
|
|
4090
|
+
h,
|
|
4091
|
+
h,
|
|
4092
|
+
h,
|
|
4093
|
+
-h,
|
|
4094
|
+
-h,
|
|
4095
|
+
h,
|
|
4096
|
+
h,
|
|
4097
|
+
h,
|
|
4098
|
+
h,
|
|
4099
|
+
-h,
|
|
4100
|
+
h,
|
|
4101
|
+
h,
|
|
4102
|
+
// -Z
|
|
4103
|
+
h,
|
|
4104
|
+
-h,
|
|
4105
|
+
-h,
|
|
4106
|
+
-h,
|
|
4107
|
+
-h,
|
|
4108
|
+
-h,
|
|
4109
|
+
-h,
|
|
4110
|
+
h,
|
|
4111
|
+
-h,
|
|
4112
|
+
h,
|
|
4113
|
+
-h,
|
|
4114
|
+
-h,
|
|
4115
|
+
-h,
|
|
4116
|
+
h,
|
|
4117
|
+
-h,
|
|
4118
|
+
h,
|
|
4119
|
+
h,
|
|
4120
|
+
-h,
|
|
4121
|
+
// +Y
|
|
4122
|
+
-h,
|
|
4123
|
+
h,
|
|
4124
|
+
h,
|
|
4125
|
+
h,
|
|
4126
|
+
h,
|
|
4127
|
+
h,
|
|
4128
|
+
h,
|
|
4129
|
+
h,
|
|
4130
|
+
-h,
|
|
4131
|
+
-h,
|
|
4132
|
+
h,
|
|
4133
|
+
h,
|
|
4134
|
+
h,
|
|
4135
|
+
h,
|
|
4136
|
+
-h,
|
|
4137
|
+
-h,
|
|
4138
|
+
h,
|
|
4139
|
+
-h,
|
|
4140
|
+
// -Y
|
|
4141
|
+
-h,
|
|
4142
|
+
-h,
|
|
4143
|
+
-h,
|
|
4144
|
+
h,
|
|
4145
|
+
-h,
|
|
4146
|
+
-h,
|
|
4147
|
+
h,
|
|
4148
|
+
-h,
|
|
4149
|
+
h,
|
|
4150
|
+
-h,
|
|
4151
|
+
-h,
|
|
4152
|
+
-h,
|
|
4153
|
+
h,
|
|
4154
|
+
-h,
|
|
4155
|
+
h,
|
|
4156
|
+
-h,
|
|
4157
|
+
-h,
|
|
4158
|
+
h,
|
|
4159
|
+
// +X
|
|
4160
|
+
h,
|
|
4161
|
+
-h,
|
|
4162
|
+
h,
|
|
4163
|
+
h,
|
|
4164
|
+
-h,
|
|
4165
|
+
-h,
|
|
4166
|
+
h,
|
|
4167
|
+
h,
|
|
4168
|
+
-h,
|
|
4169
|
+
h,
|
|
4170
|
+
-h,
|
|
4171
|
+
h,
|
|
4172
|
+
h,
|
|
4173
|
+
h,
|
|
4174
|
+
-h,
|
|
4175
|
+
h,
|
|
4176
|
+
h,
|
|
4177
|
+
h,
|
|
4178
|
+
// -X
|
|
4179
|
+
-h,
|
|
4180
|
+
-h,
|
|
4181
|
+
-h,
|
|
4182
|
+
-h,
|
|
4183
|
+
-h,
|
|
4184
|
+
h,
|
|
4185
|
+
-h,
|
|
4186
|
+
h,
|
|
4187
|
+
h,
|
|
4188
|
+
-h,
|
|
4189
|
+
-h,
|
|
4190
|
+
-h,
|
|
4191
|
+
-h,
|
|
4192
|
+
h,
|
|
4193
|
+
h,
|
|
4194
|
+
-h,
|
|
4195
|
+
h,
|
|
4196
|
+
-h
|
|
4197
|
+
]);
|
|
4198
|
+
const normals = new Float32Array([
|
|
4199
|
+
0,
|
|
4200
|
+
0,
|
|
4201
|
+
1,
|
|
4202
|
+
0,
|
|
4203
|
+
0,
|
|
4204
|
+
1,
|
|
4205
|
+
0,
|
|
4206
|
+
0,
|
|
4207
|
+
1,
|
|
4208
|
+
0,
|
|
4209
|
+
0,
|
|
4210
|
+
1,
|
|
4211
|
+
0,
|
|
4212
|
+
0,
|
|
4213
|
+
1,
|
|
4214
|
+
0,
|
|
4215
|
+
0,
|
|
4216
|
+
1,
|
|
4217
|
+
0,
|
|
4218
|
+
0,
|
|
4219
|
+
-1,
|
|
4220
|
+
0,
|
|
4221
|
+
0,
|
|
4222
|
+
-1,
|
|
4223
|
+
0,
|
|
4224
|
+
0,
|
|
4225
|
+
-1,
|
|
4226
|
+
0,
|
|
4227
|
+
0,
|
|
4228
|
+
-1,
|
|
4229
|
+
0,
|
|
4230
|
+
0,
|
|
4231
|
+
-1,
|
|
4232
|
+
0,
|
|
4233
|
+
0,
|
|
4234
|
+
-1,
|
|
4235
|
+
0,
|
|
4236
|
+
1,
|
|
4237
|
+
0,
|
|
4238
|
+
0,
|
|
4239
|
+
1,
|
|
4240
|
+
0,
|
|
4241
|
+
0,
|
|
4242
|
+
1,
|
|
4243
|
+
0,
|
|
4244
|
+
0,
|
|
4245
|
+
1,
|
|
4246
|
+
0,
|
|
4247
|
+
0,
|
|
4248
|
+
1,
|
|
4249
|
+
0,
|
|
4250
|
+
0,
|
|
4251
|
+
1,
|
|
4252
|
+
0,
|
|
4253
|
+
0,
|
|
4254
|
+
-1,
|
|
4255
|
+
0,
|
|
4256
|
+
0,
|
|
4257
|
+
-1,
|
|
4258
|
+
0,
|
|
4259
|
+
0,
|
|
4260
|
+
-1,
|
|
4261
|
+
0,
|
|
4262
|
+
0,
|
|
4263
|
+
-1,
|
|
4264
|
+
0,
|
|
4265
|
+
0,
|
|
4266
|
+
-1,
|
|
4267
|
+
0,
|
|
4268
|
+
0,
|
|
4269
|
+
-1,
|
|
4270
|
+
0,
|
|
4271
|
+
1,
|
|
4272
|
+
0,
|
|
4273
|
+
0,
|
|
4274
|
+
1,
|
|
4275
|
+
0,
|
|
4276
|
+
0,
|
|
4277
|
+
1,
|
|
4278
|
+
0,
|
|
4279
|
+
0,
|
|
4280
|
+
1,
|
|
4281
|
+
0,
|
|
4282
|
+
0,
|
|
4283
|
+
1,
|
|
4284
|
+
0,
|
|
4285
|
+
0,
|
|
4286
|
+
1,
|
|
4287
|
+
0,
|
|
4288
|
+
0,
|
|
4289
|
+
-1,
|
|
4290
|
+
0,
|
|
4291
|
+
0,
|
|
4292
|
+
-1,
|
|
4293
|
+
0,
|
|
4294
|
+
0,
|
|
4295
|
+
-1,
|
|
4296
|
+
0,
|
|
4297
|
+
0,
|
|
4298
|
+
-1,
|
|
4299
|
+
0,
|
|
4300
|
+
0,
|
|
4301
|
+
-1,
|
|
4302
|
+
0,
|
|
4303
|
+
0,
|
|
4304
|
+
-1,
|
|
4305
|
+
0,
|
|
4306
|
+
0
|
|
4307
|
+
]);
|
|
4308
|
+
return this.loadModel({ positions, normals });
|
|
4309
|
+
}
|
|
4019
4310
|
/**
|
|
4020
4311
|
* Register a model. Returns a handle for addInstance().
|
|
4021
4312
|
*
|
|
@@ -4331,18 +4622,22 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4331
4622
|
* with another instance (e.g., when spawning all parts of a character).
|
|
4332
4623
|
*/
|
|
4333
4624
|
addInstance(opts) {
|
|
4625
|
+
const userPrefabId = isPrefab3D(opts.model) ? opts.model.id : null;
|
|
4626
|
+
if (isPrefab3D(opts.model) && opts.model.type === "composite") {
|
|
4627
|
+
return this.addCompositeInstance(opts, opts.model);
|
|
4628
|
+
}
|
|
4334
4629
|
const modelOrGltf = isPrefab3D(opts.model) ? resolvePrefabHandle(opts.model) : opts.model;
|
|
4335
4630
|
if ("parts" in modelOrGltf) {
|
|
4336
|
-
return this.addGltfInstance(opts, modelOrGltf);
|
|
4631
|
+
return this.addGltfInstance(opts, modelOrGltf, userPrefabId);
|
|
4337
4632
|
}
|
|
4338
4633
|
const modelHandle = modelOrGltf;
|
|
4339
4634
|
const model = this.models[modelHandle.id];
|
|
4340
4635
|
if (model?.skinned) {
|
|
4341
|
-
return this.addSkinnedInstance(opts, modelHandle, model.skinIndex);
|
|
4636
|
+
return this.addSkinnedInstance(opts, modelHandle, model.skinIndex, void 0, userPrefabId);
|
|
4342
4637
|
}
|
|
4343
4638
|
const slot = this.freeList.allocate();
|
|
4344
4639
|
if (slot === -1)
|
|
4345
|
-
throw new Error(`Max instances (${this.
|
|
4640
|
+
throw new Error(`Max instances (${this.maxInstances}) reached`);
|
|
4346
4641
|
const dynBase = slot * DYNAMIC_MESH_FLOATS;
|
|
4347
4642
|
const statBase = slot * STATIC_MESH_FLOATS;
|
|
4348
4643
|
const t = resolveTransform(opts);
|
|
@@ -4369,8 +4664,16 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4369
4664
|
this.batcher.add(0, modelHandle.id, slot);
|
|
4370
4665
|
const dynamicData = this.dynamicData;
|
|
4371
4666
|
const staticData = this.staticData;
|
|
4372
|
-
|
|
4667
|
+
const self = this;
|
|
4668
|
+
let destroyed = false;
|
|
4669
|
+
const posOut = [0, 0, 0];
|
|
4670
|
+
const rotOut = [0, 0, 0];
|
|
4671
|
+
const sclOut = [0, 0, 0];
|
|
4672
|
+
const handle = {
|
|
4673
|
+
slot,
|
|
4674
|
+
modelId: modelHandle.id,
|
|
4373
4675
|
skinned: false,
|
|
4676
|
+
prefabId: userPrefabId,
|
|
4374
4677
|
setPosition(nx, ny, nz) {
|
|
4375
4678
|
dynamicData[dynBase + DYN_CURR_PX] = nx;
|
|
4376
4679
|
dynamicData[dynBase + DYN_CURR_PY] = ny;
|
|
@@ -4385,10 +4688,47 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4385
4688
|
staticData[statBase + STAT_SX] = nx;
|
|
4386
4689
|
staticData[statBase + STAT_SY] = ny;
|
|
4387
4690
|
staticData[statBase + STAT_SZ] = nz;
|
|
4691
|
+
},
|
|
4692
|
+
teleport(nx, ny, nz) {
|
|
4693
|
+
dynamicData[dynBase + DYN_PREV_PX] = nx;
|
|
4694
|
+
dynamicData[dynBase + DYN_PREV_PY] = ny;
|
|
4695
|
+
dynamicData[dynBase + DYN_PREV_PZ] = nz;
|
|
4696
|
+
dynamicData[dynBase + DYN_CURR_PX] = nx;
|
|
4697
|
+
dynamicData[dynBase + DYN_CURR_PY] = ny;
|
|
4698
|
+
dynamicData[dynBase + DYN_CURR_PZ] = nz;
|
|
4699
|
+
},
|
|
4700
|
+
get position() {
|
|
4701
|
+
posOut[0] = dynamicData[dynBase + DYN_CURR_PX];
|
|
4702
|
+
posOut[1] = dynamicData[dynBase + DYN_CURR_PY];
|
|
4703
|
+
posOut[2] = dynamicData[dynBase + DYN_CURR_PZ];
|
|
4704
|
+
return posOut;
|
|
4705
|
+
},
|
|
4706
|
+
get rotation() {
|
|
4707
|
+
rotOut[0] = dynamicData[dynBase + DYN_CURR_RX];
|
|
4708
|
+
rotOut[1] = dynamicData[dynBase + DYN_CURR_RY];
|
|
4709
|
+
rotOut[2] = dynamicData[dynBase + DYN_CURR_RZ];
|
|
4710
|
+
return rotOut;
|
|
4711
|
+
},
|
|
4712
|
+
get scale() {
|
|
4713
|
+
sclOut[0] = staticData[statBase + STAT_SX];
|
|
4714
|
+
sclOut[1] = staticData[statBase + STAT_SY];
|
|
4715
|
+
sclOut[2] = staticData[statBase + STAT_SZ];
|
|
4716
|
+
return sclOut;
|
|
4717
|
+
},
|
|
4718
|
+
destroy() {
|
|
4719
|
+
if (destroyed)
|
|
4720
|
+
return;
|
|
4721
|
+
destroyed = true;
|
|
4722
|
+
self.batcher.remove(0, modelHandle.id, slot);
|
|
4723
|
+
self.freeList.free(slot);
|
|
4724
|
+
dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
|
|
4725
|
+
staticData.fill(0, statBase, statBase + STATIC_MESH_FLOATS);
|
|
4726
|
+
self.staticDirty = true;
|
|
4388
4727
|
}
|
|
4389
4728
|
};
|
|
4729
|
+
return handle;
|
|
4390
4730
|
}
|
|
4391
|
-
addGltfInstance(opts, gltf) {
|
|
4731
|
+
addGltfInstance(opts, gltf, prefabId) {
|
|
4392
4732
|
const childHandles = [];
|
|
4393
4733
|
let firstSkinnedSlot;
|
|
4394
4734
|
for (const part of gltf.parts) {
|
|
@@ -4396,7 +4736,7 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4396
4736
|
const model = this.models[part.id];
|
|
4397
4737
|
let handle;
|
|
4398
4738
|
if (model?.skinned) {
|
|
4399
|
-
handle = this.addSkinnedInstance(partOpts, part, model.skinIndex, firstSkinnedSlot);
|
|
4739
|
+
handle = this.addSkinnedInstance(partOpts, part, model.skinIndex, firstSkinnedSlot, prefabId);
|
|
4400
4740
|
if (firstSkinnedSlot === void 0)
|
|
4401
4741
|
firstSkinnedSlot = handle.slot;
|
|
4402
4742
|
} else {
|
|
@@ -4405,8 +4745,10 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4405
4745
|
childHandles.push(handle);
|
|
4406
4746
|
}
|
|
4407
4747
|
const skinnedHandle = childHandles.find((h) => h.skinned);
|
|
4748
|
+
const lead = childHandles[0];
|
|
4408
4749
|
return {
|
|
4409
4750
|
skinned: gltf.skinned,
|
|
4751
|
+
prefabId,
|
|
4410
4752
|
setPosition(x, y, z) {
|
|
4411
4753
|
for (const h of childHandles)
|
|
4412
4754
|
h.setPosition(x, y, z);
|
|
@@ -4419,15 +4761,131 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4419
4761
|
for (const h of childHandles)
|
|
4420
4762
|
h.setScale(x, y, z);
|
|
4421
4763
|
},
|
|
4764
|
+
teleport(x, y, z) {
|
|
4765
|
+
for (const h of childHandles)
|
|
4766
|
+
h.teleport(x, y, z);
|
|
4767
|
+
},
|
|
4768
|
+
get position() {
|
|
4769
|
+
return lead.position;
|
|
4770
|
+
},
|
|
4771
|
+
get rotation() {
|
|
4772
|
+
return lead.rotation;
|
|
4773
|
+
},
|
|
4774
|
+
get scale() {
|
|
4775
|
+
return lead.scale;
|
|
4776
|
+
},
|
|
4422
4777
|
play: skinnedHandle?.play ? (name, opts2) => {
|
|
4423
4778
|
skinnedHandle.play(name, opts2);
|
|
4424
4779
|
} : void 0,
|
|
4425
4780
|
stop: skinnedHandle?.stop ? () => {
|
|
4426
4781
|
skinnedHandle.stop();
|
|
4427
|
-
} : void 0
|
|
4782
|
+
} : void 0,
|
|
4783
|
+
destroy() {
|
|
4784
|
+
for (const h of childHandles)
|
|
4785
|
+
h.destroy();
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
}
|
|
4789
|
+
/**
|
|
4790
|
+
* Spawn a composite prefab by spawning each of its parts at the composed
|
|
4791
|
+
* (instance + offset) transform. The returned handle broadcasts subsequent
|
|
4792
|
+
* `setPosition` / `setRotation` to every child, keeping each child's
|
|
4793
|
+
* baked offset applied on top of the new value.
|
|
4794
|
+
*/
|
|
4795
|
+
addCompositeInstance(opts, composite) {
|
|
4796
|
+
const bucket = this._prefabs;
|
|
4797
|
+
if (!bucket) {
|
|
4798
|
+
throw new Error(
|
|
4799
|
+
`addInstance: composite '${composite.id}' requires the renderer to be constructed with the bucket (\`prefabs\`).`
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
4802
|
+
const basePos = opts.position ?? [0, 0, 0];
|
|
4803
|
+
const baseRot = opts.rotation ?? [0, 0, 0];
|
|
4804
|
+
const offsets = composite.parts.map((p) => ({
|
|
4805
|
+
px: p.offset?.position?.[0] ?? 0,
|
|
4806
|
+
py: p.offset?.position?.[1] ?? 0,
|
|
4807
|
+
pz: p.offset?.position?.[2] ?? 0,
|
|
4808
|
+
rx: p.offset?.rotation?.[0] ?? 0,
|
|
4809
|
+
ry: p.offset?.rotation?.[1] ?? 0,
|
|
4810
|
+
rz: p.offset?.rotation?.[2] ?? 0
|
|
4811
|
+
}));
|
|
4812
|
+
const childHandles = [];
|
|
4813
|
+
const posOut = [basePos[0], basePos[1], basePos[2]];
|
|
4814
|
+
const rotOut = [baseRot[0], baseRot[1], baseRot[2]];
|
|
4815
|
+
const sclOut = [1, 1, 1];
|
|
4816
|
+
const initialScale = opts.scale;
|
|
4817
|
+
if (typeof initialScale === "number") {
|
|
4818
|
+
sclOut[0] = sclOut[1] = sclOut[2] = initialScale;
|
|
4819
|
+
} else if (initialScale) {
|
|
4820
|
+
sclOut[0] = initialScale[0];
|
|
4821
|
+
sclOut[1] = initialScale[1];
|
|
4822
|
+
sclOut[2] = initialScale[2];
|
|
4823
|
+
}
|
|
4824
|
+
for (let i = 0; i < composite.parts.length; i++) {
|
|
4825
|
+
const part = composite.parts[i];
|
|
4826
|
+
const off = offsets[i];
|
|
4827
|
+
const partPrefab = bucket.get(part.partId);
|
|
4828
|
+
const partOpts = {
|
|
4829
|
+
...opts,
|
|
4830
|
+
model: partPrefab,
|
|
4831
|
+
position: [basePos[0] + off.px, basePos[1] + off.py, basePos[2] + off.pz],
|
|
4832
|
+
rotation: [baseRot[0] + off.rx, baseRot[1] + off.ry, baseRot[2] + off.rz]
|
|
4833
|
+
};
|
|
4834
|
+
childHandles.push(this.addInstance(partOpts));
|
|
4835
|
+
}
|
|
4836
|
+
return {
|
|
4837
|
+
skinned: childHandles.some((h) => h.skinned),
|
|
4838
|
+
prefabId: composite.id,
|
|
4839
|
+
setPosition(x, y, z) {
|
|
4840
|
+
posOut[0] = x;
|
|
4841
|
+
posOut[1] = y;
|
|
4842
|
+
posOut[2] = z;
|
|
4843
|
+
for (let i = 0; i < childHandles.length; i++) {
|
|
4844
|
+
const o = offsets[i];
|
|
4845
|
+
childHandles[i].setPosition(x + o.px, y + o.py, z + o.pz);
|
|
4846
|
+
}
|
|
4847
|
+
},
|
|
4848
|
+
setRotation(x, y, z) {
|
|
4849
|
+
rotOut[0] = x;
|
|
4850
|
+
rotOut[1] = y;
|
|
4851
|
+
rotOut[2] = z;
|
|
4852
|
+
for (let i = 0; i < childHandles.length; i++) {
|
|
4853
|
+
const o = offsets[i];
|
|
4854
|
+
childHandles[i].setRotation(x + o.rx, y + o.ry, z + o.rz);
|
|
4855
|
+
}
|
|
4856
|
+
},
|
|
4857
|
+
setScale(x, y, z) {
|
|
4858
|
+
sclOut[0] = x;
|
|
4859
|
+
sclOut[1] = y;
|
|
4860
|
+
sclOut[2] = z;
|
|
4861
|
+
for (const h of childHandles)
|
|
4862
|
+
h.setScale(x, y, z);
|
|
4863
|
+
},
|
|
4864
|
+
teleport(x, y, z) {
|
|
4865
|
+
posOut[0] = x;
|
|
4866
|
+
posOut[1] = y;
|
|
4867
|
+
posOut[2] = z;
|
|
4868
|
+
for (let i = 0; i < childHandles.length; i++) {
|
|
4869
|
+
const o = offsets[i];
|
|
4870
|
+
childHandles[i].teleport(x + o.px, y + o.py, z + o.pz);
|
|
4871
|
+
}
|
|
4872
|
+
},
|
|
4873
|
+
get position() {
|
|
4874
|
+
return posOut;
|
|
4875
|
+
},
|
|
4876
|
+
get rotation() {
|
|
4877
|
+
return rotOut;
|
|
4878
|
+
},
|
|
4879
|
+
get scale() {
|
|
4880
|
+
return sclOut;
|
|
4881
|
+
},
|
|
4882
|
+
destroy() {
|
|
4883
|
+
for (const h of childHandles)
|
|
4884
|
+
h.destroy();
|
|
4885
|
+
}
|
|
4428
4886
|
};
|
|
4429
4887
|
}
|
|
4430
|
-
addSkinnedInstance(opts, modelHandle, skinIndex, linkedSlot) {
|
|
4888
|
+
addSkinnedInstance(opts, modelHandle, skinIndex, linkedSlot, prefabId = null) {
|
|
4431
4889
|
const slot = this.skinnedFreeList.allocate();
|
|
4432
4890
|
if (slot === -1)
|
|
4433
4891
|
throw new Error(`Max skinned instances (${this.maxSkinnedInstances}) reached`);
|
|
@@ -4438,11 +4896,27 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4438
4896
|
if (linkedSlot !== void 0) {
|
|
4439
4897
|
boneOffset = this.skinnedInstanceBoneOffsets[linkedSlot];
|
|
4440
4898
|
animState = this.skinnedAnimStates[linkedSlot];
|
|
4899
|
+
this.boneOffsetRefcount[boneOffset]++;
|
|
4441
4900
|
} else {
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4901
|
+
const pool = this.freedBoneOffsets.get(skinIndex);
|
|
4902
|
+
if (pool && pool.length > 0) {
|
|
4903
|
+
boneOffset = pool.pop();
|
|
4904
|
+
} else {
|
|
4905
|
+
boneOffset = this.nextBoneOffset + jointCount;
|
|
4906
|
+
this.nextBoneOffset += jointCount * 2;
|
|
4907
|
+
}
|
|
4908
|
+
this.boneOffsetRefcount[boneOffset] = 1;
|
|
4909
|
+
this.boneOffsetSkinIndex[boneOffset] = skinIndex;
|
|
4910
|
+
const restOffsetFloats = boneOffset * 16;
|
|
4911
|
+
const restLengthFloats = jointCount * 16;
|
|
4912
|
+
skinModel.animation.computeRestPose(this.boneMatrixData, restOffsetFloats);
|
|
4913
|
+
this.device.queue.writeBuffer(
|
|
4914
|
+
this.rawBoneMatrixBuffer,
|
|
4915
|
+
restOffsetFloats * 4,
|
|
4916
|
+
this.boneMatrixData.buffer,
|
|
4917
|
+
this.boneMatrixData.byteOffset + restOffsetFloats * 4,
|
|
4918
|
+
restLengthFloats * 4
|
|
4919
|
+
);
|
|
4446
4920
|
animState = skinModel.animation.clipCount > 0 ? skinModel.animation.createState(0, 1, true) : null;
|
|
4447
4921
|
}
|
|
4448
4922
|
this.skinnedInstanceBoneOffsets[slot] = boneOffset;
|
|
@@ -4468,11 +4942,7 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4468
4942
|
this.skinnedStaticData[statBase + SSTAT_CR] = t.cr;
|
|
4469
4943
|
this.skinnedStaticData[statBase + SSTAT_CG] = t.cg;
|
|
4470
4944
|
this.skinnedStaticData[statBase + SSTAT_CB] = t.cb;
|
|
4471
|
-
|
|
4472
|
-
(statBase + SSTAT_BONE_OFFSET) * 4,
|
|
4473
|
-
boneOffset,
|
|
4474
|
-
true
|
|
4475
|
-
);
|
|
4945
|
+
this.skinnedStaticDV.setUint32((statBase + SSTAT_BONE_OFFSET) * 4, boneOffset, true);
|
|
4476
4946
|
this.skinnedStaticDirty = true;
|
|
4477
4947
|
this.skinnedInstanceModelIds[slot] = modelHandle.id;
|
|
4478
4948
|
this.skinnedBatcher.add(0, modelHandle.id, slot);
|
|
@@ -4480,10 +4950,18 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4480
4950
|
const staticData = this.skinnedStaticData;
|
|
4481
4951
|
const animStates = this.skinnedAnimStates;
|
|
4482
4952
|
const animation = skinModel.animation;
|
|
4953
|
+
const self = this;
|
|
4954
|
+
const capturedBoneOffset = boneOffset;
|
|
4955
|
+
const capturedSkinIndex = skinIndex;
|
|
4956
|
+
let destroyed = false;
|
|
4957
|
+
const posOut = [0, 0, 0];
|
|
4958
|
+
const rotOut = [0, 0, 0];
|
|
4959
|
+
const sclOut = [0, 0, 0];
|
|
4483
4960
|
return {
|
|
4484
4961
|
slot,
|
|
4485
4962
|
modelId: modelHandle.id,
|
|
4486
4963
|
skinned: true,
|
|
4964
|
+
prefabId,
|
|
4487
4965
|
setPosition(nx, ny, nz) {
|
|
4488
4966
|
dynamicData[dynBase + DYN_CURR_PX] = nx;
|
|
4489
4967
|
dynamicData[dynBase + DYN_CURR_PY] = ny;
|
|
@@ -4499,6 +4977,32 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4499
4977
|
staticData[statBase + SSTAT_SY] = ny;
|
|
4500
4978
|
staticData[statBase + SSTAT_SZ] = nz;
|
|
4501
4979
|
},
|
|
4980
|
+
teleport(nx, ny, nz) {
|
|
4981
|
+
dynamicData[dynBase + DYN_PREV_PX] = nx;
|
|
4982
|
+
dynamicData[dynBase + DYN_PREV_PY] = ny;
|
|
4983
|
+
dynamicData[dynBase + DYN_PREV_PZ] = nz;
|
|
4984
|
+
dynamicData[dynBase + DYN_CURR_PX] = nx;
|
|
4985
|
+
dynamicData[dynBase + DYN_CURR_PY] = ny;
|
|
4986
|
+
dynamicData[dynBase + DYN_CURR_PZ] = nz;
|
|
4987
|
+
},
|
|
4988
|
+
get position() {
|
|
4989
|
+
posOut[0] = dynamicData[dynBase + DYN_CURR_PX];
|
|
4990
|
+
posOut[1] = dynamicData[dynBase + DYN_CURR_PY];
|
|
4991
|
+
posOut[2] = dynamicData[dynBase + DYN_CURR_PZ];
|
|
4992
|
+
return posOut;
|
|
4993
|
+
},
|
|
4994
|
+
get rotation() {
|
|
4995
|
+
rotOut[0] = dynamicData[dynBase + DYN_CURR_RX];
|
|
4996
|
+
rotOut[1] = dynamicData[dynBase + DYN_CURR_RY];
|
|
4997
|
+
rotOut[2] = dynamicData[dynBase + DYN_CURR_RZ];
|
|
4998
|
+
return rotOut;
|
|
4999
|
+
},
|
|
5000
|
+
get scale() {
|
|
5001
|
+
sclOut[0] = staticData[statBase + SSTAT_SX];
|
|
5002
|
+
sclOut[1] = staticData[statBase + SSTAT_SY];
|
|
5003
|
+
sclOut[2] = staticData[statBase + SSTAT_SZ];
|
|
5004
|
+
return sclOut;
|
|
5005
|
+
},
|
|
4502
5006
|
play(name, opts2) {
|
|
4503
5007
|
const state = animStates[slot];
|
|
4504
5008
|
if (state)
|
|
@@ -4508,6 +5012,25 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4508
5012
|
const state = animStates[slot];
|
|
4509
5013
|
if (state)
|
|
4510
5014
|
animation.stop(state);
|
|
5015
|
+
},
|
|
5016
|
+
destroy() {
|
|
5017
|
+
if (destroyed)
|
|
5018
|
+
return;
|
|
5019
|
+
destroyed = true;
|
|
5020
|
+
self.skinnedBatcher.remove(0, modelHandle.id, slot);
|
|
5021
|
+
self.skinnedFreeList.free(slot);
|
|
5022
|
+
dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
|
|
5023
|
+
staticData.fill(0, statBase, statBase + SKINNED_STATIC_MESH_FLOATS);
|
|
5024
|
+
animStates[slot] = null;
|
|
5025
|
+
self.skinnedStaticDirty = true;
|
|
5026
|
+
if (--self.boneOffsetRefcount[capturedBoneOffset] === 0) {
|
|
5027
|
+
let pool = self.freedBoneOffsets.get(capturedSkinIndex);
|
|
5028
|
+
if (!pool) {
|
|
5029
|
+
pool = [];
|
|
5030
|
+
self.freedBoneOffsets.set(capturedSkinIndex, pool);
|
|
5031
|
+
}
|
|
5032
|
+
pool.push(capturedBoneOffset);
|
|
5033
|
+
}
|
|
4511
5034
|
}
|
|
4512
5035
|
};
|
|
4513
5036
|
}
|
|
@@ -4593,6 +5116,31 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4593
5116
|
this.animJointLookupOffset = pb.jointLookupOffset;
|
|
4594
5117
|
return true;
|
|
4595
5118
|
}
|
|
5119
|
+
/**
|
|
5120
|
+
* Returns true if a skinned instance's bone-matrix compute should be
|
|
5121
|
+
* dispatched this frame. Combines frustum culling (scaled bounding sphere)
|
|
5122
|
+
* and a configurable distance cull from the camera. Callers pass the
|
|
5123
|
+
* camera position + cullDistSq once outside the loop to avoid re-reading.
|
|
5124
|
+
*/
|
|
5125
|
+
shouldDispatchSkinning(slot, model, camX, camY, camZ, cullDistSq) {
|
|
5126
|
+
const skinModel = model && model.skinIndex >= 0 ? this.skinnedModels[model.skinIndex] : null;
|
|
5127
|
+
const baseRadius = skinModel?.boundingRadius ?? 10;
|
|
5128
|
+
const base = slot * DYNAMIC_MESH_FLOATS;
|
|
5129
|
+
const sBase = slot * SKINNED_STATIC_MESH_FLOATS;
|
|
5130
|
+
const cx = this.skinnedDynamicData[base + DYN_CURR_PX];
|
|
5131
|
+
const cy = this.skinnedDynamicData[base + DYN_CURR_PY];
|
|
5132
|
+
const cz = this.skinnedDynamicData[base + DYN_CURR_PZ];
|
|
5133
|
+
const sx = Math.abs(this.skinnedStaticData[sBase + SSTAT_SX]);
|
|
5134
|
+
const sy = Math.abs(this.skinnedStaticData[sBase + SSTAT_SY]);
|
|
5135
|
+
const sz = Math.abs(this.skinnedStaticData[sBase + SSTAT_SZ]);
|
|
5136
|
+
const maxScale = sx > sy ? sx > sz ? sx : sz : sy > sz ? sy : sz;
|
|
5137
|
+
if (!this.isInFrustum(cx, cy, cz, baseRadius * maxScale))
|
|
5138
|
+
return false;
|
|
5139
|
+
const dxv = cx - camX, dyv = cy - camY, dzv = cz - camZ;
|
|
5140
|
+
if (dxv * dxv + dyv * dyv + dzv * dzv > cullDistSq)
|
|
5141
|
+
return false;
|
|
5142
|
+
return true;
|
|
5143
|
+
}
|
|
4596
5144
|
updateAnimations(deltaTime) {
|
|
4597
5145
|
this.syncLazyAnimationChanges();
|
|
4598
5146
|
if (this.animComputeNeedsRebuild && this.packedAnimData.clips.length > 0) {
|
|
@@ -4636,6 +5184,9 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4636
5184
|
this.updatedBoneOffsets.fill(0);
|
|
4637
5185
|
let count = 0;
|
|
4638
5186
|
const dv = this.gpuInstDV;
|
|
5187
|
+
const camPos = this.camera.position;
|
|
5188
|
+
const camX = camPos[0], camY = camPos[1], camZ = camPos[2];
|
|
5189
|
+
const cullDistSq = this.animationCullDistanceSq;
|
|
4639
5190
|
for (let slot = 0; slot < this.maxSkinnedInstances; slot++) {
|
|
4640
5191
|
const animState = this.skinnedAnimStates[slot];
|
|
4641
5192
|
if (!animState)
|
|
@@ -4674,6 +5225,8 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4674
5225
|
const modelId = this.skinnedInstanceModelIds[slot];
|
|
4675
5226
|
const model = this.models[modelId];
|
|
4676
5227
|
const skinIdx = model?.skinIndex ?? 0;
|
|
5228
|
+
if (!this.shouldDispatchSkinning(slot, model, camX, camY, camZ, cullDistSq))
|
|
5229
|
+
continue;
|
|
4677
5230
|
const off = count * 32;
|
|
4678
5231
|
dv.setInt32(off, animState.clipId, true);
|
|
4679
5232
|
dv.setFloat32(off + 4, animState.time, true);
|
|
@@ -4718,14 +5271,12 @@ var WebGPU3DRenderer = class extends Base3DRenderer {
|
|
|
4718
5271
|
}
|
|
4719
5272
|
}
|
|
4720
5273
|
}
|
|
5274
|
+
/**
|
|
5275
|
+
* Free an instance's renderer slot. Equivalent to `handle.destroy()` -
|
|
5276
|
+
* kept as a convenience for direct lookup. Safe to call multiple times.
|
|
5277
|
+
*/
|
|
4721
5278
|
removeInstance(handle) {
|
|
4722
|
-
|
|
4723
|
-
this.freeList.free(handle.slot);
|
|
4724
|
-
const dynBase = handle.slot * DYNAMIC_MESH_FLOATS;
|
|
4725
|
-
const statBase = handle.slot * STATIC_MESH_FLOATS;
|
|
4726
|
-
this.dynamicData.fill(0, dynBase, dynBase + DYNAMIC_MESH_FLOATS);
|
|
4727
|
-
this.staticData.fill(0, statBase, statBase + STATIC_MESH_FLOATS);
|
|
4728
|
-
this.staticDirty = true;
|
|
5279
|
+
handle.destroy();
|
|
4729
5280
|
}
|
|
4730
5281
|
storePreviousState() {
|
|
4731
5282
|
this.camera.storePrevious();
|