world.ts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +48 -0
- package/.prettierrc +3 -0
- package/dist/assets/image-load-worker-nFfjtK8d.js +2 -0
- package/dist/assets/image-load-worker-nFfjtK8d.js.map +1 -0
- package/dist/buffer.d.ts +14 -0
- package/dist/common.d.ts +2 -0
- package/dist/constants.d.ts +1 -0
- package/dist/control.d.ts +5 -0
- package/dist/depth-buffer.d.ts +13 -0
- package/dist/elevation.d.ts +13 -0
- package/dist/image-load-worker.d.ts +1 -0
- package/dist/image-load.d.ts +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1535 -0
- package/dist/index.js.js +1535 -0
- package/dist/index.js.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/layers/index.d.ts +36 -0
- package/dist/layers/line/index.d.ts +18 -0
- package/dist/layers/mesh/index.d.ts +4 -0
- package/dist/layers/terrain/image-texture.d.ts +11 -0
- package/dist/layers/terrain/index.d.ts +13 -0
- package/dist/layers/terrain/texture.d.ts +10 -0
- package/dist/layers/terrain/tile-cache.d.ts +14 -0
- package/dist/layers/terrain/tile-downsampler.d.ts +16 -0
- package/dist/layers/terrain/tile-index-cache.d.ts +21 -0
- package/dist/layers/terrain/tile-shapes.d.ts +8 -0
- package/dist/layers/utils.d.ts +3 -0
- package/dist/math.d.ts +7 -0
- package/dist/program.d.ts +76 -0
- package/dist/subscriber.d.ts +1 -0
- package/dist/viewport.d.ts +24 -0
- package/dist/world.d.ts +59 -0
- package/package.json +29 -0
- package/src/buffer.ts +36 -0
- package/src/common.ts +13 -0
- package/src/constants.ts +1 -0
- package/src/control.ts +83 -0
- package/src/depth-buffer.ts +92 -0
- package/src/elevation.ts +75 -0
- package/src/glsl.d.ts +4 -0
- package/src/image-load-worker.ts +32 -0
- package/src/image-load.ts +41 -0
- package/src/index.ts +1 -0
- package/src/layers/depth.glsl +22 -0
- package/src/layers/index.ts +38 -0
- package/src/layers/line/fragment.glsl +10 -0
- package/src/layers/line/index.ts +261 -0
- package/src/layers/line/vertex.glsl +63 -0
- package/src/layers/mesh/fragment.glsl +10 -0
- package/src/layers/mesh/index.ts +258 -0
- package/src/layers/mesh/vertex.glsl +35 -0
- package/src/layers/terrain/fragment.glsl +11 -0
- package/src/layers/terrain/image-texture.ts +53 -0
- package/src/layers/terrain/index.ts +307 -0
- package/src/layers/terrain/texture.ts +27 -0
- package/src/layers/terrain/tile-cache.ts +73 -0
- package/src/layers/terrain/tile-downsampler.ts +36 -0
- package/src/layers/terrain/tile-index-cache.ts +44 -0
- package/src/layers/terrain/tile-shapes.ts +45 -0
- package/src/layers/terrain/vertex.glsl +34 -0
- package/src/layers/utils.ts +5 -0
- package/src/math.ts +39 -0
- package/src/program.ts +200 -0
- package/src/subscriber.ts +17 -0
- package/src/viewport.ts +132 -0
- package/src/world.ts +192 -0
- package/tsconfig.json +13 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type Data = ["load" | "cancel", string];
|
|
2
|
+
|
|
3
|
+
addEventListener("message", async event => {
|
|
4
|
+
const [action, url] = event.data as Data;
|
|
5
|
+
if (action !== "load") return;
|
|
6
|
+
const abortController = new AbortController();
|
|
7
|
+
const { signal } = abortController;
|
|
8
|
+
const handler = (event: MessageEvent) => {
|
|
9
|
+
const [action, thisUrl] = event.data as Data;
|
|
10
|
+
if (action === "cancel" && thisUrl === url) abortController.abort();
|
|
11
|
+
};
|
|
12
|
+
addEventListener("message", handler);
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(url, { mode: "cors", signal });
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
postMessage({ url, image: undefined });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const blob = await response.blob();
|
|
20
|
+
const image = await createImageBitmap(blob);
|
|
21
|
+
postMessage({ url, image });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (
|
|
24
|
+
error instanceof Error &&
|
|
25
|
+
error.message === "The user aborted a request."
|
|
26
|
+
) {
|
|
27
|
+
// Ignore
|
|
28
|
+
} else throw error;
|
|
29
|
+
} finally {
|
|
30
|
+
removeEventListener("message", handler);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const worker = new Worker(new URL("./image-load-worker.ts", import.meta.url), {
|
|
2
|
+
type: "module",
|
|
3
|
+
});
|
|
4
|
+
|
|
5
|
+
export type ImageLoad = {
|
|
6
|
+
loaded: boolean;
|
|
7
|
+
cancel: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const createImageLoad = ({
|
|
11
|
+
url,
|
|
12
|
+
onLoad,
|
|
13
|
+
}: {
|
|
14
|
+
url: string;
|
|
15
|
+
onLoad: (image: ImageBitmap | undefined) => void;
|
|
16
|
+
}) => {
|
|
17
|
+
let loaded = false;
|
|
18
|
+
|
|
19
|
+
const handler = ({ data }: MessageEvent) => {
|
|
20
|
+
if (canceled || url !== data.url) return;
|
|
21
|
+
worker.removeEventListener("message", handler);
|
|
22
|
+
loaded = true;
|
|
23
|
+
onLoad(data.image);
|
|
24
|
+
};
|
|
25
|
+
worker.addEventListener("message", handler);
|
|
26
|
+
|
|
27
|
+
let canceled = false;
|
|
28
|
+
const cancel = () => {
|
|
29
|
+
canceled = true;
|
|
30
|
+
worker.postMessage(["cancel", url]);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
worker.postMessage(["load", url]);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
get loaded() {
|
|
37
|
+
return loaded;
|
|
38
|
+
},
|
|
39
|
+
cancel,
|
|
40
|
+
} satisfies ImageLoad;
|
|
41
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./world";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#version 300 es
|
|
2
|
+
|
|
3
|
+
precision highp float;
|
|
4
|
+
|
|
5
|
+
uniform int index;
|
|
6
|
+
|
|
7
|
+
out vec4 result;
|
|
8
|
+
|
|
9
|
+
vec2 pack_depth(in float depth) {
|
|
10
|
+
float value = depth * (256.f * 256.f - 1.f) / (256.f * 256.f);
|
|
11
|
+
vec3 encode = fract(value * vec3(1.f, 256.f, 256.f * 256.f));
|
|
12
|
+
return encode.xy - encode.yz / 256.f + 1.f / 512.f;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
vec2 pack_index(in int index) {
|
|
16
|
+
float value = float(index) / 256.f;
|
|
17
|
+
return vec2(floor(value) / 255.f, fract(value) * 256.f / 255.f);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
void main(void) {
|
|
21
|
+
result = vec4(pack_depth(gl_FragCoord.z), pack_index(index));
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { quat, vec3, vec4 } from "gl-matrix";
|
|
2
|
+
|
|
3
|
+
import type { Viewport } from "../viewport";
|
|
4
|
+
import type { LineLayer } from "./line";
|
|
5
|
+
import type { MeshLayer } from "./mesh";
|
|
6
|
+
import type { TerrainLayer } from "./terrain";
|
|
7
|
+
|
|
8
|
+
export type Terrain = {
|
|
9
|
+
readonly terrainUrl: string;
|
|
10
|
+
readonly imageryUrl: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type Mesh = {
|
|
14
|
+
vertices: vec3[];
|
|
15
|
+
indices: vec3[];
|
|
16
|
+
position: vec3;
|
|
17
|
+
orientation: quat;
|
|
18
|
+
color: vec4;
|
|
19
|
+
size: number;
|
|
20
|
+
minSizePixels?: number;
|
|
21
|
+
maxSizePixels?: number;
|
|
22
|
+
pickable: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type Line = {
|
|
26
|
+
points: vec3[];
|
|
27
|
+
color: vec4;
|
|
28
|
+
width: number;
|
|
29
|
+
minWidthPixels?: number | undefined;
|
|
30
|
+
maxWidthPixels?: number | undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type BaseLayer = {
|
|
34
|
+
render: (_: { viewport: Viewport; depth?: boolean; index?: number }) => void;
|
|
35
|
+
destroy: () => void;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type Layer = TerrainLayer | MeshLayer | LineLayer;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type { mat4, vec2, vec4 } from "gl-matrix";
|
|
2
|
+
import { vec3 } from "gl-matrix";
|
|
3
|
+
|
|
4
|
+
import type { Buffer } from "../../buffer";
|
|
5
|
+
import { createBuffer } from "../../buffer";
|
|
6
|
+
import { range } from "../../common";
|
|
7
|
+
import { circumference } from "../../constants";
|
|
8
|
+
import { mercator } from "../../math";
|
|
9
|
+
import { createProgram } from "../../program";
|
|
10
|
+
import type { Viewport } from "../../viewport";
|
|
11
|
+
import type { BaseLayer, Line } from "../";
|
|
12
|
+
import depthSource from "../depth.glsl";
|
|
13
|
+
import { to } from "../utils";
|
|
14
|
+
import fragmentSource from "./fragment.glsl";
|
|
15
|
+
import vertexSource from "./vertex.glsl";
|
|
16
|
+
|
|
17
|
+
export type LineLayer = BaseLayer & Line;
|
|
18
|
+
|
|
19
|
+
export const createLineLayer = (
|
|
20
|
+
gl: WebGL2RenderingContext,
|
|
21
|
+
line: Partial<Line>,
|
|
22
|
+
) => {
|
|
23
|
+
let { points, color, width, minWidthPixels, maxWidthPixels } = {
|
|
24
|
+
points: [],
|
|
25
|
+
color: [1, 1, 1, 1],
|
|
26
|
+
width: 1,
|
|
27
|
+
...line,
|
|
28
|
+
} satisfies Line;
|
|
29
|
+
|
|
30
|
+
let count = 0;
|
|
31
|
+
|
|
32
|
+
let center: vec3 = [0, 0, 0];
|
|
33
|
+
|
|
34
|
+
const positionBuffer = createBuffer({ gl, type: "f32", target: "array" });
|
|
35
|
+
const indexBuffer = createBuffer({ gl, type: "u16", target: "element" });
|
|
36
|
+
const cornerBuffer = createBuffer({ gl, type: "f32", target: "array" });
|
|
37
|
+
|
|
38
|
+
const { renderProgram, depthProgram } = createPrograms(gl, {
|
|
39
|
+
positionBuffer,
|
|
40
|
+
indexBuffer,
|
|
41
|
+
cornerBuffer,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const render = ({
|
|
45
|
+
viewport: { projection, modelView, camera, screen },
|
|
46
|
+
depth = false,
|
|
47
|
+
index = 0,
|
|
48
|
+
}: {
|
|
49
|
+
viewport: Viewport;
|
|
50
|
+
depth?: boolean;
|
|
51
|
+
index?: number;
|
|
52
|
+
}) =>
|
|
53
|
+
(depth ? depthProgram : renderProgram).execute({
|
|
54
|
+
projection,
|
|
55
|
+
modelView,
|
|
56
|
+
camera: to(camera),
|
|
57
|
+
center: to(center),
|
|
58
|
+
screen,
|
|
59
|
+
count,
|
|
60
|
+
color,
|
|
61
|
+
width: width / circumference,
|
|
62
|
+
minWidthPixels: minWidthPixels || 0,
|
|
63
|
+
maxWidthPixels: maxWidthPixels || Number.MAX_VALUE,
|
|
64
|
+
index,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const destroy = () => {
|
|
68
|
+
positionBuffer.destroy();
|
|
69
|
+
indexBuffer.destroy();
|
|
70
|
+
cornerBuffer.destroy();
|
|
71
|
+
renderProgram.destroy();
|
|
72
|
+
depthProgram.destroy();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const updatePoints = (_: vec3[]) => {
|
|
76
|
+
points = _;
|
|
77
|
+
count = _.length;
|
|
78
|
+
|
|
79
|
+
const [first] = _;
|
|
80
|
+
const [last] = _.slice(-1);
|
|
81
|
+
|
|
82
|
+
if (!first || !last) return;
|
|
83
|
+
|
|
84
|
+
center = mercator(first);
|
|
85
|
+
|
|
86
|
+
const positionData = [first, ..._, last]
|
|
87
|
+
.map(_ => vec3.sub(vec3.create(), mercator(_), center))
|
|
88
|
+
.flatMap(_ => [..._, ..._, ..._, ..._]);
|
|
89
|
+
const indexData = range(0, count * 2).flatMap(i => {
|
|
90
|
+
const [a = 0, b = 0, c = 0, d = 0] = range(i * 2, i * 2 + 4);
|
|
91
|
+
return [
|
|
92
|
+
[a, b, c],
|
|
93
|
+
[a, c, d],
|
|
94
|
+
].flat();
|
|
95
|
+
});
|
|
96
|
+
const cornerData = range(0, count + 1).flatMap(() =>
|
|
97
|
+
[
|
|
98
|
+
[-1, -1],
|
|
99
|
+
[-1, 1],
|
|
100
|
+
[1, 1],
|
|
101
|
+
[1, -1],
|
|
102
|
+
].flat(),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
positionBuffer.set(positionData);
|
|
106
|
+
indexBuffer.set(indexData);
|
|
107
|
+
cornerBuffer.set(cornerData);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
updatePoints(points);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
render,
|
|
114
|
+
destroy,
|
|
115
|
+
get points() {
|
|
116
|
+
return points;
|
|
117
|
+
},
|
|
118
|
+
set points(_: vec3[]) {
|
|
119
|
+
updatePoints(_);
|
|
120
|
+
},
|
|
121
|
+
get color() {
|
|
122
|
+
return color;
|
|
123
|
+
},
|
|
124
|
+
set color(_: vec4) {
|
|
125
|
+
color = _;
|
|
126
|
+
},
|
|
127
|
+
get width() {
|
|
128
|
+
return width;
|
|
129
|
+
},
|
|
130
|
+
set width(_: number) {
|
|
131
|
+
width = _;
|
|
132
|
+
},
|
|
133
|
+
get minWidthPixels() {
|
|
134
|
+
return minWidthPixels;
|
|
135
|
+
},
|
|
136
|
+
set minWidthPixels(_: number | undefined) {
|
|
137
|
+
minWidthPixels = _;
|
|
138
|
+
},
|
|
139
|
+
get maxWidthPixels() {
|
|
140
|
+
return maxWidthPixels;
|
|
141
|
+
},
|
|
142
|
+
set maxWidthPixels(_: number | undefined) {
|
|
143
|
+
maxWidthPixels = _;
|
|
144
|
+
},
|
|
145
|
+
} satisfies LineLayer;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const createPrograms = (
|
|
149
|
+
gl: WebGL2RenderingContext,
|
|
150
|
+
{
|
|
151
|
+
positionBuffer,
|
|
152
|
+
indexBuffer,
|
|
153
|
+
cornerBuffer,
|
|
154
|
+
}: {
|
|
155
|
+
positionBuffer: Buffer;
|
|
156
|
+
indexBuffer: Buffer;
|
|
157
|
+
cornerBuffer: Buffer;
|
|
158
|
+
},
|
|
159
|
+
) => {
|
|
160
|
+
const createRenderProgram = (depth = false) => {
|
|
161
|
+
const program = createProgram({
|
|
162
|
+
gl,
|
|
163
|
+
vertexSource,
|
|
164
|
+
fragmentSource: depth ? depthSource : fragmentSource,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const FLOAT_BYTES = Float32Array.BYTES_PER_ELEMENT;
|
|
168
|
+
|
|
169
|
+
const previousAttribute = program.attribute3f("previous", positionBuffer, {
|
|
170
|
+
stride: 3 * FLOAT_BYTES,
|
|
171
|
+
});
|
|
172
|
+
const currentAttribute = program.attribute3f("current", positionBuffer, {
|
|
173
|
+
stride: 3 * FLOAT_BYTES,
|
|
174
|
+
offset: FLOAT_BYTES * 3 * 4,
|
|
175
|
+
});
|
|
176
|
+
const nextAttribute = program.attribute3f("next", positionBuffer, {
|
|
177
|
+
stride: 3 * FLOAT_BYTES,
|
|
178
|
+
offset: FLOAT_BYTES * 3 * 4 * 2,
|
|
179
|
+
});
|
|
180
|
+
const cornerAttribute = program.attribute2f("corner", cornerBuffer, {
|
|
181
|
+
stride: FLOAT_BYTES * 2,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const projectionUniform = program.uniformMatrix4f("projection");
|
|
185
|
+
const modelViewUniform = program.uniformMatrix4f("model_view");
|
|
186
|
+
const cameraUniform = program.uniform3i("camera");
|
|
187
|
+
const centerUniform = program.uniform3i("center");
|
|
188
|
+
const screenUniform = program.uniform2f("screen");
|
|
189
|
+
const colorUniform = program.uniform4f("color");
|
|
190
|
+
const widthUniform = program.uniform1f("width");
|
|
191
|
+
const maxWidthPixelsUniform = program.uniform1f("max_width_pixels");
|
|
192
|
+
const minWidthPixelsUniform = program.uniform1f("min_width_pixels");
|
|
193
|
+
const indexUniform = program.uniform1i("index");
|
|
194
|
+
|
|
195
|
+
const execute = ({
|
|
196
|
+
projection,
|
|
197
|
+
modelView,
|
|
198
|
+
camera,
|
|
199
|
+
center,
|
|
200
|
+
screen,
|
|
201
|
+
count,
|
|
202
|
+
color,
|
|
203
|
+
width,
|
|
204
|
+
minWidthPixels,
|
|
205
|
+
maxWidthPixels,
|
|
206
|
+
index,
|
|
207
|
+
}: {
|
|
208
|
+
projection: mat4;
|
|
209
|
+
modelView: mat4;
|
|
210
|
+
camera: vec3;
|
|
211
|
+
center: vec3;
|
|
212
|
+
screen: vec2;
|
|
213
|
+
count: number;
|
|
214
|
+
color: vec4;
|
|
215
|
+
width: number;
|
|
216
|
+
minWidthPixels: number;
|
|
217
|
+
maxWidthPixels: number;
|
|
218
|
+
index: number;
|
|
219
|
+
}) => {
|
|
220
|
+
if (count === 0) return;
|
|
221
|
+
|
|
222
|
+
gl.enable(gl.DEPTH_TEST);
|
|
223
|
+
if (depth) gl.disable(gl.BLEND);
|
|
224
|
+
else {
|
|
225
|
+
gl.enable(gl.BLEND);
|
|
226
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
program.use();
|
|
230
|
+
|
|
231
|
+
previousAttribute.use();
|
|
232
|
+
currentAttribute.use();
|
|
233
|
+
nextAttribute.use();
|
|
234
|
+
cornerAttribute.use();
|
|
235
|
+
|
|
236
|
+
projectionUniform.set(projection);
|
|
237
|
+
modelViewUniform.set(modelView);
|
|
238
|
+
cameraUniform.set(camera);
|
|
239
|
+
centerUniform.set(center);
|
|
240
|
+
screenUniform.set(screen);
|
|
241
|
+
colorUniform.set(color);
|
|
242
|
+
widthUniform.set(width);
|
|
243
|
+
minWidthPixelsUniform.set(minWidthPixels);
|
|
244
|
+
maxWidthPixelsUniform.set(maxWidthPixels);
|
|
245
|
+
indexUniform.set(index);
|
|
246
|
+
|
|
247
|
+
indexBuffer.use();
|
|
248
|
+
|
|
249
|
+
gl.drawElements(gl.TRIANGLES, count * 3 * 4 - 4, gl.UNSIGNED_SHORT, 0);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const destroy = () => program.destroy();
|
|
253
|
+
|
|
254
|
+
return { execute, destroy };
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const renderProgram = createRenderProgram();
|
|
258
|
+
const depthProgram = createRenderProgram(true);
|
|
259
|
+
|
|
260
|
+
return { renderProgram, depthProgram };
|
|
261
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#version 300 es
|
|
2
|
+
|
|
3
|
+
precision highp float;
|
|
4
|
+
|
|
5
|
+
uniform mat4 projection;
|
|
6
|
+
uniform mat4 model_view;
|
|
7
|
+
uniform ivec3 camera;
|
|
8
|
+
uniform ivec3 center;
|
|
9
|
+
uniform vec2 screen;
|
|
10
|
+
uniform vec4 color;
|
|
11
|
+
uniform float width;
|
|
12
|
+
uniform float min_width_pixels;
|
|
13
|
+
uniform float max_width_pixels;
|
|
14
|
+
|
|
15
|
+
in vec3 previous;
|
|
16
|
+
in vec3 current;
|
|
17
|
+
in vec3 next;
|
|
18
|
+
in vec2 corner;
|
|
19
|
+
out vec4 color_out;
|
|
20
|
+
|
|
21
|
+
const int ONE = 1073741824; // 2^30
|
|
22
|
+
const float INV_ONE = 1.f / float(ONE);
|
|
23
|
+
|
|
24
|
+
vec4 transform(vec3 v) {
|
|
25
|
+
return projection * model_view * vec4(vec3(ivec3(v * float(ONE)) + center - camera) * INV_ONE, 1.f);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void main(void) {
|
|
29
|
+
vec4 projected_previous = transform(previous);
|
|
30
|
+
vec4 projected_current = transform(current);
|
|
31
|
+
vec4 projected_next = transform(next);
|
|
32
|
+
|
|
33
|
+
vec2 screen_previous = projected_previous.xy / projected_previous.w * screen;
|
|
34
|
+
vec2 screen_current = projected_current.xy / projected_current.w * screen;
|
|
35
|
+
vec2 screen_next = projected_next.xy / projected_next.w * screen;
|
|
36
|
+
|
|
37
|
+
vec2 a = normalize(screen_current - screen_previous);
|
|
38
|
+
vec2 b = normalize(screen_next - screen_current);
|
|
39
|
+
if(screen_current == screen_previous)
|
|
40
|
+
a = b;
|
|
41
|
+
if(screen_next == screen_current)
|
|
42
|
+
b = a;
|
|
43
|
+
vec2 direction = normalize(a + b);
|
|
44
|
+
vec2 point = normalize(a - b);
|
|
45
|
+
vec2 normal = vec2(-direction.y, direction.x);
|
|
46
|
+
vec2 offset;
|
|
47
|
+
|
|
48
|
+
if(sign(corner.y * dot(normal, point)) > 0.0f) {
|
|
49
|
+
vec2 ap = vec2(-a.y, a.x);
|
|
50
|
+
vec2 bp = vec2(-b.y, b.x);
|
|
51
|
+
offset = 0.5f * corner.y * (corner.x * (bp - ap) + ap + bp);
|
|
52
|
+
} else {
|
|
53
|
+
float distance = clamp(1.f / dot(direction, a), 0.f, 10.f);
|
|
54
|
+
offset = normal * distance * corner.y;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
float pixel_size = projected_current.w / screen.y;
|
|
58
|
+
float scale = clamp(width * -projection[1][1], min_width_pixels * pixel_size, max_width_pixels * pixel_size);
|
|
59
|
+
|
|
60
|
+
gl_Position = projected_current + vec4(scale * offset, 0.f, 0.f);
|
|
61
|
+
|
|
62
|
+
color_out = color;
|
|
63
|
+
}
|