cyclone-physics 1.1.2 → 1.1.4
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/package.json +1 -1
- package/scripts/collisions.js +277 -30
- package/scripts/common.js +6 -0
- package/scripts/cyclone.js +2 -2
- package/scripts/forces.js +2 -0
- package/scripts/processors.js +10 -1
package/package.json
CHANGED
package/scripts/collisions.js
CHANGED
|
@@ -9,6 +9,8 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
9
9
|
// closure scratch variables
|
|
10
10
|
var thispos = new THREE.Vector3(),
|
|
11
11
|
otherpos = new THREE.Vector3(),
|
|
12
|
+
thisvel = new THREE.Vector3(),
|
|
13
|
+
othervel = new THREE.Vector3(),
|
|
12
14
|
midline = new THREE.Vector3(),
|
|
13
15
|
scaledVelocity = new THREE.Vector3(),
|
|
14
16
|
intersectionPoint = new THREE.Vector3();
|
|
@@ -20,9 +22,6 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
20
22
|
obj1.body.localToWorldPos(thispos.set(0,0,0));
|
|
21
23
|
obj2.body.localToWorldPos(otherpos.set(0,0,0));
|
|
22
24
|
|
|
23
|
-
let scaledRadius1 = obj1.radius * Math.max(obj1.body.scaleWorld.x, obj1.body.scaleWorld.y, obj1.body.scaleWorld.z),
|
|
24
|
-
scaledRadius2 = obj2.radius * Math.max(obj2.body.scaleWorld.x, obj2.body.scaleWorld.y, obj2.body.scaleWorld.z);
|
|
25
|
-
|
|
26
25
|
let dynamic = true; // TODO - this should either be a flag on rigid bodies, or a configurable threshold based on velocity
|
|
27
26
|
if (!dynamic) {
|
|
28
27
|
midline.subVectors(otherpos, thispos),
|
|
@@ -45,9 +44,17 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
45
44
|
//console.log('crash a sphere-sphere', contact);
|
|
46
45
|
}
|
|
47
46
|
} else {
|
|
48
|
-
let r =
|
|
47
|
+
let r = obj1.radius + obj2.radius;
|
|
49
48
|
// FIXME - probably need to transform velocity into world coordinates as well
|
|
50
|
-
|
|
49
|
+
obj1.body.localToWorldDir(thisvel.copy(obj1.body.velocity));
|
|
50
|
+
obj2.body.localToWorldDir(othervel.copy(obj2.body.velocity));
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
let v = scaledVelocity.copy(thisvel).sub(othervel).multiplyScalar(dt);
|
|
54
|
+
|
|
55
|
+
midline.copy(thispos).sub(otherpos).normalize();
|
|
56
|
+
|
|
57
|
+
//if (midline.dot(scaledVelocity) > 0) return; // moving away, can't collide
|
|
51
58
|
|
|
52
59
|
let endpos = midline.copy(thispos).add(v);
|
|
53
60
|
|
|
@@ -61,7 +68,7 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
61
68
|
|
|
62
69
|
var contact = new elation.physics.contact_dynamic({
|
|
63
70
|
normal: normal,
|
|
64
|
-
point: normal.clone().multiplyScalar(
|
|
71
|
+
point: normal.clone().multiplyScalar(obj1.radius).add(thispos), // allocate point
|
|
65
72
|
penetrationTime: intersection.t,
|
|
66
73
|
bodies: [obj1.body, obj2.body],
|
|
67
74
|
});
|
|
@@ -315,7 +322,7 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
315
322
|
}
|
|
316
323
|
})();
|
|
317
324
|
|
|
318
|
-
this.
|
|
325
|
+
this.box_box_old = function() {
|
|
319
326
|
// closure scratch variables
|
|
320
327
|
var diff = new THREE.Vector3(),
|
|
321
328
|
thispos = new THREE.Vector3(),
|
|
@@ -477,6 +484,234 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
477
484
|
}
|
|
478
485
|
}();
|
|
479
486
|
|
|
487
|
+
this.box_box = (function() {
|
|
488
|
+
// closure scratch variables
|
|
489
|
+
const scratch = {
|
|
490
|
+
axes: Array(15).fill(null).map(() => new THREE.Vector3()),
|
|
491
|
+
box1Corners: Array(8).fill(null).map(() => new THREE.Vector3()),
|
|
492
|
+
box2Corners: Array(8).fill(null).map(() => new THREE.Vector3()),
|
|
493
|
+
box1Projection: { min: 0, max: 0 },
|
|
494
|
+
box2Projection: { min: 0, max: 0 },
|
|
495
|
+
tmpVec: new THREE.Vector3(),
|
|
496
|
+
tmpQuat: new THREE.Quaternion(),
|
|
497
|
+
contactAxis: new THREE.Vector3(),
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
function getAxesToTest(box1, box2, scratchAxes) {
|
|
501
|
+
const axes = scratchAxes;
|
|
502
|
+
|
|
503
|
+
// Get the three local axes of both boxes in world coordinates
|
|
504
|
+
const box1Axes = getWorldAxes(box1, axes.slice(0, 3));
|
|
505
|
+
const box2Axes = getWorldAxes(box2, axes.slice(3, 6));
|
|
506
|
+
|
|
507
|
+
// Add cross products of edges (9 cross-product axes)
|
|
508
|
+
let axesIndex = 6;
|
|
509
|
+
for (let i = 0; i < 3; i++) {
|
|
510
|
+
for (let j = 0; j < 3; j++) {
|
|
511
|
+
const cross = axes[axesIndex].crossVectors(box1Axes[i], box2Axes[j]);
|
|
512
|
+
if (cross.lengthSq() > 1e-6) { // Avoid zero vectors
|
|
513
|
+
cross.normalize();
|
|
514
|
+
}
|
|
515
|
+
axesIndex++;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return axes;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function getWorldAxes(box, axes) {
|
|
523
|
+
// The orientationWorld is already in world space
|
|
524
|
+
axes[0].set(1, 0, 0).applyQuaternion(box.body.orientationWorld);
|
|
525
|
+
axes[1].set(0, 1, 0).applyQuaternion(box.body.orientationWorld);
|
|
526
|
+
axes[2].set(0, 0, 1).applyQuaternion(box.body.orientationWorld);
|
|
527
|
+
return axes;
|
|
528
|
+
}
|
|
529
|
+
function getPenetrationOnAxis(box1, box2, axis) {
|
|
530
|
+
// Project both boxes onto the axis
|
|
531
|
+
const box1Projection = projectBoxOntoAxis(box1, axis, scratch.box1Corners, scratch.box1Projection);
|
|
532
|
+
const box2Projection = projectBoxOntoAxis(box2, axis, scratch.box2Corners, scratch.box2Projection);
|
|
533
|
+
|
|
534
|
+
// Check if projections overlap
|
|
535
|
+
const overlap = Math.min(box1Projection.max - box2Projection.min, box2Projection.max - box1Projection.min);
|
|
536
|
+
if (box1Projection.max < box2Projection.min || box2Projection.max < box1Projection.min) {
|
|
537
|
+
return false; // Separating axis found
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return overlap; // Return penetration depth on this axis
|
|
541
|
+
}
|
|
542
|
+
function projectBoxOntoAxis(box, axis, corners, projection) {
|
|
543
|
+
getBoxCorners(box, corners);
|
|
544
|
+
|
|
545
|
+
projection.min = Infinity;
|
|
546
|
+
projection.max = -Infinity;
|
|
547
|
+
|
|
548
|
+
for (let corner of corners) {
|
|
549
|
+
const proj = corner.dot(axis);
|
|
550
|
+
projection.min = Math.min(projection.min, proj);
|
|
551
|
+
projection.max = Math.max(projection.max, proj);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return projection;
|
|
555
|
+
}
|
|
556
|
+
function getBoxCorners(box, corners) {
|
|
557
|
+
const halfSize = box.halfsize;
|
|
558
|
+
const offset = box.offset;
|
|
559
|
+
|
|
560
|
+
const scale = box.body.localToWorldScale(scratch.tmpVec.set(1,1,1));
|
|
561
|
+
|
|
562
|
+
// Define the eight corners of the box in local space
|
|
563
|
+
corners[0].set(halfSize.x, halfSize.y, halfSize.z).add(offset).divide(scale);
|
|
564
|
+
corners[1].set(halfSize.x, halfSize.y, -halfSize.z).add(offset).divide(scale);
|
|
565
|
+
corners[2].set(halfSize.x, -halfSize.y, halfSize.z).add(offset).divide(scale);
|
|
566
|
+
corners[3].set(halfSize.x, -halfSize.y, -halfSize.z).add(offset).divide(scale);
|
|
567
|
+
corners[4].set(-halfSize.x, halfSize.y, halfSize.z).add(offset).divide(scale);
|
|
568
|
+
corners[5].set(-halfSize.x, halfSize.y, -halfSize.z).add(offset).divide(scale);
|
|
569
|
+
corners[6].set(-halfSize.x, -halfSize.y, halfSize.z).add(offset).divide(scale);
|
|
570
|
+
corners[7].set(-halfSize.x, -halfSize.y, -halfSize.z).add(offset).divide(scale);
|
|
571
|
+
|
|
572
|
+
// Convert local corners to world space using localToWorld
|
|
573
|
+
for (let i = 0; i < 8; i++) {
|
|
574
|
+
box.body.localToWorldPos(corners[i]);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return corners;
|
|
578
|
+
}
|
|
579
|
+
function generateContacts(box1, box2, minPenetration, contactAxis, contacts) {
|
|
580
|
+
// The contact normal is the axis of minimum penetration
|
|
581
|
+
const contactNormal = contactAxis.clone().normalize();
|
|
582
|
+
|
|
583
|
+
// Find the closest points on the surface of both boxes
|
|
584
|
+
const box1Corners = getBoxCorners(box1, scratch.box1Corners);
|
|
585
|
+
const box2Corners = getBoxCorners(box2, scratch.box2Corners);
|
|
586
|
+
|
|
587
|
+
//const box1Closest = findClosestPointOnBox(box1Corners, contactNormal);
|
|
588
|
+
//const box2Closest = findClosestPointOnBox(box2Corners, contactNormal.clone().negate());
|
|
589
|
+
const box1Closest = findClosestPointOnFace(box1Corners, box2, contactNormal);
|
|
590
|
+
const box2Closest = findClosestPointOnFace(box2Corners, box1, contactNormal);
|
|
591
|
+
|
|
592
|
+
// Contact point is the midpoint between closest points on both boxes
|
|
593
|
+
const contactPoint = box1Closest.clone().add(box2Closest).multiplyScalar(0.5);
|
|
594
|
+
|
|
595
|
+
// Create the contact
|
|
596
|
+
const contact = new elation.physics.contact({
|
|
597
|
+
normal: contactNormal,
|
|
598
|
+
point: contactPoint.clone(),
|
|
599
|
+
penetration: 0, //-minPenetration, // negative value as it's penetration
|
|
600
|
+
//penetration: box1Closest.distanceTo(box2Closest),
|
|
601
|
+
bodies: [box1.body, box2.body]
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
contacts.push(contact);
|
|
605
|
+
return contacts;
|
|
606
|
+
}
|
|
607
|
+
function findClosestPointOnBox(corners, normal) {
|
|
608
|
+
let closestPoint = corners[0];
|
|
609
|
+
let minDistance = closestPoint.dot(normal);
|
|
610
|
+
|
|
611
|
+
for (let i = 1; i < corners.length; i++) {
|
|
612
|
+
const dist = corners[i].dot(normal);
|
|
613
|
+
if (dist < minDistance) {
|
|
614
|
+
closestPoint = corners[i];
|
|
615
|
+
minDistance = dist;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return closestPoint;
|
|
619
|
+
}
|
|
620
|
+
function findClosestPointOnFace(corners, otherBox, normal) {
|
|
621
|
+
let closestPoint = null;
|
|
622
|
+
let minDistance = Infinity;
|
|
623
|
+
|
|
624
|
+
// Check the projection of each corner onto the face of the other box
|
|
625
|
+
for (let i = 0; i < corners.length; i++) {
|
|
626
|
+
const corner = corners[i];
|
|
627
|
+
const projectedPoint = projectPointOntoFace(corner, otherBox, normal);
|
|
628
|
+
|
|
629
|
+
const distance = corner.distanceTo(projectedPoint);
|
|
630
|
+
if (distance < minDistance) {
|
|
631
|
+
closestPoint = projectedPoint;
|
|
632
|
+
minDistance = distance;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return closestPoint;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function projectPointOntoFace(point, box, normal) {
|
|
640
|
+
// First, find the center of the box in world space
|
|
641
|
+
const boxCenter = new THREE.Vector3();
|
|
642
|
+
box.body.localToWorldPos(boxCenter.set(0, 0, 0));
|
|
643
|
+
|
|
644
|
+
// Determine the plane of the face by using the box's normal and a point on the plane
|
|
645
|
+
// (the center of the face, which is aligned with one of the box's axes)
|
|
646
|
+
const halfSize = box.halfsize;
|
|
647
|
+
const offset = new THREE.Vector3().addVectors(box.min, box.max).multiplyScalar(.5);
|
|
648
|
+
const planePoint = new THREE.Vector3();
|
|
649
|
+
const planeNormal = normal.clone().normalize(); // Normal of the face (aligned with one of the box's axes)
|
|
650
|
+
|
|
651
|
+
// We need to determine which face we're projecting onto, so find the direction along the normal
|
|
652
|
+
// Set the point on the plane (face center) by adding/subtracting half the box size along the normal
|
|
653
|
+
for (let i = 0; i < 3; i++) {
|
|
654
|
+
const axisValue = planeNormal.getComponent(i);
|
|
655
|
+
if (axisValue !== 0) {
|
|
656
|
+
planePoint.setComponent(i, boxCenter.getComponent(i) + (axisValue * halfSize.getComponent(i)));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Now, we project the point onto the plane
|
|
661
|
+
// To do that, find the vector from the point to the plane point
|
|
662
|
+
const pointToPlane = point.clone().sub(planePoint);
|
|
663
|
+
|
|
664
|
+
// Project this vector onto the normal to find how far away the point is from the plane
|
|
665
|
+
const distance = pointToPlane.dot(planeNormal);
|
|
666
|
+
|
|
667
|
+
// Move the point onto the plane by subtracting the distance along the normal
|
|
668
|
+
const projectedPoint = point.clone().sub(planeNormal.multiplyScalar(distance));
|
|
669
|
+
|
|
670
|
+
// Now we need to clamp the projected point to the bounds of the box face
|
|
671
|
+
// For each axis, clamp the value within the bounds of the face
|
|
672
|
+
for (let i = 0; i < 3; i++) {
|
|
673
|
+
const axisValue = planeNormal.getComponent(i);
|
|
674
|
+
if (axisValue === 0) {
|
|
675
|
+
// Clamp the coordinate to be within the face bounds (which are defined by the box size)
|
|
676
|
+
const minVal = boxCenter.getComponent(i) - halfSize.getComponent(i);
|
|
677
|
+
const maxVal = boxCenter.getComponent(i) + halfSize.getComponent(i);
|
|
678
|
+
const value = projectedPoint.getComponent(i);
|
|
679
|
+
projectedPoint.setComponent(i, Math.max(minVal, Math.min(value, maxVal)));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return projectedPoint.add(offset);
|
|
684
|
+
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
return function(box1, box2, contacts, dt) {
|
|
690
|
+
const axes = getAxesToTest(box1, box2, scratch.axes);
|
|
691
|
+
let hasCollision = true;
|
|
692
|
+
let minPenetration = Infinity;
|
|
693
|
+
|
|
694
|
+
// Check for overlap along each axis
|
|
695
|
+
for (let axis of axes) {
|
|
696
|
+
const overlap = getPenetrationOnAxis(box1, box2, axis);
|
|
697
|
+
if (overlap === false) {
|
|
698
|
+
hasCollision = false; // Separating axis found, no collision
|
|
699
|
+
break;
|
|
700
|
+
} else if (overlap < minPenetration) {
|
|
701
|
+
minPenetration = overlap; // Track the minimum penetration
|
|
702
|
+
scratch.contactAxis.copy(axis); // Store the axis with minimum penetration
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (!hasCollision) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// If colliding, generate contact points and return them
|
|
711
|
+
return generateContacts(box1, box2, minPenetration, scratch.contactAxis, contacts);
|
|
712
|
+
}
|
|
713
|
+
})();
|
|
714
|
+
|
|
480
715
|
/* cylinder helpers */
|
|
481
716
|
this.cylinder_sphere = function() {
|
|
482
717
|
// closure scratch variables
|
|
@@ -637,20 +872,23 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
637
872
|
closest = new THREE.Vector3();
|
|
638
873
|
|
|
639
874
|
return function(capsule, sphere, contacts, dt) {
|
|
640
|
-
const
|
|
641
|
-
|
|
875
|
+
const capsuleScaledRadius = capsule.radius * Math.max(capsule.body.scale.x, capsule.body.scale.z),
|
|
876
|
+
combinedRadius = capsuleScaledRadius + sphere.radius,
|
|
877
|
+
capsuleDims = capsule.getDimensions();
|
|
642
878
|
sphere.body.localToWorldPos(point.set(0,0,0));
|
|
643
879
|
|
|
880
|
+
|
|
644
881
|
elation.physics.colliders.helperfuncs.closest_point_on_line(capsuleDims.start, capsuleDims.end, point, closest);
|
|
882
|
+
|
|
645
883
|
normal.subVectors(closest, point);
|
|
646
884
|
const distance = normal.length();
|
|
647
|
-
|
|
648
|
-
if (distance <=
|
|
885
|
+
|
|
886
|
+
if (distance <= combinedRadius) {
|
|
649
887
|
normal.divideScalar(distance);
|
|
650
888
|
let contact = new elation.physics.contact({
|
|
651
889
|
normal: normal.clone(), // allocate normal
|
|
652
|
-
point: closest.clone().add(normal.multiplyScalar(
|
|
653
|
-
penetration:
|
|
890
|
+
point: closest.clone().add(normal.multiplyScalar(capsuleScaledRadius)), // allocate point
|
|
891
|
+
penetration: combinedRadius - distance,
|
|
654
892
|
bodies: [capsule.body, sphere.body]
|
|
655
893
|
});
|
|
656
894
|
contacts.push(contact);
|
|
@@ -669,20 +907,23 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
669
907
|
|
|
670
908
|
let distSquared = elation.physics.colliders.helperfuncs.distancesquared_between_lines(capsule1Dims.start, capsule1Dims.end, capsule2Dims.start, capsule2Dims.end, p1, p2);
|
|
671
909
|
|
|
672
|
-
|
|
910
|
+
const capsule1ScaledRadius = capsule1.radius * Math.max(capsule1.body.scale.x, capsule1.body.scale.z),
|
|
911
|
+
capsule2ScaledRadius = capsule2.radius * Math.max(capsule2.body.scale.x, capsule2.body.scale.z);
|
|
912
|
+
|
|
913
|
+
if (distSquared <= Math.pow(capsule1ScaledRadius + capsule2ScaledRadius, 2)) {
|
|
673
914
|
console.log('CAPSULE COLLIDE', capsule1, capsule2);
|
|
674
915
|
let normal = new THREE.Vector3().subVectors(p2, p1),
|
|
675
916
|
point = p1.clone();
|
|
676
917
|
dist = Math.sqrt(distSquared);
|
|
677
918
|
normal.divideScalar(dist);
|
|
678
|
-
point.x += normal.x *
|
|
679
|
-
point.y += normal.y *
|
|
680
|
-
point.z += normal.z *
|
|
919
|
+
point.x += normal.x * capsule1ScaledRadius;
|
|
920
|
+
point.y += normal.y * capsule1ScaledRadius;
|
|
921
|
+
point.z += normal.z * capsule1ScaledRadius;
|
|
681
922
|
|
|
682
923
|
let contact = new elation.physics.contact({
|
|
683
924
|
normal: normal,
|
|
684
925
|
point: point,
|
|
685
|
-
penetration: dist - (
|
|
926
|
+
penetration: dist - (capsule1ScaledRadius + capsule2ScaledRadius),
|
|
686
927
|
bodies: [capsule1.body, capsule2.body]
|
|
687
928
|
});
|
|
688
929
|
contacts.push(contact);
|
|
@@ -796,13 +1037,12 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
796
1037
|
p3 = worldpoints.p3,
|
|
797
1038
|
normal = worldpoints.normal;
|
|
798
1039
|
|
|
799
|
-
|
|
800
|
-
|
|
1040
|
+
let velNormal = spherevel.dot(normal);
|
|
801
1041
|
|
|
802
1042
|
// Check if we're already in contact
|
|
803
1043
|
elation.physics.colliders.helperfuncs.closest_point_on_triangle(spherepos, p1, p2, p3, triangleClosestPoint);
|
|
804
1044
|
let triangleDistSquared = triangleClosestPoint.distanceToSquared(spherepos)
|
|
805
|
-
if (triangleDistSquared < sphere.radius * sphere.radius) {
|
|
1045
|
+
if (triangleDistSquared < sphere.radius * sphere.radius && velNormal <= 0) {
|
|
806
1046
|
let contact = new elation.physics.contact({
|
|
807
1047
|
normal: normal.clone(), // allocate normal
|
|
808
1048
|
point: triangleClosestPoint.clone(), // allocate point
|
|
@@ -875,7 +1115,7 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
875
1115
|
intersectionPoint.y - triangleClosestPoint.y,
|
|
876
1116
|
intersectionPoint.z - triangleClosestPoint.z
|
|
877
1117
|
);
|
|
878
|
-
|
|
1118
|
+
collisionNormal.normalize();
|
|
879
1119
|
|
|
880
1120
|
intersectionPoint.x += collisionNormal.x * -sphere.radius;
|
|
881
1121
|
intersectionPoint.y += collisionNormal.y * -sphere.radius;
|
|
@@ -1687,6 +1927,8 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
1687
1927
|
let triangles = [];
|
|
1688
1928
|
let radiusSq = 0;
|
|
1689
1929
|
|
|
1930
|
+
this.body.updateState(); // ensure scaleWorld is up to date before processing triangles
|
|
1931
|
+
let doubleSided = false;
|
|
1690
1932
|
if (!this.modeldata && this.mesh && this.mesh.geometry) {
|
|
1691
1933
|
if (this.mesh.geometry instanceof THREE.BufferGeometry) {
|
|
1692
1934
|
this.modeldata = {
|
|
@@ -1702,6 +1944,7 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
1702
1944
|
}
|
|
1703
1945
|
}
|
|
1704
1946
|
}
|
|
1947
|
+
doubleSided = this.mesh.material.side == THREE.DoubleSide;
|
|
1705
1948
|
}
|
|
1706
1949
|
if (this.modeldata) {
|
|
1707
1950
|
if (this.modeldata.index) {
|
|
@@ -1727,6 +1970,11 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
1727
1970
|
if (!triangle.isDegenerate()) {
|
|
1728
1971
|
triangles.push(triangle);
|
|
1729
1972
|
|
|
1973
|
+
if (doubleSided) {
|
|
1974
|
+
let triangle2 = new elation.physics.colliders.triangle(this.body, [p3, p2, p1]);
|
|
1975
|
+
triangles.push(triangle2);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1730
1978
|
let l1 = p1.lengthSq(),
|
|
1731
1979
|
l2 = p2.lengthSq(),
|
|
1732
1980
|
l3 = p3.lengthSq();
|
|
@@ -1761,7 +2009,7 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
1761
2009
|
}
|
|
1762
2010
|
}
|
|
1763
2011
|
}
|
|
1764
|
-
this.radius = Math.sqrt(radiusSq);
|
|
2012
|
+
this.radius = Math.sqrt(radiusSq) * Math.max(this.body.scaleWorld.x, this.body.scaleWorld.y, this.body.scaleWorld.z);
|
|
1765
2013
|
this.boundingSphere.radius = this.radius;
|
|
1766
2014
|
return triangles;
|
|
1767
2015
|
}
|
|
@@ -1797,11 +2045,11 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
1797
2045
|
bodies[obj.uuid].scale.copy(obj.scale);
|
|
1798
2046
|
bodies[obj.uuid].orientation.copy(obj.quaternion);
|
|
1799
2047
|
bodies[obj.uuid].object = this.body.object;
|
|
2048
|
+
parent.add(bodies[obj.uuid]);
|
|
1800
2049
|
if (obj instanceof THREE.Mesh) {
|
|
1801
2050
|
bodies[obj.uuid].setCollider('mesh', {mesh: obj, isroot: false });
|
|
1802
2051
|
//elation.events.add(bodies[obj.uuid], 'physics_collide', (ev) => elation.events.fire({type: 'physics_collide', element: this.body, event: ev}));
|
|
1803
2052
|
}
|
|
1804
|
-
parent.add(bodies[obj.uuid]);
|
|
1805
2053
|
}
|
|
1806
2054
|
parent = bodies[obj.uuid];
|
|
1807
2055
|
}
|
|
@@ -2143,21 +2391,20 @@ elation.require(['physics.common', 'utils.math'], function() {
|
|
|
2143
2391
|
var lastaccel = new THREE.Vector3();
|
|
2144
2392
|
|
|
2145
2393
|
if (this.bodies[0] && !this.bodies[0].state.sleeping) {
|
|
2146
|
-
velocityFromAccel -=
|
|
2394
|
+
velocityFromAccel -= lastaccel.copy(this.bodies[0].lastacceleration).multiplyScalar(duration).dot(this.normal);
|
|
2147
2395
|
}
|
|
2148
2396
|
if (this.bodies[1] && !this.bodies[1].state.sleeping) {
|
|
2149
|
-
velocityFromAccel +=
|
|
2397
|
+
velocityFromAccel += lastaccel.copy(this.bodies[1].lastacceleration).multiplyScalar(duration).dot(this.normal);
|
|
2150
2398
|
}
|
|
2151
2399
|
|
|
2152
2400
|
var restitution = this.restitution;
|
|
2153
|
-
|
|
2154
|
-
if (Math.abs(this.velocity.y) < 0.
|
|
2401
|
+
|
|
2402
|
+
if (Math.abs(this.velocity.y) < 0.01) { // FIXME - velocity threshold should be configurable
|
|
2155
2403
|
restitution = 0;
|
|
2156
2404
|
}
|
|
2157
|
-
|
|
2405
|
+
|
|
2158
2406
|
this.desiredDeltaVelocity = -this.velocity.y - restitution * (this.velocity.y - velocityFromAccel);
|
|
2159
2407
|
//if (this.desiredDeltaVelocity > 0) this.desiredDeltaVelocity *= -1;
|
|
2160
|
-
//console.log('desiredDeltaV: ' + this.desiredDeltaVelocity);
|
|
2161
2408
|
}
|
|
2162
2409
|
this.calculateInternals = function(duration) {
|
|
2163
2410
|
this.calculateContactMatrix();
|
package/scripts/common.js
CHANGED
|
@@ -15,6 +15,9 @@ elation.require(['engine.external.three.three'], function() {
|
|
|
15
15
|
toJSON() {
|
|
16
16
|
return {x: this._x, y: this._y, z: this._z};
|
|
17
17
|
}
|
|
18
|
+
clone() {
|
|
19
|
+
return super.clone();
|
|
20
|
+
}
|
|
18
21
|
}
|
|
19
22
|
// FIXME - orientation doesn't currently work properly because THREE.Quaternion already uses getters and setters internally
|
|
20
23
|
class CycloneQuaternion extends THREE.Quaternion {
|
|
@@ -34,6 +37,9 @@ elation.require(['engine.external.three.three'], function() {
|
|
|
34
37
|
this.changed = true;
|
|
35
38
|
return super.copy(quat);
|
|
36
39
|
}
|
|
40
|
+
clone() {
|
|
41
|
+
return super.clone();
|
|
42
|
+
}
|
|
37
43
|
set(x, y, z, w) {
|
|
38
44
|
this.changed = true;
|
|
39
45
|
return super.set(x, y, z, w);
|
package/scripts/cyclone.js
CHANGED
|
@@ -7,7 +7,7 @@ elation.require(["physics.common", "physics.processors", "physics.processors.wor
|
|
|
7
7
|
this.position = this.positionWorld = new THREE.Vector3();
|
|
8
8
|
this.orientation = this.orientationWorld = new THREE.Quaternion();
|
|
9
9
|
this.scale = this.scaleWorld = new THREE.Vector3(1, 1, 1);
|
|
10
|
-
this.substep = elation.utils.any(this.args.substep, true);
|
|
10
|
+
this.substep = false; //elation.utils.any(this.args.substep, true);
|
|
11
11
|
this.substepMaxDelta = elation.utils.any(this.args.substepMaxDelta, 20/1000);
|
|
12
12
|
this.substepMaxSteps = elation.utils.any(this.args.substepMaxSteps, 4);
|
|
13
13
|
this.processortype = elation.utils.any(this.args.processortype, 'cpu');
|
|
@@ -39,7 +39,7 @@ elation.require(["physics.common", "physics.processors", "physics.processors.wor
|
|
|
39
39
|
steps = Math.min(Math.round(t / this.substepMaxDelta), this.substepMaxSteps);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
var step =
|
|
42
|
+
var step = 1;
|
|
43
43
|
while (t > 0) {
|
|
44
44
|
var steptime = (step < steps ? Math.min(t, this.substepMaxDelta) : t);
|
|
45
45
|
|
package/scripts/forces.js
CHANGED
|
@@ -415,6 +415,8 @@ elation.require(['physics.common'], function() {
|
|
|
415
415
|
var _tmpvec2 = new THREE.Vector3();
|
|
416
416
|
|
|
417
417
|
this.apply = function() {
|
|
418
|
+
if (this.disabled) return;
|
|
419
|
+
|
|
418
420
|
var lws = this.body.localToWorldPos(_tmpvec1.copy(this.connectionpoint));
|
|
419
421
|
var ows = (this.other ? this.other.localToWorldPos(_tmpvec2.copy(this.otherconnectionpoint)) : this.anchor);
|
|
420
422
|
|
package/scripts/processors.js
CHANGED
|
@@ -53,6 +53,7 @@ elation.require(["physics.common"], function() {
|
|
|
53
53
|
var obj1 = potentialpair[0], obj2 = potentialpair[1];
|
|
54
54
|
// Get list of all contact points between the two objects
|
|
55
55
|
var contacts = obj1.getContacts(obj2, [], t);
|
|
56
|
+
/*
|
|
56
57
|
if (contacts && contacts.length > 0) {
|
|
57
58
|
// Resolve the deepest contact first
|
|
58
59
|
var deepest = this.getDeepestContact(contacts);
|
|
@@ -60,6 +61,14 @@ elation.require(["physics.common"], function() {
|
|
|
60
61
|
obj1.state.colliding = true;
|
|
61
62
|
obj2.state.colliding = true;
|
|
62
63
|
}
|
|
64
|
+
*/
|
|
65
|
+
if (contacts && contacts.length > 0) {
|
|
66
|
+
for (let i = 0; i < contacts.length; i++) {
|
|
67
|
+
collisions.push(contacts[i]);
|
|
68
|
+
obj1.state.colliding = true;
|
|
69
|
+
obj2.state.colliding = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
63
72
|
}
|
|
64
73
|
//console.log(potentials.length + ' potential crashes:', potentials, collisions);
|
|
65
74
|
}
|
|
@@ -103,7 +112,7 @@ elation.require(["physics.common"], function() {
|
|
|
103
112
|
let contact = contacts.shift();
|
|
104
113
|
contact.resolve(t, linearChange, angularChange, contacts);
|
|
105
114
|
// console.log('blah', contacts.length, linearChange[1].toArray().map(x => +x.toFixed(4)), contact.bodies)
|
|
106
|
-
break;
|
|
115
|
+
//break;
|
|
107
116
|
}
|
|
108
117
|
}
|
|
109
118
|
});
|