package org.codecranachan.asteroidpush.base.visuals;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.codecranachan.asteroidpush.utils.FieldOfView;
import org.codecranachan.asteroidpush.utils.Trigonometry;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Vec2;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Transform;
import org.newdawn.slick.geom.Vector2f;
public class RepresentationRenderer {
private FieldOfView fov;
private Rectangle frame;
private List<Representation> reps;
public RepresentationRenderer() {
reps = new LinkedList<Representation>();
}
public void setRepresentations(Collection<Representation> representations) {
reps.clear();
reps.addAll(representations);
RepresentationComparator comparator = new RepresentationComparator();
Collections.sort(reps, comparator);
}
public void setFieldOfView(FieldOfView fov) {
this.fov = fov;
}
public void setFrame(Rectangle frame) {
this.frame = frame;
}
public void render(Graphics g) {
assert frame != null;
assert fov != null;
g.pushTransform();
try {
applyViewportTransform(g);
for (Representation r : reps) {
r.render(g);
}
} finally {
g.popTransform();
}
}
public Vec2 mapToWorldCoordinates(Vector2f screenCoordinate) {
Transform transform = getInverseViewportTransform();
Vector2f worldCoordinate = transform.transform(screenCoordinate);
return new Vec2(worldCoordinate.getX(), worldCoordinate.getY());
}
public Vector2f mapToScreenCoordinates(Vec2 worldCoordinate) {
Transform transform = getViewportTransform();
return transform.transform(new Vector2f(worldCoordinate.x,
worldCoordinate.y));
}
private void applyViewportTransform(Graphics g) {
// First part: Map to screen coordinates
// - translate to center in view port
// - map to screen coordinates and scale
Vector2f viewOffset = getViewOffset();
g.translate(viewOffset.getX(), viewOffset.getY());
float scale = getViewportScale();
g.scale(scale, -scale);
// Second part: Transform to camera position
// - rotate the world so the focus is pointing upwards
// - translate the world so the focus is at the center
g.rotate(0, 0, getFocusRotationAsAngle());
Vector2f focusOffset = getFocusOffset();
g.translate(-focusOffset.getX(), -focusOffset.getY());
}
private Vector2f getFocusOffset() {
return new Vector2f(fov.getCenter().x, fov.getCenter().y);
}
private float getFocusRotationAsAngle() {
return Trigonometry.radToDeg(getFocusRotationAsRadian());
}
private float getFocusRotationAsRadian() {
return MathUtils.HALF_PI - fov.getUp().rad();
}
private Vector2f getViewOffset() {
return new Vector2f(frame.getWidth() / 2.0f + frame.getX(),
frame.getMaxY() - frame.getHeight() / 2.0f);
}
private float getViewportScale() {
float frameSize = Math.min(frame.getWidth(), frame.getHeight());
float scale = frameSize / (fov.getRadius() * 2.0f);
return scale;
}
private Transform getViewportTransform() {
Vector2f viewOffset = getViewOffset();
Transform viewOffsetTransform = Transform
.createTranslateTransform(viewOffset.getX(), viewOffset.getY());
float scale = getViewportScale();
Transform viewScaleTransform = Transform.createScaleTransform(scale,
-scale);
Transform focusRotateTransform = Transform
.createRotateTransform(getFocusRotationAsRadian());
Vector2f focusOffset = getFocusOffset();
Transform focusOffsetTransform = Transform
.createTranslateTransform(-focusOffset.getX(), -focusOffset.getY());
Transform transform = new Transform();
transform.concatenate(viewOffsetTransform);
transform.concatenate(viewScaleTransform);
transform.concatenate(focusRotateTransform);
transform.concatenate(focusOffsetTransform);
return transform;
}
private Transform getInverseViewportTransform() {
// slick2d does not seem to have a getInverse() method on the Transform
// class,
// so we just engineer the inverse transform by doing the viewport
// transform
// backwards.
Vector2f viewOffset = getViewOffset();
Transform viewOffsetTransform = Transform
.createTranslateTransform(-viewOffset.getX(), -viewOffset.getY());
float scale = getViewportScale();
Transform viewScaleTransform = Transform.createScaleTransform(1 / scale,
-1 / scale);
Transform focusRotateTransform = Transform
.createRotateTransform(-getFocusRotationAsRadian());
Vector2f focusOffset = getFocusOffset();
Transform focusOffsetTransform = Transform
.createTranslateTransform(focusOffset.getX(), focusOffset.getY());
Transform transform = new Transform();
transform.concatenate(focusOffsetTransform);
transform.concatenate(focusRotateTransform);
transform.concatenate(viewScaleTransform);
transform.concatenate(viewOffsetTransform);
return transform;
}
}