$search
00001 /******************************************************************************* 00002 * Copyright (c) 2012 Stefan Profanter. All rights reserved. This program and the accompanying 00003 * materials are made available under the terms of the GNU Public License v3.0 which accompanies 00004 * this distribution, and is available at http://www.gnu.org/licenses/gpl.html 00005 * 00006 * Contributors: Stefan Profanter - initial API and implementation, Year: 2012 00007 ******************************************************************************/ 00008 package edu.tum.cs.vis.model; 00009 00010 import java.awt.Color; 00011 import java.io.BufferedWriter; 00012 import java.io.File; 00013 import java.io.FileWriter; 00014 import java.io.IOException; 00015 import java.text.DecimalFormat; 00016 import java.text.NumberFormat; 00017 import java.util.ArrayList; 00018 import java.util.Collections; 00019 import java.util.HashSet; 00020 import java.util.LinkedList; 00021 import java.util.List; 00022 import java.util.Set; 00023 import java.util.concurrent.Callable; 00024 00025 import javax.vecmath.Point3f; 00026 import javax.vecmath.Tuple3f; 00027 import javax.vecmath.Vector3f; 00028 00029 import org.apache.log4j.Logger; 00030 00031 import processing.core.PGraphics; 00032 import edu.tum.cs.ias.knowrob.utils.ThreadPool; 00033 import edu.tum.cs.vis.model.util.BSphere; 00034 import edu.tum.cs.vis.model.util.Group; 00035 import edu.tum.cs.vis.model.util.Line; 00036 import edu.tum.cs.vis.model.util.Triangle; 00037 import edu.tum.cs.vis.model.util.Vertex; 00038 00048 public class Model { 00049 00053 private static Logger logger = Logger.getLogger(Model.class); 00054 00058 private String textureBasePath = null; 00059 00063 private Group group; 00064 00068 private final ArrayList<Vertex> vertices = new ArrayList<Vertex>(); 00069 00073 final ArrayList<Triangle> triangles = new ArrayList<Triangle>(); 00074 00078 private final ArrayList<Line> lines = new ArrayList<Line>(); 00079 00085 private BSphere boundingSphere = null; 00086 00091 private float scale = 1; 00092 00096 private boolean normalsInitialized = false; 00097 00105 public void draw(PGraphics g, Color overrideColor) { 00106 if (group != null) 00107 group.draw(g, overrideColor); 00108 } 00109 00117 public BSphere getBoundingSphere() { 00118 return boundingSphere; 00119 } 00120 00126 public Group getGroup() { 00127 return group; 00128 } 00129 00135 public ArrayList<Line> getLines() { 00136 return lines; 00137 } 00138 00145 public float getScale() { 00146 return scale; 00147 } 00148 00154 public String getTextureBasePath() { 00155 return textureBasePath; 00156 } 00157 00163 public ArrayList<Triangle> getTriangles() { 00164 return triangles; 00165 } 00166 00174 public float getUnscaled(float scaled) { 00175 return scaled * 1f / scale; 00176 } 00177 00186 public Tuple3f[] getUnscaled(Point3f[] corner) { 00187 Tuple3f[] ret = new Point3f[corner.length]; 00188 for (int i = 0; i < corner.length; i++) { 00189 ret[i] = getUnscaled(corner[i]); 00190 } 00191 return ret; 00192 } 00193 00201 public Tuple3f getUnscaled(Tuple3f t) { 00202 Tuple3f tr = new Point3f(t); 00203 tr.scale(1f / scale); 00204 return tr; 00205 } 00206 00214 public Vector3f getUnscaled(Vector3f t) { 00215 Vector3f tr = new Vector3f(t); 00216 tr.scale(1f / scale); 00217 return tr; 00218 } 00219 00225 public ArrayList<Vertex> getVertices() { 00226 return vertices; 00227 } 00228 00232 public void mirrorX() { 00233 for (Vertex v : vertices) { 00234 v.x *= (-1); 00235 } 00236 group.resetMinMaxValues(); 00237 00238 } 00239 00244 public void normalize() { 00245 float x = group.getMaxX() - group.getMinX(); 00246 float y = group.getMaxY() - group.getMinY(); 00247 float z = group.getMaxZ() - group.getMinZ(); 00248 00249 float max = Math.max(x, Math.max(y, z)); 00250 00251 scale(1f / max); 00252 00253 scale = 1f / max; 00254 } 00255 00263 public void scale(float factor) { 00264 00265 for (Vertex v : vertices) { 00266 v.scale(factor); 00267 } 00268 for (Triangle t : triangles) 00269 t.updateCentroid(); 00270 for (Line l : lines) 00271 l.updateCentroid(); 00272 group.resetMinMaxValues(); 00273 } 00274 00283 public void setBoundingSphere(BSphere boundingSphere) { 00284 this.boundingSphere = boundingSphere; 00285 } 00286 00293 public void setGroup(Group group) { 00294 this.group = group; 00295 } 00296 00303 public void setTextureBasePath(String textureBasePath) { 00304 this.textureBasePath = textureBasePath; 00305 } 00306 00311 public void removeDoubleSidedTriangles() { 00312 for (int i = 0; i < triangles.size(); i++) { 00313 Triangle t1 = triangles.get(i); 00314 for (int j = i + 1; j < triangles.size(); j++) { 00315 Triangle t2 = triangles.get(j); 00316 int eqCnt = 0; 00317 for (int k = 0; k < 3; k++) { 00318 Point3f p1 = t1.getPosition()[k]; 00319 for (Point3f p2 : t2.getPosition()) { 00320 if (p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) { 00321 eqCnt++; 00322 break; 00323 } 00324 } 00325 if (eqCnt != k + 1) { 00326 // break if not enough vertices are equal 00327 break; 00328 } 00329 } 00330 if (eqCnt == 3) { 00331 // triangles are the same, so remove j 00332 triangles.remove(j); 00333 this.group.removeTriangle(t2); 00334 j--; 00335 } 00336 } 00337 00338 } 00339 } 00340 00346 public void updateVertexSharing() { 00347 00348 final Set<Triangle> checkedTriangles = Collections.synchronizedSet(new HashSet<Triangle>()); 00349 00350 for (Triangle t : triangles) 00351 updateVertexSharingForTriangle(t, checkedTriangles); 00352 } 00353 00363 private void updateVertexSharingForTriangle(final Triangle t, 00364 final Set<Triangle> checkedTriangles) { 00365 synchronized (t) { 00366 if (checkedTriangles.contains(t)) 00367 return; 00368 HashSet<Triangle> neighborsToRemove = new HashSet<Triangle>(); 00369 synchronized (t.getNeighbors()) { 00370 for (Triangle n : t.getNeighbors()) { 00371 synchronized (n) { 00372 if (checkedTriangles.contains(n)) 00373 continue; 00374 00375 double angle = t.getDihedralAngle(n); 00376 00377 // Share vertices if angle is < 30 degree 00378 boolean share = (angle < 30 / 180.0 * Math.PI); 00379 00380 for (Vertex vt : t.getPosition()) { 00381 for (int i = 0; i < n.getPosition().length; i++) { 00382 Vertex vn = n.getPosition()[i]; 00383 if (share && vn != vt && vt.sameCoordinates(vn)) { 00384 // merge vertices 00385 n.getPosition()[i] = vt; 00386 int cnt = 0; 00387 for (Triangle tmpTri : t.getNeighbors()) { 00388 for (Vertex tmpVer : tmpTri.getPosition()) 00389 if (tmpVer == vn) { 00390 cnt++; 00391 break; 00392 } 00393 } 00394 if (cnt == 0) { 00395 for (Triangle tmpTri : n.getNeighbors()) { 00396 if (tmpTri == t) 00397 continue; 00398 for (Vertex tmpVer : tmpTri.getPosition()) 00399 if (tmpVer == vn) { 00400 cnt++; 00401 break; 00402 } 00403 } 00404 } 00405 if (cnt == 0) { 00406 synchronized (vertices) { 00407 vertices.remove(vn); 00408 } 00409 } 00410 } else if (!share && vn == vt) { 00411 // split vertices 00412 Vertex clone = (Vertex) vt.clone(); 00413 synchronized (vertices) { 00414 vertices.add(clone); 00415 } 00416 n.getPosition()[i] = clone; 00417 if (!t.isAdjacentNeighbor(n)) { 00418 neighborsToRemove.add(n); 00419 } 00420 } 00421 } 00422 } 00423 } 00424 } 00425 00426 } 00427 checkedTriangles.add(t); 00428 } 00429 } 00430 00443 public void updateVertexNormals() { 00444 // Compute from faces 00445 00446 List<Callable<Void>> threads = new LinkedList<Callable<Void>>(); 00447 00448 final int interval = 500; 00449 00450 // Reset normal vectors, because vertex normals from collada aren't correct 00451 for (Vertex v : vertices) { 00452 v.getNormalVector().x = v.getNormalVector().y = v.getNormalVector().z = 0; 00453 } 00454 00455 for (int start = 0; start < triangles.size(); start += interval) { 00456 final int st = start; 00457 threads.add(new Callable<Void>() { 00458 00459 @Override 00460 public Void call() throws Exception { 00461 int end = Math.min(st + interval, triangles.size()); 00462 for (int i = st; i < end; i++) { 00463 calculateVertexNormalsForTriangle(triangles.get(i)); 00464 } 00465 return null; 00466 } 00467 00468 }); 00469 }; 00470 00471 ThreadPool.executeInPool(threads); 00472 00473 // Normalize all vectors. 00474 // Additionally search the vectors which have max x, max y and max z coordinates (needed for 00475 // vertex winding check / inverted normals check) 00476 00477 Vertex extrema[] = new Vertex[6]; 00478 00479 for (Vertex v : vertices) { 00480 v.getNormalVector().normalize(); 00481 float coord[] = new float[3]; 00482 v.get(coord); 00483 // Set all max values 00484 for (int i = 0; i < 3; i++) { 00485 float extCoord[] = new float[3]; 00486 if (extrema[i] != null) 00487 extrema[i].get(extCoord); 00488 if (extrema[i] == null || extCoord[i] < coord[i]) 00489 extrema[i] = v; 00490 } 00491 // Set all min values 00492 for (int i = 0; i < 3; i++) { 00493 float extCoord[] = new float[3]; 00494 if (extrema[i + 3] != null) 00495 extrema[i + 3].get(extCoord); 00496 if (extrema[i + 3] == null || extCoord[i] > coord[i]) 00497 extrema[i + 3] = v; 00498 } 00499 } 00500 00501 int vote = 0; 00502 // Now the vertex normal of maxX must point in approximately the same direction as the 00503 // axis. If the angle between the vertex normal and the direction is smaller than 90° vote 00504 // for inversion. 00505 for (int i = 0; i < 6; i++) { 00506 float coord[] = { 0, 0, 0 }; 00507 coord[i % 3] = i < 3 ? 1 : -1; 00508 double angle = Math.acos(extrema[i].getNormalVector().dot((new Vector3f(coord)))); 00509 System.out.println("Angle: " + (angle * 180 / Math.PI)); 00510 if (angle < Math.PI / 2) 00511 vote--; 00512 else 00513 vote++; 00514 } 00515 System.out.println("VOTE: " + vote); 00516 if (vote > 0) { 00517 // They voted for inverting 00518 for (Vertex v : vertices) { 00519 v.getNormalVector().scale(-1); 00520 } 00521 } 00522 00523 normalsInitialized = true; 00524 00525 } 00526 00533 static void calculateVertexNormalsForTriangle(final Triangle t) { 00534 Vertex p0 = t.getPosition()[0]; 00535 Vertex p1 = t.getPosition()[1]; 00536 Vertex p2 = t.getPosition()[2]; 00537 00538 // get vectors from p0 to p1 and so on 00539 Vector3f a = new Vector3f(p0); 00540 a.sub(p2); 00541 Vector3f b = new Vector3f(p1); 00542 b.sub(p0); 00543 Vector3f c = new Vector3f(p2); 00544 c.sub(p1); 00545 // length of these vectors 00546 float l2a = a.lengthSquared(), l2b = b.lengthSquared(), l2c = c.lengthSquared(); 00547 if (l2a == 0 || l2b == 0 || l2c == 0) 00548 return; 00549 00550 Vector3f facenormal = new Vector3f(); 00551 facenormal.cross(a, b); // unscaled normal 00552 00553 Vector3f normalP0 = (Vector3f) facenormal.clone(); 00554 normalP0.scale(1.0f / (l2a * l2c)); 00555 synchronized (p0.getNormalVector()) { 00556 p0.getNormalVector().add(normalP0); 00557 } 00558 00559 Vector3f normalP1 = (Vector3f) facenormal.clone(); 00560 normalP1.scale(1.0f / (l2b * l2a)); 00561 synchronized (p1.getNormalVector()) { 00562 p1.getNormalVector().add(normalP1); 00563 } 00564 00565 Vector3f normalP2 = (Vector3f) facenormal.clone(); 00566 normalP2.scale(1.0f / (l2c * l2b)); 00567 synchronized (p2.getNormalVector()) { 00568 p2.getNormalVector().add(normalP2); 00569 } 00570 } 00571 00588 public File exportVerticesAsTxt() { 00589 try { 00590 File tmp = File.createTempFile("knowrob_", ".txt", 00591 new File(System.getProperty("java.io.tmpdir"))); 00592 exportVerticesAsTxt(tmp, true); 00593 return tmp; 00594 } catch (IOException e) { 00595 logger.error("Couldn't create temp file name: " + e.getMessage()); 00596 return null; 00597 } 00598 00599 } 00600 00619 public boolean exportVerticesAsTxt(File path, boolean overwrite) { 00620 00621 try { 00622 if (!overwrite && path.exists()) { 00623 logger.error("Couldn't export model to file. Already exists: " 00624 + path.getAbsolutePath()); 00625 return false; 00626 } 00627 FileWriter fstream = new FileWriter(path); 00628 BufferedWriter out = new BufferedWriter(fstream); 00629 out.write(vertices.size() + "\n"); 00630 NumberFormat df = new DecimalFormat("0.####################"); 00631 for (int i = 0; i < vertices.size(); i++) { 00632 Vertex v = vertices.get(i); 00633 out.write(df.format(v.x) + "\t" + df.format(v.y) + "\t" + df.format(v.z)); 00634 if (normalsInitialized) { 00635 out.write("\t" + df.format(v.getNormalVector().x) + "\t" 00636 + df.format(v.getNormalVector().y) + "\t" 00637 + df.format(v.getNormalVector().z)); 00638 } 00639 if (i < vertices.size() - 1) 00640 out.write("\n"); 00641 } 00642 logger.info("Model exported to file " + path.getAbsolutePath()); 00643 out.close(); 00644 return true; 00645 } catch (IOException e) { 00646 logger.error("Couldn't export model to file " + path.getAbsolutePath() + ". " 00647 + e.getMessage()); 00648 } 00649 return false; 00650 } 00651 00652 }