Update physics
This commit is contained in:
parent
1b7dee4be0
commit
d9d8d03c36
7 changed files with 445 additions and 550 deletions
|
|
@ -1,61 +0,0 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IShape } from './i-shape';
|
||||
|
||||
export abstract class BoundingBoxBase {
|
||||
constructor(
|
||||
public readonly shape: IShape,
|
||||
protected _xMin: number = 0,
|
||||
protected _xMax: number = 0,
|
||||
protected _yMin: number = 0,
|
||||
protected _yMax: number = 0
|
||||
) {}
|
||||
|
||||
public get 0(): number {
|
||||
return this._xMin;
|
||||
}
|
||||
|
||||
public get 1(): number {
|
||||
return this._xMax;
|
||||
}
|
||||
|
||||
public get 2(): number {
|
||||
return this._yMin;
|
||||
}
|
||||
|
||||
public get 3(): number {
|
||||
return this._yMax;
|
||||
}
|
||||
|
||||
public get xMin(): number {
|
||||
return this._xMin;
|
||||
}
|
||||
|
||||
public get xMax(): number {
|
||||
return this._xMax;
|
||||
}
|
||||
|
||||
public get yMin(): number {
|
||||
return this._yMin;
|
||||
}
|
||||
|
||||
public get yMax(): number {
|
||||
return this._yMax;
|
||||
}
|
||||
|
||||
public get topLeft(): vec2 {
|
||||
return vec2.fromValues(this._xMin, this._yMax);
|
||||
}
|
||||
|
||||
public get size(): vec2 {
|
||||
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
||||
}
|
||||
|
||||
public intersects(other: BoundingBoxBase): boolean {
|
||||
return (
|
||||
this._xMin < other._xMax &&
|
||||
this._xMax > other._xMin &&
|
||||
this._yMin < other._yMax &&
|
||||
this._yMax > other._yMin
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { BoundingBoxBase } from './bounding-box-base';
|
||||
import { ImmutableBoundingBox } from './immutable-bounding-box';
|
||||
|
||||
export class BoundingBox extends BoundingBoxBase {
|
||||
public get topLeft(): vec2 {
|
||||
return vec2.fromValues(this._xMin, this._yMax);
|
||||
}
|
||||
|
||||
public set topLeft(value: vec2) {
|
||||
this._xMin = value.x;
|
||||
this._yMax = value.y;
|
||||
}
|
||||
|
||||
public get size(): vec2 {
|
||||
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
||||
}
|
||||
|
||||
public set size(value: vec2) {
|
||||
this._xMax = this.xMin + value.x;
|
||||
this._yMin = this.yMax - value.y;
|
||||
}
|
||||
|
||||
public cloneAsImmutable(): ImmutableBoundingBox {
|
||||
return new ImmutableBoundingBox(
|
||||
this.shape,
|
||||
this.xMin,
|
||||
this.xMax,
|
||||
this.yMin,
|
||||
this.yMax
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { GameObject } from '../objects/game-object';
|
||||
import { BoundingBox } from './bounding-box';
|
||||
|
||||
export interface IShape {
|
||||
readonly boundingBox: BoundingBox;
|
||||
readonly gameObject?: GameObject;
|
||||
|
||||
minDistance(target: vec2): number;
|
||||
normal(from: vec2): vec2;
|
||||
clone(): IShape;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import { BoundingBoxBase } from './bounding-box-base';
|
||||
|
||||
export class ImmutableBoundingBox extends BoundingBoxBase {}
|
||||
|
|
@ -1,110 +1,89 @@
|
|||
import { mat2d, vec2 } from 'gl-matrix';
|
||||
import { Circle, Drawable, DrawableDescriptor } from 'sdf-2d';
|
||||
import { GameObject } from '../../objects/game-object';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { IShape } from '../i-shape';
|
||||
import { CircleShape } from './circle-shape';
|
||||
import { Drawable, DrawableDescriptor } from 'sdf-2d';
|
||||
import { BoundingCircle } from '../../physics/bounds/bounding-circle';
|
||||
|
||||
export class BlobShape extends Drawable implements IShape {
|
||||
export class BlobShape extends Drawable {
|
||||
public static descriptor: DrawableDescriptor = {
|
||||
sdf: {
|
||||
shader: `
|
||||
uniform struct {
|
||||
vec2 headCenter;
|
||||
vec2 leftFootCenter;
|
||||
vec2 rightFootCenter;
|
||||
float headRadius;
|
||||
float footRadius;
|
||||
float k;
|
||||
}[BLOB_COUNT] blobs;
|
||||
uniform vec2 headCenters[BLOB_COUNT];
|
||||
uniform vec2 leftFootCenters[BLOB_COUNT];
|
||||
uniform vec2 rightFootCenters[BLOB_COUNT];
|
||||
uniform float headRadii[BLOB_COUNT];
|
||||
uniform float footRadii[BLOB_COUNT];
|
||||
//uniform float ks[BLOB_COUNT];
|
||||
|
||||
float smoothMin(float a, float b)
|
||||
{
|
||||
const float k = 80.0;
|
||||
float res = exp2( -k*a ) + exp2( -k*b );
|
||||
return -log2( res )/k;
|
||||
float smoothMin(float a, float b)
|
||||
{
|
||||
const float k = 80.0;
|
||||
float res = exp2( -k*a ) + exp2( -k*b );
|
||||
return -log2( res )/k;
|
||||
}
|
||||
|
||||
float circleDistance(vec2 circleCenter, float radius, vec2 target) {
|
||||
return distance(target, circleCenter) - radius;
|
||||
}
|
||||
|
||||
float blobMinDistance(vec2 target, out float colorIndex) {
|
||||
float minDistance = 1000.0;
|
||||
colorIndex = 3.0;
|
||||
|
||||
for (int i = 0; i < BLOB_COUNT; i++) {
|
||||
float headDistance = circleDistance(headCenters[i], headRadii[i], target);
|
||||
float leftFootDistance = circleDistance(leftFootCenters[i], footRadii[i], target);
|
||||
float rightFootDistance = circleDistance(rightFootCenters[i], footRadii[i], target);
|
||||
|
||||
float res = min(
|
||||
smoothMin(headDistance, leftFootDistance),
|
||||
smoothMin(headDistance, rightFootDistance)
|
||||
);
|
||||
|
||||
minDistance = min(minDistance, res);
|
||||
}
|
||||
|
||||
float circleDistance(vec2 circleCenter, float radius) {
|
||||
return distance(position, circleCenter) - radius;
|
||||
}
|
||||
|
||||
void blobMinDistance(inout float minDistance, inout float color) {
|
||||
for (int i = 0; i < BLOB_COUNT; i++) {
|
||||
float headDistance = circleDistance(blobs[i].headCenter, blobs[i].headRadius);
|
||||
float leftFootDistance = circleDistance(blobs[i].leftFootCenter, blobs[i].footRadius);
|
||||
float rightFootDistance = circleDistance(blobs[i].rightFootCenter, blobs[i].footRadius);
|
||||
|
||||
float res = min(
|
||||
smoothMin(headDistance, leftFootDistance),
|
||||
smoothMin(headDistance, rightFootDistance)
|
||||
);
|
||||
|
||||
minDistance = min(minDistance, res);
|
||||
color = mix(1.0, color, step(distanceNdcPixelSize + SURFACE_OFFSET, res));
|
||||
}
|
||||
}
|
||||
`,
|
||||
return minDistance;
|
||||
}
|
||||
`,
|
||||
distanceFunctionName: 'blobMinDistance',
|
||||
},
|
||||
uniformName: 'blobs',
|
||||
propertyUniformMapping: {
|
||||
footRadius: 'footRadii',
|
||||
headRadius: 'headRadii',
|
||||
rightFootCenter: 'rightFootCenters',
|
||||
leftFootCenter: 'leftFootCenters',
|
||||
headCenter: 'headCenters',
|
||||
},
|
||||
uniformCountMacroName: 'BLOB_COUNT',
|
||||
shaderCombinationSteps: [1],
|
||||
empty: new BlobShape(vec2.fromValues(0, 0)),
|
||||
shaderCombinationSteps: [0, 1, 10],
|
||||
empty: new BlobShape(),
|
||||
};
|
||||
protected head: BoundingCircle;
|
||||
protected leftFoot: BoundingCircle;
|
||||
protected rightFoot: BoundingCircle;
|
||||
|
||||
public readonly boundingCircleRadius = 100;
|
||||
|
||||
protected readonly headRadius = 40;
|
||||
protected readonly footRadius = 15;
|
||||
|
||||
private readonly headOffset = vec2.fromValues(0, -15);
|
||||
private readonly leftFootOffset = vec2.fromValues(-12, -60);
|
||||
private readonly rightFootOffset = vec2.fromValues(12, -60);
|
||||
|
||||
public readonly isInverted = false;
|
||||
protected boundingCircle = new CircleShape(vec2.create(), this.boundingCircleRadius);
|
||||
protected head = new Circle(vec2.create(), this.headRadius);
|
||||
protected leftFoot = new Circle(vec2.create(), this.footRadius);
|
||||
protected rightFoot = new Circle(vec2.create(), this.footRadius);
|
||||
|
||||
public constructor(center: vec2, public readonly gameObject: GameObject = null) {
|
||||
public constructor() {
|
||||
super();
|
||||
this.position = center;
|
||||
|
||||
const circle = new BoundingCircle(null, vec2.create(), 200);
|
||||
this.setCircles([circle, circle, circle]);
|
||||
}
|
||||
|
||||
public set position(value: vec2) {
|
||||
vec2.copy(this.boundingCircle.center, value);
|
||||
vec2.add(this.head.center, value, this.headOffset);
|
||||
vec2.add(this.leftFoot.center, value, this.leftFootOffset);
|
||||
vec2.add(this.rightFoot.center, value, this.rightFootOffset);
|
||||
}
|
||||
|
||||
public get center(): vec2 {
|
||||
return this.boundingCircle.center;
|
||||
}
|
||||
|
||||
public get radius(): number {
|
||||
return this.boundingCircle.radius;
|
||||
public setCircles([head, leftFoot, rightFoot]: [
|
||||
BoundingCircle,
|
||||
BoundingCircle,
|
||||
BoundingCircle
|
||||
]) {
|
||||
this.head = head;
|
||||
this.leftFoot = leftFoot;
|
||||
this.rightFoot = rightFoot;
|
||||
}
|
||||
|
||||
public minDistance(target: vec2): number {
|
||||
return this.boundingCircle.minDistance(target);
|
||||
}
|
||||
public normal(from: vec2): vec2 {
|
||||
return this.boundingCircle.normal(from);
|
||||
}
|
||||
|
||||
public get boundingBox(): BoundingBox {
|
||||
return this.boundingCircle.boundingBox;
|
||||
}
|
||||
|
||||
public isInside(target: vec2): boolean {
|
||||
return this.minDistance(target) < 0;
|
||||
}
|
||||
|
||||
public clone(): BlobShape {
|
||||
return new BlobShape(this.boundingCircle.center, this.gameObject);
|
||||
return Math.min(
|
||||
this.head.distance(target),
|
||||
this.leftFoot.distance(target),
|
||||
this.rightFoot.distance(target)
|
||||
);
|
||||
}
|
||||
|
||||
protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any {
|
||||
|
|
@ -120,8 +99,8 @@ export class BlobShape extends Drawable implements IShape {
|
|||
this.rightFoot.center,
|
||||
transform2d
|
||||
),
|
||||
headRadius: this.headRadius * transform1d,
|
||||
footRadius: this.footRadius * transform1d,
|
||||
headRadius: this.head.radius * transform1d,
|
||||
footRadius: this.leftFoot.radius * transform1d,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { Circle } from 'sdf-2d';
|
||||
import { GameObject } from '../../objects/game-object';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { IShape } from '../i-shape';
|
||||
|
||||
export class CircleShape extends Circle implements IShape {
|
||||
public readonly isInverted = false;
|
||||
|
||||
export class CircleShape extends Circle {
|
||||
public constructor(
|
||||
center = vec2.create(),
|
||||
radius = 0,
|
||||
|
|
@ -15,34 +11,6 @@ export class CircleShape extends Circle implements IShape {
|
|||
super(center, radius);
|
||||
}
|
||||
|
||||
public distance(target: vec2): number {
|
||||
return vec2.distance(this.center, target) - this.radius;
|
||||
}
|
||||
|
||||
public normal(from: vec2): vec2 {
|
||||
const diff = vec2.subtract(vec2.create(), from, this.center);
|
||||
return vec2.normalize(diff, diff);
|
||||
}
|
||||
|
||||
public get boundingBox(): BoundingBox {
|
||||
return new BoundingBox(
|
||||
this,
|
||||
this.center.x - this.radius,
|
||||
this.center.x + this.radius,
|
||||
this.center.y - this.radius,
|
||||
this.center.y + this.radius
|
||||
);
|
||||
}
|
||||
|
||||
public isInside(target: vec2): boolean {
|
||||
return this.distance(target) < 0;
|
||||
}
|
||||
|
||||
public areIntersecting(other: CircleShape): boolean {
|
||||
const distance = vec2.distance(this.center, other.center);
|
||||
return distance < this.radius + other.radius;
|
||||
}
|
||||
|
||||
public clone(): CircleShape {
|
||||
return new CircleShape(vec2.clone(this.center), this.radius, this.gameObject);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +1,17 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { InvertedTunnel } from 'sdf-2d';
|
||||
import { clamp01 } from '../../helper/clamp';
|
||||
import { rotate90Deg } from '../../helper/rotate-90-deg';
|
||||
import { GameObject } from '../../objects/game-object';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { IShape } from '../i-shape';
|
||||
|
||||
export class TunnelShape extends InvertedTunnel implements IShape {
|
||||
constructor(
|
||||
readonly from: vec2,
|
||||
readonly to: vec2,
|
||||
readonly fromRadius: number,
|
||||
readonly toRadius: number,
|
||||
public readonly gameObject: GameObject = null
|
||||
) {
|
||||
export class TunnelShape extends InvertedTunnel {
|
||||
constructor(from: vec2, to: vec2, fromRadius: number, toRadius: number) {
|
||||
super(from, to, fromRadius, toRadius);
|
||||
}
|
||||
|
||||
public get boundingBox(): BoundingBox {
|
||||
const xMin = Math.min(this.from.x - this.fromRadius, this.to.x - this.toRadius);
|
||||
const yMin = Math.min(this.from.y - this.fromRadius, this.to.y - this.toRadius);
|
||||
const xMax = Math.max(this.from.x + this.fromRadius, this.to.x + this.toRadius);
|
||||
const yMax = Math.max(this.from.y + this.fromRadius, this.to.y + this.toRadius);
|
||||
|
||||
return new BoundingBox(this, xMin, xMax, yMin, yMax);
|
||||
}
|
||||
|
||||
public normal(target: vec2): vec2 {
|
||||
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||
|
||||
const toFromDelta = vec2.subtract(vec2.create(), this.to, this.from);
|
||||
|
||||
const h = clamp01(
|
||||
vec2.dot(targetFromDelta, toFromDelta) / vec2.dot(toFromDelta, toFromDelta)
|
||||
);
|
||||
|
||||
let diff = vec2.create();
|
||||
|
||||
if (h == 1) {
|
||||
vec2.subtract(diff, target, this.to);
|
||||
} else if (h == 0) {
|
||||
vec2.subtract(diff, target, this.from);
|
||||
} else {
|
||||
const side = Math.sign(
|
||||
toFromDelta.x * targetFromDelta.y - toFromDelta.y * targetFromDelta.x
|
||||
);
|
||||
|
||||
const normal = rotate90Deg(toFromDelta);
|
||||
vec2.normalize(normal, normal);
|
||||
|
||||
const translatedFrom = vec2.add(
|
||||
vec2.create(),
|
||||
this.from,
|
||||
vec2.scale(vec2.create(), normal, side * this.fromRadius)
|
||||
);
|
||||
|
||||
const translatedTo = vec2.add(
|
||||
vec2.create(),
|
||||
this.to,
|
||||
vec2.scale(vec2.create(), normal, side * this.toRadius)
|
||||
);
|
||||
|
||||
diff = rotate90Deg(vec2.subtract(vec2.create(), translatedTo, translatedFrom));
|
||||
|
||||
vec2.scale(diff, diff, side);
|
||||
}
|
||||
|
||||
return vec2.normalize(diff, diff);
|
||||
}
|
||||
|
||||
public clone(): TunnelShape {
|
||||
return new TunnelShape(
|
||||
vec2.clone(this.from),
|
||||
vec2.clone(this.to),
|
||||
this.fromRadius,
|
||||
this.toRadius,
|
||||
this.gameObject
|
||||
this.toRadius
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue