/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSim Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ using System; using System.Globalization; using System.Diagnostics; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using OpenSim.Framework; using OpenSim.Region.Physics.Manager; namespace OpenSim.Region.Physics.OdePlugin { public class Mesh { public List vertices; public List triangles; public float[] normals; public Mesh() { vertices = new List(); triangles = new List(); } public void Add(Triangle triangle) { int i; i = vertices.IndexOf(triangle.v1); if (i < 0) throw new ArgumentException("Vertex v1 not known to mesh"); i = vertices.IndexOf(triangle.v2); if (i < 0) throw new ArgumentException("Vertex v2 not known to mesh"); i = vertices.IndexOf(triangle.v3); if (i < 0) throw new ArgumentException("Vertex v3 not known to mesh"); triangles.Add(triangle); } public void Add(Vertex v) { vertices.Add(v); } public float[] getVertexListAsFloat() { float[] result = new float[vertices.Count * 3]; for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; PhysicsVector point = v.point; result[3 * i + 0] = point.X; result[3 * i + 1] = point.Y; result[3 * i + 2] = point.Z; } GCHandle.Alloc(result, GCHandleType.Pinned); return result; } public int[] getIndexListAsInt() { int[] result = new int[triangles.Count * 3]; for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; result[3 * i + 0] = vertices.IndexOf(t.v1); result[3 * i + 1] = vertices.IndexOf(t.v2); result[3 * i + 2] = vertices.IndexOf(t.v3); } GCHandle.Alloc(result, GCHandleType.Pinned); return result; } public void Append(Mesh newMesh) { foreach (Vertex v in newMesh.vertices) vertices.Add(v); foreach (Triangle t in newMesh.triangles) Add(t); } } public class Meshmerizer { static List FindInfluencedTriangles(List triangles, Vertex v) { List influenced = new List(); foreach (Triangle t in triangles) { float dx, dy; if (t.isInCircle(v.point.X, v.point.Y)) { influenced.Add(t); } } return influenced; } static void InsertVertices(List vertices, int usedForSeed, List triangles, List innerBorders) { // This is a variant of the delaunay algorithm // each time a new vertex is inserted, all triangles that are influenced by it are deleted // and replaced by new ones including the new vertex // It is not very time efficient but easy to implement. int iCurrentVertex; int iMaxVertex=vertices.Count; for (iCurrentVertex = usedForSeed; iCurrentVertex < iMaxVertex; iCurrentVertex++) { // Background: A triangle mesh fulfills the delaunay condition if (iff!) // each circumlocutory circle (i.e. the circle that touches all three corners) // of each triangle is empty of other vertices. // Obviously a single (seeding) triangle fulfills this condition. // If we now add one vertex, we need to reconstruct all triangles, that // do not fulfill this condition with respect to the new triangle // Find the triangles that are influenced by the new vertex Vertex v=vertices[iCurrentVertex]; List influencedTriangles=FindInfluencedTriangles(triangles, v); List simplices = new List(); // Reconstruction phase. First step, dissolve each triangle into it's simplices, // i.e. it's "border lines" // Goal is to find "inner" borders and delete them, while the hull gets conserved. // Inner borders are special in the way that they always come twice, which is how we detect them foreach (Triangle t in influencedTriangles) { List newSimplices = t.GetSimplices(); simplices.AddRange(newSimplices); triangles.Remove(t); } // Now sort the simplices. That will make identical ones side by side in the list simplices.Sort(); // Look for duplicate simplices here. // Remember, they are directly side by side in the list right now int iSimplex; List innerSimplices=new List(); for (iSimplex = 1; iSimplex < simplices.Count; iSimplex++) // Startindex=1, so we can refer backwards { if (simplices[iSimplex - 1].CompareTo(simplices[iSimplex])==0) { innerSimplices.Add(simplices[iSimplex - 1]); innerSimplices.Add(simplices[iSimplex]); } } foreach (Simplex s in innerSimplices) { simplices.Remove(s); } // each simplex still in the list belongs to the hull of the region in question // The new vertex (yes, we still deal with verices here :-) ) forms a triangle // With each of these simplices. Build the new triangles and add them to the list foreach (Simplex s in simplices) { Triangle t = new Triangle(s.v1, s.v2, vertices[iCurrentVertex]); triangles.Add(t); } } // At this point all vertices should be inserted into the mesh // But the areas, that should be kept free still are filled with triangles // We have to remove them. For this we have a list of indices to vertices. // Each triangle that solemnly constists of vertices from the inner border // are deleted List innerTriangles = new List(); foreach (Triangle t in triangles) { if ( innerBorders.Contains(vertices.IndexOf(t.v1)) && innerBorders.Contains(vertices.IndexOf(t.v2)) && innerBorders.Contains(vertices.IndexOf(t.v3)) ) innerTriangles.Add(t); } foreach (Triangle t in innerTriangles) { triangles.Remove(t); } } static Mesh CreateBoxMeshX(PrimitiveBaseShape primShape, PhysicsVector size) // Builds the x (+ and -) surfaces of a box shaped prim { UInt16 hollowFactor = primShape.ProfileHollow; Mesh meshMX = new Mesh(); // Surface 0, -X meshMX.Add(new Vertex("-X-Y-Z", -size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); meshMX.Add(new Vertex("-X+Y-Z", -size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); meshMX.Add(new Vertex("-X-Y+Z", -size.X / 2.0f, -size.Y / 2.0f, +size.Z / 2.0f)); meshMX.Add(new Vertex("-X+Y+Z", -size.X / 2.0f, +size.Y / 2.0f, +size.Z / 2.0f)); meshMX.Add(new Triangle(meshMX.vertices[0], meshMX.vertices[2], meshMX.vertices[1])); meshMX.Add(new Triangle(meshMX.vertices[1], meshMX.vertices[2], meshMX.vertices[3])); Mesh meshPX = new Mesh(); // Surface 1, +X meshPX.Add(new Vertex("+X-Y-Z", +size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); meshPX.Add(new Vertex("+X+Y-Z", +size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); meshPX.Add(new Vertex("+X-Y+Z", +size.X / 2.0f, -size.Y / 2.0f, +size.Z / 2.0f)); meshPX.Add(new Vertex("+X+Y+Z", +size.X / 2.0f, +size.Y / 2.0f, +size.Z / 2.0f)); meshPX.Add(new Triangle(meshPX.vertices[0], meshPX.vertices[1], meshPX.vertices[2])); meshPX.Add(new Triangle(meshPX.vertices[2], meshPX.vertices[1], meshPX.vertices[3])); if (hollowFactor > 0) { float hollowFactorF = (float)hollowFactor / (float)50000; Vertex IPP; Vertex IPM; Vertex IMP; Vertex IMM; IPP = new Vertex("Inner-X+Y+Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IPM = new Vertex("Inner-X+Y-Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); IMP = new Vertex("Inner-X-Y+Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IMM = new Vertex("Inner-X-Y-Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); meshMX.Add(IPP); meshMX.Add(IPM); meshMX.Add(IMP); meshMX.Add(IMM); meshMX.Add(new Triangle(IPP, IMP, IPM)); meshMX.Add(new Triangle(IPM, IMP, IMM)); foreach (Triangle t in meshMX.triangles) { PhysicsVector n = t.getNormal(); } IPP = new Vertex("Inner+X+Y+Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IPM = new Vertex("Inner+X+Y-Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); IMP = new Vertex("Inner+X-Y+Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IMM = new Vertex("Inner+X-Y-Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); meshPX.Add(IPP); meshPX.Add(IPM); meshPX.Add(IMP); meshPX.Add(IMM); meshPX.Add(new Triangle(IPP, IPM, IMP)); meshPX.Add(new Triangle(IMP, IPM, IMM)); foreach (Triangle t in meshPX.triangles) { PhysicsVector n = t.getNormal(); } } Mesh result = new Mesh(); result.Append(meshMX); result.Append(meshPX); return result; } static Mesh CreateBoxMeshY(PrimitiveBaseShape primShape, PhysicsVector size) // Builds the y (+ and -) surfaces of a box shaped prim { UInt16 hollowFactor = primShape.ProfileHollow; // (M)inus Y Mesh MeshMY = new Mesh(); MeshMY.Add(new Vertex("-X-Y-Z", -size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); MeshMY.Add(new Vertex("+X-Y-Z", +size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); MeshMY.Add(new Vertex("-X-Y+Z", -size.X / 2.0f, -size.Y / 2.0f, +size.Z / 2.0f)); MeshMY.Add(new Vertex("+X-Y+Z", +size.X / 2.0f, -size.Y / 2.0f, +size.Z / 2.0f)); MeshMY.Add(new Triangle(MeshMY.vertices[0], MeshMY.vertices[1], MeshMY.vertices[2])); MeshMY.Add(new Triangle(MeshMY.vertices[2], MeshMY.vertices[1], MeshMY.vertices[3])); // (P)lus Y Mesh MeshPY = new Mesh(); MeshPY.Add(new Vertex("-X+Y-Z", -size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); MeshPY.Add(new Vertex("+X+Y-Z", +size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); MeshPY.Add(new Vertex("-X+Y+Z", -size.X / 2.0f, +size.Y / 2.0f, +size.Z / 2.0f)); MeshPY.Add(new Vertex("+X+Y+Z", +size.X / 2.0f, +size.Y / 2.0f, +size.Z / 2.0f)); MeshPY.Add(new Triangle(MeshPY.vertices[1], MeshPY.vertices[0], MeshPY.vertices[2])); MeshPY.Add(new Triangle(MeshPY.vertices[1], MeshPY.vertices[2], MeshPY.vertices[3])); if (hollowFactor > 0) { float hollowFactorF = (float)hollowFactor / (float)50000; Vertex IPP; Vertex IPM; Vertex IMP; Vertex IMM; IPP = new Vertex("Inner+X-Y+Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IPM = new Vertex("Inner+X-Y-Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); IMP = new Vertex("Inner-X-Y+Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IMM = new Vertex("Inner-X-Y-Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); MeshMY.Add(IPP); MeshMY.Add(IPM); MeshMY.Add(IMP); MeshMY.Add(IMM); MeshMY.Add(new Triangle(IPP, IPM, IMP)); MeshMY.Add(new Triangle(IMP, IPM, IMM)); foreach (Triangle t in MeshMY.triangles) { PhysicsVector n = t.getNormal(); } IPP = new Vertex("Inner+X+Y+Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IPM=new Vertex("Inner+X+Y-Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); IMP=new Vertex("Inner-X+Y+Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, +size.Z / 2.0f); IMM=new Vertex("Inner-X+Y-Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, -size.Z / 2.0f); MeshPY.Add(IPP); MeshPY.Add(IPM); MeshPY.Add(IMP); MeshPY.Add(IMM); MeshPY.Add(new Triangle(IPM, IPP, IMP)); MeshPY.Add(new Triangle(IMP, IMM, IPM)); foreach (Triangle t in MeshPY.triangles) { PhysicsVector n = t.getNormal(); } } Mesh result = new Mesh(); result.Append(MeshMY); result.Append(MeshPY); return result; } static Mesh CreateBoxMeshZ(PrimitiveBaseShape primShape, PhysicsVector size) // Builds the z (+ and -) surfaces of a box shaped prim { UInt16 hollowFactor = primShape.ProfileHollow; // Base, i.e. outer shape // (M)inus Z Mesh MZ = new Mesh(); MZ.Add(new Vertex("-X-Y-Z", -size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); MZ.Add(new Vertex("+X-Y-Z", +size.X / 2.0f, -size.Y / 2.0f, -size.Z / 2.0f)); MZ.Add(new Vertex("-X+Y-Z", -size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); MZ.Add(new Vertex("+X+Y-Z", +size.X / 2.0f, +size.Y / 2.0f, -size.Z / 2.0f)); MZ.Add(new Triangle(MZ.vertices[1], MZ.vertices[0], MZ.vertices[2])); MZ.Add(new Triangle(MZ.vertices[1], MZ.vertices[2], MZ.vertices[3])); // (P)lus Z Mesh PZ = new Mesh(); PZ.Add(new Vertex("-X-Y+Z", -size.X / 2.0f, -size.Y / 2.0f, 0.0f)); PZ.Add(new Vertex("+X-Y+Z", +size.X / 2.0f, -size.Y / 2.0f, 0.0f)); PZ.Add(new Vertex("-X+Y+Z", -size.X / 2.0f, +size.Y / 2.0f, 0.0f)); PZ.Add(new Vertex("+X+Y+Z", +size.X / 2.0f, +size.Y / 2.0f, 0.0f)); // Surface 5, +Z PZ.Add(new Triangle(PZ.vertices[0], PZ.vertices[1], PZ.vertices[2])); PZ.Add(new Triangle(PZ.vertices[2], PZ.vertices[1], PZ.vertices[3])); if (hollowFactor > 0) { float hollowFactorF = (float)hollowFactor / (float)50000; MZ.Add(new Vertex("-X-Y-Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, 0.0f)); MZ.Add(new Vertex("-X+Y-Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, 0.0f)); MZ.Add(new Vertex("-X-Y+Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, 0.0f)); MZ.Add(new Vertex("-X+Y+Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, 0.0f)); List innerBorders = new List(); innerBorders.Add(4); innerBorders.Add(5); innerBorders.Add(6); innerBorders.Add(7); InsertVertices(MZ.vertices, 4, MZ.triangles, innerBorders); PZ.Add(new Vertex("-X-Y-Z", -size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, 0.0f)); PZ.Add(new Vertex("-X+Y-Z", +size.X * hollowFactorF / 2.0f, -size.Y * hollowFactorF / 2.0f, 0.0f)); PZ.Add(new Vertex("-X-Y+Z", -size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, 0.0f)); PZ.Add(new Vertex("-X+Y+Z", +size.X * hollowFactorF / 2.0f, +size.Y * hollowFactorF / 2.0f, 0.0f)); innerBorders = new List(); innerBorders.Add(4); innerBorders.Add(5); innerBorders.Add(6); innerBorders.Add(7); InsertVertices(PZ.vertices, 4, PZ.triangles, innerBorders); } foreach (Vertex v in PZ.vertices) { v.point.Z = size.Z / 2.0f; } foreach (Vertex v in MZ.vertices) { v.point.Z = -size.Z / 2.0f; } foreach (Triangle t in MZ.triangles) { PhysicsVector n = t.getNormal(); if (n.Z > 0.0) t.invertNormal(); } foreach (Triangle t in PZ.triangles) { PhysicsVector n = t.getNormal(); if (n.Z < 0.0) t.invertNormal(); } Mesh result = new Mesh(); result.Append(MZ); result.Append(PZ); return result; } static Mesh CreateBoxMesh(PrimitiveBaseShape primShape, PhysicsVector size) { Mesh result = new Mesh(); Mesh MeshX = Meshmerizer.CreateBoxMeshX(primShape, size); Mesh MeshY = Meshmerizer.CreateBoxMeshY(primShape, size); Mesh MeshZ = Meshmerizer.CreateBoxMeshZ(primShape, size); result.Append(MeshX); result.Append(MeshY); result.Append(MeshZ); return result; } public static void CalcNormals(Mesh mesh) { int iTriangles = mesh.triangles.Count; mesh.normals = new float[iTriangles*3]; int i=0; foreach (Triangle t in mesh.triangles) { float ux, uy, uz; float vx, vy, vz; float wx, wy, wz; ux = t.v1.point.X; uy = t.v1.point.Y; uz = t.v1.point.Z; vx = t.v2.point.X; vy = t.v2.point.Y; vz = t.v2.point.Z; wx = t.v3.point.X; wy = t.v3.point.Y; wz = t.v3.point.Z; // Vectors for edges float e1x, e1y, e1z; float e2x, e2y, e2z; e1x = ux - vx; e1y = uy - vy; e1z = uz - vz; e2x = ux - wx; e2y = uy - wy; e2z = uz - wz; // Cross product for normal float nx, ny, nz; nx = e1y * e2z - e1z * e2y; ny = e1z * e2x - e1x * e2z; nz = e1x * e2y - e1y * e2x; // Length float l = (float)Math.Sqrt(nx * nx + ny * ny + nz * nz); // Normalized "normal" nx /= l; ny /= l; nz /= l; mesh.normals[i] = nx; mesh.normals[i + 1] = ny; mesh.normals[i + 2] = nz; i+=3; } } public static Mesh CreateMesh(PrimitiveBaseShape primShape, PhysicsVector size) { Mesh mesh = null; switch (primShape.ProfileShape) { case ProfileShape.Square: mesh=CreateBoxMesh(primShape, size); CalcNormals(mesh); break; default: mesh=null; break; } return mesh; } } }