public static ArraySimulationStepInput FromJson(JsonSpec spec, Mesh mesh, Bitmap texture) { ArraySimulationStepInput input = new ArraySimulationStepInput(); input.Heading = DegToRad(spec.Environment.HeadingDeg); input.IndirectIrradiance = spec.Environment.IndirectIrradianceWM2; input.Irradiance = spec.Environment.IrradianceWM2; input.Latitude = spec.Environment.LatitudeDeg; input.Longitude = spec.Environment.LongitudeDeg; input.Temperature = spec.Environment.TemperatureC; input.Tilt = DegToRad(spec.Environment.TiltDeg); input.TimezoneOffsetHours = spec.Environment.TimezoneOffsetHours; input.Utc = spec.Environment.Utc; input.Array = new ArraySpec(); input.Array.BypassDiodeSpec.VoltageDrop = spec.Array.BypassDiode.VoltageDrop; input.Array.CellSpec.Area = spec.Array.Cell.AreaM2; input.Array.CellSpec.DIscDT = spec.Array.Cell.DIscDT; input.Array.CellSpec.DVocDT = spec.Array.Cell.DVocDT; input.Array.CellSpec.IscStc = spec.Array.Cell.IscStc; input.Array.CellSpec.NIdeal = spec.Array.Cell.NIdeal; input.Array.CellSpec.SeriesR = spec.Array.Cell.SeriesR; input.Array.CellSpec.VocStc = spec.Array.Cell.VocStc; input.Array.EncapsulationLoss = spec.Array.EncapsulationLoss; input.Array.LayoutBounds = FromJson(spec.Array.LayoutBounds); input.Array.Mesh = mesh; input.Array.LayoutTexture = texture; input.Array.ReadStringsFromColors(); return input; }
/// <summary> /// If two mesh points have the same (X, Y, Z) coords, then they get combined into one. /// </summary> public static Mesh JoinVertices(Mesh mesh) { Dictionary<Vector3, int> pointIxMap = new Dictionary<Vector3, int>(); List<Vector3> points = new List<Vector3>(); List<Vector3> norms = new List<Vector3>(); for (int i = 0; i < mesh.points.Length; i++) { if (!pointIxMap.ContainsKey(mesh.points[i])) { points.Add(mesh.points[i]); norms.Add(mesh.normals[i]); pointIxMap.Add(mesh.points[i], points.Count-1); } } Logger.info("mesh has " + mesh.points.Length + " verts, "+ "joined " + (mesh.points.Length - pointIxMap.Count) + " dupes"); List<Mesh.Triangle> tris = new List<Mesh.Triangle>(); for (int i = 0; i < mesh.triangles.Length; i++) { var oldTri = mesh.triangles[i]; var tri = new Mesh.Triangle( pointIxMap[mesh.points[oldTri.vertexA]], pointIxMap[mesh.points[oldTri.vertexB]], pointIxMap[mesh.points[oldTri.vertexC]]); if (tri.vertexA != tri.vertexB && tri.vertexA != tri.vertexC && tri.vertexB != tri.vertexC) { tris.Add(tri); } } return new Mesh(points.ToArray(), norms.ToArray(), tris.ToArray()); }
public static Mesh MirrorAndCombine(Mesh mesh, Vector3 axis) { /* if the current mesh has n points, we'll have up to 2n-1 points in the mirrored and combined one */ var points = new List<Vector3>(); points.AddRange(mesh.points); /* find the point or points we're going to mirror around--the ones closest in the 'axis' direction * (in other words, find the mirror plane) */ var minDot = mesh.points.Min(point => Vector3.Dot(point, axis)); /* if points are this close or closer to the mirror plane, they will not be duplicated--they will simply be shared by additional triangles */ var epsilon = 0.002f; /* create a dictionary mapping point indices in the original mesh to the corresponding mirrored points */ var pointIndexMap = new Dictionary<int, int>(); for (int i = 0; i < mesh.points.Length; i++) { var distance = Vector3.Dot(mesh.points[i], axis) - minDot; if (distance < epsilon) { /* point lies on mirror plane */ pointIndexMap.Add(i, i); } else { /* point is not on mirror plane; create its mirror image as a new point */ pointIndexMap.Add(i, points.Count); var mirrorPoint = mesh.points[i] - 2 * distance * axis; points.Add(mirrorPoint); } } /* normals correspond to points, but may need to be mirrored. * if a point is on the mirror plane, make sure the corresponding normal is in the mirror plane */ var normals = new Vector3[points.Count]; for (int i = 0; i < mesh.points.Length; i++) { if (pointIndexMap[i] == i) { normals[i] = mesh.normals[i] - Vector3.Dot(mesh.normals[i], axis) * axis; normals[i].Normalize(); } else { normals[i] = mesh.normals[i]; normals[pointIndexMap[i]] = mesh.normals[i] - 2 * Vector3.Dot(mesh.normals[i], axis) * axis; } } /* triangles are simply duplicated--each triangle gets a mirror image */ var triangles = new Mesh.Triangle[mesh.triangles.Length * 2]; for (int i = 0; i < mesh.triangles.Length; i++) { triangles[i] = mesh.triangles[i]; } for (int i = 0; i < mesh.triangles.Length; i++) { var triangle = mesh.triangles[i]; triangles[mesh.triangles.Length + i] = new Mesh.Triangle( pointIndexMap[triangle.vertexA], pointIndexMap[triangle.vertexB], pointIndexMap[triangle.vertexC] ); } return new Mesh(points.ToArray(), normals, triangles); }
/// <summary> /// Returns a depth t such that position + direction*t = a point in triangle. Note that this may be a positive or negative number. /// Returns NaN if the ray does not intersect the triangle or if direction is the zero vector. /// </summary> public float Intersect(Mesh.Triangle triangle, Vector3 position, Vector3 direction) { //transform coords so that position=0, triangle = (v1, v2, v3) // find a b c such that // a*v1x + b*v1y + c*v1z = 1 // a*v2x + b*v2y + c*v2z = 1 // a*v3x + b*v3y + c*v3z = 1 // (v1 v2 v3)T(a b c)T = (1 1 1) // (a b c) = (v1 v2 v3)T^-1 (1 1 1) var m = new Matrix3( Mesh.points[triangle.vertexA], Mesh.points[triangle.vertexB], Mesh.points[triangle.vertexC]); m.Transpose(); var inv = m.Inverse; var abc = inv * new Vector3(1, 1, 1); //(p + t*d) dot (a b c) = 1 //p dot (a b c) + t*d dot (a b c) = 1 //t = (1 - p dot (a b c)) / (d dot (a b c)) var t = (1f - Vector3.Dot(position, abc)) / Vector3.Dot(direction, abc); var intersection = position + direction * t; //next, find the intersection //i = p + t*d //ap*v1 + bp*v2 + cp*v3 = i //(ap bp cp) = (v1 v2 v3)T^-1 (1 1 1) var abcPrime = inv * intersection; //if any of the components (ap bp cp) is outside of [0, 1], //then the ray does not intersect the triangle if (abcPrime.X < 0 || abcPrime.X > 1) return float.NaN; if (abcPrime.Y < 0 || abcPrime.Y > 1) return float.NaN; if (abcPrime.Z < 0 || abcPrime.Z > 1) return float.NaN; return t; }
/// <summary> /// Uses the given mesh for rendering and calculation. /// /// Computes shadow volumes for rendering. /// </summary> private void SetModel(Mesh mesh) { // create shadow volumes Logger.info("computing shadows..."); Shadow newShadow = new Shadow(mesh); newShadow.Initialize(); // make sure init works before setting shadow shadow = newShadow; // render them ShadowMeshSprite shadowSprite = new ShadowMeshSprite(shadow); var center = (mesh.BoundingBox.Max + mesh.BoundingBox.Min) / 2; shadowSprite.Position = new Vector4(-center, 1); glControl.Sprite = shadowSprite; simInput.Array.Mesh = mesh; }
public ShadowVolume(Mesh mesh, Vector4 lightSource) { this.Mesh = mesh; this.LightSource = lightSource; SilhouettePoints = new List<int>(); }
public void Parse(string filename) { var verts = new List<Vector3>(); var tris = new List<Mesh.Triangle>(); var norms = new List<Vector3>(); //deduplicate the verts... var vertIxs = new Dictionary<Vector3, int>(); // parse the file with a simple state machine const int INIT = 1, SOLID = 2, FACET = 3, LOOP = 4, ENDFACET=5; var triIxs = new List<int>(); var norm = new Vector3(); int state = INIT; StreamReader reader = new StreamReader(filename); String line; for (int lineNum = 1; (line = reader.ReadLine()) != null; lineNum++) { string[] parts = line.Trim().ToLower().Split(new char[0], StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 0) { // skip empty lines continue; } if (parts[0] == "solid") { Assert(state == INIT, filename, lineNum); Logger.info("reading stl solid " + String.Join(" ", parts, 1, parts.Length - 1)); state = SOLID; } else if (parts[0] == "facet") { Assert(state == SOLID, filename, lineNum); Assert(parts[1] == "normal", filename, lineNum); norm = ReadVector(parts, 2, filename, lineNum); state = FACET; } else if (parts[0] == "outer") { Assert(state == FACET, filename, lineNum); Assert(parts[1] == "loop", filename, lineNum); triIxs.Clear(); state = LOOP; } else if (parts[0] == "vertex") { Assert(state == LOOP, filename, lineNum); var vertex = ReadVector(parts, 1, filename, lineNum); if (vertIxs.ContainsKey(vertex)) { int vertIx = vertIxs[vertex]; triIxs.Add(vertIx); norms[vertIx] += norm; } else { int vertIx = verts.Count; vertIxs.Add(vertex, vertIx); triIxs.Add(vertIx); verts.Add(vertex); norms.Add(norm); } } else if (parts[0] == "endloop") { Assert(state == LOOP, filename, lineNum); //every face must be a triangle Assert(triIxs.Count == 3, filename, lineNum); var tri = new Mesh.Triangle(triIxs[0], triIxs[1], triIxs[2]); tri.normal = norm; tris.Add(tri); state = ENDFACET; } else if (parts[0] == "endfacet") { Assert(state == ENDFACET, filename, lineNum); state = SOLID; } else if (parts[0] == "endsolid") { Assert(state == SOLID, filename, lineNum); state = INIT; } } // get all the averaged vertex normals and set them to unit length for(int i = 0; i < norms.Count; i++){ float len = norms[i].Length; if (len == 0) { norms[i] = new Vector3(1, 0, 0); } else { norms[i] /= len; } } // finally, return a mesh mesh = new Mesh(verts.ToArray(), norms.ToArray(), tris.ToArray()); }
public static void Split(Mesh input, IVolume volume, out Mesh output, out bool[] trisInside) { /** * * for triangle in input * if triangle is entirely inside or outside * add to corresponding mesh * else split triangle into 4 pieces. * three go in one mesh, one goes into the other * * Splitting a triangle, Before: * * * vA * / \ * boundary --- /---\ --- * / \ * vB1 *-------* vB2 * * * After: * * * vA * vMid1 / \ vMid2 * boundary --- *---* --- * / \ / \ * vB1 *---*---* vB2 * vMidB * **/ int np = input.points.Length, nt = input.triangles.Length; List<Vector3> points = input.points.ToList(); List<Vector3> norms = input.normals.ToList(); List<Mesh.Triangle> tris = new List<Mesh.Triangle>(); List<bool> trisIn = new List<bool>(); for(int i = 0; i < nt; i++){ var tri = input.triangles[i]; Vector3 vA = input.points[tri.vertexA]; Vector3 vB = input.points[tri.vertexB]; Vector3 vC = input.points[tri.vertexC]; int ncontains = (volume.Contains(vA) ? 1 : 0) + (volume.Contains(vB) ? 1 : 0) + (volume.Contains(vC) ? 1 : 0); if (ncontains==3) { tris.Add(tri); trisIn.Add(true); } else if (ncontains==0){ tris.Add(tri); trisIn.Add(false); } else { // see comment above for explanation Debug.Assert(ncontains == 1 || ncontains == 2); bool containsA = (ncontains==1); int ixA, ixB1, ixB2; if (volume.Contains(vA) == containsA) { ixA = tri.vertexA; ixB1 = tri.vertexB; ixB2 = tri.vertexC; } else if (volume.Contains(vB) == containsA) { ixA = tri.vertexB; ixB1 = tri.vertexA; ixB2 = tri.vertexC; } else { Debug.Assert(volume.Contains(vC) == containsA); ixA = tri.vertexC; ixB1 = tri.vertexA; ixB2 = tri.vertexB; } Vector3 vAO = input.points[ixA]; Vector3 vB1 = input.points[ixB1]; Vector3 vB2 = input.points[ixB2]; Vector3 vMid1 = FindBoundary(vAO, vB1, volume); Vector3 vMid2 = FindBoundary(vAO, vB2, volume); Vector3 vMidB = input.points[ixB1]*0.5f + input.points[ixB2]*0.5f; points.Add(vMid1); points.Add(vMid2); points.Add(vMidB); float b1 = (vMid1 - vAO).Length / ((vB1 - vAO).Length + float.Epsilon); float b2 = (vMid2 - vAO).Length / ((vB2 - vAO).Length + float.Epsilon); Debug.Assert(0 <= b1 && b1 <= 1 && 0 <= b2 && b2 <= 1); Vector3 nMid1 = input.normals[ixA] * (1 - b1) + input.normals[ixB1] * b1; Vector3 nMid2 = input.normals[ixA] * (1 - b2) + input.normals[ixB2] * b2; Vector3 nMidB = input.normals[ixB1] * 0.5f + input.normals[ixB2] * 0.5f; nMid1.Normalize(); nMid2.Normalize(); nMidB.Normalize(); norms.Add(nMid1); norms.Add(nMid2); norms.Add(nMidB); //norms.Add(input.normals[ixA]); norms.Add(input.normals[ixA]); norms.Add(input.normals[ixA]); var tri1 = new Mesh.Triangle(ixA, points.Count - 3, points.Count - 2); var tri2 = new Mesh.Triangle(ixB1, points.Count - 3, points.Count - 1); var tri3 = new Mesh.Triangle(points.Count - 3, points.Count - 2, points.Count - 1); var tri4 = new Mesh.Triangle(ixB2, points.Count - 2, points.Count - 1); tri1.normal = tri2.normal = tri3.normal = tri4.normal = tri.normal; tris.Add(tri1); tris.Add(tri2); tris.Add(tri3); tris.Add(tri4); trisIn.Add(containsA); trisIn.Add(!containsA); trisIn.Add(!containsA); trisIn.Add(!containsA); } } // done Logger.info("split mesh, started with " + nt + " tris, added " + (tris.Count-nt)); Debug.Assert(points.Count==norms.Count); output = new Mesh(points.ToArray(), norms.ToArray(), tris.ToArray()); trisInside = trisIn.ToArray(); }
public static Mesh Scale(Mesh mesh, float p) { Vector3[] newPoints = new Vector3[mesh.points.Length]; for (int i = 0; i < mesh.points.Length; i++) { newPoints[i] = p * mesh.points[i]; } return new Mesh(newPoints, mesh.normals, mesh.triangles); }
public void Parse(string filename) { /* load file */ XmlDocument doc = new XmlDocument(); doc.Load(filename); /* parse file */ var points = new List<Vector3>(); var normals = new List<Vector3>(); var triangles = new List<Mesh.Triangle>(); //TODO: disgusting hack. //XmlNodeList xmlSurfaces; = doc.FirstChild.SelectNodes( //"//GeometricRepresentationSet/Representation/AssociatedXML/Rep"); XmlNode rep; if (doc["XMLRepresentation"] != null && doc["XMLRepresentation"]["Root"] != null && doc["XMLRepresentation"]["Root"]["Rep"] != null) { rep = doc["XMLRepresentation"]["Root"]["Rep"]; } else if (doc["Model_3dxml"] != null && doc["Model_3dxml"]["GeometricRepresentationSet"] != null && doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"] != null && doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"]["AssociatedXML"] != null) { rep = doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"]["AssociatedXML"]; } else { throw new ArgumentException("cannot parse " + filename); } foreach (XmlNode node in rep.ChildNodes) { if (node["VertexBuffer"] == null || node["VertexBuffer"]["Positions"] == null || node["VertexBuffer"]["Normals"] == null || node["Faces"]["Face"] == null) { Logger.warn("skipping rep..."); continue; } //read verts var units = 0.001f; /* units in mm */ var newPoints = Parse3DXMLVectors(node["VertexBuffer"]["Positions"].InnerText); for (int i = 0; i < newPoints.Length; i++) newPoints[i] = newPoints[i] * units; var newNormals = Parse3DXMLVectors(node["VertexBuffer"]["Normals"].InnerText); for (int i = 0; i < newNormals.Length; i++) newNormals[i] = -newNormals[i]; int offset = points.Count; points.AddRange(newPoints); normals.AddRange(newNormals); //read triangles XmlAttributeCollection faceAttrs = node["Faces"]["Face"].Attributes; Logger.info("parsing face " + faceAttrs["id"].Value); if (faceAttrs["triangles"] != null) { int[] vertIxs = Parse3DXMLInts(faceAttrs["triangles"].Value); var triangleIxs = new HashSet<int>(vertIxs); /*for (int i = 0; i < newPoints.Length - 2; i++) { if (triangleIxs.Contains(i)) { triangles.Add(new MeshSprite.Triangle() { VertexA = offset + i, VertexB = offset + i + 1, VertexC = offset + i + 2 }); } }*/ for (int i = 0; i < vertIxs.Length; i += 3) { var tri = new Mesh.Triangle( offset + vertIxs[i], offset + vertIxs[i + 1], offset + vertIxs[i + 2] ); tri.normal = newNormals[vertIxs[i]]; triangles.Add(tri); } } else if (faceAttrs["strips"] != null) { String[] strips = faceAttrs["strips"].Value.Split(','); foreach (String strip in strips) { int[] vertIxs = Parse3DXMLInts(strip); for (int i = 2; i < vertIxs.Length; i++) { var tri = new Mesh.Triangle( offset + vertIxs[i - ((i % 2) == 0 ? 1 : 2)], offset + vertIxs[i - ((i % 2) == 0 ? 2 : 1)], offset + vertIxs[i] ); tri.normal = newNormals[vertIxs[i]]; triangles.Add(tri); } } } else { throw new ArgumentException("Found an unsupported 3dxml mesh surface.\n" + "Clean up your mesh in MeshLab. Triangles only."); } } /* create mesh sprite */ mesh = new Mesh(points.ToArray(), normals.ToArray(), triangles.ToArray()); }
public Shadow(Mesh mesh) { this.Mesh = mesh; this.SilhouetteEdges = new List<Pair<int>>(); }
private Vector3 ComputeNormal(Mesh.Triangle triangle) { var normal = Vector3.Cross( Mesh.points[triangle.vertexB] - Mesh.points[triangle.vertexA], Mesh.points[triangle.vertexC] - Mesh.points[triangle.vertexA]); normal.Normalize(); return normal; }
public MeshSprite(Mesh mesh) { Debug.Assert(mesh != null); Mesh = mesh; }