-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathisf-improved.js
More file actions
248 lines (203 loc) · 8.06 KB
/
isf-improved.js
File metadata and controls
248 lines (203 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Improved ISF implementation based on msfeldstein/interactive-shader-format-js examples
// This module provides proper ISF loading and rendering functionality
class ISFManager {
constructor() {
this.renderers = new Map();
this.animationId = null;
this.startTime = Date.now();
}
// Create ISF renderer for a layer using proper WebGL2 context
createISFRenderer(layer, source, canvas) {
console.log('Creating ISF renderer for layer:', layer.id);
// Use WebGL2 for better texture support (as shown in examples)
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
// Check if ISFRenderer is available (try different possible locations)
let ISFRendererClass = null;
if (typeof window.ISFRenderer !== 'undefined') {
ISFRendererClass = window.ISFRenderer;
} else if (typeof ISFRenderer !== 'undefined') {
ISFRendererClass = ISFRenderer;
} else if (typeof window.interactiveShaderFormat !== 'undefined' && window.interactiveShaderFormat.Renderer) {
ISFRendererClass = window.interactiveShaderFormat.Renderer;
} else if (typeof window.InteractiveShaderFormat !== 'undefined' && window.InteractiveShaderFormat.Renderer) {
ISFRendererClass = window.InteractiveShaderFormat.Renderer;
}
if (!ISFRendererClass) {
throw new Error('ISFRenderer not loaded. Make sure to include the ISF library.');
}
try {
console.log('Creating ISF renderer with class:', ISFRendererClass);
const renderer = new ISFRendererClass(gl);
renderer.loadSource(source);
// Store renderer reference
this.renderers.set(layer.id, {
renderer,
canvas,
gl,
layer,
startTime: Date.now()
});
console.log('ISF renderer created successfully for layer', layer.id);
return renderer;
} catch (error) {
console.error('Error creating ISF renderer:', error);
throw error;
}
}
// Set up proper animation loop for ISF rendering
startAnimationLoop() {
if (this.animationId) {
return; // Already running
}
const animate = () => {
const currentTime = Date.now();
this.renderers.forEach(({ renderer, canvas, gl, layer, startTime }) => {
try {
// Set viewport
gl.viewport(0, 0, canvas.width, canvas.height);
// Update TIME uniform (crucial for animated shaders)
if (renderer.uniforms && renderer.uniforms.TIME) {
const timeValue = (currentTime - startTime) / 1000.0;
renderer.setValue('TIME', timeValue);
}
// Update RENDERSIZE uniform
if (renderer.uniforms && renderer.uniforms.RENDERSIZE) {
renderer.setValue('RENDERSIZE', [canvas.width, canvas.height]);
}
// Render using the ISF renderer
renderer.draw(canvas);
} catch (error) {
console.error('Error rendering ISF layer', layer.id, ':', error);
}
});
this.animationId = requestAnimationFrame(animate);
};
animate();
}
// Stop animation loop
stopAnimationLoop() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
}
// Remove renderer for a layer
removeRenderer(layerId) {
const rendererData = this.renderers.get(layerId);
if (rendererData) {
// Clean up WebGL resources if needed
this.renderers.delete(layerId);
console.log('Removed ISF renderer for layer', layerId);
}
}
// Set parameter on all renderers for a given base layer ID
setParameterForLayer(baseLayerId, paramName, value) {
console.log(`ISF Manager: Setting ${paramName} = ${value} for layer ${baseLayerId}`);
console.log('ISF Manager: Available renderers:', Array.from(this.renderers.keys()));
let updated = 0;
// Update all renderers that belong to this layer (preview and output)
this.renderers.forEach((rendererData, rendererId) => {
const isMainLayer = rendererId === baseLayerId;
const isOutputLayer = rendererId === 'output_' + baseLayerId;
console.log(`Checking renderer ${rendererId}: isMain=${isMainLayer}, isOutput=${isOutputLayer}`);
if (isMainLayer || isOutputLayer) {
if (rendererData.renderer && rendererData.renderer.setValue) {
try {
rendererData.renderer.setValue(paramName, value);
updated++;
console.log(`✓ Updated ${paramName} = ${value} on renderer ${rendererId}`);
} catch (error) {
console.error(`✗ Failed to set ${paramName} on renderer ${rendererId}:`, error);
}
} else {
console.log(`✗ Renderer ${rendererId} has no setValue method`);
}
}
});
console.log(`ISF Manager: Updated parameter ${paramName} on ${updated} renderers for layer ${baseLayerId}`);
return updated > 0;
}
// Parse ISF JSON metadata
static parseISFMetadata(source) {
try {
// Extract JSON metadata from ISF source
const jsonMatch = source.match(/\/\*(\{[\s\S]*?\})\*\//);
if (jsonMatch) {
return JSON.parse(jsonMatch[1]);
}
return null;
} catch (error) {
console.error('Error parsing ISF metadata:', error);
return null;
}
}
// Create UI controls for ISF inputs
static createISFControls(renderer, metadata, container) {
if (!metadata || !metadata.INPUTS) {
return;
}
container.innerHTML = '<h4>ISF Controls</h4>';
metadata.INPUTS.forEach(input => {
const controlDiv = document.createElement('div');
controlDiv.className = 'isf-control';
const label = document.createElement('label');
label.textContent = input.NAME + ': ';
controlDiv.appendChild(label);
let control;
switch (input.TYPE) {
case 'float':
control = document.createElement('input');
control.type = 'range';
control.min = input.MIN || 0;
control.max = input.MAX || 1;
control.step = 0.01;
control.value = input.DEFAULT || 0.5;
const valueDisplay = document.createElement('span');
valueDisplay.textContent = control.value;
control.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
valueDisplay.textContent = value.toFixed(2);
if (renderer.uniforms && renderer.uniforms[input.NAME]) {
renderer.setValue(input.NAME, value);
}
});
controlDiv.appendChild(control);
controlDiv.appendChild(valueDisplay);
break;
case 'bool':
control = document.createElement('input');
control.type = 'checkbox';
control.checked = input.DEFAULT || false;
control.addEventListener('change', (e) => {
if (renderer.uniforms && renderer.uniforms[input.NAME]) {
renderer.setValue(input.NAME, e.target.checked);
}
});
controlDiv.appendChild(control);
break;
case 'color':
control = document.createElement('input');
control.type = 'color';
control.value = input.DEFAULT || '#ffffff';
control.addEventListener('input', (e) => {
// Convert hex to RGB array
const hex = e.target.value;
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
if (renderer.uniforms && renderer.uniforms[input.NAME]) {
renderer.setValue(input.NAME, [r, g, b, 1.0]);
}
});
controlDiv.appendChild(control);
break;
}
container.appendChild(controlDiv);
});
}
}
// Export for use in main app
window.ISFManager = ISFManager;