html, body { margin: 0; }
import { Random, range } from "https://esm.sh/@byloth/core";
import { Color, Mesh, MeshBasicNodeMaterial, NearestFilter, PlaneGeometry, PerspectiveCamera, Scene, TextureLoader, Vector4, WebGPURenderer } from "https://esm.sh/three/webgpu";
import { Fn, texture, uniformArray, uv, vec2 } from "https://esm.sh/three/tsl";
import { OrbitControls } from "https://esm.sh/three/addons/controls/OrbitControls.js";
async function main()
{
const renderer = new WebGPURenderer({ antialias: true });
renderer.setClearColor(new Color(0x3f5f7f));
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new Scene();
const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const controls = new OrbitControls(camera, renderer.domElement);
window.addEventListener("resize", () =>
{
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
const MAP_WIDTH = 520;
const MAP_HEIGHT = 520;
const STAGE_WIDTH = 5;
const STAGE_HEIGHT = 5;
const STAGE_TILES_X = 4;
const STAGE_TILES_Y = 4;
const STAGE_TILES_COUNT = STAGE_TILES_X * STAGE_TILES_Y;
const TILES_X = 4;
const TILES_Y = 4;
const TILE_OUTER_WIDTH = MAP_WIDTH / TILES_X;
const TILE_OUTER_HEIGHT = MAP_HEIGHT / TILES_Y;
const TILE_INNER_WIDTH = 120;
const TILE_INNER_HEIGHT = 120;
const geometry = new PlaneGeometry(STAGE_WIDTH, STAGE_HEIGHT, STAGE_TILES_X, STAGE_TILES_Y);
const map = await new TextureLoader().loadAsync("https://files.byloth.dev/grid.png");
// map.magFilter = NearestFilter;
// map.minFilter = NearestFilter;
const material = new MeshBasicNodeMaterial({ precision: "highp", transparent: true });
const tileArray = new Array(STAGE_TILES_COUNT);
for (const yIndex of range(STAGE_TILES_Y))
{
for (const xIndex of range(STAGE_TILES_X))
{
const element = new Vector4();
const tileX = Random.Integer(TILES_X) * TILE_OUTER_WIDTH;
const tileY = Random.Integer(TILES_Y) * TILE_OUTER_HEIGHT;
const offsetX = tileX + 5;
const offsetY = tileY + 5;
element.x = offsetX;
element.y = offsetY;
element.z = TILE_INNER_WIDTH;
element.w = TILE_INNER_HEIGHT;
const index = (yIndex * STAGE_TILES_X) + xIndex;
tileArray[index] = element;
}
}
const mapSize = vec2(MAP_WIDTH, MAP_HEIGHT).toVar();
const tileCount = vec2(STAGE_TILES_X, STAGE_TILES_Y).toVar();
const tileBuffer = uniformArray(tileArray);
material.colorNode = Fn(() =>
{
const vUv = uv().mul(tileCount)
.toVar();
const tileCoords = vUv.floor()
.toVar();
const tileIndex = tileCoords.y
.mul(tileCount.x)
.add(tileCoords.x)
.toInt();
const tile = tileBuffer.element(tileIndex)
.toVar();
const tileOffset = tile.xy
.div(mapSize);
const tileRatio = tile.zw
.div(mapSize);
const tileUv = vUv.fract()
.mul(tileRatio)
.add(tileOffset);
return texture(map, tileUv);
})();
/* setInterval(() =>
{
const index = (Random.Integer(STAGE_TILES_Y) * STAGE_TILES_X) + Random.Integer(STAGE_TILES_X);
const element = tileBuffer.array[index];
element.x = Random.Integer(TILES_X) * TILE_OUTER_WIDTH + 5;
element.y = Random.Integer(TILES_Y) * TILE_OUTER_HEIGHT + 5;
}, 250); */
scene.add(new Mesh(geometry, material));
renderer.setAnimationLoop(() =>
{
controls.update();
renderer.render(scene, camera);
});
}
window.addEventListener("DOMContentLoaded", main);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.