123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- import { Engine } from "../Engines/engine.js";
- import { Vector2, Vector3 } from "../Maths/math.vector.js";
- import { Texture } from "../Materials/Textures/texture.js";
- import { RawTexture } from "../Materials/Textures/rawTexture.js";
- import { ShaderMaterial } from "../Materials/shaderMaterial.js";
- import { Effect } from "../Materials/effect.js";
- import { CreatePlane } from "../Meshes/Builders/planeBuilder.js";
- import "../Shaders/spriteMap.fragment.js";
- import "../Shaders/spriteMap.vertex.js";
- /**
- * Class used to manage a grid restricted sprite deployment on an Output plane.
- */
- export class SpriteMap {
- /** Returns the Number of Sprites in the System */
- get spriteCount() {
- return this.sprites.length;
- }
- /** Returns the Position of Output Plane*/
- get position() {
- return this._output.position;
- }
- /** Returns the Position of Output Plane*/
- set position(v) {
- this._output.position = v;
- }
- /** Returns the Rotation of Output Plane*/
- get rotation() {
- return this._output.rotation;
- }
- /** Returns the Rotation of Output Plane*/
- set rotation(v) {
- this._output.rotation = v;
- }
- /** Sets the AnimationMap*/
- get animationMap() {
- return this._animationMap;
- }
- /** Sets the AnimationMap*/
- set animationMap(v) {
- const buffer = v._texture._bufferView;
- const am = this._createTileAnimationBuffer(buffer);
- this._animationMap.dispose();
- this._animationMap = am;
- this._material.setTexture("animationMap", this._animationMap);
- }
- /**
- * Creates a new SpriteMap
- * @param name defines the SpriteMaps Name
- * @param atlasJSON is the JSON file that controls the Sprites Frames and Meta
- * @param spriteSheet is the Texture that the Sprites are on.
- * @param options a basic deployment configuration
- * @param scene The Scene that the map is deployed on
- */
- constructor(name, atlasJSON, spriteSheet, options, scene) {
- this.name = name;
- this.sprites = [];
- this.atlasJSON = atlasJSON;
- this.sprites = this.atlasJSON["frames"];
- this.spriteSheet = spriteSheet;
- /**
- * Run through the options and set what ever defaults are needed that where not declared.
- */
- this.options = options;
- options.stageSize = options.stageSize || new Vector2(1, 1);
- options.outputSize = options.outputSize || options.stageSize;
- options.outputPosition = options.outputPosition || Vector3.Zero();
- options.outputRotation = options.outputRotation || Vector3.Zero();
- options.layerCount = options.layerCount || 1;
- options.maxAnimationFrames = options.maxAnimationFrames || 0;
- options.baseTile = options.baseTile || 0;
- options.flipU = options.flipU || false;
- options.colorMultiply = options.colorMultiply || new Vector3(1, 1, 1);
- this._scene = scene;
- this._frameMap = this._createFrameBuffer();
- this._tileMaps = new Array();
- for (let i = 0; i < options.layerCount; i++) {
- this._tileMaps.push(this._createTileBuffer(null, i));
- }
- this._animationMap = this._createTileAnimationBuffer(null);
- const defines = [];
- defines.push("#define LAYERS " + options.layerCount);
- if (options.flipU) {
- defines.push("#define FLIPU");
- }
- defines.push(`#define MAX_ANIMATION_FRAMES ${options.maxAnimationFrames}.0`);
- const shaderString = Effect.ShadersStore["spriteMapPixelShader"];
- let layerSampleString;
- if (!scene.getEngine()._features.supportSwitchCaseInShader) {
- layerSampleString = "";
- for (let i = 0; i < options.layerCount; i++) {
- layerSampleString += `if (${i} == i) { frameID = texture2D(tileMaps[${i}], (tileID + 0.5) / stageSize, 0.).x; }`;
- }
- }
- else {
- layerSampleString = "switch(i) {";
- for (let i = 0; i < options.layerCount; i++) {
- layerSampleString += "case " + i + " : frameID = texture(tileMaps[" + i + "], (tileID + 0.5) / stageSize, 0.).x;";
- layerSampleString += "break;";
- }
- layerSampleString += "}";
- }
- Effect.ShadersStore["spriteMap" + this.name + "PixelShader"] = shaderString.replace("#define LAYER_ID_SWITCH", layerSampleString);
- this._material = new ShaderMaterial("spriteMap:" + this.name, this._scene, {
- vertex: "spriteMap",
- fragment: "spriteMap" + this.name,
- }, {
- defines,
- attributes: ["position", "normal", "uv"],
- uniforms: ["worldViewProjection", "time", "stageSize", "outputSize", "spriteMapSize", "spriteCount", "time", "colorMul", "mousePosition", "curTile", "flipU"],
- samplers: ["spriteSheet", "frameMap", "tileMaps", "animationMap"],
- needAlphaBlending: true,
- });
- this._time = 0;
- this._material.setFloat("spriteCount", this.spriteCount);
- this._material.setVector2("stageSize", options.stageSize);
- this._material.setVector2("outputSize", options.outputSize);
- this._material.setTexture("spriteSheet", this.spriteSheet);
- this._material.setVector2("spriteMapSize", new Vector2(1, 1));
- this._material.setVector3("colorMul", options.colorMultiply);
- let tickSave = 0;
- const bindSpriteTexture = () => {
- if (this.spriteSheet && this.spriteSheet.isReady()) {
- if (this.spriteSheet._texture) {
- this._material.setVector2("spriteMapSize", new Vector2(this.spriteSheet._texture.baseWidth || 1, this.spriteSheet._texture.baseHeight || 1));
- return;
- }
- }
- if (tickSave < 100) {
- setTimeout(() => {
- tickSave++;
- bindSpriteTexture();
- }, 100);
- }
- };
- bindSpriteTexture();
- this._material.setVector3("colorMul", options.colorMultiply);
- this._material.setTexture("frameMap", this._frameMap);
- this._material.setTextureArray("tileMaps", this._tileMaps);
- this._material.setTexture("animationMap", this._animationMap);
- this._material.setFloat("time", this._time);
- this._output = CreatePlane(name + ":output", { size: 1, updatable: true }, scene);
- this._output.scaling.x = options.outputSize.x;
- this._output.scaling.y = options.outputSize.y;
- this.position = options.outputPosition;
- this.rotation = options.outputRotation;
- const obfunction = () => {
- this._time += this._scene.getEngine().getDeltaTime();
- this._material.setFloat("time", this._time);
- };
- this._scene.onBeforeRenderObservable.add(obfunction);
- this._output.material = this._material;
- }
- /**
- * Returns tileID location
- * @returns Vector2 the cell position ID
- */
- getTileID() {
- const p = this.getMousePosition();
- p.multiplyInPlace(this.options.stageSize || Vector2.Zero());
- p.x = Math.floor(p.x);
- p.y = Math.floor(p.y);
- return p;
- }
- /**
- * Gets the UV location of the mouse over the SpriteMap.
- * @returns Vector2 the UV position of the mouse interaction
- */
- getMousePosition() {
- const out = this._output;
- const pickinfo = this._scene.pick(this._scene.pointerX, this._scene.pointerY, (mesh) => {
- if (mesh !== out) {
- return false;
- }
- return true;
- });
- if (!pickinfo || !pickinfo.hit || !pickinfo.getTextureCoordinates) {
- return new Vector2(-1, -1);
- }
- const coords = pickinfo.getTextureCoordinates();
- if (coords) {
- return coords;
- }
- return new Vector2(-1, -1);
- }
- /**
- * Creates the "frame" texture Buffer
- * -------------------------------------
- * Structure of frames
- * "filename": "Falling-Water-2.png",
- * "frame": {"x":69,"y":103,"w":24,"h":32},
- * "rotated": true,
- * "trimmed": true,
- * "spriteSourceSize": {"x":4,"y":0,"w":24,"h":32},
- * "sourceSize": {"w":32,"h":32}
- * @returns RawTexture of the frameMap
- */
- _createFrameBuffer() {
- const data = [];
- //Do two Passes
- for (let i = 0; i < this.spriteCount; i++) {
- data.push(0, 0, 0, 0); //frame
- data.push(0, 0, 0, 0); //spriteSourceSize
- data.push(0, 0, 0, 0); //sourceSize, rotated, trimmed
- data.push(0, 0, 0, 0); //Keep it pow2 cause I"m cool like that... it helps with sampling accuracy as well. Plus then we have 4 other parameters for future stuff.
- }
- //Second Pass
- for (let i = 0; i < this.spriteCount; i++) {
- const f = this.sprites[i]["frame"];
- const sss = this.sprites[i]["spriteSourceSize"];
- const ss = this.sprites[i]["sourceSize"];
- const r = this.sprites[i]["rotated"] ? 1 : 0;
- const t = this.sprites[i]["trimmed"] ? 1 : 0;
- //frame
- data[i * 4] = f.x;
- data[i * 4 + 1] = f.y;
- data[i * 4 + 2] = f.w;
- data[i * 4 + 3] = f.h;
- //spriteSourceSize
- data[i * 4 + this.spriteCount * 4] = sss.x;
- data[i * 4 + 1 + this.spriteCount * 4] = sss.y;
- data[i * 4 + 3 + this.spriteCount * 4] = sss.h;
- //sourceSize, rotated, trimmed
- data[i * 4 + this.spriteCount * 8] = ss.w;
- data[i * 4 + 1 + this.spriteCount * 8] = ss.h;
- data[i * 4 + 2 + this.spriteCount * 8] = r;
- data[i * 4 + 3 + this.spriteCount * 8] = t;
- }
- const floatArray = new Float32Array(data);
- const t = RawTexture.CreateRGBATexture(floatArray, this.spriteCount, 4, this._scene, false, false, Texture.NEAREST_NEAREST, Engine.TEXTURETYPE_FLOAT);
- return t;
- }
- /**
- * Creates the tileMap texture Buffer
- * @param buffer normally and array of numbers, or a false to generate from scratch
- * @param _layer indicates what layer for a logic trigger dealing with the baseTile. The system uses this
- * @returns RawTexture of the tileMap
- */
- _createTileBuffer(buffer, _layer = 0) {
- let data = [];
- const _ty = this.options.stageSize.y || 0;
- const _tx = this.options.stageSize.x || 0;
- if (!buffer) {
- let bt = this.options.baseTile;
- if (_layer != 0) {
- bt = 0;
- }
- for (let y = 0; y < _ty; y++) {
- for (let x = 0; x < _tx * 4; x += 4) {
- data.push(bt, 0, 0, 0);
- }
- }
- }
- else {
- data = buffer;
- }
- const floatArray = new Float32Array(data);
- const t = RawTexture.CreateRGBATexture(floatArray, _tx, _ty, this._scene, false, false, Texture.NEAREST_NEAREST, Engine.TEXTURETYPE_FLOAT);
- return t;
- }
- /**
- * Modifies the data of the tileMaps
- * @param _layer is the ID of the layer you want to edit on the SpriteMap
- * @param pos is the iVector2 Coordinates of the Tile
- * @param tile The SpriteIndex of the new Tile
- */
- changeTiles(_layer = 0, pos, tile = 0) {
- const buffer = this._tileMaps[_layer]._texture._bufferView;
- if (buffer === null) {
- return;
- }
- let p = [];
- if (pos instanceof Vector2) {
- p.push(pos);
- }
- else {
- p = pos;
- }
- const _tx = this.options.stageSize.x || 0;
- for (let i = 0; i < p.length; i++) {
- const _p = p[i];
- _p.x = Math.floor(_p.x);
- _p.y = Math.floor(_p.y);
- const id = _p.x * 4 + _p.y * (_tx * 4);
- buffer[id] = tile;
- }
- const t = this._createTileBuffer(buffer);
- this._tileMaps[_layer].dispose();
- this._tileMaps[_layer] = t;
- this._material.setTextureArray("tileMap", this._tileMaps);
- }
- /**
- * Creates the animationMap texture Buffer
- * @param buffer normally and array of numbers, or a false to generate from scratch
- * @returns RawTexture of the animationMap
- */
- _createTileAnimationBuffer(buffer) {
- const data = [];
- let floatArray;
- if (!buffer) {
- for (let i = 0; i < this.spriteCount; i++) {
- data.push(0, 0, 0, 0);
- let count = 1;
- while (count < (this.options.maxAnimationFrames || 4)) {
- data.push(0, 0, 0, 0);
- count++;
- }
- }
- floatArray = new Float32Array(data);
- }
- else {
- floatArray = buffer;
- }
- const t = RawTexture.CreateRGBATexture(floatArray, this.spriteCount, this.options.maxAnimationFrames || 4, this._scene, false, false, Texture.NEAREST_NEAREST, Engine.TEXTURETYPE_FLOAT);
- return t;
- }
- /**
- * Modifies the data of the animationMap
- * @param cellID is the Index of the Sprite
- * @param _frame is the target Animation frame
- * @param toCell is the Target Index of the next frame of the animation
- * @param time is a value between 0-1 that is the trigger for when the frame should change tiles
- * @param speed is a global scalar of the time variable on the map.
- */
- addAnimationToTile(cellID = 0, _frame = 0, toCell = 0, time = 0, speed = 1) {
- const buffer = this._animationMap._texture._bufferView;
- const id = cellID * 4 + this.spriteCount * 4 * _frame;
- if (!buffer) {
- return;
- }
- buffer[id] = toCell;
- buffer[id + 1] = time;
- buffer[id + 2] = speed;
- const t = this._createTileAnimationBuffer(buffer);
- this._animationMap.dispose();
- this._animationMap = t;
- this._material.setTexture("animationMap", this._animationMap);
- }
- /**
- * Exports the .tilemaps file
- */
- saveTileMaps() {
- let maps = "";
- for (let i = 0; i < this._tileMaps.length; i++) {
- if (i > 0) {
- maps += "\n\r";
- }
- maps += this._tileMaps[i]._texture._bufferView.toString();
- }
- const hiddenElement = document.createElement("a");
- hiddenElement.href = "data:octet/stream;charset=utf-8," + encodeURI(maps);
- hiddenElement.target = "_blank";
- hiddenElement.download = this.name + ".tilemaps";
- hiddenElement.click();
- hiddenElement.remove();
- }
- /**
- * Imports the .tilemaps file
- * @param url of the .tilemaps file
- */
- loadTileMaps(url) {
- const xhr = new XMLHttpRequest();
- xhr.open("GET", url);
- const _lc = this.options.layerCount || 0;
- xhr.onload = () => {
- const data = xhr.response.split("\n\r");
- for (let i = 0; i < _lc; i++) {
- const d = data[i].split(",").map(Number);
- const t = this._createTileBuffer(d);
- this._tileMaps[i].dispose();
- this._tileMaps[i] = t;
- }
- this._material.setTextureArray("tileMap", this._tileMaps);
- };
- xhr.send();
- }
- /**
- * Release associated resources
- */
- dispose() {
- this._output.dispose();
- this._material.dispose();
- this._animationMap.dispose();
- this._tileMaps.forEach((tm) => {
- tm.dispose();
- });
- this._frameMap.dispose();
- }
- }
- //# sourceMappingURL=spriteMap.js.map
|