lecodes-cli 0.19.2 → 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.
Files changed (59) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1324 -658
  3. package/package.json +9 -9
  4. package/runtime/materials/decal-relief.mat +178 -0
  5. package/runtime/materials/decal.mat +172 -0
  6. package/runtime/materials/lightmap-baked-lite.mat +175 -0
  7. package/runtime/materials/lightmap-baked.mat +176 -0
  8. package/runtime/materials/lightmap.mat +181 -0
  9. package/runtime/materials/lit.mat +43 -0
  10. package/runtime/materials/particles-quad.mat +171 -0
  11. package/runtime/materials/particles.mat +121 -0
  12. package/runtime/materials/shadow.mat +21 -0
  13. package/runtime/materials/terrain.mat +134 -0
  14. package/runtime/materials/unlit-transparent.mat +42 -0
  15. package/runtime/materials/unlit.mat +44 -0
  16. package/runtime/materials/video.mat +31 -0
  17. package/runtime/sdk-types.json +1 -1
  18. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  19. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  20. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  21. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  22. package/runtime/web/assets/createViewerLite-C_bkKXLg.js +852 -0
  23. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  24. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  25. package/runtime/web/assets/index-BTB5KV9x.js +2 -0
  26. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  27. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  28. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  29. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  30. package/runtime/web/embed.html +32 -0
  31. package/runtime/web/embed.js +171 -0
  32. package/src/cmgenTool.ts +17 -9
  33. package/src/commands/app.ts +6 -1
  34. package/src/commands/appDesktop.ts +27 -4
  35. package/src/commands/appShared.ts +145 -3
  36. package/src/commands/appTemplates.ts +28 -4
  37. package/src/commands/assets.ts +3 -7
  38. package/src/commands/design.ts +9 -26
  39. package/src/commands/desktop.ts +9 -1
  40. package/src/commands/dev.ts +64 -0
  41. package/src/commands/projectTemplates.ts +17 -4
  42. package/src/commands/render.ts +9 -11
  43. package/src/commands/scene.ts +2 -6
  44. package/src/commands/shaders.ts +17 -1
  45. package/src/commands/shadersNew.ts +133 -0
  46. package/src/commands/test.ts +2 -1
  47. package/src/commands/thumbs.ts +3 -6
  48. package/src/commands/update.ts +42 -21
  49. package/src/desktopRenderer.ts +32 -2
  50. package/src/desktopScript.ts +1 -1
  51. package/src/dev/androidDev.ts +154 -0
  52. package/src/dev/devServer.ts +53 -6
  53. package/src/dev/webRunner.ts +22 -0
  54. package/src/index.ts +24 -4
  55. package/src/matcTool.ts +31 -19
  56. package/src/peerInstall.ts +160 -0
  57. package/src/peers.ts +48 -14
  58. package/src/projectEnv.ts +4 -0
  59. package/src/releases.ts +16 -0
@@ -0,0 +1,121 @@
1
+ // Default particle material — one universal point-sprite shader for every particle system
2
+ // (SDK: Material.particles(), the default when `new Particles({...})` gets no material).
3
+ //
4
+ // Per-particle inputs come from the particles.cpp vertex stream:
5
+ // COLOR — tint (the color/dynamicColor curves), multiplied into the texture
6
+ // CUSTOM0 — x: size (world units, diameter), y: rotation (radians),
7
+ // z: opacity, w: flipbook frame index
8
+ // Per-system looks are uniforms (all default 0; Material.particles() primes them):
9
+ // sheetSize — (cols, rows) of the flipbook sheet the texture is cut into. The shader trusts
10
+ // both components ≥ 1 (no divide-by-zero guard here) — the SDK clamps; (1,1) =
11
+ // single image. A raw material instance MUST prime this before drawing.
12
+ // emissive — extra brightness multiplier for a glow look (0 = off)
13
+ // additive — 0 = alpha compositing (smoke), 1 = additive (fire/sparks); fractional blends mix.
14
+ // Works inside `blending: fade` because Filament blends premultiplied
15
+ // (ONE, ONE_MINUS_SRC_ALPHA): we premultiply rgb ourselves and scale down the
16
+ // written alpha, so dst attenuation fades out as `additive` rises.
17
+ // softness — radial falloff so an untextured particle is a soft round dot, not a square
18
+ // (baseColorMap is host-primed to 1×1 white; the SDK sets softness=0 with a map)
19
+ //
20
+ // Sizing: perspScale (viewportHeightPx / (2·tan(fov/2))) is auto-published by the engine in
21
+ // material-global slot 3 (scene.cpp updateShaderGlobalParams), so gl_PointSize = size·scale/depth
22
+ // gives a true world-space diameter. Object vertex domain: particles ride the emitter's transform.
23
+
24
+ material {
25
+ name : particles,
26
+ shadingModel : unlit,
27
+ parameters : [
28
+ { type : sampler2d, name : baseColorMap },
29
+ { type : float2, name : sheetSize, precision : high },
30
+ { type : float, name : emissive },
31
+ { type : float, name : additive },
32
+ { type : float, name : softness },
33
+ { type : float, name : depthFade }
34
+ ],
35
+ // `custom` carries (size, rotation, opacity, FRAME) and the frame index feeds the atlas uv, so
36
+ // this varying must stay full precision — see the highp note in the fragment stage.
37
+ variables : [
38
+ { name : custom, precision : high }
39
+ ],
40
+ blending : fade,
41
+ culling : none,
42
+ requires: [ color, custom0 ]
43
+ }
44
+
45
+ vertex {
46
+ void materialVertex(inout MaterialVertexInputs material) {
47
+ float4 viewPos = getViewFromWorldMatrix() * material.worldPosition;
48
+ float z = max(-viewPos.z, 1e-3);
49
+ gl_PointSize = getCustom0().x * getMaterialGlobal3().x / z;
50
+ material.custom = getCustom0();
51
+ }
52
+ }
53
+
54
+ fragment {
55
+ void material(inout MaterialInputs material) {
56
+ prepareMaterial(material);
57
+
58
+ // EVERYTHING on the atlas-uv path is explicitly `highp`. matc's `-p mobile` profile lowers
59
+ // unqualified floats to `mediump`, which Apple GPUs really do execute as fp16 (desktop GL
60
+ // and desktop browsers quietly run mediump at fp32, which is why this only showed on iOS).
61
+ // In fp16, `cell + uv` for the last column lands in [7,8) where the ULP is 0.0039; divided
62
+ // by 8 that is ~1 whole texel of a 2048px sheet, so the sprite visibly stair-steps — and it
63
+ // degrades with the cell index (column 7 quantizes ~8x coarser than column 0).
64
+ highp float2 uv = gl_PointCoord.xy;
65
+
66
+ highp float rot = variable_custom.y;
67
+ if (rot != 0.0) {
68
+ highp float co = cos(rot);
69
+ highp float si = sin(rot);
70
+ highp float2 p = uv - float2(0.5, 0.5);
71
+ uv = float2(co * p.x - si * p.y, si * p.x + co * p.y) + float2(0.5, 0.5);
72
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
73
+ discard;
74
+ }
75
+ }
76
+
77
+ highp float2 sheet = materialParams.sheetSize;
78
+ highp float frame = floor(variable_custom.w);
79
+ // integer cell math — float `floor(frame / sheet.x)` lands one row early at every row start
80
+ // on the GPU (6 * (1/6) floors to 0); see particles-quad.mat
81
+ int sx = int(sheet.x + 0.5);
82
+ int fi = int(frame + 0.5);
83
+ int total_i = sx * int(sheet.y + 0.5);
84
+ fi = fi - (fi / total_i) * total_i;
85
+ highp float2 cell = float2(float(fi - (fi / sx) * sx), float(fi / sx));
86
+ highp float2 tuv = (cell + uv) / sheet;
87
+
88
+ float4 tex = texture(materialParams_baseColorMap, tuv);
89
+ float4 tint = getColor();
90
+
91
+ // soft round falloff from the (unrotated) sprite center
92
+ float radial = 1.0 - smoothstep(0.0, 0.5, length(gl_PointCoord.xy - float2(0.5, 0.5)));
93
+ float shape = mix(1.0, radial, saturate(materialParams.softness));
94
+
95
+ float alpha = tex.a * tint.a * variable_custom.z * shape;
96
+
97
+ // Soft particles (depthFade > 0, metres): fade the sprite out where it cuts into the scene
98
+ // geometry behind it. sampler0_structure is Filament's depth ("structure") buffer — bound
99
+ // to the colour pass by patch 0017 while creator-gl sees a drawn system with depthFade > 0
100
+ // (otherwise it is a 1x1 dummy, and this branch is off). The fragment is reprojected to
101
+ // render-target uv the way Filament's own SSR does (surface_light_indirect.fs), and both
102
+ // depths are linearized with the SSR linearizeDepth formula (reversed-Z, far at infinity).
103
+ // (not in the depth/shadow/picking variants: their per-view layout has no structure sampler)
104
+ #if !defined(VARIANT_DEPTH)
105
+ if (materialParams.depthFade > 0.0) {
106
+ highp vec3 wp = getWorldPosition();
107
+ highp vec4 clip = getClipFromWorldMatrix() * vec4(wp, 1.0);
108
+ highp vec2 suv = clip.xy * (0.5 / clip.w) + 0.5;
109
+ highp float sceneDepth = textureLod(sampler0_structure, uvToRenderTargetUV(suv), 0.0).r;
110
+ highp mat4 p = getViewFromClipMatrix();
111
+ highp float sceneZ = (sceneDepth * p[2].z + p[3].z) / max(sceneDepth * p[2].w + p[3].w, 1.0 / 16777216.0);
112
+ highp float fragZ = (getViewFromWorldMatrix() * vec4(wp, 1.0)).z;
113
+ // both are view-space z (negative ahead): fragZ - sceneZ = how far the scene is behind
114
+ alpha *= saturate((fragZ - sceneZ) / materialParams.depthFade);
115
+ }
116
+ #endif
117
+ float3 rgb = tex.rgb * tint.rgb * (1.0 + materialParams.emissive);
118
+
119
+ material.baseColor = float4(rgb * alpha, alpha * (1.0 - saturate(materialParams.additive)));
120
+ }
121
+ }
@@ -0,0 +1,21 @@
1
+ material {
2
+ name : "Invisible shadow plane",
3
+ shadingModel : unlit,
4
+ shadowMultiplier : true,
5
+ blending : transparent,
6
+ parameters : [
7
+ // Color and opacity of the projected shadow (RGBA)
8
+ {
9
+ type : float4,
10
+ name : color
11
+ }
12
+ ]
13
+ }
14
+
15
+ fragment {
16
+ void material(inout MaterialInputs material) {
17
+ prepareMaterial(material);
18
+ // baseColor defines the color and opacity of the final shadow
19
+ material.baseColor = materialParams.color;
20
+ }
21
+ }
@@ -0,0 +1,134 @@
1
+ // terrain — the splat-map ground material (docs/terrain-plan.md §1.5). Up to four PBR layers
2
+ // (albedo + optional normal map, per-layer tiling / roughness) blended by a control map's RGBA
3
+ // weights (one texel per height sample); a layer may sample triplanar (rock on cliffs). UV0 is the
4
+ // terrain's local XZ in metres (creator-gl/src/terrain.cpp writes it), so `tiling` reads as metres
5
+ // per repeat and the control map is addressed as (uv / cellSize + 0.5) / gridSize.
6
+ //
7
+ // Sampler budget: feature level 1 (every host, WebGL included) allows 9 samplers per material, so the
8
+ // normal maps come PACKED two per texture — normals01 = (layer 0 XY in RG, layer 1 XY in BA),
9
+ // normals23 likewise; Z is reconstructed. `lecodes assets` packs them; an unset pack (the shared
10
+ // 1x1 white) decodes to a flat normal, and normalScale 0 skips the read.
11
+ //
12
+ // Baked lighting: lightmap.mat's atlas block (R = sun visibility, G = AO on UV1 = the terrain's ONE
13
+ // atlas rect; the light volume for movers is not needed — a terrain never moves), so a baked level
14
+ // applies the atlas to this material directly, no swap. Every sampler defaults to the shared 1x1
15
+ // white (applyIdentityDefaults): an unset layer is white, an unset lightmap is "fully lit".
16
+ material {
17
+ name : terrain,
18
+ shadingModel : lit,
19
+ flipUV : false,
20
+ requires : [ uv0, uv1 ],
21
+ postLightingBlending : multiply,
22
+ parameters : [
23
+ { type : sampler2d, name : control },
24
+ { type : sampler2d, name : albedo0 },
25
+ { type : sampler2d, name : albedo1 },
26
+ { type : sampler2d, name : albedo2 },
27
+ { type : sampler2d, name : albedo3 },
28
+ { type : sampler2d, name : normals01 },
29
+ { type : sampler2d, name : normals23 },
30
+ // metres per texture repeat, per layer
31
+ { type : float4, name : tiling },
32
+ { type : float4, name : roughness },
33
+ // normal-map strength per layer; 0 = the layer has no normal map
34
+ { type : float4, name : normalScale },
35
+ // 1 = sample that layer triplanar in world space (its normal map is then skipped)
36
+ { type : float4, name : triplanar },
37
+ // samples along X and Z
38
+ { type : float2, name : gridSize },
39
+ { type : float, name : cellSize },
40
+ { type : float3, name : tint },
41
+ { type : sampler2d, name : lightmap },
42
+ // the point lights' baked irradiance, sRGB(E / lightScale), same rect; lightScale 0 = none (the 9th sampler)
43
+ { type : sampler2d, name : lightmapLight },
44
+ { type : float, name : lightScale },
45
+ // the light store's curve: texel = sRGB(pow(E / lightScale, 1 / lightGamma)); 0/1 = linear (v2 bakes), 2 = the square-root store
46
+ { type : float, name : lightGamma },
47
+ { type : float4, name : lightmapST },
48
+ { type : float, name : ambientScale },
49
+ { type : float, name : sunStrength },
50
+ // how much of the baked ambient occlusion applies (1 = all)
51
+ { type : float, name : bakedAo }
52
+ ]
53
+ }
54
+
55
+ fragment {
56
+ vec4 triplanarSample(sampler2D tex, vec3 p, vec3 bw, float s) {
57
+ return texture(tex, p.zy / s) * bw.x + texture(tex, p.xz / s) * bw.y + texture(tex, p.xy / s) * bw.z;
58
+ }
59
+
60
+ // `slot` 0 = this layer's normal XY sits in the pack's RG, 1 = in its BA
61
+ void splatLayer(sampler2D alb, sampler2D pack, float slot, float tiling, float nscale, float tri, float w,
62
+ vec2 uv, vec3 wp, vec3 bw, inout vec3 albedo, inout vec3 nrm) {
63
+ if (w <= 0.0) return;
64
+ float s = max(tiling, 1e-3);
65
+ if (tri > 0.5) {
66
+ albedo += triplanarSample(alb, wp, bw, s).rgb * w;
67
+ nrm += vec3(0.0, 0.0, 1.0) * w;
68
+ } else {
69
+ vec2 luv = uv / s;
70
+ albedo += texture(alb, luv).rgb * w;
71
+ if (nscale > 0.0) {
72
+ vec4 p = texture(pack, luv);
73
+ vec2 xy = (slot > 0.5 ? p.ba : p.rg) * 2.0 - 1.0;
74
+ xy *= nscale;
75
+ float z = sqrt(max(1.0 - dot(xy, xy), 0.0));
76
+ nrm += vec3(xy, z) * w;
77
+ } else {
78
+ nrm += vec3(0.0, 0.0, 1.0) * w;
79
+ }
80
+ }
81
+ }
82
+
83
+ void material(inout MaterialInputs material) {
84
+ vec2 uv = getUV0();
85
+ vec2 cuv = (uv / max(materialParams.cellSize, 1e-4) + 0.5) / max(materialParams.gridSize, vec2(1.0));
86
+ vec4 w = texture(materialParams_control, cuv);
87
+ float ws = w.r + w.g + w.b + w.a;
88
+ w = ws > 1e-4 ? w / ws : vec4(1.0, 0.0, 0.0, 0.0);
89
+
90
+ vec3 wp = getUserWorldPosition();
91
+ vec3 gn = getWorldGeometricNormalVector();
92
+ vec3 bw = abs(gn);
93
+ bw = bw / max(bw.x + bw.y + bw.z, 1e-4);
94
+
95
+ vec3 albedo = vec3(0.0);
96
+ vec3 nrm = vec3(0.0);
97
+ splatLayer(materialParams_albedo0, materialParams_normals01, 0.0, materialParams.tiling.x, materialParams.normalScale.x, materialParams.triplanar.x, w.r, uv, wp, bw, albedo, nrm);
98
+ splatLayer(materialParams_albedo1, materialParams_normals01, 1.0, materialParams.tiling.y, materialParams.normalScale.y, materialParams.triplanar.y, w.g, uv, wp, bw, albedo, nrm);
99
+ splatLayer(materialParams_albedo2, materialParams_normals23, 0.0, materialParams.tiling.z, materialParams.normalScale.z, materialParams.triplanar.z, w.b, uv, wp, bw, albedo, nrm);
100
+ splatLayer(materialParams_albedo3, materialParams_normals23, 1.0, materialParams.tiling.w, materialParams.normalScale.w, materialParams.triplanar.w, w.a, uv, wp, bw, albedo, nrm);
101
+ material.normal = normalize(nrm);
102
+ prepareMaterial(material);
103
+
104
+ material.baseColor = vec4(albedo * materialParams.tint, 1.0);
105
+ material.roughness = dot(materialParams.roughness, w);
106
+ material.metallic = 0.0;
107
+
108
+ // ---- baked lighting (lightmap.mat's atlas block) ----
109
+ vec3 n = gn;
110
+ vec2 luv = getUV1() * materialParams.lightmapST.xy + materialParams.lightmapST.zw;
111
+ vec2 lm = texture(materialParams_lightmap, luv).rg;
112
+ lm.g = mix(1.0, lm.g, materialParams.bakedAo);
113
+ material.ambientOcclusion = lm.g;
114
+ // the point lights' baked diffuse (lightmap.mat's emissive path; the terrain has no glTF emissive)
115
+ float lg = materialParams.lightGamma > 0.0 ? materialParams.lightGamma : 1.0;
116
+ vec3 baked = pow(texture(materialParams_lightmapLight, luv).rgb, vec3(lg)) * materialParams.lightScale;
117
+ material.emissive = vec4(material.baseColor.rgb * baked * (getExposure() / PI), 0.0);
118
+
119
+ float vis = mix(1.0, lm.r, materialParams.sunStrength);
120
+ vec3 sh = frameUniforms.iblSH[0].xyz
121
+ + frameUniforms.iblSH[1].xyz * n.y + frameUniforms.iblSH[2].xyz * n.z + frameUniforms.iblSH[3].xyz * n.x
122
+ + frameUniforms.iblSH[4].xyz * (n.y * n.x) + frameUniforms.iblSH[5].xyz * (n.y * n.z)
123
+ + frameUniforms.iblSH[6].xyz * (3.0 * n.z * n.z - 1.0) + frameUniforms.iblSH[7].xyz * (n.z * n.x)
124
+ + frameUniforms.iblSH[8].xyz * (n.x * n.x - n.y * n.y);
125
+ vec3 luma = vec3(0.2126, 0.7152, 0.0722);
126
+ float amb = max(dot(max(sh, 0.0), luma), 0.0) * frameUniforms.iblLuminance * materialParams.ambientScale * lm.g;
127
+ float nol = max(dot(n, frameUniforms.lightDirection), 0.0);
128
+ float sun = dot(frameUniforms.lightColorIntensity.rgb, luma) * frameUniforms.lightColorIntensity.w * nol / PI;
129
+ // the baked lamps ride the emissive slot the multiply scales too: same share on both sides
130
+ float lamp = dot(baked, luma) * (getExposure() / PI);
131
+ float k = (amb + sun * vis + lamp) / max(amb + sun + lamp, 1e-6);
132
+ material.postLightingColor = vec4(k, k, k, 1.0);
133
+ }
134
+ }
@@ -0,0 +1,42 @@
1
+ // Unlit, ALPHA-BLENDED: the twin of unlit.mat for `Material.unlit({ transparent: true })`.
2
+ //
3
+ // Two differences from unlit.mat, and both are why this is a separate source rather than the same
4
+ // shader compiled twice — blending is baked in by matc (a MaterialInstance cannot change it):
5
+ //
6
+ // 1. baseColor is a float4, so the colour carries its own alpha (`color: "#ffffff80"`). The opaque
7
+ // material keeps a float3, where an alpha would be a lie.
8
+ // 2. the result is PREMULTIPLIED here. Filament's `blending : transparent` expects premultiplied
9
+ // source, and neither input is: creator-gl sends the colour as RgbaType::PREMULTIPLIED_sRGB,
10
+ // which only converts sRGB→linear and does NOT multiply (Color.h), and a decoded PNG is
11
+ // straight alpha too. So multiply once, at the end. (The pre-2026-08 unlit blended without
12
+ // this step, which is what made half-transparent textures read too bright at the edges.)
13
+ //
14
+ // Keep the colour × map maths in step with unlit.mat.
15
+
16
+ material {
17
+ name : unlit_transparent,
18
+ shadingModel : unlit,
19
+ parameters : [
20
+ // baseColor multiplies baseColorMap (both white-primed by the host — see lit.mat), alpha included
21
+ {
22
+ type : float4,
23
+ name : baseColor
24
+ },
25
+ {
26
+ type : sampler2d,
27
+ name : baseColorMap
28
+ }
29
+ ],
30
+ blending : transparent,
31
+ requires: [
32
+ uv0
33
+ ]
34
+ }
35
+
36
+ fragment {
37
+ void material(inout MaterialInputs material) {
38
+ prepareMaterial(material);
39
+ vec4 base = texture(materialParams_baseColorMap, getUV0()) * materialParams.baseColor;
40
+ material.baseColor = vec4(base.rgb * base.a, base.a);
41
+ }
42
+ }
@@ -0,0 +1,44 @@
1
+ // Unlit (flat) material: baseColor × baseColorMap, OPAQUE.
2
+ //
3
+ // Opaque is the default for the same reason lit.mat is: a blended draw writes no depth, is sorted
4
+ // back-to-front by centre, and Filament drops it from the shadow pass (RenderPass.cpp's
5
+ // filterTranslucentObjects) — so a "flat colour" cube used to occlude nothing and cast no shadow.
6
+ // Alpha is meaningless here: baseColor is a float3 and the map's alpha channel is ignored.
7
+ //
8
+ // The blended twin is unlit-transparent.mat (float4 baseColor, premultiplied). It is a SEPARATE
9
+ // source, not a variant: blending has to be baked in at compile time (a MaterialInstance can't
10
+ // change it), and the two shaders differ in the colour type and the premultiply anyway. Keep an
11
+ // edit to the colour/map maths in step across both — they are ten lines each, side by side.
12
+ //
13
+ // baseColorMap is a plain sampler2d on EVERY target. Apple used to compile its own variant with a
14
+ // samplerExternal map (plus a `useBaseColorMap` bool to branch on), which made Material.unlit refuse
15
+ // an ordinary texture there — filament rejects a SAMPLER_2D bound to an external parameter. Camera
16
+ // passthrough has its own camera.mat / camera_obj.mat, so that split is gone: one source, one
17
+ // behaviour, and the flag with it (nothing in the SDK has emitted `useBaseColorMap` for a while).
18
+
19
+ material {
20
+ name : unlit,
21
+ shadingModel : unlit,
22
+ parameters : [
23
+ // baseColor multiplies baseColorMap (white-primed by the host) — see lit.mat
24
+ {
25
+ type : float3,
26
+ name : baseColor
27
+ },
28
+ {
29
+ type : sampler2d,
30
+ name : baseColorMap
31
+ }
32
+ ],
33
+ requires: [
34
+ uv0
35
+ ]
36
+ }
37
+
38
+ fragment {
39
+ void material(inout MaterialInputs material) {
40
+ prepareMaterial(material);
41
+ material.baseColor = texture(materialParams_baseColorMap, getUV0());
42
+ material.baseColor.rgb *= materialParams.baseColor;
43
+ }
44
+ }
@@ -0,0 +1,31 @@
1
+ // Video material — samples the frame a media player hands the engine as a texture.
2
+ //
3
+ // The sampler is EXTERNAL because that is what the platforms which actually play video give us:
4
+ // Android a SurfaceTexture (GL_TEXTURE_EXTERNAL_OES), Apple an AVPlayerItemVideoOutput texture.
5
+ // Desktop OpenGL has no external samplers and uploads frames as ordinary RGBA8 instead — it has no
6
+ // video decode yet (parity `gl-media-texture`: desktop = todo), so the desktop build compiles this
7
+ // material only to keep one source per target; whoever wires video up there decides then whether
8
+ // the desktop blob needs its own sampler2d variant.
9
+
10
+ material {
11
+ name : video,
12
+ shadingModel : unlit,
13
+ parameters : [
14
+ {
15
+ type : samplerExternal,
16
+ name : baseColorMap
17
+ }
18
+ ],
19
+ requires: [
20
+ uv0
21
+ ]
22
+ }
23
+
24
+ fragment {
25
+ void material(inout MaterialInputs material) {
26
+ prepareMaterial(material);
27
+ // inverseTonemapSRGB round-trips the View's tonemapper, so the decoded frame displays with
28
+ // its true colors instead of washed-out/over-bright.
29
+ material.baseColor.rgb = inverseTonemapSRGB(texture(materialParams_baseColorMap, getUV0()).rgb);
30
+ }
31
+ }