- 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
import { WRAP_MODES } from '@pixi/constants';
import { BaseTexture, Buffer, ObjectRenderer, Renderer } from '@pixi/core';
import { settings } from './settings';
import { TilemapGeometry, TilemapShader } from './TilemapShader';
import { TextileResource } from './TextileResource';
import * as utils from '@pixi/utils';
// For some reason ESLint goes mad with indendation in this file ^&^
/* eslint-disable no-mixed-spaces-and-tabs, indent */
/**
* Rendering helper pipeline for tilemaps. This plugin is registered automatically.
*/
export class TileRenderer extends ObjectRenderer
{
/** The managing renderer */
public readonly renderer: Renderer;
/** The tile animation frame */
public tileAnim = [0, 0];
private ibLen = 0;// index buffer length
/** The index buffer for the tilemaps to share. */
private indexBuffer: Buffer = null;
/** The shader used to render tilemaps. */
private shader: TilemapShader;
/**
* {@link TextileResource} instances used to upload textures batched in tiled groups. This is
* used only if {@link settings.TEXTURES_PER_TILEMAP} is greater than 1.
*/
private textiles: Array<TextileResource> = [];
/** @param renderer - The managing renderer */
constructor(renderer: Renderer)
{
super(renderer);
this.shader = new TilemapShader(settings.TEXTURES_PER_TILEMAP);
this.indexBuffer = new Buffer(undefined, true, true);
this.checkIndexBuffer(2000);
this.makeTextiles();
}
/**
* Binds the tile textures to the renderer, and updates the tilemap shader's `uSamplerSize` uniform.
*
* If {@link settings.TEXTILE_UNITS}
*
* @param renderer - The renderer to which the textures are to be bound.
* @param textures - The tile textures being bound.
*/
bindTileTextures(renderer: Renderer, textures: Array<BaseTexture>): void
{
const len = textures.length;
const shader = this.shader;
const maxTextures = settings.TEXTURES_PER_TILEMAP;
const samplerSize: Array<number> = shader.uniforms.uSamplerSize;
if (len > settings.TEXTILE_UNITS * maxTextures)
{
// TODO: Show error message instead of silently failing!
return;
}
if (settings.TEXTILE_UNITS <= 1)
{
// Bind each texture directly & update samplerSize.
for (let i = 0; i < textures.length; i++)
{
const texture = textures[i];
if (!texture || !texture.valid)
{
return;
}
renderer.texture.bind(textures[i], i);
samplerSize[i * 2] = 1.0 / textures[i].realWidth;
samplerSize[(i * 2) + 1] = 1.0 / textures[i].realHeight;
}
}
else
{
// Ensure we have enough textiles, in case settings.TEXTILE_UNITS was modified.
this.makeTextiles();
const usedTextiles = Math.ceil(len / settings.TEXTILE_UNITS);
// First ensure each textile has all tiles point to the right textures.
for (let i = 0; i < len; i++)
{
const texture = textures[i];
if (texture && texture.valid)
{
const resourceIndex = Math.floor(i / settings.TEXTILE_UNITS);
const tileIndex = i % settings.TEXTILE_UNITS;
this.textiles[resourceIndex].tile(tileIndex, texture);
}
}
// Then bind the textiles + update samplerSize.
for (let i = 0; i < usedTextiles; i++)
{
renderer.texture.bind(this.textiles[i].baseTexture, i);
samplerSize[i * 2] = 1.0 / this.textiles[i].width;
samplerSize[(i * 2) + 1] = 1.0 / this.textiles[i].baseTexture.height;
}
}
shader.uniforms.uSamplerSize = samplerSize;
}
start(): void
{
// sorry, nothing
}
/**
* @internal
* @ignore
*/
createVb(): TilemapGeometry
{
const geom = new TilemapGeometry();
geom.addIndex(this.indexBuffer);
geom.lastTimeAccess = Date.now();
return geom;
}
/** @return The {@link TilemapShader} shader that this rendering pipeline is using. */
getShader(): TilemapShader { return this.shader; }
destroy(): void
{
super.destroy();
// this.rectShader.destroy();
this.shader = null;
}
public checkIndexBuffer(size: number, _vb: TilemapGeometry = null): void
{
const totalIndices = size * 6;
if (totalIndices <= this.ibLen)
{
return;
}
let len = totalIndices;
while (len < totalIndices)
{
len <<= 1;
}
this.ibLen = totalIndices;
this.indexBuffer.update(utils.createIndicesForQuads(size,
settings.use32bitIndex ? new Uint32Array(size * 6) : undefined));
// TODO: create new index buffer instead?
// if (vb) {
// const curIndex = vb.getIndex();
// if (curIndex !== this.indexBuffer && (curIndex.data as any).length < totalIndices) {
// this.swapIndex(vb, this.indexBuffer);
// }
// }
}
/** Makes textile resources and initializes {@link TileRenderer.textiles}. */
private makeTextiles(): void
{
if (settings.TEXTILE_UNITS <= 1)
{
return;
}
for (let i = 0; i < settings.TEXTILE_UNITS; i++)
{
if (this.textiles[i]) continue;
const resource = new TextileResource();
const baseTex = new BaseTexture(resource);
baseTex.scaleMode = settings.TEXTILE_SCALE_MODE;
baseTex.wrapMode = WRAP_MODES.CLAMP;
this.textiles[i] = resource;
}
}
}