diff --git a/dist/cannon-es.cjs.js b/dist/cannon-es.cjs.js index d7caa2c8d..139ee69ce 100644 --- a/dist/cannon-es.cjs.js +++ b/dist/cannon-es.cjs.js @@ -3326,7 +3326,7 @@ class Body extends EventTarget { } this.allowSleep = typeof options.allowSleep !== 'undefined' ? options.allowSleep : true; - this.sleepState = 0; + this.sleepState = Body.AWAKE; this.sleepSpeedLimit = typeof options.sleepSpeedLimit !== 'undefined' ? options.sleepSpeedLimit : 0.1; this.sleepTimeLimit = typeof options.sleepTimeLimit !== 'undefined' ? options.sleepTimeLimit : 1; this.timeLastSleepy = 0; @@ -3393,7 +3393,7 @@ class Body extends EventTarget { wakeUp() { const prevState = this.sleepState; - this.sleepState = 0; + this.sleepState = Body.AWAKE; this.wakeUpAfterNarrowphase = false; if (prevState === Body.SLEEPING) { @@ -5042,6 +5042,7 @@ const Ray_intersectSphere_normal = new Vec3(); Ray.prototype[Shape.types.SPHERE] = Ray.prototype._intersectSphere; const intersectConvex_normal = new Vec3(); const intersectConvex_vector = new Vec3(); +Ray.prototype[Shape.types.CYLINDER] = Ray.prototype._intersectConvex; Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype._intersectConvex; const intersectTrimesh_normal = new Vec3(); const intersectTrimesh_localDirection = new Vec3(); @@ -7758,7 +7759,15 @@ const SPHSystem_update_u = new Vec3(); */ class Cylinder extends ConvexPolyhedron { - constructor(radiusTop, radiusBottom, height, numSegments) { + constructor(radiusTop = 1, radiusBottom = 1, height = 1, numSegments = 8) { + if (radiusTop < 0) { + throw new Error('The cylinder radiusTop cannot be negative.'); + } + + if (radiusBottom < 0) { + throw new Error('The cylinder radiusBottom cannot be negative.'); + } + const N = numSegments; const vertices = []; const axes = []; @@ -7812,6 +7821,11 @@ class Cylinder extends ConvexPolyhedron { faces, axes }); + this.type = Shape.types.CYLINDER; + this.radiusTop = radiusTop; + this.radiusBottom = radiusBottom; + this.height = height; + this.numSegments = numSegments; } } @@ -9711,6 +9725,10 @@ class Vec3Pool extends Pool { } +// Naming rule: based of the order in SHAPE_TYPES, +// the first part of the method is formed by the +// shape type that comes before, in the second part +// there is the shape type that comes after in the SHAPE_TYPES list const COLLISION_TYPES = { sphereSphere: Shape.types.SPHERE, spherePlane: Shape.types.SPHERE | Shape.types.PLANE, @@ -9728,6 +9746,13 @@ const COLLISION_TYPES = { planeParticle: Shape.types.PLANE | Shape.types.PARTICLE, boxParticle: Shape.types.BOX | Shape.types.PARTICLE, convexParticle: Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON, + cylinderCylinder: Shape.types.CYLINDER, + sphereCylinder: Shape.types.SPHERE | Shape.types.CYLINDER, + planeCylinder: Shape.types.PLANE | Shape.types.CYLINDER, + boxCylinder: Shape.types.BOX | Shape.types.CYLINDER, + convexCylinder: Shape.types.CONVEXPOLYHEDRON | Shape.types.CYLINDER, + heightfieldCylinder: Shape.types.HEIGHTFIELD | Shape.types.CYLINDER, + particleCylinder: Shape.types.PARTICLE | Shape.types.CYLINDER, sphereTrimesh: Shape.types.SPHERE | Shape.types.TRIMESH, planeTrimesh: Shape.types.PLANE | Shape.types.TRIMESH }; @@ -10895,6 +10920,14 @@ class Narrowphase { } } + heightfieldCylinder(hfShape, convexShape, hfPos, convexPos, hfQuat, convexQuat, hfBody, convexBody, rsi, rsj, justTest) { + return this.convexHeightfield(convexShape, hfShape, convexPos, hfPos, convexQuat, hfQuat, convexBody, hfBody, rsi, rsj, justTest); + } + + particleCylinder(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + return this.convexParticle(sj, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest); + } + sphereTrimesh(sphereShape, trimeshShape, spherePos, trimeshPos, sphereQuat, trimeshQuat, sphereBody, trimeshBody, rsi, rsj, justTest) { const edgeVertexA = sphereTrimesh_edgeVertexA; const edgeVertexB = sphereTrimesh_edgeVertexB; @@ -11247,7 +11280,14 @@ const particlePlane_relpos = new Vec3(); const particlePlane_projected = new Vec3(); Narrowphase.prototype[COLLISION_TYPES.planeParticle] = Narrowphase.prototype.planeParticle; const particleSphere_normal = new Vec3(); -Narrowphase.prototype[COLLISION_TYPES.sphereParticle] = Narrowphase.prototype.sphereParticle; // WIP +Narrowphase.prototype[COLLISION_TYPES.sphereParticle] = Narrowphase.prototype.sphereParticle; +Narrowphase.prototype[COLLISION_TYPES.cylinderCylinder] = Narrowphase.prototype.convexConvex; +Narrowphase.prototype[COLLISION_TYPES.sphereCylinder] = Narrowphase.prototype.sphereConvex; +Narrowphase.prototype[COLLISION_TYPES.planeCylinder] = Narrowphase.prototype.planeConvex; +Narrowphase.prototype[COLLISION_TYPES.boxCylinder] = Narrowphase.prototype.boxConvex; +Narrowphase.prototype[COLLISION_TYPES.convexCylinder] = Narrowphase.prototype.convexConvex; +Narrowphase.prototype[COLLISION_TYPES.heightfieldCylinder] = Narrowphase.prototype.heightfieldCylinder; +Narrowphase.prototype[COLLISION_TYPES.particleCylinder] = Narrowphase.prototype.particleCylinder; // WIP const cqj = new Quaternion(); const convexParticle_local = new Vec3(); @@ -11820,9 +11860,9 @@ class World extends EventTarget { this.accumulator -= dt; substeps++; - if (performance.now() - t0 > dt * 2 * 1000) { + if (performance.now() - t0 > dt * 1000) { // The framerate is not interactive anymore. - // We are at half of the target framerate. + // We are below the target framerate. // Better bail out. break; } diff --git a/dist/cannon-es.d.ts b/dist/cannon-es.d.ts index bb6aa6a55..dae833bdb 100644 --- a/dist/cannon-es.d.ts +++ b/dist/cannon-es.d.ts @@ -143,15 +143,15 @@ declare module "shapes/Shape" { import type { Body } from "objects/Body"; import type { Material } from "material/Material"; export const SHAPE_TYPES: { - SPHERE: 1; - PLANE: 2; - BOX: 4; - COMPOUND: 8; - CONVEXPOLYHEDRON: 16; - HEIGHTFIELD: 32; - PARTICLE: 64; - CYLINDER: 128; - TRIMESH: 256; + readonly SPHERE: 1; + readonly PLANE: 2; + readonly BOX: 4; + readonly COMPOUND: 8; + readonly CONVEXPOLYHEDRON: 16; + readonly HEIGHTFIELD: 32; + readonly PARTICLE: 64; + readonly CYLINDER: 128; + readonly TRIMESH: 256; }; export type ShapeType = typeof SHAPE_TYPES[keyof typeof SHAPE_TYPES]; export type ShapeOptions = { @@ -568,6 +568,16 @@ declare module "shapes/Particle" { calculateWorldAABB(pos: Vec3, quat: Quaternion, min: Vec3, max: Vec3): void; } } +declare module "shapes/Cylinder" { + import { ConvexPolyhedron } from "shapes/ConvexPolyhedron"; + export class Cylinder extends ConvexPolyhedron { + radiusTop: number; + radiusBottom: number; + height: number; + numSegments: number; + constructor(radiusTop?: number, radiusBottom?: number, height?: number, numSegments?: number); + } +} declare module "material/ContactMaterial" { import type { Material } from "material/Material"; export type ContactMaterialOptions = { @@ -606,6 +616,7 @@ declare module "world/Narrowphase" { import type { Plane } from "shapes/Plane"; import type { Trimesh } from "shapes/Trimesh"; import type { Heightfield } from "shapes/Heightfield"; + import { Cylinder } from "shapes/Cylinder"; import type { ContactMaterial } from "material/ContactMaterial"; import type { World } from "world/World"; export const COLLISION_TYPES: { @@ -625,6 +636,13 @@ declare module "world/Narrowphase" { planeParticle: 66; boxParticle: 68; convexParticle: 80; + cylinderCylinder: 128; + sphereCylinder: 129; + planeCylinder: 130; + boxCylinder: 132; + convexCylinder: 144; + heightfieldCylinder: 160; + particleCylinder: 192; sphereTrimesh: 257; planeTrimesh: 258; }; @@ -654,6 +672,13 @@ declare module "world/Narrowphase" { [COLLISION_TYPES.planeParticle]: typeof Narrowphase.prototype.planeParticle; [COLLISION_TYPES.boxParticle]: typeof Narrowphase.prototype.boxParticle; [COLLISION_TYPES.convexParticle]: typeof Narrowphase.prototype.convexParticle; + [COLLISION_TYPES.cylinderCylinder]: typeof Narrowphase.prototype.convexConvex; + [COLLISION_TYPES.sphereCylinder]: typeof Narrowphase.prototype.sphereConvex; + [COLLISION_TYPES.planeCylinder]: typeof Narrowphase.prototype.planeConvex; + [COLLISION_TYPES.boxCylinder]: typeof Narrowphase.prototype.boxConvex; + [COLLISION_TYPES.convexCylinder]: typeof Narrowphase.prototype.convexConvex; + [COLLISION_TYPES.heightfieldCylinder]: typeof Narrowphase.prototype.heightfieldCylinder; + [COLLISION_TYPES.particleCylinder]: typeof Narrowphase.prototype.particleCylinder; [COLLISION_TYPES.sphereTrimesh]: typeof Narrowphase.prototype.sphereTrimesh; [COLLISION_TYPES.planeTrimesh]: typeof Narrowphase.prototype.planeTrimesh; constructor(world: World); @@ -677,6 +702,8 @@ declare module "world/Narrowphase" { planeParticle(sj: Plane, si: Particle, xj: Vec3, xi: Vec3, qj: Quaternion, qi: Quaternion, bj: Body, bi: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; boxParticle(si: Box, sj: Particle, xi: Vec3, xj: Vec3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; convexParticle(sj: ConvexPolyhedron, si: Particle, xj: Vec3, xi: Vec3, qj: Quaternion, qi: Quaternion, bj: Body, bi: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; + heightfieldCylinder(hfShape: Heightfield, convexShape: Cylinder, hfPos: Vec3, convexPos: Vec3, hfQuat: Quaternion, convexQuat: Quaternion, hfBody: Body, convexBody: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; + particleCylinder(si: Particle, sj: Cylinder, xi: Vec3, xj: Vec3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; sphereTrimesh(sphereShape: Sphere, trimeshShape: Trimesh, spherePos: Vec3, trimeshPos: Vec3, sphereQuat: Quaternion, trimeshQuat: Quaternion, sphereBody: Body, trimeshBody: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; planeTrimesh(planeShape: Plane, trimeshShape: Trimesh, planePos: Vec3, trimeshPos: Vec3, planeQuat: Quaternion, trimeshQuat: Quaternion, planeBody: Body, trimeshBody: Body, rsi?: Shape | null, rsj?: Shape | null, justTest?: boolean): true | void; } @@ -883,6 +910,7 @@ declare module "collision/Ray" { [Shape.types.SPHERE]: typeof Ray.prototype._intersectSphere; [Shape.types.PLANE]: typeof Ray.prototype._intersectPlane; [Shape.types.BOX]: typeof Ray.prototype._intersectBox; + [Shape.types.CYLINDER]: typeof Ray.prototype._intersectConvex; [Shape.types.CONVEXPOLYHEDRON]: typeof Ray.prototype._intersectConvex; [Shape.types.HEIGHTFIELD]: typeof Ray.prototype._intersectHeightfield; [Shape.types.TRIMESH]: typeof Ray.prototype._intersectTrimesh; @@ -1478,12 +1506,6 @@ declare module "objects/SPHSystem" { nablaw(r: number): number; } } -declare module "shapes/Cylinder" { - import { ConvexPolyhedron } from "shapes/ConvexPolyhedron"; - export class Cylinder extends ConvexPolyhedron { - constructor(radiusTop: number, radiusBottom: number, height: number, numSegments: number); - } -} declare module "solver/SplitSolver" { import { Solver } from "solver/Solver"; import { Body } from "objects/Body"; diff --git a/dist/cannon-es.js b/dist/cannon-es.js index 0b1b72a09..900fa04d2 100644 --- a/dist/cannon-es.js +++ b/dist/cannon-es.js @@ -3322,7 +3322,7 @@ class Body extends EventTarget { } this.allowSleep = typeof options.allowSleep !== 'undefined' ? options.allowSleep : true; - this.sleepState = 0; + this.sleepState = Body.AWAKE; this.sleepSpeedLimit = typeof options.sleepSpeedLimit !== 'undefined' ? options.sleepSpeedLimit : 0.1; this.sleepTimeLimit = typeof options.sleepTimeLimit !== 'undefined' ? options.sleepTimeLimit : 1; this.timeLastSleepy = 0; @@ -3389,7 +3389,7 @@ class Body extends EventTarget { wakeUp() { const prevState = this.sleepState; - this.sleepState = 0; + this.sleepState = Body.AWAKE; this.wakeUpAfterNarrowphase = false; if (prevState === Body.SLEEPING) { @@ -5038,6 +5038,7 @@ const Ray_intersectSphere_normal = new Vec3(); Ray.prototype[Shape.types.SPHERE] = Ray.prototype._intersectSphere; const intersectConvex_normal = new Vec3(); const intersectConvex_vector = new Vec3(); +Ray.prototype[Shape.types.CYLINDER] = Ray.prototype._intersectConvex; Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype._intersectConvex; const intersectTrimesh_normal = new Vec3(); const intersectTrimesh_localDirection = new Vec3(); @@ -7754,7 +7755,15 @@ const SPHSystem_update_u = new Vec3(); */ class Cylinder extends ConvexPolyhedron { - constructor(radiusTop, radiusBottom, height, numSegments) { + constructor(radiusTop = 1, radiusBottom = 1, height = 1, numSegments = 8) { + if (radiusTop < 0) { + throw new Error('The cylinder radiusTop cannot be negative.'); + } + + if (radiusBottom < 0) { + throw new Error('The cylinder radiusBottom cannot be negative.'); + } + const N = numSegments; const vertices = []; const axes = []; @@ -7808,6 +7817,11 @@ class Cylinder extends ConvexPolyhedron { faces, axes }); + this.type = Shape.types.CYLINDER; + this.radiusTop = radiusTop; + this.radiusBottom = radiusBottom; + this.height = height; + this.numSegments = numSegments; } } @@ -9707,6 +9721,10 @@ class Vec3Pool extends Pool { } +// Naming rule: based of the order in SHAPE_TYPES, +// the first part of the method is formed by the +// shape type that comes before, in the second part +// there is the shape type that comes after in the SHAPE_TYPES list const COLLISION_TYPES = { sphereSphere: Shape.types.SPHERE, spherePlane: Shape.types.SPHERE | Shape.types.PLANE, @@ -9724,6 +9742,13 @@ const COLLISION_TYPES = { planeParticle: Shape.types.PLANE | Shape.types.PARTICLE, boxParticle: Shape.types.BOX | Shape.types.PARTICLE, convexParticle: Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON, + cylinderCylinder: Shape.types.CYLINDER, + sphereCylinder: Shape.types.SPHERE | Shape.types.CYLINDER, + planeCylinder: Shape.types.PLANE | Shape.types.CYLINDER, + boxCylinder: Shape.types.BOX | Shape.types.CYLINDER, + convexCylinder: Shape.types.CONVEXPOLYHEDRON | Shape.types.CYLINDER, + heightfieldCylinder: Shape.types.HEIGHTFIELD | Shape.types.CYLINDER, + particleCylinder: Shape.types.PARTICLE | Shape.types.CYLINDER, sphereTrimesh: Shape.types.SPHERE | Shape.types.TRIMESH, planeTrimesh: Shape.types.PLANE | Shape.types.TRIMESH }; @@ -10891,6 +10916,14 @@ class Narrowphase { } } + heightfieldCylinder(hfShape, convexShape, hfPos, convexPos, hfQuat, convexQuat, hfBody, convexBody, rsi, rsj, justTest) { + return this.convexHeightfield(convexShape, hfShape, convexPos, hfPos, convexQuat, hfQuat, convexBody, hfBody, rsi, rsj, justTest); + } + + particleCylinder(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + return this.convexParticle(sj, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest); + } + sphereTrimesh(sphereShape, trimeshShape, spherePos, trimeshPos, sphereQuat, trimeshQuat, sphereBody, trimeshBody, rsi, rsj, justTest) { const edgeVertexA = sphereTrimesh_edgeVertexA; const edgeVertexB = sphereTrimesh_edgeVertexB; @@ -11243,7 +11276,14 @@ const particlePlane_relpos = new Vec3(); const particlePlane_projected = new Vec3(); Narrowphase.prototype[COLLISION_TYPES.planeParticle] = Narrowphase.prototype.planeParticle; const particleSphere_normal = new Vec3(); -Narrowphase.prototype[COLLISION_TYPES.sphereParticle] = Narrowphase.prototype.sphereParticle; // WIP +Narrowphase.prototype[COLLISION_TYPES.sphereParticle] = Narrowphase.prototype.sphereParticle; +Narrowphase.prototype[COLLISION_TYPES.cylinderCylinder] = Narrowphase.prototype.convexConvex; +Narrowphase.prototype[COLLISION_TYPES.sphereCylinder] = Narrowphase.prototype.sphereConvex; +Narrowphase.prototype[COLLISION_TYPES.planeCylinder] = Narrowphase.prototype.planeConvex; +Narrowphase.prototype[COLLISION_TYPES.boxCylinder] = Narrowphase.prototype.boxConvex; +Narrowphase.prototype[COLLISION_TYPES.convexCylinder] = Narrowphase.prototype.convexConvex; +Narrowphase.prototype[COLLISION_TYPES.heightfieldCylinder] = Narrowphase.prototype.heightfieldCylinder; +Narrowphase.prototype[COLLISION_TYPES.particleCylinder] = Narrowphase.prototype.particleCylinder; // WIP const cqj = new Quaternion(); const convexParticle_local = new Vec3(); @@ -11816,9 +11856,9 @@ class World extends EventTarget { this.accumulator -= dt; substeps++; - if (performance.now() - t0 > dt * 2 * 1000) { + if (performance.now() - t0 > dt * 1000) { // The framerate is not interactive anymore. - // We are at half of the target framerate. + // We are below the target framerate. // Better bail out. break; } diff --git a/examples/convex.html b/examples/convex.html index 66340b1cb..4ad75028a 100644 --- a/examples/convex.html +++ b/examples/convex.html @@ -70,7 +70,7 @@ world.addBody(tetraBody) demo.addVisual(tetraBody) - // ConvexPolyhedron cylinder shape + // The Cylinder is a ConvexPolyhedron under the hood const height = 2 const radius = 0.5 const detail = 20 diff --git a/examples/shapes.html b/examples/shapes.html index 53a900d97..f5379ab11 100644 --- a/examples/shapes.html +++ b/examples/shapes.html @@ -66,12 +66,10 @@ // Cylinder shape 2 const cylinderShape2 = new CANNON.Cylinder(size, size, size * 2, 10) - const quaternion = new CANNON.Quaternion() - quaternion.setFromEuler(Math.PI / 2, Math.PI / 2, 0) - cylinderShape2.transformAllPoints(new CANNON.Vec3(), quaternion) const cylinderBody2 = new CANNON.Body({ mass }) cylinderBody2.addShape(cylinderShape2) cylinderBody2.position.set(size * 2, size * 4 + 1, size * 2) + cylinderBody2.quaternion.setFromEuler(Math.PI / 2, Math.PI / 2, 0) world.addBody(cylinderBody2) demo.addVisual(cylinderBody2) diff --git a/examples/stacks.html b/examples/stacks.html index 8f9e30bc5..fe5700bcc 100644 --- a/examples/stacks.html +++ b/examples/stacks.html @@ -149,6 +149,26 @@ demo.addVisual(body2) }) + demo.addScene('sphere/cylinder', () => { + const world = setupWorld(demo) + + // Sphere + const sphereShape = new CANNON.Sphere(size * 0.5) + const body1 = new CANNON.Body({ mass }) + body1.addShape(sphereShape) + body1.position.set(0, size * 6, 0) + world.addBody(body1) + demo.addVisual(body1) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body2 = new CANNON.Body({ mass }) + body2.addShape(cylinderShape) + body2.position.set(0, size * 3, 0) + world.addBody(body2) + demo.addVisual(body2) + }) + demo.addScene('sphere/particle', () => { const world = setupWorld(demo) @@ -202,6 +222,18 @@ demo.addVisual(body) }) + demo.addScene('plane/cylinder', () => { + const world = setupWorld(demo) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body = new CANNON.Body({ mass }) + body.addShape(cylinderShape) + body.position.set(0, size * 3, 0) + world.addBody(body) + demo.addVisual(body) + }) + demo.addScene('plane/particle', () => { const world = setupWorld(demo) @@ -270,6 +302,26 @@ demo.addVisual(body2) }) + demo.addScene('box/cylinder', () => { + const world = setupWorld(demo) + + // Box + const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5)) + const body1 = new CANNON.Body({ mass }) + body1.addShape(boxShape) + body1.position.set(0, size * 5, 0) + world.addBody(body1) + demo.addVisual(body1) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body2 = new CANNON.Body({ mass }) + body2.addShape(cylinderShape) + body2.position.set(0, size * 2, 0) + world.addBody(body2) + demo.addVisual(body2) + }) + demo.addScene('box/particle', () => { const world = setupWorld(demo) @@ -323,6 +375,24 @@ demo.addVisual(body2) }) + demo.addScene('compound/cylinder', () => { + const world = setupWorld(demo) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body1 = new CANNON.Body({ mass }) + body1.addShape(cylinderShape) + body1.position.set(0, size * 5, 0) + world.addBody(body1) + demo.addVisual(body1) + + // Compound + const body2 = createCompound(5) + body2.position.set(0, size, 0) + world.addBody(body2) + demo.addVisual(body2) + }) + demo.addScene('compound/particle', () => { const world = setupWorld(demo) @@ -361,6 +431,26 @@ demo.addVisual(body2) }) + demo.addScene('convex/cylinder', () => { + const world = setupWorld(demo) + + // Convex tetra + const tetraShape = createTetra() + const body1 = new CANNON.Body({ mass }) + body1.addShape(tetraShape) + body1.position.set(-0.1, size * 5, 0.1) + world.addBody(body1) + demo.addVisual(body1) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body2 = new CANNON.Body({ mass }) + body2.addShape(cylinderShape) + body2.position.set(0, size * 2, 0) + world.addBody(body2) + demo.addVisual(body2) + }) + demo.addScene('convex/particle', () => { const world = setupWorld(demo) @@ -381,6 +471,45 @@ demo.addVisual(particle) }) + demo.addScene('cylinder/cylinder', () => { + const world = setupWorld(demo) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body1 = new CANNON.Body({ mass }) + body1.addShape(cylinderShape) + body1.position.set(0, size * 3, 0) + world.addBody(body1) + demo.addVisual(body1) + + // Cylinder + const body2 = new CANNON.Body({ mass }) + body2.addShape(cylinderShape) + body2.position.set(0, size * 6, 0) + body2.quaternion.setFromEuler(-Math.PI / 2, 0, 0) + world.addBody(body2) + demo.addVisual(body2) + }) + + demo.addScene('cylinder/particle', () => { + const world = setupWorld(demo) + + // Cylinder + const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10) + const body1 = new CANNON.Body({ mass }) + body1.addShape(cylinderShape) + body1.position.set(0, size * 2, 0) + world.addBody(body1) + demo.addVisual(body1) + + // Particle + const particle = new CANNON.Body({ mass: 1 }) + particle.addShape(new CANNON.Particle()) + particle.position.set(0, size * 4, 0) + world.addBody(particle) + demo.addVisual(particle) + }) + demo.start() function setupWorld(demo) { diff --git a/readme.md b/readme.md index 22a1166ca..0a129d46b 100644 --- a/readme.md +++ b/readme.md @@ -9,6 +9,7 @@ These minor changes and improvements were also made: - These PRs from the original repo were merged: [schteppe/cannon.js#433](https://github.com/schteppe/cannon.js/pull/433), [schteppe/cannon.js#430](https://github.com/schteppe/cannon.js/pull/430), [schteppe/cannon.js#418](https://github.com/schteppe/cannon.js/pull/418), [schteppe/cannon.js#360](https://github.com/schteppe/cannon.js/pull/360), [schteppe/cannon.js#265](https://github.com/schteppe/cannon.js/pull/265), [schteppe/cannon.js#392](https://github.com/schteppe/cannon.js/pull/392) - The `ConvexPolyhedron` constructor now accepts an object instead of a list of arguments. [#6](https://github.com/pmndrs/cannon-es/pull/6) - The `Cylinder` is now oriented on the Y axis. [#30](https://github.com/pmndrs/cannon-es/pull/30) +- The `type` property of the `Cylinder` is now equal to `Shape.types.CYLINDER`. [#59](https://github.com/pmndrs/cannon-es/pull/59) - `Body.applyImpulse()` and `Body.applyForce()` are now relative to the center of the body instead of the center of the world [86b0444](https://github.com/schteppe/cannon.js/commit/86b0444c93356aeaa25dd1af795fa162574c6f4b) - Added a property `World.hasActiveBodies: boolean` which will be false when all physics bodies are sleeping. This allows for invalidating frames when physics aren't active for increased performance. - Deprecated properties and methods have been removed. diff --git a/src/collision/Ray.ts b/src/collision/Ray.ts index a5d1d7834..c261145d0 100644 --- a/src/collision/Ray.ts +++ b/src/collision/Ray.ts @@ -65,6 +65,7 @@ export class Ray { [Shape.types.SPHERE]: typeof Ray.prototype._intersectSphere; [Shape.types.PLANE]: typeof Ray.prototype._intersectPlane; [Shape.types.BOX]: typeof Ray.prototype._intersectBox; + [Shape.types.CYLINDER]: typeof Ray.prototype._intersectConvex; [Shape.types.CONVEXPOLYHEDRON]: typeof Ray.prototype._intersectConvex; [Shape.types.HEIGHTFIELD]: typeof Ray.prototype._intersectHeightfield; [Shape.types.TRIMESH]: typeof Ray.prototype._intersectTrimesh @@ -199,9 +200,9 @@ export class Ray { return } - const intersectMethod = this[shape.type as RayMode] + const intersectMethod = this[shape.type as RayMode] as any if (intersectMethod) { - ;(intersectMethod as any).call(this, shape, quat, position, body, shape) + intersectMethod.call(this, shape, quat, position, body, shape) } } @@ -714,6 +715,8 @@ const intersectConvex_minDistNormal = new Vec3() const intersectConvex_minDistIntersect = new Vec3() const intersectConvex_vector = new Vec3() +Ray.prototype[Shape.types.CYLINDER] = Ray.prototype._intersectConvex + Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype._intersectConvex const intersectTrimesh_normal = new Vec3() diff --git a/src/shapes/Cylinder.ts b/src/shapes/Cylinder.ts index 169139eba..2d0b370b5 100644 --- a/src/shapes/Cylinder.ts +++ b/src/shapes/Cylinder.ts @@ -1,5 +1,6 @@ import { ConvexPolyhedron } from '../shapes/ConvexPolyhedron' import { Vec3 } from '../math/Vec3' +import { Shape } from './Shape' /** * @class Cylinder @@ -12,7 +13,20 @@ import { Vec3 } from '../math/Vec3' * @param {Number} numSegments The number of segments to build the cylinder out of */ export class Cylinder extends ConvexPolyhedron { - constructor(radiusTop: number, radiusBottom: number, height: number, numSegments: number) { + radiusTop: number + radiusBottom: number + height: number + numSegments: number + + constructor(radiusTop: number = 1, radiusBottom: number = 1, height: number = 1, numSegments: number = 8) { + if (radiusTop < 0) { + throw new Error('The cylinder radiusTop cannot be negative.') + } + + if (radiusBottom < 0) { + throw new Error('The cylinder radiusBottom cannot be negative.') + } + const N = numSegments const vertices = [] const axes = [] @@ -63,5 +77,11 @@ export class Cylinder extends ConvexPolyhedron { faces.push(temp) super({ vertices, faces, axes }) + + this.type = Shape.types.CYLINDER + this.radiusTop = radiusTop + this.radiusBottom = radiusBottom + this.height = height + this.numSegments = numSegments } } diff --git a/src/shapes/Shape.ts b/src/shapes/Shape.ts index e0240e5a6..84f802649 100644 --- a/src/shapes/Shape.ts +++ b/src/shapes/Shape.ts @@ -4,16 +4,16 @@ import type { Body } from '../objects/Body' import type { Material } from '../material/Material' export const SHAPE_TYPES = { - SPHERE: 1 as const, - PLANE: 2 as const, - BOX: 4 as const, - COMPOUND: 8 as const, - CONVEXPOLYHEDRON: 16 as const, - HEIGHTFIELD: 32 as const, - PARTICLE: 64 as const, - CYLINDER: 128 as const, - TRIMESH: 256 as const, -} + SPHERE: 1, + PLANE: 2, + BOX: 4, + COMPOUND: 8, + CONVEXPOLYHEDRON: 16, + HEIGHTFIELD: 32, + PARTICLE: 64, + CYLINDER: 128, + TRIMESH: 256, +} as const export type ShapeType = typeof SHAPE_TYPES[keyof typeof SHAPE_TYPES] diff --git a/src/world/Narrowphase.ts b/src/world/Narrowphase.ts index 870671bb8..1bf464324 100644 --- a/src/world/Narrowphase.ts +++ b/src/world/Narrowphase.ts @@ -15,9 +15,14 @@ import type { Particle } from '../shapes/Particle' import type { Plane } from '../shapes/Plane' import type { Trimesh } from '../shapes/Trimesh' import type { Heightfield } from '../shapes/Heightfield' +import { Cylinder } from '../shapes/Cylinder' import type { ContactMaterial } from '../material/ContactMaterial' import type { World } from '../world/World' +// Naming rule: based of the order in SHAPE_TYPES, +// the first part of the method is formed by the +// shape type that comes before, in the second part +// there is the shape type that comes after in the SHAPE_TYPES list export const COLLISION_TYPES = { sphereSphere: Shape.types.SPHERE as 1, spherePlane: (Shape.types.SPHERE | Shape.types.PLANE) as 3, @@ -35,6 +40,13 @@ export const COLLISION_TYPES = { planeParticle: (Shape.types.PLANE | Shape.types.PARTICLE) as 66, boxParticle: (Shape.types.BOX | Shape.types.PARTICLE) as 68, convexParticle: (Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON) as 80, + cylinderCylinder: Shape.types.CYLINDER as 128, + sphereCylinder: (Shape.types.SPHERE | Shape.types.CYLINDER) as 129, + planeCylinder: (Shape.types.PLANE | Shape.types.CYLINDER) as 130, + boxCylinder: (Shape.types.BOX | Shape.types.CYLINDER) as 132, + convexCylinder: (Shape.types.CONVEXPOLYHEDRON | Shape.types.CYLINDER) as 144, + heightfieldCylinder: (Shape.types.HEIGHTFIELD | Shape.types.CYLINDER) as 160, + particleCylinder: (Shape.types.PARTICLE | Shape.types.CYLINDER) as 192, sphereTrimesh: (Shape.types.SPHERE | Shape.types.TRIMESH) as 257, planeTrimesh: (Shape.types.PLANE | Shape.types.TRIMESH) as 258, } @@ -75,6 +87,13 @@ export class Narrowphase { [COLLISION_TYPES.planeParticle]: typeof Narrowphase.prototype.planeParticle; [COLLISION_TYPES.boxParticle]: typeof Narrowphase.prototype.boxParticle; [COLLISION_TYPES.convexParticle]: typeof Narrowphase.prototype.convexParticle; + [COLLISION_TYPES.cylinderCylinder]: typeof Narrowphase.prototype.convexConvex; + [COLLISION_TYPES.sphereCylinder]: typeof Narrowphase.prototype.sphereConvex; + [COLLISION_TYPES.planeCylinder]: typeof Narrowphase.prototype.planeConvex; + [COLLISION_TYPES.boxCylinder]: typeof Narrowphase.prototype.boxConvex; + [COLLISION_TYPES.convexCylinder]: typeof Narrowphase.prototype.convexConvex; + [COLLISION_TYPES.heightfieldCylinder]: typeof Narrowphase.prototype.heightfieldCylinder; + [COLLISION_TYPES.particleCylinder]: typeof Narrowphase.prototype.particleCylinder; [COLLISION_TYPES.sphereTrimesh]: typeof Narrowphase.prototype.sphereTrimesh; [COLLISION_TYPES.planeTrimesh]: typeof Narrowphase.prototype.planeTrimesh @@ -1551,6 +1570,50 @@ export class Narrowphase { } } + heightfieldCylinder( + hfShape: Heightfield, + convexShape: Cylinder, + hfPos: Vec3, + convexPos: Vec3, + hfQuat: Quaternion, + convexQuat: Quaternion, + hfBody: Body, + convexBody: Body, + rsi?: Shape | null, + rsj?: Shape | null, + justTest?: boolean + ): true | void { + return this.convexHeightfield( + convexShape as ConvexPolyhedron, + hfShape, + convexPos, + hfPos, + convexQuat, + hfQuat, + convexBody, + hfBody, + rsi, + rsj, + justTest + ) + } + + particleCylinder( + si: Particle, + sj: Cylinder, + xi: Vec3, + xj: Vec3, + qi: Quaternion, + qj: Quaternion, + bi: Body, + bj: Body, + rsi?: Shape | null, + rsj?: Shape | null, + justTest?: boolean + ): true | void { + return this.convexParticle(sj as ConvexPolyhedron, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest) + } + sphereTrimesh( sphereShape: Sphere, trimeshShape: Trimesh, @@ -2016,6 +2079,14 @@ const particleSphere_normal = new Vec3() Narrowphase.prototype[COLLISION_TYPES.sphereParticle] = Narrowphase.prototype.sphereParticle +Narrowphase.prototype[COLLISION_TYPES.cylinderCylinder] = Narrowphase.prototype.convexConvex +Narrowphase.prototype[COLLISION_TYPES.sphereCylinder] = Narrowphase.prototype.sphereConvex +Narrowphase.prototype[COLLISION_TYPES.planeCylinder] = Narrowphase.prototype.planeConvex +Narrowphase.prototype[COLLISION_TYPES.boxCylinder] = Narrowphase.prototype.boxConvex +Narrowphase.prototype[COLLISION_TYPES.convexCylinder] = Narrowphase.prototype.convexConvex +Narrowphase.prototype[COLLISION_TYPES.heightfieldCylinder] = Narrowphase.prototype.heightfieldCylinder +Narrowphase.prototype[COLLISION_TYPES.particleCylinder] = Narrowphase.prototype.particleCylinder + // WIP const cqj = new Quaternion() const convexParticle_local = new Vec3()