Merge pull request #163 from LAX1DUDE/image-maps

Image maps
This commit is contained in:
LAX1DUDE 2022-05-14 18:37:04 -07:00 committed by GitHub
commit 75cdac50a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 349 additions and 24 deletions

View file

@ -48,7 +48,14 @@ public class VideoMapPacketCodec {
* @param posZ audio playback Z coord * @param posZ audio playback Z coord
*/ */
public VideoMapPacketCodec(int[][] mapIds, double posX, double posY, double posZ) { public VideoMapPacketCodec(int[][] mapIds, double posX, double posY, double posZ) {
this(mapIds, posX, posY, posZ, 1.0f); this(mapIds, posX, posY, posZ, 0.5f);
}
/**
* @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x])
*/
public VideoMapPacketCodec(int[][] mapIds) {
this(mapIds, 0, 100, 0, 0.5f);
} }
/** /**
@ -167,7 +174,7 @@ public class VideoMapPacketCodec {
/** /**
* @param url URL to an MP4 or other HTML5 supported video file * @param url URL to an MP4 or other HTML5 supported video file
* @param loop If the video file should loop * @param loop If the video file should loop
* @param durationSeconds duration of the video in seconds * @param duration duration of the video in seconds
* @return packet to send to players * @return packet to send to players
*/ */
public byte[] beginPlayback(String url, boolean loop, float duration) { public byte[] beginPlayback(String url, boolean loop, float duration) {

View file

@ -28,13 +28,23 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec {
* @param posZ audio playback Z coord * @param posZ audio playback Z coord
*/ */
public VideoMapPacketCodecBukkit(int[][] mapIds, double posX, double posY, double posZ) { public VideoMapPacketCodecBukkit(int[][] mapIds, double posX, double posY, double posZ) {
super(mapIds, posX, posY, posZ, 1.0f); super(mapIds, posX, posY, posZ);
}
/**
* @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x])
*/
public VideoMapPacketCodecBukkit(int[][] mapIds) {
super(mapIds);
} }
public static class VideoMapPacket { public static class VideoMapPacket {
protected final Object packet; protected final Object packet;
protected VideoMapPacket(byte[] packet) { protected VideoMapPacket(byte[] packet) {
this.packet = new Packet131ItemData((short)104, (short)0, packet); this(packet, false);
}
protected VideoMapPacket(byte[] packet, boolean image) {
this.packet = new Packet131ItemData((short)(104 + (image ? 1 : 0)), (short)0, packet);
} }
public Object getNativePacket() { public Object getNativePacket() {
return packet; return packet;
@ -73,6 +83,14 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec {
return new VideoMapPacket(disableVideo()); return new VideoMapPacket(disableVideo());
} }
/**
* unloads image and resets all map object to vanilla renderer
* @return packet to send to players
*/
public VideoMapPacket disableImageBukkit() {
return new VideoMapPacket(disableVideo(), true);
}
/** /**
* syncs the server side video timestamp with players * syncs the server side video timestamp with players
* @return packet to send to players * @return packet to send to players
@ -81,6 +99,14 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec {
return new VideoMapPacket(syncPlaybackWithPlayers()); return new VideoMapPacket(syncPlaybackWithPlayers());
} }
/**
* syncs the server side image with players
* @return packet to send to players
*/
public VideoMapPacket syncPlaybackWithPlayersImageBukkit() {
return new VideoMapPacket(syncPlaybackWithPlayers(), true);
}
/** /**
* @param url URL to an MP4 or other HTML5 supported video file * @param url URL to an MP4 or other HTML5 supported video file
* @param loop If the video file should loop * @param loop If the video file should loop
@ -91,6 +117,14 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec {
return new VideoMapPacket(beginPlayback(url, loop, duration)); return new VideoMapPacket(beginPlayback(url, loop, duration));
} }
/**
* @param url URL to a PNG, JPEG, GIF, or other HTML5 supported image file
* @return packet to send to players
*/
public VideoMapPacket beginPlaybackImageBukkit(String url) {
return new VideoMapPacket(beginPlayback(url));
}
/** /**
* Tells the browser to pre-load a URL to a video to be played in the future * Tells the browser to pre-load a URL to a video to be played in the future
* @param url the URL of the video * @param url the URL of the video
@ -101,6 +135,16 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec {
return new VideoMapPacket(bufferVideo(url, ttl)); return new VideoMapPacket(bufferVideo(url, ttl));
} }
/**
* Tells the browser to pre-load a URL to an image to be played in the future
* @param url the URL of the image
* @param ttl the amount of time the image should stay loaded
* @return packet to send to players
*/
public static VideoMapPacket bufferImageBukkit(String url, int ttl) {
return new VideoMapPacket(bufferVideo(url, ttl), true);
}
/** /**
* @param time time in seconds to seek the video to * @param time time in seconds to seek the video to
*/ */

View file

@ -675,6 +675,43 @@ public class EaglerAdapterImpl2 {
throw new UnsupportedOperationException("Video is not supported in LWJGL runtime"); throw new UnsupportedOperationException("Video is not supported in LWJGL runtime");
} }
public static final boolean isImageSupported() {
return false;
}
public static final void loadImage(String src) {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void loadImage(String src, String setJavascriptPointer) {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void loadImage(String src, String setJavascriptPointer, String javascriptOnloadFunction) {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void bufferImage(String src, int ttl) {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void unloadImage() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final boolean isImageLoaded() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void updateImageTexture() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void bindImageTexture() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final int getImageWidth() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final int getImageHeight() {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
public static final void setImageFrameRate(float seconds) {
throw new UnsupportedOperationException("Image is not supported in LWJGL runtime");
}
// ======================================================================================= // =======================================================================================
// ======================================================================================= // =======================================================================================
// ======================================================================================= // =======================================================================================

View file

@ -360,4 +360,64 @@ public class ItemMap extends ItemMapBase {
e.printStackTrace(); e.printStackTrace();
} }
} }
public static void processImageMap(WorldClient theWorld, byte[] data) {
if(!EaglerAdapter.isImageSupported()) {
return;
}
try {
DataInputStream dat = new DataInputStream(new ByteArrayInputStream(data));
int op = dat.read();
if(op == 0) {
int count = dat.read();
int w = (count >> 4) & 0xF;
int h = count & 0xF;
for(int y = 0; y < h; ++y) {
for(int x = 0; x < w; ++x) {
getMapById(theWorld, dat.readUnsignedShort()).enableVideoPlayback = false;
}
}
EaglerAdapter.unloadImage();
}else if(op == 8) {
int ttl = dat.readInt();
String src = dat.readUTF();
EaglerAdapter.bufferImage(src, ttl);
}else {
boolean fullResetPacket = (op & 2) == 2;
boolean positionPacket = (op & 4) == 4;
int fps = 0;
int len = 0;
String url = null;
if(fullResetPacket) {
int count = dat.read();
int w = (count >> 4) & 0xF;
int h = count & 0xF;
float wf = 1.0f / w;
float hf = 1.0f / h;
for(int y = 0; y < h; ++y) {
for(int x = 0; x < w; ++x) {
MapData mp = getMapById(theWorld, dat.readUnsignedShort());
mp.videoX1 = x * wf;
mp.videoY1 = y * hf;
mp.videoX2 = mp.videoX1 + wf;
mp.videoY2 = mp.videoY1 + hf;
mp.enableVideoPlayback = true;
}
}
fps = dat.read();
len = dat.readInt();
url = dat.readUTF();
}
if(fullResetPacket) {
EaglerAdapter.setImageFrameRate(fps);
EaglerAdapter.loadImage(url);
}
}
}catch(IOException e) {
System.err.println("Failed to read image map packet! " + e.toString());
e.printStackTrace();
}
}
} }

View file

@ -1,6 +1,5 @@
package net.minecraft.src; package net.minecraft.src;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View file

@ -30,7 +30,9 @@ public class MapItemRenderer {
float texX2 = 1.0f; float texX2 = 1.0f;
float texY1 = 0.0f; float texY1 = 0.0f;
float texY2 = 1.0f; float texY2 = 1.0f;
boolean isVideoMode = EaglerAdapter.isVideoSupported() && par3MapData.enableVideoPlayback && EaglerAdapter.isVideoLoaded(); boolean isVideoOrImageMode = EaglerAdapter.isVideoSupported() && par3MapData.enableVideoPlayback;
boolean isVideoMode = isVideoOrImageMode && EaglerAdapter.isVideoLoaded();
boolean isImageMode = isVideoOrImageMode && EaglerAdapter.isImageLoaded();
if(isVideoMode) { if(isVideoMode) {
EaglerAdapter.glEnable(EaglerAdapter.EAG_SWAP_RB); EaglerAdapter.glEnable(EaglerAdapter.EAG_SWAP_RB);
EaglerAdapter.updateVideoTexture(); EaglerAdapter.updateVideoTexture();
@ -39,6 +41,14 @@ public class MapItemRenderer {
texY1 = par3MapData.videoY1; texY1 = par3MapData.videoY1;
texX2 = par3MapData.videoX2; texX2 = par3MapData.videoX2;
texY2 = par3MapData.videoY2; texY2 = par3MapData.videoY2;
}else if(isImageMode) {
EaglerAdapter.glEnable(EaglerAdapter.EAG_SWAP_RB);
EaglerAdapter.updateImageTexture();
EaglerAdapter.bindImageTexture();
texX1 = par3MapData.videoX1;
texY1 = par3MapData.videoY1;
texX2 = par3MapData.videoX2;
texY2 = par3MapData.videoY2;
}else { }else {
if(par3MapData.enableAyunami) { if(par3MapData.enableAyunami) {
System.arraycopy(par3MapData.ayunamiPixels, 0, intArray, 0, intArray.length); System.arraycopy(par3MapData.ayunamiPixels, 0, intArray, 0, intArray.length);
@ -98,11 +108,11 @@ public class MapItemRenderer {
EaglerAdapter.glDisable(EaglerAdapter.GL_BLEND); EaglerAdapter.glDisable(EaglerAdapter.GL_BLEND);
par2RenderEngine.resetBoundTexture(); par2RenderEngine.resetBoundTexture();
if(isVideoMode) { if(isVideoMode || isImageMode) {
EaglerAdapter.glDisable(EaglerAdapter.EAG_SWAP_RB); EaglerAdapter.glDisable(EaglerAdapter.EAG_SWAP_RB);
} }
if(!par3MapData.enableAyunami && !isVideoMode) { if(!par3MapData.enableAyunami && !(isVideoMode || isImageMode)) {
mapicons.bindTexture(); mapicons.bindTexture();
int var19 = 0; int var19 = 0;

View file

@ -1017,6 +1017,8 @@ public class NetClientHandler extends NetHandler {
ItemMap.readAyunamiMapPacket(this.mc.theWorld, par1Packet131MapData.uniqueID, par1Packet131MapData.itemData); ItemMap.readAyunamiMapPacket(this.mc.theWorld, par1Packet131MapData.uniqueID, par1Packet131MapData.itemData);
} else if (par1Packet131MapData.itemID == 104) { } else if (par1Packet131MapData.itemID == 104) {
ItemMap.processVideoMap(this.mc.theWorld, par1Packet131MapData.itemData); ItemMap.processVideoMap(this.mc.theWorld, par1Packet131MapData.itemData);
} else if (par1Packet131MapData.itemID == 105) {
ItemMap.processImageMap(this.mc.theWorld, par1Packet131MapData.itemData);
} else { } else {
System.err.println("Unknown itemid: " + par1Packet131MapData.itemID); System.err.println("Unknown itemid: " + par1Packet131MapData.itemID);
} }

View file

@ -35,6 +35,7 @@ import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.HTMLElement;
import org.teavm.jso.dom.html.HTMLVideoElement; import org.teavm.jso.dom.html.HTMLVideoElement;
import org.teavm.jso.dom.html.HTMLImageElement;
import org.teavm.jso.media.MediaError; import org.teavm.jso.media.MediaError;
import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Float32Array; import org.teavm.jso.typedarrays.Float32Array;
@ -1189,6 +1190,171 @@ public class EaglerAdapterImpl2 {
} }
} }
private static HTMLImageElement currentImage = null;
private static TextureGL imageTexture = null;
private static boolean imageIsLoaded = false;
private static boolean imageTexIsInitialized = false;
private static int imageFrameRate = 33;
private static long imageFrameTimer = 0l;
public static final boolean isImageSupported() {
return true;
}
public static final void loadImage(String src) {
loadImage(src, null);
}
public static final void loadImage(String src, String setJavascriptPointer) {
loadImage(src, setJavascriptPointer, null);
}
@JSBody(params = { "ptr", "el" }, script = "window[ptr] = el;")
private static native void setImagePointer(String ptr, HTMLImageElement el);
@JSBody(params = { "ptr", "el" }, script = "window[ptr](el);")
private static native void callImageLoadEvent(String ptr, HTMLImageElement el);
public static final void loadImage(String src, String setJavascriptPointer, final String javascriptOnloadFunction) {
imageIsLoaded = false;
imageTexIsInitialized = false;
if(imageTexture == null) {
imageTexture = _wglGenTextures();
}
if(currentImage != null) {
currentImage.setSrc("");
}
BufferedImageElem img = imagesBuffer.get(src);
if(img != null) {
currentImage = img.imageElement;
imagesBuffer.remove(src);
}else {
currentImage = (HTMLImageElement) win.getDocument().createElement("img");
currentImage.setAttribute("crossorigin", "anonymous");
}
if(setJavascriptPointer != null) {
setImagePointer(setJavascriptPointer, currentImage);
}
currentImage.addEventListener("load", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
imageIsLoaded = true;
if(javascriptOnloadFunction != null) {
callImageLoadEvent(javascriptOnloadFunction, currentImage);
}
}
});
if(img == null) {
currentImage.setSrc(src);
}
}
private static class BufferedImageElem {
protected final HTMLImageElement imageElement;
protected final String url;
protected final long requestedTime;
protected final int ttl;
public BufferedImageElem(HTMLImageElement imageElement, String url, int ttl) {
this.imageElement = imageElement;
this.url = url;
this.requestedTime = System.currentTimeMillis();
this.ttl = ttl;
}
}
private static final HashMap<String, BufferedImageElem> imagesBuffer = new HashMap();
public static final void bufferImage(String src, int ttl) {
if(!imagesBuffer.containsKey(src)) {
HTMLImageElement image = (HTMLImageElement) win.getDocument().createElement("img");
image.setAttribute("crossorigin", "anonymous");
image.setSrc(src);
imagesBuffer.put(src, new BufferedImageElem(image, src, ttl));
}
}
public static final void unloadImage() {
if(imageTexture != null) {
_wglDeleteTextures(imageTexture);
imageTexture = null;
}
if(currentImage != null) {
currentImage.setSrc("");
currentImage = null;
}
}
public static final boolean isImageLoaded() {
return imageTexture != null && currentImage != null && imageIsLoaded;
}
@JSBody(
params = {"ctx", "target", "internalformat", "format", "type", "image"},
script = "ctx.texImage2D(target, 0, internalformat, format, type, image);"
)
private static native void html5ImageTexImage2D(WebGL2RenderingContext ctx, int target, int internalformat, int format, int type, HTMLImageElement image);
@JSBody(
params = {"ctx", "target", "format", "type", "image"},
script = "ctx.texSubImage2D(target, 0, 0, 0, format, type, image);"
)
private static native void html5ImageTexSubImage2D(WebGL2RenderingContext ctx, int target, int format, int type, HTMLImageElement image);
public static final void updateImageTexture() {
long ms = System.currentTimeMillis();
if(ms - imageFrameTimer < imageFrameRate && imageTexIsInitialized) {
return;
}
imageFrameTimer = ms;
if(currentImage != null && imageTexture != null && imageIsLoaded) {
try {
_wglBindTexture(_wGL_TEXTURE_2D, imageTexture);
if(imageTexIsInitialized) {
html5ImageTexSubImage2D(webgl, _wGL_TEXTURE_2D, _wGL_RGBA, _wGL_UNSIGNED_BYTE, currentImage);
}else {
html5ImageTexImage2D(webgl, _wGL_TEXTURE_2D, _wGL_RGBA, _wGL_RGBA, _wGL_UNSIGNED_BYTE, currentImage);
_wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_WRAP_S, _wGL_CLAMP);
_wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_WRAP_T, _wGL_CLAMP);
_wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_MIN_FILTER, _wGL_LINEAR);
_wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_MAG_FILTER, _wGL_LINEAR);
imageTexIsInitialized = true;
}
}catch(Throwable t) {
// rip
}
}
}
public static final void bindImageTexture() {
if(imageTexture != null) {
_wglBindTexture(_wGL_TEXTURE_2D, imageTexture);
}
}
public static final int getImageWidth() {
if(currentImage != null && imageIsLoaded) {
return currentImage.getWidth();
}else {
return -1;
}
}
public static final int getImageHeight() {
if(currentImage != null && imageIsLoaded) {
return currentImage.getHeight();
}else {
return -1;
}
}
public static final void setImageFrameRate(float fps) {
frameRate = (int)(1000.0f / fps);
if(frameRate < 1) {
frameRate = 1;
}
}
private static MouseEvent currentEvent = null; private static MouseEvent currentEvent = null;
private static KeyboardEvent currentEventK = null; private static KeyboardEvent currentEventK = null;
private static boolean[] buttonStates = new boolean[8]; private static boolean[] buttonStates = new boolean[8];