bg2e-js 2.3.11 → 2.3.13
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/bg2e-js.js +356 -326
- package/dist/bg2e-js.js.map +1 -1
- package/package.json +56 -56
- package/src/app/AppController.ts +39 -39
- package/src/app/Bg2KeyboardEvent.ts +54 -54
- package/src/app/Bg2MouseEvent.ts +82 -82
- package/src/app/Bg2TouchEvent.ts +18 -18
- package/src/app/Canvas.ts +108 -108
- package/src/app/EventBase.ts +10 -10
- package/src/app/MainLoop.ts +273 -273
- package/src/app/index.ts +24 -24
- package/src/base/Color.ts +134 -134
- package/src/base/Environment.ts +183 -183
- package/src/base/Light.ts +192 -192
- package/src/base/Material.ts +620 -620
- package/src/base/PolyList.ts +365 -365
- package/src/base/Texture.ts +620 -620
- package/src/base/index.ts +81 -81
- package/src/db/Bg2LoaderPlugin.ts +143 -143
- package/src/db/DBPluginApi.ts +48 -48
- package/src/db/Loader.ts +116 -116
- package/src/db/LoaderPlugin.ts +34 -34
- package/src/db/MtlParser.ts +7 -7
- package/src/db/ObjLoaderPlugin.ts +54 -54
- package/src/db/ObjParser.ts +252 -252
- package/src/db/ObjWriterPlugin.ts +18 -18
- package/src/db/VitscnjLoaderPlugin.ts +112 -112
- package/src/db/Writer.ts +52 -52
- package/src/db/WriterPlugin.ts +22 -22
- package/src/db/index.ts +44 -44
- package/src/debug/DebugRenderer.ts +173 -173
- package/src/debug/WebGLTextureViewer.ts +75 -75
- package/src/debug/index.ts +6 -6
- package/src/index.html +11 -11
- package/src/index.ts +33 -33
- package/src/manipulation/SelectionBuffer.ts +81 -81
- package/src/manipulation/SelectionHighlight.ts +105 -84
- package/src/manipulation/SelectionIdAssignVisitor.ts +96 -96
- package/src/manipulation/SelectionManager.ts +196 -188
- package/src/manipulation/SelectionMode.ts +6 -6
- package/src/math/Mat3.ts +259 -259
- package/src/math/Mat4.ts +710 -710
- package/src/math/MatrixStrategy.ts +25 -25
- package/src/math/Quat.ts +65 -65
- package/src/math/Vec.ts +753 -753
- package/src/math/constants.ts +46 -46
- package/src/math/functions.ts +103 -103
- package/src/math/index.ts +74 -74
- package/src/phsics/joint.ts +137 -137
- package/src/primitives/arrow.ts +57 -57
- package/src/primitives/cone.ts +138 -138
- package/src/primitives/cube.ts +60 -60
- package/src/primitives/cylinder.ts +216 -216
- package/src/primitives/index.ts +13 -13
- package/src/primitives/plane.ts +31 -31
- package/src/primitives/sphere.ts +809 -809
- package/src/react/useBg2e.ts +69 -69
- package/src/render/BRDFIntegrationMap.ts +4 -4
- package/src/render/Environment.ts +135 -135
- package/src/render/FrameBuffer.ts +35 -35
- package/src/render/MaterialRenderer.ts +34 -34
- package/src/render/Pipeline.ts +108 -108
- package/src/render/PolyListRenderer.ts +47 -47
- package/src/render/RenderBuffer.ts +197 -197
- package/src/render/RenderQueue.ts +198 -198
- package/src/render/RenderState.ts +116 -116
- package/src/render/Renderer.ts +248 -248
- package/src/render/SceneAppController.ts +250 -250
- package/src/render/SceneRenderer.ts +387 -387
- package/src/render/Shader.ts +32 -32
- package/src/render/ShadowRenderer.ts +176 -176
- package/src/render/SkyCube.ts +105 -105
- package/src/render/SkySphere.ts +117 -117
- package/src/render/TextureMergerRenderer.ts +70 -70
- package/src/render/TextureRenderer.ts +34 -34
- package/src/render/index.ts +67 -67
- package/src/render/webgl/FrameBuffer.ts +9 -9
- package/src/render/webgl/MaterialRenderer.ts +112 -112
- package/src/render/webgl/Pipeline.ts +88 -88
- package/src/render/webgl/PolyListRenderer.ts +260 -260
- package/src/render/webgl/RenderBuffer.ts +226 -226
- package/src/render/webgl/Renderer.ts +262 -262
- package/src/render/webgl/SceneRenderer.ts +67 -67
- package/src/render/webgl/ShaderProgram.ts +424 -424
- package/src/render/webgl/ShadowRenderer.ts +6 -6
- package/src/render/webgl/SkyCube.ts +15 -15
- package/src/render/webgl/SkySphere.ts +15 -15
- package/src/render/webgl/State.ts +152 -152
- package/src/render/webgl/TextureRenderer.ts +167 -167
- package/src/render/webgl/VertexBuffer.ts +137 -137
- package/src/render/webgl/index.ts +35 -35
- package/src/scene/Camera.ts +458 -458
- package/src/scene/Chain.ts +44 -44
- package/src/scene/ChainJoint.ts +58 -58
- package/src/scene/Component.ts +177 -177
- package/src/scene/ComponentMap.ts +106 -106
- package/src/scene/Drawable.ts +154 -154
- package/src/scene/EnvironmentComponent.ts +141 -141
- package/src/scene/FindNodeVisitor.ts +59 -59
- package/src/scene/LightComponent.ts +154 -154
- package/src/scene/MatrixState.ts +46 -46
- package/src/scene/Node.ts +328 -328
- package/src/scene/NodeVisitor.ts +15 -15
- package/src/scene/OrbitCameraController.ts +450 -450
- package/src/scene/SmoothOrbitCameraController.ts +99 -99
- package/src/scene/Transform.ts +73 -73
- package/src/scene/index.ts +60 -60
- package/src/shaders/BasicDiffuseColorShader.ts +111 -111
- package/src/shaders/BasicPBRLightShader.ts +276 -276
- package/src/shaders/DebugRenderShader.ts +97 -97
- package/src/shaders/DepthRenderShader.ts +127 -127
- package/src/shaders/IrradianceMapCubeShader.ts +115 -115
- package/src/shaders/PBRLightIBLShader.ts +486 -486
- package/src/shaders/PickSelectionShader.ts +101 -101
- package/src/shaders/PresentDebugFramebufferShader.ts +118 -118
- package/src/shaders/PresentTextureShader.ts +99 -99
- package/src/shaders/SelectionHighlightShader.ts +143 -127
- package/src/shaders/ShaderFunction.ts +318 -318
- package/src/shaders/SkyCubeShader.ts +93 -93
- package/src/shaders/SkySphereShader.ts +102 -102
- package/src/shaders/SpecularMapCubeShader.ts +164 -164
- package/src/shaders/TextureMergerShader.ts +171 -171
- package/src/shaders/index.ts +36 -36
- package/src/shaders/webgl/color_correction.glsl +47 -47
- package/src/shaders/webgl/constants.glsl +6 -6
- package/src/shaders/webgl/index.ts +70 -70
- package/src/shaders/webgl/normal_map.glsl +9 -9
- package/src/shaders/webgl/pbr.glsl +173 -173
- package/src/shaders/webgl/uniforms.glsl +91 -91
- package/src/shaders/webgl_shader_lib.ts +213 -213
- package/src/tools/BinaryResourceProvider.ts +14 -14
- package/src/tools/ImageResourceProvider.ts +66 -66
- package/src/tools/MaterialModifier.ts +446 -446
- package/src/tools/Resource.ts +203 -203
- package/src/tools/ResourceProvider.ts +69 -69
- package/src/tools/TextResourceProvider.ts +24 -24
- package/src/tools/TextureCache.ts +51 -51
- package/src/tools/TextureResourceDatabase.ts +100 -100
- package/src/tools/UserAgent.ts +362 -362
- package/src/tools/VideoResourceProvider.ts +50 -50
- package/src/tools/WriteStrategy.ts +22 -22
- package/src/tools/base64.ts +11 -11
- package/src/tools/crypto.ts +19 -19
- package/src/tools/endiantess.ts +13 -13
- package/src/tools/image.ts +18 -18
- package/src/tools/index.ts +41 -41
- package/src/tools/processType.ts +39 -39
- package/src/vite-env.d.ts +12 -12
|
@@ -1,318 +1,318 @@
|
|
|
1
|
-
interface FunctionLike {
|
|
2
|
-
name: string;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export type DependencyItem = string | ShaderFunction;
|
|
6
|
-
|
|
7
|
-
export interface ConstantDefinition {
|
|
8
|
-
name: string;
|
|
9
|
-
value: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const isRequired = (candidateFunction: FunctionLike, includedFunctions: FunctionLike[]): boolean => {
|
|
13
|
-
return !includedFunctions.find(includedFunc => includedFunc.name === candidateFunction.name);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const getAllDependencies = (fn: ShaderFunction, result: ShaderFunction[] = []): void => {
|
|
17
|
-
fn.dependencies
|
|
18
|
-
.filter((depFn): depFn is ShaderFunction => typeof depFn !== 'string')
|
|
19
|
-
.forEach(depFn => {
|
|
20
|
-
getAllDependencies(depFn, result);
|
|
21
|
-
result.push(depFn);
|
|
22
|
-
});
|
|
23
|
-
result.push(fn);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const getDependencies = (fn: ShaderFunction): ShaderFunction[] => {
|
|
27
|
-
const allFunctions: ShaderFunction[] = [];
|
|
28
|
-
getAllDependencies(fn, allFunctions);
|
|
29
|
-
const includedFunctions: string[] = [];
|
|
30
|
-
return allFunctions.filter(candidateFn => {
|
|
31
|
-
if (includedFunctions.indexOf(candidateFn.name) === -1) {
|
|
32
|
-
includedFunctions.push(candidateFn.name);
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const getDefinitions = (requiredFunctions: (string | ShaderFunction)[]): string => {
|
|
39
|
-
const includedDefinitions: string[] = [];
|
|
40
|
-
requiredFunctions.flatMap(req => typeof req === 'string' ? [] : req.dependencies).forEach(req => {
|
|
41
|
-
if (typeof req === 'string') {
|
|
42
|
-
includedDefinitions.push(req);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return Array.from(new Set(includedDefinitions)).join('\n\n');
|
|
46
|
-
}
|
|
47
|
-
export default class ShaderFunction {
|
|
48
|
-
private _returnType: string;
|
|
49
|
-
private _name: string;
|
|
50
|
-
private _params: string;
|
|
51
|
-
private _body: string;
|
|
52
|
-
private _deps: DependencyItem[];
|
|
53
|
-
|
|
54
|
-
constructor(returnType: string, name: string, params: string, body: string, deps: DependencyItem[] = []) {
|
|
55
|
-
this._returnType = returnType;
|
|
56
|
-
this._name = name;
|
|
57
|
-
this._params = params;
|
|
58
|
-
this._body = body;
|
|
59
|
-
this._deps = deps;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get returnType(): string {
|
|
63
|
-
return this._returnType;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
get name(): string {
|
|
67
|
-
return this._name;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
get params(): string {
|
|
71
|
-
return this._params;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
get body(): string {
|
|
75
|
-
return this._body;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
get dependencies(): DependencyItem[] {
|
|
79
|
-
return this._deps;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getFunctionText(): string {
|
|
83
|
-
return `${this.returnType} ${this.name}(${this.params}) ${this.body}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static GetShaderCode(header: string, requiredFunctions: (string | ShaderFunction)[]): string {
|
|
87
|
-
let allFunctions: ShaderFunction[] = [];
|
|
88
|
-
let rawCode = '';
|
|
89
|
-
let definitions = getDefinitions(requiredFunctions);
|
|
90
|
-
requiredFunctions.forEach(req => {
|
|
91
|
-
if (typeof req === 'string') {
|
|
92
|
-
// Add directly the string to the code
|
|
93
|
-
rawCode += req + '\n\n';
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
allFunctions = [...allFunctions, ...getDependencies(req)];
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
let code = header + '\n\n' + definitions + '\n\n' + rawCode + '\n\n';
|
|
100
|
-
allFunctions.forEach(fn => {
|
|
101
|
-
code += fn.getFunctionText() + "\n\n";
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return code;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// This utility function generate an array of ShaderFunction objects from a block of GLSL code
|
|
109
|
-
export function generateShaderLibrary(glslCode: string): (string | ShaderFunction)[] {
|
|
110
|
-
return [
|
|
111
|
-
...splitStructs(glslCode),
|
|
112
|
-
...splitFunctions(glslCode)
|
|
113
|
-
.map(func => createShaderFunctionObject(func))
|
|
114
|
-
];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Extracts #define constants from a block of GLSL code
|
|
118
|
-
export function extractConstants(glslCode: string): ConstantDefinition[] {
|
|
119
|
-
const constants: ConstantDefinition[] = [];
|
|
120
|
-
const lines = glslCode.split('\n');
|
|
121
|
-
|
|
122
|
-
for (let line of lines) {
|
|
123
|
-
const trimmedLine = line.trim();
|
|
124
|
-
|
|
125
|
-
// Check if line is a #define directive
|
|
126
|
-
if (trimmedLine.startsWith('#define')) {
|
|
127
|
-
// Remove #define and split by whitespace
|
|
128
|
-
const parts = trimmedLine.substring(7).trim().split(/\s+/);
|
|
129
|
-
|
|
130
|
-
if (parts.length >= 2) {
|
|
131
|
-
const name = parts[0];
|
|
132
|
-
// Join remaining parts as the value (in case value contains spaces)
|
|
133
|
-
const value = parts.slice(1).join(' ');
|
|
134
|
-
|
|
135
|
-
constants.push({
|
|
136
|
-
name: name,
|
|
137
|
-
value: value
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return constants;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function splitStructs(glslCode: string): string[] {
|
|
147
|
-
const structs: string[] = [];
|
|
148
|
-
const lines = glslCode.split('\n');
|
|
149
|
-
let currentStruct = '';
|
|
150
|
-
let braceCount = 0;
|
|
151
|
-
let inStruct = false;
|
|
152
|
-
|
|
153
|
-
for (let line of lines) {
|
|
154
|
-
const trimmedLine = line.trim();
|
|
155
|
-
|
|
156
|
-
// Skip empty lines, comments, and preprocessor directives
|
|
157
|
-
if (!trimmedLine ||
|
|
158
|
-
trimmedLine.startsWith('//') ||
|
|
159
|
-
trimmedLine.startsWith('/*') ||
|
|
160
|
-
trimmedLine.startsWith('#')) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Check if this line starts a struct definition
|
|
165
|
-
if (!inStruct && trimmedLine.startsWith('struct')) {
|
|
166
|
-
inStruct = true;
|
|
167
|
-
currentStruct = line + '\n';
|
|
168
|
-
braceCount += (line.match(/\{/g) || []).length;
|
|
169
|
-
braceCount -= (line.match(/\}/g) || []).length;
|
|
170
|
-
|
|
171
|
-
// Check if struct ends on the same line (unlikely but possible)
|
|
172
|
-
if (braceCount === 0 && trimmedLine.includes('{') && trimmedLine.includes('}')) {
|
|
173
|
-
structs.push(currentStruct.trim());
|
|
174
|
-
currentStruct = '';
|
|
175
|
-
inStruct = false;
|
|
176
|
-
}
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (inStruct) {
|
|
181
|
-
currentStruct += line + '\n';
|
|
182
|
-
braceCount += (line.match(/\{/g) || []).length;
|
|
183
|
-
braceCount -= (line.match(/\}/g) || []).length;
|
|
184
|
-
|
|
185
|
-
// Struct definition ends when we reach the closing brace and optional semicolon
|
|
186
|
-
if (braceCount === 0) {
|
|
187
|
-
structs.push(currentStruct.trim());
|
|
188
|
-
currentStruct = '';
|
|
189
|
-
inStruct = false;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return structs;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Replace the constants in the GLSL code with their values
|
|
198
|
-
export function processConstants(glslCode: string, constants: ConstantDefinition[]): string {
|
|
199
|
-
let processedCode = glslCode;
|
|
200
|
-
|
|
201
|
-
// Sort constants by name length (descending) to avoid partial replacements
|
|
202
|
-
// For example, if we have PI and PI_2, we want to replace PI_2 first
|
|
203
|
-
const sortedConstants = [...constants].sort((a, b) => b.name.length - a.name.length);
|
|
204
|
-
|
|
205
|
-
sortedConstants.forEach(constant => {
|
|
206
|
-
// Create a regex that matches the constant name as a whole word
|
|
207
|
-
// This prevents partial matches (e.g., PI matching inside PIRATE)
|
|
208
|
-
const regex = new RegExp(`\\b${escapeRegExp(constant.name)}\\b`, 'g');
|
|
209
|
-
processedCode = processedCode.replace(regex, constant.value);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return processedCode;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
///// Private helper functions
|
|
216
|
-
|
|
217
|
-
// Helper function to escape special regex characters
|
|
218
|
-
function escapeRegExp(string: string): string {
|
|
219
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function splitFunctions(shaderCode: string): string[] {
|
|
223
|
-
const functions: string[] = [];
|
|
224
|
-
const lines = shaderCode.split('\n');
|
|
225
|
-
let currentFunction = '';
|
|
226
|
-
let braceCount = 0;
|
|
227
|
-
let inFunction = false;
|
|
228
|
-
|
|
229
|
-
for (let line of lines) {
|
|
230
|
-
const trimmedLine = line.trim();
|
|
231
|
-
|
|
232
|
-
// Skip empty lines, comments, preprocessor directives, and global variables
|
|
233
|
-
if (!trimmedLine ||
|
|
234
|
-
trimmedLine.startsWith('//') ||
|
|
235
|
-
trimmedLine.startsWith('/*') ||
|
|
236
|
-
trimmedLine.startsWith('#') ||
|
|
237
|
-
(trimmedLine.includes(';') && !inFunction && !trimmedLine.includes('{'))) {
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Check if this line starts a function (contains parentheses and opening brace pattern)
|
|
242
|
-
if (!inFunction && trimmedLine.includes('(') && trimmedLine.includes(')')) {
|
|
243
|
-
// Look for function pattern: type name(params) or name(params)
|
|
244
|
-
const functionPattern = /^\s*\w+\s+\w+\s*\([^)]*\)\s*\{?|^\s*\w+\s*\([^)]*\)\s*\{?/;
|
|
245
|
-
if (functionPattern.test(trimmedLine)) {
|
|
246
|
-
inFunction = true;
|
|
247
|
-
currentFunction = line + '\n';
|
|
248
|
-
braceCount += (line.match(/\{/g) || []).length;
|
|
249
|
-
braceCount -= (line.match(/\}/g) || []).length;
|
|
250
|
-
|
|
251
|
-
if (braceCount === 0 && trimmedLine.includes('{') && trimmedLine.includes('}')) {
|
|
252
|
-
// Single line function
|
|
253
|
-
functions.push(currentFunction.trim());
|
|
254
|
-
currentFunction = '';
|
|
255
|
-
inFunction = false;
|
|
256
|
-
}
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (inFunction) {
|
|
262
|
-
currentFunction += line + '\n';
|
|
263
|
-
braceCount += (line.match(/\{/g) || []).length;
|
|
264
|
-
braceCount -= (line.match(/\}/g) || []).length;
|
|
265
|
-
|
|
266
|
-
if (braceCount === 0) {
|
|
267
|
-
functions.push(currentFunction.trim());
|
|
268
|
-
currentFunction = '';
|
|
269
|
-
inFunction = false;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return functions;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function createShaderFunctionObject(functionCode: string): ShaderFunction {
|
|
278
|
-
const trimmedCode = functionCode.trim();
|
|
279
|
-
|
|
280
|
-
// Find the opening brace to separate signature from body
|
|
281
|
-
const openBraceIndex = trimmedCode.indexOf('{');
|
|
282
|
-
if (openBraceIndex === -1) {
|
|
283
|
-
throw new Error('Invalid function: no opening brace found');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Extract function signature and body
|
|
287
|
-
const signature = trimmedCode.substring(0, openBraceIndex).trim();
|
|
288
|
-
const body = trimmedCode.substring(openBraceIndex + 1, trimmedCode.lastIndexOf('}')).trim();
|
|
289
|
-
|
|
290
|
-
// Parse the signature to extract return type, name, and parameters
|
|
291
|
-
const parenIndex = signature.indexOf('(');
|
|
292
|
-
const closeParenIndex = signature.lastIndexOf(')');
|
|
293
|
-
|
|
294
|
-
if (parenIndex === -1 || closeParenIndex === -1) {
|
|
295
|
-
throw new Error('Invalid function signature: missing parentheses');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Extract parameters
|
|
299
|
-
const parameters = signature.substring(parenIndex + 1, closeParenIndex).trim();
|
|
300
|
-
|
|
301
|
-
// Extract return type and function name from the part before parentheses
|
|
302
|
-
const beforeParens = signature.substring(0, parenIndex).trim();
|
|
303
|
-
const parts = beforeParens.split(/\s+/);
|
|
304
|
-
|
|
305
|
-
let returnType, functionName;
|
|
306
|
-
if (parts.length >= 2) {
|
|
307
|
-
// Format: "returnType functionName"
|
|
308
|
-
returnType = parts.slice(0, -1).join(' ');
|
|
309
|
-
functionName = parts[parts.length - 1];
|
|
310
|
-
} else {
|
|
311
|
-
// Format: "functionName" (assuming void return type)
|
|
312
|
-
returnType = 'void';
|
|
313
|
-
functionName = parts[0];
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return new ShaderFunction(returnType, functionName, parameters, ` {\n\t${body}\n}`);
|
|
317
|
-
}
|
|
318
|
-
|
|
1
|
+
interface FunctionLike {
|
|
2
|
+
name: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export type DependencyItem = string | ShaderFunction;
|
|
6
|
+
|
|
7
|
+
export interface ConstantDefinition {
|
|
8
|
+
name: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isRequired = (candidateFunction: FunctionLike, includedFunctions: FunctionLike[]): boolean => {
|
|
13
|
+
return !includedFunctions.find(includedFunc => includedFunc.name === candidateFunction.name);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const getAllDependencies = (fn: ShaderFunction, result: ShaderFunction[] = []): void => {
|
|
17
|
+
fn.dependencies
|
|
18
|
+
.filter((depFn): depFn is ShaderFunction => typeof depFn !== 'string')
|
|
19
|
+
.forEach(depFn => {
|
|
20
|
+
getAllDependencies(depFn, result);
|
|
21
|
+
result.push(depFn);
|
|
22
|
+
});
|
|
23
|
+
result.push(fn);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const getDependencies = (fn: ShaderFunction): ShaderFunction[] => {
|
|
27
|
+
const allFunctions: ShaderFunction[] = [];
|
|
28
|
+
getAllDependencies(fn, allFunctions);
|
|
29
|
+
const includedFunctions: string[] = [];
|
|
30
|
+
return allFunctions.filter(candidateFn => {
|
|
31
|
+
if (includedFunctions.indexOf(candidateFn.name) === -1) {
|
|
32
|
+
includedFunctions.push(candidateFn.name);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const getDefinitions = (requiredFunctions: (string | ShaderFunction)[]): string => {
|
|
39
|
+
const includedDefinitions: string[] = [];
|
|
40
|
+
requiredFunctions.flatMap(req => typeof req === 'string' ? [] : req.dependencies).forEach(req => {
|
|
41
|
+
if (typeof req === 'string') {
|
|
42
|
+
includedDefinitions.push(req);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return Array.from(new Set(includedDefinitions)).join('\n\n');
|
|
46
|
+
}
|
|
47
|
+
export default class ShaderFunction {
|
|
48
|
+
private _returnType: string;
|
|
49
|
+
private _name: string;
|
|
50
|
+
private _params: string;
|
|
51
|
+
private _body: string;
|
|
52
|
+
private _deps: DependencyItem[];
|
|
53
|
+
|
|
54
|
+
constructor(returnType: string, name: string, params: string, body: string, deps: DependencyItem[] = []) {
|
|
55
|
+
this._returnType = returnType;
|
|
56
|
+
this._name = name;
|
|
57
|
+
this._params = params;
|
|
58
|
+
this._body = body;
|
|
59
|
+
this._deps = deps;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get returnType(): string {
|
|
63
|
+
return this._returnType;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get name(): string {
|
|
67
|
+
return this._name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get params(): string {
|
|
71
|
+
return this._params;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get body(): string {
|
|
75
|
+
return this._body;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get dependencies(): DependencyItem[] {
|
|
79
|
+
return this._deps;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getFunctionText(): string {
|
|
83
|
+
return `${this.returnType} ${this.name}(${this.params}) ${this.body}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static GetShaderCode(header: string, requiredFunctions: (string | ShaderFunction)[]): string {
|
|
87
|
+
let allFunctions: ShaderFunction[] = [];
|
|
88
|
+
let rawCode = '';
|
|
89
|
+
let definitions = getDefinitions(requiredFunctions);
|
|
90
|
+
requiredFunctions.forEach(req => {
|
|
91
|
+
if (typeof req === 'string') {
|
|
92
|
+
// Add directly the string to the code
|
|
93
|
+
rawCode += req + '\n\n';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
allFunctions = [...allFunctions, ...getDependencies(req)];
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
let code = header + '\n\n' + definitions + '\n\n' + rawCode + '\n\n';
|
|
100
|
+
allFunctions.forEach(fn => {
|
|
101
|
+
code += fn.getFunctionText() + "\n\n";
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return code;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// This utility function generate an array of ShaderFunction objects from a block of GLSL code
|
|
109
|
+
export function generateShaderLibrary(glslCode: string): (string | ShaderFunction)[] {
|
|
110
|
+
return [
|
|
111
|
+
...splitStructs(glslCode),
|
|
112
|
+
...splitFunctions(glslCode)
|
|
113
|
+
.map(func => createShaderFunctionObject(func))
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Extracts #define constants from a block of GLSL code
|
|
118
|
+
export function extractConstants(glslCode: string): ConstantDefinition[] {
|
|
119
|
+
const constants: ConstantDefinition[] = [];
|
|
120
|
+
const lines = glslCode.split('\n');
|
|
121
|
+
|
|
122
|
+
for (let line of lines) {
|
|
123
|
+
const trimmedLine = line.trim();
|
|
124
|
+
|
|
125
|
+
// Check if line is a #define directive
|
|
126
|
+
if (trimmedLine.startsWith('#define')) {
|
|
127
|
+
// Remove #define and split by whitespace
|
|
128
|
+
const parts = trimmedLine.substring(7).trim().split(/\s+/);
|
|
129
|
+
|
|
130
|
+
if (parts.length >= 2) {
|
|
131
|
+
const name = parts[0];
|
|
132
|
+
// Join remaining parts as the value (in case value contains spaces)
|
|
133
|
+
const value = parts.slice(1).join(' ');
|
|
134
|
+
|
|
135
|
+
constants.push({
|
|
136
|
+
name: name,
|
|
137
|
+
value: value
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return constants;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function splitStructs(glslCode: string): string[] {
|
|
147
|
+
const structs: string[] = [];
|
|
148
|
+
const lines = glslCode.split('\n');
|
|
149
|
+
let currentStruct = '';
|
|
150
|
+
let braceCount = 0;
|
|
151
|
+
let inStruct = false;
|
|
152
|
+
|
|
153
|
+
for (let line of lines) {
|
|
154
|
+
const trimmedLine = line.trim();
|
|
155
|
+
|
|
156
|
+
// Skip empty lines, comments, and preprocessor directives
|
|
157
|
+
if (!trimmedLine ||
|
|
158
|
+
trimmedLine.startsWith('//') ||
|
|
159
|
+
trimmedLine.startsWith('/*') ||
|
|
160
|
+
trimmedLine.startsWith('#')) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if this line starts a struct definition
|
|
165
|
+
if (!inStruct && trimmedLine.startsWith('struct')) {
|
|
166
|
+
inStruct = true;
|
|
167
|
+
currentStruct = line + '\n';
|
|
168
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
169
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
170
|
+
|
|
171
|
+
// Check if struct ends on the same line (unlikely but possible)
|
|
172
|
+
if (braceCount === 0 && trimmedLine.includes('{') && trimmedLine.includes('}')) {
|
|
173
|
+
structs.push(currentStruct.trim());
|
|
174
|
+
currentStruct = '';
|
|
175
|
+
inStruct = false;
|
|
176
|
+
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (inStruct) {
|
|
181
|
+
currentStruct += line + '\n';
|
|
182
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
183
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
184
|
+
|
|
185
|
+
// Struct definition ends when we reach the closing brace and optional semicolon
|
|
186
|
+
if (braceCount === 0) {
|
|
187
|
+
structs.push(currentStruct.trim());
|
|
188
|
+
currentStruct = '';
|
|
189
|
+
inStruct = false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return structs;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Replace the constants in the GLSL code with their values
|
|
198
|
+
export function processConstants(glslCode: string, constants: ConstantDefinition[]): string {
|
|
199
|
+
let processedCode = glslCode;
|
|
200
|
+
|
|
201
|
+
// Sort constants by name length (descending) to avoid partial replacements
|
|
202
|
+
// For example, if we have PI and PI_2, we want to replace PI_2 first
|
|
203
|
+
const sortedConstants = [...constants].sort((a, b) => b.name.length - a.name.length);
|
|
204
|
+
|
|
205
|
+
sortedConstants.forEach(constant => {
|
|
206
|
+
// Create a regex that matches the constant name as a whole word
|
|
207
|
+
// This prevents partial matches (e.g., PI matching inside PIRATE)
|
|
208
|
+
const regex = new RegExp(`\\b${escapeRegExp(constant.name)}\\b`, 'g');
|
|
209
|
+
processedCode = processedCode.replace(regex, constant.value);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return processedCode;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
///// Private helper functions
|
|
216
|
+
|
|
217
|
+
// Helper function to escape special regex characters
|
|
218
|
+
function escapeRegExp(string: string): string {
|
|
219
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function splitFunctions(shaderCode: string): string[] {
|
|
223
|
+
const functions: string[] = [];
|
|
224
|
+
const lines = shaderCode.split('\n');
|
|
225
|
+
let currentFunction = '';
|
|
226
|
+
let braceCount = 0;
|
|
227
|
+
let inFunction = false;
|
|
228
|
+
|
|
229
|
+
for (let line of lines) {
|
|
230
|
+
const trimmedLine = line.trim();
|
|
231
|
+
|
|
232
|
+
// Skip empty lines, comments, preprocessor directives, and global variables
|
|
233
|
+
if (!trimmedLine ||
|
|
234
|
+
trimmedLine.startsWith('//') ||
|
|
235
|
+
trimmedLine.startsWith('/*') ||
|
|
236
|
+
trimmedLine.startsWith('#') ||
|
|
237
|
+
(trimmedLine.includes(';') && !inFunction && !trimmedLine.includes('{'))) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check if this line starts a function (contains parentheses and opening brace pattern)
|
|
242
|
+
if (!inFunction && trimmedLine.includes('(') && trimmedLine.includes(')')) {
|
|
243
|
+
// Look for function pattern: type name(params) or name(params)
|
|
244
|
+
const functionPattern = /^\s*\w+\s+\w+\s*\([^)]*\)\s*\{?|^\s*\w+\s*\([^)]*\)\s*\{?/;
|
|
245
|
+
if (functionPattern.test(trimmedLine)) {
|
|
246
|
+
inFunction = true;
|
|
247
|
+
currentFunction = line + '\n';
|
|
248
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
249
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
250
|
+
|
|
251
|
+
if (braceCount === 0 && trimmedLine.includes('{') && trimmedLine.includes('}')) {
|
|
252
|
+
// Single line function
|
|
253
|
+
functions.push(currentFunction.trim());
|
|
254
|
+
currentFunction = '';
|
|
255
|
+
inFunction = false;
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (inFunction) {
|
|
262
|
+
currentFunction += line + '\n';
|
|
263
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
264
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
265
|
+
|
|
266
|
+
if (braceCount === 0) {
|
|
267
|
+
functions.push(currentFunction.trim());
|
|
268
|
+
currentFunction = '';
|
|
269
|
+
inFunction = false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return functions;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function createShaderFunctionObject(functionCode: string): ShaderFunction {
|
|
278
|
+
const trimmedCode = functionCode.trim();
|
|
279
|
+
|
|
280
|
+
// Find the opening brace to separate signature from body
|
|
281
|
+
const openBraceIndex = trimmedCode.indexOf('{');
|
|
282
|
+
if (openBraceIndex === -1) {
|
|
283
|
+
throw new Error('Invalid function: no opening brace found');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Extract function signature and body
|
|
287
|
+
const signature = trimmedCode.substring(0, openBraceIndex).trim();
|
|
288
|
+
const body = trimmedCode.substring(openBraceIndex + 1, trimmedCode.lastIndexOf('}')).trim();
|
|
289
|
+
|
|
290
|
+
// Parse the signature to extract return type, name, and parameters
|
|
291
|
+
const parenIndex = signature.indexOf('(');
|
|
292
|
+
const closeParenIndex = signature.lastIndexOf(')');
|
|
293
|
+
|
|
294
|
+
if (parenIndex === -1 || closeParenIndex === -1) {
|
|
295
|
+
throw new Error('Invalid function signature: missing parentheses');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Extract parameters
|
|
299
|
+
const parameters = signature.substring(parenIndex + 1, closeParenIndex).trim();
|
|
300
|
+
|
|
301
|
+
// Extract return type and function name from the part before parentheses
|
|
302
|
+
const beforeParens = signature.substring(0, parenIndex).trim();
|
|
303
|
+
const parts = beforeParens.split(/\s+/);
|
|
304
|
+
|
|
305
|
+
let returnType, functionName;
|
|
306
|
+
if (parts.length >= 2) {
|
|
307
|
+
// Format: "returnType functionName"
|
|
308
|
+
returnType = parts.slice(0, -1).join(' ');
|
|
309
|
+
functionName = parts[parts.length - 1];
|
|
310
|
+
} else {
|
|
311
|
+
// Format: "functionName" (assuming void return type)
|
|
312
|
+
returnType = 'void';
|
|
313
|
+
functionName = parts[0];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return new ShaderFunction(returnType, functionName, parameters, ` {\n\t${body}\n}`);
|
|
317
|
+
}
|
|
318
|
+
|