Refactor rendering

This commit is contained in:
schmelczerandras 2020-08-18 15:49:15 +02:00
parent edca93fcfe
commit 76282a4cf7
25 changed files with 239 additions and 213 deletions

View file

@ -1,5 +1,6 @@
{
"trailingComma": "es5",
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,
"endOfLine": "lf"

View file

@ -2,6 +2,7 @@ import { IDrawable } from './i-drawable';
import { IDrawableDescriptor } from './i-drawable-descriptor';
import { settings } from '../settings';
import { Blob } from '../../shapes/types/blob';
import { vec2, mat2d } from 'gl-matrix';
export class DrawableBlob extends Blob implements IDrawable {
public static descriptor: IDrawableDescriptor = {
@ -10,17 +11,37 @@ export class DrawableBlob extends Blob implements IDrawable {
shaderCombinationSteps: settings.shaderCombinations.blobSteps,
};
public serializeToUniforms(uniforms: any): void {
public serializeToUniforms(
uniforms: any,
scale: number,
transform: mat2d
): void {
const uniformName = DrawableBlob.descriptor.uniformName;
if (!uniforms.hasOwnProperty(uniformName)) {
uniforms[uniformName] = [];
}
uniforms[uniformName].push({
headCenter: this.head.center,
torsoCenter: this.torso.center,
leftFootCenter: this.leftFoot.center,
rightFootCenter: this.rightFoot.center,
headCenter: vec2.transformMat2d(
vec2.create(),
this.head.center,
transform
),
torsoCenter: vec2.transformMat2d(
vec2.create(),
this.torso.center,
transform
),
leftFootCenter: vec2.transformMat2d(
vec2.create(),
this.leftFoot.center,
transform
),
rightFootCenter: vec2.transformMat2d(
vec2.create(),
this.rightFoot.center,
transform
),
});
}
}

View file

@ -2,6 +2,7 @@ import { IDrawable } from './i-drawable';
import { TunnelShape } from '../../shapes/types/tunnel-shape';
import { IDrawableDescriptor } from './i-drawable-descriptor';
import { settings } from '../settings';
import { mat2d, vec2 } from 'gl-matrix';
export class DrawableTunnel extends TunnelShape implements IDrawable {
public static descriptor: IDrawableDescriptor = {
@ -10,17 +11,25 @@ export class DrawableTunnel extends TunnelShape implements IDrawable {
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
};
public serializeToUniforms(uniforms: any): void {
public serializeToUniforms(
uniforms: any,
scale: number,
transform: mat2d
): 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,
from: vec2.transformMat2d(vec2.create(), this.from, transform),
toFromDelta: vec2.transformMat2d(
vec2.create(),
this.toFromDelta,
transform
),
fromRadius: this.fromRadius * scale,
toRadius: this.toRadius * scale,
});
}
}

View file

@ -1,6 +1,6 @@
import { vec2 } from 'gl-matrix';
import { vec2, mat2d } from 'gl-matrix';
export interface IDrawable {
distance(target: vec2): number;
serializeToUniforms(uniforms: any): void;
serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void;
}

View file

@ -1,4 +1,4 @@
import { vec2, vec3 } from 'gl-matrix';
import { vec2, vec3, mat2d } from 'gl-matrix';
import { GameObject } from '../../../objects/game-object';
import { settings } from '../../settings';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
@ -12,7 +12,6 @@ export class CircleLight implements ILight {
};
constructor(
public readonly owner: GameObject,
public center: vec2,
public radius: number,
public color: vec3,
@ -23,7 +22,11 @@ export class CircleLight implements ILight {
return 0;
}
public serializeToUniforms(uniforms: any): void {
public serializeToUniforms(
uniforms: any,
scale: number,
transform: mat2d
): void {
const uniformName = CircleLight.descriptor.uniformName;
if (!uniforms.hasOwnProperty(uniformName)) {
@ -31,8 +34,8 @@ export class CircleLight implements ILight {
}
uniforms[uniformName].push({
center: this.center,
radius: this.radius,
center: vec2.transformMat2d(vec2.create(), this.center, transform),
radius: this.radius * scale,
value: this.value,
});
}

View file

@ -1,5 +1,5 @@
import { ILight } from './i-light';
import { vec2, vec3 } from 'gl-matrix';
import { vec2, vec3, mat2d } from 'gl-matrix';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { settings } from '../../settings';
import { GameObject } from '../../../objects/game-object';
@ -12,7 +12,6 @@ export class PointLight implements ILight {
};
public constructor(
public readonly owner: GameObject,
public center: vec2,
public radius: number,
public color: vec3,
@ -23,7 +22,11 @@ export class PointLight implements ILight {
return vec2.distance(this.center, target) - this.radius;
}
public serializeToUniforms(uniforms: any): void {
public serializeToUniforms(
uniforms: any,
scale: number,
transform: mat2d
): void {
const listName = PointLight.descriptor.uniformName;
if (!uniforms.hasOwnProperty(listName)) {
@ -31,8 +34,8 @@ export class PointLight implements ILight {
}
uniforms[listName].push({
center: this.center,
radius: this.radius,
center: vec2.transformMat2d(vec2.create(), this.center, transform),
radius: this.radius * scale,
value: this.value,
});
}

View file

@ -1,6 +1,7 @@
import { vec2 } from 'gl-matrix';
import { ILight } from './drawables/lights/i-light';
import { IDrawable } from './drawables/i-drawable';
import { BoundingBoxBase } from '../shapes/bounding-box-base';
export interface IRenderer {
startFrame(deltaTime: DOMHighResTimeStamp): void;
@ -10,7 +11,7 @@ export interface IRenderer {
drawLight(light: ILight): void;
drawInfoText(text: string): void;
setCameraPosition(position: vec2): void;
readonly canvasSize: vec2;
setViewArea(viewArea: BoundingBoxBase): void;
setCursorPosition(position: vec2): void;
setInViewArea(size: number): vec2;
}

View file

@ -1,4 +1,4 @@
import { vec2 } from 'gl-matrix';
import { vec2, mat2d } from 'gl-matrix';
import { InfoText } from '../../objects/types/info-text';
import { IDrawable } from '../drawables/i-drawable';
import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor';
@ -27,20 +27,11 @@ export class RenderingPass {
this.drawables.push(drawable);
}
public render(
commonUniforms: any,
viewBoxCenter: vec2,
viewBoxRadius: number,
inputTexture?: WebGLTexture
) {
public render(commonUniforms: any, inputTexture?: WebGLTexture) {
this.frame.bindAndClear(inputTexture);
const q = 1 / settings.tileMultiplier;
const tileUvSize = vec2.fromValues(q, q);
const possiblyOnScreenDrawables = this.drawables.filter(
(p) => p.distance(viewBoxCenter) < viewBoxRadius
);
const origin = vec2.transformMat2d(
vec2.create(),
vec2.fromValues(0, 0),
@ -59,6 +50,9 @@ export class RenderingPass {
let sumLineCount = 0;
const scale = 1;
const transform = mat2d.create();
for (let x = 0; x < 1; x += q) {
for (let y = 0; y < 1; y += q) {
const uniforms = { ...commonUniforms };
@ -73,24 +67,24 @@ export class RenderingPass {
uniforms.uvToWorld
);
const primitivesNearTile = possiblyOnScreenDrawables.filter(
const primitivesNearTile = this.drawables.filter(
(p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR
);
sumLineCount += primitivesNearTile.length;
primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms));
primitivesNearTile.forEach((p) =>
p.serializeToUniforms(uniforms, scale, transform)
);
this.program.bindAndSetUniforms(uniforms);
this.program.draw();
}
}
this.drawables = [];
InfoText.modifyRecord(
'nearby ' + this.drawableDescriptors[0].countMacroName,
possiblyOnScreenDrawables.length.toFixed(2)
this.drawables.length.toFixed(2)
);
InfoText.modifyRecord(
@ -101,5 +95,7 @@ export class RenderingPass {
settings.tileMultiplier
).toFixed(2)
);
this.drawables = [];
}
}

View file

@ -18,13 +18,13 @@ import { IDrawable } from '../drawables/i-drawable';
import { DrawableTunnel } from '../drawables/drawable-tunnel';
import { enableExtension } from '../graphics-library/helper/enable-extension';
import { DrawableBlob } from '../drawables/drawable-blob';
import { BoundingBoxBase } from '../../shapes/bounding-box-base';
export class WebGl2Renderer implements IRenderer {
private gl: WebGL2RenderingContext;
private stopwatch?: WebGlStopwatch;
private viewBoxBottomLeft = vec2.create();
private cameraPosition = vec2.create();
private viewBoxSize = vec2.create();
private cursorPosition = vec2.create();
@ -92,16 +92,10 @@ export class WebGl2Renderer implements IRenderer {
public finishFrame() {
this.calculateMatrices();
const viewBoxRadius = vec2.length(
vec2.scale(vec2.create(), this.viewBoxSize, 0.5)
);
this.distancePass.render(this.uniforms, this.cameraPosition, viewBoxRadius);
this.distancePass.render(this.uniforms);
this.lightingPass.render(
this.uniforms,
this.cameraPosition,
viewBoxRadius,
this.distanceFieldFrameBuffer.colorTexture
);
@ -164,11 +158,16 @@ export class WebGl2Renderer implements IRenderer {
);
}
public setCameraPosition(position: vec2) {
this.cameraPosition = position;
this.viewBoxBottomLeft = vec2.fromValues(
this.cameraPosition.x - this.viewBoxSize.x / 2,
this.cameraPosition.y - this.viewBoxSize.y / 2
public get canvasSize(): vec2 {
return vec2.fromValues(this.canvas.clientWidth, this.canvas.clientHeight);
}
public setViewArea(viewArea: BoundingBoxBase) {
this.viewBoxSize = viewArea.size;
this.viewBoxBottomLeft = vec2.add(
vec2.create(),
viewArea.topLeft,
vec2.fromValues(0, -viewArea.size.y)
);
}
@ -176,16 +175,6 @@ export class WebGl2Renderer implements IRenderer {
this.cursorPosition = position;
}
public setInViewArea(size: number): vec2 {
const canvasAspectRatio =
this.canvas.clientWidth / this.canvas.clientHeight;
return (this.viewBoxSize = vec2.fromValues(
Math.sqrt(size * canvasAspectRatio),
Math.sqrt(size / canvasAspectRatio)
));
}
public drawInfoText(text: string) {
if (this.overlay.innerText != text) {
this.overlay.innerText = text;

View file

@ -7,7 +7,7 @@ export const settings = {
scaleTargets: [
[0.2, 0.1],
[0.6, 0.1],
[1, 0.3],
[1, 1],
/*[1.25, 0.75],
[1.5, 1],
[1.75, 1.25],
@ -20,12 +20,12 @@ export const settings = {
multiplicativeDecrease: 1.15,
},
},
tileMultiplier: 5,
tileMultiplier: 8,
shaderMacros: {
edgeSmoothing: 10,
headRadius: 5,
torsoRadius: 10,
footRadius: 4,
torsoRadius: 8,
footRadius: 2,
},
shaderCombinations: {
lineSteps: [0, 1, 2, 4, 8, 16, 128],

View file

@ -69,8 +69,6 @@ uniform float maxMinDistance;
}
#endif
in vec2 worldCoordinates;
out vec2 fragmentColor;

View file

@ -61,40 +61,41 @@ void main() {
#if CIRCLE_LIGHT_COUNT > 0
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
float lightCenterDistance = distance(circleLights[i].center, worldCoordinates);
float lightCenterDistance = distance(
circleLights[i].center,
worldCoordinates
);
float lightDistance = lightCenterDistance - circleLights[i].radius;
/*if (lightDistance < 0.0) {
lighting = vec3(1.0, 1.0, 0.0);
}*/
vec3 lightColorAtPosition = circleLights[i].value / pow(
lightCenterDistance / LIGHT_DROP + 1.0, 2.0
lightDistance / LIGHT_DROP + 1.0, 2.0
);
float q = INFINITY;
float rayLength = startingDistance;
float exponentialDecayDistance = rayLength;
vec2 direction = normalize(circleLightDirections[i]) / viewBoxSize;
for (int j = 0; j < 48; j++) {
if (rayLength > lightCenterDistance - circleLights[i].radius) {
rayLength = lightCenterDistance - circleLights[i].radius;
float minDistance = getDistance(uvCoordinates + direction * rayLength);
exponentialDecayDistance = (exponentialDecayDistance + minDistance) / 2.0;
q = min(q, minDistance / rayLength);
if (rayLength >= lightDistance) {
lighting += lightColorAtPosition * clamp(
q / circleLights[i].radius * (lightCenterDistance + 1.0), 0.0, 1.0
q / circleLights[i].radius * lightCenterDistance, 0.0, 1.0
);
break;
}
float minDistance = getDistance(uvCoordinates + direction * rayLength);
exponentialDecayDistance = (exponentialDecayDistance + minDistance) / 2.0;
q = min(q, exponentialDecayDistance / rayLength);
q = min(q, minDistance / rayLength);
rayLength += minDistance;
}
}
#endif
#if POINT_LIGHT_COUNT > 0
for (int i = 0; i < POINT_LIGHT_COUNT; i++) {
/*for (int i = 0; i < POINT_LIGHT_COUNT; i++) {
float lightDistance = distance(pointLights[i].center, worldCoordinates);
vec3 lightColorAtPosition = mix(
@ -120,8 +121,11 @@ void main() {
q = min(q, exponentialDecayDistance);
rayLength += minDistance;
}
}
}*/
#endif
fragmentColor = vec4(colorAtPosition * lighting * step(0.0, startingDistance), 1.0);
fragmentColor = vec4(
colorAtPosition * lighting * step(0.0, startingDistance),
1.0
);
}

View file

@ -1,49 +0,0 @@
#version 300 es
precision mediump float;
vec3 rainbow(float level) {
float r = float(level <= 2.0) + float(level > 4.0) * 0.5;
float g = max(1.0 - abs(level - 2.0) * 0.5, 0.0);
float b = (1.0 - (level - 4.0) * 0.5) * float(level >= 4.0);
return vec3(r, g, b);
}
vec4 smoothRainbow(float x) {
float level1 = floor(x*6.0);
float level2 = min(6.0, floor(x*6.0) + 1.0);
vec3 a = rainbow(level1);
vec3 b = rainbow(level2);
return vec4(mix(a, b, fract(x*6.0)), 1.0);
}
uniform sampler2D distanceTexture;
uniform mat3 worldToDistanceUV;
uniform mat3 lightingScreenToWorld;
out vec4 fragmentColor;
uniform vec2 cursorPosition;
float getDistance(in vec2 targetUV, out vec3 color) {
vec4 values = texture(distanceTexture, targetUV);
color = values.rgb;
return values.a;
}
in vec2 worldCoordinates;
void main() {
vec2 targetUV = (vec3(worldCoordinates.xy, 1.0) * worldToDistanceUV).xy;
vec4 previous = texture(distanceTexture, targetUV);
//fragmentColor = smoothRainbow(previous.a);
fragmentColor = previous.a > 0.5 ? vec4(1.0, 1.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);
if (distance(worldCoordinates, cursorPosition) < 10.0) {
fragmentColor = vec4(1.0, 1.0, 0.0, 1.0);
}
}

View file

@ -8,22 +8,33 @@ import { MouseListener } from './input/mouse-listener';
import { TouchListener } from './input/touch-listener';
import { Objects } from './objects/objects';
import { InfoText } from './objects/types/info-text';
import { createCharacter } from './objects/world/create-character';
import { createDungeon } from './objects/world/create-dungeon';
import { RenderCommand } from './drawing/commands/render';
import { Physics } from './physics/physics';
import { TeleportToCommand } from './physics/commands/teleport-to';
import { IRenderer } from './drawing/i-renderer';
import { Random } from './helper/random';
import { Camera } from './objects/types/camera';
import { Character } from './objects/types/character';
import { IGame } from './i-game';
import { GameObject } from './objects/game-object';
import { vec2 } from 'gl-matrix';
import { BoundingBoxBase } from './shapes/bounding-box-base';
import { MoveToCommand } from './physics/commands/move-to';
import { BoundingBox } from './shapes/bounding-box';
export class Game implements IGame {
public readonly objects = new Objects();
public readonly physics = new Physics();
public readonly camera = new Camera();
export class Game {
private previousTime?: DOMHighResTimeStamp = null;
private objects = new Objects();
private physics = new Physics();
private renderer: IRenderer;
private previousFpsValues: Array<number> = [];
private infoText = new InfoText();
private character: Character;
private renderer: IRenderer;
constructor() {
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
const overlay: HTMLElement = document.querySelector('#overlay');
@ -52,13 +63,29 @@ export class Game {
requestAnimationFrame(this.gameLoop.bind(this));
}
public addObject(o: GameObject) {
this.objects.addObject(o);
}
public get viewArea(): BoundingBoxBase {
return this.camera.viewArea;
}
public findIntersecting(box: BoundingBoxBase): Array<BoundingBoxBase> {
return this.physics.findIntersecting(box);
}
private initializeScene() {
this.objects.addObject(new InfoText());
this.objects.addObject(this.infoText);
const start = createDungeon(this.objects, this.physics);
createDungeon(this.objects, this.physics);
const character = createCharacter(this.objects, this.physics);
console.log('start', start.from);
character.sendCommand(new TeleportToCommand(start.from));
this.character = new Character(this);
//this.physics.addDynamicBoundingBox(this.character.boundingBox);
this.addObject(this.character);
this.addObject(this.camera);
this.character.sendCommand(new TeleportToCommand(start.from));
}
private handleVisibilityChange() {
@ -78,10 +105,23 @@ export class Game {
this.calculateFps(deltaTime);
this.objects.sendCommand(new StepCommand(deltaTime));
this.camera.sendCommand(new MoveToCommand(this.character.position));
this.renderer.startFrame(deltaTime);
this.objects.sendCommand(new BeforeRenderCommand(this.renderer));
this.objects.sendCommand(new RenderCommand(this.renderer));
this.camera.sendCommand(new BeforeRenderCommand(this.renderer));
const shouldBeDrawn = this.physics
.findIntersecting(this.camera.viewArea)
.map((b) => b.shape?.gameObject);
for (let object of shouldBeDrawn) {
object?.sendCommand(new BeforeRenderCommand(this.renderer));
object?.sendCommand(new RenderCommand(this.renderer));
}
this.character.sendCommand(new RenderCommand(this.renderer));
this.infoText.sendCommand(new RenderCommand(this.renderer));
this.renderer.finishFrame();
window.requestAnimationFrame(this.gameLoop.bind(this));

View file

@ -1,11 +1,8 @@
import { InfoText } from '../objects/types/info-text';
import { last } from './last';
export function timeIt(interval = 60) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let i = 0;
let previousTimes: Array<DOMHighResTimeStamp> = [];
@ -19,10 +16,9 @@ export function timeIt(interval = 60) {
previousTimes.push(end - start);
if (i++ % interval == 0) {
previousTimes.sort();
const text = `Max: ${previousTimes[previousTimes.length - 1].toFixed(
2
)} ms\n\tMedian: ${previousTimes[
previousTimes = previousTimes.sort();
const text = `Max: ${last(previousTimes).toFixed(2)} ms\n\tMedian: ${previousTimes[
Math.floor(previousTimes.length / 2)
].toFixed(2)} ms`;

View file

@ -0,0 +1,9 @@
import { GameObject } from './objects/game-object';
import { vec2 } from 'gl-matrix';
import { BoundingBoxBase } from './shapes/bounding-box-base';
export interface IGame {
addObject(o: GameObject);
readonly viewArea: BoundingBoxBase;
findIntersecting(box: BoundingBoxBase): Array<BoundingBoxBase>;
}

View file

@ -9,40 +9,46 @@ import { GameObject } from '../game-object';
import { Lamp } from './lamp';
export class Camera extends GameObject {
private inViewArea = 1920 * 1080 * 5;
private inViewAreaSize = 1920 * 1080 * 5;
private cursorPosition = vec2.create();
private boundingBox: BoundingBox;
private _viewArea: BoundingBox;
constructor() {
super();
this.boundingBox = new BoundingBox(null);
this._viewArea = new BoundingBox(null);
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
this.addCommandExecutor(
CursorMoveCommand,
this.setCursorPosition.bind(this)
);
this.addCommandExecutor(CursorMoveCommand, this.setCursorPosition.bind(this));
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
}
public get viewAreaSize(): vec2 {
return this.boundingBox.size;
public get viewArea(): BoundingBox {
return this._viewArea;
}
private draw(c: BeforeRenderCommand) {
c.renderer.setCameraPosition(this.boundingBox.topLeft);
const canvasAspectRatio = c.renderer.canvasSize.x / c.renderer.canvasSize.y;
this.viewArea.size = vec2.fromValues(
Math.sqrt(this.inViewAreaSize * canvasAspectRatio),
Math.sqrt(this.inViewAreaSize / canvasAspectRatio)
);
c.renderer.setViewArea(this._viewArea);
c.renderer.setCursorPosition(this.cursorPosition);
this.boundingBox.size = c.renderer.setInViewArea(this.inViewArea);
}
private moveTo(c: MoveToCommand) {
this.boundingBox.topLeft = c.position;
this._viewArea.topLeft = vec2.fromValues(
c.position.x - this._viewArea.size.x / 2,
c.position.y + this._viewArea.size.y / 2
);
}
private zoom(c: ZoomCommand) {
this.inViewArea *= c.factor;
this.inViewAreaSize *= c.factor;
}
private setCursorPosition(c: CursorMoveCommand) {

View file

@ -1,4 +1,4 @@
import { vec2 } from 'gl-matrix';
import { vec2, vec3 } from 'gl-matrix';
import { KeyDownCommand } from '../../input/commands/key-down';
import { KeyUpCommand } from '../../input/commands/key-up';
import { SwipeCommand } from '../../input/commands/swipe';
@ -13,21 +13,25 @@ import { Blob } from '../../shapes/types/blob';
import { RenderCommand } from '../../drawing/commands/render';
import { DrawableBlob } from '../../drawing/drawables/drawable-blob';
import { Lamp } from './lamp';
import { Game } from '../../game';
import { IGame } from '../../i-game';
export class Character extends GameObject {
private keysDown: Set<string> = new Set();
private light = new Lamp(
vec2.create(),
40,
vec3.fromValues(0.67, 0.0, 0.33),
2
);
private shape: DrawableBlob;
private shape = new DrawableBlob(vec2.create());
private static speed = 1.5;
constructor(
private physics: Physics,
private camera: Camera,
private light: Lamp
) {
constructor(private game: IGame) {
super();
this.shape = new DrawableBlob(vec2.create());
game.addObject(this.light);
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
@ -38,13 +42,18 @@ export class Character extends GameObject {
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
this.addCommandExecutor(SwipeCommand, (c) => {
this.tryMoving(
vec2.multiply(vec2.create(), c.delta, this.camera.viewAreaSize)
vec2.multiply(vec2.create(), c.delta, this.game.viewArea.size)
);
});
}
public get position(): vec2 {
return this.shape.center;
}
private draw(c: RenderCommand) {
c.renderer.drawShape(this.shape);
this.light.sendCommand(c);
}
private tryMoving(delta: vec2, isFirstIteration = true) {
@ -87,6 +96,9 @@ export class Character extends GameObject {
)
.sort((e) => Math.abs(e.distance));
if (intersecting.length < 1) {
return;
}
const normal = intersecting[0].shape.normal(this.shape.center);
const maxDistance = intersecting.reduce((p, c) =>
@ -106,7 +118,7 @@ export class Character extends GameObject {
private getNearShapesTo(
shape: Blob
): Array<{ shape: IShape; distance: number }> {
return this.physics
return this.game
.findIntersecting(shape.boundingBox)
.filter((b) => b.shape)
.map((b) => ({
@ -118,7 +130,6 @@ export class Character extends GameObject {
private setPosition(value: vec2) {
this.shape.position = value;
this.camera.sendCommand(new MoveToCommand(value));
vec2.add(value, value, vec2.fromValues(80, 0));
this.light.sendCommand(new MoveToCommand(value));
}

View file

@ -10,7 +10,7 @@ export class Lamp extends GameObject {
constructor(center: vec2, radius: number, color: vec3, lightness: number) {
super();
this.light = new CircleLight(this, center, radius, color, lightness);
this.light = new CircleLight(center, radius, color, lightness);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));

View file

@ -17,7 +17,7 @@ export class Tunnel extends GameObject {
) {
super();
this.shape = new DrawableTunnel(from, to, fromRadius, toRadius);
this.shape = new DrawableTunnel(from, to, fromRadius, toRadius, this);
physics.addStaticBoundingBox(this.shape.boundingBox);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
}

View file

@ -1,27 +0,0 @@
import { vec2, vec3 } from 'gl-matrix';
import { Objects } from '../objects';
import { Camera } from '../types/camera';
import { Character } from '../types/character';
import { Lamp } from '../types/lamp';
import { Physics } from '../../physics/physics';
import { GameObject } from '../game-object';
export const createCharacter = (
objects: Objects,
physics: Physics
): GameObject => {
const light = new Lamp(
vec2.create(),
40,
vec3.fromValues(0.67, 0.0, 0.33),
2
);
const camera = new Camera();
const character = new Character(physics, camera, light);
objects.addObject(light);
objects.addObject(camera);
objects.addObject(character);
return character;
};

View file

@ -1,10 +1,13 @@
import { BoundingBox } from './bounding-box';
import { vec2 } from 'gl-matrix';
import { GameObject } from '../objects/game-object';
export interface IShape {
readonly isInverted: boolean;
readonly boundingBox: BoundingBox;
readonly gameObject?: GameObject;
distance(target: vec2): number;
normal(from: vec2): vec2;

View file

@ -3,6 +3,7 @@ import { IShape } from '../i-shape';
import { BoundingBox } from '../bounding-box';
import { Circle } from './circle';
import { settings } from '../../drawing/settings';
import { GameObject } from '../../objects/game-object';
export class Blob implements IShape {
private static readonly boundingCircleRadius = 19;
@ -31,7 +32,10 @@ export class Blob implements IShape {
settings.shaderMacros.footRadius
);
public constructor(center: vec2) {
public constructor(
center: vec2,
public readonly gameObject: GameObject = null
) {
this.position = center;
}
@ -68,6 +72,6 @@ export class Blob implements IShape {
}
public clone(): Blob {
return new Blob(this.boundingCircle.center);
return new Blob(this.boundingCircle.center, this.gameObject);
}
}

View file

@ -1,11 +1,16 @@
import { vec2 } from 'gl-matrix';
import { IShape } from '../i-shape';
import { BoundingBox } from '../bounding-box';
import { GameObject } from '../../objects/game-object';
export class Circle implements IShape {
public readonly isInverted = false;
public constructor(public center = vec2.create(), public radius = 0) {}
public constructor(
public center = vec2.create(),
public radius = 0,
public readonly gameObject: GameObject = null
) {}
public distance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius;
@ -36,6 +41,6 @@ export class Circle implements IShape {
}
public clone(): Circle {
return new Circle(vec2.clone(this.center), this.radius);
return new Circle(vec2.clone(this.center), this.radius, this.gameObject);
}
}

View file

@ -4,6 +4,7 @@ import { clamp01 } from '../../helper/clamp';
import { mix } from '../../helper/mix';
import { IShape } from '../i-shape';
import { rotate90Deg } from '../../helper/rotate-90-deg';
import { GameObject } from '../../objects/game-object';
export class TunnelShape implements IShape {
public readonly isInverted = true;
@ -14,7 +15,8 @@ export class TunnelShape implements IShape {
public readonly from: vec2,
public readonly to: vec2,
public readonly fromRadius: number,
public readonly toRadius: number
public readonly toRadius: number,
public readonly gameObject: GameObject = null
) {
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
}
@ -106,7 +108,8 @@ export class TunnelShape implements IShape {
vec2.clone(this.from),
vec2.clone(this.to),
this.fromRadius,
this.toRadius
this.toRadius,
this.gameObject
);
}
}