@pluto-engine/vite-plugin-wgsl 1.0.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/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +152 -0
- package/dist/index.mjs +127 -0
- package/package.json +23 -0
- package/src/index.ts +150 -0
- package/test.ts +5 -0
- package/tsconfig.json +8 -0
package/dist/index.d.mts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => wgslPlugin,
|
|
24
|
+
transpileWGSLtoGLSL: () => transpileWGSLtoGLSL
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
function transpileWGSLtoGLSL(wgsl) {
|
|
28
|
+
const vertexMatch = wgsl.match(
|
|
29
|
+
/@vertex\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*(?:->\s*([a-zA-Z0-9_]+))?\s*\{([\s\S]*?)\n\}/
|
|
30
|
+
);
|
|
31
|
+
const fragMatch = wgsl.match(
|
|
32
|
+
/@fragment\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*->\s*(.*?)\s*\{([\s\S]*?)\n\}/
|
|
33
|
+
);
|
|
34
|
+
if (!vertexMatch || !fragMatch) {
|
|
35
|
+
console.log("Vertex:", !!vertexMatch, "Frag:", !!fragMatch);
|
|
36
|
+
throw new Error("Failed to parse WGSL vertex or fragment block");
|
|
37
|
+
}
|
|
38
|
+
const structMatch = wgsl.match(/struct\s+VertexOutput\s*\{([\s\S]*?)\}/);
|
|
39
|
+
const varyings = [];
|
|
40
|
+
if (structMatch) {
|
|
41
|
+
const lines = structMatch[1].split("\n");
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
if (!line.trim() || line.includes("@builtin(position)")) continue;
|
|
44
|
+
const m = line.match(/@location\(\d+\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
45
|
+
if (m) {
|
|
46
|
+
varyings.push({ name: m[1], type: convertType(m[2]) });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function convertType(t) {
|
|
51
|
+
if (t === "vec2<f32>" || t === "vec2f") return "vec2";
|
|
52
|
+
if (t === "vec3<f32>" || t === "vec3f") return "vec3";
|
|
53
|
+
if (t === "vec4<f32>" || t === "vec4f") return "vec4";
|
|
54
|
+
if (t === "mat4x4<f32>" || t === "mat4x4f") return "mat4";
|
|
55
|
+
if (t === "f32") return "float";
|
|
56
|
+
if (t === "i32") return "int";
|
|
57
|
+
if (t === "u32") return "uint";
|
|
58
|
+
return t;
|
|
59
|
+
}
|
|
60
|
+
const globals = [];
|
|
61
|
+
const globalRegex = /@group\(\d+\)\s*@binding\(\d+\)\s*var(?:<uniform>)?\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+);/g;
|
|
62
|
+
let gm;
|
|
63
|
+
while ((gm = globalRegex.exec(wgsl)) !== null) {
|
|
64
|
+
const name = gm[1];
|
|
65
|
+
const type = gm[2];
|
|
66
|
+
if (type.includes("texture_2d_array")) {
|
|
67
|
+
globals.push(`uniform sampler2DArray ${name};`);
|
|
68
|
+
} else if (type === "sampler") {
|
|
69
|
+
} else {
|
|
70
|
+
globals.push(`uniform ${convertType(type)} ${name};`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const vertInputArgs = vertexMatch[2].split(",").map((s) => s.trim()).filter(Boolean);
|
|
74
|
+
const vertAttrs = [];
|
|
75
|
+
for (const arg of vertInputArgs) {
|
|
76
|
+
const m = arg.match(/@location\((\d+)\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
77
|
+
if (m) {
|
|
78
|
+
vertAttrs.push(`layout(location = ${m[1]}) in ${convertType(m[3])} ${m[2]};`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let vertBody = vertexMatch[4];
|
|
82
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec2<f32>/g, "vec2 $1 = vec2");
|
|
83
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec3<f32>/g, "vec3 $1 = vec3");
|
|
84
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec4<f32>/g, "vec4 $1 = vec4");
|
|
85
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, (_, name) => `${name} = `);
|
|
86
|
+
vertBody = vertBody.replace(/vec2<f32>/g, "vec2");
|
|
87
|
+
vertBody = vertBody.replace(/vec3<f32>/g, "vec3");
|
|
88
|
+
vertBody = vertBody.replace(/vec4<f32>/g, "vec4");
|
|
89
|
+
vertBody = vertBody.replace(/var\s+out\s*:\s*VertexOutput;/g, "");
|
|
90
|
+
vertBody = vertBody.replace(/out\.position/g, "gl_Position");
|
|
91
|
+
vertBody = vertBody.replace(/out\.([a-zA-Z0-9_]+)/g, "v_$1");
|
|
92
|
+
vertBody = vertBody.replace(/return\s+out;/g, "");
|
|
93
|
+
const vertVaryings = varyings.map((v) => `out ${v.type} v_${v.name};`).join("\n");
|
|
94
|
+
const glslVert = `#version 300 es
|
|
95
|
+
precision highp float;
|
|
96
|
+
|
|
97
|
+
${vertAttrs.join("\n")}
|
|
98
|
+
${globals.join("\n")}
|
|
99
|
+
|
|
100
|
+
${vertVaryings}
|
|
101
|
+
|
|
102
|
+
void main() {
|
|
103
|
+
${vertBody}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
let fragBody = fragMatch[4];
|
|
107
|
+
fragBody = fragBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, "vec4 $1 = ");
|
|
108
|
+
fragBody = fragBody.replace(
|
|
109
|
+
/textureSample\(([^,]+),\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/g,
|
|
110
|
+
"texture($1, vec3($2, float($3)))"
|
|
111
|
+
);
|
|
112
|
+
fragBody = fragBody.replace(/in\.([a-zA-Z0-9_]+)/g, "v_$1");
|
|
113
|
+
fragBody = fragBody.replace(/return\s+(.*?);/g, "fragColor = $1;");
|
|
114
|
+
fragBody = fragBody.replace(/i32\((.*?)\)/g, "int($1)");
|
|
115
|
+
const fragVaryings = varyings.map((v) => `in ${v.type} v_${v.name};`).join("\n");
|
|
116
|
+
const glslFrag = `#version 300 es
|
|
117
|
+
precision highp float;
|
|
118
|
+
precision highp sampler2DArray;
|
|
119
|
+
|
|
120
|
+
${globals.join("\n")}
|
|
121
|
+
${fragVaryings}
|
|
122
|
+
|
|
123
|
+
out vec4 fragColor;
|
|
124
|
+
|
|
125
|
+
void main() {
|
|
126
|
+
${fragBody}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
return { vert: glslVert, frag: glslFrag };
|
|
130
|
+
}
|
|
131
|
+
function wgslPlugin() {
|
|
132
|
+
return {
|
|
133
|
+
name: "vite-plugin-wgsl",
|
|
134
|
+
transform(code, id) {
|
|
135
|
+
if (id.endsWith(".wgsl")) {
|
|
136
|
+
const glsl = transpileWGSLtoGLSL(code);
|
|
137
|
+
return {
|
|
138
|
+
code: `
|
|
139
|
+
export const wgsl = ${JSON.stringify(code)};
|
|
140
|
+
export const glsl = ${JSON.stringify(glsl)};
|
|
141
|
+
export default wgsl;
|
|
142
|
+
`,
|
|
143
|
+
map: null
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
150
|
+
0 && (module.exports = {
|
|
151
|
+
transpileWGSLtoGLSL
|
|
152
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function transpileWGSLtoGLSL(wgsl) {
|
|
3
|
+
const vertexMatch = wgsl.match(
|
|
4
|
+
/@vertex\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*(?:->\s*([a-zA-Z0-9_]+))?\s*\{([\s\S]*?)\n\}/
|
|
5
|
+
);
|
|
6
|
+
const fragMatch = wgsl.match(
|
|
7
|
+
/@fragment\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*->\s*(.*?)\s*\{([\s\S]*?)\n\}/
|
|
8
|
+
);
|
|
9
|
+
if (!vertexMatch || !fragMatch) {
|
|
10
|
+
console.log("Vertex:", !!vertexMatch, "Frag:", !!fragMatch);
|
|
11
|
+
throw new Error("Failed to parse WGSL vertex or fragment block");
|
|
12
|
+
}
|
|
13
|
+
const structMatch = wgsl.match(/struct\s+VertexOutput\s*\{([\s\S]*?)\}/);
|
|
14
|
+
const varyings = [];
|
|
15
|
+
if (structMatch) {
|
|
16
|
+
const lines = structMatch[1].split("\n");
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
if (!line.trim() || line.includes("@builtin(position)")) continue;
|
|
19
|
+
const m = line.match(/@location\(\d+\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
20
|
+
if (m) {
|
|
21
|
+
varyings.push({ name: m[1], type: convertType(m[2]) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function convertType(t) {
|
|
26
|
+
if (t === "vec2<f32>" || t === "vec2f") return "vec2";
|
|
27
|
+
if (t === "vec3<f32>" || t === "vec3f") return "vec3";
|
|
28
|
+
if (t === "vec4<f32>" || t === "vec4f") return "vec4";
|
|
29
|
+
if (t === "mat4x4<f32>" || t === "mat4x4f") return "mat4";
|
|
30
|
+
if (t === "f32") return "float";
|
|
31
|
+
if (t === "i32") return "int";
|
|
32
|
+
if (t === "u32") return "uint";
|
|
33
|
+
return t;
|
|
34
|
+
}
|
|
35
|
+
const globals = [];
|
|
36
|
+
const globalRegex = /@group\(\d+\)\s*@binding\(\d+\)\s*var(?:<uniform>)?\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+);/g;
|
|
37
|
+
let gm;
|
|
38
|
+
while ((gm = globalRegex.exec(wgsl)) !== null) {
|
|
39
|
+
const name = gm[1];
|
|
40
|
+
const type = gm[2];
|
|
41
|
+
if (type.includes("texture_2d_array")) {
|
|
42
|
+
globals.push(`uniform sampler2DArray ${name};`);
|
|
43
|
+
} else if (type === "sampler") {
|
|
44
|
+
} else {
|
|
45
|
+
globals.push(`uniform ${convertType(type)} ${name};`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const vertInputArgs = vertexMatch[2].split(",").map((s) => s.trim()).filter(Boolean);
|
|
49
|
+
const vertAttrs = [];
|
|
50
|
+
for (const arg of vertInputArgs) {
|
|
51
|
+
const m = arg.match(/@location\((\d+)\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
52
|
+
if (m) {
|
|
53
|
+
vertAttrs.push(`layout(location = ${m[1]}) in ${convertType(m[3])} ${m[2]};`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let vertBody = vertexMatch[4];
|
|
57
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec2<f32>/g, "vec2 $1 = vec2");
|
|
58
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec3<f32>/g, "vec3 $1 = vec3");
|
|
59
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec4<f32>/g, "vec4 $1 = vec4");
|
|
60
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, (_, name) => `${name} = `);
|
|
61
|
+
vertBody = vertBody.replace(/vec2<f32>/g, "vec2");
|
|
62
|
+
vertBody = vertBody.replace(/vec3<f32>/g, "vec3");
|
|
63
|
+
vertBody = vertBody.replace(/vec4<f32>/g, "vec4");
|
|
64
|
+
vertBody = vertBody.replace(/var\s+out\s*:\s*VertexOutput;/g, "");
|
|
65
|
+
vertBody = vertBody.replace(/out\.position/g, "gl_Position");
|
|
66
|
+
vertBody = vertBody.replace(/out\.([a-zA-Z0-9_]+)/g, "v_$1");
|
|
67
|
+
vertBody = vertBody.replace(/return\s+out;/g, "");
|
|
68
|
+
const vertVaryings = varyings.map((v) => `out ${v.type} v_${v.name};`).join("\n");
|
|
69
|
+
const glslVert = `#version 300 es
|
|
70
|
+
precision highp float;
|
|
71
|
+
|
|
72
|
+
${vertAttrs.join("\n")}
|
|
73
|
+
${globals.join("\n")}
|
|
74
|
+
|
|
75
|
+
${vertVaryings}
|
|
76
|
+
|
|
77
|
+
void main() {
|
|
78
|
+
${vertBody}
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
let fragBody = fragMatch[4];
|
|
82
|
+
fragBody = fragBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, "vec4 $1 = ");
|
|
83
|
+
fragBody = fragBody.replace(
|
|
84
|
+
/textureSample\(([^,]+),\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/g,
|
|
85
|
+
"texture($1, vec3($2, float($3)))"
|
|
86
|
+
);
|
|
87
|
+
fragBody = fragBody.replace(/in\.([a-zA-Z0-9_]+)/g, "v_$1");
|
|
88
|
+
fragBody = fragBody.replace(/return\s+(.*?);/g, "fragColor = $1;");
|
|
89
|
+
fragBody = fragBody.replace(/i32\((.*?)\)/g, "int($1)");
|
|
90
|
+
const fragVaryings = varyings.map((v) => `in ${v.type} v_${v.name};`).join("\n");
|
|
91
|
+
const glslFrag = `#version 300 es
|
|
92
|
+
precision highp float;
|
|
93
|
+
precision highp sampler2DArray;
|
|
94
|
+
|
|
95
|
+
${globals.join("\n")}
|
|
96
|
+
${fragVaryings}
|
|
97
|
+
|
|
98
|
+
out vec4 fragColor;
|
|
99
|
+
|
|
100
|
+
void main() {
|
|
101
|
+
${fragBody}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
return { vert: glslVert, frag: glslFrag };
|
|
105
|
+
}
|
|
106
|
+
function wgslPlugin() {
|
|
107
|
+
return {
|
|
108
|
+
name: "vite-plugin-wgsl",
|
|
109
|
+
transform(code, id) {
|
|
110
|
+
if (id.endsWith(".wgsl")) {
|
|
111
|
+
const glsl = transpileWGSLtoGLSL(code);
|
|
112
|
+
return {
|
|
113
|
+
code: `
|
|
114
|
+
export const wgsl = ${JSON.stringify(code)};
|
|
115
|
+
export const glsl = ${JSON.stringify(glsl)};
|
|
116
|
+
export default wgsl;
|
|
117
|
+
`,
|
|
118
|
+
map: null
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
wgslPlugin as default,
|
|
126
|
+
transpileWGSLtoGLSL
|
|
127
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pluto-engine/vite-plugin-wgsl",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vite plugin to load WGSL files and optionally transpile to GLSL",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
10
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
11
|
+
"lint": "biome check src",
|
|
12
|
+
"format": "biome format --write src"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"vite": "^5.0.0",
|
|
17
|
+
"tsup": "^8.0.0",
|
|
18
|
+
"typescript": "^5.0.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
export function transpileWGSLtoGLSL(wgsl: string): { vert: string; frag: string } {
|
|
4
|
+
// Extract Vertex & Fragment blocks
|
|
5
|
+
const vertexMatch = wgsl.match(
|
|
6
|
+
/@vertex\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*(?:->\s*([a-zA-Z0-9_]+))?\s*\{([\s\S]*?)\n\}/,
|
|
7
|
+
);
|
|
8
|
+
const fragMatch = wgsl.match(
|
|
9
|
+
/@fragment\s*fn\s+([a-zA-Z0-9_]+)\s*\(([\s\S]*?)\)\s*->\s*(.*?)\s*\{([\s\S]*?)\n\}/,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (!vertexMatch || !fragMatch) {
|
|
13
|
+
console.log('Vertex:', !!vertexMatch, 'Frag:', !!fragMatch);
|
|
14
|
+
throw new Error('Failed to parse WGSL vertex or fragment block');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Parse struct for VertexOutput
|
|
18
|
+
const structMatch = wgsl.match(/struct\s+VertexOutput\s*\{([\s\S]*?)\}/);
|
|
19
|
+
const varyings: { name: string; type: string }[] = [];
|
|
20
|
+
if (structMatch) {
|
|
21
|
+
const lines = structMatch[1].split('\n');
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
if (!line.trim() || line.includes('@builtin(position)')) continue;
|
|
24
|
+
const m = line.match(/@location\(\d+\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
25
|
+
if (m) {
|
|
26
|
+
varyings.push({ name: m[1], type: convertType(m[2]) });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function convertType(t: string): string {
|
|
32
|
+
if (t === 'vec2<f32>' || t === 'vec2f') return 'vec2';
|
|
33
|
+
if (t === 'vec3<f32>' || t === 'vec3f') return 'vec3';
|
|
34
|
+
if (t === 'vec4<f32>' || t === 'vec4f') return 'vec4';
|
|
35
|
+
if (t === 'mat4x4<f32>' || t === 'mat4x4f') return 'mat4';
|
|
36
|
+
if (t === 'f32') return 'float';
|
|
37
|
+
if (t === 'i32') return 'int';
|
|
38
|
+
if (t === 'u32') return 'uint';
|
|
39
|
+
return t;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse globals (uniforms, textures)
|
|
43
|
+
const globals: string[] = [];
|
|
44
|
+
const globalRegex =
|
|
45
|
+
/@group\(\d+\)\s*@binding\(\d+\)\s*var(?:<uniform>)?\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+);/g;
|
|
46
|
+
let gm;
|
|
47
|
+
while ((gm = globalRegex.exec(wgsl)) !== null) {
|
|
48
|
+
const name = gm[1];
|
|
49
|
+
const type = gm[2];
|
|
50
|
+
if (type.includes('texture_2d_array')) {
|
|
51
|
+
globals.push(`uniform sampler2DArray ${name};`);
|
|
52
|
+
} else if (type === 'sampler') {
|
|
53
|
+
// GLSL combines texture and sampler, skip
|
|
54
|
+
} else {
|
|
55
|
+
globals.push(`uniform ${convertType(type)} ${name};`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build Vertex Shader
|
|
60
|
+
const vertInputArgs = vertexMatch[2]
|
|
61
|
+
.split(',')
|
|
62
|
+
.map((s) => s.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
const vertAttrs: string[] = [];
|
|
65
|
+
for (const arg of vertInputArgs) {
|
|
66
|
+
const m = arg.match(/@location\((\d+)\)\s*([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)/);
|
|
67
|
+
if (m) {
|
|
68
|
+
vertAttrs.push(`layout(location = ${m[1]}) in ${convertType(m[3])} ${m[2]};`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let vertBody = vertexMatch[4];
|
|
73
|
+
// Naive replacement for `let x = vec2<f32>(...)` -> `vec2 x = vec2(...)`
|
|
74
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec2<f32>/g, 'vec2 $1 = vec2');
|
|
75
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec3<f32>/g, 'vec3 $1 = vec3');
|
|
76
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*vec4<f32>/g, 'vec4 $1 = vec4');
|
|
77
|
+
vertBody = vertBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, (_, name) => `${name} = `); // Need types for lets in GLSL...
|
|
78
|
+
vertBody = vertBody.replace(/vec2<f32>/g, 'vec2');
|
|
79
|
+
vertBody = vertBody.replace(/vec3<f32>/g, 'vec3');
|
|
80
|
+
vertBody = vertBody.replace(/vec4<f32>/g, 'vec4');
|
|
81
|
+
|
|
82
|
+
// Handle `out.xxx = ...`
|
|
83
|
+
vertBody = vertBody.replace(/var\s+out\s*:\s*VertexOutput;/g, '');
|
|
84
|
+
vertBody = vertBody.replace(/out\.position/g, 'gl_Position');
|
|
85
|
+
vertBody = vertBody.replace(/out\.([a-zA-Z0-9_]+)/g, 'v_$1');
|
|
86
|
+
vertBody = vertBody.replace(/return\s+out;/g, '');
|
|
87
|
+
|
|
88
|
+
const vertVaryings = varyings.map((v) => `out ${v.type} v_${v.name};`).join('\n');
|
|
89
|
+
|
|
90
|
+
const glslVert = `#version 300 es
|
|
91
|
+
precision highp float;
|
|
92
|
+
|
|
93
|
+
${vertAttrs.join('\n')}
|
|
94
|
+
${globals.join('\n')}
|
|
95
|
+
|
|
96
|
+
${vertVaryings}
|
|
97
|
+
|
|
98
|
+
void main() {
|
|
99
|
+
${vertBody}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
// Build Fragment Shader
|
|
104
|
+
let fragBody = fragMatch[4];
|
|
105
|
+
fragBody = fragBody.replace(/let\s+([a-zA-Z0-9_]+)\s*=\s*/g, 'vec4 $1 = '); // assuming color
|
|
106
|
+
fragBody = fragBody.replace(
|
|
107
|
+
/textureSample\(([^,]+),\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/g,
|
|
108
|
+
'texture($1, vec3($2, float($3)))',
|
|
109
|
+
); // 2D array texture
|
|
110
|
+
fragBody = fragBody.replace(/in\.([a-zA-Z0-9_]+)/g, 'v_$1');
|
|
111
|
+
fragBody = fragBody.replace(/return\s+(.*?);/g, 'fragColor = $1;');
|
|
112
|
+
fragBody = fragBody.replace(/i32\((.*?)\)/g, 'int($1)');
|
|
113
|
+
|
|
114
|
+
const fragVaryings = varyings.map((v) => `in ${v.type} v_${v.name};`).join('\n');
|
|
115
|
+
|
|
116
|
+
const glslFrag = `#version 300 es
|
|
117
|
+
precision highp float;
|
|
118
|
+
precision highp sampler2DArray;
|
|
119
|
+
|
|
120
|
+
${globals.join('\n')}
|
|
121
|
+
${fragVaryings}
|
|
122
|
+
|
|
123
|
+
out vec4 fragColor;
|
|
124
|
+
|
|
125
|
+
void main() {
|
|
126
|
+
${fragBody}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
return { vert: glslVert, frag: glslFrag };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default function wgslPlugin(): Plugin {
|
|
134
|
+
return {
|
|
135
|
+
name: 'vite-plugin-wgsl',
|
|
136
|
+
transform(code, id) {
|
|
137
|
+
if (id.endsWith('.wgsl')) {
|
|
138
|
+
const glsl = transpileWGSLtoGLSL(code);
|
|
139
|
+
return {
|
|
140
|
+
code: `
|
|
141
|
+
export const wgsl = ${JSON.stringify(code)};
|
|
142
|
+
export const glsl = ${JSON.stringify(glsl)};
|
|
143
|
+
export default wgsl;
|
|
144
|
+
`,
|
|
145
|
+
map: null,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
package/test.ts
ADDED