use texture for rendering ships
This commit is contained in:
parent
6e494aca46
commit
d092f5d89c
6 changed files with 96 additions and 88 deletions
BIN
web/pw-visualizer/assets/res/ship.png
Normal file
BIN
web/pw-visualizer/assets/res/ship.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
|
@ -2,20 +2,20 @@
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
width="10.231839cm"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
height="19.597593cm"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
viewBox="0 0 102.31839 195.97593"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="100mm"
|
|
||||||
height="100mm"
|
|
||||||
viewBox="0 0 100 99.999999"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg8"
|
id="svg8"
|
||||||
sodipodi:docname="ship.svg"
|
sodipodi:docname="ship.svg"
|
||||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs2" />
|
id="defs2" />
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="1.0993438"
|
inkscape:zoom="1.0993438"
|
||||||
inkscape:cx="676.08563"
|
inkscape:cx="424.34405"
|
||||||
inkscape:cy="474.10966"
|
inkscape:cy="384.32017"
|
||||||
inkscape:document-units="mm"
|
inkscape:document-units="mm"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
|
@ -35,15 +35,19 @@
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:window-width="2560"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1417"
|
inkscape:window-height="1048"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="32"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="1"
|
||||||
gridtolerance="10">
|
gridtolerance="10"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
units="cm">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid894" />
|
id="grid894"
|
||||||
|
originx="-160.50747"
|
||||||
|
originy="118.75037" />
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata5">
|
id="metadata5">
|
||||||
|
@ -53,7 +57,6 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -61,17 +64,17 @@
|
||||||
inkscape:label="Layer 1"
|
inkscape:label="Layer 1"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer1"
|
id="layer1"
|
||||||
transform="translate(229.05372,-117.27915)">
|
transform="translate(68.546255,1.4712222)">
|
||||||
<ellipse
|
<ellipse
|
||||||
ry="79.47506"
|
ry="79.47506"
|
||||||
rx="48.672089"
|
rx="48.672089"
|
||||||
cy="39.779182"
|
cy="39.779182"
|
||||||
cx="439.0813"
|
cx="439.0813"
|
||||||
id="ellipse888"
|
id="ellipse888"
|
||||||
style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:none;stroke-width:6.61458302;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:none;stroke-width:6.61458;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:24.99999809;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 800 -448.82031 C 800 -448.82031 640 -342.04689 640 -92.046875 C 640 -16.101728 661.67774 51.924976 695.88672 97.775391 C 640.96482 152.90966 593.39426 234.74166 610 267.95312 C 620.00003 287.95314 720.00001 297.95311 730 287.95312 C 751.8315 266.12161 757.39662 198.03742 758.92773 149.62305 C 772.03579 155.04778 785.80002 157.95312 800 157.95312 C 814.19998 157.95312 827.96422 155.04778 841.07227 149.62305 C 842.60338 198.03742 848.1685 266.12161 870 287.95312 C 879.99999 297.95311 979.99997 287.95314 990 267.95312 C 1006.6057 234.74166 959.03518 152.90966 904.11328 97.775391 C 938.32226 51.924976 959.99999 -16.101728 960 -92.046875 C 960.00002 -342.04689 800 -448.82031 800 -448.82031 z M 800 -352.04688 C 800 -352.04688 908.96486 -279.33252 908.96484 -109.07617 C 908.96484 -15.046189 860.17918 61.179688 800 61.179688 C 739.82082 61.179688 691.03515 -15.046189 691.03516 -109.07617 C 691.03516 -279.33252 800 -352.04687 800 -352.04688 z "
|
d="m 800,-448.82031 c 0,0 -160,106.77342 -160,356.773435 0,75.945147 21.67774,143.971851 55.88672,189.822266 C 640.96482,152.90966 593.39426,234.74166 610,267.95312 c 10.00003,20.00002 110.00001,29.99999 120,20 21.8315,-21.83151 27.39662,-89.9157 28.92773,-138.33007 13.10806,5.42473 26.87229,8.33007 41.07227,8.33007 14.19998,0 27.96422,-2.90534 41.07227,-8.33007 1.53111,48.41437 7.09623,116.49856 28.92773,138.33007 9.99999,9.99999 109.99997,2e-5 120,-20 C 1006.6057,234.74166 959.03518,152.90966 904.11328,97.775391 938.32226,51.924976 959.99999,-16.101728 960,-92.046875 960.00002,-342.04689 800,-448.82031 800,-448.82031 Z m 0,96.77343 c 0,0 108.96486,72.71436 108.96484,242.97071 0,94.029981 -48.78566,170.255858 -108.96484,170.255858 -60.17918,0 -108.96485,-76.225877 -108.96484,-170.255858 C 691.03516,-279.33252 800,-352.04687 800,-352.04688 Z"
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,-229.05372,117.27915)"
|
transform="matrix(0.26458333,0,0,0.26458333,-229.05372,117.27915)"
|
||||||
id="path4600" />
|
id="path4600" />
|
||||||
</g>
|
</g>
|
||||||
|
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
@ -5,6 +5,7 @@ export {default as marsSvg} from "../assets/res/mars.svg";
|
||||||
export {default as venusSvg} from "../assets/res/venus.svg";
|
export {default as venusSvg} from "../assets/res/venus.svg";
|
||||||
|
|
||||||
export {default as earthPng} from "../assets/res/earth.png";
|
export {default as earthPng} from "../assets/res/earth.png";
|
||||||
|
export {default as shipPng} from "../assets/res/ship.png";
|
||||||
|
|
||||||
export {default as fontPng} from "../assets/res/font.png";
|
export {default as fontPng} from "../assets/res/font.png";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { Game } from "planetwars-rs";
|
import { Game } from "planetwars-rs";
|
||||||
// import { memory } from "planetwars-rs/planetwars_rs_bg";
|
|
||||||
// const memory = planetwars_bg.memory;
|
|
||||||
import type { Dictionary } from './webgl/util';
|
import type { Dictionary } from './webgl/util';
|
||||||
import type { BBox } from "./voronoi/voronoi-core";
|
import type { BBox } from "./voronoi/voronoi-core";
|
||||||
|
|
||||||
|
@ -8,8 +6,6 @@ import {
|
||||||
Resizer,
|
Resizer,
|
||||||
resizeCanvasToDisplaySize,
|
resizeCanvasToDisplaySize,
|
||||||
FPSCounter,
|
FPSCounter,
|
||||||
url_to_mesh,
|
|
||||||
Mesh,
|
|
||||||
} from "./webgl/util";
|
} from "./webgl/util";
|
||||||
import {
|
import {
|
||||||
Shader,
|
Shader,
|
||||||
|
@ -40,14 +36,6 @@ function to_bbox(box: number[]): BBox {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// function f32v(ptr: number, size: number): Float32Array {
|
|
||||||
// return new Float32Array(memory.buffer, ptr, size);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function i32v(ptr: number, size: number): Int32Array {
|
|
||||||
// return new Int32Array(memory.buffer, ptr, size);
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function set_game_name(name: string) {
|
export function set_game_name(name: string) {
|
||||||
ELEMENTS["name"].innerHTML = name;
|
ELEMENTS["name"].innerHTML = name;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +130,7 @@ export class GameInstance {
|
||||||
|
|
||||||
ship_ibo: IndexBuffer;
|
ship_ibo: IndexBuffer;
|
||||||
ship_vao: VertexArray;
|
ship_vao: VertexArray;
|
||||||
|
ship_texture: Texture;
|
||||||
// TODO: find a better way
|
// TODO: find a better way
|
||||||
max_num_ships: number;
|
max_num_ships: number;
|
||||||
|
|
||||||
|
@ -161,8 +150,9 @@ export class GameInstance {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
game: Game,
|
game: Game,
|
||||||
meshes: Mesh[],
|
planets_textures: Texture[],
|
||||||
ship_mesh: Mesh,
|
ship_texture: Texture,
|
||||||
|
font_texture: Texture,
|
||||||
shaders: Dictionary<ShaderFactory>
|
shaders: Dictionary<ShaderFactory>
|
||||||
) {
|
) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
@ -178,10 +168,12 @@ export class GameInstance {
|
||||||
});
|
});
|
||||||
this.masked_image_shader = shaders["masked_image"].create_shader(GL);
|
this.masked_image_shader = shaders["masked_image"].create_shader(GL);
|
||||||
|
|
||||||
this.text_factory = defaultLabelFactory(GL, this.image_shader);
|
this.text_factory = defaultLabelFactory(GL, font_texture, this.image_shader);
|
||||||
this.planet_labels = [];
|
this.planet_labels = [];
|
||||||
this.ship_labels = [];
|
this.ship_labels = [];
|
||||||
|
|
||||||
|
this.ship_texture = ship_texture
|
||||||
|
|
||||||
this.resizer = new Resizer(CANVAS, [...game.get_viewbox()], true);
|
this.resizer = new Resizer(CANVAS, [...game.get_viewbox()], true);
|
||||||
this.renderer = new Renderer();
|
this.renderer = new Renderer();
|
||||||
this.game.update_turn(0);
|
this.game.update_turn(0);
|
||||||
|
@ -191,15 +183,8 @@ export class GameInstance {
|
||||||
|
|
||||||
// List of [(x, y, r)] for all planets
|
// List of [(x, y, r)] for all planets
|
||||||
this._create_voronoi(planets);
|
this._create_voronoi(planets);
|
||||||
this._create_planets(planets, meshes);
|
this._create_planets(planets, planets_textures);
|
||||||
|
|
||||||
// create_shipes
|
|
||||||
this.ship_ibo = new IndexBuffer(GL, ship_mesh.cells);
|
|
||||||
const ship_positions = new VertexBuffer(GL, ship_mesh.positions);
|
|
||||||
const ship_layout = new VertexBufferLayout();
|
|
||||||
ship_layout.push(GL.FLOAT, 3, 4, "a_position");
|
|
||||||
this.ship_vao = new VertexArray();
|
|
||||||
this.ship_vao.addBuffer(ship_positions, ship_layout);
|
|
||||||
this.max_num_ships = 0;
|
this.max_num_ships = 0;
|
||||||
|
|
||||||
// Set slider correctly
|
// Set slider correctly
|
||||||
|
@ -236,9 +221,7 @@ export class GameInstance {
|
||||||
this.renderer.addRenderable(this.vor_builder.getRenderable(), LAYERS.vor);
|
this.renderer.addRenderable(this.vor_builder.getRenderable(), LAYERS.vor);
|
||||||
}
|
}
|
||||||
|
|
||||||
_create_planets(planets: Float32Array, meshes: Mesh[]) {
|
_create_planets(planets: Float32Array, planets_textures: Texture[]) {
|
||||||
const earth = Texture.fromImage(GL, assets.earthPng, 'earth');
|
|
||||||
|
|
||||||
for (let i = 0; i < this.planet_count; i++) {
|
for (let i = 0; i < this.planet_count; i++) {
|
||||||
{
|
{
|
||||||
const transform = new UniformMatrix3fv([
|
const transform = new UniformMatrix3fv([
|
||||||
|
@ -280,7 +263,7 @@ export class GameInstance {
|
||||||
u_trans_next: transform,
|
u_trans_next: transform,
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderable = new DefaultRenderable(ib, vao, this.masked_image_shader, [earth], uniforms);
|
const renderable = new DefaultRenderable(ib, vao, this.masked_image_shader, [planets_textures[0]], uniforms);
|
||||||
|
|
||||||
this.renderer.addRenderable(renderable, LAYERS.planet);
|
this.renderer.addRenderable(renderable, LAYERS.planet);
|
||||||
|
|
||||||
|
@ -361,16 +344,39 @@ export class GameInstance {
|
||||||
const ship_colours = this.game.get_ship_colours();
|
const ship_colours = this.game.get_ship_colours();
|
||||||
|
|
||||||
for (let i = this.max_num_ships; i < ship_counts.length; i++) {
|
for (let i = this.max_num_ships; i < ship_counts.length; i++) {
|
||||||
this.renderer.addToDraw(
|
const gl = GL;
|
||||||
this.ship_ibo,
|
const ib = new IndexBuffer(gl, [
|
||||||
this.ship_vao,
|
0, 1, 2,
|
||||||
this.shader,
|
1, 2, 3
|
||||||
{},
|
]);
|
||||||
[],
|
const ratio = this.ship_texture.getWidth() / this.ship_texture.getHeight();
|
||||||
LAYERS.ship
|
const vb_pos = new VertexBuffer(gl, [
|
||||||
);
|
-ratio, 1,
|
||||||
|
ratio, 1,
|
||||||
|
-ratio, -1,
|
||||||
|
ratio, -1
|
||||||
|
]);
|
||||||
|
const vb_tex = new VertexBuffer(gl, [
|
||||||
|
0, 0,
|
||||||
|
1, 0,
|
||||||
|
0, 1,
|
||||||
|
1, 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const layout_pos = new VertexBufferLayout();
|
||||||
|
layout_pos.push(gl.FLOAT, 2, 4, "a_position");
|
||||||
|
|
||||||
|
const layout_tex = new VertexBufferLayout();
|
||||||
|
layout_tex.push(gl.FLOAT, 2, 4, "a_texCoord");
|
||||||
|
|
||||||
|
const vao = new VertexArray();
|
||||||
|
vao.addBuffer(vb_pos, layout_pos);
|
||||||
|
vao.addBuffer(vb_tex, layout_tex);
|
||||||
|
|
||||||
|
const renderable = new DefaultRenderable(ib, vao, this.masked_image_shader, [this.ship_texture], {});
|
||||||
|
this.renderer.addRenderable(renderable, LAYERS.ship);
|
||||||
const label = this.text_factory.build(GL);
|
const label = this.text_factory.build(GL);
|
||||||
|
|
||||||
this.ship_labels.push(label);
|
this.ship_labels.push(label);
|
||||||
this.renderer.addRenderable(label.getRenderable(), LAYERS.ship_label);
|
this.renderer.addRenderable(label.getRenderable(), LAYERS.ship_label);
|
||||||
}
|
}
|
||||||
|
@ -579,18 +585,17 @@ export class GameInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
var game_instance: GameInstance;
|
var game_instance: GameInstance;
|
||||||
var meshes: Mesh[];
|
var textures: Texture[];
|
||||||
var shaders: Dictionary<ShaderFactory>;
|
var shaders: Dictionary<ShaderFactory>;
|
||||||
|
|
||||||
export async function set_instance(source: string): Promise<GameInstance> {
|
export async function set_instance(source: string): Promise<GameInstance> {
|
||||||
// TODO: embed shader programs
|
// TODO: embed shader programs
|
||||||
if (!meshes || !shaders) {
|
if (!textures || !shaders) {
|
||||||
const mesh_promises = [
|
const texture_promises = [
|
||||||
assets.shipSvg,
|
Texture.fromImage(GL, assets.fontPng, "font"),
|
||||||
assets.earthSvg,
|
Texture.fromImage(GL, assets.shipPng, "ship"),
|
||||||
assets.marsSvg,
|
Texture.fromImage(GL, assets.earthPng, "earth")
|
||||||
assets.venusSvg,
|
];
|
||||||
].map(url_to_mesh);
|
|
||||||
|
|
||||||
const shader_promies = [
|
const shader_promies = [
|
||||||
(async () =>
|
(async () =>
|
||||||
|
@ -628,8 +633,8 @@ export async function set_instance(source: string): Promise<GameInstance> {
|
||||||
|
|
||||||
];
|
];
|
||||||
let shaders_array: [string, ShaderFactory][];
|
let shaders_array: [string, ShaderFactory][];
|
||||||
[meshes, shaders_array] = await Promise.all([
|
[textures, shaders_array] = await Promise.all([
|
||||||
Promise.all(mesh_promises),
|
Promise.all(texture_promises),
|
||||||
Promise.all(shader_promies),
|
Promise.all(shader_promies),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -641,8 +646,9 @@ export async function set_instance(source: string): Promise<GameInstance> {
|
||||||
|
|
||||||
game_instance = new GameInstance(
|
game_instance = new GameInstance(
|
||||||
Game.new(source),
|
Game.new(source),
|
||||||
meshes.slice(1),
|
textures.slice(2),
|
||||||
meshes[0],
|
textures[1],
|
||||||
|
textures[0],
|
||||||
shaders
|
shaders
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@ export class LabelFactory {
|
||||||
font: FontInfo;
|
font: FontInfo;
|
||||||
shader: Shader;
|
shader: Shader;
|
||||||
|
|
||||||
constructor(gl: WebGLRenderingContext, loc: string, font: FontInfo, shader: Shader) {
|
constructor(gl: WebGLRenderingContext, fontTexture: Texture, font: FontInfo, shader: Shader) {
|
||||||
this.texture = Texture.fromImage(gl, loc, 'font');
|
this.texture = fontTexture;
|
||||||
this.font = font;
|
this.font = font;
|
||||||
this.shader = shader;
|
this.shader = shader;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ export class Label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultLabelFactory(gl: WebGLRenderingContext, shader: Shader): LabelFactory {
|
export function defaultLabelFactory(gl: WebGLRenderingContext, fontTexture: Texture, shader: Shader): LabelFactory {
|
||||||
const fontInfo = {
|
const fontInfo = {
|
||||||
letterHeight: 8,
|
letterHeight: 8,
|
||||||
spaceWidth: 8,
|
spaceWidth: 8,
|
||||||
|
@ -195,5 +195,5 @@ export function defaultLabelFactory(gl: WebGLRenderingContext, shader: Shader):
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return new LabelFactory(gl, fontPng, fontInfo, shader);
|
return new LabelFactory(gl, fontTexture, fontInfo, shader);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,18 @@ export class Texture {
|
||||||
gl: WebGLRenderingContext,
|
gl: WebGLRenderingContext,
|
||||||
path: string,
|
path: string,
|
||||||
name: string,
|
name: string,
|
||||||
): Texture {
|
): Promise<Texture> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
const out = new Texture(gl, name);
|
const out = new Texture(gl, name);
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.onload = out.setImage.bind(out, gl, image);
|
image.onload = () => {
|
||||||
image.onerror = error;
|
out.setImage(gl, image);
|
||||||
|
resolve(out);
|
||||||
|
}
|
||||||
|
image.onerror = reject;
|
||||||
image.src = path;
|
image.src = path;
|
||||||
|
})
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromRenderer(
|
static fromRenderer(
|
||||||
|
@ -99,8 +102,3 @@ export class Texture {
|
||||||
return this.height;
|
return this.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(e: any) {
|
|
||||||
console.error("IMAGE LOAD ERROR");
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue