lecodes-cli 0.20.0 → 0.20.1
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/dist/index.js +1065 -577
- package/package.json +8 -8
- package/runtime/materials/decal-relief.mat +178 -0
- package/runtime/materials/decal.mat +172 -0
- package/runtime/materials/lightmap-baked-lite.mat +175 -0
- package/runtime/materials/lightmap-baked.mat +176 -0
- package/runtime/materials/lightmap.mat +181 -0
- package/runtime/materials/lit.mat +43 -0
- package/runtime/materials/particles-quad.mat +171 -0
- package/runtime/materials/particles.mat +121 -0
- package/runtime/materials/shadow.mat +21 -0
- package/runtime/materials/terrain.mat +134 -0
- package/runtime/materials/unlit-transparent.mat +42 -0
- package/runtime/materials/unlit.mat +44 -0
- package/runtime/materials/video.mat +31 -0
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/assets/{createViewerLite-BvHobVhp.js → createViewerLite-C_bkKXLg.js} +22 -22
- package/runtime/web/assets/{index-CfNCCBjN.js → index-BTB5KV9x.js} +1 -1
- package/runtime/web/embed.js +1 -1
- package/src/cmgenTool.ts +17 -9
- package/src/commands/appDesktop.ts +12 -2
- package/src/commands/appShared.ts +48 -0
- package/src/commands/assets.ts +3 -7
- package/src/commands/design.ts +9 -26
- package/src/commands/desktop.ts +3 -0
- package/src/commands/dev.ts +42 -0
- package/src/commands/projectTemplates.ts +13 -1
- package/src/commands/render.ts +4 -8
- package/src/commands/scene.ts +2 -6
- package/src/commands/shaders.ts +17 -1
- package/src/commands/shadersNew.ts +133 -0
- package/src/commands/thumbs.ts +3 -6
- package/src/commands/update.ts +42 -21
- package/src/desktopRenderer.ts +17 -0
- package/src/desktopScript.ts +1 -1
- package/src/dev/androidDev.ts +154 -0
- package/src/dev/devServer.ts +8 -2
- package/src/index.ts +21 -4
- package/src/matcTool.ts +31 -19
- package/src/peerInstall.ts +160 -0
- package/src/peers.ts +48 -14
- package/src/projectEnv.ts +4 -0
- package/src/releases.ts +16 -0
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lecodes-cli",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@letary/chisel": "^0.10.0",
|
|
6
6
|
"jimp": "^1.6.1",
|
|
7
|
-
"lecodes-sdk": "
|
|
7
|
+
"lecodes-sdk": "workspace:*"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"lecodes-design": "
|
|
11
|
-
"lecodes-renderer": "
|
|
10
|
+
"lecodes-design": "^0.9.0",
|
|
11
|
+
"lecodes-renderer": "workspace:*",
|
|
12
12
|
"qrcode-terminal": "^0.12.0",
|
|
13
|
-
"lecodes-3d-editor": "
|
|
14
|
-
"lecodes-assets": "
|
|
15
|
-
"lecodes-creator-nav": "
|
|
16
|
-
"lecodes-viewer-lite": "
|
|
13
|
+
"lecodes-3d-editor": "^0.7.0",
|
|
14
|
+
"lecodes-assets": "workspace:*",
|
|
15
|
+
"lecodes-creator-nav": "workspace:*",
|
|
16
|
+
"lecodes-viewer-lite": "workspace:*"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"lecodes-design": "^0.9.0",
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Relief decal material (SDK: Material.decal({ normalMap }) — a DecalSet with a `normalMap`). The
|
|
2
|
+
// projection is decal.mat's: the box, the depth-aware bilinear structure read, the unit-space
|
|
3
|
+
// test, the atlas rect, the life fade. What differs is WHAT it paints: not a colour but a change
|
|
4
|
+
// of the surface's own lighting. A forward renderer has already shaded the wall when the decal is
|
|
5
|
+
// drawn, so the normal map cannot bend the wall's normal; instead this shader shades the surface
|
|
6
|
+
// point twice — with the normal reconstructed from the depth buffer, and with that normal bent by
|
|
7
|
+
// the map in the decal's tangent frame — and multiplies the frame buffer by the ratio (`blending :
|
|
8
|
+
// multiply`). A footprint or a dent thus shows relief on ANY surface, whatever its albedo, and
|
|
9
|
+
// needs no colour of its own; a bullet hole adds its colour map on top (useColor: the crater
|
|
10
|
+
// darkens, the map's alpha is its coverage).
|
|
11
|
+
//
|
|
12
|
+
// Lighting = the same per-frame terms filament shades the pixel with (lightmap.mat's recipe): the
|
|
13
|
+
// IBL's SH irradiance for the normal + the directional light's NoL, both exposure-premultiplied.
|
|
14
|
+
// No shadow-map read is possible from here, so the sun term applies in shadow too: relief in a
|
|
15
|
+
// shadowed area reads slightly sunlit. Normal maps are tangent-space, +X = the image's right,
|
|
16
|
+
// +Y = its top (OpenGL convention), stored LINEAR (Texture.fromPixels srgb:false / a linear KTX2).
|
|
17
|
+
//
|
|
18
|
+
// Per-vertex inputs and the uniforms time / edge / angleFade: as decal.mat. Extra uniforms:
|
|
19
|
+
// normalMap — the tangent-space atlas (same cell grid as baseColorMap)
|
|
20
|
+
// bump — normal xy scale (1 = as authored)
|
|
21
|
+
// useColor — 1: multiply the colour map in (rgb by its alpha); 0: relief only (footprints)
|
|
22
|
+
|
|
23
|
+
material {
|
|
24
|
+
name : decal_relief,
|
|
25
|
+
shadingModel : unlit,
|
|
26
|
+
parameters : [
|
|
27
|
+
{ type : sampler2d, name : baseColorMap },
|
|
28
|
+
{ type : sampler2d, name : normalMap },
|
|
29
|
+
{ type : float, name : time, precision : high },
|
|
30
|
+
{ type : float, name : edge },
|
|
31
|
+
{ type : float, name : angleFade },
|
|
32
|
+
{ type : float, name : bump },
|
|
33
|
+
{ type : float, name : useColor }
|
|
34
|
+
],
|
|
35
|
+
variables : [
|
|
36
|
+
{ name : inv0, precision : high },
|
|
37
|
+
{ name : inv1, precision : high },
|
|
38
|
+
{ name : inv2, precision : high },
|
|
39
|
+
{ name : rect, precision : high }
|
|
40
|
+
],
|
|
41
|
+
blending : multiply,
|
|
42
|
+
culling : front,
|
|
43
|
+
depthWrite : false,
|
|
44
|
+
depthCulling : false,
|
|
45
|
+
requires : [ color, custom0, custom1, custom2, custom3, custom4 ]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
vertex {
|
|
49
|
+
void materialVertex(inout MaterialVertexInputs material) {
|
|
50
|
+
highp mat4 invDecal = transpose(mat4(getCustom0(), getCustom1(), getCustom2(), vec4(0.0, 0.0, 0.0, 1.0)));
|
|
51
|
+
highp mat4 total = invDecal * inverse(getWorldFromModelMatrix());
|
|
52
|
+
material.inv0 = vec4(total[0][0], total[1][0], total[2][0], total[3][0]);
|
|
53
|
+
material.inv1 = vec4(total[0][1], total[1][1], total[2][1], total[3][1]);
|
|
54
|
+
material.inv2 = vec4(total[0][2], total[1][2], total[2][2], total[3][2]);
|
|
55
|
+
material.rect = getCustom3();
|
|
56
|
+
|
|
57
|
+
highp vec4 l = getCustom4();
|
|
58
|
+
highp float age = materialParams.time - l.x;
|
|
59
|
+
float a = 1.0;
|
|
60
|
+
if (l.z > 0.0) a *= saturate(age / l.z);
|
|
61
|
+
if (l.y > 0.0) {
|
|
62
|
+
a *= l.w > 0.0 ? saturate((l.y - age) / l.w) : 1.0;
|
|
63
|
+
if (age >= l.y) a = 0.0;
|
|
64
|
+
}
|
|
65
|
+
material.color.a *= a;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fragment {
|
|
70
|
+
// Diffuse irradiance filament would give a white surface with normal n: the IBL's SH + the sun.
|
|
71
|
+
float lightOf(const highp vec3 n) {
|
|
72
|
+
const vec3 luma = vec3(0.2126, 0.7152, 0.0722);
|
|
73
|
+
vec3 sh = frameUniforms.iblSH[0].xyz
|
|
74
|
+
+ frameUniforms.iblSH[1].xyz * n.y + frameUniforms.iblSH[2].xyz * n.z + frameUniforms.iblSH[3].xyz * n.x
|
|
75
|
+
+ frameUniforms.iblSH[4].xyz * (n.y * n.x) + frameUniforms.iblSH[5].xyz * (n.y * n.z)
|
|
76
|
+
+ frameUniforms.iblSH[6].xyz * (3.0 * n.z * n.z - 1.0) + frameUniforms.iblSH[7].xyz * (n.z * n.x)
|
|
77
|
+
+ frameUniforms.iblSH[8].xyz * (n.x * n.x - n.y * n.y);
|
|
78
|
+
float amb = max(dot(max(sh, vec3(0.0)), luma), 0.0) * frameUniforms.iblLuminance;
|
|
79
|
+
float nol = max(dot(n, frameUniforms.lightDirection), 0.0);
|
|
80
|
+
float sun = dot(frameUniforms.lightColorIntensity.rgb, luma) * frameUniforms.lightColorIntensity.w * nol / PI;
|
|
81
|
+
return amb + sun;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void material(inout MaterialInputs material) {
|
|
85
|
+
prepareMaterial(material);
|
|
86
|
+
vec3 value = vec3(1.0);
|
|
87
|
+
float alpha = 0.0;
|
|
88
|
+
#if !defined(VARIANT_DEPTH)
|
|
89
|
+
highp vec3 wp = getWorldPosition();
|
|
90
|
+
highp vec4 clip = getClipFromWorldMatrix() * vec4(wp, 1.0);
|
|
91
|
+
highp vec2 suv = clip.xy * (0.5 / clip.w) + 0.5;
|
|
92
|
+
highp mat4 p = getViewFromClipMatrix();
|
|
93
|
+
// depth-aware bilinear structure read — see decal.mat
|
|
94
|
+
highp vec2 ssz = vec2(textureSize(sampler0_structure, 0));
|
|
95
|
+
highp vec2 ts = 1.0 / ssz;
|
|
96
|
+
highp vec2 pix = suv * ssz - 0.5;
|
|
97
|
+
highp vec2 base = (floor(pix) + 0.5) * ts;
|
|
98
|
+
highp vec2 fr = fract(pix);
|
|
99
|
+
highp float d00 = textureLod(sampler0_structure, uvToRenderTargetUV(base), 0.0).r;
|
|
100
|
+
highp float d10 = textureLod(sampler0_structure, uvToRenderTargetUV(base + vec2(ts.x, 0.0)), 0.0).r;
|
|
101
|
+
highp float d01 = textureLod(sampler0_structure, uvToRenderTargetUV(base + vec2(0.0, ts.y)), 0.0).r;
|
|
102
|
+
highp float d11 = textureLod(sampler0_structure, uvToRenderTargetUV(base + ts), 0.0).r;
|
|
103
|
+
highp float z00 = (d00 * p[2].z + p[3].z) / max(d00 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
104
|
+
highp float z10 = (d10 * p[2].z + p[3].z) / max(d10 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
105
|
+
highp float z01 = (d01 * p[2].z + p[3].z) / max(d01 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
106
|
+
highp float z11 = (d11 * p[2].z + p[3].z) / max(d11 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
107
|
+
highp float zRef = fr.x < 0.5 ? (fr.y < 0.5 ? z00 : z01) : (fr.y < 0.5 ? z10 : z11);
|
|
108
|
+
highp float tol = max(0.05 * abs(zRef), 0.03);
|
|
109
|
+
highp float w00 = (1.0 - fr.x) * (1.0 - fr.y) * step(abs(z00 - zRef), tol);
|
|
110
|
+
highp float w10 = fr.x * (1.0 - fr.y) * step(abs(z10 - zRef), tol);
|
|
111
|
+
highp float w01 = (1.0 - fr.x) * fr.y * step(abs(z01 - zRef), tol);
|
|
112
|
+
highp float w11 = fr.x * fr.y * step(abs(z11 - zRef), tol);
|
|
113
|
+
highp float wsum = max(w00 + w10 + w01 + w11, 1e-6);
|
|
114
|
+
highp float sceneDepth = (d00 * w00 + d10 * w10 + d01 * w01 + d11 * w11) / wsum;
|
|
115
|
+
highp vec4 vc = p * vec4(suv * 2.0 - 1.0, sceneDepth, 1.0);
|
|
116
|
+
highp vec3 sv = vc.xyz / vc.w;
|
|
117
|
+
highp vec3 sw = (getWorldFromViewMatrix() * vec4(sv, 1.0)).xyz;
|
|
118
|
+
|
|
119
|
+
highp vec3 local = vec3(
|
|
120
|
+
dot(variable_inv0.xyz, sw) + variable_inv0.w,
|
|
121
|
+
dot(variable_inv1.xyz, sw) + variable_inv1.w,
|
|
122
|
+
dot(variable_inv2.xyz, sw) + variable_inv2.w);
|
|
123
|
+
if (any(greaterThan(abs(local), vec3(0.5)))) discard;
|
|
124
|
+
|
|
125
|
+
float4 tint = getColor();
|
|
126
|
+
alpha = tint.a;
|
|
127
|
+
|
|
128
|
+
// The surface normal from texel centres of the structure buffer (see decal.mat) — needed
|
|
129
|
+
// here always: it is the normal the relief bends.
|
|
130
|
+
highp vec2 sel = vec2(fr.x < 0.5 ? 0.0 : ts.x, fr.y < 0.5 ? 0.0 : ts.y);
|
|
131
|
+
highp vec2 cuv = base + sel;
|
|
132
|
+
highp float dc = fr.x < 0.5 ? (fr.y < 0.5 ? d00 : d01) : (fr.y < 0.5 ? d10 : d11);
|
|
133
|
+
highp float dl = textureLod(sampler0_structure, uvToRenderTargetUV(cuv - vec2(ts.x, 0.0)), 0.0).r;
|
|
134
|
+
highp float dr = textureLod(sampler0_structure, uvToRenderTargetUV(cuv + vec2(ts.x, 0.0)), 0.0).r;
|
|
135
|
+
highp float dd = textureLod(sampler0_structure, uvToRenderTargetUV(cuv - vec2(0.0, ts.y)), 0.0).r;
|
|
136
|
+
highp float du = textureLod(sampler0_structure, uvToRenderTargetUV(cuv + vec2(0.0, ts.y)), 0.0).r;
|
|
137
|
+
highp vec2 ndc = cuv * 2.0 - 1.0;
|
|
138
|
+
highp vec4 vcc = p * vec4(ndc, dc, 1.0);
|
|
139
|
+
highp vec4 vl = p * vec4(ndc - vec2(2.0 * ts.x, 0.0), dl, 1.0);
|
|
140
|
+
highp vec4 vr = p * vec4(ndc + vec2(2.0 * ts.x, 0.0), dr, 1.0);
|
|
141
|
+
highp vec4 vd = p * vec4(ndc - vec2(0.0, 2.0 * ts.y), dd, 1.0);
|
|
142
|
+
highp vec4 vu = p * vec4(ndc + vec2(0.0, 2.0 * ts.y), du, 1.0);
|
|
143
|
+
highp vec3 sc = vcc.xyz / vcc.w;
|
|
144
|
+
highp vec3 ex = abs(dr - dc) < abs(dl - dc) ? vr.xyz / vr.w - sc : sc - vl.xyz / vl.w;
|
|
145
|
+
highp vec3 ey = abs(du - dc) < abs(dd - dc) ? vu.xyz / vu.w - sc : sc - vd.xyz / vd.w;
|
|
146
|
+
highp vec3 nv = cross(ex, ey);
|
|
147
|
+
highp float nl = length(nv);
|
|
148
|
+
highp vec3 axis = normalize(variable_inv2.xyz);
|
|
149
|
+
highp vec3 N = nl > 0.0 ? normalize(mat3(getWorldFromViewMatrix()) * (nv / nl)) : axis;
|
|
150
|
+
if (dot(N, axis) < 0.0) N = -N; // the cross product's sign follows the screen
|
|
151
|
+
if (materialParams.angleFade > 0.0) {
|
|
152
|
+
float facing = abs(dot(N, axis));
|
|
153
|
+
alpha *= smoothstep(materialParams.angleFade, materialParams.angleFade + 0.2, facing);
|
|
154
|
+
}
|
|
155
|
+
float edge = materialParams.edge;
|
|
156
|
+
if (edge > 0.0) alpha *= saturate((0.5 - abs(local.z)) / (0.5 * edge));
|
|
157
|
+
|
|
158
|
+
highp vec2 uv = vec2(local.x + 0.5, 0.5 - local.y);
|
|
159
|
+
highp vec2 tuv = mix(variable_rect.xy, variable_rect.zw, uv);
|
|
160
|
+
|
|
161
|
+
// The bent normal: the map's xy along the decal's right / up, z along the surface normal.
|
|
162
|
+
vec3 T = normalize(variable_inv0.xyz);
|
|
163
|
+
vec3 B = normalize(variable_inv1.xyz);
|
|
164
|
+
vec3 m = texture(materialParams_normalMap, tuv).xyz * 2.0 - 1.0;
|
|
165
|
+
m.xy *= materialParams.bump;
|
|
166
|
+
vec3 Nb = normalize(T * m.x + B * m.y + N * max(m.z, 0.05));
|
|
167
|
+
float ratio = lightOf(Nb) / max(lightOf(N), 1e-4);
|
|
168
|
+
value = vec3(ratio);
|
|
169
|
+
if (materialParams.useColor > 0.5) {
|
|
170
|
+
float4 tex = texture(materialParams_baseColorMap, tuv);
|
|
171
|
+
value *= mix(vec3(1.0), tex.rgb * tint.rgb, tex.a);
|
|
172
|
+
}
|
|
173
|
+
if (alpha <= 0.002) discard;
|
|
174
|
+
#endif
|
|
175
|
+
// multiply blending: the frame buffer is scaled by this; 1 = untouched
|
|
176
|
+
material.baseColor = float4(mix(vec3(1.0), value, alpha), 1.0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Projected decal material (SDK: Material.decal({ map }); native DecalSet in creator-gl
|
|
2
|
+
// src/decals.cpp). One DecalSet is ONE renderable of unit boxes. The fragment stage does not shade
|
|
3
|
+
// the box: it reads the scene depth behind the fragment (Filament's structure buffer, bound to the
|
|
4
|
+
// colour pass by creator-gl patch 0017 while a set has live decals), reconstructs the scene point
|
|
5
|
+
// on the fragment's eye ray, maps it into the decal's unit space with the inverse frame the vertex
|
|
6
|
+
// carries, discards outside the box and samples the atlas cell there. So a decal lands on whatever
|
|
7
|
+
// opaque surface sits inside its box — floor, wall, corner, stairs, a curved rock — with no
|
|
8
|
+
// geometry access at all. It never lands on blended things (particles, glass, other decals): they
|
|
9
|
+
// are not in the structure pass.
|
|
10
|
+
//
|
|
11
|
+
// Per-vertex inputs (DECAL_VERTEX_FLOATS in decals.h, one interleaved buffer, 8 corners per decal):
|
|
12
|
+
// POSITION — the box corner in the SET's space (the renderable's model space)
|
|
13
|
+
// CUSTOM0..2 — rows of the 3x4 inverse frame: unit box ← set space
|
|
14
|
+
// CUSTOM3 — atlas rect u0 v0 u1 v1 (v0 = the cell's top, image top = +Y of the box)
|
|
15
|
+
// COLOR — tint rgba (a = opacity)
|
|
16
|
+
// CUSTOM4 — born, life, fadeIn, fadeOut (seconds on the `time` clock; life 0 = forever)
|
|
17
|
+
// Uniforms:
|
|
18
|
+
// baseColorMap — the atlas
|
|
19
|
+
// time — creator-gl's decal clock (sim seconds), written every frame by updateDecals
|
|
20
|
+
// edge — soft fraction of the box's half depth at both Z ends (0 = hard cut), so an
|
|
21
|
+
// oblique surface leaves the box gently
|
|
22
|
+
// angleFade — cosine below which a surface facing away from the projection axis fades out
|
|
23
|
+
// (0.3 ≈ 72°): keeps a floor hit off the wall it meets. 0 = project onto anything
|
|
24
|
+
// emissive — HDR boost of the texture colour (0 = none)
|
|
25
|
+
// State: `culling : front` keeps the box's FAR faces, `depthCulling : false` draws them even when a
|
|
26
|
+
// wall (or the camera, standing inside a floor decal's box) is in front — the projection test is
|
|
27
|
+
// the only visibility test. depthWrite off, no shadows.
|
|
28
|
+
|
|
29
|
+
material {
|
|
30
|
+
name : decal,
|
|
31
|
+
shadingModel : unlit,
|
|
32
|
+
parameters : [
|
|
33
|
+
{ type : sampler2d, name : baseColorMap },
|
|
34
|
+
{ type : float, name : time, precision : high },
|
|
35
|
+
{ type : float, name : edge },
|
|
36
|
+
{ type : float, name : angleFade },
|
|
37
|
+
{ type : float, name : emissive }
|
|
38
|
+
],
|
|
39
|
+
variables : [
|
|
40
|
+
{ name : inv0, precision : high },
|
|
41
|
+
{ name : inv1, precision : high },
|
|
42
|
+
{ name : inv2, precision : high },
|
|
43
|
+
{ name : rect, precision : high }
|
|
44
|
+
],
|
|
45
|
+
blending : fade,
|
|
46
|
+
culling : front,
|
|
47
|
+
depthWrite : false,
|
|
48
|
+
depthCulling : false,
|
|
49
|
+
requires : [ color, custom0, custom1, custom2, custom3, custom4 ]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
vertex {
|
|
53
|
+
void materialVertex(inout MaterialVertexInputs material) {
|
|
54
|
+
// unit box ← set space (CPU rows) composed with set space ← world (the renderable's model
|
|
55
|
+
// transform, inverted here: the set node may sit under a moving parent)
|
|
56
|
+
highp mat4 invDecal = transpose(mat4(getCustom0(), getCustom1(), getCustom2(), vec4(0.0, 0.0, 0.0, 1.0)));
|
|
57
|
+
highp mat4 total = invDecal * inverse(getWorldFromModelMatrix());
|
|
58
|
+
material.inv0 = vec4(total[0][0], total[1][0], total[2][0], total[3][0]);
|
|
59
|
+
material.inv1 = vec4(total[0][1], total[1][1], total[2][1], total[3][1]);
|
|
60
|
+
material.inv2 = vec4(total[0][2], total[1][2], total[2][2], total[3][2]);
|
|
61
|
+
material.rect = getCustom3();
|
|
62
|
+
|
|
63
|
+
// Life: fade in over fadeIn, hold, fade out over the last fadeOut seconds; nothing after life.
|
|
64
|
+
highp vec4 l = getCustom4();
|
|
65
|
+
highp float age = materialParams.time - l.x;
|
|
66
|
+
float a = 1.0;
|
|
67
|
+
if (l.z > 0.0) a *= saturate(age / l.z);
|
|
68
|
+
if (l.y > 0.0) {
|
|
69
|
+
a *= l.w > 0.0 ? saturate((l.y - age) / l.w) : 1.0;
|
|
70
|
+
if (age >= l.y) a = 0.0;
|
|
71
|
+
}
|
|
72
|
+
material.color.a *= a;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fragment {
|
|
77
|
+
void material(inout MaterialInputs material) {
|
|
78
|
+
prepareMaterial(material);
|
|
79
|
+
float alpha = 0.0;
|
|
80
|
+
float3 rgb = float3(0.0);
|
|
81
|
+
#if !defined(VARIANT_DEPTH)
|
|
82
|
+
// The scene point behind this fragment: the structure buffer's depth on the same eye ray.
|
|
83
|
+
// Depth goes through Filament's SSR linearizeDepth formula (reversed-Z, far at infinity);
|
|
84
|
+
// clip z is the stored depth itself, so viewFromClip * (ndc.xy, depth, 1) is a view point.
|
|
85
|
+
highp vec3 wp = getWorldPosition();
|
|
86
|
+
highp vec4 clip = getClipFromWorldMatrix() * vec4(wp, 1.0);
|
|
87
|
+
highp vec2 suv = clip.xy * (0.5 / clip.w) + 0.5;
|
|
88
|
+
highp mat4 p = getViewFromClipMatrix();
|
|
89
|
+
// Depth-aware BILINEAR read of the structure buffer. Filament binds it NEAREST and at half
|
|
90
|
+
// resolution, so a plain read quantises the projection to 2x2-pixel blocks (stair-stepped
|
|
91
|
+
// edges, blocky uv at oblique views). Hardware depth is affine in screen space on a plane,
|
|
92
|
+
// so interpolating the RAW depth between the four texel centres is exact there. Across a
|
|
93
|
+
// silhouette a mix would float in the air: taps whose linear depth strays from the nearest
|
|
94
|
+
// texel by more than a few percent are dropped (weights renormalised).
|
|
95
|
+
highp vec2 ssz = vec2(textureSize(sampler0_structure, 0));
|
|
96
|
+
highp vec2 ts = 1.0 / ssz;
|
|
97
|
+
highp vec2 pix = suv * ssz - 0.5;
|
|
98
|
+
highp vec2 base = (floor(pix) + 0.5) * ts;
|
|
99
|
+
highp vec2 fr = fract(pix);
|
|
100
|
+
highp float d00 = textureLod(sampler0_structure, uvToRenderTargetUV(base), 0.0).r;
|
|
101
|
+
highp float d10 = textureLod(sampler0_structure, uvToRenderTargetUV(base + vec2(ts.x, 0.0)), 0.0).r;
|
|
102
|
+
highp float d01 = textureLod(sampler0_structure, uvToRenderTargetUV(base + vec2(0.0, ts.y)), 0.0).r;
|
|
103
|
+
highp float d11 = textureLod(sampler0_structure, uvToRenderTargetUV(base + ts), 0.0).r;
|
|
104
|
+
highp float z00 = (d00 * p[2].z + p[3].z) / max(d00 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
105
|
+
highp float z10 = (d10 * p[2].z + p[3].z) / max(d10 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
106
|
+
highp float z01 = (d01 * p[2].z + p[3].z) / max(d01 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
107
|
+
highp float z11 = (d11 * p[2].z + p[3].z) / max(d11 * p[2].w + p[3].w, 1.0 / 16777216.0);
|
|
108
|
+
highp float zRef = fr.x < 0.5 ? (fr.y < 0.5 ? z00 : z01) : (fr.y < 0.5 ? z10 : z11);
|
|
109
|
+
highp float tol = max(0.05 * abs(zRef), 0.03);
|
|
110
|
+
highp float w00 = (1.0 - fr.x) * (1.0 - fr.y) * step(abs(z00 - zRef), tol);
|
|
111
|
+
highp float w10 = fr.x * (1.0 - fr.y) * step(abs(z10 - zRef), tol);
|
|
112
|
+
highp float w01 = (1.0 - fr.x) * fr.y * step(abs(z01 - zRef), tol);
|
|
113
|
+
highp float w11 = fr.x * fr.y * step(abs(z11 - zRef), tol);
|
|
114
|
+
highp float wsum = max(w00 + w10 + w01 + w11, 1e-6);
|
|
115
|
+
highp float sceneDepth = (d00 * w00 + d10 * w10 + d01 * w01 + d11 * w11) / wsum;
|
|
116
|
+
highp vec4 vc = p * vec4(suv * 2.0 - 1.0, sceneDepth, 1.0);
|
|
117
|
+
highp vec3 sv = vc.xyz / vc.w;
|
|
118
|
+
highp vec3 sw = (getWorldFromViewMatrix() * vec4(sv, 1.0)).xyz;
|
|
119
|
+
|
|
120
|
+
highp vec3 local = vec3(
|
|
121
|
+
dot(variable_inv0.xyz, sw) + variable_inv0.w,
|
|
122
|
+
dot(variable_inv1.xyz, sw) + variable_inv1.w,
|
|
123
|
+
dot(variable_inv2.xyz, sw) + variable_inv2.w);
|
|
124
|
+
if (any(greaterThan(abs(local), vec3(0.5)))) discard;
|
|
125
|
+
|
|
126
|
+
float4 tint = getColor();
|
|
127
|
+
alpha = tint.a;
|
|
128
|
+
|
|
129
|
+
// Surface facing for the angle fade (a floor decal reaching a wall). The normal comes from
|
|
130
|
+
// the structure buffer's own texels — NOT screen derivatives: the buffer is half-res, so a
|
|
131
|
+
// 2x2 quad often reads one texel and dFdx/dFdy then describe the view plane, not the
|
|
132
|
+
// surface. Every point here is a TEXEL CENTRE (the nearest one and its four neighbours):
|
|
133
|
+
// mixing the bilinear centre with nearest neighbours makes the differences wobble at texel
|
|
134
|
+
// frequency, which reads as hatching on a wall. Per axis the neighbour closer in depth wins
|
|
135
|
+
// (a silhouette is crossed on one side only).
|
|
136
|
+
if (materialParams.angleFade > 0.0) {
|
|
137
|
+
highp vec2 sel = vec2(fr.x < 0.5 ? 0.0 : ts.x, fr.y < 0.5 ? 0.0 : ts.y);
|
|
138
|
+
highp vec2 cuv = base + sel; // nearest texel centre
|
|
139
|
+
highp float dc = fr.x < 0.5 ? (fr.y < 0.5 ? d00 : d01) : (fr.y < 0.5 ? d10 : d11);
|
|
140
|
+
highp float dl = textureLod(sampler0_structure, uvToRenderTargetUV(cuv - vec2(ts.x, 0.0)), 0.0).r;
|
|
141
|
+
highp float dr = textureLod(sampler0_structure, uvToRenderTargetUV(cuv + vec2(ts.x, 0.0)), 0.0).r;
|
|
142
|
+
highp float dd = textureLod(sampler0_structure, uvToRenderTargetUV(cuv - vec2(0.0, ts.y)), 0.0).r;
|
|
143
|
+
highp float du = textureLod(sampler0_structure, uvToRenderTargetUV(cuv + vec2(0.0, ts.y)), 0.0).r;
|
|
144
|
+
highp vec2 ndc = cuv * 2.0 - 1.0;
|
|
145
|
+
highp vec4 vcc = p * vec4(ndc, dc, 1.0);
|
|
146
|
+
highp vec4 vl = p * vec4(ndc - vec2(2.0 * ts.x, 0.0), dl, 1.0);
|
|
147
|
+
highp vec4 vr = p * vec4(ndc + vec2(2.0 * ts.x, 0.0), dr, 1.0);
|
|
148
|
+
highp vec4 vd = p * vec4(ndc - vec2(0.0, 2.0 * ts.y), dd, 1.0);
|
|
149
|
+
highp vec4 vu = p * vec4(ndc + vec2(0.0, 2.0 * ts.y), du, 1.0);
|
|
150
|
+
highp vec3 sc = vcc.xyz / vcc.w;
|
|
151
|
+
highp vec3 ex = abs(dr - dc) < abs(dl - dc) ? vr.xyz / vr.w - sc : sc - vl.xyz / vl.w;
|
|
152
|
+
highp vec3 ey = abs(du - dc) < abs(dd - dc) ? vu.xyz / vu.w - sc : sc - vd.xyz / vd.w;
|
|
153
|
+
highp vec3 nv = cross(ex, ey);
|
|
154
|
+
highp float nl = length(nv);
|
|
155
|
+
highp vec3 axisView = mat3(getViewFromWorldMatrix()) * normalize(variable_inv2.xyz);
|
|
156
|
+
float facing = nl > 0.0 ? abs(dot(nv / nl, axisView)) : 1.0;
|
|
157
|
+
alpha *= smoothstep(materialParams.angleFade, materialParams.angleFade + 0.2, facing);
|
|
158
|
+
}
|
|
159
|
+
// Soft Z ends of the box, so an oblique surface leaves the box without a hard cut.
|
|
160
|
+
float edge = materialParams.edge;
|
|
161
|
+
if (edge > 0.0) alpha *= saturate((0.5 - abs(local.z)) / (0.5 * edge));
|
|
162
|
+
|
|
163
|
+
highp vec2 uv = vec2(local.x + 0.5, 0.5 - local.y);
|
|
164
|
+
highp vec2 tuv = mix(variable_rect.xy, variable_rect.zw, uv);
|
|
165
|
+
float4 tex = texture(materialParams_baseColorMap, tuv);
|
|
166
|
+
alpha *= tex.a;
|
|
167
|
+
if (alpha <= 0.002) discard;
|
|
168
|
+
rgb = tex.rgb * tint.rgb * (1.0 + materialParams.emissive);
|
|
169
|
+
#endif
|
|
170
|
+
material.baseColor = float4(rgb * alpha, alpha);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// lightmap-baked-lite: the lightmap material's CHEAPEST tier (SDK `Material.lightmapShading =
|
|
2
|
+
// "baked-lite"`). lightmap-baked (the unlit twin with hand-written lighting) with the per-pixel texture
|
|
3
|
+
// reads cut on the LEVEL: a static (the atlas path) reads base colour, emissive and the two atlas pages
|
|
4
|
+
// only — no normal map (the geometric normal shades), no metallic/roughness map, no occlusion map (the
|
|
5
|
+
// atlas' baked AO stays). A dynamic object (the volume path: weapons, hands, bots — few pixels) keeps
|
|
6
|
+
// every read of lightmap-baked, so the gun in front of the camera looks the same on every tier.
|
|
7
|
+
// Emissive MUST stay: the lamps and signs glow through it.
|
|
8
|
+
//
|
|
9
|
+
// Trap (2026-09-07): with the metallic/roughness map gone, `metallicFactor` alone is WRONG — every
|
|
10
|
+
// pack material here declares metallicFactor 1 and lets the map carry the metalness, so the factor
|
|
11
|
+
// made the whole level fully metallic (diffuse 0 → a dark scene, a dark rifle). Without the map a
|
|
12
|
+
// static is a dielectric (metallic 0, roughness = the factor floored at 0.6).
|
|
13
|
+
// Measured on fps-demo's shooting range at 1920x1080 (integrated GPU, LOW preset): a 3-read probe of
|
|
14
|
+
// this shape ran +20 % over lightmap-baked (62–69 vs 48–59 fps); at 1280x720 only +3–5 % — texture
|
|
15
|
+
// bandwidth is the remaining per-pixel bill at the target resolution.
|
|
16
|
+
// Keep the parameter list IDENTICAL to lightmap.mat (see lightmap-baked.mat): every parameter is set
|
|
17
|
+
// by creator-gl whether the shader reads it or not, and an undeclared uniform is a panic.
|
|
18
|
+
|
|
19
|
+
material {
|
|
20
|
+
name : lightmap_baked_lite,
|
|
21
|
+
shadingModel : unlit,
|
|
22
|
+
doubleSided : true,
|
|
23
|
+
flipUV : false,
|
|
24
|
+
requires : [ uv0, uv1, tangents ],
|
|
25
|
+
parameters : [
|
|
26
|
+
{ type : float4, name : baseColorFactor },
|
|
27
|
+
{ type : sampler2d, name : baseColorMap },
|
|
28
|
+
{ type : float, name : metallicFactor },
|
|
29
|
+
{ type : float, name : roughnessFactor },
|
|
30
|
+
{ type : float3, name : emissiveFactor },
|
|
31
|
+
{ type : float, name : normalScale },
|
|
32
|
+
{ type : float, name : aoStrength },
|
|
33
|
+
{ type : sampler2d, name : normalMap },
|
|
34
|
+
{ type : float, name : hasNormalMap },
|
|
35
|
+
{ type : sampler2d, name : metallicRoughnessMap },
|
|
36
|
+
{ type : sampler2d, name : occlusionMap },
|
|
37
|
+
{ type : sampler2d, name : emissiveMap },
|
|
38
|
+
{ type : sampler2d, name : lightmap },
|
|
39
|
+
{ type : sampler2d, name : lightmapLight },
|
|
40
|
+
{ type : float, name : lightScale },
|
|
41
|
+
{ type : float, name : lightGamma },
|
|
42
|
+
{ type : float4, name : lightmapST },
|
|
43
|
+
{ type : float, name : ambientScale },
|
|
44
|
+
{ type : float, name : sunStrength },
|
|
45
|
+
{ type : float, name : bakedAo },
|
|
46
|
+
{ type : sampler3d, name : probeVolume },
|
|
47
|
+
{ type : sampler3d, name : probeLight },
|
|
48
|
+
{ type : float4, name : probeMin },
|
|
49
|
+
{ type : float4, name : probeInvSize }
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fragment {
|
|
54
|
+
// filament's SH irradiance (Lambert baked into the coefficients, like Irradiance_SphericalHarmonics)
|
|
55
|
+
vec3 shIrradiance(const vec3 n) {
|
|
56
|
+
return max(
|
|
57
|
+
frameUniforms.iblSH[0].xyz
|
|
58
|
+
+ frameUniforms.iblSH[1].xyz * n.y + frameUniforms.iblSH[2].xyz * n.z + frameUniforms.iblSH[3].xyz * n.x
|
|
59
|
+
+ frameUniforms.iblSH[4].xyz * (n.y * n.x) + frameUniforms.iblSH[5].xyz * (n.y * n.z)
|
|
60
|
+
+ frameUniforms.iblSH[6].xyz * (3.0 * n.z * n.z - 1.0) + frameUniforms.iblSH[7].xyz * (n.z * n.x)
|
|
61
|
+
+ frameUniforms.iblSH[8].xyz * (n.x * n.x - n.y * n.y), 0.0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Karis' analytic DFG (UE4 mobile): the split-sum's E term without the LUT
|
|
65
|
+
vec3 envBrdfApprox(const vec3 f0, float roughness, float NoV) {
|
|
66
|
+
vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
|
67
|
+
vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
|
|
68
|
+
vec4 r = roughness * c0 + c1;
|
|
69
|
+
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
|
70
|
+
vec2 ab = vec2(-1.04, 1.04) * a004 + r.zw;
|
|
71
|
+
return f0 * ab.x + ab.y;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
void material(inout MaterialInputs material) {
|
|
75
|
+
prepareMaterial(material);
|
|
76
|
+
vec4 base = texture(materialParams_baseColorMap, getUV0()) * materialParams.baseColorFactor;
|
|
77
|
+
// a dynamic object (volume-lit) keeps lightmap-baked's reads; a static reads nothing but base,
|
|
78
|
+
// emissive and the atlas — and without its metal/rough map it is a dielectric (see the header)
|
|
79
|
+
bool dyn = materialParams.probeInvSize.w > 0.5;
|
|
80
|
+
float metallic = 0.0;
|
|
81
|
+
float roughness = max(materialParams.roughnessFactor, 0.6);
|
|
82
|
+
float occ = 1.0;
|
|
83
|
+
if (dyn) {
|
|
84
|
+
vec2 mr = texture(materialParams_metallicRoughnessMap, getUV0()).bg;
|
|
85
|
+
metallic = materialParams.metallicFactor * mr.x;
|
|
86
|
+
roughness = materialParams.roughnessFactor * mr.y;
|
|
87
|
+
occ = 1.0 + materialParams.aoStrength * (texture(materialParams_occlusionMap, getUV0()).r - 1.0);
|
|
88
|
+
}
|
|
89
|
+
vec3 glow = texture(materialParams_emissiveMap, getUV0()).rgb * materialParams.emissiveFactor;
|
|
90
|
+
vec3 n = getWorldGeometricNormalVector();
|
|
91
|
+
vec3 ns = n;
|
|
92
|
+
if (dyn && materialParams.hasNormalMap > 0.5) {
|
|
93
|
+
vec3 tn = texture(materialParams_normalMap, getUV0()).xyz * 2.0 - 1.0;
|
|
94
|
+
tn.xy *= materialParams.normalScale;
|
|
95
|
+
// unlit has no vertex tangent frame (MATERIAL_NEEDS_TBN is lit-only): build one from the
|
|
96
|
+
// screen-space derivatives of position and uv0 (Schueler's cotangent frame)
|
|
97
|
+
highp vec3 p = getWorldPosition();
|
|
98
|
+
highp vec2 uv = getUV0();
|
|
99
|
+
highp vec3 dp1 = dFdx(p), dp2 = dFdy(p);
|
|
100
|
+
highp vec2 duv1 = dFdx(uv), duv2 = dFdy(uv);
|
|
101
|
+
highp vec3 dp2perp = cross(dp2, n), dp1perp = cross(n, dp1);
|
|
102
|
+
highp vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
|
|
103
|
+
highp vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
|
|
104
|
+
highp float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
|
|
105
|
+
ns = normalize(mat3(T * invmax, B * invmax, n) * tn);
|
|
106
|
+
}
|
|
107
|
+
vec3 v = getWorldViewVector();
|
|
108
|
+
float NoV = max(dot(ns, v), 1e-4);
|
|
109
|
+
vec3 luma = vec3(0.2126, 0.7152, 0.0722);
|
|
110
|
+
float lg = materialParams.lightGamma > 0.0 ? materialParams.lightGamma : 1.0;
|
|
111
|
+
|
|
112
|
+
// the baked lamps: the volume for a dynamic object, the atlas for a static — as lightmap.mat
|
|
113
|
+
vec2 lm;
|
|
114
|
+
vec3 baked;
|
|
115
|
+
vec3 l;
|
|
116
|
+
if (materialParams.probeInvSize.w > 0.5) {
|
|
117
|
+
vec3 p = getUserWorldPosition() + n * materialParams.probeMin.w;
|
|
118
|
+
vec3 uvw = (p - materialParams.probeMin.xyz) * materialParams.probeInvSize.xyz;
|
|
119
|
+
lm = texture(materialParams_probeVolume, uvw).rg;
|
|
120
|
+
float nz = materialParams.probeInvSize.w;
|
|
121
|
+
float wz = clamp(uvw.z, 0.5 / nz, 1.0 - 0.5 / nz);
|
|
122
|
+
vec3 fx = vec3(uvw.xy, ((ns.x >= 0.0 ? 0.0 : 1.0) + wz) / 6.0);
|
|
123
|
+
vec3 fy = vec3(uvw.xy, ((ns.y >= 0.0 ? 2.0 : 3.0) + wz) / 6.0);
|
|
124
|
+
vec3 fz = vec3(uvw.xy, ((ns.z >= 0.0 ? 4.0 : 5.0) + wz) / 6.0);
|
|
125
|
+
vec3 ex = pow(texture(materialParams_probeLight, fx).rgb, vec3(lg));
|
|
126
|
+
vec3 ey = pow(texture(materialParams_probeLight, fy).rgb, vec3(lg));
|
|
127
|
+
vec3 ez = pow(texture(materialParams_probeLight, fz).rgb, vec3(lg));
|
|
128
|
+
vec3 w2 = ns * ns;
|
|
129
|
+
baked = (w2.x * ex + w2.y * ey + w2.z * ez) * materialParams.lightScale;
|
|
130
|
+
vec3 dirE = vec3(sign(ns.x) * dot(ex, luma), sign(ns.y) * dot(ey, luma), sign(ns.z) * dot(ez, luma));
|
|
131
|
+
l = dot(dirE, dirE) > 1e-12 ? normalize(dirE) : ns;
|
|
132
|
+
} else {
|
|
133
|
+
vec2 luv = getUV1() * materialParams.lightmapST.xy + materialParams.lightmapST.zw;
|
|
134
|
+
lm = texture(materialParams_lightmap, luv).rg;
|
|
135
|
+
baked = pow(texture(materialParams_lightmapLight, luv).rgb, vec3(lg)) * materialParams.lightScale;
|
|
136
|
+
l = ns;
|
|
137
|
+
}
|
|
138
|
+
lm.g = mix(1.0, lm.g, materialParams.bakedAo);
|
|
139
|
+
float ao = lm.g * occ;
|
|
140
|
+
float vis = mix(1.0, lm.r, materialParams.sunStrength);
|
|
141
|
+
|
|
142
|
+
vec3 diffuseColor = base.rgb * (1.0 - metallic);
|
|
143
|
+
vec3 f0 = mix(vec3(0.04), base.rgb, metallic);
|
|
144
|
+
vec3 E = envBrdfApprox(f0, roughness, NoV);
|
|
145
|
+
|
|
146
|
+
vec3 ambD = diffuseColor * shIrradiance(ns) * (1.0 - E) * ao * frameUniforms.iblLuminance;
|
|
147
|
+
vec3 ambS = E * shIrradiance(reflect(-v, ns)) * ao * frameUniforms.iblLuminance;
|
|
148
|
+
vec3 color = (ambD + ambS) * materialParams.ambientScale;
|
|
149
|
+
|
|
150
|
+
// the sun (a level with one): Lambert × the atlas' visibility mask; pre-exposed like filament's own
|
|
151
|
+
float sunW = frameUniforms.lightColorIntensity.w;
|
|
152
|
+
if (sunW > 0.0) {
|
|
153
|
+
float NoL = max(dot(ns, frameUniforms.lightDirection), 0.0);
|
|
154
|
+
color += diffuseColor * frameUniforms.lightColorIntensity.rgb * (sunW * NoL * vis / PI);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// the baked lamps: diffuse + the cheap specular lobe lightmap.mat also uses (physical units × exposure)
|
|
158
|
+
{
|
|
159
|
+
vec3 h = normalize(v + l);
|
|
160
|
+
float NoL = max(dot(ns, l), 0.0);
|
|
161
|
+
float NoH = max(dot(ns, h), 0.0);
|
|
162
|
+
float LoH = max(dot(l, h), 0.0);
|
|
163
|
+
float pr = max(roughness, 0.5);
|
|
164
|
+
float a2 = pr * pr;
|
|
165
|
+
a2 *= a2;
|
|
166
|
+
float dd = NoH * NoH * (a2 - 1.0) + 1.0;
|
|
167
|
+
float D = a2 / (PI * dd * dd);
|
|
168
|
+
float V = 0.5 / max(NoL * sqrt(NoV * NoV * (1.0 - a2) + a2) + NoV * sqrt(NoL * NoL * (1.0 - a2) + a2), 1e-4);
|
|
169
|
+
vec3 F = f0 + (1.0 - f0) * pow(1.0 - LoH, 5.0);
|
|
170
|
+
color += (diffuseColor / PI + D * V * F) * baked * getExposure();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
material.baseColor = vec4(color + glow, 1.0);
|
|
174
|
+
}
|
|
175
|
+
}
|