use of org.twak.utils.geom.ObjDump in project chordatlas by twak.
the class MiniGen method clip.
public void clip(Loop<Point3d> in, File objLocation) {
ObjDump obj = new ObjDump();
double[] bounds = Loopz.minMaxXZ(in);
List<LinearForm3D> halfPlanes = new ArrayList();
File writeFolder = objLocation.getParentFile();
for (Pair<Point3d, Point3d> p : in.pairs()) {
Vector3d norm = new Vector3d(p.second());
norm.sub(p.first());
norm = new Vector3d(-norm.z, 0, norm.x);
norm.normalize();
halfPlanes.add(new LinearForm3D(norm, p.first()));
}
Map<File, File> copied = new HashMap<>();
int nameCount = 0;
double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
for (Map.Entry<Integer, Matrix4d> e : trans.index.entrySet()) {
if (!inBounds(e.getValue(), Collections.singletonList(bounds)))
continue;
else {
Matrix4d m = new Matrix4d();
m.mul(Jme3z.fromMatrix(trans.offset), e.getValue());
File readFolder = new File(Tweed.toWorkspace(root), e.getKey() + "");
ObjDump or = new ObjDump(new File(readFolder, "model.obj"));
or.computeMissingNormals();
for (ObjDump.Material mat : or.material2Face.keySet()) {
f: for (Face f : or.material2Face.get(mat)) {
for (int j = 0; j < f.vtIndexes.size(); j++) {
Point3d pt = new Point3d(or.orderVert.get(f.vtIndexes.get(j)));
m.transform(pt);
if (pt.x > bounds[0] && pt.x < bounds[1] && pt.z > bounds[2] && pt.z < bounds[3])
if (inside(pt, halfPlanes)) {
if (IMPORT_TEXTURES && !((obj.currentMaterial != null && obj.currentMaterial.equals(mat)) || (obj.currentMaterial == null && mat == null))) {
File source = new File(readFolder, mat.filename);
ObjDump.Material newMat;
if (copied.containsKey(source)) {
newMat = new ObjDump.Material(mat);
newMat.filename = copied.get(source).getName();
} else {
newMat = makeUnique(mat, writeFolder);
File destFile = new File(writeFolder, newMat.filename);
copied.put(source, destFile);
try {
Files.copy(source.toPath(), new FileOutputStream(destFile));
} catch (IOException e1) {
e1.printStackTrace();
}
}
newMat.diffuse = new double[] { 0, 0, 0 };
newMat.ambient = new double[] { 1, 1, 1 };
newMat.specular = new double[] { 0, 0, 0 };
newMat.name = "mat_" + (nameCount++);
obj.setCurrentMaterial(newMat);
}
List<Point3d> fVerts = new ArrayList<>(3), fNorms = new ArrayList<>(3);
List<Point2d> fUVs = new ArrayList<>(2);
for (int i = 0; i < f.vtIndexes.size(); i++) {
Point3d vts = new Point3d(or.orderVert.get(f.vtIndexes.get(i)));
Point3d ns = new Point3d(or.orderNorm.get(f.normIndexes.get(i)));
ns.add(vts);
m.transform(vts);
m.transform(ns);
ns.sub(vts);
minY = Math.min(vts.y, minY);
maxY = Math.max(vts.y, maxY);
fVerts.add(vts);
fNorms.add(ns);
fUVs.add(new Point2d(or.orderUV.get(f.uvIndexes.get(i))));
}
obj.addFace(fVerts, fNorms, fUVs);
continue f;
}
}
}
}
}
}
for (Tuple3d t : obj.orderVert) t.y -= (maxY - minY) * 0.03 + minY;
obj.dump(objLocation);
}
use of org.twak.utils.geom.ObjDump in project chordatlas by twak.
the class MiniTransform method convertToMini.
public static void convertToMini(Iterable<File> bigObj, File outfile, Matrix4d transform) {
outfile.mkdirs();
// .iterator().next() );
ObjDump src = new ObjDump(bigObj);
src.centerVerts();
src.transform(transform);
long count = src.material2Face.entrySet().stream().mapToInt(x -> x.getValue().size()).sum();
double[] bounds = src.orderVert.stream().collect(new InaxPoint3dCollector());
long targetCount = 5000;
double volume = (bounds[1] - bounds[0]) * (bounds[3] - bounds[2]) * (bounds[5] - bounds[4]);
double edgeLength = Math.pow(volume / (count / targetCount), 0.3333);
int xc = (int) Math.ceil((bounds[1] - bounds[0]) / edgeLength), yc = (int) Math.ceil((bounds[3] - bounds[2]) / edgeLength), zc = (int) Math.ceil((bounds[5] - bounds[4]) / edgeLength);
Set<Face>[][][] faces = new Set[xc][yc][zc];
for (Entry<Material, List<Face>> e : src.material2Face.entrySet()) for (Face f : e.getValue()) {
Tuple3d vt = src.orderVert.get(f.vtIndexes.get(0));
int ix = (int) ((vt.x - bounds[0]) / edgeLength);
int iy = (int) ((vt.y - bounds[2]) / edgeLength);
int iz = (int) ((vt.z - bounds[4]) / edgeLength);
if (faces[ix][iy][iz] == null)
faces[ix][iy][iz] = new HashSet();
faces[ix][iy][iz].add(f);
}
int dir = 0;
MiniTransform mt = new MiniTransform();
for (int x = 0; x < xc; x++) for (int y = 0; y < yc; y++) for (int z = 0; z < zc; z++) {
Set<Face> miniF = faces[x][y][z];
if (miniF == null)
continue;
Matrix4d trans = new Matrix4d();
trans.setIdentity();
trans.setScale(edgeLength / 255);
trans.setTranslation(new Vector3d(x * edgeLength + bounds[0], y * edgeLength + bounds[2], z * edgeLength + bounds[4]));
Matrix4d pack = new Matrix4d(trans);
pack.invert();
ObjDump mini = new ObjDump();
miniF.stream().forEach(f -> mini.addFaceFrom(f, src));
mini.transform(pack);
mini.dump(new File(new File(outfile, "" + dir), OBJ));
mt.index.put(dir, trans);
dir++;
}
try {
new XStream().toXML(mt, new FileWriter(new File(outfile, INDEX)));
} catch (IOException e1) {
e1.printStackTrace();
}
System.out.println("wrote " + count + " faces to " + dir + " meshes");
}
use of org.twak.utils.geom.ObjDump in project chordatlas by twak.
the class ReadTrace method postProcessFrames.
private void postProcessFrames() throws Throwable {
ObjDump out = new ObjDump();
// start with data most likely to be loaded
Collections.reverse(frames);
double[] tmp = new double[4];
for (Frame f : frames) {
// filter out large tiles
CountThings<Double> lengths = new CountThings<>();
for (FrameGeometry fg : f) {
for (int i = 0; i < 3; i++) {
fg.viewMatrix.getColumn(i, tmp);
lengths.count((double) Math.round(new Vector4d(tmp).length() * 20) / 20);
}
}
// we assume the most popular tile size is the smallest (and that they've loaded)
double targetLength = lengths.getMax().first();
double t = 0.01 / targetLength;
Matrix4d scale = new Matrix4d(t, 0, 0, 0, 0, t, 0, 0, 0, 0, t, 0, 0, 0, 0, t);
Iterator<FrameGeometry> fig = f.iterator();
fig: while (fig.hasNext()) {
FrameGeometry fg = fig.next();
for (int i = 0; i < 3; i++) {
fg.viewMatrix.getColumn(i, tmp);
if (new Vector4d(tmp).lengthSquared() > (targetLength * targetLength * 1.01)) {
// remove non-smallest tile size
fig.remove();
continue fig;
}
}
fg.viewMatrix.mul(scale);
// y-up!
swapRows(fg.viewMatrix, 0, 1);
swapRows(fg.viewMatrix, 0, 2);
fg.viewMatrix.m33 = 1;
}
}
Matrix4d frameTransform = new Matrix4d();
frameTransform.setIdentity();
Map<String, Matrix4d> knownVerts = new HashMap<>();
Iterator<Frame> fit = frames.iterator();
while (fit.hasNext()) if (fit.next().size() < 50) {
System.out.println("warning: removing small frame");
fit.remove();
}
fit = frames.iterator();
while (fit.hasNext()) {
Frame f = fit.next();
Set<Matrix4d> mats = new HashSet<>();
for (FrameGeometry fg : f) {
if (knownVerts.containsKey(fg.loc)) {
Matrix4d toFrame = new Matrix4d(fg.viewMatrix);
toFrame.invert();
toFrame.mul(knownVerts.get(fg.loc));
mats.add(toFrame);
// frameTransform = new Matrix4d( fg.viewMatrix );
// frameTransform.invert();
// frameTransform.mul( knownVerts.get( fg.loc ) );
//
// break;
// frameTransform = toFrame;
}
}
if (!mats.isEmpty())
frameTransform = average(mats);
if (frameTransform != null) {
// apply found transform to all within same frame
Iterator<FrameGeometry> fig = f.iterator();
while (fig.hasNext()) {
FrameGeometry fg = fig.next();
if (knownVerts.containsKey(fg.loc)) {
fig.remove();
} else {
fg.viewMatrix.mul(frameTransform);
knownVerts.put(fg.loc, fg.viewMatrix);
}
}
} else {
System.out.println("failed to find origin for frame");
fit.remove();
}
frameTransform = null;
}
int count = 0, c2 = 0;
MiniTransform miniTransform = new MiniTransform();
for (Frame f : frames) {
// write out the frames into a single file
System.out.println("post-processing " + count++ + "/" + frames.size());
for (FrameGeometry fg : f) {
File miniFrameFolder = null;
File outFolder = folder;
Matrix4d meshTransform = fg.viewMatrix;
if (miniMesh != null) {
// if we're writing out in the miniMesh format
out = new ObjDump();
miniFrameFolder = new File(miniMesh, c2 + "");
miniFrameFolder.mkdirs();
miniTransform.index.put(c2, fg.viewMatrix);
outFolder = miniFrameFolder;
meshTransform = new Matrix4d();
meshTransform.setIdentity();
}
int[] ind = BitTwiddle.byteToUShort(getBytes(fg.ind));
if (ind == null) {
System.out.println("*** missing index buffer " + fg.ind);
continue;
}
byte[] vtLocBytes = getBytes(fg.loc);
if (vtLocBytes == null)
continue;
int[][] vtLoc = BitTwiddle.byteToUByte(vtLocBytes, 4, (int) fg.iVals[VERTEX_BUFFER_OFFSET], (int) fg.iVals[VERTEX_BUFFER_STRIDE]);
byte[] uvBytes = Files.readAllBytes(new File(folder, fg.uvs).toPath());
int[][] uvLoc = BitTwiddle.byteToUShort(uvBytes, 2, (int) fg.iVals[UV_BUFFER_OFFSET], (int) fg.iVals[UV_BUFFER_STRIDE]);
out.setCurrentTexture("" + c2++, 512, 512);
Tex tex = fg.tex;
File texFile = null;
if (tex != null && (texFile = new File(folder, tex.filename)).exists()) {
File mat = DTX1.toPNG(Files.readAllBytes(texFile.toPath()), outFolder, tex.width, tex.height);
out.setCurrentTexture(mat.getName(), tex.width, tex.height);
} else {
out.setCurrentTexture("missing_" + c2, 1, 1);
System.err.println("missing texture!" + c2);
}
for (int i = 2; i < fg.iVals[TRI_COUNT]; i++) {
// GL_TRIANGLES
int a = ind[i], b = ind[i - (i % 2 == 0 ? 2 : 1)], c = ind[i - (i % 2 == 0 ? 1 : 2)];
if (a != b && b != c && c != a && // google viewer magically hides some verts
isMagic8Visible(fg.magic8, vtLoc[a][3]) && isMagic8Visible(fg.magic8, vtLoc[b][3]) && isMagic8Visible(fg.magic8, vtLoc[c][3]) && // removes the tabs from the edges and centers of tiles (much easier before transform)
!isTab(vtLoc[a], vtLoc[b], vtLoc[c])) {
float[][] pos = new float[][] { locTrans(vtLoc[a], meshTransform), locTrans(vtLoc[b], meshTransform), locTrans(vtLoc[c], meshTransform) };
out.addFace(pos, new float[][] { uvMunge(uvLoc[a][0], uvLoc[a][1], tex, fg.fVals), uvMunge(uvLoc[b][0], uvLoc[b][1], tex, fg.fVals), uvMunge(uvLoc[c][0], uvLoc[c][1], tex, fg.fVals) }, findNormals(pos));
}
}
if (miniMesh != null) {
// out.writeMtlFile = false;
out.dump(new File(miniFrameFolder, "model.obj"));
}
}
}
if (miniMesh == null)
out.dump(new File(folder, "model.obj"));
else {
File f = new File(miniMesh, "index.xml");
f.getParentFile().mkdirs();
new XStream().toXML(miniTransform, new FileOutputStream(f));
}
}
use of org.twak.utils.geom.ObjDump in project chordatlas by twak.
the class SkelFootprintConsole method main.
public static void main(String[] args) {
System.out.println("solving: first arg: input state. second arg: output state");
System.out.println("reconstruction: first arg: input state. outputs as obj. can be given a folder of blocks");
File f = new File(args[0]);
if (!f.exists()) {
System.out.println(args[0] + " not found");
return;
}
if (args.length >= 2) {
System.out.println("running solver");
long runTime = Long.MAX_VALUE;
if (args.length >= 3) {
runTime = Long.parseLong(args[2]);
System.out.println("for " + runTime + " seconds");
}
new SkelFootprintConsole().go((SolverState) new XStream().fromXML(f), new File(args[1]), runTime);
} else if (args.length == 1) {
// File f = new File (args[0]);
ObjDump obj = new ObjDump();
TweedFrame.HEADLESS = true;
TweedFrame tf = new TweedFrame();
SkelGen gen = new SkelGen(tf.tweed);
File out;
if (f.exists() && f.isDirectory()) {
out = new File("combined.obj");
for (File b : f.listFiles()) dump(gen, new File(b, "done.xml"), obj);
} else {
out = new File(args[0] + ".obj");
dump(gen, new File(args[1]), obj);
}
obj.dump(out);
tf.tweed.destroy();
System.exit(0);
} else {
System.out.println("takes 1 or 2 arguments");
return;
}
}
use of org.twak.utils.geom.ObjDump in project chordatlas by twak.
the class Slice method buildUI.
private void buildUI() {
ma = new PanMouseAdaptor(this);
ma.center(new Point2d((max[flatAxis[0]] - min[flatAxis[0]]) / 2 + min[flatAxis[0]], (max[flatAxis[1]] - min[flatAxis[1]]) / 2 + min[flatAxis[1]]));
ma.setZoom(16);
JFrame jf = new JFrame("slice");
WindowManager.register(jf);
jf.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// System.exit(0);
}
});
jf.setLayout(new BorderLayout());
this.setPreferredSize(new Dimension(600, 600));
jf.add(this, BorderLayout.CENTER);
final JSlider heightSlider = new JSlider(SwingConstants.VERTICAL, (int) (min[majorAxis] * sliderScale), (int) (max[majorAxis] * sliderScale), (int) ((max[majorAxis] - min[majorAxis]) * 0.5 + min[majorAxis]) * sliderScale);
final JSlider lineSlider = new JSlider(SwingConstants.VERTICAL, -1, 50, -1);
heightSlider.setPaintLabels(false);
final ChangeListener cl;
heightSlider.addChangeListener(cl = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent arg0) {
sliceHeight = heightSlider.getValue() / (double) sliderScale;
LineSoup rawSoup = sliceMesh(rawMesh, sliceHeight);
slice = sliceMesh(filteredMesh, sliceHeight);
foundLines = new FindLines(slice, gisBias, lineSlider.getValue(), rawSoup.clique(P.CL_HIGH, P.CL_LOW), P);
Concarnie cc = new Concarnie(foundLines.result, foundLines.cliques, P);
// Concarnie cc = new Concarnie(slice, rawSoup.clique() );
carnieSoup = cc.graph;
carnie = cc.out;
Slice.this.repaint();
}
});
cl.stateChanged(null);
lineSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
cl.stateChanged(null);
}
});
final JSlider parameterScaleSlider = new JSlider(SwingConstants.VERTICAL, 0, 2000, (int) (P.getScale() * 100));
parameterScaleSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
P.setScale(parameterScaleSlider.getValue() / 100.);
}
});
JButton go = new JButton("support");
go.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
setupObj(new ObjRead(toSlice));
// supportPoints.clear();
// Slice.this.repaint();
// SwingUtilities.invokeLater(new Runnable() {
// @Override
// public void run() {
// PerpSupport ps = new PerpSupport(slice, gis);
// Slice.this.supportMax = ps.supportMax;
// Slice.this.supportPoints = ps.supportPoints;
// Slice.this.repaint();
// }
// });
}
});
JButton fs = new JButton("full support");
fs.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Set<Point2d> points = new HashSet();
for (double v = min[majorAxis]; v < max[majorAxis]; v += 0.01) {
System.out.println("processing slice " + v + " / " + max[majorAxis]);
slice = sliceMesh(filteredMesh, sliceHeight);
for (SupportPoint sp : new PerpSupport(slice, gis).supportPoints) points.add(new Point2d(sp.support, max[majorAxis] - v));
}
System.out.println("found " + points.size() + " pts");
new Plot(points);
}
});
JButton ge = new JButton("lines");
ge.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
foundLines = new FindLines(slice, gisBias, lineSlider.getValue(), slice.clique(P.CL_HIGH, P.CL_LOW), P);
Slice.this.repaint();
}
});
JButton cc = new JButton("carnie");
cc.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
LineSoup rawSoup = sliceMesh(rawMesh, sliceHeight);
foundLines = new FindLines(slice, gisBias, lineSlider.getValue(), rawSoup.clique(P.CL_HIGH, P.CL_LOW), P);
Concarnie cc = new Concarnie(foundLines.result, foundLines.cliques, P);
carnieSoup = cc.graph;
carnie = cc.out;
Slice.this.repaint();
}
});
JButton ob = new JButton("dump");
ob.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
ObjDump out = new ObjDump();
double delta = 2.5;
for (double d = min[majorAxis]; d <= max[majorAxis]; d += delta) {
System.out.println(d + " / " + max[majorAxis]);
boolean error;
int count = 0;
do {
error = false;
try {
LineSoup rawSoup = sliceMesh(rawMesh, d);
slice = sliceMesh(filteredMesh, d);
foundLines = new FindLines(slice, null, lineSlider.getValue(), rawSoup.clique(P.CL_HIGH, P.CL_LOW), P);
// foundLines = new FindLines(slice, gisBias, lineSlider.getValue(), rawSoup.clique(P.CL_HIGH, P.CL_LOW), P);
Concarnie c = new Concarnie(foundLines.result, foundLines.cliques, P);
//
extrude(out, c.out, d, d + delta);
// capAtHeight (out, c.out, false, d );
capAtHeight(out, c.out, true, d + delta);
} catch (Throwable th) {
error = true;
}
} while (error && count++ < 10);
}
out.dump(new File(Tweed.SCRATCH + "test2.obj"));
}
});
JButton an = new JButton("anim");
an.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
BufferedImage bi = new BufferedImage(Slice.this.getWidth(), Slice.this.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
for (File f : new File(Tweed.SCRATCH + "vid").listFiles()) f.delete();
int c = 0;
for (int i = heightSlider.getMinimum(); i < heightSlider.getMaximum(); i += 100) {
heightSlider.setValue(i);
Slice.this.paintComponent(bi.getGraphics());
try {
ImageIO.write(bi, "png", new File(String.format(Tweed.SCRATCH + "vid/%05d.png", c++)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
JButton sl = new JButton("global");
sl.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SliceSolver(new File(Tweed.SCRATCH + "test2.obj"), Slice.this, P);
}
});
JButton pr = new JButton("profiles");
pr.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
double delta = 0.5;
BufferedImage out = new BufferedImage(Slice.this.getWidth(), Slice.this.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2 = (Graphics2D) out.getGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(0, 0, 0, 50));
for (double d = min[majorAxis]; d <= max[majorAxis]; d += delta) {
System.out.println(d + " / " + max[majorAxis]);
boolean error;
int count = 0;
do {
error = false;
try {
LineSoup rawSoup = sliceMesh(rawMesh, d);
slice = sliceMesh(filteredMesh, d);
FindLines fl = new FindLines(slice, gisBias, lineSlider.getValue(), rawSoup.clique(P.CL_HIGH, P.CL_LOW), P);
PaintThing.paint(fl.result, g2, ma);
// Concarnie c = new Concarnie(foundLines.result, foundLines.cliques, P);
} catch (Throwable th) {
error = true;
}
} while (error && count++ < 10);
}
g2.dispose();
try {
ImageIO.write(out, "png", new File(Tweed.SCRATCH + "lines"));
} catch (IOException e) {
e.printStackTrace();
}
}
});
final JCheckBox sg = new JCheckBox("gis", true);
sg.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showGis = sg.isSelected();
Slice.this.repaint();
}
});
final JCheckBox sf = new JCheckBox("fit", true);
sf.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showFit = sf.isSelected();
Slice.this.repaint();
}
});
final JCheckBox sc = new JCheckBox("poly-out", true);
sc.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showCarnie = sc.isSelected();
Slice.this.repaint();
}
});
final JCheckBox ss = new JCheckBox("slice", true);
ss.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showSlice = ss.isSelected();
Slice.this.repaint();
}
});
JPanel buttons = new JPanel(new ListDownLayout());
buttons.add(go);
buttons.add(fs);
buttons.add(ge);
buttons.add(cc);
buttons.add(an);
buttons.add(ob);
buttons.add(sl);
buttons.add(pr);
buttons.add(sg);
buttons.add(sf);
buttons.add(sc);
buttons.add(ss);
buttons.add(lineSlider);
JPanel controls = new JPanel(new BorderLayout());
controls.add(heightSlider, BorderLayout.CENTER);
controls.add(parameterScaleSlider, BorderLayout.WEST);
controls.add(buttons, BorderLayout.EAST);
jf.add(controls, BorderLayout.EAST);
jf.pack();
jf.setVisible(true);
}
Aggregations