Add blob and fix some issues
This commit is contained in:
parent
5b4e67cbf0
commit
edca93fcfe
13 changed files with 237 additions and 67 deletions
26
frontend/src/scripts/drawing/drawables/drawable-blob.ts
Normal file
26
frontend/src/scripts/drawing/drawables/drawable-blob.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { IDrawable } from './i-drawable';
|
||||||
|
import { IDrawableDescriptor } from './i-drawable-descriptor';
|
||||||
|
import { settings } from '../settings';
|
||||||
|
import { Blob } from '../../shapes/types/blob';
|
||||||
|
|
||||||
|
export class DrawableBlob extends Blob implements IDrawable {
|
||||||
|
public static descriptor: IDrawableDescriptor = {
|
||||||
|
uniformName: 'blobs',
|
||||||
|
countMacroName: 'blobCount',
|
||||||
|
shaderCombinationSteps: settings.shaderCombinations.blobSteps,
|
||||||
|
};
|
||||||
|
|
||||||
|
public serializeToUniforms(uniforms: any): 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,22 @@
|
||||||
import { FrameBuffer } from './frame-buffer';
|
import { FrameBuffer } from './frame-buffer';
|
||||||
|
import { enableExtension } from '../helper/enable-extension';
|
||||||
|
|
||||||
export class IntermediateFrameBuffer extends FrameBuffer {
|
export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
private frameTexture: WebGLTexture;
|
private frameTexture: WebGLTexture;
|
||||||
|
|
||||||
|
private floatLinearEnabled = true;
|
||||||
|
|
||||||
constructor(gl: WebGL2RenderingContext) {
|
constructor(gl: WebGL2RenderingContext) {
|
||||||
super(gl);
|
super(gl);
|
||||||
|
|
||||||
|
enableExtension(gl, 'EXT_color_buffer_float');
|
||||||
|
|
||||||
|
try {
|
||||||
|
enableExtension(gl, 'OES_texture_float_linear');
|
||||||
|
} catch {
|
||||||
|
this.floatLinearEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
this.frameTexture = this.gl.createTexture();
|
this.frameTexture = this.gl.createTexture();
|
||||||
this.configureTexture();
|
this.configureTexture();
|
||||||
|
|
||||||
|
|
@ -27,12 +38,12 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
this.gl.texImage2D(
|
this.gl.texImage2D(
|
||||||
this.gl.TEXTURE_2D,
|
this.gl.TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
this.gl.RGBA,
|
this.gl.RG16F,
|
||||||
this.size.x,
|
this.size.x,
|
||||||
this.size.y,
|
this.size.y,
|
||||||
0,
|
0,
|
||||||
this.gl.RGBA,
|
this.gl.RG,
|
||||||
this.gl.UNSIGNED_BYTE,
|
this.gl.FLOAT,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -42,12 +53,12 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
this.gl.texParameteri(
|
this.gl.texParameteri(
|
||||||
this.gl.TEXTURE_2D,
|
this.gl.TEXTURE_2D,
|
||||||
this.gl.TEXTURE_MAG_FILTER,
|
this.gl.TEXTURE_MAG_FILTER,
|
||||||
this.gl.LINEAR
|
this.floatLinearEnabled ? this.gl.LINEAR : this.gl.NEAREST
|
||||||
);
|
);
|
||||||
this.gl.texParameteri(
|
this.gl.texParameteri(
|
||||||
this.gl.TEXTURE_2D,
|
this.gl.TEXTURE_2D,
|
||||||
this.gl.TEXTURE_MIN_FILTER,
|
this.gl.TEXTURE_MIN_FILTER,
|
||||||
this.gl.LINEAR
|
this.floatLinearEnabled ? this.gl.LINEAR : this.gl.NEAREST
|
||||||
);
|
);
|
||||||
this.gl.texParameteri(
|
this.gl.texParameteri(
|
||||||
this.gl.TEXTURE_2D,
|
this.gl.TEXTURE_2D,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
export const enableExtension = (
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
name: string
|
||||||
|
): any => {
|
||||||
|
if (gl.getSupportedExtensions().indexOf(name) == -1) {
|
||||||
|
throw new Error(`Unsupported extension ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gl.getExtension(name);
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { InfoText } from '../../../objects/types/info-text';
|
import { InfoText } from '../../../objects/types/info-text';
|
||||||
|
import { enableExtension } from './enable-extension';
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/
|
// https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/
|
||||||
|
|
||||||
|
|
@ -10,15 +11,8 @@ export class WebGlStopwatch {
|
||||||
private resultsInNanoSeconds: number;
|
private resultsInNanoSeconds: number;
|
||||||
|
|
||||||
constructor(private gl: WebGL2RenderingContext) {
|
constructor(private gl: WebGL2RenderingContext) {
|
||||||
if (
|
this.timerExtension = enableExtension(
|
||||||
this.gl
|
gl,
|
||||||
.getSupportedExtensions()
|
|
||||||
.indexOf('EXT_disjoint_timer_query_webgl2') == -1
|
|
||||||
) {
|
|
||||||
throw new Error('Unsupported extension');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timerExtension = this.gl.getExtension(
|
|
||||||
'EXT_disjoint_timer_query_webgl2'
|
'EXT_disjoint_timer_query_webgl2'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
);
|
);
|
||||||
|
|
||||||
const closest = this.programs.find((p) =>
|
const closest = this.programs.find((p) =>
|
||||||
p.values.every((v, i) => v > values[i])
|
p.values.every((v, i) => v >= values[i])
|
||||||
);
|
);
|
||||||
|
|
||||||
this.current = closest ? closest.program : last(this.programs).program;
|
this.current = closest ? closest.program : last(this.programs).program;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import { FpsAutoscaler } from './fps-autoscaler';
|
||||||
import { RenderingPass } from './rendering-pass';
|
import { RenderingPass } from './rendering-pass';
|
||||||
import { IDrawable } from '../drawables/i-drawable';
|
import { IDrawable } from '../drawables/i-drawable';
|
||||||
import { DrawableTunnel } from '../drawables/drawable-tunnel';
|
import { DrawableTunnel } from '../drawables/drawable-tunnel';
|
||||||
|
import { enableExtension } from '../graphics-library/helper/enable-extension';
|
||||||
|
import { DrawableBlob } from '../drawables/drawable-blob';
|
||||||
|
|
||||||
export class WebGl2Renderer implements IRenderer {
|
export class WebGl2Renderer implements IRenderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
|
|
@ -50,7 +52,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.distancePass = new RenderingPass(
|
this.distancePass = new RenderingPass(
|
||||||
this.gl,
|
this.gl,
|
||||||
[caveVertexShader, caveFragmentShader],
|
[caveVertexShader, caveFragmentShader],
|
||||||
[DrawableTunnel.descriptor],
|
[DrawableTunnel.descriptor, DrawableBlob.descriptor],
|
||||||
this.distanceFieldFrameBuffer
|
this.distanceFieldFrameBuffer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ export const settings = {
|
||||||
[0.2, 0.1],
|
[0.2, 0.1],
|
||||||
[0.6, 0.1],
|
[0.6, 0.1],
|
||||||
[1, 0.3],
|
[1, 0.3],
|
||||||
[1.25, 0.75],
|
/*[1.25, 0.75],
|
||||||
[1.5, 1],
|
[1.5, 1],
|
||||||
[1.75, 1.25],
|
[1.75, 1.25],
|
||||||
[1.75, 1.75],
|
[1.75, 1.75],
|
||||||
|
[2, 2],*/
|
||||||
],
|
],
|
||||||
startingTargetIndex: 2,
|
startingTargetIndex: 2,
|
||||||
scalingOptions: {
|
scalingOptions: {
|
||||||
|
|
@ -21,13 +22,15 @@ export const settings = {
|
||||||
},
|
},
|
||||||
tileMultiplier: 5,
|
tileMultiplier: 5,
|
||||||
shaderMacros: {
|
shaderMacros: {
|
||||||
distanceScale: 64,
|
|
||||||
distanceOffset: 0.0,
|
|
||||||
edgeSmoothing: 10,
|
edgeSmoothing: 10,
|
||||||
|
headRadius: 5,
|
||||||
|
torsoRadius: 10,
|
||||||
|
footRadius: 4,
|
||||||
},
|
},
|
||||||
shaderCombinations: {
|
shaderCombinations: {
|
||||||
lineSteps: [0, 1, 2, 3, 4, 8, 16, 128],
|
lineSteps: [0, 1, 2, 4, 8, 16, 128],
|
||||||
circleLightSteps: [0, 1, 2, 3],
|
blobSteps: [0, 1, 2, 8],
|
||||||
|
circleLightSteps: [0, 1],
|
||||||
pointLightSteps: [0, 1, 2, 3],
|
pointLightSteps: [0, 1, 2, 3],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define LINE_COUNT {lineCount}
|
#define LINE_COUNT {lineCount}
|
||||||
#define CAVE_COLOR vec3(0.36, 0.38, 0.76)
|
#define BLOB_COUNT {blobCount}
|
||||||
#define AIR_COLOR vec3(0.7)
|
|
||||||
#define DISTANCE_SCALE {distanceScale}
|
|
||||||
#define DISTANCE_OFFSET {distanceOffset}
|
|
||||||
|
|
||||||
uniform float maxMinDistance;
|
uniform float maxMinDistance;
|
||||||
|
|
||||||
|
|
@ -17,32 +14,76 @@ uniform float maxMinDistance;
|
||||||
float fromRadius;
|
float fromRadius;
|
||||||
float toRadius;
|
float toRadius;
|
||||||
}[LINE_COUNT] lines;
|
}[LINE_COUNT] lines;
|
||||||
#endif
|
|
||||||
|
|
||||||
in vec2 worldCoordinates;
|
void lineMinDistance(vec2 worldCoordinates, inout float minDistance) {
|
||||||
out vec4 fragmentColor;
|
minDistance = maxMinDistance;
|
||||||
|
|
||||||
void main() {
|
|
||||||
float realDistance = 0.0;
|
|
||||||
|
|
||||||
#if LINE_COUNT > 0
|
|
||||||
float minDistance = maxMinDistance;
|
|
||||||
|
|
||||||
for (int i = 0; i < LINE_COUNT; i++) {
|
for (int i = 0; i < LINE_COUNT; i++) {
|
||||||
vec2 targetFromDelta = worldCoordinates - lines[i].from;
|
vec2 targetFromDelta = worldCoordinates - lines[i].from;
|
||||||
vec2 toFromDelta = lines[i].toFromDelta;
|
vec2 toFromDelta = lines[i].toFromDelta;
|
||||||
float h = clamp(dot(targetFromDelta, toFromDelta) / dot(toFromDelta, toFromDelta), 0.0, 1.0);
|
|
||||||
float lineDistance = distance(targetFromDelta, toFromDelta * h) - mix(lines[i].fromRadius, lines[i].toRadius, h);
|
float h = clamp(
|
||||||
|
dot(targetFromDelta, toFromDelta) / dot(toFromDelta, toFromDelta),
|
||||||
|
0.0, 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
float lineDistance = (
|
||||||
|
distance(targetFromDelta, toFromDelta * h)
|
||||||
|
- mix(lines[i].fromRadius, lines[i].toRadius, h)
|
||||||
|
);
|
||||||
|
|
||||||
minDistance = min(minDistance, lineDistance);
|
minDistance = min(minDistance, lineDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
realDistance = -minDistance;
|
minDistance *= -1.0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BLOB_COUNT > 0
|
||||||
|
#define headRadius {headRadius}
|
||||||
|
#define torsoRadius {torsoRadius}
|
||||||
|
#define footRadius {footRadius}
|
||||||
|
|
||||||
|
uniform struct Blob {
|
||||||
|
vec2 headCenter;
|
||||||
|
vec2 torsoCenter;
|
||||||
|
vec2 leftFootCenter;
|
||||||
|
vec2 rightFootCenter;
|
||||||
|
}[BLOB_COUNT] blobs;
|
||||||
|
|
||||||
|
float circleMinDistance(vec2 worldCoordinates, vec2 circleCenter, float radius) {
|
||||||
|
return distance(worldCoordinates, circleCenter) - radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blobMinDistance(vec2 worldCoordinates, inout float minDistance) {
|
||||||
|
float k = 1.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < BLOB_COUNT; i++) {
|
||||||
|
float res = exp2(-k * circleMinDistance(worldCoordinates, blobs[i].headCenter, headRadius));
|
||||||
|
res += exp2(-k * circleMinDistance(worldCoordinates, blobs[i].torsoCenter, torsoRadius));
|
||||||
|
res += exp2(-k * circleMinDistance(worldCoordinates, blobs[i].leftFootCenter, footRadius));
|
||||||
|
res += exp2(-k * circleMinDistance(worldCoordinates, blobs[i].rightFootCenter, footRadius));
|
||||||
|
|
||||||
|
minDistance = min(minDistance, -log2(res) / k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
in vec2 worldCoordinates;
|
||||||
|
out vec2 fragmentColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float minDistance = -maxMinDistance;
|
||||||
|
|
||||||
|
#if LINE_COUNT > 0
|
||||||
|
lineMinDistance(worldCoordinates, minDistance);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fragmentColor = vec4(
|
#if BLOB_COUNT > 0
|
||||||
mix(CAVE_COLOR, AIR_COLOR, clamp(realDistance, 0.0, 1.0)),
|
blobMinDistance(worldCoordinates, minDistance);
|
||||||
(realDistance + DISTANCE_OFFSET) / DISTANCE_SCALE
|
#endif
|
||||||
|
|
||||||
);
|
fragmentColor = vec2(minDistance, 0.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,26 @@ precision mediump float;
|
||||||
|
|
||||||
#define CIRCLE_LIGHT_COUNT {circleLightCount}
|
#define CIRCLE_LIGHT_COUNT {circleLightCount}
|
||||||
#define POINT_LIGHT_COUNT {pointLightCount}
|
#define POINT_LIGHT_COUNT {pointLightCount}
|
||||||
#define DISTANCE_SCALE {distanceScale}
|
|
||||||
#define DISTANCE_OFFSET {distanceOffset}
|
|
||||||
#define EDGE_SMOOTHING {edgeSmoothing}
|
#define EDGE_SMOOTHING {edgeSmoothing}
|
||||||
|
|
||||||
uniform sampler2D distanceTexture;
|
uniform sampler2D distanceTexture;
|
||||||
uniform vec2 viewBoxSize;
|
uniform vec2 viewBoxSize;
|
||||||
|
|
||||||
|
vec3[4] colors = vec3[4](
|
||||||
|
vec3(0.5),
|
||||||
|
vec3(1.0, 0.0, 0.0),
|
||||||
|
vec3(0.0, 1.0, 0.0),
|
||||||
|
vec3(0.0, 0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
float getDistance(in vec2 target, out vec3 color) {
|
float getDistance(in vec2 target, out vec3 color) {
|
||||||
vec4 values = texture(distanceTexture, target);
|
vec4 values = texture(distanceTexture, target);
|
||||||
color = values.rgb;
|
color = colors[int(values[1])];
|
||||||
return (values.w - DISTANCE_OFFSET) * DISTANCE_SCALE;
|
return values[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDistance(in vec2 target) {
|
float getDistance(in vec2 target) {
|
||||||
return (texture(distanceTexture, target).w - DISTANCE_OFFSET) * DISTANCE_SCALE;
|
return texture(distanceTexture, target)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
|
|
@ -58,10 +63,6 @@ void main() {
|
||||||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||||
float lightCenterDistance = distance(circleLights[i].center, worldCoordinates);
|
float lightCenterDistance = distance(circleLights[i].center, worldCoordinates);
|
||||||
|
|
||||||
/*if (lightCenterDistance < circleLights[i].radius) {
|
|
||||||
fragmentColor = vec4(1.0, 1.0, 0.0, 1.0);
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
vec3 lightColorAtPosition = circleLights[i].value / pow(
|
vec3 lightColorAtPosition = circleLights[i].value / pow(
|
||||||
lightCenterDistance / LIGHT_DROP + 1.0, 2.0
|
lightCenterDistance / LIGHT_DROP + 1.0, 2.0
|
||||||
);
|
);
|
||||||
|
|
@ -76,7 +77,7 @@ void main() {
|
||||||
rayLength = lightCenterDistance - circleLights[i].radius;
|
rayLength = lightCenterDistance - circleLights[i].radius;
|
||||||
float minDistance = getDistance(uvCoordinates + direction * rayLength);
|
float minDistance = getDistance(uvCoordinates + direction * rayLength);
|
||||||
exponentialDecayDistance = (exponentialDecayDistance + minDistance) / 2.0;
|
exponentialDecayDistance = (exponentialDecayDistance + minDistance) / 2.0;
|
||||||
q = min(q, exponentialDecayDistance / rayLength);
|
q = min(q, minDistance / rayLength);
|
||||||
|
|
||||||
lighting += lightColorAtPosition * clamp(
|
lighting += lightColorAtPosition * clamp(
|
||||||
q / circleLights[i].radius * (lightCenterDistance + 1.0), 0.0, 1.0
|
q / circleLights[i].radius * (lightCenterDistance + 1.0), 0.0, 1.0
|
||||||
|
|
@ -122,5 +123,5 @@ void main() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fragmentColor = vec4(colorAtPosition * lighting * clamp(startingDistance, 0.0, 1.0), 1.0);
|
fragmentColor = vec4(colorAtPosition * lighting * step(0.0, startingDistance), 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,10 @@ export class Camera extends GameObject {
|
||||||
private cursorPosition = vec2.create();
|
private cursorPosition = vec2.create();
|
||||||
private boundingBox: BoundingBox;
|
private boundingBox: BoundingBox;
|
||||||
|
|
||||||
constructor(physics: Physics, private light: Lamp) {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.boundingBox = new BoundingBox(null);
|
this.boundingBox = new BoundingBox(null);
|
||||||
//physics.addDynamicBoundingBox(this.boundingBox);
|
|
||||||
|
|
||||||
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
|
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
|
||||||
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
||||||
|
|
@ -40,7 +39,6 @@ export class Camera extends GameObject {
|
||||||
|
|
||||||
private moveTo(c: MoveToCommand) {
|
private moveTo(c: MoveToCommand) {
|
||||||
this.boundingBox.topLeft = c.position;
|
this.boundingBox.topLeft = c.position;
|
||||||
this.light.sendCommand(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private zoom(c: ZoomCommand) {
|
private zoom(c: ZoomCommand) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
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';
|
||||||
|
|
@ -10,22 +9,28 @@ import { Physics } from '../../physics/physics';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { Camera } from './camera';
|
import { Camera } from './camera';
|
||||||
import { IShape } from '../../shapes/i-shape';
|
import { IShape } from '../../shapes/i-shape';
|
||||||
import { rotate90Deg } from '../../helper/rotate-90-deg';
|
import { Blob } from '../../shapes/types/blob';
|
||||||
import { max } from 'gl-matrix/src/gl-matrix/vec2';
|
import { RenderCommand } from '../../drawing/commands/render';
|
||||||
|
import { DrawableBlob } from '../../drawing/drawables/drawable-blob';
|
||||||
|
import { Lamp } from './lamp';
|
||||||
|
|
||||||
export class Character extends GameObject {
|
export class Character extends GameObject {
|
||||||
private keysDown: Set<string> = new Set();
|
private keysDown: Set<string> = new Set();
|
||||||
|
|
||||||
private shape: Circle;
|
private shape: DrawableBlob;
|
||||||
private static speed = 1.5;
|
private static speed = 1.5;
|
||||||
|
|
||||||
constructor(private physics: Physics, private camera: Camera) {
|
constructor(
|
||||||
|
private physics: Physics,
|
||||||
|
private camera: Camera,
|
||||||
|
private light: Lamp
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.shape = new Circle();
|
this.shape = new DrawableBlob(vec2.create());
|
||||||
this.shape.radius = 80;
|
|
||||||
|
|
||||||
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
|
||||||
|
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
|
||||||
this.addCommandExecutor(TeleportToCommand, (c) =>
|
this.addCommandExecutor(TeleportToCommand, (c) =>
|
||||||
this.setPosition(c.position)
|
this.setPosition(c.position)
|
||||||
);
|
);
|
||||||
|
|
@ -38,6 +43,10 @@ export class Character extends GameObject {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private draw(c: RenderCommand) {
|
||||||
|
c.renderer.drawShape(this.shape);
|
||||||
|
}
|
||||||
|
|
||||||
private tryMoving(delta: vec2, isFirstIteration = true) {
|
private tryMoving(delta: vec2, isFirstIteration = true) {
|
||||||
const maxStep = 2;
|
const maxStep = 2;
|
||||||
if (vec2.length(delta) > maxStep) {
|
if (vec2.length(delta) > maxStep) {
|
||||||
|
|
@ -95,7 +104,7 @@ export class Character extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getNearShapesTo(
|
private getNearShapesTo(
|
||||||
shape: Circle
|
shape: Blob
|
||||||
): Array<{ shape: IShape; distance: number }> {
|
): Array<{ shape: IShape; distance: number }> {
|
||||||
return this.physics
|
return this.physics
|
||||||
.findIntersecting(shape.boundingBox)
|
.findIntersecting(shape.boundingBox)
|
||||||
|
|
@ -108,8 +117,10 @@ export class Character extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPosition(value: vec2) {
|
private setPosition(value: vec2) {
|
||||||
this.shape.center = value;
|
this.shape.position = value;
|
||||||
this.camera.sendCommand(new MoveToCommand(this.shape.center));
|
this.camera.sendCommand(new MoveToCommand(value));
|
||||||
|
vec2.add(value, value, vec2.fromValues(80, 0));
|
||||||
|
this.light.sendCommand(new MoveToCommand(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public stepHandler(c: StepCommand) {
|
public stepHandler(c: StepCommand) {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ export const createCharacter = (
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
const camera = new Camera(physics, light);
|
const camera = new Camera();
|
||||||
const character = new Character(physics, camera);
|
const character = new Character(physics, camera, light);
|
||||||
objects.addObject(light);
|
objects.addObject(light);
|
||||||
objects.addObject(camera);
|
objects.addObject(camera);
|
||||||
objects.addObject(character);
|
objects.addObject(character);
|
||||||
|
|
|
||||||
73
frontend/src/scripts/shapes/types/blob.ts
Normal file
73
frontend/src/scripts/shapes/types/blob.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { IShape } from '../i-shape';
|
||||||
|
import { BoundingBox } from '../bounding-box';
|
||||||
|
import { Circle } from './circle';
|
||||||
|
import { settings } from '../../drawing/settings';
|
||||||
|
|
||||||
|
export class Blob implements IShape {
|
||||||
|
private static readonly boundingCircleRadius = 19;
|
||||||
|
private static readonly headOffset = vec2.fromValues(0, 15);
|
||||||
|
private static readonly torsoOffset = vec2.fromValues(0, 0);
|
||||||
|
private static readonly leftFootOffset = vec2.fromValues(-5, -10);
|
||||||
|
private static readonly rightFootOffset = vec2.fromValues(5, -10);
|
||||||
|
|
||||||
|
public readonly isInverted = false;
|
||||||
|
|
||||||
|
protected boundingCircle = new Circle(
|
||||||
|
vec2.create(),
|
||||||
|
Blob.boundingCircleRadius
|
||||||
|
);
|
||||||
|
protected head = new Circle(vec2.create(), settings.shaderMacros.headRadius);
|
||||||
|
protected torso = new Circle(
|
||||||
|
vec2.create(),
|
||||||
|
settings.shaderMacros.torsoRadius
|
||||||
|
);
|
||||||
|
protected leftFoot = new Circle(
|
||||||
|
vec2.create(),
|
||||||
|
settings.shaderMacros.footRadius
|
||||||
|
);
|
||||||
|
protected rightFoot = new Circle(
|
||||||
|
vec2.create(),
|
||||||
|
settings.shaderMacros.footRadius
|
||||||
|
);
|
||||||
|
|
||||||
|
public constructor(center: vec2) {
|
||||||
|
this.position = center;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set position(value: vec2) {
|
||||||
|
vec2.copy(this.boundingCircle.center, value);
|
||||||
|
vec2.add(this.head.center, value, Blob.headOffset);
|
||||||
|
vec2.add(this.torso.center, value, Blob.torsoOffset);
|
||||||
|
vec2.add(this.leftFoot.center, value, Blob.leftFootOffset);
|
||||||
|
vec2.add(this.rightFoot.center, value, Blob.rightFootOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get center(): vec2 {
|
||||||
|
return this.boundingCircle.center;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get radius(): number {
|
||||||
|
return this.boundingCircle.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public distance(target: vec2): number {
|
||||||
|
return this.boundingCircle.distance(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.distance(target) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): Blob {
|
||||||
|
return new Blob(this.boundingCircle.center);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue