/** * 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.plugins.ship.hull; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Set; import jo.sm.data.BlockTypes; import jo.sm.data.CubeIterator; import jo.sm.data.SparseMatrix; import jo.sm.data.StarMade; import jo.sm.logic.PluginUtils; import jo.sm.mods.IBlocksPlugin; import jo.sm.mods.IPluginCallback; import jo.sm.plugins.planet.hollow.HollowPlugin; import jo.sm.plugins.ship.imp.PlotLogic; import jo.sm.plugins.ship.rotate.RotateParameters; import jo.sm.plugins.ship.rotate.RotatePlugin; import jo.sm.ship.data.Block; import jo.sm.ship.logic.ShipLogic; import jo.vecmath.Point3f; import jo.vecmath.Point3i; import jo.vecmath.ext.Hull3f; import jo.vecmath.logic.MathUtils; import jo.vecmath.logic.Point3iLogic; /** * @Auther Jo Jaquinta for SMEdit Classic - version 1.0 **/ public class HullPlugin implements IBlocksPlugin { public static final String NAME = "Hull"; public static final String DESC = "Generate Basic Hull"; public static final String AUTH = "Jo Jaquinta"; public static final int[][] CLASSIFICATIONS = { {TYPE_SHIP, SUBTYPE_GENERATE}, {TYPE_STATION, SUBTYPE_GENERATE},}; private static final Random mRND = new Random(); @Override public String getName() { return NAME; } @Override public String getDescription() { return DESC; } @Override public String getAuthor() { return AUTH; } @Override public Object newParameterBean() { return new HullParameters(); } @Override public void initParameterBean(SparseMatrix<Block> original, Object params, StarMade sm, IPluginCallback cb) { } @Override public int[][] getClassifications() { return CLASSIFICATIONS; } @Override public SparseMatrix<Block> modify(SparseMatrix<Block> original, Object p, StarMade sm, IPluginCallback cb) { HullParameters params; params = (HullParameters) p; Point3i center; Point3i lower; Point3i upper; SparseMatrix<Block> modified; if ((sm.getSelectedLower() == null) || (sm.getSelectedUpper() == null)) { center = new Point3i(params.getCenterX(), params.getCenterY(), params.getCenterZ()); lower = new Point3i(center.x - params.getSizeX() / 2, center.y - params.getSizeY() / 2, center.z - params.getSizeZ() / 2); upper = new Point3i(center.x + params.getSizeX() / 2, center.y + params.getSizeY() / 2, center.z + params.getSizeZ() / 2); modified = new SparseMatrix<>(); } else { lower = sm.getSelectedLower(); upper = sm.getSelectedUpper(); center = Point3iLogic.interpolate(lower, upper, .5f); modified = new SparseMatrix<>(original); } switch (params.getType()) { case HullParameters.OPEN_FRAME: generateOpenFrame(modified, center, lower, upper, cb); break; case HullParameters.NEEDLE: generateNeedle(modified, center, lower, upper, cb); break; case HullParameters.CONE: generateCone(modified, center, lower, upper, cb); break; case HullParameters.CYLINDER: generateCylinder(modified, center, lower, upper, cb); break; case HullParameters.BOX: generateBox(modified, center, lower, upper, cb); break; case HullParameters.SPHERE: generateSphere(modified, center, lower, upper, cb, false); break; case HullParameters.HEMISPHERE: generateSphere(modified, center, lower, upper, cb, true); break; case HullParameters.DISC: generateDisc(modified, center, lower, upper, cb); break; case HullParameters.IRREGULAR: generateIrregular(modified, center, lower, upper, cb); break; case HullParameters.TORUS: generateTorus(modified, center, lower, upper, cb); break; } HollowPlugin.doHollow(modified, cb); if (ShipLogic.findCore(modified) == null) { modified.set(params.getCenterX(), params.getCenterY(), params.getCenterZ(), new Block(BlockTypes.CORE_ID)); } return modified; } private void generateBox(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling Box"); cb.startTask(PluginUtils.getVolume(lower, upper)); for (Iterator<Point3i> i = new CubeIterator(lower, upper); i.hasNext();) { Point3i p = i.next(); addHull(grid, p); cb.workTask(1); } cb.endTask(); } private void generateTorus(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling torus"); Point3f radius = new Point3f(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z); radius.scale(.5f); float maxDiam = Math.max(Math.max(radius.x, radius.y), radius.z); float resolution = 2.5f / maxDiam; Hull3f torus = PlotLogic.makeTorus(resolution); PlotLogic.mapHull(grid, torus, radius, new Point3i(), new Point3i(), cb); } private void generateSphere(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb, boolean hemi) { cb.setStatus("Filling sphere"); Point3f radius = new Point3f(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z); radius.scale(0.50f); float maxDiam = Math.max(Math.max(radius.x, radius.y), radius.z); float resolution = 2.50f / maxDiam; Hull3f sphere = PlotLogic.makeSphere(resolution, hemi); PlotLogic.mapHull(grid, sphere, radius, new Point3i(), new Point3i(), cb); } private void generateCone(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling Cone"); cb.startTask(upper.z - lower.z + 1); for (int z = lower.z; z <= upper.z; z++) { cb.workTask(1); int xRad = (int) MathUtils.interpolate(z, lower.z, upper.z, (upper.x - lower.x) / 2, 0); int yRad = (int) MathUtils.interpolate(z, lower.z, upper.z, (upper.y - lower.y) / 2, 0); drawElipse(grid, center.x, center.y, z, xRad, yRad); } cb.endTask(); } private void generateNeedle(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling Cone"); cb.startTask(upper.z - lower.z + 1); int xMidRad; xMidRad = center.x - lower.x; int yMidRad; yMidRad = center.y - lower.y; int xRad; int yRad; for (int z = lower.z; z <= upper.z; z++) { if (z < center.z) { xRad = (int) MathUtils.interpolate(z, lower.z, center.z, 0, xMidRad); yRad = (int) MathUtils.interpolate(z, lower.z, center.z, 0, yMidRad); } else { xRad = (int) MathUtils.interpolate(z, center.z, upper.z, xMidRad, 0); yRad = (int) MathUtils.interpolate(z, center.z, upper.z, yMidRad, 0); } drawElipse(grid, center.x, center.y, z, xRad, yRad); cb.workTask(1); } cb.endTask(); } private void generateCylinder(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling Cylinder"); cb.startTask(upper.z - lower.z + 1); int xRad; xRad = (upper.x - lower.x) / 2; int yRad; yRad = (upper.y - lower.y) / 2; for (int z = lower.z; z <= upper.z; z++) { drawElipse(grid, center.x, center.y, z, xRad, yRad); cb.workTask(1); } cb.endTask(); } private void generateDisc(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { cb.setStatus("Filling Disk"); cb.startTask(upper.y - lower.y + 1); int xRad; xRad = (upper.x - lower.x) / 2; int zRad; zRad = (upper.z - lower.z) / 2; for (int y = lower.y; y <= upper.y; y++) { drawElipse(grid, center.x, center.z, y, xRad, zRad); cb.workTask(1); } cb.endTask(); RotateParameters params = new RotateParameters(); params.setXRotate(90); SparseMatrix<Block> modified; modified = RotatePlugin.rotateAround(grid, params, center); grid.set(modified); } private void generateIrregular(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { Set<Point3i> done; done = new HashSet<>(); List<Point3i> todo; todo = new ArrayList<>(); todo.add(center); int volume; volume = PluginUtils.getVolume(lower, upper); cb.setStatus("Filling Irregular"); cb.startTask(volume); for (int i = 0; i < volume; i++) { int idx = mRND.nextInt(todo.size()); Point3i p = todo.get(idx); todo.remove(idx); addHull(grid, p); done.add(p); add(done, todo, p, 1, 0, 0); add(done, todo, p, -1, 0, 0); add(done, todo, p, 0, 1, 0); add(done, todo, p, 0, -1, 0); add(done, todo, p, 0, 0, 1); add(done, todo, p, 0, 0, -1); cb.workTask(1); } cb.endTask(); } private void add(Set<Point3i> done, List<Point3i> todo, Point3i p, int dx, int dy, int dz) { Point3i next; next = new Point3i(p.x + dx, p.y + dy, p.z + dz); if (done.contains(next)) { return; } if (!todo.contains(next)) { todo.add(next); } } private void generateOpenFrame(SparseMatrix<Block> grid, Point3i center, Point3i lower, Point3i upper, IPluginCallback cb) { Set<Point3i> done; done = new HashSet<>(); List<Point3i> todo; todo = new ArrayList<>(); todo.add(new Point3i(0, 0, 0)); int volume; volume = PluginUtils.getVolume(lower, upper); cb.setStatus("Filling Open frame"); cb.startTask(volume); for (int i = volume / (8 * 8 * 8); i > 0; i--) { int idx = mRND.nextInt(todo.size()); Point3i p = todo.get(idx); todo.remove(idx); addFrame(grid, p, center); done.add(p); addFrameLink(grid, done, todo, p, center, 1, 0, 0); addFrameLink(grid, done, todo, p, center, -1, 0, 0); addFrameLink(grid, done, todo, p, center, 0, 1, 0); addFrameLink(grid, done, todo, p, center, 0, -1, 0); addFrameLink(grid, done, todo, p, center, 0, 0, 1); addFrameLink(grid, done, todo, p, center, 0, 0, -1); cb.workTask(1); } cb.endTask(); } private void addFrameLink(SparseMatrix<Block> grid, Set<Point3i> done, List<Point3i> todo, Point3i p, Point3i center, int dx, int dy, int dz) { add(done, todo, p, dx, dy, dz); Point3i next = new Point3i(p.x + dx, p.y + dy, p.z + dz); if (!done.contains(next)) { return; } Point3i planeX = new Point3i(); Point3i planeY = new Point3i(); Point3i axisZ = new Point3i(dx, dy, dz); Point3i o = new Point3i(center.x + p.x * 12, center.y + p.y * 12, center.z + p.z * 12); if (dx == 0) { planeX.x = 1; if (dy == 0) { planeY.y = 1; } else { planeY.z = 1; } } else { planeX.y = 1; planeY.z = 1; } if (dx == 1) { o.x += 7; } else if (dy == 1) { o.y += 7; } else if (dz == 1) { o.z += 7; } for (int z = 0; z <= 5; z++) { int skip = ((z == 0) || (z == 5)) ? 1 : 3; int rad = ((z == 0) || (z == 5)) ? 6 : 2; short type = ((z == 0) || (z == 5)) ? BlockTypes.HULL_COLOR_BLUE_ID : BlockTypes.HULL_COLOR_WHITE_ID; Point3i squareO = new Point3i(o); for (int s = 0; s < skip; s++) { squareO.add(planeX); squareO.add(planeY); } for (int x = 0; x < rad; x++) { Point3i squareOO = new Point3i(squareO); for (int y = 0; y < rad; y++) { addHull(grid, squareOO, type); squareOO.add(planeY); } squareO.add(planeX); } o.add(axisZ); } } private void addFrame(SparseMatrix<Block> grid, Point3i p, Point3i center) { Point3i o; o = new Point3i(center.x + p.x * 12, center.y + p.y * 12, center.z + p.z * 12); for (int i = 0; i < 8; i++) { addHull(grid, o.x + i, o.y, o.z); addHull(grid, o.x + i, o.y + 7, o.z); addHull(grid, o.x + i, o.y + 7, o.z + 7); addHull(grid, o.x + i, o.y, o.z + 7); addHull(grid, o.x, o.y + i, o.z); addHull(grid, o.x + 7, o.y + i, o.z); addHull(grid, o.x + 7, o.y + i, o.z + 7); addHull(grid, o.x, o.y + i, o.z + 7); addHull(grid, o.x, o.y, o.z + i); addHull(grid, o.x + 7, o.y, o.z + i); addHull(grid, o.x + 7, o.y + 7, o.z + i); addHull(grid, o.x, o.y + 7, o.z + i); } } private void drawElipse(SparseMatrix<Block> grid, int xc, int yc, int zc, int xRad, int yRad) { if (xRad == 0) { if (yRad == 0) { addHull(grid, xc, yc, zc); return; } else { for (int y = -yRad; y <= yRad; y++) { addHull(grid, xc, yc + y, zc); } return; } } else if (yRad == 0) { for (int x = -xRad; x <= xRad; x++) { addHull(grid, xc + x, yc, zc); } return; } int a2 = xRad * xRad; int b2 = yRad * yRad; int fa2 = 4 * a2; int x, y, sigma; for (x = 0, y = yRad, sigma = 2 * b2 + a2 * (1 - 2 * yRad); b2 * x <= a2 * y; x++) { drawXLine(grid, xc - x, yc + y, zc, xc + x); drawXLine(grid, xc - x, yc - y, zc, xc + x); drawYLine(grid, xc - x, yc - y, zc, yc + y); drawYLine(grid, xc + x, yc - y, zc, yc + y); if (sigma >= 0) { sigma += fa2 * (1 - y); y--; } sigma += b2 * (4 * x + 6); } int fb2 = 4 * b2; for (x = xRad, y = 0, sigma = 2 * a2 + b2 * (1 - 2 * xRad); a2 * y <= b2 * x; y++) { drawXLine(grid, xc - x, yc + y, zc, xc + x); drawXLine(grid, xc - x, yc - y, zc, xc + x); drawYLine(grid, xc - x, yc - y, zc, yc + y); drawYLine(grid, xc + x, yc - y, zc, yc + y); if (sigma >= 0) { sigma += fb2 * (1 - x); x--; } sigma += a2 * (4 * y + 6); } } private void drawXLine(SparseMatrix<Block> grid, int x1, int y, int z, int x2) { for (int x = x1; x <= x2; x++) { addHull(grid, x, y, z); } } private void drawYLine(SparseMatrix<Block> grid, int x, int y1, int z, int y2) { for (int y = y1; y <= y2; y++) { addHull(grid, x, y, z); } } private void addHull(SparseMatrix<Block> grid, Point3i p) { addHull(grid, p.x, p.y, p.z); } private void addHull(SparseMatrix<Block> grid, Point3i p, short type) { addHull(grid, p.x, p.y, p.z, type); } private void addHull(SparseMatrix<Block> grid, int x, int y, int z) { addHull(grid, x, y, z, BlockTypes.HULL_COLOR_GREY_ID); } private void addHull(SparseMatrix<Block> grid, int x, int y, int z, short type) { Block b; b = new Block(); b.setBlockID(type); grid.set(x, y, z, b); } }