├── .gitignore
├── .idea
├── .gitignore
├── .name
├── ShaderPeeper.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── babel.config.js
├── package.json
├── src
├── ShaderPeeper_Core
│ ├── Controller
│ │ ├── GLSLPeepAnalyzer.ts
│ │ ├── GLSLPeepCodeGenerator.ts
│ │ └── ICodeAnalyzer.ts
│ └── Data
│ │ ├── CursorPos.ts
│ │ ├── DefinitionData.ts
│ │ ├── PeepAnalyzeResult.ts
│ │ ├── ShaderDepthData.ts
│ │ ├── ShaderPeeperRegex.ts
│ │ └── VariableType.ts
└── ShaderPeeper_Test
│ ├── analyze.test.ts
│ └── sample.test.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /node_modules/
3 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | ShaderPeeper
--------------------------------------------------------------------------------
/.idea/ShaderPeeper.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GLSLibrary
2 |
3 |
4 |
5 | ## About
6 | ShaderPeeper is the package that generate variable-rendering glsl code.
7 | It enable us debugging parameter in main function.
8 | ## Feature
9 | - enable us to renering-debug of variable in main function.
10 | - generate glsl-code which render variable with cursor position.
11 | [](https://gyazo.com/45010fc431f1a978c901d727a77ae55c)
12 | ## Usage
13 | - Install
14 | ```bash
15 | yarn add https://github.com/Hirai0827/ShaderCutter
16 | ```
17 | when the package is installed, compile from ts to js will automatically begin.
18 | - Use
19 | ```typescript
20 | const a = GLSLPeepAnalyzer.Analyze(code,{row:2,column:6});
21 | if(a){
22 | const generated = GLSLPeepCodeGenerator.Generate(a);
23 | }
24 | ```
25 | ## Contact
26 | If you have something about the project please contact us
27 | - Hirai0827([@lucknknock](https://twitter.com/lucknknock))
28 |
29 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [['@babel/preset-env', {targets: {node: 'current'}}],'@babel/preset-typescript'],
3 | };
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ShaderPeeper",
3 | "version": "1.0.0",
4 | "main": "build/index.js",
5 | "files": [
6 | "build"
7 | ],
8 | "scripts": {
9 | "build": "tsc",
10 | "prepare": "npm run build"
11 | },
12 | "author": "hirai ",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "@babel/core": "^7.12.10",
16 | "@babel/preset-env": "^7.12.11",
17 | "@babel/preset-typescript": "^7.12.7",
18 | "babel-jest": "^26.6.3",
19 | "jest": "^26.6.3",
20 | "typescript": "^4.1.3"
21 | },
22 | "dependencies": {
23 | "@types/jest": "^26.0.19"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Controller/GLSLPeepAnalyzer.ts:
--------------------------------------------------------------------------------
1 | import {PeepAnalyzeResult, SplitShader} from "../Data/PeepAnalyzeResult";
2 | import {CursorPos} from "../Data/CursorPos";
3 | import {ShaderDepthMap} from "../Data/ShaderDepthData";
4 | import {DefinitionData} from "../Data/DefinitionData";
5 | import {ShaderPeeperRegex} from "../Data/ShaderPeeperRegex";
6 | import {VariableType} from "../Data/VariableType";
7 |
8 | export class GLSLPeepAnalyzer{
9 | static Analyze:(src:string,cursorPos:CursorPos) => PeepAnalyzeResult|false = (src:string, cursorPos:CursorPos) => {
10 | const splitCode = GLSLPeepAnalyzer.SplitShader(src);
11 | if(!splitCode){
12 | return false;
13 | }
14 | const index = GLSLPeepAnalyzer.cursorPos2Index(src,cursorPos) - splitCode.prefix.length;
15 | const depthMap = GLSLPeepAnalyzer.AnalyzeDepth(splitCode.mainFunc);
16 | const depthIndex = GLSLPeepAnalyzer.GetDepthMapIndex(depthMap,index);
17 | const definitionData = GLSLPeepAnalyzer.AnalyzeLocalVariable(depthMap);
18 |
19 | return {
20 | src:src,cursorPos:cursorPos,splitShader:splitCode,index:index,depthMap:depthMap,depthMapIndex:depthIndex,definitionData:definitionData
21 | } as PeepAnalyzeResult;
22 | };
23 |
24 | static SplitShader:(src:string) => SplitShader|false = (src:string) => {
25 | //TODO Shaderのメイン関数のみ分離 正規表現のprefixを利用する(予定)
26 | const mainPartRegRes = ShaderPeeperRegex.mainPartRegex.exec(src);
27 | if(mainPartRegRes){
28 | const beginPoint = mainPartRegRes.index + mainPartRegRes[0].length;
29 | const prefix = src.substring(0,beginPoint);
30 | const mainAndSuff = src.substring(beginPoint,src.length);
31 | let bracketCount = 1;
32 | let endPoint = mainAndSuff.length;
33 | for(let i = 0; i < mainAndSuff.length; i++){
34 | switch (mainAndSuff[i]) {
35 | case '{':
36 | bracketCount++;
37 | break;
38 | case '}':
39 | bracketCount--;
40 | break;
41 | }
42 | if(bracketCount == 0){
43 | //ここまでメイン関数
44 | endPoint = i;
45 | break;
46 | }
47 | }
48 | const main = mainAndSuff.substring(0,endPoint);
49 | const suffix = mainAndSuff.substring(endPoint,mainAndSuff.length);
50 | //TODO main関数とSuffix部分の分離
51 | return {prefix:prefix,mainFunc:main,suffix:suffix};
52 | }else{
53 | return false;
54 | }
55 | };
56 |
57 | static cursorPos2Index:(src:string,cursorPos:CursorPos) => number = (src:string,cursorPos:CursorPos) => {
58 | //カーソル位置からindexを生成する
59 | const splited = src.split('\n');
60 | let count = 0;
61 | if(cursorPos.row != 0){
62 | for(let i = 0; i < cursorPos.row; i++){
63 | if(!splited[i]){
64 | break;
65 | }
66 | count += splited[i].length + 1;
67 | }
68 | }
69 | count += cursorPos.column;
70 | return count;
71 | };
72 |
73 | static AnalyzeDepth:(src:string) => ShaderDepthMap = (src:string) => {
74 | //Shaderのネスト深度を測る
75 | const analyzeDepth = [] as ShaderDepthMap;
76 | let buffer:string = "";
77 | let currentDepth = 0;
78 | for(let i = 0; i < src.length; i++){
79 | switch (src[i]) {
80 | case "{":
81 | analyzeDepth.push({src:buffer + "{",depth:currentDepth});
82 | buffer = "";
83 | currentDepth++;
84 | break;
85 | case "}":
86 | analyzeDepth.push({src:buffer + "}",depth:currentDepth});
87 | buffer = "";
88 | currentDepth--;
89 | break;
90 | case ";":
91 | analyzeDepth.push({src:buffer + ";",depth:currentDepth});
92 | buffer = "";
93 | break;
94 | default:
95 | buffer += src[i];
96 | break;
97 | }
98 | }
99 | if(buffer != ""){
100 | analyzeDepth.push({src:buffer,depth:currentDepth});
101 | }
102 | return analyzeDepth;
103 | };
104 |
105 | static GetDepthMapIndex:(depthMap:ShaderDepthMap,index:number) => number = (depthMap:ShaderDepthMap,index:number) => {
106 | let count = 0;
107 | for(let i = 0; i < depthMap.length;i++){
108 | count += depthMap[i].src.length;
109 | if(count >= index){
110 | return i;
111 | }
112 | }
113 | return depthMap.length - 1;
114 | };
115 | static AnalyzeLocalVariable:(depthMap:ShaderDepthMap) => DefinitionData = (depthMap:ShaderDepthMap) => {
116 | //TODO ローカル変数の分析(対象変数の決定をするため)
117 | const definitionData:DefinitionData = {};
118 | definitionData[0] = {};
119 | definitionData[0]["gl_FragCoord"] = "vec4";
120 | definitionData[0]["gl_FragColor"] = "vec4";
121 |
122 | for(let i = 0; i < depthMap.length; i++){
123 | const depthData = depthMap[i];
124 | const data = ShaderPeeperRegex.defineRegex.exec(depthData.src);
125 | if(data){
126 | if(!definitionData[depthData.depth]){
127 | definitionData[depthData.depth] = {};
128 | }
129 | definitionData[depthData.depth][data[2]] = data[1] as VariableType;
130 | }
131 | }
132 | return definitionData;
133 | };
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Controller/GLSLPeepCodeGenerator.ts:
--------------------------------------------------------------------------------
1 | import {PeepAnalyzeResult} from "../Data/PeepAnalyzeResult";
2 | import {VariableType} from "../Data/VariableType";
3 | import {ShaderDepthData} from "../Data/ShaderDepthData";
4 | import {DefinitionData} from "../Data/DefinitionData";
5 | import {ShaderPeeperRegex} from "../Data/ShaderPeeperRegex";
6 |
7 |
8 | type VariableTypeAndName = {type:VariableType,name:string};
9 |
10 | export class GLSLPeepCodeGenerator {
11 | static Generate:(result:PeepAnalyzeResult) => string = (result:PeepAnalyzeResult) => {
12 | let code = "";
13 | let targetDepth = result.depthMap[result.depthMapIndex].depth;
14 | const variableInfo = GLSLPeepCodeGenerator.GetVariableTypeAndName(result.depthMap[result.depthMapIndex],result.definitionData);
15 | if(variableInfo == false){
16 | return "";
17 | }
18 | //コードをフラグメントから復元
19 | for(let i = 0; i <= result.depthMapIndex; i++){
20 | code += result.depthMap[i].src;
21 | }
22 | if(result.depthMapIndex != result.depthMap.length){
23 | if(targetDepth != 0){
24 | for(let i = result.depthMapIndex + 1;i < result.depthMap.length; i++){
25 | if(result.depthMap[i].depth < targetDepth){
26 | break;
27 | }else{
28 | code += result.depthMap[i].src;
29 | }
30 | }
31 | }
32 | }
33 |
34 | code += "\n"+GLSLPeepCodeGenerator.GenerateSuffixCode(variableInfo.type,variableInfo.name);
35 | return result.splitShader.prefix + code + result.splitShader.suffix;
36 | };
37 |
38 | static GetVariableTypeAndName:(depthData:ShaderDepthData,definitionData:DefinitionData) => VariableTypeAndName|false = (depthData:ShaderDepthData,definitionData:DefinitionData) => {
39 | const definitionRegRes = ShaderPeeperRegex.defineRegex.exec(depthData.src);
40 | if(definitionRegRes){
41 | const type:VariableType = definitionRegRes[1] as VariableType;
42 | const name:string = definitionRegRes[2];
43 | return{type,name};
44 | }
45 | const substituteRegRes = ShaderPeeperRegex.substitutionRegex.exec(depthData.src);
46 | console.log(substituteRegRes);
47 | if(substituteRegRes){
48 | const name:string = substituteRegRes[1];
49 | for(let i = depthData.depth; i >= 0; i--){
50 | if(definitionData[i]){
51 | if(definitionData[i][name]){
52 | const type:VariableType = definitionData[i][name];
53 | console.log(type,name);
54 | return{type,name};
55 | }
56 | }
57 | }
58 | }
59 | return false;
60 | };
61 |
62 | static GenerateSuffixCode:(variableType:VariableType,variableName:string) => string = (variableType:VariableType,variableName:string) => {
63 | switch (variableType) {
64 | case "int":
65 | return `gl_FragColor.xyz = vec3(${variableName});`;
66 | break;
67 | case "float":
68 | return `gl_FragColor = vec4(${variableName},0.0,0.0,1.0);`;
69 | break;
70 | case "vec2":
71 | return `gl_FragColor = vec4(${variableName},0.0,1.0);`;
72 | break;
73 | case "vec3":
74 | return `gl_FragColor = vec4(${variableName},1.0);`;
75 | break;
76 | case "vec4":
77 | return `gl_FragColor = vec4(${variableName});`;
78 | break;
79 | }
80 | };
81 | }
82 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Controller/ICodeAnalyzer.ts:
--------------------------------------------------------------------------------
1 | export interface ICodeAnalyzer {
2 | Analyze:any;
3 |
4 | }
5 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/CursorPos.ts:
--------------------------------------------------------------------------------
1 | export interface CursorPos {
2 | row:number,
3 | column:number
4 | }
5 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/DefinitionData.ts:
--------------------------------------------------------------------------------
1 | import {VariableType} from "./VariableType";
2 |
3 | export type DefinitionList = {[key:string]:VariableType};
4 | export type DefinitionData = {[key:number]:DefinitionList};
5 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/PeepAnalyzeResult.ts:
--------------------------------------------------------------------------------
1 | import {CursorPos} from "./CursorPos";
2 | import {VariableType} from "./VariableType";
3 | import {ShaderDepthData, ShaderDepthMap} from "./ShaderDepthData";
4 | import {DefinitionData} from "./DefinitionData";
5 |
6 | export type SplitShader = {
7 | prefix:string,
8 | mainFunc:string,
9 | suffix:string
10 | }
11 |
12 | export type PeepAnalyzeResult = {
13 | src:string;
14 | splitShader:SplitShader;
15 | cursorPos:CursorPos;
16 | index:number;
17 | depthMap:ShaderDepthMap;
18 | depthMapIndex:number;
19 | variableType:VariableType;
20 | definitionData:DefinitionData;
21 | }
22 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/ShaderDepthData.ts:
--------------------------------------------------------------------------------
1 | export type ShaderDepthData = {src:string,depth:number}
2 | export type ShaderDepthMap = Array;
3 |
4 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/ShaderPeeperRegex.ts:
--------------------------------------------------------------------------------
1 | export class ShaderPeeperRegex{
2 | static spaceRegex = new RegExp("\\s|\\t|\\n");
3 | static typeRegex = new RegExp("int|float|vec2|vec3");
4 | static equalRegex = new RegExp("(?:\\+=|-=|\\*=|/=|=)")
5 | static defineRegex = new RegExp(`(${ShaderPeeperRegex.typeRegex.source})(?:${ShaderPeeperRegex.spaceRegex.source}+?)((?:\\w|_)+)(?:${ShaderPeeperRegex.spaceRegex.source}*?)=`);
6 | static substitutionRegex = new RegExp(`((?:\\w|_)+)(?:|\\.\\w+)(?:${ShaderPeeperRegex.spaceRegex.source}*?)${ShaderPeeperRegex.equalRegex.source}`);
7 | static mainPartRegex = new RegExp(`void(?:${ShaderPeeperRegex.spaceRegex.source}+?)main(?:${ShaderPeeperRegex.spaceRegex.source}*?)\\(.*?\\)(?:${ShaderPeeperRegex.spaceRegex.source}*?){`);
8 | }
9 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Core/Data/VariableType.ts:
--------------------------------------------------------------------------------
1 | export type VariableType = "int"|"float"|"vec2"|"vec3"|"vec4";
2 |
3 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Test/analyze.test.ts:
--------------------------------------------------------------------------------
1 | import {GLSLPeepAnalyzer} from "../ShaderPeeper_Core/Controller/GLSLPeepAnalyzer";
2 | import {GLSLPeepCodeGenerator} from "../ShaderPeeper_Core/Controller/GLSLPeepCodeGenerator";
3 | import {ShaderPeeperRegex} from "../ShaderPeeper_Core/Data/ShaderPeeperRegex";
4 |
5 | test("analyze test",()=>{
6 | const code =
7 | `
8 | vec3 col = vec3(uv,1.0);
9 | {vec2 col2 = vec2(0.5,1.0);
10 | float hoge = 0.0025;}
11 | float huga = 0.0125;
12 | `;
13 | const a = GLSLPeepAnalyzer.Analyze(code,{row:2,column:6});
14 | console.log(a);
15 | if(a){
16 | expect(GLSLPeepCodeGenerator.Generate(a)).toBe(code);
17 | }
18 | const codeB = `
19 | precision highp float;
20 |
21 | uniform vec3 resolution;
22 | uniform float time;
23 |
24 | void main(void) {
25 | vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
26 | uv.xy += sin(uv + time) * 0.5 + 0.5;
27 | gl_FragColor = vec4(uv, cos(time) * 0.5 + 0.5, 1.0);
28 | }
29 | `;
30 | const b = GLSLPeepAnalyzer.Analyze(codeB,{row:8,column:10});
31 | console.log(b);
32 | if(b){
33 | console.log(GLSLPeepCodeGenerator.Generate(b));
34 | expect(GLSLPeepCodeGenerator.Generate(b)).toBe(codeB);
35 | }
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/src/ShaderPeeper_Test/sample.test.ts:
--------------------------------------------------------------------------------
1 | test("sample test",() => {
2 | expect(1+2).toBe(3);
3 | });
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./build", /* Redirect output structure to the directory. */
18 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
43 |
44 | /* Module Resolution Options */
45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
55 |
56 | /* Source Map Options */
57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
61 |
62 | /* Experimental Options */
63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
65 |
66 | /* Advanced Options */
67 | "skipLibCheck": true, /* Skip type checking of declaration files. */
68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
69 | },
70 | "exclude": ["babel.config.js"]
71 | }
72 |
--------------------------------------------------------------------------------