@relu-interactives/spatial-ecs 0.2.23 → 0.2.24
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/dist/{chunk-7BEZBIVB.js → chunk-CCE5K2TU.js} +91 -65
- package/dist/chunk-CCE5K2TU.js.map +1 -0
- package/dist/docs/guide/systems/physics-sync-system.md +1 -1
- package/dist/index.cjs +89 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/systems/index.cjs +89 -63
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-7BEZBIVB.js.map +0 -1
|
@@ -45,7 +45,7 @@ Initializes and manages the Rapier physics world, creates rigid bodies and colli
|
|
|
45
45
|
## Behavior notes
|
|
46
46
|
|
|
47
47
|
- **Async init** — the Rapier WASM module is loaded once on the first `update()` call. Physics simulation does not start until the module is ready; entities created before init are processed on the first frame after init completes.
|
|
48
|
-
- **Trimesh** — `trimesh` colliders
|
|
48
|
+
- **Trimesh** — `trimesh` colliders merge the vertex/index data of **every mesh** under the entity (multi-primitive GLBs are fully covered) with the entity's **world scale baked into the vertices** — Rapier bodies carry no scale, so scaling the entity is how you resize a trimesh; the collider rebuilds automatically when the scale or geometry changes. The extracted data is cached per entity by a geometry+scale signature, and the heavy vertex merge runs only when that signature changes.
|
|
49
49
|
- **Sensor colliders** — setting `values.collider.isSensor = true` creates a Rapier sensor collider. Sensor contacts do not generate forces but can be used for overlap/trigger queries.
|
|
50
50
|
- **Gravity** — the global gravity vector is read from the `Environment` component (`gravity` field, default `{ x: 0, y: -9.81, z: 0 }`) and applied to the Rapier world gravity setting each frame.
|
|
51
51
|
- **Preview only** — the system is typically constructed with `{ isEditor: false }` for preview. In the editor the physics world step is skipped so objects don't fall during authoring.
|
package/dist/index.cjs
CHANGED
|
@@ -6789,82 +6789,108 @@ function createColliderDesc(RAPIER, values, triMeshData) {
|
|
|
6789
6789
|
return desc;
|
|
6790
6790
|
}
|
|
6791
6791
|
__name(createColliderDesc, "createColliderDesc");
|
|
6792
|
-
function
|
|
6793
|
-
if (object
|
|
6794
|
-
return
|
|
6792
|
+
function collectTriMeshParts(object) {
|
|
6793
|
+
if (!object) {
|
|
6794
|
+
return null;
|
|
6795
6795
|
}
|
|
6796
|
-
|
|
6796
|
+
object.updateWorldMatrix(true, true);
|
|
6797
|
+
const worldScale = new THREE36__namespace.Vector3();
|
|
6798
|
+
object.getWorldScale(worldScale);
|
|
6799
|
+
const scale = {
|
|
6800
|
+
x: Math.abs(worldScale.x) || 1,
|
|
6801
|
+
y: Math.abs(worldScale.y) || 1,
|
|
6802
|
+
z: Math.abs(worldScale.z) || 1
|
|
6803
|
+
};
|
|
6804
|
+
const worldToObject = new THREE36__namespace.Matrix4().copy(object.matrixWorld).invert();
|
|
6805
|
+
const parts = [];
|
|
6797
6806
|
object.traverse((child) => {
|
|
6798
|
-
if (
|
|
6807
|
+
if (!(child instanceof THREE36__namespace.Mesh) || !child.geometry) {
|
|
6799
6808
|
return;
|
|
6800
6809
|
}
|
|
6801
|
-
|
|
6802
|
-
|
|
6810
|
+
const geometry = child.geometry;
|
|
6811
|
+
const positionAttribute = geometry.getAttribute("position");
|
|
6812
|
+
if (!positionAttribute || positionAttribute.itemSize < 3) {
|
|
6813
|
+
return;
|
|
6803
6814
|
}
|
|
6815
|
+
const positionCount = positionAttribute.count;
|
|
6816
|
+
if (positionCount < 3) {
|
|
6817
|
+
return;
|
|
6818
|
+
}
|
|
6819
|
+
const indexAttribute = geometry.getIndex();
|
|
6820
|
+
const indexCount = indexAttribute ? indexAttribute.count : Math.floor(positionCount / 3) * 3;
|
|
6821
|
+
if (indexCount < 3) {
|
|
6822
|
+
return;
|
|
6823
|
+
}
|
|
6824
|
+
const relativeMatrix = new THREE36__namespace.Matrix4().multiplyMatrices(
|
|
6825
|
+
worldToObject,
|
|
6826
|
+
child.matrixWorld
|
|
6827
|
+
);
|
|
6828
|
+
const matrixSignature = relativeMatrix.elements.map((value) => value.toFixed(5)).join(",");
|
|
6829
|
+
parts.push({
|
|
6830
|
+
positionAttribute,
|
|
6831
|
+
indexAttribute,
|
|
6832
|
+
positionCount,
|
|
6833
|
+
indexCount,
|
|
6834
|
+
relativeMatrix,
|
|
6835
|
+
signature: `${child.uuid}:${geometry.uuid}:${positionCount}:${indexCount}:${matrixSignature}`
|
|
6836
|
+
});
|
|
6804
6837
|
});
|
|
6805
|
-
|
|
6806
|
-
}
|
|
6807
|
-
__name(findFirstMeshWithGeometry, "findFirstMeshWithGeometry");
|
|
6808
|
-
function createTriMeshData(object) {
|
|
6809
|
-
if (!object) {
|
|
6838
|
+
if (parts.length === 0) {
|
|
6810
6839
|
return null;
|
|
6811
6840
|
}
|
|
6812
|
-
const
|
|
6813
|
-
|
|
6841
|
+
const scaleSignature = `${scale.x.toFixed(5)},${scale.y.toFixed(5)},${scale.z.toFixed(5)}`;
|
|
6842
|
+
return {
|
|
6843
|
+
parts,
|
|
6844
|
+
scale,
|
|
6845
|
+
signature: `${scaleSignature}|${parts.map((p) => p.signature).join("|")}`
|
|
6846
|
+
};
|
|
6847
|
+
}
|
|
6848
|
+
__name(collectTriMeshParts, "collectTriMeshParts");
|
|
6849
|
+
function createTriMeshData(collected) {
|
|
6850
|
+
if (!collected) {
|
|
6814
6851
|
return null;
|
|
6815
6852
|
}
|
|
6816
|
-
const
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6853
|
+
const { parts, scale } = collected;
|
|
6854
|
+
let vertexTotal = 0;
|
|
6855
|
+
let indexTotal = 0;
|
|
6856
|
+
for (const part of parts) {
|
|
6857
|
+
vertexTotal += part.positionCount;
|
|
6858
|
+
indexTotal += part.indexCount;
|
|
6820
6859
|
}
|
|
6821
|
-
|
|
6822
|
-
if (positionCount < 3) {
|
|
6860
|
+
if (vertexTotal < 3 || indexTotal < 3) {
|
|
6823
6861
|
return null;
|
|
6824
6862
|
}
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
const worldToObject = new THREE36__namespace.Matrix4().copy(object.matrixWorld).invert();
|
|
6828
|
-
const relativeMatrix = new THREE36__namespace.Matrix4().multiplyMatrices(
|
|
6829
|
-
worldToObject,
|
|
6830
|
-
mesh.matrixWorld
|
|
6831
|
-
);
|
|
6832
|
-
const vertices = new Float32Array(positionCount * 3);
|
|
6863
|
+
const vertices = new Float32Array(vertexTotal * 3);
|
|
6864
|
+
const indices = new Uint32Array(indexTotal);
|
|
6833
6865
|
const point = new THREE36__namespace.Vector3();
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
indices = new Uint32Array(triangleIndexCount);
|
|
6858
|
-
for (let index = 0; index < triangleIndexCount; index += 1) {
|
|
6859
|
-
indices[index] = index;
|
|
6866
|
+
let vertexOffset = 0;
|
|
6867
|
+
let indexOffset = 0;
|
|
6868
|
+
for (const part of parts) {
|
|
6869
|
+
const { positionAttribute, indexAttribute, relativeMatrix } = part;
|
|
6870
|
+
for (let index = 0; index < part.positionCount; index += 1) {
|
|
6871
|
+
point.set(
|
|
6872
|
+
positionAttribute.getX(index),
|
|
6873
|
+
positionAttribute.getY(index),
|
|
6874
|
+
positionAttribute.getZ(index)
|
|
6875
|
+
).applyMatrix4(relativeMatrix);
|
|
6876
|
+
const outputIndex = (vertexOffset + index) * 3;
|
|
6877
|
+
vertices[outputIndex] = point.x * scale.x;
|
|
6878
|
+
vertices[outputIndex + 1] = point.y * scale.y;
|
|
6879
|
+
vertices[outputIndex + 2] = point.z * scale.z;
|
|
6880
|
+
}
|
|
6881
|
+
if (indexAttribute) {
|
|
6882
|
+
for (let index = 0; index < part.indexCount; index += 1) {
|
|
6883
|
+
indices[indexOffset + index] = vertexOffset + indexAttribute.getX(index);
|
|
6884
|
+
}
|
|
6885
|
+
} else {
|
|
6886
|
+
for (let index = 0; index < part.indexCount; index += 1) {
|
|
6887
|
+
indices[indexOffset + index] = vertexOffset + index;
|
|
6888
|
+
}
|
|
6860
6889
|
}
|
|
6890
|
+
vertexOffset += part.positionCount;
|
|
6891
|
+
indexOffset += part.indexCount;
|
|
6861
6892
|
}
|
|
6862
|
-
|
|
6863
|
-
return {
|
|
6864
|
-
vertices,
|
|
6865
|
-
indices,
|
|
6866
|
-
signature: `${mesh.uuid}:${geometry.uuid}:${positionCount}:${indices.length}:${matrixSignature}`
|
|
6867
|
-
};
|
|
6893
|
+
return { vertices, indices, signature: collected.signature };
|
|
6868
6894
|
}
|
|
6869
6895
|
__name(createTriMeshData, "createTriMeshData");
|
|
6870
6896
|
function syncBodyFromTransform(body, transform) {
|
|
@@ -7041,7 +7067,7 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
|
|
|
7041
7067
|
syncBodyFromTransform(body, transform);
|
|
7042
7068
|
}
|
|
7043
7069
|
const effectiveCollider = computeColliderState(physics.values.collider, object);
|
|
7044
|
-
const
|
|
7070
|
+
const triMeshParts = effectiveCollider.shape === "trimesh" ? collectTriMeshParts(object) : null;
|
|
7045
7071
|
if (!effectiveCollider || !effectiveCollider.enabled) {
|
|
7046
7072
|
const colliderHandle = this.colliderHandles.get(entityId);
|
|
7047
7073
|
if (typeof colliderHandle === "number") {
|
|
@@ -7055,7 +7081,7 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
|
|
|
7055
7081
|
physics.collider = null;
|
|
7056
7082
|
continue;
|
|
7057
7083
|
}
|
|
7058
|
-
const colliderSignature = `${JSON.stringify(effectiveCollider)}:${
|
|
7084
|
+
const colliderSignature = `${JSON.stringify(effectiveCollider)}:${triMeshParts?.signature ?? ""}`;
|
|
7059
7085
|
const previousColliderSignature = this.colliderSignatures.get(entityId);
|
|
7060
7086
|
if (!this.colliderHandles.has(entityId) || previousColliderSignature !== colliderSignature) {
|
|
7061
7087
|
const previousColliderHandle = this.colliderHandles.get(entityId);
|
|
@@ -7068,7 +7094,7 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
|
|
|
7068
7094
|
const colliderDesc = createColliderDesc(
|
|
7069
7095
|
this.rapier,
|
|
7070
7096
|
effectiveCollider,
|
|
7071
|
-
|
|
7097
|
+
createTriMeshData(triMeshParts)
|
|
7072
7098
|
);
|
|
7073
7099
|
const createdCollider = physicsWorld.createCollider(colliderDesc, body);
|
|
7074
7100
|
this.colliderHandles.set(entityId, createdCollider.handle);
|