UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

328 lines (228 loc) • 9.08 kB
import { ParameterLookupTable } from "../../../particles/particular/engine/parameter/ParameterLookupTable.js"; import { MouseEvents } from "../../../../input/devices/events/MouseEvents.js"; import { readPositionFromMouseEvent } from "../../../../input/devices/PointerDevice.js"; import Vector2 from "../../../../../core/geom/Vector2.js"; import { VisualTip } from "../../../../../view/tooltip/VisualTip.js"; import Rectangle from "../../../../../core/geom/2d/Rectangle.js"; import LabelView from "../../../../../view/common/LabelView.js"; import { CanvasView } from "../../../../../view/elements/CanvasView.js"; import EmptyView from "../../../../../view/elements/EmptyView.js"; import { Color } from "../../../../../core/color/Color.js"; import { halton_sequence } from "../../../../../core/math/statistics/halton_sequence.js"; import { lerp } from "../../../../../core/math/lerp.js"; /** * * @param {TooltipManager} tooltips * @param {View} view * @param {LightManager} lm */ function make_tooltips({ tooltips, view, lm }) { let tip = null; view.css({ pointerEvents: 'auto' }); /** * * @param {number} x * @param {number} y * @param {number} z */ function build_cluster_tip(x, y, z) { const image = lm.getTextureClusters().image; const lookup_image = lm.getTextureLookup().image; const light_data_size = lm.__light_data.size; const texel_index = image.width * image.height * z + image.width * y + x; // RGB format const texel_address = texel_index * 3; const lookup_index = image.data[texel_address + 0]; const light_count = image.data[texel_address + 1]; const vContainer = new EmptyView(); vContainer.css({ display: 'flex', flexDirection: 'column', padding: '2px', borderColor: 'rgba(255,255,255,0.1)', borderWidth: '1px', borderStyle: 'solid' }); vContainer.addChild(new LabelView(`${x}:${y}:${z}`)); for (let i = 0; i < light_count; i++) { const lookup_offset = lookup_index + i; const light_descriptor = lookup_image.data[lookup_offset]; const light_type = light_descriptor & 0x3; const light_address = light_descriptor >> 2; const light_address_normalized = light_address / light_data_size; const vLight = new LabelView(`${light_type}:${light_address}`); const hue = light_address_normalized; const color = new Color(); const saturation_phase = halton_sequence(4, Math.floor(light_address / 4) % 4) color.setHSV(hue, lerp(0.4, 0.7, saturation_phase), 1); vLight.css({ color: color.toHex(), textShadow: '0 0 1px black' }); vContainer.addChild(vLight); } return vContainer; } function clear_tip() { if (tip !== null) { tooltips.remove(tip); tip = null; } } const click_handler = (mouseEvent) => { console.log('Click'); clear_tip(); const resolution = lm.getResolution(); const p = new Vector2(); readPositionFromMouseEvent(p, mouseEvent, view.el); const tile_w = view.size.x / resolution.x; const tile_h = view.size.y / resolution.y; const tile_x = Math.floor(p.x / tile_w); const tile_y = Math.floor(p.y / tile_h); tip = new VisualTip(new Rectangle( tile_x * tile_w, tile_y * tile_h, tile_w, tile_h ), () => { const vContainer = new EmptyView(); vContainer.css({ display: 'flex', flexDirection: 'row' }); for (let i = 0; i < resolution.z; i++) { const vCluster = build_cluster_tip((resolution.x - 1) - tile_x, (resolution.y - 1) - tile_y, i); vContainer.addChild(vCluster); } return vContainer; }); tooltips.add(tip); }; view.on.linked.add(() => { view.el.addEventListener(MouseEvents.Click, click_handler); }); view.on.unlinked.add(() => { view.el.removeEventListener(MouseEvents.Click, click_handler); clear_tip(); }); } /** * * @param {number} width * @param {number} height * @param {LightManager} lm * @param {boolean} [heatmap] * @param {TooltipManager} [tooltips] * @returns {{domElement: HTMLCanvasElement, view:View}} */ export function createScreenGrid({ width, height, lm, heatmap = true, tooltips }) { const heatmap_lut = new ParameterLookupTable(4); heatmap_lut.write([ 0, 0, 0, 255, 0, 0, 255, 255, 0, 179, 179, 255, 0, 255, 0, 255, 255, 255, 0, 255, 255, 5, 5, 255 ]); heatmap_lut.computeUniformPositions(); const canvasView = new CanvasView(); canvasView.size.set(width, height); const canvasElement = canvasView.el; const ctx = canvasView.context2d; canvasElement.style.width = `${width}px`; canvasElement.style.height = `${height}px`; canvasElement.style.position = 'absolute'; canvasElement.style.left = '0'; canvasElement.style.top = '0'; canvasElement.style.zIndex = '1'; // canvasElement.style.mixBlendMode = 'difference'; if (tooltips !== undefined) { make_tooltips({ lm, tooltips, width, height, view: canvasView }); } else { canvasElement.style.pointerEvents = 'none'; } function draw_grid() { ctx.strokeStyle = "rgba(255,255,255,0.2)"; ctx.lineWidth = 1; ctx.beginPath(); const x_divisions = lm.getResolution().x; const y_divisions = lm.getResolution().y; const x_spacing = width / x_divisions; const y_spacing = height / y_divisions; for (let i = 0; i < x_divisions; i++) { ctx.moveTo(i * x_spacing, 0); ctx.lineTo(i * x_spacing, height); } ctx.stroke(); ctx.beginPath(); for (let i = 0; i < y_divisions; i++) { ctx.moveTo(0, i * y_spacing); ctx.lineTo(width, i * y_spacing); } ctx.stroke(); } function draw_cluster() { const textureTiles = lm.getTextureClusters(); const image = textureTiles.image; const data = image.data; const resolution = lm.getResolution(); const x_divisions = resolution.x; const y_divisions = resolution.y; const x_spacing = width / x_divisions; const y_spacing = height / y_divisions; ctx.font = '10px Tahoma'; ctx.lineWidth = 1; const color = []; for (let i_x = 0; i_x < resolution.x + 1; i_x++) { for (let i_y = 0; i_y < resolution.y + 1; i_y++) { let found_matches = 0; for (let i_z = 0; i_z < resolution.z + 1; i_z++) { const index = i_z * resolution.x * resolution.y + (resolution.y - 1 - i_y) * resolution.x + (resolution.x - 1 - i_x); const address = index * 3; const point_light_count = data[address + 1]; if (point_light_count > 0) { found_matches++; } } const z_light_saturation = found_matches / resolution.z; heatmap_lut.sample(z_light_saturation, color) const p_x = i_x * x_spacing; const p_y = i_y * y_spacing; ctx.fillStyle = 'white'; ctx.strokeStyle = `rgb(${color[0]},${color[1]},${color[2]})`; ctx.strokeText(found_matches, p_x, p_y + 10); ctx.fillText(found_matches, p_x, p_y + 10); if (heatmap) { ctx.fillStyle = `rgba(${color[0]},${color[1]},${color[2]},0.2)`; ctx.fillRect(p_x, p_y, x_spacing, y_spacing); } } } } function update() { ctx.clearRect(0, 0, width, height); draw_grid(); draw_cluster(); } update(); canvasView.on.linked.add(update); return { update, domElement: canvasElement, view: canvasView }; }