Skip to content

Commit

Permalink
feat(physics): add Solid and NoCollide collision classes
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaisthorpe committed Aug 19, 2023
1 parent ba182a2 commit 0f4616f
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-stingrays-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tedengine/ted': minor
---

Add draft of collision classes with Solid and NoCollide
22 changes: 22 additions & 0 deletions apps/docs/pages/examples/physics/collision-classes.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Collision Classes

import useBrowserOnly from '@hooks/useBrowserOnly';
import { TGame } from '@tedengine/ted';

export const Game = () => {
const onBrowser = useBrowserOnly();
if (!onBrowser) {
return null;
}
return (
<TGame
game={
new Worker(
new URL('@examples/physics/collision-classes.ts', import.meta.url)
)
}
/>
);
};

<Game />
89 changes: 89 additions & 0 deletions apps/docs/src/physics/collision-classes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { vec3 } from 'gl-matrix';
import {
TBoxComponent,
TGameState,
TActor,
TOrbitCamera,
TPlaneComponent,
TPlaneCollider,
TSphereComponent,
TSphereCollider,
TBoxCollider,
TEngine,
} from '@tedengine/ted';

class Cube extends TActor {
constructor(_: TEngine, x: number, y: number, z: number) {
super();

const box = new TBoxComponent(engine, this, 1, 1, 1);
this.rootComponent = box;
this.rootComponent.collider = new TBoxCollider(1, 1, 1);

this.rootComponent.transform.translation = vec3.fromValues(x, y, z);
}
}

class Sphere extends TActor {
constructor(_: TEngine, x: number, y: number, z: number) {
super();

const box = new TSphereComponent(engine, this, 0.5, 9, 12);
this.rootComponent = box;
this.rootComponent.collider = new TSphereCollider(0.5, 'NoCollide');

this.rootComponent.transform.translation = vec3.fromValues(x, y, z);
}
}

class Plane extends TActor {
constructor(engine: TEngine) {
super();

const box = new TPlaneComponent(engine, this, 10, 10);
this.rootComponent = box;
this.rootComponent.collider = new TPlaneCollider(10, 10);
this.rootComponent.mass = 0;

this.rootComponent.transform.translation = vec3.fromValues(0, 0, 0);
}
}

class ColliderState extends TGameState {
public async onCreate(engine: TEngine) {
this.onReady(engine);
}

public onReady(engine: TEngine) {
const box = new Cube(engine, 0, 5, 0);
this.addActor(box);

const box2 = new Cube(engine, -0.1, 10, 0.6);
this.addActor(box2);

const box3 = new Cube(engine, 0.6, 3, 0.2);
this.addActor(box3);

const sphere = new Sphere(engine, -0.1, 8, 0.6);
this.addActor(sphere);

const plane = new Plane(engine);
this.addActor(plane);

const orbitCamera = new TOrbitCamera(engine, 20);
orbitCamera.speed = 0.5;
orbitCamera.cameraComponent.showDebug = true;
this.addActor(orbitCamera);
this.activeCamera = orbitCamera;
}
}

const config = {
states: {
game: ColliderState,
},
defaultState: 'game',
};

const engine = new TEngine(config, postMessage.bind(self));
onmessage = engine.onMessage;
5 changes: 5 additions & 0 deletions packages/ted/src/core/level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ export default class TLevel {
this.world = new TWorld(this);
await this.world.create({
enableGravity: true,
defaultCollisionClass: 'Solid',
collisionClasses: [
{ name: 'Solid' },
{ name: 'NoCollide', ignores: ['Solid'] },
],
});
}

Expand Down
6 changes: 3 additions & 3 deletions packages/ted/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ import TPlaneCollider from './physics/colliders/plane-collider';
import type { TPlaneColliderConfig } from './physics/colliders/plane-collider';
import TSphereCollider from './physics/colliders/sphere-collider';
import type { TSphereColliderConfig } from './physics/colliders/sphere-collider';
import TWorld from './physics/world';
import TProgram from './renderer/program';
import TGame from './ui/components/Game';
import type { TGameContextData, TEngineContextData } from './ui/context';
Expand Down Expand Up @@ -174,8 +173,6 @@ export type { TEventTypesInput as TInputEventType, TKeyUpEvent, TKeyDownEvent };

export { TTransform };

export { TWorld };

export { TColliderType, TBoxCollider, TPlaneCollider, TSphereCollider };
export type {
ICollider,
Expand All @@ -188,3 +185,6 @@ export type {
export { TGame, useGameContext, useEngineContext, useEventQueue };

export type { TGameContextData, TEngineContextData };

export * from './physics/world';
export { default as TWorld } from './physics/world';
5 changes: 4 additions & 1 deletion packages/ted/src/physics/colliders/box-collider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ export interface TBoxColliderConfig {
width: number;
height: number;
depth: number;
collisionClass?: string;
}

export default class TBoxCollider {
constructor(
public width: number,
public height: number,
public depth: number
public depth: number,
private collisionClass?: string
) {}

public getConfig(): TBoxColliderConfig {
Expand All @@ -20,6 +22,7 @@ export default class TBoxCollider {
width: this.width,
height: this.height,
depth: this.depth,
collisionClass: this.collisionClass,
};
}
}
8 changes: 7 additions & 1 deletion packages/ted/src/physics/colliders/plane-collider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ export interface TPlaneColliderConfig {
type: TColliderType.PLANE;
width: number;
height: number;
collisionClass?: string;
}

export default class TPlaneCollider {
constructor(public width: number, public height: number) {}
constructor(
public width: number,
public height: number,
private collisionClass?: string
) {}

public getConfig(): TPlaneColliderConfig {
return {
type: TColliderType.PLANE,
width: this.width,
height: this.height,
collisionClass: this.collisionClass,
};
}
}
4 changes: 3 additions & 1 deletion packages/ted/src/physics/colliders/sphere-collider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { TColliderType } from '.';
export interface TSphereColliderConfig {
type: TColliderType.SPHERE;
radius: number;
collisionClass?: string;
}

export default class TSphereCollider {
constructor(public radius: number) {}
constructor(public radius: number, private collisionClass?: string) {}

public getConfig(): TSphereColliderConfig {
return {
type: TColliderType.SPHERE,
radius: this.radius,
collisionClass: this.collisionClass,
};
}
}
75 changes: 73 additions & 2 deletions packages/ted/src/physics/worker/cannon-world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,64 @@ export default class TCannonWorld implements TPhysicsWorld {

public collisions: TPhysicsCollision[] = [];

private defaultCollisionClass!: string;
private collisionClasses: {
[key: string]: {
groupNumber: number;
mask?: number;
ignoredBy: number[];
};
} = {};

public async create(config: TWorldConfig): Promise<void> {
const options: { gravity?: CANNON.Vec3 } = {};

if (config.enableGravity) {
options.gravity = new CANNON.Vec3(0, -9.82, 0);
}

this.defaultCollisionClass = config.defaultCollisionClass;

// Setup collision classes
let nextGroup = 1;
for (const cc of config.collisionClasses) {
let mask: number | undefined = undefined;

if (cc.ignores) {
for (const ig of cc.ignores) {
// Find the group number
const id = this.collisionClasses[ig];
id.ignoredBy.push(nextGroup);

if (mask === undefined) {
mask = ~id;
} else {
mask &= ~id;
}
}
}

this.collisionClasses[cc.name] = {
groupNumber: nextGroup,
mask,
ignoredBy: [],
};

nextGroup *= 2;
}

// Apply ignoredBy to masks
for (const name in this.collisionClasses) {
const cc = this.collisionClasses[name];
for (const ig of cc.ignoredBy) {
if (cc.mask === undefined) {
cc.mask = ~ig;
} else {
cc.mask &= ~ig;
}
}
}

this.world = new CANNON.World(options);

this.world.addEventListener(
Expand All @@ -41,7 +92,7 @@ export default class TCannonWorld implements TPhysicsWorld {
if (!bodyAUUID || !bodyBUUID) return;

// @todo collisions are going to be one frame behind?
this.collisions.push({ bodyA: bodyAUUID, bodyB: bodyBUUID });
this.collisions.push({ bodies: [bodyAUUID, bodyBUUID] });
}
);
}
Expand Down Expand Up @@ -98,7 +149,27 @@ export default class TCannonWorld implements TPhysicsWorld {
return;
}

const body = new CANNON.Body({ mass, shape });
const colliderFilter: {
collisionFilterGroup?: number;
collisionFilterMask?: number;
} = {};

if (collider.collisionClass) {
const collisionClass = this.collisionClasses[collider.collisionClass];

colliderFilter.collisionFilterGroup = collisionClass.groupNumber;
colliderFilter.collisionFilterMask = collisionClass.mask;
} else {
const defaultClass = this.collisionClasses[this.defaultCollisionClass];
colliderFilter.collisionFilterGroup = defaultClass.groupNumber;
colliderFilter.collisionFilterMask = defaultClass.mask;
}

const body = new CANNON.Body({
mass,
shape,
...colliderFilter,
});
body.position.set(...translation);
body.quaternion.set(...rotation);

Expand Down
3 changes: 1 addition & 2 deletions packages/ted/src/physics/worker/physics-world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ export interface TPhysicsBody {
}

export interface TPhysicsCollision {
bodyA: string;
bodyB: string;
bodies: [string, string];
}
7 changes: 7 additions & 0 deletions packages/ted/src/physics/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import { TPhysicsMessageTypes } from './worker/messages';

export interface TWorldConfig {
enableGravity: boolean;
defaultCollisionClass: string;
collisionClasses: TCollisionClass[];
}

export interface TCollisionClass {
name: string;
ignores?: [string];
}

export default class TWorld {
Expand Down

0 comments on commit 0f4616f

Please sign in to comment.