<commit_before>import {
	Object3D, Raycaster, Mesh, DoubleSide, BufferAttribute, BufferGeometry,
	ShaderMaterial, FaceColors, Vector2
} from "three";

import { TileGeometry } from "./tileGeometry";
import { RenderTile } from "./tile";

import vertexSrc from "./shader/layer.vert";
import fragmentSrc from "./shader/layer.frag";

const material = new ShaderMaterial({
	uniforms: {
		texture: { type: "t", value: null },
		tileSize: { type: "2f", value: new Vector2(0, 0) },
		lineWidth: { type: "1f", value: 0 }
	},
	vertexShader: vertexSrc,
	fragmentShader: fragmentSrc,

	extensions: {
		derivatives: true
	},

	transparent: true,
	side: DoubleSide,
	vertexColors: FaceColors
});

const outlineWidth = 1;

export class RenderLayer extends Object3D {
	constructor(renderer, tilelayer) {
		super();

		this._renderer = renderer;
		this._tilelayer = tilelayer;
		this._tiles = [];

		this._tilesetMeshes = new Map();

		this._raycaster = null;

		this.init();
	}

	init() {
		const mapWidth = this._renderer.map.width,
			mapHeight = this._renderer.map.height;

		for (let y = 0; y < mapHeight; y++) {
			for (let x = 0; x < mapWidth; x++) {
				const index = y * mapWidth + x,
					tile = new RenderTile(x, y,
						this._tilelayer.tiles[index], this._renderer);

				this._tiles[index] = tile;
			}
		}
	}

	update() {
		const tilesetsFound = new Set();
		for (let tile of this._tiles) {
			tile.update();
			const tilesetId = tile.currentTilesetId;
			if (tilesetId >= 0) {
				tilesetsFound.add(tilesetId);
			}
		}

		this._tilesetMeshes.forEach((mesh, meshId) => {
			let exist = tilesetsFound.has(meshId);
			if (exist) {
				tilesetsFound.delete(meshId);
			} else {
				this.remove(mesh);
				this._tilesetMeshes.delete(meshId);
			}
		});
		// These are the tilesets that need to be added. The others are removed.
		tilesetsFound.forEach(id => {
			this.generateMesh(id);
		});

		this.applyTextures();
		this.updateMeshes();
	}

	generateMesh(id) {
		const mapWidth = this._renderer.map.width;
		const mapHeight = this._renderer.map.height;
		const width = mapWidth * this._renderer.tileSize.x;
		const height = mapHeight * this._renderer.tileSize.y;

		const tileGeometry = new TileGeometry(width, height, mapWidth, mapHeight);
		const geometry = new BufferGeometry().fromGeometry(tileGeometry);

		const opacity = new Float32Array(mapWidth * mapHeight * 2 * 3);
		geometry.addAttribute("opacity", new BufferAttribute(opacity, 1));

		const mesh = new Mesh(geometry, material.clone());

		this._tilesetMeshes.set(id, mesh);
		this.add(mesh);
	}

	applyTextures() {
		this._tilesetMeshes.forEach((mesh, id) => {
			const tileset = this._renderer.getTileset(id);
			if (tileset) {
				mesh.visible = true;
				mesh.material.uniforms.texture.value = tileset.texture;
			} else {
				mesh.visible = false;
			}
		});
	}

	updateMeshes() {
		this._tilesetMeshes.forEach((mesh, meshTilesetId) => {
			const { uv, color, opacity } = mesh.geometry.attributes;

			let offset2 = 0, offset3 = 0;

			const faces = uv.count / 3;

			for (let i = 0; i < faces; i++) {
				const tileObject = this._tiles[Math.floor(i * 0.5)];

				if (tileObject.triRenderCount < 2) {
					tileObject.triRenderCount++;

					const { currentTileId: tileId, currentTilesetId: tilesetId,
						tint, opacity: tileOpacity } = tileObject;

					let tileUvs = tileObject.uvs;

					if (tileId < 0 || tileUvs.length === 0 || tilesetId !== meshTilesetId) {
						tileUvs = [[-1, -1, -1], [-1, -1, -1]];
					}

					let newOff2 = 0, newOff3 = 0;
					const uvOffset = i % 2;

					for (let j = 0; j < 3; j++) {
						opacity.array[i * 3 + j] = tileOpacity;

						uv.array[offset2 + newOff2] = tileUvs[uvOffset][j].x;
						uv.array[offset2 + newOff2 + 1] = tileUvs[uvOffset][j].y;

						color.array[offset3 + newOff3] = tint.r;
						color.array[offset3 + newOff3 + 1] = tint.g;
						color.array[offset3 + newOff3 + 2] = tint.b;

						newOff2 = newOff2 + 2;
						newOff3 = newOff3 + 3;
					}
				} else {
					// The tile is rendered, so we skip rendering the other tri.
					i++;
					offset2 = offset2 + 6;
					offset3 = offset3 + 9;
				}

				offset2 = offset2 + 6;
				offset3 = offset3 + 9;
			}

			this._updateMeshUniforms(mesh);

			uv.needsUpdate = true;
			color.needsUpdate = true;
			opacity.needsUpdate = true;
		});
	}

	raycastToTile(cursorPosition, camera) {
		if (!this._raycaster) this._raycaster = new Raycaster();

		this._raycaster.setFromCamera(cursorPosition, camera);

		const intersections = this._raycaster.intersectObjects(this.children);
		for (let intersection of intersections) {
			return intersection.object.tilePosition;
		}
	}

	getTile(x, y) {
		return this._tiles[x + y * this._renderer.map.width];
	}

	_updateMeshUniforms(mesh) {
		const uniforms = mesh.material.uniforms;
		uniforms.tileSize.value = this._renderer.tileSize;
		uniforms.lineWidth.value = this._renderer.outlineEnabled ? outlineWidth : 0;
	}
}
<commit_msg>Fix rendering multiple tilesets<commit_after>import {
	Object3D, Raycaster, Mesh, DoubleSide, BufferAttribute, BufferGeometry,
	ShaderMaterial, FaceColors, Vector2, Color
} from "three";

import { TileGeometry } from "./tileGeometry";
import { RenderTile } from "./tile";

import vertexSrc from "./shader/layer.vert";
import fragmentSrc from "./shader/layer.frag";

const material = new ShaderMaterial({
	uniforms: {
		texture: { type: "t", value: null },
		tileSize: { type: "2f", value: new Vector2(0, 0) },
		lineWidth: { type: "1f", value: 0 }
	},
	vertexShader: vertexSrc,
	fragmentShader: fragmentSrc,

	extensions: {
		derivatives: true
	},

	transparent: true,
	side: DoubleSide,
	vertexColors: FaceColors
});

const outlineWidth = 1;

export class RenderLayer extends Object3D {
	constructor(renderer, tilelayer) {
		super();

		this._renderer = renderer;
		this._tilelayer = tilelayer;
		this._tiles = [];

		this._tilesetMeshes = new Map();

		this._raycaster = null;

		this.init();
	}

	init() {
		const mapWidth = this._renderer.map.width,
			mapHeight = this._renderer.map.height;

		for (let y = 0; y < mapHeight; y++) {
			for (let x = 0; x < mapWidth; x++) {
				const index = y * mapWidth + x,
					tile = new RenderTile(x, y,
						this._tilelayer.tiles[index], this._renderer);

				this._tiles[index] = tile;
			}
		}
	}

	update() {
		const tilesetsFound = new Set();
		for (let tile of this._tiles) {
			tile.update();
			const tilesetId = tile.currentTilesetId;
			if (tilesetId >= 0) {
				tilesetsFound.add(tilesetId);
			}
		}

		this._tilesetMeshes.forEach((mesh, meshId) => {
			let exist = tilesetsFound.has(meshId);
			if (exist) {
				tilesetsFound.delete(meshId);
			} else {
				this.remove(mesh);
				this._tilesetMeshes.delete(meshId);
			}
		});
		// These are the tilesets that need to be added. The others are removed.
		tilesetsFound.forEach(id => {
			this.generateMesh(id);
		});

		this.applyTextures();
		this.updateMeshes();
	}

	generateMesh(id) {
		const mapWidth = this._renderer.map.width;
		const mapHeight = this._renderer.map.height;
		const width = mapWidth * this._renderer.tileSize.x;
		const height = mapHeight * this._renderer.tileSize.y;

		const tileGeometry = new TileGeometry(width, height, mapWidth, mapHeight);
		const geometry = new BufferGeometry().fromGeometry(tileGeometry);

		const opacity = new Float32Array(mapWidth * mapHeight * 2 * 3);
		geometry.addAttribute("opacity", new BufferAttribute(opacity, 1));

		const mesh = new Mesh(geometry, material.clone());

		this._tilesetMeshes.set(id, mesh);
		this.add(mesh);
	}

	applyTextures() {
		this._tilesetMeshes.forEach((mesh, id) => {
			const tileset = this._renderer.getTileset(id);
			if (tileset) {
				mesh.visible = true;
				mesh.material.uniforms.texture.value = tileset.texture;
			} else {
				mesh.visible = false;
			}
		});
	}

	updateMeshes() {
		this._tilesetMeshes.forEach((mesh, meshTilesetId) => {
			const attributes = mesh.geometry.attributes;
			const noColor = new Color(0x000000);
			const noUv = new Vector2(-1, -1);

			let offset2 = 0, offset3 = 0;

			const faces = attributes.uv.count / 3;

			for (let i = 0; i < faces; i++) {
				const tileObject = this._tiles[Math.floor(i * 0.5)];

				const { currentId: tileId, currentTilesetId: tilesetId,
					tint, opacity: tileOpacity, uvs } = tileObject;

				if (tilesetId === meshTilesetId && tileId >= 0 && uvs.length > 0) {
					if (tileObject.triRenderCount < 2) {
						tileObject.triRenderCount++;
						setAttributesForFace(attributes, offset2, offset3, i,
							tileOpacity, uvs[i % 2], tint);
					}
				} else {
					setAttributesForFace(attributes, offset2, offset3, i,
						1, [noUv, noUv, noUv], noColor);
				}

				offset2 = offset2 + 6;
				offset3 = offset3 + 9;
			}

			this._updateMeshUniforms(mesh);

			attributes.uv.needsUpdate = true;
			attributes.color.needsUpdate = true;
			attributes.opacity.needsUpdate = true;
		});
	}

	raycastToTile(cursorPosition, camera) {
		if (!this._raycaster) this._raycaster = new Raycaster();

		this._raycaster.setFromCamera(cursorPosition, camera);

		const intersections = this._raycaster.intersectObjects(this.children);
		for (let intersection of intersections) {
			return intersection.object.tilePosition;
		}
	}

	getTile(x, y) {
		return this._tiles[x + y * this._renderer.map.width];
	}

	_updateMeshUniforms(mesh) {
		const uniforms = mesh.material.uniforms;
		uniforms.tileSize.value = this._renderer.tileSize;
		uniforms.lineWidth.value = this._renderer.outlineEnabled ? outlineWidth : 0;
	}
}

function setAttributesForFace(attributes, offset2, offset3, i,
	faceOpacity, uvArray, tint) {
	let off2 = 0, off3 = 0;
	for (let j = 0; j < 3; j++) {
		attributes.opacity.array[i * 3 + j] = faceOpacity;

		attributes.uv.array[offset2 + off2] = uvArray[j].x;
		attributes.uv.array[offset2 + off2 + 1] = uvArray[j].y;

		attributes.color.array[offset3 + off3] = tint.r;
		attributes.color.array[offset3 + off3 + 1] = tint.g;
		attributes.color.array[offset3 + off3 + 2] = tint.b;

		off2 = off2 + 2;
		off3 = off3 + 3;
	}
};

{'fraction_non_alphanumeric': 0.13742894185698615, 'fraction_numerical': 0.015458262690735015, 'mean_word_length': 3.423467137185708, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3465108', 'n_tokens_mistral': 4092, 'n_tokens_neox': 3492, 'n_words': 791}