phoria2 1.3.0 → 1.4.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/README.md +9 -6
- package/dist/bloom.js +4 -4
- package/dist/bloom.js.map +1 -1
- package/dist/charts.js +436 -436
- package/dist/charts.js.map +1 -1
- package/dist/core.js +1223 -862
- package/dist/core.js.map +1 -1
- package/dist/debug.js +297 -0
- package/dist/debug.js.map +1 -0
- package/dist/edit.js +40 -40
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +32 -13
- package/dist/explain.js.map +1 -1
- package/dist/gltf.js +128 -128
- package/dist/gltf.js.map +1 -1
- package/dist/keyboard.js +19 -19
- package/dist/keyboard.js.map +1 -1
- package/dist/motion.js +35 -35
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +36 -36
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +116 -104
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +88 -71
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/types/View.d.ts +16 -0
- package/dist/types/debug.d.ts +74 -0
- package/dist/types/explain.d.ts +4 -0
- package/dist/types/frame.d.ts +5 -0
- package/dist/types/material/Material.d.ts +52 -0
- package/dist/types/watch.d.ts +8 -1
- package/dist/types/webgl/hooks.d.ts +48 -0
- package/dist/types/webgl/passes.d.ts +4 -0
- package/dist/types/webgl/programs.d.ts +15 -5
- package/dist/types/webgl/shaders.d.ts +36 -5
- package/llms-full.txt +227 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/schema/scene.schema.json +27 -0
- package/src/View.js +105 -7
- package/src/debug.js +283 -0
- package/src/explain.js +19 -0
- package/src/frame.js +7 -2
- package/src/material/Material.js +121 -2
- package/src/scene/drawables.js +21 -4
- package/src/watch.js +18 -2
- package/src/webgl/WebGLRenderer.js +19 -2
- package/src/webgl/hooks.js +121 -0
- package/src/webgl/passes.js +17 -2
- package/src/webgl/picking.js +23 -2
- package/src/webgl/programs.js +20 -9
- package/src/webgl/resources.js +1 -1
- package/src/webgl/shaders.js +443 -35
package/src/webgl/shaders.js
CHANGED
|
@@ -116,9 +116,17 @@ export const CLIP_ONLY_FRAGMENT = /* glsl */ `
|
|
|
116
116
|
precision highp float;
|
|
117
117
|
in vec3 vWorldPosition;
|
|
118
118
|
out vec4 fragColor;
|
|
119
|
+
#if defined(ALPHA_TEST) || defined(HOOK_FRAGMENT)
|
|
120
|
+
in vec2 vUv;
|
|
121
|
+
#endif
|
|
122
|
+
#ifdef HOOK_FRAGMENT
|
|
123
|
+
// a fragment hook's discards cut the shadow too - a dissolving thing casts what is left of it
|
|
124
|
+
in vec3 vNormal;
|
|
125
|
+
in vec3 vColor;
|
|
126
|
+
//@hook uniforms
|
|
127
|
+
#endif
|
|
119
128
|
#ifdef ALPHA_TEST
|
|
120
129
|
// a cut-out casts the shadow of what is left of it - leaves, fences, grilles - not of its rectangle
|
|
121
|
-
in vec2 vUv;
|
|
122
130
|
uniform float uOpacity;
|
|
123
131
|
uniform float uAlphaTest;
|
|
124
132
|
#ifdef MAP
|
|
@@ -129,6 +137,15 @@ ${SAMPLING}
|
|
|
129
137
|
${CLIP}
|
|
130
138
|
void main() {
|
|
131
139
|
clipAway(vWorldPosition);
|
|
140
|
+
#ifdef HOOK_FRAGMENT
|
|
141
|
+
{
|
|
142
|
+
vec4 base = vec4(vColor, 1.0);
|
|
143
|
+
vec3 emissive = vec3(0.0);
|
|
144
|
+
vec3 normal = normalize(vNormal);
|
|
145
|
+
//@hook fragment
|
|
146
|
+
if (base.a <= 0.0) discard;
|
|
147
|
+
}
|
|
148
|
+
#endif
|
|
132
149
|
#ifdef ALPHA_TEST
|
|
133
150
|
float alpha = uOpacity;
|
|
134
151
|
#ifdef MAP
|
|
@@ -225,6 +242,7 @@ uniform int uHighlightIndex; // -2 none, -1 the whole node, otherwise the instan
|
|
|
225
242
|
uniform mat4 uView;
|
|
226
243
|
out float vFogDepth;
|
|
227
244
|
#endif
|
|
245
|
+
//@hook uniforms
|
|
228
246
|
|
|
229
247
|
out vec3 vWorldPosition;
|
|
230
248
|
out vec3 vNormal;
|
|
@@ -248,25 +266,34 @@ ${COMMON}
|
|
|
248
266
|
void main() {
|
|
249
267
|
vColor = vec3(1.0);
|
|
250
268
|
vHighlight = uHighlightIndex == -1 ? 1.0 : 0.0;
|
|
269
|
+
// the vertex in the geometry's own space - where a shader hook moves it
|
|
270
|
+
vec3 local = position;
|
|
271
|
+
vec3 n = normal;
|
|
272
|
+
#ifdef HOOK_VERTEX
|
|
273
|
+
{
|
|
274
|
+
int index = gl_InstanceID;
|
|
275
|
+
//@hook vertex
|
|
276
|
+
}
|
|
277
|
+
#endif
|
|
251
278
|
#ifdef INSTANCED
|
|
252
|
-
vec4
|
|
279
|
+
vec4 placed = instanceMatrix * vec4(local, 1.0);
|
|
253
280
|
// the instance's normal matrix as its cofactor: the inverse transpose up to scale, from three
|
|
254
281
|
// cross products, flipped back for mirroring transforms
|
|
255
282
|
mat3 m = mat3(instanceMatrix);
|
|
256
283
|
mat3 instanceNormal = mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1])) * sign(dot(m[0], cross(m[1], m[2])));
|
|
257
|
-
vec3
|
|
284
|
+
vec3 placedNormal = instanceNormal * n;
|
|
258
285
|
vColor *= instanceColor;
|
|
259
286
|
if (gl_InstanceID == uHighlightIndex) vHighlight = 1.0;
|
|
260
287
|
#else
|
|
261
|
-
vec4
|
|
262
|
-
vec3
|
|
288
|
+
vec4 placed = vec4(local, 1.0);
|
|
289
|
+
vec3 placedNormal = n;
|
|
263
290
|
#endif
|
|
264
291
|
#ifdef VERTEX_COLORS
|
|
265
292
|
vColor *= srgbToLinear(color);
|
|
266
293
|
#endif
|
|
267
|
-
vec4 world = uModel *
|
|
294
|
+
vec4 world = uModel * placed;
|
|
268
295
|
vWorldPosition = world.xyz;
|
|
269
|
-
vNormal = uNormalMatrix *
|
|
296
|
+
vNormal = uNormalMatrix * placedNormal;
|
|
270
297
|
vUv = uv;
|
|
271
298
|
#ifdef UV2
|
|
272
299
|
vUv2 = uv2;
|
|
@@ -280,8 +307,8 @@ void main() {
|
|
|
280
307
|
#endif
|
|
281
308
|
#endif
|
|
282
309
|
#ifdef TRIPLANAR
|
|
283
|
-
vObjectPosition =
|
|
284
|
-
vObjectNormal =
|
|
310
|
+
vObjectPosition = local;
|
|
311
|
+
vObjectNormal = n;
|
|
285
312
|
#ifdef INSTANCED
|
|
286
313
|
vObjectToWorld = uNormalMatrix * instanceNormal;
|
|
287
314
|
#else
|
|
@@ -322,6 +349,7 @@ uniform float uAlphaTest;
|
|
|
322
349
|
#ifdef EMISSIVE_MAP
|
|
323
350
|
uniform sampler2D uEmissiveMap;
|
|
324
351
|
#endif
|
|
352
|
+
//@hook uniforms
|
|
325
353
|
|
|
326
354
|
${SAMPLING}
|
|
327
355
|
#ifdef LIT
|
|
@@ -738,6 +766,25 @@ void main() {
|
|
|
738
766
|
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
739
767
|
#ifdef MAP
|
|
740
768
|
base *= uMapTexel; // sRGB textures are decoded to linear by the hardware
|
|
769
|
+
#endif
|
|
770
|
+
vec3 emissive = uEmissive;
|
|
771
|
+
#ifdef EMISSIVE_MAP
|
|
772
|
+
emissive *= uEmissiveMapTexel.rgb;
|
|
773
|
+
#endif
|
|
774
|
+
#ifdef HOOK_FRAGMENT
|
|
775
|
+
// the shading normal in world space, facing the viewer - a fragment hook can bend it, for relief
|
|
776
|
+
// lit per pixel (bumps from noise) whatever the mesh's resolution
|
|
777
|
+
#ifdef FLAT_SHADING
|
|
778
|
+
vec3 normal = normalize(cross(dFdx(vWorldPosition), dFdy(vWorldPosition)));
|
|
779
|
+
#else
|
|
780
|
+
vec3 normal = normalize(vNormal);
|
|
781
|
+
#ifdef DOUBLE_SIDED
|
|
782
|
+
if (!gl_FrontFacing) normal = -normal;
|
|
783
|
+
#endif
|
|
784
|
+
#endif
|
|
785
|
+
{
|
|
786
|
+
//@hook fragment
|
|
787
|
+
}
|
|
741
788
|
#endif
|
|
742
789
|
#ifdef ALPHA_TEST
|
|
743
790
|
if (base.a < uAlphaTest) discard;
|
|
@@ -745,19 +792,18 @@ void main() {
|
|
|
745
792
|
#endif
|
|
746
793
|
// how much of what is behind it this hides: its opacity - less for glass, which lets light through
|
|
747
794
|
float coverage = base.a;
|
|
748
|
-
vec3 emissive = uEmissive;
|
|
749
|
-
#ifdef EMISSIVE_MAP
|
|
750
|
-
emissive *= uEmissiveMapTexel.rgb;
|
|
751
|
-
#endif
|
|
752
795
|
|
|
753
796
|
#ifdef LIT
|
|
754
|
-
#ifdef
|
|
797
|
+
#ifdef HOOK_FRAGMENT
|
|
798
|
+
// the fragment hook's normal, facing the viewer already
|
|
799
|
+
vec3 n = normalize(normal);
|
|
800
|
+
#elif defined(FLAT_SHADING)
|
|
755
801
|
// from the screen-space slope: it faces the camera whichever side of the face is seen
|
|
756
802
|
vec3 n = normalize(cross(dFdx(vWorldPosition), dFdy(vWorldPosition)));
|
|
757
803
|
#else
|
|
758
804
|
vec3 n = normalize(vNormal);
|
|
759
805
|
#endif
|
|
760
|
-
#if defined(DOUBLE_SIDED) && !defined(FLAT_SHADING)
|
|
806
|
+
#if defined(DOUBLE_SIDED) && !defined(FLAT_SHADING) && !defined(HOOK_FRAGMENT)
|
|
761
807
|
if (!gl_FrontFacing) n = -n;
|
|
762
808
|
#endif
|
|
763
809
|
vec3 surfaceNormal = n;
|
|
@@ -948,6 +994,14 @@ void main() {
|
|
|
948
994
|
#endif
|
|
949
995
|
#else
|
|
950
996
|
vec3 color = base.rgb + emissive;
|
|
997
|
+
#endif
|
|
998
|
+
#ifdef HOOK_LIT
|
|
999
|
+
{
|
|
1000
|
+
// a fade here fades what it hides too
|
|
1001
|
+
float before = max(base.a, 1e-5);
|
|
1002
|
+
//@hook lit
|
|
1003
|
+
coverage *= clamp(base.a / before, 0.0, 1.0);
|
|
1004
|
+
}
|
|
951
1005
|
#endif
|
|
952
1006
|
color = mix(color, uHighlight.rgb, vHighlight * uHighlight.a);
|
|
953
1007
|
// a highlight shows on glass as on anything else
|
|
@@ -981,9 +1035,13 @@ uniform mat4 uProjection;
|
|
|
981
1035
|
uniform float uSize; // pixels (with the pixel ratio applied), or world units when attenuated
|
|
982
1036
|
uniform float uScale; // projection focal term x half the buffer height: world size -> pixels at depth 1
|
|
983
1037
|
uniform int uHighlightIndex; // -2 none, -1 every point, otherwise the point
|
|
1038
|
+
//@hook uniforms
|
|
984
1039
|
|
|
985
1040
|
out vec3 vColor;
|
|
986
1041
|
flat out float vHighlight;
|
|
1042
|
+
#ifdef HOOK_FRAGMENT
|
|
1043
|
+
flat out float vPointIndex;
|
|
1044
|
+
#endif
|
|
987
1045
|
#ifdef PICK
|
|
988
1046
|
flat out uint vPickElement; // which instance, point or segment, for GPU picking
|
|
989
1047
|
#endif
|
|
@@ -994,29 +1052,46 @@ out float vFogDepth;
|
|
|
994
1052
|
${COMMON}
|
|
995
1053
|
|
|
996
1054
|
void main() {
|
|
997
|
-
|
|
1055
|
+
// the point in the geometry's own space, and its size as a multiple of the material's - where a
|
|
1056
|
+
// shader hook changes them
|
|
1057
|
+
vec3 local = position;
|
|
1058
|
+
#ifdef SPRITES
|
|
1059
|
+
int self = int(pointIndex + 0.5);
|
|
1060
|
+
float size = pointSize;
|
|
1061
|
+
#else
|
|
1062
|
+
int self = gl_VertexID;
|
|
1063
|
+
float size = 1.0;
|
|
1064
|
+
#endif
|
|
1065
|
+
#ifdef HOOK_VERTEX
|
|
1066
|
+
{
|
|
1067
|
+
int index = self;
|
|
1068
|
+
//@hook vertex
|
|
1069
|
+
}
|
|
1070
|
+
#endif
|
|
1071
|
+
#ifdef HOOK_FRAGMENT
|
|
1072
|
+
vPointIndex = float(self);
|
|
1073
|
+
#endif
|
|
1074
|
+
vec4 viewPosition = uView * (uModel * vec4(local, 1.0));
|
|
998
1075
|
#ifdef SPRITES
|
|
999
1076
|
// the square's corner, turned by the point's angle: the texture turns with it
|
|
1000
1077
|
vec2 c = vec2(corner.x * 2.0 - 1.0, corner.y);
|
|
1001
1078
|
float s = sin(pointAngle), k = cos(pointAngle);
|
|
1002
1079
|
vec2 turned = vec2(c.x * k - c.y * s, c.x * s + c.y * k);
|
|
1003
|
-
float
|
|
1080
|
+
float across = uSize * size;
|
|
1004
1081
|
#ifdef SIZE_ATTENUATION
|
|
1005
1082
|
// world units: the square sits in the view, facing the camera
|
|
1006
|
-
viewPosition.xy += turned *
|
|
1083
|
+
viewPosition.xy += turned * across * 0.5;
|
|
1007
1084
|
gl_Position = uProjection * viewPosition;
|
|
1008
1085
|
#else
|
|
1009
1086
|
// pixels: the square is laid out on the screen
|
|
1010
1087
|
gl_Position = uProjection * viewPosition;
|
|
1011
|
-
gl_Position.xy += turned *
|
|
1088
|
+
gl_Position.xy += turned * across / uResolution * gl_Position.w;
|
|
1012
1089
|
#endif
|
|
1013
1090
|
// the image upright on the square, its top at the top, as a point's is
|
|
1014
1091
|
vUv = vec2(c.x * 0.5 + 0.5, 0.5 - c.y * 0.5);
|
|
1015
1092
|
vOpacity = pointOpacity;
|
|
1016
|
-
int self = int(pointIndex + 0.5);
|
|
1017
1093
|
#else
|
|
1018
1094
|
gl_Position = uProjection * viewPosition;
|
|
1019
|
-
int self = gl_VertexID;
|
|
1020
1095
|
#endif
|
|
1021
1096
|
#ifdef FOG
|
|
1022
1097
|
vFogDepth = -viewPosition.z;
|
|
@@ -1025,9 +1100,9 @@ void main() {
|
|
|
1025
1100
|
#elif defined(SIZE_ATTENUATION)
|
|
1026
1101
|
// perspective shrinks with distance; an orthographic projection (w row 0, 0, 0, 1) doesn't
|
|
1027
1102
|
float depth = uProjection[3][3] == 1.0 ? 1.0 : max(-viewPosition.z, 1e-4);
|
|
1028
|
-
gl_PointSize = uSize * uScale / depth;
|
|
1103
|
+
gl_PointSize = uSize * size * uScale / depth;
|
|
1029
1104
|
#else
|
|
1030
|
-
gl_PointSize = uSize;
|
|
1105
|
+
gl_PointSize = uSize * size;
|
|
1031
1106
|
#endif
|
|
1032
1107
|
vColor = vec3(1.0);
|
|
1033
1108
|
#ifdef VERTEX_COLORS
|
|
@@ -1054,6 +1129,10 @@ uniform float uOpacity;
|
|
|
1054
1129
|
#ifdef MAP
|
|
1055
1130
|
uniform sampler2D uMap;
|
|
1056
1131
|
#endif
|
|
1132
|
+
#ifdef HOOK_FRAGMENT
|
|
1133
|
+
flat in float vPointIndex;
|
|
1134
|
+
#endif
|
|
1135
|
+
//@hook uniforms
|
|
1057
1136
|
out vec4 fragColor;
|
|
1058
1137
|
|
|
1059
1138
|
${COMMON}
|
|
@@ -1075,10 +1154,22 @@ void main() {
|
|
|
1075
1154
|
float r = dot(p, p);
|
|
1076
1155
|
float edge = fwidth(r);
|
|
1077
1156
|
base.a *= 1.0 - smoothstep(1.0 - edge, 1.0, r);
|
|
1157
|
+
#endif
|
|
1158
|
+
#ifdef HOOK_FRAGMENT
|
|
1159
|
+
{
|
|
1160
|
+
vec2 pointUv = at;
|
|
1161
|
+
int index = int(vPointIndex + 0.5);
|
|
1162
|
+
//@hook fragment
|
|
1163
|
+
}
|
|
1078
1164
|
#endif
|
|
1079
1165
|
if (base.a <= 0.0) discard;
|
|
1080
|
-
|
|
1081
|
-
|
|
1166
|
+
vec3 color = fogged(mix(base.rgb, uHighlight.rgb, vHighlight * uHighlight.a));
|
|
1167
|
+
#ifdef HOOK_LIT
|
|
1168
|
+
{
|
|
1169
|
+
//@hook lit
|
|
1170
|
+
}
|
|
1171
|
+
#endif
|
|
1172
|
+
fragColor = vec4(linearToSrgb(color) * base.a, base.a);
|
|
1082
1173
|
}
|
|
1083
1174
|
`
|
|
1084
1175
|
|
|
@@ -1099,14 +1190,20 @@ uniform float uWidth; // pixels, already scaled by the pixel ratio
|
|
|
1099
1190
|
uniform float uNear;
|
|
1100
1191
|
uniform int uArrows; // arrowheads: 1 at the start of each chain, 2 at its end, 3 at both
|
|
1101
1192
|
uniform float uArrowSize; // their length in pixels, scaled by the pixel ratio
|
|
1193
|
+
//@hook uniforms
|
|
1102
1194
|
|
|
1103
1195
|
// six vertices an instance make the segment's quad, as two triangles - x: 0 at its start, 1 at its
|
|
1104
1196
|
// end; y: -1 or 1 across - and six more its two arrowheads, drawn only when asked for
|
|
1105
1197
|
const vec2 QUAD[6] = vec2[6](vec2(0.0, -1.0), vec2(0.0, 1.0), vec2(1.0, -1.0), vec2(1.0, -1.0), vec2(0.0, 1.0), vec2(1.0, 1.0));
|
|
1106
1198
|
|
|
1107
1199
|
out vec3 vColor;
|
|
1108
|
-
#
|
|
1200
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1109
1201
|
out float vDistance; // along the chain, in the model's units
|
|
1202
|
+
#endif
|
|
1203
|
+
#ifdef HOOK_FRAGMENT
|
|
1204
|
+
flat out float vSegment; // which segment, for a fragment hook's index
|
|
1205
|
+
#endif
|
|
1206
|
+
#ifdef DASHED
|
|
1110
1207
|
flat out float vArrow; // 1 on an arrowhead, which is never dashed
|
|
1111
1208
|
#endif
|
|
1112
1209
|
#ifdef PICK
|
|
@@ -1118,9 +1215,22 @@ out float vFogDepth;
|
|
|
1118
1215
|
|
|
1119
1216
|
${COMMON}
|
|
1120
1217
|
|
|
1218
|
+
#ifdef HOOK_VERTEX
|
|
1219
|
+
// a shader hook moves each end of each segment: local is the end, along 0 at the start and 1 at the end
|
|
1220
|
+
vec3 hookVertex(vec3 local, float along, int index) {
|
|
1221
|
+
//@hook vertex
|
|
1222
|
+
return local;
|
|
1223
|
+
}
|
|
1224
|
+
#endif
|
|
1225
|
+
|
|
1121
1226
|
void main() {
|
|
1122
|
-
|
|
1123
|
-
|
|
1227
|
+
vec3 start = segmentStart, end = segmentEnd;
|
|
1228
|
+
#ifdef HOOK_VERTEX
|
|
1229
|
+
start = hookVertex(segmentStart, 0.0, gl_InstanceID);
|
|
1230
|
+
end = hookVertex(segmentEnd, 1.0, gl_InstanceID);
|
|
1231
|
+
#endif
|
|
1232
|
+
vec4 a = uView * (uModel * vec4(start, 1.0));
|
|
1233
|
+
vec4 b = uView * (uModel * vec4(end, 1.0));
|
|
1124
1234
|
// clip the segment to the near plane so lines passing behind the camera stay correct
|
|
1125
1235
|
float clipZ = -uNear;
|
|
1126
1236
|
if (a.z > clipZ && b.z > clipZ) {
|
|
@@ -1170,8 +1280,13 @@ void main() {
|
|
|
1170
1280
|
#endif
|
|
1171
1281
|
clip.xy += offset / (uResolution * 0.5) * clip.w;
|
|
1172
1282
|
gl_Position = clip;
|
|
1283
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1284
|
+
vDistance = segmentInfo.x + length(end - start) * along;
|
|
1285
|
+
#endif
|
|
1286
|
+
#ifdef HOOK_FRAGMENT
|
|
1287
|
+
vSegment = float(gl_InstanceID);
|
|
1288
|
+
#endif
|
|
1173
1289
|
#ifdef DASHED
|
|
1174
|
-
vDistance = segmentInfo.x + length(segmentEnd - segmentStart) * along;
|
|
1175
1290
|
vArrow = arrow;
|
|
1176
1291
|
#endif
|
|
1177
1292
|
#ifdef PICK
|
|
@@ -1190,8 +1305,14 @@ precision highp float;
|
|
|
1190
1305
|
in vec3 vColor;
|
|
1191
1306
|
uniform vec3 uColor;
|
|
1192
1307
|
uniform float uOpacity;
|
|
1193
|
-
#
|
|
1308
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1194
1309
|
in float vDistance;
|
|
1310
|
+
#endif
|
|
1311
|
+
#ifdef HOOK_FRAGMENT
|
|
1312
|
+
flat in float vSegment;
|
|
1313
|
+
#endif
|
|
1314
|
+
//@hook uniforms
|
|
1315
|
+
#ifdef DASHED
|
|
1195
1316
|
flat in float vArrow;
|
|
1196
1317
|
uniform float uDash; // dash and gap lengths, in the model's units
|
|
1197
1318
|
uniform float uGap;
|
|
@@ -1206,8 +1327,22 @@ void main() {
|
|
|
1206
1327
|
#ifdef DASHED
|
|
1207
1328
|
if (vArrow < 0.5 && mod(vDistance - uDashOffset, uDash + uGap) >= uDash) discard;
|
|
1208
1329
|
#endif
|
|
1209
|
-
|
|
1210
|
-
|
|
1330
|
+
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
1331
|
+
#ifdef HOOK_FRAGMENT
|
|
1332
|
+
{
|
|
1333
|
+
float distance = vDistance;
|
|
1334
|
+
int index = int(vSegment + 0.5);
|
|
1335
|
+
//@hook fragment
|
|
1336
|
+
}
|
|
1337
|
+
if (base.a <= 0.0) discard;
|
|
1338
|
+
#endif
|
|
1339
|
+
vec3 color = fogged(base.rgb);
|
|
1340
|
+
#ifdef HOOK_LIT
|
|
1341
|
+
{
|
|
1342
|
+
//@hook lit
|
|
1343
|
+
}
|
|
1344
|
+
#endif
|
|
1345
|
+
fragColor = vec4(linearToSrgb(color) * base.a, base.a);
|
|
1211
1346
|
}
|
|
1212
1347
|
`
|
|
1213
1348
|
|
|
@@ -1220,13 +1355,286 @@ const SOURCES = {
|
|
|
1220
1355
|
}
|
|
1221
1356
|
|
|
1222
1357
|
/**
|
|
1223
|
-
*
|
|
1358
|
+
* GLSL of a material's own, spliced in at the shaders' `//@hook` lines: uniform declarations, and
|
|
1359
|
+
* code run on each vertex, before lighting and after it.
|
|
1360
|
+
* @typedef {object} HookSource
|
|
1361
|
+
* @property {string} uniforms declarations
|
|
1362
|
+
* @property {string} [helpers] functions every hook can call - hash, noise, fbm
|
|
1363
|
+
* @property {string} vertex
|
|
1364
|
+
* @property {string} fragment
|
|
1365
|
+
* @property {string} lit
|
|
1366
|
+
*/
|
|
1367
|
+
|
|
1368
|
+
// Functions every shader hook can call - hooks are statements, so they can't define their own. All of
|
|
1369
|
+
// it is built on an integer bit-mix (lowbias32), which GLSL ES 3.00 defines exactly, so lattice
|
|
1370
|
+
// values and gradients are the same on every GPU - fract(sin(x) * 43758.5453) is not. Everything 0-1:
|
|
1371
|
+
// - hash(x): a float or vector's exact bits, mixed
|
|
1372
|
+
// - vnoise(p): value noise - random values at whole-number points, eased between (quintic); cheap,
|
|
1373
|
+
// blocky-looking at low frequencies
|
|
1374
|
+
// - snoise(p): simplex noise - gradients on a simplex grid; smoother, less grid-bound, the better
|
|
1375
|
+
// default; snoised(p) also gives its gradient, for normals
|
|
1376
|
+
// - vfbm / sfbm (p[, octaves]): octaves of either added up, each turned and scaled by 2.03 so their
|
|
1377
|
+
// grids never line up; sfbmd adds the gradient. Five octaves unless told; 1-12
|
|
1378
|
+
// - voronoi(p): cellular (Worley) noise - a random point in every unit cell; returns the distance to
|
|
1379
|
+
// the nearest (F1) and the second nearest (F2), in cells, and a random 0-1 for the nearest's cell
|
|
1380
|
+
// And the everyday ones people (and agents) reach for: srgb() to turn a colour as picked into the
|
|
1381
|
+
// linear one hooks work in, hsv2rgb() and palette() (Quilez's cosine palette) - both giving linear
|
|
1382
|
+
// colour - fresnel() for rim light, rotate2() / rotate3() matrices, remap() and saturate().
|
|
1383
|
+
// Every int highp: a fragment shader's ints are mediump unless they say so.
|
|
1384
|
+
export const HOOK_HELPERS = /* glsl */ `
|
|
1385
|
+
highp uint phoriaMix(highp uint h) {
|
|
1386
|
+
h ^= h >> 16u; h *= 0x7feb352du; h ^= h >> 15u; h *= 0x846ca68bu; h ^= h >> 16u;
|
|
1387
|
+
return h;
|
|
1388
|
+
}
|
|
1389
|
+
float phoriaUnit(highp uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }
|
|
1390
|
+
float hash(float x) { return phoriaUnit(phoriaMix(floatBitsToUint(x))); }
|
|
1391
|
+
float hash(vec2 p) { highp uvec2 u = floatBitsToUint(p); return phoriaUnit(phoriaMix(u.x ^ phoriaMix(u.y + 0x9e3779b9u))); }
|
|
1392
|
+
float hash(vec3 p) { highp uvec3 u = floatBitsToUint(p); return phoriaUnit(phoriaMix(u.x ^ phoriaMix(u.y ^ phoriaMix(u.z + 0x9e3779b9u)))); }
|
|
1393
|
+
// a lattice point's bits: each coordinate times its own large odd number, mixed once
|
|
1394
|
+
highp uint phoriaCell(highp ivec3 c) {
|
|
1395
|
+
highp uvec3 u = uvec3(c) * uvec3(0x8da6b343u, 0xd8163841u, 0xcb1ab31fu);
|
|
1396
|
+
return phoriaMix(u.x ^ u.y ^ u.z);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
float vnoise(vec3 p) {
|
|
1400
|
+
vec3 i = floor(p), f = p - i;
|
|
1401
|
+
vec3 s = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
1402
|
+
highp ivec3 c = ivec3(i);
|
|
1403
|
+
float a = mix(phoriaUnit(phoriaCell(c)), phoriaUnit(phoriaCell(c + ivec3(1, 0, 0))), s.x);
|
|
1404
|
+
float b = mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 0))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 0))), s.x);
|
|
1405
|
+
float d = mix(phoriaUnit(phoriaCell(c + ivec3(0, 0, 1))), phoriaUnit(phoriaCell(c + ivec3(1, 0, 1))), s.x);
|
|
1406
|
+
float e = mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 1))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 1))), s.x);
|
|
1407
|
+
return mix(mix(a, b, s.y), mix(d, e, s.y), s.z);
|
|
1408
|
+
}
|
|
1409
|
+
float vnoise(vec2 p) {
|
|
1410
|
+
vec2 i = floor(p), f = p - i;
|
|
1411
|
+
vec2 s = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
1412
|
+
highp ivec3 c = ivec3(ivec2(i), 0);
|
|
1413
|
+
return mix(mix(phoriaUnit(phoriaCell(c)), phoriaUnit(phoriaCell(c + ivec3(1, 0, 0))), s.x),
|
|
1414
|
+
mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 0))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 0))), s.x), s.y);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// simplex: a corner's share of the value and the gradient - t^4 times the gradient's ramp, falling
|
|
1418
|
+
// to nothing at the corner's reach
|
|
1419
|
+
vec2 phoriaGrad2(highp ivec2 c) {
|
|
1420
|
+
float a = phoriaUnit(phoriaCell(ivec3(c, 7))) * 6.28318530718;
|
|
1421
|
+
return vec2(cos(a), sin(a));
|
|
1422
|
+
}
|
|
1423
|
+
void phoriaCorner2(vec2 x, highp ivec2 c, inout float n, inout vec2 d) {
|
|
1424
|
+
float t = 0.5 - dot(x, x);
|
|
1425
|
+
if (t <= 0.0) return;
|
|
1426
|
+
vec2 g = phoriaGrad2(c);
|
|
1427
|
+
float t2 = t * t, gx = dot(g, x);
|
|
1428
|
+
n += t2 * t2 * gx;
|
|
1429
|
+
d += t2 * t2 * g - 8.0 * t2 * t * gx * x;
|
|
1430
|
+
}
|
|
1431
|
+
vec3 snoised(vec2 p) {
|
|
1432
|
+
const float F = 0.366025403784, G = 0.211324865405;
|
|
1433
|
+
vec2 i = floor(p + (p.x + p.y) * F);
|
|
1434
|
+
vec2 x0 = p - i + (i.x + i.y) * G;
|
|
1435
|
+
vec2 o = x0.x > x0.y ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
|
1436
|
+
highp ivec2 c = ivec2(i);
|
|
1437
|
+
float n = 0.0;
|
|
1438
|
+
vec2 d = vec2(0.0);
|
|
1439
|
+
phoriaCorner2(x0, c, n, d);
|
|
1440
|
+
phoriaCorner2(x0 - o + G, c + ivec2(o), n, d);
|
|
1441
|
+
phoriaCorner2(x0 - 1.0 + 2.0 * G, c + 1, n, d);
|
|
1442
|
+
// unit gradients peak near 1/70: scaled to -1-1, then to 0-1
|
|
1443
|
+
return vec3(0.5 + 35.0 * n, 35.0 * d);
|
|
1444
|
+
}
|
|
1445
|
+
float snoise(vec2 p) { return snoised(p).x; }
|
|
1446
|
+
|
|
1447
|
+
const vec3 PHORIA_EDGES[12] = vec3[12](vec3(1, 1, 0), vec3(-1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0),
|
|
1448
|
+
vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1), vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, 1, -1), vec3(0, -1, -1));
|
|
1449
|
+
void phoriaCorner3(vec3 x, highp ivec3 c, inout float n, inout vec3 d) {
|
|
1450
|
+
float t = 0.6 - dot(x, x);
|
|
1451
|
+
if (t <= 0.0) return;
|
|
1452
|
+
vec3 g = PHORIA_EDGES[phoriaCell(c) % 12u];
|
|
1453
|
+
float t2 = t * t, gx = dot(g, x);
|
|
1454
|
+
n += t2 * t2 * gx;
|
|
1455
|
+
d += t2 * t2 * g - 8.0 * t2 * t * gx * x;
|
|
1456
|
+
}
|
|
1457
|
+
vec4 snoised(vec3 p) {
|
|
1458
|
+
const float F = 1.0 / 3.0, G = 1.0 / 6.0;
|
|
1459
|
+
vec3 i = floor(p + dot(p, vec3(F)));
|
|
1460
|
+
vec3 x0 = p - i + dot(i, vec3(G));
|
|
1461
|
+
vec3 g = step(x0.yzx, x0.xyz), l = 1.0 - g;
|
|
1462
|
+
vec3 i1 = min(g, l.zxy), i2 = max(g, l.zxy);
|
|
1463
|
+
highp ivec3 c = ivec3(i);
|
|
1464
|
+
float n = 0.0;
|
|
1465
|
+
vec3 d = vec3(0.0);
|
|
1466
|
+
phoriaCorner3(x0, c, n, d);
|
|
1467
|
+
phoriaCorner3(x0 - i1 + G, c + ivec3(i1), n, d);
|
|
1468
|
+
phoriaCorner3(x0 - i2 + 2.0 * G, c + ivec3(i2), n, d);
|
|
1469
|
+
phoriaCorner3(x0 - 1.0 + 3.0 * G, c + 1, n, d);
|
|
1470
|
+
// edge gradients peak near 1/32: scaled to -1-1, then to 0-1
|
|
1471
|
+
return vec4(0.5 + 16.0 * n, 16.0 * d);
|
|
1472
|
+
}
|
|
1473
|
+
float snoise(vec3 p) { return snoised(p).x; }
|
|
1474
|
+
|
|
1475
|
+
// fbm: each octave turned (so grids don't line up into a visible lattice), scaled by 2.03 and moved
|
|
1476
|
+
// on, its amplitude halved; the sum divided by the amplitudes, so 0-1 like the noise
|
|
1477
|
+
const mat2 PHORIA_TURN2 = mat2(0.8, 0.6, -0.6, 0.8);
|
|
1478
|
+
const mat3 PHORIA_TURN3 = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
|
|
1479
|
+
float vfbm(vec2 p, int octaves) {
|
|
1480
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1481
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * vnoise(p); sum += a; p = PHORIA_TURN2 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1482
|
+
return v / sum;
|
|
1483
|
+
}
|
|
1484
|
+
float vfbm(vec3 p, int octaves) {
|
|
1485
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1486
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * vnoise(p); sum += a; p = PHORIA_TURN3 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1487
|
+
return v / sum;
|
|
1488
|
+
}
|
|
1489
|
+
float sfbm(vec2 p, int octaves) {
|
|
1490
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1491
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * snoise(p); sum += a; p = PHORIA_TURN2 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1492
|
+
return v / sum;
|
|
1493
|
+
}
|
|
1494
|
+
float sfbm(vec3 p, int octaves) {
|
|
1495
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1496
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * snoise(p); sum += a; p = PHORIA_TURN3 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1497
|
+
return v / sum;
|
|
1498
|
+
}
|
|
1499
|
+
// with the gradient: each octave's, carried back through the turns and scalings so far
|
|
1500
|
+
vec3 sfbmd(vec2 p, int octaves) {
|
|
1501
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1502
|
+
vec2 d = vec2(0.0);
|
|
1503
|
+
mat2 m = mat2(1.0);
|
|
1504
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) {
|
|
1505
|
+
vec3 n = snoised(p);
|
|
1506
|
+
v += a * n.x;
|
|
1507
|
+
d += a * (transpose(m) * n.yz);
|
|
1508
|
+
sum += a;
|
|
1509
|
+
p = PHORIA_TURN2 * p * 2.03 + 17.0;
|
|
1510
|
+
m = PHORIA_TURN2 * m * 2.03;
|
|
1511
|
+
a *= 0.5;
|
|
1512
|
+
}
|
|
1513
|
+
return vec3(v, d) / sum;
|
|
1514
|
+
}
|
|
1515
|
+
vec4 sfbmd(vec3 p, int octaves) {
|
|
1516
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1517
|
+
vec3 d = vec3(0.0);
|
|
1518
|
+
mat3 m = mat3(1.0);
|
|
1519
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) {
|
|
1520
|
+
vec4 n = snoised(p);
|
|
1521
|
+
v += a * n.x;
|
|
1522
|
+
d += a * (transpose(m) * n.yzw);
|
|
1523
|
+
sum += a;
|
|
1524
|
+
p = PHORIA_TURN3 * p * 2.03 + 17.0;
|
|
1525
|
+
m = PHORIA_TURN3 * m * 2.03;
|
|
1526
|
+
a *= 0.5;
|
|
1527
|
+
}
|
|
1528
|
+
return vec4(v, d) / sum;
|
|
1529
|
+
}
|
|
1530
|
+
float vfbm(vec2 p) { return vfbm(p, 5); }
|
|
1531
|
+
float vfbm(vec3 p) { return vfbm(p, 5); }
|
|
1532
|
+
float sfbm(vec2 p) { return sfbm(p, 5); }
|
|
1533
|
+
float sfbm(vec3 p) { return sfbm(p, 5); }
|
|
1534
|
+
vec3 sfbmd(vec2 p) { return sfbmd(p, 5); }
|
|
1535
|
+
vec4 sfbmd(vec3 p) { return sfbmd(p, 5); }
|
|
1536
|
+
|
|
1537
|
+
// cellular noise: each unit cell's own point, from its hash; the nearest and second nearest of the
|
|
1538
|
+
// 3 x 3 (or 3 x 3 x 3) cells round p - F1, F2 - and the nearest cell's own random number
|
|
1539
|
+
vec3 voronoi(vec2 p) {
|
|
1540
|
+
vec2 i = floor(p), f = p - i;
|
|
1541
|
+
highp ivec2 c = ivec2(i);
|
|
1542
|
+
float f1 = 8.0, f2 = 8.0, id = 0.0;
|
|
1543
|
+
for (int y = -1; y <= 1; y++) {
|
|
1544
|
+
for (int x = -1; x <= 1; x++) {
|
|
1545
|
+
highp uint h = phoriaCell(ivec3(c + ivec2(x, y), 11));
|
|
1546
|
+
vec2 o = vec2(float(x), float(y)) + vec2(phoriaUnit(h), phoriaUnit(phoriaMix(h ^ 0x68e31da4u))) - f;
|
|
1547
|
+
float d = dot(o, o);
|
|
1548
|
+
if (d < f1) { f2 = f1; f1 = d; id = phoriaUnit(phoriaMix(h + 0x1b56c4e9u)); }
|
|
1549
|
+
else if (d < f2) f2 = d;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return vec3(sqrt(f1), sqrt(f2), id);
|
|
1553
|
+
}
|
|
1554
|
+
vec3 voronoi(vec3 p) {
|
|
1555
|
+
vec3 i = floor(p), f = p - i;
|
|
1556
|
+
highp ivec3 c = ivec3(i);
|
|
1557
|
+
float f1 = 8.0, f2 = 8.0, id = 0.0;
|
|
1558
|
+
for (int z = -1; z <= 1; z++) {
|
|
1559
|
+
for (int y = -1; y <= 1; y++) {
|
|
1560
|
+
for (int x = -1; x <= 1; x++) {
|
|
1561
|
+
highp uint h = phoriaCell(c + ivec3(x, y, z)) ^ 0x2545f491u;
|
|
1562
|
+
h = phoriaMix(h);
|
|
1563
|
+
vec3 o = vec3(float(x), float(y), float(z)) - f
|
|
1564
|
+
+ vec3(phoriaUnit(h), phoriaUnit(phoriaMix(h ^ 0x68e31da4u)), phoriaUnit(phoriaMix(h ^ 0xb5297a4du)));
|
|
1565
|
+
float d = dot(o, o);
|
|
1566
|
+
if (d < f1) { f2 = f1; f1 = d; id = phoriaUnit(phoriaMix(h + 0x1b56c4e9u)); }
|
|
1567
|
+
else if (d < f2) f2 = d;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return vec3(sqrt(f1), sqrt(f2), id);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// a colour as picked (sRGB) to the linear colour hooks work in
|
|
1575
|
+
vec3 srgb(vec3 c) { return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c)); }
|
|
1576
|
+
// hue, saturation and value 0-1 to a linear colour - the colour a colour picker shows
|
|
1577
|
+
vec3 hsv2rgb(vec3 hsv) {
|
|
1578
|
+
vec3 k = clamp(abs(mod(hsv.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
|
1579
|
+
return srgb(hsv.z * mix(vec3(1.0), k, hsv.y));
|
|
1580
|
+
}
|
|
1581
|
+
vec3 hsv2rgb(float h, float s, float v) { return hsv2rgb(vec3(h, s, v)); }
|
|
1582
|
+
// a cosine palette (Quilez): a + b cos(2 pi (c t + d)), its four vectors as picked; a linear colour
|
|
1583
|
+
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) { return srgb(clamp(a + b * cos(6.28318530718 * (c * t + d)), 0.0, 1.0)); }
|
|
1584
|
+
// how edge-on a surface is seen: 0 facing the camera, 1 side on, sharpened by the power - rim light
|
|
1585
|
+
float fresnel(vec3 normal, vec3 position, float power) {
|
|
1586
|
+
vec3 v = normalize(cameraPosition - position);
|
|
1587
|
+
return pow(1.0 - clamp(abs(dot(normalize(normal), v)), 0.0, 1.0), power);
|
|
1588
|
+
}
|
|
1589
|
+
// turning: rotate2(a) * v anticlockwise by a radians; rotate3(axis, a) * v about an axis
|
|
1590
|
+
mat2 rotate2(float a) { float s = sin(a), c = cos(a); return mat2(c, s, -s, c); }
|
|
1591
|
+
mat3 rotate3(vec3 axis, float a) {
|
|
1592
|
+
vec3 u = normalize(axis);
|
|
1593
|
+
float s = sin(a), c = cos(a), t = 1.0 - c;
|
|
1594
|
+
return mat3(t * u.x * u.x + c, t * u.x * u.y + s * u.z, t * u.x * u.z - s * u.y,
|
|
1595
|
+
t * u.x * u.y - s * u.z, t * u.y * u.y + c, t * u.y * u.z + s * u.x,
|
|
1596
|
+
t * u.x * u.z + s * u.y, t * u.y * u.z - s * u.x, t * u.z * u.z + c);
|
|
1597
|
+
}
|
|
1598
|
+
// x from one range to another, not clamped
|
|
1599
|
+
float remap(float x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1600
|
+
vec2 remap(vec2 x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1601
|
+
vec3 remap(vec3 x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1602
|
+
// clamped to 0-1, as HLSL has it
|
|
1603
|
+
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
|
1604
|
+
vec2 saturate(vec2 x) { return clamp(x, 0.0, 1.0); }
|
|
1605
|
+
vec3 saturate(vec3 x) { return clamp(x, 0.0, 1.0); }
|
|
1606
|
+
vec4 saturate(vec4 x) { return clamp(x, 0.0, 1.0); }
|
|
1607
|
+
`
|
|
1608
|
+
|
|
1609
|
+
// a hook's own lines are numbered from 1 in a source string of their own, so compile errors point
|
|
1610
|
+
// at the line of the hook, not of the generated shader
|
|
1611
|
+
export const HOOK_SOURCE_NUMBERS = { vertex: 101, fragment: 102, lit: 103 }
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Vertex and fragment source for a kind of drawable, or a pass's own pair, with a set of feature
|
|
1615
|
+
* defines - and a material's shader hooks, when it has them.
|
|
1224
1616
|
* @param {ShaderKind | [string, string]} kind
|
|
1225
1617
|
* @param {string[]} defines e.g. ['LIT', 'MAP']
|
|
1618
|
+
* @param {HookSource | null} [hooks]
|
|
1226
1619
|
* @returns {[string, string]}
|
|
1227
1620
|
*/
|
|
1228
|
-
export function shaderSource(kind, defines) {
|
|
1621
|
+
export function shaderSource(kind, defines, hooks = null) {
|
|
1229
1622
|
const header = `#version 300 es\n${defines.map((d) => `#define ${d}\n`).join('')}`
|
|
1230
1623
|
const [vs, fs] = typeof kind === 'string' ? SOURCES[kind] : kind
|
|
1231
|
-
return [header + vs, header + fs]
|
|
1624
|
+
if (!hooks) return [header + vs, header + fs]
|
|
1625
|
+
const first = 2 + defines.length
|
|
1626
|
+
return [header + splice(vs, hooks, first), header + splice(fs, hooks, first)]
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/** @param {string} source @param {HookSource} hooks @param {number} first the line number of the source's first line */
|
|
1630
|
+
function splice(source, hooks, first) {
|
|
1631
|
+
return source.split('\n').map((line, i) => {
|
|
1632
|
+
const at = /^\s*\/\/@hook (\w+)$/.exec(line)
|
|
1633
|
+
if (!at) return line
|
|
1634
|
+
const part = /** @type {keyof HookSource} */ (at[1])
|
|
1635
|
+
// the shader's own numbering carries on after the hook as if it weren't there
|
|
1636
|
+
const resume = `#line ${first + i + 1} 0`
|
|
1637
|
+
if (part === 'uniforms') return `${hooks.uniforms}\n${hooks.helpers ?? ''}\n${resume}`
|
|
1638
|
+
return `#line 1 ${HOOK_SOURCE_NUMBERS[part]}\n${hooks[part]}\n${resume}`
|
|
1639
|
+
}).join('\n')
|
|
1232
1640
|
}
|