/* * This file is part of MoleculeViewer. * * MoleculeViewer is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MoleculeViewer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with MoleculeViewer. If not, see <http://www.gnu.org/licenses/>. */ package astex; /** * Tmesh.java - Mike's Tmesh class. * * Object for storing a triangular mesh object. */ import java.util.*; public class Tmesh { /** The number of points in the object. */ public int np = 0; /** The number of points allocated for the object. */ private int npalloc = 0; /** The x coordinates of the points. */ public float x[] = null; /** The y coordinates of the points. */ public float y[] = null; /** The z coordinates of the points. */ public float z[] = null; /** The normal vectors of the points. */ public float nx[] = null; /** The normal vectors of the points. */ public float ny[] = null; /** The normal vectors of the points. */ public float nz[] = null; /** The texture coordinates of the points. */ public float u[] = null; /** The texture coordinates of the points. */ public float v[] = null; /** The color the points. */ public int vcolor[] = null; /** The texture map for this object. */ Texture texture = null; /** The transparency of the object. */ public int transparency = 0xff; /** The offset for the u texture coord. */ private float uoffset = 0.0f; /** The scale for the u texture coord. */ private float uscale = 1.0f; /** The offset for the v texture coord. */ private float voffset = 0.0f; /** The scale for the v texture coord. */ private float vscale = 1.0f; public static final int UScale = 1; public static final int VScale = 2; public static final int UOffset = 3; public static final int VOffset = 4; public static final int UTexture = 1; public static final int VTexture = 2; /** Sphere graphical object. */ public Tmesh spheres = null; /** Cylinder graphical object. */ public Tmesh cylinders = null; /** Sphere graphical object. */ public Tmesh lines = null; /** The number of triangles in the object. */ public int nt = 0; /** The number of triangles allocated. */ private int ntalloc = 0; /** The indices of the points in the triangles. */ public int t0[] = null; /** The indices of the points in the triangles. */ public int t1[] = null; /** The indices of the points in the triangles. */ public int t2[] = null; /** The indices of the points in the triangles. */ public int tcolor[] = null; /** The number of points we will grow a tmesh by. */ private static final int PointAllocationIncrement = 4096; /** The number of triangles we will grow a tmesh by. */ private static final int TriangleAllocationIncrement = 8192; public enum Style { DOTS, LINES, TRIANGLES, CYLINDERS, SPHERES } /** Renderering Style. */ public Style style = Style.TRIANGLES; public enum ColorStyle { ObjectColor, TriangleColor, VertexColor } /** Color style. */ public ColorStyle colorStyle = ColorStyle.ObjectColor; /** Debugging on? */ private boolean debug = false; /** Set the uscale. */ public void setUScale(double f){ uscale = (float)f; } /** Set the vscale. */ public void setVScale(double f){ vscale = (float)f; } /** Set the uoffset. */ public void setUOffset(double f){ uoffset = (float)f; } /** Set the voffset. */ public void setVOffset(double f){ voffset = (float)f; } /** Get the uscale. */ public double getUScale(){ return uscale; } /** Get the vscale. */ public double getVScale(){ return vscale; } /** Get the uoffset. */ public double getUOffset(){ return uoffset; } /** Get the voffset. */ public double getVOffset(){ return voffset; } /** Empty an object. */ public void empty(){ np = 0; nt = 0; } /** Get inverse texture coordinate. */ public double getInverseTexture(int uv, double val){ double inv = 0.0; if(uv == UTexture){ inv = uoffset + (val/uscale); }else if(uv == VTexture){ inv = voffset + (val/vscale); }else{ Log.error("texture coordinate must be 1 or 2, not "+ uv); } return inv; } /** Set the texture range. */ public void setTextureRange(int uv, double min, double max){ // protect against equal min max values double delta = Math.abs(max - min); if(delta < 1.e-3){ delta = 1.e-3; } double scale = 1./delta; if(uv == UTexture){ setUOffset(min); setUScale(scale); }else if(uv == VTexture){ setVOffset(min); setVScale(scale); }else{ Log.error("texture coordinate must be 1 or 2, not "+ uv); } } /** Set the object color style. */ public void setColorStyle(ColorStyle s){ colorStyle = s; if(lines != null) lines.setColorStyle(s); if(spheres != null) spheres.setColorStyle(s); if(cylinders != null) cylinders.setColorStyle(s); } /** Get the object color style. */ public ColorStyle getColorStyle(){ return colorStyle; } /** Create a tmesh object. */ public Tmesh(){ } /** Is this object visible. */ private boolean visible = true; /** * Get the value of visible. * @return value of visible. */ public boolean isVisible() { return visible; } /** * Set the value of visible. * @param v Value to assign to visible. */ public void setVisible(boolean v) { this.visible = v; } /** Does this object show backfacing triangles. */ public boolean backface; /** * Set the value of backface. * @param v Value to assign to backface. */ public void setBackface(boolean v) { this.backface = v; } /** Base color of this object. */ public int color = 0x00ff00; /** * Get the value of color. * @return value of color. */ public int getColor() { return color; } /** * Set the value of color. * @param v Value to assign to color. */ public void setColor(int v) { this.color = v; // set the texture to null if we change the // color. this.texture = null; if(lines != null) lines.setColor(v); if(spheres != null) spheres.setColor(v); if(cylinders != null) cylinders.setColor(v); } /** The name of the object. */ public String name; /** * Get the value of name. * @return value of name. */ public String getName() { return name; } /** Set the transparency. */ public void setTransparency(int t){ if(t < 0){ System.out.println("setTransparency: illegal transparency "+ t); t = 0; }else if(t > 255){ System.out.println("setTransparency: illegal transparency "+ t); t = 255; } transparency = t; } /** * Set the value of name. * @param v Value to assign to name. */ public void setName(String v) { this.name = v; } /** Default line width (-1.0 indicates line of 1 pixel width). */ private double lineWidth = -1.0; /** Set the linewidth. */ public void setLineWidth(double d){ lineWidth = d; if(lines != null){ lines.setLineWidth(d); } } /** Set the linewidth. */ public double getLineWidth(){ return lineWidth; } /** Add a point to the object. */ public int addPoint(double x[], double n[], double tu, double tv){ return addPoint(x[0], x[1], x[2], n[0], n[1], n[2], tu, tv); } /** Add a point to the object. */ public int addPoint(double xp, double yp, double zp, double xn, double yn, double zn, double tu, double tv){ ensurePointCapacity(); x[np] = (float)xp; y[np] = (float)yp; z[np] = (float)zp; nx[np] = (float)xn; ny[np] = (float)yn; nz[np] = (float)zn; u[np] = (float)tu; v[np] = (float)tv; vcolor[np] = 0; return np++; } public int addPoint(double xp, double yp, double zp, int c){ ensurePointCapacity(); x[np] = (float)xp; y[np] = (float)yp; z[np] = (float)zp; vcolor[np] = c; return np++; } /** Get the vertex info for the specified point. */ public void getVertex(int v, double px[], double nxx[]){ px[0] = x[v]; px[1] = y[v]; px[2] = z[v]; if(nxx != null){ nxx[0] = nx[v]; nxx[1] = ny[v]; nxx[2] = nz[v]; } } /** Get the number of points. */ public int getnPoints(){ return np; } /** Set the initial capacity for points. */ private void setPointCapacity(int nn){ npalloc = nn; x = new float[npalloc]; y = new float[npalloc]; z = new float[npalloc]; nx = new float[npalloc]; ny = new float[npalloc]; nz = new float[npalloc]; u = new float[npalloc]; v = new float[npalloc]; vcolor = new int[npalloc]; } /** Make sure we have room for the next point. */ private void ensurePointCapacity(){ if(np == npalloc){ npalloc += PointAllocationIncrement; float newx[] = new float[npalloc]; float newy[] = new float[npalloc]; float newz[] = new float[npalloc]; float newnx[] = new float[npalloc]; float newny[] = new float[npalloc]; float newnz[] = new float[npalloc]; float newu[] = new float[npalloc]; float newv[] = new float[npalloc]; int newvcolor[] = new int[npalloc]; if(np != 0){ for(int i = 0; i < np; i++){ newx[i] = x[i]; newy[i] = y[i]; newz[i] = z[i]; newnx[i] = nx[i]; newny[i] = ny[i]; newnz[i] = nz[i]; newu[i] = u[i]; newv[i] = v[i]; newvcolor[i] = vcolor[i]; } } x = newx; y = newy; z = newz; nx = newnx; ny = newny; nz = newnz; u = newu; v = newv; vcolor = newvcolor; } } /** Add a line to the object. */ public int addLine(int i, int j, int c){ if(i == -1 || j == -1){ System.out.println("addLine i " + i + " j " + j + " faceIndex "+ c); return nt; } ensureTriangleCapacity(); t0[nt] = i; t1[nt] = j; t2[nt] = -1; tcolor[nt] = c; return nt++; } public int addTriangle(int i, int j, int k){ return addTriangle(i, j, k, 0); } public int addTriangle(int i, int j, int k, int color){ ensureTriangleCapacity(); if((i == j || i == k || j == k) || debug){ System.out.println("tmesh.addTriangle i=" + i + " j=" + j + " k=" + k); } if(i != -1 && j != -1 && k != -1){ t0[nt] = i; t1[nt] = j; t2[nt] = k; tcolor[nt] = color; return nt++; } return -1; } /** Set the initial capacity for triangles. */ private void setTriangleCapacity(int nn){ ntalloc = nn; t0 = new int[ntalloc]; t1 = new int[ntalloc]; t2 = new int[ntalloc]; tcolor = new int[ntalloc]; } /** Make sure we have room for the next triangle. */ private void ensureTriangleCapacity(){ if(nt == ntalloc){ ntalloc += TriangleAllocationIncrement; int newt0[] = new int[ntalloc]; int newt1[] = new int[ntalloc]; int newt2[] = new int[ntalloc]; int newtcolor[] = new int[ntalloc]; if(nt != 0){ for(int i = 0; i < nt; i++){ newt0[i] = t0[i]; newt1[i] = t1[i]; newt2[i] = t2[i]; newtcolor[i] = tcolor[i]; } } t0 = newt0; t1 = newt1; t2 = newt2; tcolor = newtcolor; } } /** Add a sphere to this tmesh. */ public void addSphere(double xx, double yy, double zz, double rr, int cc){ if(style != Style.SPHERES){ if(spheres == null){ spheres = new Tmesh(); spheres.style = Style.SPHERES; } spheres.addSphere(xx, yy, zz, rr, cc); }else{ ensurePointCapacity(); x[np] = (float)xx; y[np] = (float)yy; z[np] = (float)zz; vcolor[np] = cc; /** Store radius in nx. */ nx[np] = (float)rr; np++; } } /** Add a cylinder to this tmesh. */ public void addCylinder(double x1, double y1, double z1, double x2, double y2, double z2, double r, int c1, int c2){ if(style != Style.CYLINDERS){ if(cylinders == null){ cylinders = new Tmesh(); cylinders.style = Style.CYLINDERS; cylinders.colorStyle = ColorStyle.VertexColor; } cylinders.addCylinder(x1, y1, z1, x2, y2, z2, r, c1, c2); }else{ ensurePointCapacity(); x[np] = (float)x1; y[np] = (float)y1; z[np] = (float)z1; vcolor[np] = c1; /** Store radius in nx. */ nx[np] = (float)r; np++; ensurePointCapacity(); x[np] = (float)x2; y[np] = (float)y2; z[np] = (float)z2; vcolor[np] = c2; /** Store radius in nx. */ nx[np] = (float)r; np++; ensureTriangleCapacity(); t0[nt] = np - 2; t1[nt] = np - 1; nt++; } } /** Read a ply file. */ private static Tmesh readPly(FILE f){ System.out.println("reading ply file"); Tmesh tmesh = new Tmesh(); int vcount = 0; int fcount = 0; while(f.nextLine()){ int fieldCount = f.getFieldCount(); if(fieldCount == 3 && "element".equals(f.getField(0))){ if("vertex".equals(f.getField(1))){ vcount = f.getInteger(2); tmesh.setPointCapacity(vcount); System.out.println("vertex count " + vcount); }else if("face".equals(f.getField(1))){ fcount = f.getInteger(2); tmesh.setTriangleCapacity(fcount); System.out.println("face count " + fcount); } }else if(fieldCount == 1 && "end_header".equals(f.getField(0))){ System.out.println("seen end of header"); break; } } for(int i = 0; i < vcount; i++){ f.nextLine(); int fieldCount = f.getFieldCount(); float u = 0.0f, v = 0.0f; if(fieldCount == 5){ u = v = f.getFloat(4); } tmesh.addPoint((float)f.getDouble(0), (float)f.getDouble(1), (float)f.getDouble(2), 0.0, 0.0, 0.0, u, v); if(i != 0 && i % 100000 == 0){ System.out.println("vertex " + i); } } for(int i = 0; i < fcount; i++){ f.nextLine(); int fieldCount = f.getFieldCount(); if(fieldCount == 4){ tmesh.addTriangle(f.getInteger(1), f.getInteger(2), f.getInteger(3)); }else{ System.out.println("more than 4 fields on face record."); break; } if(i != 0 && i % 100000 == 0){ System.out.println("face " + i); } } double a[] = new double[3]; double b[] = new double[3]; double n[] = new double[3]; for(int i = 0;i < tmesh.nt; i++){ int ti = tmesh.t0[i]; int tj = tmesh.t1[i]; int tk = tmesh.t2[i]; a[0] = tmesh.x[ti] - tmesh.x[tj]; a[1] = tmesh.y[ti] - tmesh.y[tj]; a[2] = tmesh.z[ti] - tmesh.z[tj]; b[0] = tmesh.x[tk] - tmesh.x[tj]; b[1] = tmesh.y[tk] - tmesh.y[tj]; b[2] = tmesh.z[tk] - tmesh.z[tj]; Point3d.cross(n, b, a); tmesh.nx[ti] += n[0]; tmesh.ny[ti] += n[1]; tmesh.nz[ti] += n[2]; tmesh.nx[tj] += n[0]; tmesh.ny[tj] += n[1]; tmesh.nz[tj] += n[2]; tmesh.nx[tk] += n[0]; tmesh.ny[tk] += n[1]; tmesh.nz[tk] += n[2]; } for(int i = 0;i < tmesh.np; i++){ double dx = tmesh.nx[i]; double dy = tmesh.ny[i]; double dz = tmesh.nz[i]; double len = Math.sqrt(dx*dx+dy*dy+dz*dz); tmesh.nx[i] /= len; tmesh.ny[i] /= len; tmesh.nz[i] /= len; } return tmesh; } /** Create a tmesh from a file. */ public static Tmesh read(String file){ FILE f = FILE.open(file); if(f == null){ FILE.getException().printStackTrace(); return null; } if(file.indexOf(".ply") != -1){ return readPly(f); } f.nextLine(); Tmesh tmesh = new Tmesh(); tmesh.name = file; int nv = f.readIntegerFromField(0); tmesh.setPointCapacity(nv); for(int i = 0; i < nv; i++){ f.nextLine(); double vx = f.readDoubleFromField(0); double vy = f.readDoubleFromField(1); double vz = f.readDoubleFromField(2); double nx = f.readDoubleFromField(3); double ny = f.readDoubleFromField(4); double nz = f.readDoubleFromField(5); // field 6 is the colour index double u = f.readDoubleFromField(7); double v = f.readDoubleFromField(8); tmesh.addPoint(vx, vy, vz, nx, ny, nz, u, v); } f.nextLine(); int ntm = f.readIntegerFromField(0); // guess number of triangles as about 2*nv tmesh.setTriangleCapacity(2 * nv); System.out.println("number of tmeshes " + ntm); int vertices[] = new int[100]; int tc = 0; for(int i = 0; i < ntm; i++){ f.nextLine(); int np = f.readIntegerFromField(0); if(np > vertices.length){ vertices = new int[np]; } tc = 0; if(f.getFieldCount() > 1){ tc = f.readIntegerFromField(1); } for(int j = 0; j < np; j++){ f.nextLine(); vertices[j] = f.readIntegerFromField(0); } for(int j = 0; j < np - 2; j++){ tmesh.addTriangle(vertices[j],vertices[j+1],vertices[j+2], tc); } } f.close(); return tmesh; } /** Distance between vertex and point. */ public double distance(int v0, double p[]){ double dx = x[v0] - p[0]; double dy = y[v0] - p[1]; double dz = z[v0] - p[2]; return Math.sqrt(dx*dx + dy*dy + dz*dz); } /** Prints the tmesh to a file. */ public void output(String filename){ int i; FILE output = FILE.write(filename); if(output == null){ System.err.println("tmesh.output() couldn't open " + filename); return; } output.print("%d\n", np); for(i = 0; i < np; i++){ output.print(" %.3f", x[i]); output.print(" %.3f", y[i]); output.print(" %.3f", z[i]); output.print(" %.3f", nx[i]); output.print(" %.3f", ny[i]); output.print(" %.3f", nz[i]); output.print(" 0"); if(u != null){ output.print(" %.3f", u[i]); }else{ output.print(" 0"); } if(v != null){ output.print(" %.3f", v[i]); }else{ output.print(" 0"); } output.println(""); } output.print("%d\n", nt); for(i = 0; i < nt; i++){ output.println("3"); output.print("%d\n", t0[i]); output.print("%d\n", t1[i]); output.print("%d\n", t2[i]); } output.close(); } private HashMap<Integer, Integer> clipHash = null; /** * Clip Tmesh to those triangles and points that have * visible texture coordinates. */ public void clip(int uv){ int pclip[] = new int[np]; int used[] = new int[np]; int remainingPoints = 0; clipHash = new HashMap<Integer,Integer>((2*nt)*4/3); //loadfactor = 0.75 System.out.println("Tmesh.clip: clipping " + getName()); System.out.println("Initial points " + np + " triangles " + nt); // clip and reorder the points. for(int i = 0; i < np; i++){ int clipped = 0; if((uv | 1) != 0){ double ut = uscale * (u[i] - uoffset); if(ut < 0.0 || ut > 1.0) clipped = 1; } if(clipped == 0 && ((uv | 2) != 0)){ double vt = vscale * (v[i] - voffset); if(vt < 0.0 || vt > 1.0) clipped = 1; } pclip[i] = clipped; used[i] = 1 - clipped; } // clip the triangles int remainingTriangles = 0; int t0new[] = new int[2*nt]; int t1new[] = new int[2*nt]; int t2new[] = new int[2*nt]; int tcolornew[] = new int[2*nt]; for(int i = 0; i < nt; i++){ int totalUsed = used[t0[i]] + used[t1[i]] + used[t2[i]]; if(totalUsed == 3){ // simple case take whole triangle t0new[remainingTriangles] = t0[i]; t1new[remainingTriangles] = t1[i]; t2new[remainingTriangles] = t2[i]; tcolornew[remainingTriangles] = tcolor[i]; remainingTriangles++; }else if(totalUsed == 1){ // next most simple case... // one vertex left in int v0 = -1, v1 = -1, v2 = -1; if(used[t0[i]] == 1){ v0 = t0[i]; v1 = addClipVertex(t0[i], t1[i], uv); v2 = addClipVertex(t0[i], t2[i], uv); }else if(used[t1[i]] == 1){ v0 = t1[i]; v1 = addClipVertex(t1[i], t2[i], uv); v2 = addClipVertex(t1[i], t0[i], uv); }else if(used[t2[i]] == 1){ v0 = t2[i]; v1 = addClipVertex(t2[i], t0[i], uv); v2 = addClipVertex(t2[i], t1[i], uv); } if(v0 != -1 && v1 != -1 && v2 != -1){ t0new[remainingTriangles] = v0; t1new[remainingTriangles] = v1; t2new[remainingTriangles] = v2; tcolornew[remainingTriangles] = tcolor[i]; remainingTriangles++; }else{ System.out.println("skipping triangle"); } }else if(totalUsed == 2){ // most complicated case // two vertices left in // produces two triangles int v0 = -1, v1 = -1, v2 = -1, va = -1, vb = -1; if(used[t0[i]] == 0){ v0 = t0[i]; v1 = t1[i]; v2 = t2[i]; va = addClipVertex(t0[i], t1[i], uv); vb = addClipVertex(t0[i], t2[i], uv); }else if(used[t1[i]] == 0){ v0 = t1[i]; v2 = t0[i]; v1 = t2[i]; va = addClipVertex(t1[i], t2[i], uv); vb = addClipVertex(t1[i], t0[i], uv); }else if(used[t2[i]] == 0){ v0 = t2[i]; v1 = t0[i]; v2 = t1[i]; va = addClipVertex(t2[i], t0[i], uv); vb = addClipVertex(t2[i], t1[i], uv); } if(v0 != -1 && v1 != -1 && v2 != -1 && va != -1 && vb != -1){ t0new[remainingTriangles] = va; t1new[remainingTriangles] = v1; t2new[remainingTriangles] = v2; tcolornew[remainingTriangles] = tcolor[i]; remainingTriangles++; t0new[remainingTriangles] = va; t1new[remainingTriangles] = vb; t2new[remainingTriangles] = v2; tcolornew[remainingTriangles] = tcolor[i]; remainingTriangles++; }else{ System.out.println("skipping triangle"); } } } nt = remainingTriangles; t0 = t0new; t1 = t1new; t2 = t2new; tcolor = tcolornew; pclip = new int[np]; used = new int[np]; for(int i = 0; i < np; i++){ used[i] = 0; } for(int i = 0; i < nt; i++){ used[t0[i]] = 1; used[t1[i]] = 1; used[t2[i]] = 1; } // pack down the remaining visible triangles remainingPoints = 0; for(int i = 0; i < np; i++){ if(used[i] == 1){ x[remainingPoints] = x[i]; y[remainingPoints] = y[i]; z[remainingPoints] = z[i]; nx[remainingPoints] = nx[i]; ny[remainingPoints] = ny[i]; nz[remainingPoints] = nz[i]; u[remainingPoints] = u[i]; v[remainingPoints] = v[i]; vcolor[remainingPoints] = vcolor[i]; pclip[i] = remainingPoints++; }else{ pclip[i] = -1; } } np = remainingPoints; for(int i = 0; i < nt; i++){ t0[i] = pclip[t0[i]]; t1[i] = pclip[t1[i]]; t2[i] = pclip[t2[i]]; } System.out.println("Final points " + np + " triangles " + nt); } private int addClipVertex(int v0, int v1, int uv){ if(v0 == -1 || v1 == -1){ System.out.println("vertex is out of use v0 " + v0 + " v1 " + v1); return -1; } Integer hashVal = null; if(v0 < v1){ hashVal = Integer.valueOf(v0 + 1000000*v1); }else{ hashVal = Integer.valueOf(v1 + 1000000*v0); } Integer newVertex = clipHash.get(hashVal); if(newVertex != null){ return newVertex.intValue(); } newVertex = Integer.valueOf(np); double t0 = 0.0; double t1 = 0.0; if(uv == UTexture){ t0 = uscale * (u[v0] - uoffset); t1 = uscale * (u[v1] - uoffset); }else{ t0 = vscale * (v[v0] - voffset); t1 = vscale * (v[v1] - voffset); } if(t0 >= 0.0 && t1 >= 0.0 && t0 <= 1.0 && t1 <= 1.0){ System.out.println("t0 " + t0); System.out.println("t1 " + t1); System.out.println("edge shouldn't be clipped, both points are in 0-1"); return -1; } double frac = (1.0 - t0)/(t1 - t0); if(frac < 0.0 || frac > 1.0){ System.out.println("t0 " + t0); System.out.println("t1 " + t1); System.out.println("frac " + frac); return -1; } ensurePointCapacity(); x[np] = (float)( x[v0] + frac * (x[v1] - x[v0])); y[np] = (float)( y[v0] + frac * (y[v1] - y[v0])); z[np] = (float)( z[v0] + frac * (z[v1] - z[v0])); nx[np] = (float)(nx[v0] + frac * (nx[v1] - nx[v0])); ny[np] = (float)(ny[v0] + frac * (ny[v1] - ny[v0])); nz[np] = (float)(nz[v0] + frac * (nz[v1] - nz[v0])); u[np] = (float)( u[v0] + frac * (u[v1] - u[v0])); v[np] = (float)( v[v0] + frac * (v[v1] - v[v0])); vcolor[np] = Color32.blend(vcolor[v0], vcolor[v1], frac); double len = nx[np]*nx[np]; len += ny[np]*ny[np]; len += nz[np]*nz[np]; len = Math.sqrt(len); nx[np] /= len; ny[np] /= len; nz[np] /= len; clipHash.put(hashVal, newVertex); np++; return newVertex.intValue(); } private Point3d ab = new Point3d(); private Point3d bc = new Point3d(); private Point3d norm = new Point3d(); private Point3d origNorm = new Point3d(); /** Recalculate normals. */ public void recalculateNormals(){ double newnx[] = new double[np]; double newny[] = new double[np]; double newnz[] = new double[np]; for(int i = 0; i < nt; i++){ origNorm.x = nx[t0[i]] + nx[t1[i]] + nx[t2[i]]; origNorm.y = ny[t0[i]] + ny[t1[i]] + ny[t2[i]]; origNorm.z = nz[t0[i]] + nz[t1[i]] + nz[t2[i]]; ab.x = x[t0[i]] - x[t1[i]]; ab.y = y[t0[i]] - y[t1[i]]; ab.z = z[t0[i]] - z[t1[i]]; bc.x = x[t1[i]] - x[t2[i]]; bc.y = y[t1[i]] - y[t2[i]]; bc.z = z[t1[i]] - z[t2[i]]; Point3d.crossNoNormalise(norm, ab, bc); double dot = norm.x*origNorm.x + norm.y*origNorm.y + norm.z*origNorm.z; if(dot < 0.0){ norm.negate(); } newnx[t0[i]] += norm.x; newny[t0[i]] += norm.y; newnz[t0[i]] += norm.z; newnx[t1[i]] += norm.x; newny[t1[i]] += norm.y; newnz[t1[i]] += norm.z; newnx[t2[i]] += norm.x; newny[t2[i]] += norm.y; newnz[t2[i]] += norm.z; } for(int i = 0; i < np; i++){ double len = newnx[i]*newnx[i]; len += newny[i]*newny[i]; len += newnz[i]*newnz[i]; len = Math.sqrt(len); nx[i] = (float)(newnx[i] / len); ny[i] = (float)(newny[i] / len); nz[i] = (float)(newnz[i] / len); } newnx = null; newny = null; newnz = null; } /** String representation of object. */ @Override public String toString(){ return name + ": " + np + " points, " + nt + " triangles"; } /** Copy the specified objects into a new object. */ public static Tmesh copy(List<Tmesh> objects){ Tmesh newTmesh = new Tmesh(); for(Tmesh tm : objects){ int cp = newTmesh.np; for(int i = 0; i < tm.np; i++){ newTmesh.addPoint(tm.x[i], tm.y[i], tm.z[i], tm.nx[i], tm.ny[i], tm.nz[i], tm.u[i], tm.v[i]); newTmesh.vcolor[cp + i] = tm.vcolor[i]; } for(int i = 0; i < tm.nt; i++){ newTmesh.addTriangle(tm.t0[i] + cp, tm.t1[i] + cp, tm.t2[i] + cp, tm.tcolor[i]); } newTmesh.setRenderPass(tm.getRenderPass()); newTmesh.setColorStyle(tm.getColorStyle()); newTmesh.texture = tm.texture; newTmesh.transparency = tm.transparency; newTmesh.uoffset = tm.uoffset; newTmesh.voffset = tm.voffset; newTmesh.uscale = tm.uscale; newTmesh.vscale = tm.vscale; newTmesh.backface = tm.backface; newTmesh.visible = tm.visible; if(tm.spheres != null){ List<Tmesh> tmp = Collections.singletonList(tm.spheres); newTmesh.spheres = copy(tmp); } if(tm.cylinders != null){ List<Tmesh> tmp = Collections.singletonList(tm.cylinders); newTmesh.cylinders = copy(tmp); } if(tm.lines != null){ List<Tmesh> tmp = Collections.singletonList(tm.lines); newTmesh.lines = copy(tmp); } } return newTmesh; } /** Which pass of the renderer do we get drawn in. */ private Renderer.Pass renderPass = Renderer.Pass.RenderPass; /** * Get the RenderPass value. * @return the RenderPass value. */ public Renderer.Pass getRenderPass() { return renderPass; } /** * Set the RenderPass value. * @param newRenderPass The new RenderPass value. */ private void setRenderPass(Renderer.Pass newRenderPass) { this.renderPass = newRenderPass; } }