/* * 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 OpenSimulator 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.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using OpenSim.Region.Physics.Manager; using PrimMesher; using OpenMetaverse; namespace OpenSim.Region.Physics.Meshing { public class Mesh : IMesh { private Dictionary m_vertices; private List m_triangles; GCHandle m_pinnedVertexes; GCHandle m_pinnedIndex; IntPtr m_verticesPtr = IntPtr.Zero; int m_vertexCount = 0; IntPtr m_indicesPtr = IntPtr.Zero; int m_indexCount = 0; public float[] m_normals; Vector3 m_centroid; float m_obbXmin; float m_obbXmax; float m_obbYmin; float m_obbYmax; float m_obbZmin; float m_obbZmax; int m_centroidDiv; private class vertexcomp : IEqualityComparer { public bool Equals(Vertex v1, Vertex v2) { if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z) return true; else return false; } public int GetHashCode(Vertex v) { int a = v.X.GetHashCode(); int b = v.Y.GetHashCode(); int c = v.Z.GetHashCode(); return (a << 16) ^ (b << 8) ^ c; } } public Mesh() { vertexcomp vcomp = new vertexcomp(); m_vertices = new Dictionary(vcomp); m_triangles = new List(); m_centroid = Vector3.Zero; m_centroidDiv = 0; m_obbXmin = float.MaxValue; m_obbXmax = float.MinValue; m_obbYmin = float.MaxValue; m_obbYmax = float.MinValue; m_obbZmin = float.MaxValue; m_obbZmax = float.MinValue; } public int RefCount { get; set; } public AMeshKey Key { get; set; } public void Scale(Vector3 scale) { } public Mesh Clone() { Mesh result = new Mesh(); foreach (Triangle t in m_triangles) { result.Add(new Triangle(t.v1.Clone(), t.v2.Clone(), t.v3.Clone())); } result.m_centroid = m_centroid; result.m_centroidDiv = m_centroidDiv; result.m_obbXmin = m_obbXmin; result.m_obbXmax = m_obbXmax; result.m_obbYmin = m_obbYmin; result.m_obbYmax = m_obbYmax; result.m_obbZmin = m_obbZmin; result.m_obbZmax = m_obbZmax; return result; } public void addVertexLStats(Vertex v) { float x = v.X; float y = v.Y; float z = v.Z; m_centroid.X += x; m_centroid.Y += y; m_centroid.Z += z; m_centroidDiv++; if (x > m_obbXmax) m_obbXmax = x; else if (x < m_obbXmin) m_obbXmin = x; if (y > m_obbYmax) m_obbYmax = y; else if (y < m_obbYmin) m_obbYmin = y; if (z > m_obbZmax) m_obbZmax = z; else if (z < m_obbZmin) m_obbZmin = z; } public void Add(Triangle triangle) { if (m_pinnedIndex.IsAllocated || m_pinnedVertexes.IsAllocated || m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero) throw new NotSupportedException("Attempt to Add to a pinned Mesh"); // If a vertex of the triangle is not yet in the vertices list, // add it and set its index to the current index count // vertex == seems broken // skip colapsed triangles if ((triangle.v1.X == triangle.v2.X && triangle.v1.Y == triangle.v2.Y && triangle.v1.Z == triangle.v2.Z) || (triangle.v1.X == triangle.v3.X && triangle.v1.Y == triangle.v3.Y && triangle.v1.Z == triangle.v3.Z) || (triangle.v2.X == triangle.v3.X && triangle.v2.Y == triangle.v3.Y && triangle.v2.Z == triangle.v3.Z) ) { return; } if (m_vertices.Count == 0) { m_centroidDiv = 0; m_centroid = Vector3.Zero; } if (!m_vertices.ContainsKey(triangle.v1)) { m_vertices[triangle.v1] = m_vertices.Count; addVertexLStats(triangle.v1); } if (!m_vertices.ContainsKey(triangle.v2)) { m_vertices[triangle.v2] = m_vertices.Count; addVertexLStats(triangle.v2); } if (!m_vertices.ContainsKey(triangle.v3)) { m_vertices[triangle.v3] = m_vertices.Count; addVertexLStats(triangle.v3); } m_triangles.Add(triangle); } public Vector3 GetCentroid() { if (m_centroidDiv > 0) return new Vector3(m_centroid.X / m_centroidDiv, m_centroid.Y / m_centroidDiv, m_centroid.Z / m_centroidDiv); else return Vector3.Zero; } public Vector3 GetOBB() { float x, y, z; if (m_centroidDiv > 0) { x = (m_obbXmax - m_obbXmin) * 0.5f; y = (m_obbYmax - m_obbYmin) * 0.5f; z = (m_obbZmax - m_obbZmin) * 0.5f; } else // ?? { x = 0.5f; y = 0.5f; z = 0.5f; } return new Vector3(x, y, z); } public void CalcNormals() { int iTriangles = m_triangles.Count; this.m_normals = new float[iTriangles * 3]; int i = 0; foreach (Triangle t in m_triangles) { float ux, uy, uz; float vx, vy, vz; float wx, wy, wz; ux = t.v1.X; uy = t.v1.Y; uz = t.v1.Z; vx = t.v2.X; vy = t.v2.Y; vz = t.v2.Z; wx = t.v3.X; wy = t.v3.Y; wz = t.v3.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); float lReciprocal = 1.0f / l; // Normalized "normal" //nx /= l; //ny /= l; //nz /= l; m_normals[i] = nx * lReciprocal; m_normals[i + 1] = ny * lReciprocal; m_normals[i + 2] = nz * lReciprocal; i += 3; } } public List getVertexList() { List result = new List(); foreach (Vertex v in m_vertices.Keys) { result.Add(new Vector3(v.X, v.Y, v.Z)); } return result; } private float[] getVertexListAsFloat() { if (m_vertices == null) throw new NotSupportedException(); float[] result = new float[m_vertices.Count * 3]; foreach (KeyValuePair kvp in m_vertices) { Vertex v = kvp.Key; int i = kvp.Value; result[3 * i + 0] = v.X; result[3 * i + 1] = v.Y; result[3 * i + 2] = v.Z; } return result; } public float[] getVertexListAsFloatLocked() { if (m_pinnedVertexes.IsAllocated) return (float[])(m_pinnedVertexes.Target); float[] result = getVertexListAsFloat(); m_pinnedVertexes = GCHandle.Alloc(result, GCHandleType.Pinned); // Inform the garbage collector of this unmanaged allocation so it can schedule // the next GC round more intelligently GC.AddMemoryPressure(Buffer.ByteLength(result)); return result; } public void PrepForOde() { // If there isn't an unmanaged array allocated yet, do it now if (m_verticesPtr == IntPtr.Zero) { float[] vertexList = getVertexListAsFloat(); // Each vertex is 3 elements (floats) m_vertexCount = vertexList.Length / 3; int byteCount = m_vertexCount * 3 * sizeof(float); m_verticesPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(byteCount); System.Runtime.InteropServices.Marshal.Copy(vertexList, 0, m_verticesPtr, m_vertexCount * 3); } // If there isn't an unmanaged array allocated yet, do it now if (m_indicesPtr == IntPtr.Zero) { int[] indexList = getIndexListAsInt(); m_indexCount = indexList.Length; int byteCount = m_indexCount * sizeof(int); m_indicesPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(byteCount); System.Runtime.InteropServices.Marshal.Copy(indexList, 0, m_indicesPtr, m_indexCount); } releaseSourceMeshData(); } public void getVertexListAsPtrToFloatArray(out IntPtr vertices, out int vertexStride, out int vertexCount) { // A vertex is 3 floats vertexStride = 3 * sizeof(float); // If there isn't an unmanaged array allocated yet, do it now if (m_verticesPtr == IntPtr.Zero) { float[] vertexList = getVertexListAsFloat(); // Each vertex is 3 elements (floats) m_vertexCount = vertexList.Length / 3; int byteCount = m_vertexCount * vertexStride; m_verticesPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(byteCount); System.Runtime.InteropServices.Marshal.Copy(vertexList, 0, m_verticesPtr, m_vertexCount * 3); } vertices = m_verticesPtr; vertexCount = m_vertexCount; } public int[] getIndexListAsInt() { if (m_triangles == null) throw new NotSupportedException(); int[] result = new int[m_triangles.Count * 3]; for (int i = 0; i < m_triangles.Count; i++) { Triangle t = m_triangles[i]; result[3 * i + 0] = m_vertices[t.v1]; result[3 * i + 1] = m_vertices[t.v2]; result[3 * i + 2] = m_vertices[t.v3]; } return result; } /// /// creates a list of index values that defines triangle faces. THIS METHOD FREES ALL NON-PINNED MESH DATA /// /// public int[] getIndexListAsIntLocked() { if (m_pinnedIndex.IsAllocated) return (int[])(m_pinnedIndex.Target); int[] result = getIndexListAsInt(); m_pinnedIndex = GCHandle.Alloc(result, GCHandleType.Pinned); // Inform the garbage collector of this unmanaged allocation so it can schedule // the next GC round more intelligently GC.AddMemoryPressure(Buffer.ByteLength(result)); return result; } public void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount) { // If there isn't an unmanaged array allocated yet, do it now if (m_indicesPtr == IntPtr.Zero) { int[] indexList = getIndexListAsInt(); m_indexCount = indexList.Length; int byteCount = m_indexCount * sizeof(int); m_indicesPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(byteCount); System.Runtime.InteropServices.Marshal.Copy(indexList, 0, m_indicesPtr, m_indexCount); } // A triangle is 3 ints (indices) triStride = 3 * sizeof(int); indices = m_indicesPtr; indexCount = m_indexCount; } public void releasePinned() { if (m_pinnedVertexes.IsAllocated) m_pinnedVertexes.Free(); if (m_pinnedIndex.IsAllocated) m_pinnedIndex.Free(); if (m_verticesPtr != IntPtr.Zero) { System.Runtime.InteropServices.Marshal.FreeHGlobal(m_verticesPtr); m_verticesPtr = IntPtr.Zero; } if (m_indicesPtr != IntPtr.Zero) { System.Runtime.InteropServices.Marshal.FreeHGlobal(m_indicesPtr); m_indicesPtr = IntPtr.Zero; } } /// /// frees up the source mesh data to minimize memory - call this method after calling get*Locked() functions /// public void releaseSourceMeshData() { m_triangles = null; m_vertices = null; } public void Append(IMesh newMesh) { if (m_pinnedIndex.IsAllocated || m_pinnedVertexes.IsAllocated || m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero) throw new NotSupportedException("Attempt to Append to a pinned Mesh"); if (!(newMesh is Mesh)) return; foreach (Triangle t in ((Mesh)newMesh).m_triangles) Add(t); } // Do a linear transformation of mesh. public void TransformLinear(float[,] matrix, float[] offset) { if (m_pinnedIndex.IsAllocated || m_pinnedVertexes.IsAllocated || m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero) throw new NotSupportedException("Attempt to TransformLinear a pinned Mesh"); foreach (Vertex v in m_vertices.Keys) { if (v == null) continue; float x, y, z; x = v.X*matrix[0, 0] + v.Y*matrix[1, 0] + v.Z*matrix[2, 0]; y = v.X*matrix[0, 1] + v.Y*matrix[1, 1] + v.Z*matrix[2, 1]; z = v.X*matrix[0, 2] + v.Y*matrix[1, 2] + v.Z*matrix[2, 2]; v.X = x + offset[0]; v.Y = y + offset[1]; v.Z = z + offset[2]; } } public void DumpRaw(String path, String name, String title) { if (path == null) return; String fileName = name + "_" + title + ".raw"; String completePath = System.IO.Path.Combine(path, fileName); StreamWriter sw = new StreamWriter(completePath); foreach (Triangle t in m_triangles) { String s = t.ToStringRaw(); sw.WriteLine(s); } sw.Close(); } public void TrimExcess() { m_triangles.TrimExcess(); } } }