/**
* Copyright 2014
* SMEdit https://github.com/StarMade/SMEdit
* SMTools https://github.com/StarMade/SMTools
*
* 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 jo.sm.logic;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import jo.sm.data.BlockTypes;
import jo.sm.data.CubeIterator;
import jo.sm.data.RenderTile;
import jo.sm.data.SparseMatrix;
import jo.sm.ship.data.Block;
import jo.sm.ui.BlockTypeColors;
import jo.vecmath.Matrix3f;
import jo.vecmath.Matrix4f;
import jo.vecmath.Point3f;
import jo.vecmath.Point3i;
public class RenderLogic {
private static final Logger log = Logger.getLogger(RenderLogic.class.getName());
public static List<RenderTile> getRender(SparseMatrix<Block> blocks) {
List<RenderTile> polys = new ArrayList<>();
Point3i lower = new Point3i();
Point3i upper = new Point3i();
blocks.getBounds(lower, upper);
getBasicPolys(blocks, upper, lower, polys);
return polys;
}
private static void getBasicPolys(SparseMatrix<Block> blocks,
Point3i upper, Point3i lower, List<RenderTile> polys) {
for (CubeIterator i = new CubeIterator(lower, upper); i.hasNext();) {
Point3i p = i.next();
if (!blocks.contains(p)) {
continue;
}
Block b = blocks.get(p);
if (BlockTypes.isCorner(b.getBlockID()) || BlockTypes.isPowerCorner(b.getBlockID())) {
doCorner(blocks, p, polys);
} else if (BlockTypes.isWedge(b.getBlockID()) || BlockTypes.isPowerWedge(b.getBlockID())) {
doWedge(blocks, p, polys);
} else if (BlockTypes.isPenta(b.getBlockID()) || BlockTypes.isPowerPenta(b.getBlockID())) {
doPenta(blocks, p, polys);
} else if (BlockTypes.isTetra(b.getBlockID()) || BlockTypes.isPowerTetra(b.getBlockID())) {
doTetra(blocks, p, polys);
} else {
doCube(blocks, p, polys);
}
}
}
private static void doPenta(SparseMatrix<Block> blocks, Point3i p, List<RenderTile> polys) {
}
private static void doTetra(SparseMatrix<Block> blocks, Point3i p, List<RenderTile> polys) {
}
private static void doCorner(SparseMatrix<Block> blocks, Point3i p, List<RenderTile> polys) {
log.log(Level.INFO, "Corner, ori="+blocks.get(p).getOrientation());
switch (blocks.get(p).getOrientation()) {
case 0: // spire: xp,zm>yp
doYMSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXMSquare(blocks, p, polys, RenderTile.TRI4); // back
doZPSquare(blocks, p, polys, RenderTile.TRI1); // back
doRect(blocks, p, polys, RenderTile.XPYP);
doRect(blocks, p, polys, RenderTile.YPZM);
break;
case 1: // spire: xp,zp>yp
doYMSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXMSquare(blocks, p, polys, RenderTile.TRI1); // back
doZMSquare(blocks, p, polys, RenderTile.TRI1); // back
doRect(blocks, p, polys, RenderTile.XPYP);
doRect(blocks, p, polys, RenderTile.YPZP);
break;
case 2: // spire: xm,zp>yp
doYMSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXPSquare(blocks, p, polys, RenderTile.TRI1); // back
doZMSquare(blocks, p, polys, RenderTile.TRI2); // back
doRect(blocks, p, polys, RenderTile.XMYP);
doRect(blocks, p, polys, RenderTile.YPZP);
break;
case 3: // spire: xm,zp>yp
doYMSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXPSquare(blocks, p, polys, RenderTile.TRI4); // back
doZPSquare(blocks, p, polys, RenderTile.TRI2); // back
doRect(blocks, p, polys, RenderTile.XMYP);
doRect(blocks, p, polys, RenderTile.YPZM);
break;
case 4: // spire: xm,zp>yp
doYPSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXMSquare(blocks, p, polys, RenderTile.TRI3); // back
doZPSquare(blocks, p, polys, RenderTile.TRI4); // back
doRect(blocks, p, polys, RenderTile.XPYM);
doRect(blocks, p, polys, RenderTile.YMZM);
break;
case 5: // spire: xm,zp>yp
doYPSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXMSquare(blocks, p, polys, RenderTile.TRI2); // back
doZMSquare(blocks, p, polys, RenderTile.TRI4); // back
doRect(blocks, p, polys, RenderTile.XPYM);
doRect(blocks, p, polys, RenderTile.YMZP);
break;
case 6: // spire: xm,zp>yp
doYPSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXPSquare(blocks, p, polys, RenderTile.TRI2); // back
doZMSquare(blocks, p, polys, RenderTile.TRI3); // back
doRect(blocks, p, polys, RenderTile.XMYM);
doRect(blocks, p, polys, RenderTile.YMZP);
break;
case 7: // spire: xm,zp>yp
doYPSquare(blocks, p, polys, RenderTile.SQUARE); // bottom
doXPSquare(blocks, p, polys, RenderTile.TRI3); // back
doZPSquare(blocks, p, polys, RenderTile.TRI3); // back
doRect(blocks, p, polys, RenderTile.XMYM);
doRect(blocks, p, polys, RenderTile.YMZM);
break;
}
}
private static void doWedge(SparseMatrix<Block> blocks, Point3i p, List<RenderTile> polys) {
switch (blocks.get(p).getOrientation()) {
case 0: // YPZM
doXMSquare(blocks, p, polys, RenderTile.TRI4);
doXPSquare(blocks, p, polys, RenderTile.TRI4);
// no YP face
doYMSquare(blocks, p, polys, RenderTile.SQUARE);
doZPSquare(blocks, p, polys, RenderTile.SQUARE);
// no ZM face
doRect(blocks, p, polys, RenderTile.YPZM);
break;
case 1: // XMYP
doXPSquare(blocks, p, polys, RenderTile.SQUARE);
// no XM face
// no YP face
doYMSquare(blocks, p, polys, RenderTile.SQUARE);
doZMSquare(blocks, p, polys, RenderTile.TRI2);
doZPSquare(blocks, p, polys, RenderTile.TRI2);
doRect(blocks, p, polys, RenderTile.XMYP);
break;
case 2: // YPZP
doXMSquare(blocks, p, polys, RenderTile.TRI1);
doXPSquare(blocks, p, polys, RenderTile.TRI1);
// no YP face
doYMSquare(blocks, p, polys, RenderTile.SQUARE);
// no ZP face
doZMSquare(blocks, p, polys, RenderTile.SQUARE);
doRect(blocks, p, polys, RenderTile.YPZP);
break;
case 3: // XPYP
// no XP face
doXMSquare(blocks, p, polys, RenderTile.SQUARE);
// no YP face
doYMSquare(blocks, p, polys, RenderTile.SQUARE);
doZMSquare(blocks, p, polys, RenderTile.TRI1);
doZPSquare(blocks, p, polys, RenderTile.TRI1);
doRect(blocks, p, polys, RenderTile.XPYP);
break;
case 4: // YMZM
doXMSquare(blocks, p, polys, RenderTile.TRI3);
doXPSquare(blocks, p, polys, RenderTile.TRI3);
doYPSquare(blocks, p, polys, RenderTile.SQUARE);
// no YM face
doZPSquare(blocks, p, polys, RenderTile.SQUARE);
// no ZM face
doRect(blocks, p, polys, RenderTile.YMZM);
break;
case 5: // XPYM
// no XP face
doXMSquare(blocks, p, polys, RenderTile.SQUARE);
doYPSquare(blocks, p, polys, RenderTile.SQUARE);
// no YM face
doZMSquare(blocks, p, polys, RenderTile.TRI4);
doZPSquare(blocks, p, polys, RenderTile.TRI4);
doRect(blocks, p, polys, RenderTile.XPYM);
break;
case 6: // YMZP
doXMSquare(blocks, p, polys, RenderTile.TRI2);
doXPSquare(blocks, p, polys, RenderTile.TRI2);
doYPSquare(blocks, p, polys, RenderTile.SQUARE);
// no YM face
// no ZP face
doZMSquare(blocks, p, polys, RenderTile.SQUARE);
doRect(blocks, p, polys, RenderTile.YMZP);
break;
case 7: // XMYM
doXPSquare(blocks, p, polys, RenderTile.SQUARE);
// no XM face
doYPSquare(blocks, p, polys, RenderTile.SQUARE);
// no YM face
doZMSquare(blocks, p, polys, RenderTile.TRI3);
doZPSquare(blocks, p, polys, RenderTile.TRI3);
doRect(blocks, p, polys, RenderTile.XMYM);
break;
case 8: // XPZM
case 12: // ???
// no XP face
doXMSquare(blocks, p, polys, RenderTile.SQUARE);
doYPSquare(blocks, p, polys, RenderTile.TRI2);
doYMSquare(blocks, p, polys, RenderTile.TRI2);
doZPSquare(blocks, p, polys, RenderTile.SQUARE);
// no ZM face
doRect(blocks, p, polys, RenderTile.ZMXP);
break;
case 10: // XMZM
doXPSquare(blocks, p, polys, RenderTile.SQUARE);
// no XM face
doYPSquare(blocks, p, polys, RenderTile.TRI3);
doYMSquare(blocks, p, polys, RenderTile.TRI3);
doZPSquare(blocks, p, polys, RenderTile.SQUARE);
// no ZM face
doRect(blocks, p, polys, RenderTile.ZMXM);
break;
case 11: // XMZP
doXPSquare(blocks, p, polys, RenderTile.SQUARE);
// no XM face
doYPSquare(blocks, p, polys, RenderTile.TRI4);
doYMSquare(blocks, p, polys, RenderTile.TRI4);
// no ZP face
doZMSquare(blocks, p, polys, RenderTile.SQUARE);
doRect(blocks, p, polys, RenderTile.ZPXM);
break;
case 13: // XPZP
// no XP face
doXMSquare(blocks, p, polys, RenderTile.SQUARE);
doYPSquare(blocks, p, polys, RenderTile.TRI1);
doYMSquare(blocks, p, polys, RenderTile.TRI1);
// no ZP face
doZMSquare(blocks, p, polys, RenderTile.SQUARE);
doRect(blocks, p, polys, RenderTile.ZPXP);
break;
default:
log.log(Level.INFO, "Wedge with unknown ori="+blocks.get(p).getOrientation());
break;
}
}
private static void doCube(SparseMatrix<Block> blocks, Point3i p, List<RenderTile> polys) {
doXPSquare(blocks, p, polys, RenderTile.SQUARE);
doXMSquare(blocks, p, polys, RenderTile.SQUARE);
doYPSquare(blocks, p, polys, RenderTile.SQUARE);
doYMSquare(blocks, p, polys, RenderTile.SQUARE);
doZPSquare(blocks, p, polys, RenderTile.SQUARE);
doZMSquare(blocks, p, polys, RenderTile.SQUARE);
}
private static void doRect(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int facing) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(facing);
rp.setCenter(new Point3i(p));
rp.setType(RenderTile.RECTANGLE);
polys.add(rp);
}
private static void doZMSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x, p.y, p.z - 1))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.ZM);
rp.setCenter(new Point3i(p));
rp.setType(type);
polys.add(rp);
}
}
private static void doZPSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x, p.y, p.z + 1))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.ZP);
rp.setCenter(new Point3i(p.x, p.y, p.z + 1));
rp.setType(type);
polys.add(rp);
}
}
private static void doYMSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x, p.y - 1, p.z))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.YM);
rp.setCenter(new Point3i(p));
rp.setType(type);
polys.add(rp);
}
}
private static void doYPSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x, p.y + 1, p.z))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.YP);
rp.setCenter(new Point3i(p.x, p.y + 1, p.z));
rp.setType(type);
polys.add(rp);
}
}
private static void doXMSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x - 1, p.y, p.z))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.XM);
rp.setCenter(new Point3i(p));
rp.setType(type);
polys.add(rp);
}
}
private static void doXPSquare(SparseMatrix<Block> blocks, Point3i p,
List<RenderTile> polys, int type) {
if (!blocks.contains(new Point3i(p.x + 1, p.y, p.z))) {
RenderTile rp = new RenderTile();
rp.setBlock(blocks.get(p));
rp.setFacing(RenderTile.XP);
rp.setCenter(new Point3i(p.x + 1, p.y, p.z));
rp.setType(type);
polys.add(rp);
}
}
public static void transformAndSort(List<RenderTile> tiles, Matrix4f transform) {
Matrix3f rot = new Matrix3f();
transform.get(rot);
boolean[] showing = new boolean[6 + 12];
calcShowing(showing, rot, 1, 0, 0, RenderTile.XP, RenderTile.XM);
calcShowing(showing, rot, 0, 1, 0, RenderTile.YP, RenderTile.YM);
calcShowing(showing, rot, 0, 0, 1, RenderTile.ZP, RenderTile.ZM);
calcShowing(showing, rot, 1, 1, 0, RenderTile.XPYP, RenderTile.XMYM);
calcShowing(showing, rot, -1, 1, 0, RenderTile.XMYP, RenderTile.XPYM);
calcShowing(showing, rot, 0, 1, 1, RenderTile.YPZP, RenderTile.YMZM);
calcShowing(showing, rot, 0, -1, 1, RenderTile.YMZP, RenderTile.YPZM);
calcShowing(showing, rot, 1, 0, 1, RenderTile.ZPXP, RenderTile.ZMXM);
calcShowing(showing, rot, 1, 0, -1, RenderTile.ZMXP, RenderTile.ZPXM);
log.log(Level.INFO, "Showing +x="+showing[0]+", -x="+showing[1]+", +y="+showing[2]+", -y="+showing[3]+", +z="+showing[4]+", -z="+showing[5]);
for (RenderTile tile : tiles) {
if (!showing[tile.getFacing()]) {
tile.setVisual(null);
continue;
}
Point3i center = tile.getCenter();
Point3f visual = new Point3f(center.x, center.y, center.z);
transform.transform(visual);
tile.setVisual(visual);
}
Collections.sort(tiles, new Comparator<RenderTile>() {
@Override
public int compare(RenderTile tile1, RenderTile tile2) {
if (tile1.getVisual() == null) {
if (tile2.getVisual() == null) {
return 0;
} else {
return 1;
}
} else if (tile2.getVisual() == null) {
return -1;
} else {
return (int) Math.signum(tile2.getVisual().z - tile1.getVisual().z);
}
}
});
}
private static void calcShowing(boolean[] showing, Matrix3f rot, int dx, int dy, int dz, int axis, int naxis) {
Point3f xp = new Point3f(dx, dy, dz);
rot.transform(xp);
showing[axis] = xp.z < 0;
showing[naxis] = !showing[axis];
}
public static void draw(Graphics2D g2, List<RenderTile> tiles, Point3f unitX, Point3f unitY, Point3f unitZ,
boolean fancyGraphics) {
float[][] corners = new float[4][2];
for (RenderTile tile : tiles) {
Point3f corner = tile.getVisual();
if (corner == null) {
break;
}
if (!getCorners(tile, corner, corners, unitX, unitY, unitZ)) {
continue;
}
ImageIcon icon = null;
if (fancyGraphics) {
icon = BlockTypeColors.getBlockImage(tile.getBlock().getBlockID());
}
if (tile.getType() == RenderTile.SQUARE) {
renderSquare(g2, corners, tile, icon);
} else if ((tile.getType() >= RenderTile.TRI1) && (tile.getType() <= RenderTile.TRI4)) {
renderTriangle(g2, corners, tile, icon);
} else if ((tile.getType() == RenderTile.RECTANGLE)) {
renderSquare(g2, corners, tile, icon);
}
}
}
private static void renderTriangle(Graphics2D g2, float[][] corners,
RenderTile tile, ImageIcon icon) {
log.log(Level.INFO, "Render triangle "+tile.getType());
int pCenter = (tile.getType() - RenderTile.TRI1);
int pLeft = (pCenter + 1) % 4;
int pRight = (pCenter + 3) % 4;
if (icon != null) {
float m00 = (corners[pRight][0] - corners[pCenter][0]) / 64f;
float m10 = (corners[pRight][1] - corners[pCenter][1]) / 64f;
float m01 = (corners[pLeft][0] - corners[pCenter][0]) / 64f;
float m11 = (corners[pLeft][1] - corners[pCenter][1]) / 64f;
float m02 = corners[pCenter][0];
float m12 = corners[pCenter][1];
AffineTransform t = new AffineTransform(m00, m10, m01, m11, m02, m12);
g2.drawImage(icon.getImage(), t, null);
} else {
Path2D p = new Path2D.Float();
p.moveTo(corners[pCenter][0], corners[pCenter][1]);
p.lineTo(corners[pLeft][0], corners[pLeft][1]);
p.lineTo(corners[pRight][0], corners[pRight][1]);
p.lineTo(corners[pCenter][0], corners[pCenter][1]);
g2.setPaint(BlockTypeColors.getFillColor(tile.getBlock().getBlockID()));
g2.fill(p);
g2.setPaint(BlockTypeColors.getOutlineColor(tile.getBlock().getBlockID()));
g2.draw(p);
}
}
private static void renderSquare(Graphics2D g2, float[][] corners,
RenderTile tile, ImageIcon icon) {
if (icon != null) {
float m00 = (corners[1][0] - corners[0][0]) / 64f;
float m10 = (corners[1][1] - corners[0][1]) / 64f;
float m01 = (corners[3][0] - corners[0][0]) / 64f;
float m11 = (corners[3][1] - corners[0][1]) / 64f;
float m02 = corners[0][0];
float m12 = corners[0][1];
AffineTransform t = new AffineTransform(m00, m10, m01, m11, m02, m12);
g2.drawImage(icon.getImage(), t, null);
} else {
Path2D p = new Path2D.Float();
p.moveTo(corners[0][0], corners[0][1]);
p.lineTo(corners[1][0], corners[1][1]);
p.lineTo(corners[2][0], corners[2][1]);
p.lineTo(corners[3][0], corners[3][1]);
p.lineTo(corners[0][0], corners[0][1]);
g2.setPaint(BlockTypeColors.getFillColor(tile.getBlock().getBlockID()));
g2.fill(p);
g2.setPaint(BlockTypeColors.getOutlineColor(tile.getBlock().getBlockID()));
g2.draw(p);
}
}
public static boolean getCorners(RenderTile tile, Point3f corner, float[][] corners,
Point3f unitX, Point3f unitY, Point3f unitZ) {
switch (tile.getFacing()) {
case RenderTile.XP:
case RenderTile.XM:
setCorner(corners, corner, 0, false, false, false, unitX, unitY, unitZ);
setCorner(corners, corner, 1, false, true, false, unitX, unitY, unitZ);
setCorner(corners, corner, 2, false, true, true, unitX, unitY, unitZ);
setCorner(corners, corner, 3, false, false, true, unitX, unitY, unitZ);
break;
case RenderTile.YP:
case RenderTile.YM:
setCorner(corners, corner, 0, false, false, false, unitX, unitY, unitZ);
setCorner(corners, corner, 1, false, false, true, unitX, unitY, unitZ);
setCorner(corners, corner, 2, true, false, true, unitX, unitY, unitZ);
setCorner(corners, corner, 3, true, false, false, unitX, unitY, unitZ);
break;
case RenderTile.ZP:
case RenderTile.ZM:
setCorner(corners, corner, 0, false, false, false, unitX, unitY, unitZ);
setCorner(corners, corner, 1, true, false, false, unitX, unitY, unitZ);
setCorner(corners, corner, 2, true, true, false, unitX, unitY, unitZ);
setCorner(corners, corner, 3, false, true, false, unitX, unitY, unitZ);
break;
case RenderTile.XPYP:
case RenderTile.XMYM:
setDiagonalCorners(corners, corner, false, true, false, false, true, true, unitX, unitY, unitZ);
break;
case RenderTile.XPYM:
case RenderTile.XMYP:
setDiagonalCorners(corners, corner, true, true, false, true, true, true, unitX, unitY, unitZ);
break;
case RenderTile.YPZM:
case RenderTile.YMZP:
setDiagonalCorners(corners, corner, false, false, false, true, false, false, unitX, unitY, unitZ);
break;
case RenderTile.YPZP:
case RenderTile.YMZM:
setDiagonalCorners(corners, corner, false, false, true, true, false, true, unitX, unitY, unitZ);
break;
case RenderTile.ZPXP:
case RenderTile.ZMXM:
setDiagonalCorners(corners, corner, false, false, true, true, false, false, unitX, unitY, unitZ);
break;
case RenderTile.ZPXM:
case RenderTile.ZMXP:
setDiagonalCorners(corners, corner, true, true, true, true, false, true, unitX, unitY, unitZ);
break;
default:
return false;
}
return true;
}
private static void setDiagonalCorners(float[][] corners, Point3f corner,
boolean x0, boolean y0, boolean z0,
boolean x1, boolean y1, boolean z1,
Point3f unitX, Point3f unitY, Point3f unitZ) {
boolean x2 = (x0 == x1) ? !x1 : x1;
boolean x3 = (x1 == x2) ? !x2 : x2;
boolean y2 = (y0 == y1) ? !y1 : y1;
boolean y3 = (y1 == y2) ? !y2 : y2;
boolean z2 = (z0 == z1) ? !z1 : z1;
boolean z3 = (z1 == z2) ? !z2 : z2;
setCorner(corners, corner, 0, x0, y0, z0, unitX, unitY, unitZ);
setCorner(corners, corner, 1, x1, y1, z1, unitX, unitY, unitZ);
setCorner(corners, corner, 2, x2, y2, z2, unitX, unitY, unitZ);
setCorner(corners, corner, 3, x3, y3, z3, unitX, unitY, unitZ);
}
private static void setCorner(float[][] corners, Point3f corner, int off, boolean x, boolean y, boolean z, Point3f unitX, Point3f unitY, Point3f unitZ) {
corners[off][0] = corner.x;
corners[off][1] = corner.y;
if (x) {
corners[off][0] += unitX.x;
corners[off][1] += unitX.y;
}
if (y) {
corners[off][0] += unitY.x;
corners[off][1] += unitY.y;
}
if (z) {
corners[off][0] += unitZ.x;
corners[off][1] += unitZ.y;
}
}
}