mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 02:26:00 +01:00
Implement animated textures
This commit is contained in:
parent
2899646adc
commit
aecbd23ba7
@ -294,7 +294,7 @@ export class MapViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// render
|
// render
|
||||||
if (delta >= 1000 || Date.now() - this.lastRedrawChange < 1000) {
|
if (delta >= 50 || Date.now() - this.lastRedrawChange < 1000) {
|
||||||
this.lastFrame = now;
|
this.lastFrame = now;
|
||||||
this.render(delta);
|
this.render(delta);
|
||||||
}
|
}
|
||||||
@ -325,6 +325,8 @@ export class MapViewer {
|
|||||||
|
|
||||||
if (this.map && this.map.isLoaded) {
|
if (this.map && this.map.isLoaded) {
|
||||||
|
|
||||||
|
this.map.animations.forEach(animation => animation.step(delta))
|
||||||
|
|
||||||
// shift whole scene including camera towards 0,0 to tackle shader-precision issues
|
// shift whole scene including camera towards 0,0 to tackle shader-precision issues
|
||||||
const s = 10000;
|
const s = 10000;
|
||||||
const sX = Math.round(this.camera.position.x / s) * s;
|
const sX = Math.round(this.camera.position.x / s) * s;
|
||||||
|
@ -39,6 +39,7 @@ import {TileManager} from "./TileManager";
|
|||||||
import {TileLoader} from "./TileLoader";
|
import {TileLoader} from "./TileLoader";
|
||||||
import {LowresTileLoader} from "./LowresTileLoader";
|
import {LowresTileLoader} from "./LowresTileLoader";
|
||||||
import {reactive} from "vue";
|
import {reactive} from "vue";
|
||||||
|
import {TextureAnimation} from "@/js/map/TextureAnimation";
|
||||||
|
|
||||||
export class Map {
|
export class Map {
|
||||||
|
|
||||||
@ -86,6 +87,9 @@ export class Map {
|
|||||||
/** @type {Texture[]} */
|
/** @type {Texture[]} */
|
||||||
this.loadedTextures = [];
|
this.loadedTextures = [];
|
||||||
|
|
||||||
|
/** @type {TextureAnimation[]} */
|
||||||
|
this.animations = [];
|
||||||
|
|
||||||
/** @type {TileManager} */
|
/** @type {TileManager} */
|
||||||
this.hiresTileManager = null;
|
this.hiresTileManager = null;
|
||||||
/** @type {TileManager[]} */
|
/** @type {TileManager[]} */
|
||||||
@ -264,7 +268,8 @@ export class Map {
|
|||||||
* resourcePath: string,
|
* resourcePath: string,
|
||||||
* color: number[],
|
* color: number[],
|
||||||
* halfTransparent: boolean,
|
* halfTransparent: boolean,
|
||||||
* texture: string
|
* texture: string,
|
||||||
|
* animation: any | undefined
|
||||||
* }[]} the textures-data
|
* }[]} the textures-data
|
||||||
* @returns {ShaderMaterial[]} the hires Material (array because its a multi-material)
|
* @returns {ShaderMaterial[]} the hires Material (array because its a multi-material)
|
||||||
*/
|
*/
|
||||||
@ -293,7 +298,24 @@ export class Map {
|
|||||||
texture.wrapT = ClampToEdgeWrapping;
|
texture.wrapT = ClampToEdgeWrapping;
|
||||||
texture.flipY = false;
|
texture.flipY = false;
|
||||||
texture.flatShading = true;
|
texture.flatShading = true;
|
||||||
texture.image.addEventListener("load", () => texture.needsUpdate = true);
|
|
||||||
|
let animationUniforms = {
|
||||||
|
animationFrameHeight: { value: 1 },
|
||||||
|
animationFrameIndex: { value: 0 },
|
||||||
|
animationInterpolationFrameIndex: { value: 0 },
|
||||||
|
animationInterpolation: { value: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let animation = null;
|
||||||
|
if (textureSettings.animation) {
|
||||||
|
animation = new TextureAnimation(animationUniforms, textureSettings.animation);
|
||||||
|
this.animations.push(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.image.addEventListener("load", () => {
|
||||||
|
texture.needsUpdate = true
|
||||||
|
if (animation) animation.init(texture.image.naturalWidth, texture.image.naturalHeight)
|
||||||
|
});
|
||||||
|
|
||||||
this.loadedTextures.push(texture);
|
this.loadedTextures.push(texture);
|
||||||
|
|
||||||
@ -304,7 +326,8 @@ export class Map {
|
|||||||
type: 't',
|
type: 't',
|
||||||
value: texture
|
value: texture
|
||||||
},
|
},
|
||||||
transparent: { value: transparent }
|
transparent: { value: transparent },
|
||||||
|
...animationUniforms
|
||||||
},
|
},
|
||||||
vertexShader: vertexShader,
|
vertexShader: vertexShader,
|
||||||
fragmentShader: fragmentShader,
|
fragmentShader: fragmentShader,
|
||||||
@ -363,6 +386,8 @@ export class Map {
|
|||||||
|
|
||||||
this.loadedTextures.forEach(texture => texture.dispose());
|
this.loadedTextures.forEach(texture => texture.dispose());
|
||||||
this.loadedTextures = [];
|
this.loadedTextures = [];
|
||||||
|
|
||||||
|
this.animations = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
83
BlueMapCommon/webapp/src/js/map/TextureAnimation.js
Normal file
83
BlueMapCommon/webapp/src/js/map/TextureAnimation.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export class TextureAnimation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uniforms {{
|
||||||
|
* animationFrameHeight: { value: number },
|
||||||
|
* animationFrameIndex: { value: number },
|
||||||
|
* animationInterpolationFrameIndex: { value: number },
|
||||||
|
* animationInterpolation: { value: number }
|
||||||
|
* }}
|
||||||
|
* @param data {{
|
||||||
|
* interpolate: boolean,
|
||||||
|
* width: number,
|
||||||
|
* height: number,
|
||||||
|
* frametime: number,
|
||||||
|
* frames: {
|
||||||
|
* index: number,
|
||||||
|
* time: number
|
||||||
|
* }[] | undefined
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
constructor(uniforms, data) {
|
||||||
|
this.uniforms = uniforms;
|
||||||
|
this.data = {
|
||||||
|
interpolate: false,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
frametime: 1,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
this.frameImages = 1;
|
||||||
|
this.frameDelta = 0;
|
||||||
|
this.frameTime = this.data.frametime * 50;
|
||||||
|
this.frames = 1;
|
||||||
|
this.frameIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param width {number}
|
||||||
|
* @param height {number}
|
||||||
|
*/
|
||||||
|
init(width, height) {
|
||||||
|
this.frameImages = height / width;
|
||||||
|
this.uniforms.animationFrameHeight.value = 1 / this.frameImages;
|
||||||
|
this.frames = this.frameImages;
|
||||||
|
if (this.data.frames) {
|
||||||
|
this.frames = this.data.frames.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param delta {number}
|
||||||
|
*/
|
||||||
|
step(delta) {
|
||||||
|
this.frameDelta += delta;
|
||||||
|
|
||||||
|
if (this.frameDelta > this.frameTime) {
|
||||||
|
this.frameDelta -= this.frameTime;
|
||||||
|
this.frameDelta %= this.frameTime;
|
||||||
|
|
||||||
|
this.frameIndex++;
|
||||||
|
this.frameIndex %= this.frames;
|
||||||
|
|
||||||
|
if (this.data.frames) {
|
||||||
|
let frame = this.data.frames[this.frameIndex]
|
||||||
|
let nextFrame = this.data.frames[(this.frameIndex + 1) % this.frames];
|
||||||
|
|
||||||
|
this.uniforms.animationFrameIndex.value = frame.index;
|
||||||
|
this.uniforms.animationInterpolationFrameIndex.value = nextFrame.index;
|
||||||
|
this.frameTime = frame.time * 50;
|
||||||
|
} else {
|
||||||
|
this.uniforms.animationFrameIndex.value = this.frameIndex;
|
||||||
|
this.uniforms.animationInterpolationFrameIndex.value = (this.frameIndex + 1) % this.frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.data.interpolate) {
|
||||||
|
this.uniforms.animationInterpolation.value = this.frameDelta / this.frameTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,6 +34,10 @@ ${ShaderChunk.logdepthbuf_pars_fragment}
|
|||||||
uniform sampler2D textureImage;
|
uniform sampler2D textureImage;
|
||||||
uniform float sunlightStrength;
|
uniform float sunlightStrength;
|
||||||
uniform float ambientLight;
|
uniform float ambientLight;
|
||||||
|
uniform float animationFrameHeight;
|
||||||
|
uniform float animationFrameIndex;
|
||||||
|
uniform float animationInterpolationFrameIndex;
|
||||||
|
uniform float animationInterpolation;
|
||||||
|
|
||||||
varying vec3 vPosition;
|
varying vec3 vPosition;
|
||||||
//varying vec3 vWorldPosition;
|
//varying vec3 vWorldPosition;
|
||||||
@ -46,7 +50,12 @@ varying float vBlocklight;
|
|||||||
//varying float vDistance;
|
//varying float vDistance;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture(textureImage, vUv);
|
|
||||||
|
vec4 color = texture(textureImage, vec2(vUv.x, animationFrameHeight * (vUv.y + animationFrameIndex)));
|
||||||
|
if (animationInterpolation > 0.0) {
|
||||||
|
color = mix(color, texture(textureImage, vec2(vUv.x, animationFrameHeight * (vUv.y + animationInterpolationFrameIndex))), animationInterpolation);
|
||||||
|
}
|
||||||
|
|
||||||
if (color.a <= 0.01) discard;
|
if (color.a <= 0.01) discard;
|
||||||
|
|
||||||
//apply vertex-color
|
//apply vertex-color
|
||||||
|
@ -33,7 +33,7 @@ export const VEC3_Z = new Vector3(0, 0, 1);
|
|||||||
/**
|
/**
|
||||||
* Converts a url-encoded image string to an actual image-element
|
* Converts a url-encoded image string to an actual image-element
|
||||||
* @param string {string}
|
* @param string {string}
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLImageElement}
|
||||||
*/
|
*/
|
||||||
export const stringToImage = string => {
|
export const stringToImage = string => {
|
||||||
let image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
|
let image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.BlockModel;
|
import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.BlockModel;
|
||||||
import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.TextureVariable;
|
import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.TextureVariable;
|
||||||
import de.bluecolored.bluemap.core.resources.resourcepack.blockstate.BlockState;
|
import de.bluecolored.bluemap.core.resources.resourcepack.blockstate.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.resources.resourcepack.texture.AnimationMeta;
|
||||||
import de.bluecolored.bluemap.core.resources.resourcepack.texture.Texture;
|
import de.bluecolored.bluemap.core.resources.resourcepack.texture.Texture;
|
||||||
import de.bluecolored.bluemap.core.util.Tristate;
|
import de.bluecolored.bluemap.core.util.Tristate;
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
@ -50,6 +51,8 @@
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -381,9 +384,23 @@ private void loadTextures(Path root) throws IOException {
|
|||||||
ResourcePath<Texture> resourcePath = new ResourcePath<>(root.relativize(file));
|
ResourcePath<Texture> resourcePath = new ResourcePath<>(root.relativize(file));
|
||||||
if (!usedTextures.contains(resourcePath)) return null; // don't load unused textures
|
if (!usedTextures.contains(resourcePath)) return null; // don't load unused textures
|
||||||
|
|
||||||
|
// load image
|
||||||
|
BufferedImage image;
|
||||||
try (InputStream in = Files.newInputStream(file)) {
|
try (InputStream in = Files.newInputStream(file)) {
|
||||||
return Texture.from(resourcePath, ImageIO.read(in), Files.exists(file.resolveSibling(file.getFileName() + ".mcmeta")));
|
image = ImageIO.read(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load animation
|
||||||
|
AnimationMeta animation = null;
|
||||||
|
Path animationPathFile = file.resolveSibling(file.getFileName() + ".mcmeta");
|
||||||
|
if (Files.exists(animationPathFile)) {
|
||||||
|
try (Reader in = Files.newBufferedReader(animationPathFile, StandardCharsets.UTF_8)) {
|
||||||
|
animation = ResourcesGson.INSTANCE.fromJson(in, AnimationMeta.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Texture.from(resourcePath, image, animation);
|
||||||
|
|
||||||
}, textures));
|
}, textures));
|
||||||
|
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
package de.bluecolored.bluemap.core.resources.resourcepack.texture;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonToken;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import de.bluecolored.bluemap.core.resources.AbstractTypeAdapterFactory;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JsonAdapter(AnimationMeta.Adapter.class)
|
||||||
|
public class AnimationMeta {
|
||||||
|
|
||||||
|
private boolean interpolate = false;
|
||||||
|
private int width = 1;
|
||||||
|
private int height = 1;
|
||||||
|
private int frametime = 1;
|
||||||
|
|
||||||
|
@Nullable private List<FrameMeta> frames = null;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class FrameMeta {
|
||||||
|
private int index;
|
||||||
|
private int time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Adapter extends AbstractTypeAdapterFactory<AnimationMeta> {
|
||||||
|
|
||||||
|
public Adapter() {
|
||||||
|
super(AnimationMeta.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimationMeta read(JsonReader in, Gson gson) throws IOException {
|
||||||
|
AnimationMeta animationMeta = new AnimationMeta();
|
||||||
|
|
||||||
|
in.beginObject();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
if (!in.nextName().equals("animation")){
|
||||||
|
in.skipValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
in.beginObject();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
switch (in.nextName()) {
|
||||||
|
case "interpolate" : animationMeta.interpolate = in.nextBoolean(); break;
|
||||||
|
case "width" : animationMeta.width = in.nextInt(); break;
|
||||||
|
case "height" : animationMeta.height = in.nextInt(); break;
|
||||||
|
case "frametime" : animationMeta.frametime = in.nextInt(); break;
|
||||||
|
case "frames" : readFramesList(in, animationMeta); break;
|
||||||
|
default: in.skipValue(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.endObject();
|
||||||
|
|
||||||
|
}
|
||||||
|
in.endObject();
|
||||||
|
|
||||||
|
// default frame-time
|
||||||
|
if (animationMeta.frames != null) {
|
||||||
|
for (FrameMeta frameMeta : animationMeta.frames) {
|
||||||
|
if (frameMeta.time == -1) frameMeta.time = animationMeta.frametime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return animationMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFramesList(JsonReader in, AnimationMeta animationMeta) throws IOException {
|
||||||
|
animationMeta.frames = new ArrayList<>();
|
||||||
|
|
||||||
|
in.beginArray();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
int index = 0;
|
||||||
|
int time = -1;
|
||||||
|
|
||||||
|
if (in.peek() == JsonToken.NUMBER) {
|
||||||
|
index = in.nextInt();
|
||||||
|
} else {
|
||||||
|
in.beginObject();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
switch (in.nextName()) {
|
||||||
|
case "index" : index = in.nextInt(); break;
|
||||||
|
case "time" : time = in.nextInt(); break;
|
||||||
|
default: in.skipValue(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.endObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
animationMeta.frames.add(new FrameMeta(index, time));
|
||||||
|
}
|
||||||
|
in.endArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, AnimationMeta value, Gson gson) throws IOException {
|
||||||
|
gson.getDelegateAdapter(this, TypeToken.get(AnimationMeta.class)).write(out, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,7 @@
|
|||||||
import de.bluecolored.bluemap.core.resources.ResourcePath;
|
import de.bluecolored.bluemap.core.resources.ResourcePath;
|
||||||
import de.bluecolored.bluemap.core.util.BufferedImageUtil;
|
import de.bluecolored.bluemap.core.util.BufferedImageUtil;
|
||||||
import de.bluecolored.bluemap.core.util.math.Color;
|
import de.bluecolored.bluemap.core.util.math.Color;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@ -42,24 +43,27 @@ public class Texture {
|
|||||||
new ResourcePath<>("bluemap", "missing"),
|
new ResourcePath<>("bluemap", "missing"),
|
||||||
new Color().set(0.5f, 0f, 0.5f, 1.0f, false),
|
new Color().set(0.5f, 0f, 0.5f, 1.0f, false),
|
||||||
false,
|
false,
|
||||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAPklEQVR4Xu3MsQkAMAwDQe2/tFPnBB4gpLhG8MpkZpNkZ6AKZKAKZKAKZKAKZKAKZKAKZKAKWg0XD/UPnjg4MbX+EDdeTUwAAAAASUVORK5CYII\u003d"
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAPklEQVR4Xu3MsQkAMAwDQe2/tFPnBB4gpLhG8MpkZpNkZ6AKZKAKZKAKZKAKZKAKZKAKZKAKWg0XD/UPnjg4MbX+EDdeTUwAAAAASUVORK5CYII\u003d",
|
||||||
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
private ResourcePath<Texture> resourcePath;
|
private ResourcePath<Texture> resourcePath;
|
||||||
private Color color;
|
private Color color;
|
||||||
private boolean halfTransparent;
|
private boolean halfTransparent;
|
||||||
private String texture;
|
private String texture;
|
||||||
|
@Nullable private AnimationMeta animation;
|
||||||
|
|
||||||
private transient Color colorPremultiplied;
|
private transient Color colorPremultiplied;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private Texture() {}
|
private Texture() {}
|
||||||
|
|
||||||
private Texture(ResourcePath<Texture> resourcePath, Color color, boolean halfTransparent, String texture) {
|
private Texture(ResourcePath<Texture> resourcePath, Color color, boolean halfTransparent, String texture, @Nullable AnimationMeta animation) {
|
||||||
this.resourcePath = resourcePath;
|
this.resourcePath = resourcePath;
|
||||||
this.color = color.straight();
|
this.color = color.straight();
|
||||||
this.halfTransparent = halfTransparent;
|
this.halfTransparent = halfTransparent;
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
|
this.animation = animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Texture(ResourcePath<Texture> resourcePath) {
|
private Texture(ResourcePath<Texture> resourcePath) {
|
||||||
@ -67,6 +71,7 @@ private Texture(ResourcePath<Texture> resourcePath) {
|
|||||||
this.color = MISSING.color;
|
this.color = MISSING.color;
|
||||||
this.halfTransparent = MISSING.halfTransparent;
|
this.halfTransparent = MISSING.halfTransparent;
|
||||||
this.texture = MISSING.texture;
|
this.texture = MISSING.texture;
|
||||||
|
this.animation = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourcePath<Texture> getResourcePath() {
|
public ResourcePath<Texture> getResourcePath() {
|
||||||
@ -95,19 +100,7 @@ public String getTexture() {
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unloadImageData() {
|
public static Texture from(ResourcePath<Texture> resourcePath, BufferedImage image, @Nullable AnimationMeta animation) throws IOException {
|
||||||
texture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Texture from(ResourcePath<Texture> resourcePath, BufferedImage image) throws IOException {
|
|
||||||
return from(resourcePath, image, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Texture from(ResourcePath<Texture> resourcePath, BufferedImage image, boolean animated) throws IOException {
|
|
||||||
//crop off animation frames
|
|
||||||
if (animated && image.getHeight() > image.getWidth()){
|
|
||||||
image = image.getSubimage(0, 0, image.getWidth(), image.getWidth());
|
|
||||||
}
|
|
||||||
|
|
||||||
//check halfTransparency
|
//check halfTransparency
|
||||||
boolean halfTransparent = checkHalfTransparent(image);
|
boolean halfTransparent = checkHalfTransparent(image);
|
||||||
@ -120,7 +113,7 @@ public static Texture from(ResourcePath<Texture> resourcePath, BufferedImage ima
|
|||||||
ImageIO.write(image, "png", os);
|
ImageIO.write(image, "png", os);
|
||||||
String base64 = "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray());
|
String base64 = "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray());
|
||||||
|
|
||||||
return new Texture(resourcePath, color, halfTransparent, base64);
|
return new Texture(resourcePath, color, halfTransparent, base64, animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkHalfTransparent(BufferedImage image){
|
private static boolean checkHalfTransparent(BufferedImage image){
|
||||||
|
Loading…
Reference in New Issue
Block a user