/*******************************************************************************
* Copyright (c) 2015
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.graphics.map.minimap;
import go.graphics.GLDrawContext;
import go.graphics.IllegalBufferException;
import go.graphics.TextureHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.LinkedList;
import jsettlers.common.map.IGraphicsGrid;
import jsettlers.common.map.shapes.MapRectangle;
import jsettlers.common.position.ShortPoint2D;
import jsettlers.graphics.map.MapDrawContext;
import jsettlers.graphics.map.controls.original.MiniMapLayoutProperties;
import jsettlers.graphics.map.geometry.MapCoordinateConverter;
/**
* This is the minimap. It is drawn on on the rectangle:
*
* <pre>
* (width * stride | height) (width * (stride + 1) | height)
* (0 | 0) (width * 1 | 0)
* </pre>
*
* currently stride is fixed to (mapwidth / 2) / mapwidth.
*
* @author michael
*/
public final class Minimap implements IMinimapData {
private final MapCoordinateConverter converter;
private int width;
private int height;
private TextureHandle texture = null;
private final float stride;
private boolean imageIsValid = false;
private final Object updateMutex = new Object();
private final MapDrawContext context;
private MapRectangle mapViewport;
private short[][] buffer;
private final LinkedList<Integer> updatedLines = new LinkedList<Integer>();
private final LineLoader lineLoader;
private boolean stopped = false;
public Minimap(MapDrawContext context, MinimapMode modeSettings) {
this.context = context;
IGraphicsGrid map = context.getMap();
stride = MiniMapLayoutProperties.getStride(map.getWidth()) / map.getWidth();
converter = new MapCoordinateConverter(map.getWidth(), map.getHeight(), 1, 1);
lineLoader = new LineLoader(this, modeSettings);
Thread minimapThread = new Thread(lineLoader, "minimap loader");
minimapThread.setDaemon(true);
minimapThread.start();
}
public void setSize(int width, int height) {
synchronized (updateMutex) {
this.width = width;
this.height = height;
imageIsValid = false;
updateMutex.notifyAll();
}
}
public void draw(GLDrawContext context) {
boolean imageWasCreatedJustNow = false;
try {
synchronized (updateMutex) {
if (!imageIsValid) {
imageWasCreatedJustNow = true;
if (texture != null) {
texture.delete();
texture = null;
}
ShortBuffer data = ByteBuffer.allocateDirect(width * height * 2)
.order(ByteOrder.nativeOrder()).asShortBuffer();
for (int i = 0; i < width * height; i++) {
data.put(LineLoader.BLACK);
}
data.position(0);
texture = context.generateTexture(width, height, data);
updatedLines.clear();
imageIsValid = true;
}
if (!updatedLines.isEmpty()) {
ShortBuffer currData = ByteBuffer.allocateDirect(width * 2)
.order(ByteOrder.nativeOrder()).asShortBuffer();
for (Integer currLine : updatedLines) {
currData.position(0);
currData.put(buffer[currLine]);
currData.position(0);
context.updateTexture(texture, 0, currLine, width, 1,
currData);
}
updatedLines.clear();
}
updateMutex.notifyAll();
}
context.color(1, 1, 1, 1);
context.drawQuadWithTexture(texture, new float[] {
0,
0,
0,
0,
0,
width,
0,
0,
1,
0,
(stride + 1) * width,
height,
0,
1,
1,
stride * width,
height,
0,
0,
1,
});
drawViewmark(context);
} catch (IllegalBufferException e) {
if (imageWasCreatedJustNow) {
// TODO: Error reporting
e.printStackTrace();
} else {
// Retry with a new image.
synchronized (updateMutex) {
imageIsValid = false;
}
draw(context);
}
}
}
private void drawViewmark(GLDrawContext context) {
if (mapViewport == null) {
return;
}
int lineStartX = mapViewport.getLineStartX(0);
int firstY = mapViewport.getLineY(0);
float minviewx = converter.getViewX(lineStartX, firstY, 0) * width;
float maxviewy = Math.min(converter.getViewY(lineStartX, firstY, 0), 1) * height;
float maxviewx = converter.getViewX(mapViewport.getLineEndX(0), firstY, 0)
* width;
int lastY = mapViewport.getLineY(mapViewport.getLines());
float minviewy = Math.max(converter.getViewY(lineStartX, lastY, 0), 0) * height;
context.drawLine(
new float[] {
// bottom left
Math.max(minviewx, minviewy / height * stride * width),
minviewy,
0,
// bottom right
Math.min(maxviewx, (minviewy / height * stride + 1)
* width),
minviewy,
0,
Math.min(maxviewx, (maxviewy / height * stride + 1)
* width),
Math.max(
(Math.min(maxviewx,
(maxviewy / height * stride + 1)
* width)
- width)
/ width / stride * height,
minviewy),
0,
// top right
Math.min(maxviewx, (maxviewy / height * stride + 1)
* width),
maxviewy,
0,
// top left
Math.max(minviewx, maxviewy / height * stride * width),
maxviewy,
0,
Math.max(minviewx, minviewy / height * stride * width),
Math.min(
Math.max(minviewx, minviewy / height * stride
* width)
/ width / stride * height,
maxviewy),
0,
}, true);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
/**
* Sets the data
*
* @param line
* @param data
*/
public void setUpdatedLine(int line) {
synchronized (updateMutex) {
updatedLines.add(line);
}
}
public MapDrawContext getContext() {
return context;
}
public ShortPoint2D getClickPositionIfOnMap(float relativex, float relativey) {
int x = converter.getMapX(relativex, relativey);
int y = converter.getMapY(relativex, relativey);
if (context.checkMapCoordinates(x, y)) {
return new ShortPoint2D(x, y);
} else {
return null;
}
}
public void setMapViewport(MapRectangle rect) {
mapViewport = rect;
}
/**
* a call to this method blocks until it's ok to update a line.
*/
public void blockUntilUpdateAllowedOrStopped() {
synchronized (updateMutex) {
while (!stopped
&& (!updatedLines.isEmpty() || width < 1 || height < 1)) {
try {
updateMutex.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void setBufferArray(short[][] buffer) {
this.buffer = buffer;
}
public void stop() {
lineLoader.stop();
stopped = true;
synchronized (updateMutex) {
}
}
}