package Hexel.things;
import java.util.ArrayList;
import java.util.HashSet;
import Hexel.Engine;
import Hexel.Resources;
import Hexel.blocks.BlockTools;
import Hexel.blocks.BlockTools.PointFloodSearchMatcher;
import Hexel.blocks.types.Block;
import Hexel.blocks.types.BlockEmpty;
import Hexel.blocks.types.BlockWater;
import Hexel.chunk.Chunk;
import Hexel.chunk.Chunks;
import Hexel.math.HexGeometry;
import Hexel.math.Vector2d;
import Hexel.math.Vector2i;
import Hexel.math.Vector3d;
import Hexel.math.Vector3i;
import Hexel.things.types.Cuboid;
import Hexel.things.types.Thing;
import Hexel.things.types.Volumetric;
public class ThingTools {
private Chunks chunks;
private Engine engine;
private static final Vector3d zeroOffset = new Vector3d(0, 0, 0);
public ThingTools(Engine engine, Chunks chunks) {
this.engine = engine;
this.chunks = chunks;
}
public ArrayList<Vector3i> getBlocksIntersectingThing(final Cuboid c, final FixOffsetTmps fixOffsetTmps){
Vector3i checkBlockPos = new Vector3i();
Vector2d tmp = new Vector2d();
HexGeometry.cartesianToBlock(c.getX(), c.getY(), c.getZ(), tmp, checkBlockPos);
final ArrayList<Vector3i> blocks = new ArrayList<Vector3i>();
BlockTools.pointFloodSearch(checkBlockPos, new PointFloodSearchMatcher(){
@Override
public boolean matches(Vector3i p) {
Vector3d partialFix = Resources.vector3dResourcePool.aquire();
partialFix.x = 0;
partialFix.y = 0;
partialFix.z = 0;
collides(c, zeroOffset, p.x, p.y, p.z, partialFix, fixOffsetTmps.collidesTmps);
if (partialFix.x != 0 || partialFix.y != 0 || partialFix.z != 0){
blocks.add(new Vector3i(p));
return true;
}
else {
Resources.vector3dResourcePool.recycle(partialFix);
}
return false;
}
});
return blocks;
}
public boolean isOutOfRangeOfCamera(Volumetric v){
Vector3d c = Resources.vector3dResourcePool.aquire();
c.x = this.engine.camera.getCameraX();
c.y = this.engine.camera.getCameraY();
c.z = this.engine.camera.getCameraZ();
Vector3d p = Resources.vector3dResourcePool.aquire();
v.getXYZ(p);
Resources.vector3dResourcePool.recycle(c);
Resources.vector3dResourcePool.recycle(p);
return p.distance(c) > this.engine.chunkVisibilityManager.renderDistance;
}
private interface CheckCollidableBlock {
public boolean isColliding(Block block);
}
private CheckCollidableBlock checkCollidesSolid = new CheckCollidableBlock() {
@Override
public boolean isColliding(Block block) {
return !(block instanceof BlockEmpty || block instanceof BlockWater);
}
};
private CheckCollidableBlock checkCollidesWater = new CheckCollidableBlock() {
@Override
public boolean isColliding(Block block) {
return block instanceof BlockWater;
}
};
public class CollidesTmps {
public Vector3d[] v3d6 = new Vector3d[6];
public Vector2d vd = new Vector2d();
public Vector2d ud = new Vector2d();
public double[] d6 = new double[6];
public Vector2d v2d = new Vector2d();
public final Vector3d up = new Vector3d(0, 0, 1);
public final Vector3d vd100 = new Vector3d(1, 0, 0);
public final Vector3d vd010 = new Vector3d(0, 1, 0);
public final Vector3d vd001 = new Vector3d(0, 0, 1);
public Vector3d[] v3d3 = new Vector3d[]{ new Vector3d(), new Vector3d(), new Vector3d() };
}
public class FixOffsetTmps {
public Vector3i tmp3i = new Vector3i();
public Vector2d tmp2d = new Vector2d();
public HashSet<Vector3i> toCheck = new HashSet<Vector3i>();
public HashSet<Vector3d> toFixBlockSet = new HashSet<Vector3d>();
public Vector3d attempt = new Vector3d();
public Vector3i tmp = new Vector3i();
public CollidesTmps collidesTmps = new CollidesTmps();
}
public boolean collidesSolid(Volumetric v, Vector3d offset, FixOffsetTmps tmps) {
if (v instanceof Cuboid) {
return this.fixOffset((Cuboid) v, offset, false, this.checkCollidesSolid, tmps) != null;
} else {
return false;
}
}
public boolean collidesWater(Volumetric v, Vector3d offset, FixOffsetTmps tmps) {
if (v instanceof Cuboid) {
return this.fixOffset((Cuboid) v, offset, false, this.checkCollidesWater, tmps) != null;
} else {
return false;
}
}
public boolean collidesWater(double x, double y, double z, Vector2d tmp2d, Vector3i tmp3i) {
Vector3i blockPos = new Vector3i();
HexGeometry.cartesianToBlock(x, y, z, tmp2d, blockPos);
Block b = this.chunks.getBlock(blockPos.x, blockPos.y, blockPos.z, tmp3i, (Chunk)null);
return b instanceof BlockWater;
}
public Vector3d fixOffset(Cuboid c, Vector3d offset, boolean considerNeighbors, FixOffsetTmps tmps) {
return fixOffset(c, offset, considerNeighbors, this.checkCollidesSolid, tmps);
}
public Vector3d fixOffset(final Cuboid c, final Vector3d offset,
boolean considerNeighbors, final CheckCollidableBlock checkCollidableBlock, final FixOffsetTmps tmps) {
tmps.toCheck.clear();
tmps.toFixBlockSet.clear();
Vector3i checkBlockPos = tmps.tmp3i;
HexGeometry.cartesianToBlock(c.getX(), c.getY(), c.getZ(), tmps.tmp2d, checkBlockPos);
BlockTools.pointFloodSearch(checkBlockPos, new PointFloodSearchMatcher(){
@Override
public boolean matches(Vector3i p) {
Vector3d partialFix = Resources.vector3dResourcePool.aquire();
partialFix.x = 0;
partialFix.y = 0;
partialFix.z = 0;
collides(c, offset, p.x, p.y, p.z, partialFix, tmps.collidesTmps);
if (partialFix.x != 0 || partialFix.y != 0 || partialFix.z != 0){
Block b = chunks.getBlock(p.x, p.y, p.z, tmps.tmp3i, (Chunk)null);
if (checkCollidableBlock.isColliding(b)){
tmps.toFixBlockSet.add(partialFix);
return true;
}
else {
Resources.vector3dResourcePool.recycle(partialFix);
return true;
}
}
else {
Resources.vector3dResourcePool.recycle(partialFix);
}
return false;
}
});
Vector3d toFix = new Vector3d();
for (Vector3d toFixBlock : tmps.toFixBlockSet) {
toFix.add(toFixBlock);
Resources.vector3dResourcePool.recycle(toFixBlock);
}
if (toFix.mag() == 0)
return null;
else {
return toFix;
}
}
public void collides(Cuboid c, Vector3d offset, int blockx, int blocky,
int blockz, Vector3d toFix, CollidesTmps tmps) {
Vector2i[] points;
if (blockx % 2 == 0) {
points = HexGeometry.evenTringlePoints;
} else {
points = HexGeometry.oddTringlePoints;
}
Vector3d[] axis = tmps.v3d6;
axis[0] = tmps.vd100;
axis[1] = tmps.vd010;
axis[2] = tmps.vd001;
axis[3] = null;
axis[4] = null;
axis[5] = null;
Vector3d up = tmps.up;
for (int i = 0; i < points.length; i++) {
Vector2i v = points[i];
Vector2i u = points[(i + 1) % points.length];
Vector2d vd = tmps.vd;
Vector2d ud = tmps.ud;
HexGeometry.hexToCartesian(v.x, v.y, vd);
HexGeometry.hexToCartesian(u.x, u.y, ud);
Vector3d plane = tmps.v3d3[i];
plane.x = vd.x - ud.x;
plane.y = vd.y - ud.y;
plane.z = 0;
plane.cross(up);
plane.unit();
axis[3 + i] = plane;
if (plane.y == -1)
plane.y = 1;
}
double[] magnitudes = tmps.d6;
magnitudes[0] = 0;
magnitudes[1] = 0;
magnitudes[2] = 0;
magnitudes[3] = 0;
magnitudes[4] = 0;
magnitudes[5] = 0;
for (int i = 0; i < axis.length; i++) {
Double m = calcOverlapMagnitude(c, offset, blockx, blocky, blockz,
axis[i], tmps);
if (m == 0)
return;
magnitudes[i] = m;
}
for (int i = 0; i < axis.length; i++) {
double overlapMagnitude = magnitudes[i];
if (overlapMagnitude != 0
&& (toFix.mag() == 0 || Math.abs(overlapMagnitude) < toFix.mag())) {
toFix.x = overlapMagnitude * axis[i].x;
toFix.y = overlapMagnitude * axis[i].y;
toFix.z = overlapMagnitude * axis[i].z;
}
}
}
public Double calcOverlapMagnitude(Cuboid c, Vector3d offset, int blockx,
int blocky, int blockz, Vector3d axis, CollidesTmps tmps) {
double minC = Integer.MAX_VALUE;
double maxC = Integer.MIN_VALUE;
double cx = c.getX() + offset.x;
double cy = c.getY() + offset.y;
double cz = c.getZ() + offset.z;
minC = Math.min(minC, axis.dot(cx, cy, cz));
minC = Math.min(minC, axis.dot(cx + c.getWidth(), cy, cz));
minC = Math.min(minC, axis.dot(cx, cy + c.getHeight(), cz));
minC = Math.min(minC,
axis.dot(cx + c.getWidth(), cy + c.getHeight(), cz));
minC = Math.min(minC, axis.dot(cx, cy, cz + c.getDepth()));
minC = Math.min(minC,
axis.dot(cx + c.getWidth(), cy, cz + c.getDepth()));
minC = Math.min(minC,
axis.dot(cx, cy + c.getHeight(), cz + c.getDepth()));
minC = Math.min(
minC,
axis.dot(cx + c.getWidth(), cy + c.getHeight(),
cz + c.getDepth()));
maxC = Math.max(maxC, axis.dot(cx, cy, cz));
maxC = Math.max(maxC, axis.dot(cx + c.getWidth(), cy, cz));
maxC = Math.max(maxC, axis.dot(cx, cy + c.getHeight(), cz));
maxC = Math.max(maxC,
axis.dot(cx + c.getWidth(), cy + c.getHeight(), cz));
maxC = Math.max(maxC, axis.dot(cx, cy, cz + c.getDepth()));
maxC = Math.max(maxC,
axis.dot(cx + c.getWidth(), cy, cz + c.getDepth()));
maxC = Math.max(maxC,
axis.dot(cx, cy + c.getHeight(), cz + c.getDepth()));
maxC = Math.max(
maxC,
axis.dot(cx + c.getWidth(), cy + c.getHeight(),
cz + c.getDepth()));
double minB = Integer.MAX_VALUE;
double maxB = Integer.MIN_VALUE;
Vector2d p = tmps.v2d;
Vector2i[] points;
if (blockx % 2 == 0) {
points = HexGeometry.evenTringlePoints;
} else {
points = HexGeometry.oddTringlePoints;
}
for (Vector2i point : points) {
HexGeometry.hexToCartesian(Math.floor(blockx / 2.0) + point.x,
blocky + point.y, p);
minB = Math.min(minB, axis.dot(p.x, p.y, blockz / 2.0));
maxB = Math.max(maxB, axis.dot(p.x, p.y, blockz / 2.0));
minB = Math.min(minB, axis.dot(p.x, p.y, blockz / 2.0 + .5));
maxB = Math.max(maxB, axis.dot(p.x, p.y, blockz / 2.0 + .5));
}
if (maxC <= minB || minC >= maxB) {
return 0.0;
} else {
double a = minB - maxC;
double b = maxB - minC;
if (Math.abs(a) > Math.abs(b))
return b;
else
return a;
}
}
}