// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.android.stardroid.renderer;
import android.graphics.Bitmap;
import android.opengl.GLUtils;
import com.google.android.stardroid.renderer.util.TexCoordBuffer;
import com.google.android.stardroid.renderer.util.TextureManager;
import com.google.android.stardroid.renderer.util.TextureReference;
import com.google.android.stardroid.renderer.util.VertexBuffer;
import com.google.android.stardroid.source.ImageSource;
import com.google.android.stardroid.units.GeocentricCoordinates;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.EnumSet;
import java.util.List;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
/**
* Manages the rendering of image objects.
*
* @author James Powell
*
*/
public class ImageObjectManager extends RendererObjectManager {
private VertexBuffer mVertexBuffer = new VertexBuffer(false);
private TexCoordBuffer mTexCoordBuffer = new TexCoordBuffer(false);
private Image[] mImages = new Image[0];
private TextureReference[] mTextures = new TextureReference[0];
private TextureReference[] mRedTextures = new TextureReference[0];
EnumSet<UpdateType> mUpdates = EnumSet.noneOf(UpdateType.class);
public ImageObjectManager(int layer, TextureManager manager) {
super(layer, manager);
}
public void updateObjects(List<ImageSource> imageSources, EnumSet<UpdateType> type) {
if (!type.contains(UpdateType.Reset) && imageSources.size() != mImages.length) {
logUpdateMismatch("ImageObjectManager", imageSources.size(), mImages.length, type);
return;
}
mUpdates.addAll(type);
int numVertices = imageSources.size() * 4;
VertexBuffer vertexBuffer = mVertexBuffer;
vertexBuffer.reset(numVertices);
TexCoordBuffer texCoordBuffer = mTexCoordBuffer;
texCoordBuffer.reset(numVertices);
Image[] images;
boolean reset = type.contains(UpdateType.Reset) || type.contains(UpdateType.UpdateImages);
if (reset) {
images = new Image[imageSources.size()];
} else {
images = mImages;
}
if (reset) {
for (int i = 0; i < imageSources.size(); i++) {
ImageSource is = imageSources.get(i);
images[i] = new Image();
//TODO(brent): Fix this method.
images[i].name = "no url";
images[i].useBlending = false;
images[i].bitmap = is.getImage();
}
}
// Update the positions in the position and tex coord buffers.
if (reset || type.contains(UpdateType.UpdatePositions)) {
for (int i = 0; i < imageSources.size(); i++) {
ImageSource is = imageSources.get(i);
GeocentricCoordinates xyz = is.getLocation();
float px = xyz.x;
float py = xyz.y;
float pz = xyz.z;
float[] u = is.getHorizontalCorner();
float ux = u[0];
float uy = u[1];
float uz = u[2];
float[] v = is.getVerticalCorner();
float vx = v[0];
float vy = v[1];
float vz = v[2];
// lower left
vertexBuffer.addPoint(px - ux - vx, py - uy - vy, pz - uz - vz);
texCoordBuffer.addTexCoords(0, 1);
// upper left
vertexBuffer.addPoint(px - ux + vx, py - uy + vy, pz - uz + vz);
texCoordBuffer.addTexCoords(0, 0);
// lower right
vertexBuffer.addPoint(px + ux - vx, py + uy - vy, pz + uz - vz);
texCoordBuffer.addTexCoords(1, 1);
// upper right
vertexBuffer.addPoint(px + ux + vx, py + uy + vy, pz + uz + vz);
texCoordBuffer.addTexCoords(1, 0);
}
}
// We already set the image in reset, so only set them here if we're
// not doing a reset.
if (type.contains(UpdateType.UpdateImages)) {
for (int i = 0; i < imageSources.size(); i++) {
ImageSource is = imageSources.get(i);
images[i].bitmap = is.getImage();
}
}
mImages = images;
queueForReload(false);
}
@Override
public void reload(GL10 gl, boolean fullReload) {
Image[] images = mImages;
boolean reloadBuffers = false;
boolean reloadImages = false;
if (fullReload) {
reloadBuffers = true;
reloadImages = true;
// If this is a full reload, all the textures were automatically deleted,
// so just create new arrays so we won't try to delete the old ones again.
mTextures = new TextureReference[images.length];
mRedTextures = new TextureReference[images.length];
} else {
// Process any queued updates.
boolean reset = mUpdates.contains(UpdateType.Reset);
reloadBuffers |= reset || mUpdates.contains(UpdateType.UpdatePositions);
reloadImages |= reset || mUpdates.contains(UpdateType.UpdateImages);
mUpdates.clear();
}
if (reloadBuffers) {
mVertexBuffer.reload();
mTexCoordBuffer.reload();
}
if (reloadImages) {
for (int i = 0; i < mTextures.length; i++) {
// If the image is already allocated, delete it.
if (mTextures[i] != null) {
mTextures[i].delete(gl);
mRedTextures[i].delete(gl);
}
Bitmap bmp = mImages[i].bitmap;
mTextures[i] = textureManager().createTexture(gl);
mTextures[i].bind(gl);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
IntBuffer redPixels = createRedImage(bmp);
mRedTextures[i] = textureManager().createTexture(gl);
mRedTextures[i].bind(gl);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, bmp.getWidth(), bmp.getHeight(),
0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, redPixels);
}
}
}
@Override
protected void drawInternal(GL10 gl) {
if (mVertexBuffer.size() == 0) {
return;
}
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
mVertexBuffer.set(gl);
mTexCoordBuffer.set(gl);
TextureReference[] textures = mTextures;
TextureReference[] redTextures = mRedTextures;
for (int i = 0; i < textures.length; i++) {
if (mImages[i].useBlending) {
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
} else {
gl.glEnable(GL10.GL_ALPHA_TEST);
gl.glAlphaFunc(GL10.GL_GREATER, 0.5f);
}
if (getRenderState().getNightVisionMode()) {
redTextures[i].bind(gl);
} else {
textures[i].bind(gl);
}
((GL11) gl).glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4 * i, 4);
if (mImages[i].useBlending) {
gl.glDisable(GL10.GL_BLEND);
} else {
gl.glDisable(GL10.GL_ALPHA_TEST);
}
}
gl.glDisable(GL10.GL_TEXTURE_2D);
}
private IntBuffer createRedImage(Bitmap bmp) {
int width = bmp.getWidth();
int height = bmp.getHeight();
int numPixels = width * height;
int[] pixels = new int[numPixels];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
ByteBuffer redPixelsBB = ByteBuffer.allocateDirect(4 * numPixels);
IntBuffer redPixels = redPixelsBB.order(ByteOrder.nativeOrder()).asIntBuffer();
for (int j = 0; j < numPixels; j++) {
int r = pixels[j] & 0xff;
int g = (pixels[j] >> 8) & 0xff;
int b = (pixels[j] >> 16) & 0xff;
int alphaMask = pixels[j] & 0xff000000;
redPixels.put(alphaMask | ((r + g + b) / 3));
}
redPixels.position(0);
return redPixels;
}
private static class Image {
String name;
Bitmap bitmap;
int textureID;
boolean useBlending;
}
}