Java tutorial
/* Copyright (C) 2010 Copyright 2010 Google Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.googlecode.gwtquake.client; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.ArrayList; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayInteger; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.event.dom.client.ErrorEvent; import com.google.gwt.event.dom.client.ErrorHandler; import com.google.gwt.event.dom.client.LoadEvent; import com.google.gwt.event.dom.client.LoadHandler; import com.google.gwt.html5.client.CanvasElement; import com.google.gwt.html5.client.CanvasPixelArray; import com.google.gwt.html5.client.CanvasRenderingContext2D; import com.google.gwt.html5.client.ImageData; import com.google.gwt.user.client.Timer; import com.googlecode.gwtquake.shared.client.Renderer; import com.googlecode.gwtquake.shared.common.Com; import com.googlecode.gwtquake.shared.common.ResourceLoader; import com.googlecode.gwtquake.shared.render.GlRenderer; import com.googlecode.gwtquake.shared.render.GlState; import com.googlecode.gwtquake.shared.render.Images; import com.googlecode.gwtquake.shared.render.Image; import com.googlecode.gwtquake.shared.sys.KBD; import static com.google.gwt.webgl.client.WebGLRenderingContext.*; public class GwtWebGLRenderer extends GlRenderer implements Renderer { KBD kbd = new GwtKBD(); public KBD getKeyboardHandler() { return kbd; } static class VideoElement extends Element { protected VideoElement() { } native final JavaScriptObject getError() /*-{ return this.error; }-*/; public final native void pause() /*-{ this.pause(); }-*/; public final native void play() /*-{ this.play(); }-*/; public final native double getDuration() /*-{ return this.duration; }-*/; public final native double getCurrentTime() /*-{ return this.currentTime; }-*/; public final native void setCurrentTime(double s) /*-{ return this.currentTime = s; }-*/; public final boolean ended() { // System.out.println("video.error: " + getError() + // " duration: " + getDuration() + // " currentTime: " + getCurrentTime()); // return getError() != null || !(Double.isNaN(getDuration()) || getCurrentTime() < getDuration()); } } static final int IMAGE_CHECK_TIME = 250; static final int MAX_IMAGE_REQUEST_COUNT = 12; static int HOLODECK_TEXTURE_SIZE = 128; static int MASK = 15; static int HIT = MASK / 2; ByteBuffer holoDeckTexture = ByteBuffer.allocateDirect(HOLODECK_TEXTURE_SIZE * HOLODECK_TEXTURE_SIZE * 4); WebGLGl1Contect webGL; CanvasElement canvas1; CanvasElement canvas2; ArrayList<Image> imageQueue = new ArrayList<Image>(); int waitingForImages; VideoElement video; CanvasElement canvas; public GwtWebGLRenderer(CanvasElement canvas, Element video) { super(canvas.getWidth(), canvas.getHeight()); GlState.gl = this.webGL = new WebGLGl1Contect(canvas); this.canvas = canvas; this.video = (VideoElement) video; for (int y = 0; y < HOLODECK_TEXTURE_SIZE; y++) { for (int x = 0; x < HOLODECK_TEXTURE_SIZE; x++) { holoDeckTexture.put((byte) 0); holoDeckTexture.put((byte) (((x & MASK) == HIT) || ((y & MASK) == HIT) ? 255 : 0)); holoDeckTexture.put((byte) 0); holoDeckTexture.put((byte) 0xff); } } holoDeckTexture.rewind(); canvas1 = (CanvasElement) Document.get().createElement("canvas"); canvas1.getStyle().setDisplay(Display.NONE); canvas1.setWidth(128); canvas1.setHeight(128); Document.get().getBody().appendChild(canvas1); canvas2 = (CanvasElement) Document.get().createElement("canvas"); canvas2.setWidth(128); canvas2.setHeight(128); canvas2.getStyle().setDisplay(Display.NONE); Document.get().getBody().appendChild(canvas2); init(); } @Override public void GL_ResampleTexture(int[] in, int inwidth, int inheight, int[] out, int outwidth, int outheight) { if (canvas1.getWidth() < inwidth) { canvas1.setWidth(inwidth); } if (canvas1.getHeight() < inheight) { canvas1.setHeight(inheight); } CanvasRenderingContext2D inCtx = canvas1.getContext2D(); ImageData data = inCtx.createImageData(inwidth, inheight); CanvasPixelArray pixels = data.getData(); int len = inwidth * inheight; int p = 0; for (int i = 0; i < len; i++) { int abgr = in[i]; pixels.set(p, (abgr & 255)); pixels.set(p + 1, (abgr >> 8) & 255); pixels.set(p + 2, (abgr >> 16) & 255); pixels.set(p + 3, (abgr >> 24) & 255); p += 4; } inCtx.putImageData(data, 0, 0); if (canvas2.getWidth() < outwidth) { canvas2.setWidth(outwidth); } if (canvas2.getHeight() < outheight) { canvas2.setHeight(outheight); } CanvasRenderingContext2D outCtx = canvas2.getContext2D(); outCtx.drawImage(canvas1, 0, 0, inwidth, inheight, 0, 0, outwidth, outheight); data = outCtx.getImageData(0, 0, outwidth, outheight); pixels = data.getData(); len = outwidth * outheight; p = 0; for (int i = 0; i < len; i++) { int r = pixels.get(p) & 255; int g = pixels.get(p + 1) & 255; int b = pixels.get(p + 2) & 255; int a = pixels.get(p + 3) & 255; p += 4; out[i] = (a << 24) | (b << 16) | (g << 8) | r; } } static native JsArrayInteger getImageSize(String name) /*-{ return $wnd.__imageSizes[name]; }-*/; public Image GL_LoadNewImage(final String name, int type) { final Image image = Images.GL_Find_free_image_t(name, type); int cut = name.lastIndexOf('.'); String normalizedName = cut == -1 ? name : name.substring(0, cut); JsArrayInteger d = getImageSize(normalizedName); if (d == null) { GlState.gl.log("Size not found for " + name); image.width = 128; image.height = 128; } else { image.width = d.get(0); image.height = d.get(1); } if (type != com.googlecode.gwtquake.shared.common.QuakeImage.it_pic) { GlState.gl.glTexImage2D(TEXTURE_2D, 0, RGBA, HOLODECK_TEXTURE_SIZE, HOLODECK_TEXTURE_SIZE, 0, RGBA, UNSIGNED_BYTE, holoDeckTexture); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR); } imageQueue.add(image); if (imageQueue.size() == 1) { new ImageLoader().schedule(); } return image; } class ImageLoader extends Timer { @Override public void run() { Document doc = Document.get(); while (!ResourceLoader.Pump() && waitingForImages < MAX_IMAGE_REQUEST_COUNT && imageQueue.size() > 0) { final Image image = imageQueue.remove(0); final ImageElement img = doc.createImageElement(); String picUrl = convertPicName(image.name, image.type); /* if (picUrl.endsWith("ggrat6_2.png")) { picUrl = convertPicName("textures/tron_poster.jpg", 0); }*/ img.setSrc(picUrl); img.getStyle().setDisplay(Display.NONE); doc.getBody().appendChild(img); if (img.getPropertyBoolean("complete")) { loaded(image, img); } else { waitingForImages(+1); com.google.gwt.user.client.ui.Image imgWidget = com.google.gwt.user.client.ui.Image.wrap(img); final ImageElement finalImg = img; imgWidget.addLoadHandler(new LoadHandler() { public void onLoad(LoadEvent event) { waitingForImages(-1); loaded(image, finalImg); } }); imgWidget.addErrorHandler(new ErrorHandler() { public void onError(ErrorEvent event) { // String src = finalImg.getSrc(); // if (src.endsWith("&rt&rt&rt&rt")) { GlState.gl.log("load errors for " + finalImg.getSrc()); waitingForImages(-1); image.complete = true; // } else { // finalImg.setSrc(finalImg.getSrc() + "&rt"); // } } }); } // else } // while if (imageQueue.size() > 0) { schedule(); } } protected void waitingForImages(int i) { waitingForImages += i; if (waitingForImages > 0) { Com.Printf("Waiting for " + waitingForImages + " images\n"); } } public void schedule() { schedule(IMAGE_CHECK_TIME); } } public void loaded(Image image, ImageElement img) { setPicDataHighLevel(image, img); // setPicDataLowLevel(image, img); } ByteBuffer bb = ByteBuffer.allocateDirect(128 * 128 * 4); public void setPicDataHighLevel(Image image, ImageElement img) { image.has_alpha = true; image.complete = true; image.height = img.getHeight(); image.width = img.getWidth(); boolean mipMap = image.type != com.googlecode.gwtquake.shared.common.QuakeImage.it_pic && image.type != com.googlecode.gwtquake.shared.common.QuakeImage.it_sky; Images.GL_Bind(image.texnum); int p2w = 1 << ((int) Math.ceil(Math.log(image.width) / Math.log(2))); int p2h = 1 << ((int) Math.ceil(Math.log(image.height) / Math.log(2))); if (mipMap) { p2w = p2h = Math.max(p2w, p2h); } image.upload_width = p2w; image.upload_height = p2h; int level = 0; do { canvas1.setWidth(p2w); canvas1.setHeight(p2h); canvas1.getContext2D().clearRect(0, 0, p2w, p2h); canvas1.getContext2D().drawImage(img, 0, 0, p2w, p2h); webGL.glTexImage2d(TEXTURE_2D, level++, RGBA, RGBA, UNSIGNED_BYTE, canvas1); p2w = p2w / 2; p2h = p2h / 2; } while (mipMap && p2w > 0); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MIN_FILTER, mipMap ? LINEAR_MIPMAP_NEAREST : LINEAR); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR); } public void __setPicDataHighLevel(Image image, ImageElement img) { image.has_alpha = true; image.complete = true; image.height = img.getHeight(); image.width = img.getWidth(); image.upload_height = image.height; image.upload_width = image.width; Images.GL_Bind(image.texnum); webGL.glTexImage2d(TEXTURE_2D, 0, RGBA, RGBA, UNSIGNED_BYTE, img); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR); GlState.gl.glTexParameterf(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR); } public void setPicDataLowLevel(Image image, ImageElement img) { CanvasElement canvas = (CanvasElement) Document.get().createElement("canvas"); int w = img.getWidth(); int h = img.getHeight(); canvas.setWidth(w); canvas.setHeight(h); // canvas.getStyle().setProperty("border", "solid 1px green"); canvas.getStyle().setDisplay(Display.NONE); Document.get().getBody().appendChild(canvas); CanvasRenderingContext2D ctx = canvas.getContext2D(); ctx.drawImage(img, 0, 0); ImageData data = ctx.getImageData(0, 0, w, h); CanvasPixelArray pixels = data.getData(); int count = w * h * 4; byte[] pic = new byte[count]; for (int i = 0; i < count; i += 4) { pic[i + 3] = (byte) pixels.get(i + 3); // alpha, then bgr pic[i + 2] = (byte) pixels.get(i + 2); pic[i + 1] = (byte) pixels.get(i + 1); pic[i] = (byte) pixels.get(i); } image.setData(pic, w, h, 32); } protected void debugLightmap(IntBuffer lightmapBuffer, int w, int h, float scale) { CanvasElement canvas = (CanvasElement) Document.get().createElement("canvas"); canvas.setWidth(w); canvas.setHeight(h); Document.get().getBody().appendChild(canvas); ImageData id = canvas.getContext2D().createImageData(w, h); CanvasPixelArray pd = id.getData(); for (int i = 0; i < w * h; i++) { int abgr = lightmapBuffer.get(i); pd.set(i * 4, abgr & 255); pd.set(i * 4 + 1, abgr & 255); pd.set(i * 4 + 2, abgr & 255); pd.set(i * 4 + 3, abgr & 255); } canvas.getContext2D().putImageData(id, 0, 0); } private static String convertPicName(String name, int type) { int dotIdx = name.indexOf('.'); assert dotIdx != -1; return "baseq2/" + name.substring(0, dotIdx) + ".png"; } public boolean updateVideo() { return !video.ended(); } public void CinematicSetPalette(byte[] palette) { setVideoVisible(palette != null); } boolean videoVisible = false; private void setVideoVisible(boolean show) { if (videoVisible == show) { return; } System.out.println("setVideoVisible(" + show + ")"); videoVisible = show; if (show) { canvas.getStyle().setProperty("display", "none"); video.getStyle().setProperty("display", ""); if (video.getAttribute("src") != null && !video.ended()) { video.play(); } } else { canvas.getStyle().setProperty("display", ""); video.getStyle().setProperty("display", "none"); if (video.getAttribute("src") != null && !video.ended()) { video.pause(); } } } public boolean showVideo(String name) { if (name == null) { setVideoVisible(false); return true; } // String src = GWT.getModuleBaseURL(); // int cut = src.indexOf("/", 8); // if (cut == -1) { // cut = src.length(); // } String src = "baseq2/video/" + name + ".mp4"; System.out.println("trying to play video: " + src); video.setAttribute("class", "video-stream"); video.setAttribute("src", src); if (!Double.isNaN(video.getDuration())) { video.setCurrentTime(0); } setVideoVisible(true); return true; } }