Refactor shapes
This commit is contained in:
parent
eb39846b75
commit
006ab3c4e6
24 changed files with 203 additions and 256 deletions
26
frontend/src/scripts/drawing/drawables/drawable-tunnel.ts
Normal file
26
frontend/src/scripts/drawing/drawables/drawable-tunnel.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { IDrawable } from './i-drawable';
|
||||||
|
import { TunnelShape } from '../../shapes/types/tunnel-shape';
|
||||||
|
import { IDrawableDescriptor } from './i-drawable-descriptor';
|
||||||
|
import { settings } from '../settings';
|
||||||
|
|
||||||
|
export class DrawableTunnel extends TunnelShape implements IDrawable {
|
||||||
|
public static descriptor: IDrawableDescriptor = {
|
||||||
|
uniformName: 'lines',
|
||||||
|
countMacroName: 'lineCount',
|
||||||
|
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
|
||||||
|
};
|
||||||
|
|
||||||
|
public serializeToUniforms(uniforms: any): void {
|
||||||
|
const uniformName = DrawableTunnel.descriptor.uniformName;
|
||||||
|
if (!uniforms.hasOwnProperty(uniformName)) {
|
||||||
|
uniforms[uniformName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms[uniformName].push({
|
||||||
|
from: this.from,
|
||||||
|
toFromDelta: this.toFromDelta,
|
||||||
|
fromRadius: this.fromRadius,
|
||||||
|
toRadius: this.toRadius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { ImmutableBoundingBox } from '../../physics/containers/immutable-bounding-box';
|
|
||||||
import { GameObject } from '../../objects/game-object';
|
|
||||||
|
|
||||||
export interface IDrawable {
|
export interface IDrawable {
|
||||||
serializeToUniforms(uniforms: any): void;
|
|
||||||
distance(target: vec2): number;
|
distance(target: vec2): number;
|
||||||
minimumDistance(target: vec2): number;
|
serializeToUniforms(uniforms: any): void;
|
||||||
readonly owner: GameObject;
|
|
||||||
readonly boundingBox: ImmutableBoundingBox;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { GameObject } from '../../../objects/game-object';
|
import { GameObject } from '../../../objects/game-object';
|
||||||
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
|
|
||||||
import { settings } from '../../settings';
|
import { settings } from '../../settings';
|
||||||
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
||||||
import { ILight } from './i-light';
|
import { ILight } from './i-light';
|
||||||
|
|
@ -20,24 +19,18 @@ export class CircleLight implements ILight {
|
||||||
public lightness: number
|
public lightness: number
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
boundingBox: ImmutableBoundingBox;
|
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
public distance(target: vec2): number {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public minimumDistance(target: vec2): number {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public serializeToUniforms(uniforms: any): void {
|
public serializeToUniforms(uniforms: any): void {
|
||||||
const listName = CircleLight.descriptor.uniformName;
|
const uniformName = CircleLight.descriptor.uniformName;
|
||||||
|
|
||||||
if (!uniforms.hasOwnProperty(listName)) {
|
if (!uniforms.hasOwnProperty(uniformName)) {
|
||||||
uniforms[listName] = [];
|
uniforms[uniformName] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
uniforms[listName].push({
|
uniforms[uniformName].push({
|
||||||
center: this.center,
|
center: this.center,
|
||||||
radius: this.radius,
|
radius: this.radius,
|
||||||
value: this.value,
|
value: this.value,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
import { IDrawable } from '../i-drawable';
|
import { IDrawable } from '../i-drawable';
|
||||||
|
|
||||||
export interface ILight extends IDrawable {}
|
export interface ILight extends IDrawable {}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { ILight } from './i-light';
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
||||||
import { settings } from '../../settings';
|
import { settings } from '../../settings';
|
||||||
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
|
|
||||||
import { GameObject } from '../../../objects/game-object';
|
import { GameObject } from '../../../objects/game-object';
|
||||||
|
|
||||||
export class PointLight implements ILight {
|
export class PointLight implements ILight {
|
||||||
|
|
@ -20,16 +19,10 @@ export class PointLight implements ILight {
|
||||||
public lightness: number
|
public lightness: number
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
boundingBox: ImmutableBoundingBox;
|
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
public distance(target: vec2): number {
|
||||||
return vec2.distance(this.center, target) - this.radius;
|
return vec2.distance(this.center, target) - this.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
public minimumDistance(target: vec2): number {
|
|
||||||
return vec2.distance(this.center, target) - this.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public serializeToUniforms(uniforms: any): void {
|
public serializeToUniforms(uniforms: any): void {
|
||||||
const listName = PointLight.descriptor.uniformName;
|
const listName = PointLight.descriptor.uniformName;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { GameObject } from '../../../objects/game-object';
|
|
||||||
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
|
|
||||||
import { IPrimitive } from './i-primitive';
|
|
||||||
|
|
||||||
export class Circle implements IPrimitive {
|
|
||||||
public constructor(
|
|
||||||
public readonly owner: GameObject,
|
|
||||||
public center = vec2.create(),
|
|
||||||
public radius = 0
|
|
||||||
) { }
|
|
||||||
|
|
||||||
public serializeToUniforms(uniforms: any): void {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
|
||||||
return vec2.distance(this.center, target) - this.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public minimumDistance(target: vec2): number {
|
|
||||||
return vec2.distance(this.center, target) - this.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get boundingBox(): ImmutableBoundingBox {
|
|
||||||
return new ImmutableBoundingBox(
|
|
||||||
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: Circle): boolean {
|
|
||||||
const distance = vec2.distance(this.center, other.center);
|
|
||||||
return distance < this.radius + other.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public clone(): Circle {
|
|
||||||
return new Circle(this.owner, this.center, this.radius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { IDrawable } from '../i-drawable';
|
|
||||||
|
|
||||||
export interface IPrimitive extends IDrawable {
|
|
||||||
clone(): IPrimitive
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { clamp01 } from '../../../helper/clamp';
|
|
||||||
import { mix } from '../../../helper/mix';
|
|
||||||
import { GameObject } from '../../../objects/game-object';
|
|
||||||
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
|
|
||||||
import { settings } from '../../settings';
|
|
||||||
import { IDrawableDescriptor } from '../i-drawable-descriptor';
|
|
||||||
import { Circle } from './circle';
|
|
||||||
import { IPrimitive } from './i-primitive';
|
|
||||||
|
|
||||||
export class TunnelShape implements IPrimitive {
|
|
||||||
public static descriptor: IDrawableDescriptor = {
|
|
||||||
uniformName: 'lines',
|
|
||||||
countMacroName: 'lineCount',
|
|
||||||
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
|
|
||||||
};
|
|
||||||
|
|
||||||
public readonly toFromDelta: vec2;
|
|
||||||
|
|
||||||
private boundingCircle: Circle;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public readonly owner: GameObject,
|
|
||||||
public readonly from: vec2,
|
|
||||||
public readonly to: vec2,
|
|
||||||
public readonly fromRadius: number,
|
|
||||||
public readonly toRadius: number
|
|
||||||
) {
|
|
||||||
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
|
|
||||||
|
|
||||||
this.boundingCircle = new Circle(
|
|
||||||
this.owner,
|
|
||||||
vec2.fromValues(from.x / 2 + to.x / 2, from.y / 2 + to.y / 2),
|
|
||||||
Math.max(fromRadius, toRadius) + vec2.distance(from, to)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public serializeToUniforms(uniforms: any): void {
|
|
||||||
if (!uniforms.hasOwnProperty(TunnelShape.descriptor.uniformName)) {
|
|
||||||
uniforms[TunnelShape.descriptor.uniformName] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
uniforms[TunnelShape.descriptor.uniformName].push({
|
|
||||||
from: this.from,
|
|
||||||
toFromDelta: this.toFromDelta,
|
|
||||||
fromRadius: this.fromRadius,
|
|
||||||
toRadius: this.toRadius,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public get boundingBox(): ImmutableBoundingBox {
|
|
||||||
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 ImmutableBoundingBox(this, xMin, xMax, yMin, yMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
|
||||||
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
|
||||||
const h = clamp01(
|
|
||||||
vec2.dot(targetFromDelta, this.toFromDelta) /
|
|
||||||
vec2.dot(this.toFromDelta, this.toFromDelta)
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
vec2.distance(
|
|
||||||
targetFromDelta,
|
|
||||||
vec2.scale(vec2.create(), this.toFromDelta, h)
|
|
||||||
) - mix(this.fromRadius, this.toRadius, h)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public minimumDistance(target: vec2): number {
|
|
||||||
return this.boundingCircle.distance(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public clone(): TunnelShape {
|
|
||||||
return new TunnelShape(
|
|
||||||
this.owner,
|
|
||||||
this.from,
|
|
||||||
this.to,
|
|
||||||
this.fromRadius,
|
|
||||||
this.toRadius
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { ILight } from './drawables/lights/i-light';
|
import { ILight } from './drawables/lights/i-light';
|
||||||
import { IPrimitive } from './drawables/primitives/i-primitive';
|
import { IDrawable } from './drawables/i-drawable';
|
||||||
|
|
||||||
export interface IRenderer {
|
export interface IRenderer {
|
||||||
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
finishFrame(): void;
|
finishFrame(): void;
|
||||||
|
|
||||||
drawPrimitive(primitive: IPrimitive): void;
|
drawShape(drawable: IDrawable): void;
|
||||||
drawLight(light: ILight): void;
|
drawLight(light: ILight): void;
|
||||||
drawInfoText(text: string): void;
|
drawInfoText(text: string): void;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export class RenderingPass {
|
||||||
const tileUvSize = vec2.fromValues(q, q);
|
const tileUvSize = vec2.fromValues(q, q);
|
||||||
|
|
||||||
const possiblyOnScreenDrawables = this.drawables.filter(
|
const possiblyOnScreenDrawables = this.drawables.filter(
|
||||||
(p) => p.minimumDistance(viewBoxCenter) < viewBoxRadius
|
(p) => p.distance(viewBoxCenter) < viewBoxRadius
|
||||||
);
|
);
|
||||||
|
|
||||||
const origin = vec2.transformMat2d(
|
const origin = vec2.transformMat2d(
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { CircleLight } from '../drawables/lights/circle-light';
|
import { CircleLight } from '../drawables/lights/circle-light';
|
||||||
import { ILight } from '../drawables/lights/i-light';
|
import { ILight } from '../drawables/lights/i-light';
|
||||||
import { PointLight } from '../drawables/lights/point-light';
|
import { PointLight } from '../drawables/lights/point-light';
|
||||||
import { IPrimitive } from '../drawables/primitives/i-primitive';
|
|
||||||
import { TunnelShape } from '../drawables/primitives/tunnel-shape';
|
|
||||||
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
||||||
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
||||||
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
||||||
|
|
@ -16,6 +14,8 @@ import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
|
||||||
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
||||||
import { FpsAutoscaler } from './fps-autoscaler';
|
import { FpsAutoscaler } from './fps-autoscaler';
|
||||||
import { RenderingPass } from './rendering-pass';
|
import { RenderingPass } from './rendering-pass';
|
||||||
|
import { IDrawable } from '../drawables/i-drawable';
|
||||||
|
import { DrawableTunnel } from '../drawables/drawable-tunnel';
|
||||||
|
|
||||||
export class WebGl2Renderer implements IRenderer {
|
export class WebGl2Renderer implements IRenderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
|
|
@ -50,7 +50,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.distancePass = new RenderingPass(
|
this.distancePass = new RenderingPass(
|
||||||
this.gl,
|
this.gl,
|
||||||
[caveVertexShader, caveFragmentShader],
|
[caveVertexShader, caveFragmentShader],
|
||||||
[TunnelShape.descriptor],
|
[DrawableTunnel.descriptor],
|
||||||
this.distanceFieldFrameBuffer
|
this.distanceFieldFrameBuffer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -71,8 +71,8 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawPrimitive(primitive: IPrimitive) {
|
public drawShape(shape: IDrawable) {
|
||||||
this.distancePass.addDrawable(primitive);
|
this.distancePass.addDrawable(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawLight(light: ILight) {
|
public drawLight(light: ILight) {
|
||||||
|
|
@ -94,14 +94,11 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
vec2.scale(vec2.create(), this.viewBoxSize, 0.5)
|
vec2.scale(vec2.create(), this.viewBoxSize, 0.5)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.distancePass.render(
|
this.distancePass.render(this.uniforms, this.cameraPosition, viewBoxRadius);
|
||||||
this.uniforms,
|
|
||||||
this.cameraPosition,
|
|
||||||
viewBoxRadius
|
|
||||||
);
|
|
||||||
|
|
||||||
this.lightingPass.render(
|
this.lightingPass.render(
|
||||||
this.uniforms, this.cameraPosition,
|
this.uniforms,
|
||||||
|
this.cameraPosition,
|
||||||
viewBoxRadius,
|
viewBoxRadius,
|
||||||
this.distanceFieldFrameBuffer.colorTexture
|
this.distanceFieldFrameBuffer.colorTexture
|
||||||
);
|
);
|
||||||
|
|
@ -111,7 +108,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
private get uniforms(): any {
|
private get uniforms(): any {
|
||||||
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
||||||
return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize }
|
return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateMatrices() {
|
private calculateMatrices() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { BeforeRenderCommand } from '../../drawing/commands/before-render';
|
||||||
import { CursorMoveCommand } from '../../input/commands/cursor-move-command';
|
import { CursorMoveCommand } from '../../input/commands/cursor-move-command';
|
||||||
import { ZoomCommand } from '../../input/commands/zoom';
|
import { ZoomCommand } from '../../input/commands/zoom';
|
||||||
import { MoveToCommand } from '../../physics/commands/move-to';
|
import { MoveToCommand } from '../../physics/commands/move-to';
|
||||||
import { BoundingBox } from '../../physics/containers/bounding-box';
|
import { BoundingBox } from '../../shapes/bounding-box';
|
||||||
import { Physics } from '../../physics/physics';
|
import { Physics } from '../../physics/physics';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { Lamp } from './lamp';
|
import { Lamp } from './lamp';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Circle } from '../../drawing/drawables/primitives/circle';
|
import { Circle } from '../../shapes/types/circle';
|
||||||
import { KeyDownCommand } from '../../input/commands/key-down';
|
import { KeyDownCommand } from '../../input/commands/key-down';
|
||||||
import { KeyUpCommand } from '../../input/commands/key-up';
|
import { KeyUpCommand } from '../../input/commands/key-up';
|
||||||
import { SwipeCommand } from '../../input/commands/swipe';
|
import { SwipeCommand } from '../../input/commands/swipe';
|
||||||
|
|
@ -19,7 +19,7 @@ export class Character extends GameObject {
|
||||||
constructor(private physics: Physics, private camera: Camera) {
|
constructor(private physics: Physics, private camera: Camera) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.primitive = new Circle(this);
|
this.primitive = new Circle();
|
||||||
this.primitive.radius = 40;
|
this.primitive.radius = 40;
|
||||||
|
|
||||||
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
||||||
|
|
@ -45,9 +45,9 @@ export class Character extends GameObject {
|
||||||
|
|
||||||
const intersects = this.physics
|
const intersects = this.physics
|
||||||
.findIntersecting(nextPrimitive.boundingBox)
|
.findIntersecting(nextPrimitive.boundingBox)
|
||||||
.filter((b) => b.value)
|
.filter((b) => b.shape)
|
||||||
.map(
|
.map(
|
||||||
(b) => b.value.distance(nextPrimitive.center) + nextPrimitive.radius
|
(b) => b.shape.distance(nextPrimitive.center) + nextPrimitive.radius
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(intersects);
|
console.log(intersects);
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,11 @@ import { vec2 } from 'gl-matrix';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { RenderCommand } from '../../drawing/commands/render';
|
import { RenderCommand } from '../../drawing/commands/render';
|
||||||
import { Physics } from '../../physics/physics';
|
import { Physics } from '../../physics/physics';
|
||||||
import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape';
|
import { TunnelShape } from '../../shapes/types/tunnel-shape';
|
||||||
|
import { DrawableTunnel } from '../../drawing/drawables/drawable-tunnel';
|
||||||
export interface Line {}
|
|
||||||
|
|
||||||
export class Tunnel extends GameObject {
|
export class Tunnel extends GameObject {
|
||||||
private primitive: TunnelShape;
|
private shape: DrawableTunnel;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
physics: Physics,
|
physics: Physics,
|
||||||
|
|
@ -18,12 +17,12 @@ export class Tunnel extends GameObject {
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.primitive = new TunnelShape(this, from, to, fromRadius, toRadius);
|
this.shape = new DrawableTunnel(from, to, fromRadius, toRadius);
|
||||||
physics.addStaticBoundingBox(this.primitive.boundingBox);
|
physics.addStaticBoundingBox(this.shape.boundingBox);
|
||||||
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
|
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private draw(c: RenderCommand) {
|
private draw(c: RenderCommand) {
|
||||||
c.renderer.drawPrimitive(this.primitive);
|
c.renderer.drawShape(this.shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BoundingBoxBase } from './bounding-box-base';
|
import { BoundingBoxBase } from '../../shapes/bounding-box-base';
|
||||||
|
|
||||||
export class BoundingBoxList {
|
export class BoundingBoxList {
|
||||||
constructor(private boundingBoxes: Array<BoundingBoxBase> = []) {}
|
constructor(private boundingBoxes: Array<BoundingBoxBase> = []) {}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { ImmutableBoundingBox } from './immutable-bounding-box';
|
|
||||||
|
|
||||||
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
|
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
|
||||||
|
|
||||||
|
import { ImmutableBoundingBox } from '../../shapes/immutable-bounding-box';
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
public left?: Node = null;
|
public left?: Node = null;
|
||||||
public right?: Node = null;
|
public right?: Node = null;
|
||||||
|
|
@ -63,20 +63,6 @@ export class BoundingBoxTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public print() {
|
|
||||||
this.printRecursive(this.root, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private printRecursive(node: Node, tabCount: number) {
|
|
||||||
if (node === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(' '.repeat(tabCount) + '- ' + node.rectangle.value);
|
|
||||||
this.printRecursive(node.left, tabCount + 2);
|
|
||||||
this.printRecursive(node.right, tabCount + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public findIntersecting(
|
public findIntersecting(
|
||||||
box: ImmutableBoundingBox
|
box: ImmutableBoundingBox
|
||||||
): Array<ImmutableBoundingBox> {
|
): Array<ImmutableBoundingBox> {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { BoundingBoxBase } from './bounding-box-base';
|
|
||||||
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive';
|
|
||||||
|
|
||||||
export class ImmutableBoundingBox extends BoundingBoxBase {
|
|
||||||
constructor(
|
|
||||||
value: IPrimitive,
|
|
||||||
xMin: number = 0,
|
|
||||||
xMax: number = 0,
|
|
||||||
yMin: number = 0,
|
|
||||||
yMax: number = 0
|
|
||||||
) {
|
|
||||||
super(value, xMin, xMax, yMin, yMax);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { BoundingBoxTree } from './containers/bounding-box-tree';
|
import { BoundingBoxTree } from './containers/bounding-box-tree';
|
||||||
import { BoundingBoxList } from './containers/bounding-box-list';
|
import { BoundingBoxList } from './containers/bounding-box-list';
|
||||||
import { ImmutableBoundingBox } from './containers/immutable-bounding-box';
|
import { ImmutableBoundingBox } from '../shapes/immutable-bounding-box';
|
||||||
import { BoundingBoxBase } from './containers/bounding-box-base';
|
import { BoundingBoxBase } from '../shapes/bounding-box-base';
|
||||||
|
|
||||||
export class Physics {
|
export class Physics {
|
||||||
private isTreeInitialized = false;
|
private isTreeInitialized = false;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive';
|
import { IShape } from './i-shape';
|
||||||
|
|
||||||
export abstract class BoundingBoxBase {
|
export abstract class BoundingBoxBase {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly value: IPrimitive,
|
public readonly shape: IShape,
|
||||||
protected _xMin: number,
|
protected _xMin: number = 0,
|
||||||
protected _xMax: number,
|
protected _xMax: number = 0,
|
||||||
protected _yMin: number,
|
protected _yMin: number = 0,
|
||||||
protected _yMax: number
|
protected _yMax: number = 0
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public get 0(): number {
|
public get 0(): number {
|
||||||
|
|
@ -1,32 +1,33 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { BoundingBoxBase } from './bounding-box-base';
|
import { BoundingBoxBase } from './bounding-box-base';
|
||||||
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive';
|
import { ImmutableBoundingBox } from './immutable-bounding-box';
|
||||||
|
|
||||||
export class BoundingBox extends BoundingBoxBase {
|
export class BoundingBox extends BoundingBoxBase {
|
||||||
constructor(
|
|
||||||
value: IPrimitive,
|
|
||||||
xMin: number = 0,
|
|
||||||
xMax: number = 0,
|
|
||||||
yMin: number = 0,
|
|
||||||
yMax: number = 0
|
|
||||||
) {
|
|
||||||
super(value, xMin, xMax, yMin, yMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get topLeft(): vec2 {
|
public get topLeft(): vec2 {
|
||||||
return vec2.fromValues(this._xMin, this._yMax);
|
return vec2.fromValues(this._xMin, this._yMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get size(): vec2 {
|
|
||||||
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
|
||||||
}
|
|
||||||
public set topLeft(value: vec2) {
|
public set topLeft(value: vec2) {
|
||||||
this._xMin = value.x;
|
this._xMin = value.x;
|
||||||
this._yMax = value.y;
|
this._yMax = value.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get size(): vec2 {
|
||||||
|
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
||||||
|
}
|
||||||
|
|
||||||
public set size(value: vec2) {
|
public set size(value: vec2) {
|
||||||
this._xMax = this.xMin + value.x;
|
this._xMax = this.xMin + value.x;
|
||||||
this._yMin = this.yMax - value.y;
|
this._yMin = this.yMax - value.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cloneAsImmutable(): ImmutableBoundingBox {
|
||||||
|
return new ImmutableBoundingBox(
|
||||||
|
this.shape,
|
||||||
|
this.xMin,
|
||||||
|
this.xMax,
|
||||||
|
this.yMin,
|
||||||
|
this.yMax
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
12
frontend/src/scripts/shapes/i-shape.ts
Normal file
12
frontend/src/scripts/shapes/i-shape.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { BoundingBox } from './bounding-box';
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export interface IShape {
|
||||||
|
readonly isInverted: boolean;
|
||||||
|
readonly boundingBox: BoundingBox;
|
||||||
|
|
||||||
|
distance(target: vec2): number;
|
||||||
|
normal(from: vec2): vec2;
|
||||||
|
|
||||||
|
clone(): IShape;
|
||||||
|
}
|
||||||
3
frontend/src/scripts/shapes/immutable-bounding-box.ts
Normal file
3
frontend/src/scripts/shapes/immutable-bounding-box.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { BoundingBoxBase } from './bounding-box-base';
|
||||||
|
|
||||||
|
export class ImmutableBoundingBox extends BoundingBoxBase {}
|
||||||
41
frontend/src/scripts/shapes/types/circle.ts
Normal file
41
frontend/src/scripts/shapes/types/circle.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { IShape } from '../i-shape';
|
||||||
|
import { BoundingBox } from '../bounding-box';
|
||||||
|
|
||||||
|
export class Circle implements IShape {
|
||||||
|
public readonly isInverted = false;
|
||||||
|
|
||||||
|
public constructor(public center = vec2.create(), public radius = 0) {}
|
||||||
|
|
||||||
|
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: Circle): boolean {
|
||||||
|
const distance = vec2.distance(this.center, other.center);
|
||||||
|
return distance < this.radius + other.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): Circle {
|
||||||
|
return new Circle(this.center, this.radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
frontend/src/scripts/shapes/types/tunnel-shape.ts
Normal file
65
frontend/src/scripts/shapes/types/tunnel-shape.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { BoundingBox } from '../bounding-box';
|
||||||
|
import { clamp01 } from '../../helper/clamp';
|
||||||
|
import { mix } from '../../helper/mix';
|
||||||
|
import { IShape } from '../i-shape';
|
||||||
|
|
||||||
|
export class TunnelShape implements IShape {
|
||||||
|
public readonly isInverted = true;
|
||||||
|
|
||||||
|
public readonly toFromDelta: vec2;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly from: vec2,
|
||||||
|
public readonly to: vec2,
|
||||||
|
public readonly fromRadius: number,
|
||||||
|
public readonly toRadius: number
|
||||||
|
) {
|
||||||
|
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(from: vec2): vec2 {
|
||||||
|
throw new Error('Unimplemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
public distance(target: vec2): number {
|
||||||
|
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||||
|
|
||||||
|
const h = clamp01(
|
||||||
|
vec2.dot(targetFromDelta, this.toFromDelta) /
|
||||||
|
vec2.dot(this.toFromDelta, this.toFromDelta)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
vec2.distance(
|
||||||
|
targetFromDelta,
|
||||||
|
vec2.scale(vec2.create(), this.toFromDelta, h)
|
||||||
|
) - mix(this.fromRadius, this.toRadius, h)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): TunnelShape {
|
||||||
|
return new TunnelShape(this.from, this.to, this.fromRadius, this.toRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue