/**
* 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.imp;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import jo.vecmath.Point3i;
/**
* @Auther Jo Jaquinta for SMEdit Classic - version 1.0
**/
public class BinvoxLogic {
private static final Logger log = Logger.getLogger(BinvoxLogic.class.getName());
public static BinvoxData read(InputStream is) throws IOException {
BinvoxData data = new BinvoxData();
DataInputStream dis = new DataInputStream(is);
//
// read header
//
String line = readLine(dis); // deprecated function though
if (!line.startsWith("#binvox")) {
log.log(Level.WARNING, "Error: first line reads [" + line + "] instead of [#binvox]");
return null;
}
String version_string = line.substring(8);
int version = Integer.parseInt(version_string);
log.log(Level.INFO, "reading binvox version " + version);
data.setZSpan(0);
data.setXSPan(0);
data.setYSpan(0);
boolean done = false;
while (!done) {
line = readLine(dis);
if (line.startsWith("data")) {
done = true;
} else {
if (line.startsWith("dim")) {
String[] dimensions = line.split(" ");
data.setZSpan(Integer.parseInt(dimensions[1]));
data.setXSPan(Integer.parseInt(dimensions[2]));
data.setYSpan(Integer.parseInt(dimensions[3]));
} else {
if (line.startsWith("translate")) {
String[] dimensions = line.split(" ");
data.setTX(Double.parseDouble(dimensions[1]));
data.setTY(Double.parseDouble(dimensions[2]));
data.setTZ(Double.parseDouble(dimensions[3]));
} else {
if (line.startsWith("scale")) {
String[] dimensions = line.split(" ");
data.setScale(Double.parseDouble(dimensions[1]));
} else {
log.log(Level.WARNING, " unrecognized keyword ["
+ line + "], skipping");
}
}
}
}
}
if (!done) {
log.log(Level.WARNING, " error reading header");
return null;
}
if (data.getZSpan() == 0) {
log.log(Level.WARNING, " missing dimensions in header");
return null;
}
data.setSize(data.getYSpan() * data.getXSpan() * data.getZSpan());
data.setVoxels(new boolean[data.getZSpan()][data.getYSpan()][data.getXSpan()]);
//
// read voxel data
//
byte value;
int count;
int index = 0;
int end_index = 0;
int nr_voxels = 0;
// *input >> value; // read the linefeed char
while (end_index < data.getSize()) {
value = dis.readByte();
// idiotic Java language doesn't have unsigned types, so we have to
// use an int for 'count'
// and make sure that we don't interpret it as a negative number if
// bit 7 (the sign bit) is on
count = dis.readByte() & 0xff;
end_index = index + count;
if (end_index > data.getSize()) {
return null;
}
for (int i = index; i < end_index; i++) {
if (value != 0) {
Point3i p = getCoords(i, data);
data.getVoxels()[p.z][p.y][p.x] = true;
}
}
if (value > 0) {
nr_voxels += count;
}
index = end_index;
log.log(Level.INFO, index + "/" + data.getSize() + " " + Runtime.getRuntime().freeMemory());
} // while
log.log(Level.INFO, " read " + nr_voxels + " voxels");
return data;
} // read_binvox
private static String readLine(InputStream dis) throws IOException {
StringBuilder sb = new StringBuilder();
for (;;) {
int ch = dis.read();
if (ch == -1) {
break;
}
if (ch == '\r') {
continue;
}
if (ch == '\n') {
break;
}
sb.append((char) ch);
}
return sb.toString();
}
public static BinvoxData readHeader(InputStream is) throws IOException {
BinvoxData data = new BinvoxData();
data.setInput(new DataInputStream(is));
//
// read header
//
String line = readLine(data.getInput()); // deprecated function though
if (!line.startsWith("#binvox")) {
log.log(Level.WARNING, "Error: first line reads [" + line
+ "] instead of [#binvox]");
return null;
}
String version_string = line.substring(8);
int version = Integer.parseInt(version_string);
log.log(Level.INFO, "reading binvox version " + version);
data.setZSpan(0);
data.setXSPan(0);
data.setYSpan(0);
boolean done = false;
while (!done) {
line = readLine(data.getInput());
if (line.startsWith("data")) {
done = true;
} else {
if (line.startsWith("dim")) {
String[] dimensions = line.split(" ");
data.setZSpan(Integer.parseInt(dimensions[1]));
data.setXSPan(Integer.parseInt(dimensions[2]));
data.setYSpan(Integer.parseInt(dimensions[3]));
} else {
if (line.startsWith("translate")) {
String[] dimensions = line.split(" ");
data.setTX(Double.parseDouble(dimensions[1]));
data.setTY(Double.parseDouble(dimensions[2]));
data.setTZ(Double.parseDouble(dimensions[3]));
} else {
if (line.startsWith("scale")) {
String[] dimensions = line.split(" ");
data.setScale(Double.parseDouble(dimensions[1]));
} else {
log.log(Level.WARNING, " unrecognized keyword ["
+ line + "], skipping");
}
}
}
}
}
if (!done) {
log.log(Level.WARNING, " error reading header");
return null;
}
if (data.getZSpan() == 0) {
log.log(Level.WARNING, " missing dimensions in header");
return null;
}
data.setSize(data.getYSpan() * data.getXSpan() * data.getZSpan());
data.setVoxels(new boolean[data.getZSpan()][][]);
data.setDone(false);
data.setEndIndex(0);
return data;
}
public static boolean readNextSlice(BinvoxData data) throws IOException {
if (data.isDone()) {
return true;
}
int index = data.getEndIndex();
Point3i startPos = getCoords(index, data);
while (data.getEndIndex() < data.getSize()) {
byte value = data.getInput().readByte();
// idiotic Java language doesn't have unsigned types, so we have to
// use an int for 'count'
// and make sure that we don't interpret it as a negative number if
// bit 7 (the sign bit) is on
int count = data.getInput().readByte() & 0xff;
data.setEndIndex(index + count);
if (data.getEndIndex() > data.getSize()) {
data.getInput().close();
return true;
}
for (int i = index; i < data.getEndIndex(); i++) {
Point3i p = getCoords(i, data);
setVoxel(data, p.x, p.y, p.z, value != 0);
}
index = data.getEndIndex();
Point3i endPos = getCoords(index, data);
log.log(Level.INFO, index+"/"+data.getSize()+" "+Runtime.getRuntime().freeMemory());
if (endPos.x > startPos.x) {
break;
}
} // while
return false;
}
public static void getBounds(BinvoxData hull, Point3i lower, Point3i upper) {
boolean first = true;
for (int z = 0; z < hull.getZSpan(); z++) {
for (int x = 0; x < hull.getXSpan(); x++) {
for (int y = 0; y < hull.getYSpan(); y++) {
if (hull.getVoxels()[z][y][x] == false) {
continue;
}
if (first) {
lower.x = x;
lower.y = y;
lower.z = z;
upper.x = x;
upper.y = y;
upper.z = z;
first = false;
} else {
lower.x = Math.min(lower.x, x);
lower.y = Math.min(lower.y, y);
lower.z = Math.min(lower.z, z);
upper.x = Math.max(upper.x, x);
upper.y = Math.max(upper.y, y);
upper.z = Math.max(upper.z, z);
}
}
}
}
}
public static void setVoxel(BinvoxData hull, int x, int y, int z, boolean value) {
if (hull.getVoxels() == null) {
hull.setVoxels(new boolean[hull.getXSpan()][][]);
}
if (hull.getVoxels()[x] == null) {
hull.getVoxels()[x] = new boolean[hull.getZSpan()][];
}
if (hull.getVoxels()[x][z] == null) {
hull.getVoxels()[x][z] = new boolean[hull.getYSpan()];
}
hull.getVoxels()[x][z][y] = value;
}
public static boolean getVoxel(BinvoxData hull, int x, int y, int z) throws IOException {
if ((x < 0) || (y < 0) || (z < 0)) {
return false;
}
if ((x >= hull.getXSpan()) || (y >= hull.getYSpan()) || (z >= hull.getZSpan())) {
return false;
}
while (hull.getVoxels() == null) {
if (hull.isDone()) {
return false;
}
readNextSlice(hull);
}
while (hull.getVoxels()[x] == null) {
if (hull.isDone()) {
return false;
}
readNextSlice(hull);
}
while (hull.getVoxels()[x][z] == null) {
if (hull.isDone()) {
return false;
}
readNextSlice(hull);
}
return hull.getVoxels()[x][z][y];
}
public static int getIndex(int x, int y, int z, BinvoxData hull) {
int index = x * hull.getYSpan() * hull.getXSpan() + z * hull.getYSpan() + y;
return index;
}
public static Point3i getCoords(int o, BinvoxData hull) {
Point3i p = new Point3i();
p.x = o / (hull.getYSpan() * hull.getXSpan());
p.y = o % hull.getYSpan();
p.z = (o / hull.getYSpan()) % hull.getXSpan();
return p;
}
}