Jump to content

MediaWiki:Gadget-WebGL2.js: Difference between revisions

From Apogea Wiki
Dane (talk | contribs)
Implement instance mode for dynamic scene loading from WebGL: namespace (via update-page on MediaWiki MCP Server)
Dane (talk | contribs)
Pass gl as first argument to all lifecycle functions (via update-page on MediaWiki MCP Server)
Line 58: Line 58:
* Start the render loop for a scene
* Start the render loop for a scene
*/
*/
function startRenderLoop(context) {
function startRenderLoop(context, gl) {
if (!context.render) return;
if (!context.render) return;


Line 67: Line 67:
if (!context._running) return;
if (!context._running) return;
var elapsed = (timestamp - startTime) / 1000;
var elapsed = (timestamp - startTime) / 1000;
context.render(elapsed);
context.render(gl, elapsed);
context._animationId = requestAnimationFrame(loop);
context._animationId = requestAnimationFrame(loop);
}
}
Line 105: Line 105:


if (context.init) {
if (context.init) {
context.init(canvas, gl);
context.init(gl, canvas);
}
}


startRenderLoop(context);
startRenderLoop(context, gl);


// Handle resize
// Handle resize
if (context.resize) {
if (context.resize) {
$(window).on('resize.webgl-' + sceneName, function() {
$(window).on('resize.webgl-' + sceneName, function() {
context.resize(canvas.width, canvas.height);
context.resize(gl, canvas.width, canvas.height);
});
});
}
}
Line 129: Line 129:
}
}
if (context.cleanup) {
if (context.cleanup) {
context.cleanup();
context.cleanup(gl);
}
}
$(window).off('resize.webgl-' + sceneName);
$(window).off('resize.webgl-' + sceneName);

Revision as of 15:38, 1 February 2026

$(function() {
	'use strict';

	/**
	 * Fetch a scene script from the WebGL: namespace
	 */
	function fetchScene(sceneName) {
		return $.ajax({
			url: mw.util.wikiScript('api'),
			data: {
				action: 'query',
				titles: 'WebGL:' + sceneName,
				prop: 'revisions',
				rvprop: 'content',
				rvslots: 'main',
				format: 'json'
			}
		}).then(function(response) {
			var pages = response.query.pages;
			var pageId = Object.keys(pages)[0];
			if (pageId === '-1') {
				throw new Error('Scene not found: WebGL:' + sceneName);
			}
			return pages[pageId].revisions[0].slots.main['*'];
		});
	}

	/**
	 * Create a scene context object with lifecycle methods
	 */
	function createSceneContext(canvas, gl) {
		return {
			canvas: canvas,
			gl: gl,
			init: null,
			render: null,
			resize: null,
			cleanup: null,
			_running: false,
			_animationId: null
		};
	}

	/**
	 * Execute a scene script in instance mode
	 */
	function executeScene(scriptText, context) {
		try {
			var factory = new Function('scene', scriptText);
			factory(context);
		} catch (e) {
			console.error('WebGL scene execution error:', e);
			throw e;
		}
	}

	/**
	 * Start the render loop for a scene
	 */
	function startRenderLoop(context, gl) {
		if (!context.render) return;

		context._running = true;
		var startTime = performance.now();

		function loop(timestamp) {
			if (!context._running) return;
			var elapsed = (timestamp - startTime) / 1000;
			context.render(gl, elapsed);
			context._animationId = requestAnimationFrame(loop);
		}

		context._animationId = requestAnimationFrame(loop);
	}

	/**
	 * Initialize a WebGL mount point
	 */
	function initMount(mount) {
		var $mount = $(mount);
		var sceneName = $mount.data('scene');

		if (!sceneName) {
			console.warn('WebGL mount missing data-scene attribute');
			return;
		}

		var canvas = document.createElement('canvas');
		canvas.width = $mount.data('width') || 800;
		canvas.height = $mount.data('height') || 600;
		canvas.className = 'webgl-canvas';
		$mount.append(canvas);

		var gl = canvas.getContext('webgl2');
		if (!gl) {
			$mount.append('<p class="webgl-error">WebGL2 not supported</p>');
			return;
		}

		var context = createSceneContext(canvas, gl);

		fetchScene(sceneName)
			.then(function(scriptText) {
				executeScene(scriptText, context);

				if (context.init) {
					context.init(gl, canvas);
				}

				startRenderLoop(context, gl);

				// Handle resize
				if (context.resize) {
					$(window).on('resize.webgl-' + sceneName, function() {
						context.resize(gl, canvas.width, canvas.height);
					});
				}
			})
			.catch(function(err) {
				console.error('Failed to load scene:', sceneName, err);
				$mount.append('<p class="webgl-error">Failed to load scene: ' + sceneName + '</p>');
			});

		// Cleanup when element is removed
		$mount.on('remove', function() {
			context._running = false;
			if (context._animationId) {
				cancelAnimationFrame(context._animationId);
			}
			if (context.cleanup) {
				context.cleanup(gl);
			}
			$(window).off('resize.webgl-' + sceneName);
		});
	}

	// Initialize all mount points
	$('.webgl-mount').each(function() {
		initMount(this);
	});
});