Пример #1
0
        public void RoundTripTest()
        {
            var tests = new[]
            {
                new XbimVector3D(0.0749087609620539, 0.264167604633194, 0.961563390626687),
                new XbimVector3D(-0.0535755113215756, 0.316639902069201, 0.947031592400295),
                new XbimVector3D(-0.0403690470743153, -0.0845001599207948, 0.995605375142015),
                new XbimVector3D(-0.170389413996118, 0.321003309980549, 0.931624560957681)
            };

            foreach (var vec in tests)
            {
                Debug.WriteLine(vec);

                var pack = new XbimPackedNormal(vec);
                var roundVec = pack.Normal;
                Debug.WriteLine(roundVec);

                var a = vec.CrossProduct(roundVec);
                var x = Math.Abs(a.Length);
                var y = vec.DotProduct(roundVec);
                var angle = Math.Atan2(x, y);
                if (angle > 0.1)
                    Debug.WriteLine("vector: {0}, angle: {1:F3}", vec, angle*180.0/Math.PI);
                Assert.IsTrue(angle < 0.13);    
            }
            

        }
Пример #2
0
        /// <summary>
        /// returns the normal for a specific point at a specific index on the face
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public XbimVector3D NormalAt(int index)
        {
            var indexOffset = _offsetStart + sizeof(int); //offset + trianglecount

            if (IsPlanar)                                 //no matter what you send in for the index you will get the same value because it is planar
            {
                var u  = _array[indexOffset];
                var v  = _array[indexOffset + 1];
                var pn = new XbimPackedNormal(u, v);
                return(pn.Normal);
            }
            else
            {
                var indexSpan    = _sizeofIndex + 2;
                int normalOffset = indexOffset + (index * indexSpan) + _sizeofIndex;
                var u            = _array[normalOffset];
                var v            = _array[normalOffset + 1];
                var pn           = new XbimPackedNormal(u, v);
                return(pn.Normal);
            }
        }
Пример #3
0
        public void PackedNormalTests()
        {
            var vectors = (List<XbimVector3D>)UniformPointsOnSphere(100);

            foreach (var v in vectors)
            {
                var packed = new XbimPackedNormal(v);
                var v2 = packed.Normal;
                var a = v.CrossProduct(v2);
                var x = Math.Abs(a.Length);
                var y = v.DotProduct(v2);
                var angle = Math.Atan2(x, y);
                if (angle > 0.1) 
                    Debug.WriteLine("vector: {0}, angle: {1:F3}", v, angle);
                Assert.IsTrue(angle < 0.13);
            }

            //text axis aligned normals (this should be much more precise)
            var vArray = new[]
            {
                new XbimVector3D(0, 0, 1), 
                new XbimVector3D(0, 0, -1), 
                new XbimVector3D(0, 1, 0), 
                new XbimVector3D(0, -1, 0), 
                new XbimVector3D(1, 0, 0), 
                new XbimVector3D(-1, 0, 0)
            };
            foreach (var v in vArray)
            {
                var packed = new XbimPackedNormal(v);
                var v2 = packed.Normal;
                var a = v.CrossProduct(v2);
                var x = Math.Abs(a.Length);
                var y = v.DotProduct(v2);
                var angle = Math.Atan2(x, y);
                Assert.IsTrue(angle < 1e-10);
            }

        }
 internal void AddNormal(XbimPackedNormal xbimPackedNormal)
 {
     _normals.Add(xbimPackedNormal);
 }
        public static void Read(this MeshGeometry3D m3D, string shapeData, XbimMatrix3D? transform = null)
        {
            transform = null;
            RotateTransform3D qrd = new RotateTransform3D();
            Matrix3D? matrix3D = null;
            if (transform.HasValue)
            {
                XbimQuaternion xq = transform.Value.GetRotationQuaternion();
                qrd.Rotation = new QuaternionRotation3D(new Quaternion(xq.X, xq.Y, xq.Z, xq.W));
                matrix3D = transform.Value.ToMatrix3D();
            }
            
            using (StringReader sr = new StringReader(shapeData))
            {
                int version = 1;
                List<Point3D> vertexList = new List<Point3D>(512); //holds the actual unique positions of the vertices in this data set in the mesh
                List<Vector3D> normalList = new List<Vector3D>(512); //holds the actual unique normals of the vertices in this data set in the mesh

                List<Point3D> positions = new List<Point3D>(1024); //holds the actual positions of the vertices in this data set in the mesh
                List<Vector3D> normals = new List<Vector3D>(1024); //holds the actual normals of the vertices in this data set in the mesh
                List<int> triangleIndices = new List<int>(2048);
                String line;
                // Read and display lines from the data until the end of
                // the data is reached.

                while ((line = sr.ReadLine()) != null)
                {

                    string[] tokens = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (tokens.Length > 0) //we need a command
                    {
                        string command = tokens[0].Trim().ToUpper();
                        switch (command)
                        {
                            case "P":
                                vertexList = new List<Point3D>(512);
                                normalList = new List<Vector3D>(512);
                                if (tokens.Length > 0)
                                    version = Int32.Parse(tokens[1]);
                                break;
                            case "V": //process vertices
                                for (int i = 1; i < tokens.Length; i++)
                                {
                                    string[] xyz = tokens[i].Split(',');
                                    Point3D p = new Point3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                                                      Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                                                      Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                    if (matrix3D.HasValue)
                                        p = matrix3D.Value.Transform(p);
                                    vertexList.Add(p);
                                }
                                break;
                            case "N": //processes normals
                                for (int i = 1; i < tokens.Length; i++)
                                {
                                    string[] xyz = tokens[i].Split(',');
                                    Vector3D v = new Vector3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                                                       Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                                                       Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                    normalList.Add(v);
                                }
                                break;
                            case "T": //process triangulated meshes
                                Vector3D currentNormal = new Vector3D(0,0,0);
                                //each time we start a new mesh face we have to duplicate the vertices to ensure that we get correct shading of planar and non planar faces
                                var writtenVertices = new Dictionary<int, int>();

                                for (int i = 1; i < tokens.Length; i++)
                                {
                                    string[] indices = tokens[i].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                    if (indices.Length != 3) throw new Exception("Invalid triangle definition");
                                    for (int t = 0; t < 3; t++)
                                    {
                                        string[] indexNormalPair = indices[t].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                                        if (indexNormalPair.Length > 1) //we have a normal defined
                                        {
                                            if (version == 1)
                                            {
                                                string normalStr = indexNormalPair[1].Trim();
                                                switch (normalStr)
                                                {
                                                    case "F": //Front
                                                        currentNormal = new Vector3D(0, -1, 0);
                                                        break;
                                                    case "B": //Back
                                                        currentNormal = new Vector3D(0, 1, 0);
                                                        break;
                                                    case "L": //Left
                                                        currentNormal = new Vector3D(-1, 0, 0);
                                                        break;
                                                    case "R": //Right
                                                        currentNormal = new Vector3D(1, 0, 0);
                                                        break;
                                                    case "U": //Up
                                                        currentNormal = new Vector3D(0, 0, 1);
                                                        break;
                                                    case "D": //Down
                                                        currentNormal = new Vector3D(0, 0, -1);
                                                        break;
                                                    default: //it is an index number
                                                        int normalIndex = int.Parse(indexNormalPair[1]);
                                                        currentNormal = normalList[normalIndex];
                                                        break;
                                                }
                                            }
                                            else //we have support for packed normals
                                            {
                                                var packedNormal = new XbimPackedNormal(ushort.Parse(indexNormalPair[1]));
                                                var n = packedNormal.Normal;
                                                currentNormal = new Vector3D(n.X, n.Y, n.Z);
                                            }
                                            if (matrix3D.HasValue)
                                            { 
                                                currentNormal = qrd.Transform(currentNormal);
                                            }
                                        }

                                        //now add the index
                                        int index = int.Parse(indexNormalPair[0]);

                                        int alreadyWrittenAt; //in case it is the first mesh
                                        if (!writtenVertices.TryGetValue(index, out alreadyWrittenAt)) //if we haven't  written it in this mesh pass, add it again unless it is the first one which we know has been written
                                        {
                                            //all vertices will be unique and have only one normal
                                            writtenVertices.Add(index, positions.Count);
                                            triangleIndices.Add(positions.Count + m3D.TriangleIndices.Count);
                                            positions.Add(vertexList[index]);
                                            normals.Add(currentNormal);
                                        }
                                        else //just add the index reference
                                        {
                                            if(normals[alreadyWrittenAt] == currentNormal)
                                                triangleIndices.Add(alreadyWrittenAt);
                                            else //we need another
                                            {
                                                triangleIndices.Add(positions.Count + m3D.TriangleIndices.Count);
                                                positions.Add(vertexList[index]);
                                                normals.Add(currentNormal);
                                            }
                                        }
                                    }
                                }
                                break;
                            case "F": //skip faces for now, can be used to draw edges
                                break;
                            default:
                                throw new Exception("Invalid Geometry Command");

                        }
                    }
                }
                m3D.Positions = new Point3DCollection(m3D.Positions.Concat(positions)); //we do this for wpf performance issues
                m3D.Normals = new Vector3DCollection(m3D.Normals.Concat(normals)); //we do this for wpf performance issues
                m3D.TriangleIndices = new Int32Collection(m3D.TriangleIndices.Concat(triangleIndices)); //we do this for wpf performance issues
            }
        }
Пример #6
0
        /// <summary>
        /// Reads an ascii string of Xbim mesh geometry data
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Read(String data, XbimMatrix3D? trans = null)
        {
            var version = 2; //we are at at least verson 2 now
            var q = new XbimQuaternion();
            if (trans.HasValue)
                q = trans.Value.GetRotationQuaternion();
            using (var sr = new StringReader(data))
            {

                var vertexList = new List<XbimPoint3D>(); //holds the actual positions of the vertices in this data set in the mesh
                var normalList = new List<XbimVector3D>(); //holds the actual normals of the vertices in this data set in the mesh
                String line;
                // Read and display lines from the data until the end of
                // the data is reached.

                while ((line = sr.ReadLine()) != null)
                {

                    var tokens = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (tokens.Length > 1) //we need a command and some data
                    {
                        var command = tokens[0].Trim().ToUpper();
                        switch (command)
                        {
                            case "P":
                                version = Int32.Parse(tokens[1]);
                                var pointCount = 512;
                                //var faceCount = 128;
                                //var triangleCount = 256;
                                var normalCount = 512;
                                if (tokens.Length > 1) pointCount = Int32.Parse(tokens[2]);
                               // if (tokens.Length > 2) faceCount = Int32.Parse(tokens[3]);
                               // if (tokens.Length > 3) triangleCount = Int32.Parse(tokens[4]);
                                //version 2 of the string format uses packed normals
                                if (version < 2 && tokens.Length > 4) normalCount = Int32.Parse(tokens[5]);
                                vertexList = new List<XbimPoint3D>(pointCount);
                                normalList = new List<XbimVector3D>(normalCount);
                                break;
                            case "V": //process vertices
                                for (var i = 1; i < tokens.Length; i++)
                                {
                                    var xyz = tokens[i].Split(',');
                                    var p = new XbimPoint3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                                                      Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                                                      Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                    if (trans.HasValue)
                                        p = trans.Value.Transform(p);
                                    vertexList.Add(p);
                                }
                                break;
                            case "N": //processes normals
                                for (var i = 1; i < tokens.Length; i++)
                                {
                                    var xyz = tokens[i].Split(',');
                                    var v = new XbimVector3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                                                       Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                                                       Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                    normalList.Add(v);
                                }
                                break;
                            case "T": //process triangulated meshes
                                var currentNormal = XbimVector3D.Zero;
                                //each time we start a new mesh face we have to duplicate the vertices to ensure that we get correct shading of planar and non planar faces
                                var writtenVertices = new Dictionary<int, int>();

                                for (var i = 1; i < tokens.Length; i++)
                                {
                                    var triangleIndices = tokens[i].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                    if (triangleIndices.Length != 3) throw new Exception("Invalid triangle definition");
                                    for (var t = 0; t < 3; t++)
                                    {
                                        var indexNormalPair = triangleIndices[t].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                                        if (indexNormalPair.Length > 1) //we have a normal defined
                                        {
                                            var normalStr = indexNormalPair[1].Trim();
                                            if (version < 2)
                                            {
                                                switch (normalStr)
                                                {
                                                    case "F": //Front
                                                        currentNormal = new XbimVector3D(0, -1, 0);
                                                        break;
                                                    case "B": //Back
                                                        currentNormal = new XbimVector3D(0, 1, 0);
                                                        break;
                                                    case "L": //Left
                                                        currentNormal = new XbimVector3D(-1, 0, 0);
                                                        break;
                                                    case "R": //Right
                                                        currentNormal = new XbimVector3D(1, 0, 0);
                                                        break;
                                                    case "U": //Up
                                                        currentNormal = new XbimVector3D(0, 0, 1);
                                                        break;
                                                    case "D": //Down
                                                        currentNormal = new XbimVector3D(0, 0, -1);
                                                        break;
                                                    default: //it is an index number
                                                        var normalIndex = int.Parse(indexNormalPair[1]);
                                                        currentNormal = normalList[normalIndex];
                                                        break;
                                                }
                                            }
                                            else
                                            {
                                                var normalIndex = ushort.Parse(indexNormalPair[1]);
                                                var packedNormal = new XbimPackedNormal(normalIndex);
                                                currentNormal = packedNormal.Normal;
                                            }
                                            if (trans.HasValue)
                                            {
                                                XbimVector3D v;
                                                XbimQuaternion.Transform(ref currentNormal, ref q, out v);
                                                currentNormal = v;

                                            }
                                        }

                                        //now add the index
                                        var index = int.Parse(indexNormalPair[0]);

                                        int alreadyWrittenAt;
                                        if (!writtenVertices.TryGetValue(index, out alreadyWrittenAt)) //if we haven't  written it in this mesh pass, add it again unless it is the first one which we know has been written
                                        {
                                            //all vertices will be unique and have only one normal
                                            writtenVertices.Add(index, PositionCount);
                                            TriangleIndices.Add(PositionCount);
                                            Positions.Add(vertexList[index]);
                                            Normals.Add(currentNormal);
                                        }
                                        else //just add the index reference
                                        {
                                            TriangleIndices.Add(alreadyWrittenAt);
                                        }
                                    }
                                }

                                break;
                            case "F":
                                break;
                            default:
                                throw new Exception("Invalid Geometry Command");

                        }
                    }
                }
            }
            return true;
        }
 public PointAndNormal(int index, XbimPackedNormal n)
 {
     Index  = index;
     Normal = n;
 }
Пример #8
0
        public bool Read(string data, XbimMatrix3D? tr = null)
        {
            int version = 1;
            using (var sr = new StringReader(data))
            {
                Matrix3D? m3D = null;
                var r = new RotateTransform3D();
                if (tr.HasValue) //set up the windows media transforms
                {
                    m3D = new Matrix3D(tr.Value.M11, tr.Value.M12, tr.Value.M13, tr.Value.M14,
                                                  tr.Value.M21, tr.Value.M22, tr.Value.M23, tr.Value.M24,
                                                  tr.Value.M31, tr.Value.M32, tr.Value.M33, tr.Value.M34,
                                                  tr.Value.OffsetX, tr.Value.OffsetY, tr.Value.OffsetZ, tr.Value.M44);
                    r = tr.Value.GetRotateTransform3D();
                }
                var vertexList = new Point3DCollection(); //holds the actual positions of the vertices in this data set in the mesh
                var normalList = new Vector3DCollection(); //holds the actual normals of the vertices in this data set in the mesh
                string line;
                // Read and display lines from the data until the end of
                // the data is reached.
                while ((line = sr.ReadLine()) != null)
                {
                    var tokens = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (tokens.Length <= 1) 
                        continue;
                    var command = tokens[0].Trim().ToUpper();
                    switch (command)
                    {
                        case "P":
                            var pointCount = 512;
// ReSharper disable once NotAccessedVariable
                            var faceCount = 128;
// ReSharper disable once NotAccessedVariable
                            var triangleCount = 256;
                            var normalCount = 512;
                            if (tokens.Length > 0)
                                version = Int32.Parse(tokens[1]);
                            if (tokens.Length > 1)
                                pointCount = Int32.Parse(tokens[2]);
                            // ReSharper disable once RedundantAssignment
                            if (tokens.Length > 2) 
                                faceCount = Int32.Parse(tokens[3]);
// ReSharper disable once RedundantAssignment
                            if (tokens.Length > 3) 
                                triangleCount = Int32.Parse(tokens[4]);
                            if (tokens.Length > 4) 
                                normalCount = Math.Max(Int32.Parse(tokens[5]),pointCount); //can't really have less normals than points
                            vertexList = new Point3DCollection(pointCount);
                            normalList = new Vector3DCollection(normalCount);
                            //for efficienciency avoid continual regrowing
                            //this.Mesh.Positions = this.Mesh.Positions.GrowBy(pointCount);
                            //this.Mesh.Normals = this.Mesh.Normals.GrowBy(normalCount);
                            //this.Mesh.TriangleIndices = this.Mesh.TriangleIndices.GrowBy(triangleCount*3);
                            break;
                        case "F":
                            break;
                        case "V": //process vertices
                            for (int i = 1; i < tokens.Length; i++)
                            {
                                string[] xyz = tokens[i].Split(',');
                                Point3D p = new Point3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                    Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                    Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                if (m3D.HasValue)
                                    p = m3D.Value.Transform(p);
                                vertexList.Add(p);
                            }
                            break;
                        case "N": //processes normals
                            for (int i = 1; i < tokens.Length; i++)
                            {
                                string[] xyz = tokens[i].Split(',');
                                Vector3D v = new Vector3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture),
                                    Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture),
                                    Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture));
                                normalList.Add(v);
                            }
                            break;
                        case "T": //process triangulated meshes
                            var currentNormal = new Vector3D();
                            //each time we start a new mesh face we have to duplicate the vertices to ensure that we get correct shading of planar and non planar faces
                            var writtenVertices = new Dictionary<int, int>();

                            for (var i = 1; i < tokens.Length; i++)
                            {
                                var triangleIndices = tokens[i].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                if (triangleIndices.Length != 3) throw new Exception("Invalid triangle definition");
                                for (var t = 0; t < 3; t++)
                                {
                                    var indexNormalPair = triangleIndices[t].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                                    if (indexNormalPair.Length > 1) //we have a normal defined
                                    {
                                        if (version == 1)
                                        {
                                            var normalStr = indexNormalPair[1].Trim();
                                            switch (normalStr)
                                            {
                                                case "F": //Front
                                                    currentNormal = new Vector3D(0, -1, 0);
                                                    break;
                                                case "B": //Back
                                                    currentNormal = new Vector3D(0, 1, 0);
                                                    break;
                                                case "L": //Left
                                                    currentNormal = new Vector3D(-1, 0, 0);
                                                    break;
                                                case "R": //Right
                                                    currentNormal = new Vector3D(1, 0, 0);
                                                    break;
                                                case "U": //Up
                                                    currentNormal = new Vector3D(0, 0, 1);
                                                    break;
                                                case "D": //Down
                                                    currentNormal = new Vector3D(0, 0, -1);
                                                    break;
                                                default: //it is an index number
                                                    int normalIndex = int.Parse(indexNormalPair[1]);
                                                    currentNormal = normalList[normalIndex];
                                                    break;
                                            }
                                        }
                                        else //we have support for packed normals
                                        {
                                            var packedNormal = new XbimPackedNormal(ushort.Parse(indexNormalPair[1]));
                                            var n = packedNormal.Normal;
                                            currentNormal = new Vector3D(n.X, n.Y, n.Z);
                                        }
                                        if (tr.HasValue)
                                        {
                                            currentNormal = r.Transform(currentNormal);
                                        }
                                    }
                                    //now add the index
                                    var index = int.Parse(indexNormalPair[0]);

                                    int alreadyWrittenAt; //in case it is the first mesh
                                    if (!writtenVertices.TryGetValue(index, out alreadyWrittenAt)) //if we haven't  written it in this mesh pass, add it again unless it is the first one which we know has been written
                                    {
                                        //all vertices will be unique and have only one normal
                                        writtenVertices.Add(index, PositionCount);

                                        _unfrozenIndices.Add(PositionCount);
                                        _unfrozenPositions.Add(vertexList[index]);
                                        _unfrozenNormals.Add(currentNormal);

                                    }
                                    else //just add the index reference
                                    {
                                        if (_unfrozenNormals[alreadyWrittenAt] == currentNormal)
                                            _unfrozenIndices.Add(alreadyWrittenAt);
                                        else //we need another
                                        {
                                            _unfrozenIndices.Add(PositionCount);
                                            _unfrozenPositions.Add(vertexList[index]);
                                            _unfrozenNormals.Add(currentNormal);
                                        }
                                    }
                                   
                                }
                            }

                            break;
                        default:
                            throw new Exception("Invalid Geometry Command");
                    }
                }
            }
            return true;
        }
Пример #9
0
        private IXbimShapeGeometryData MeshPolyhedronBinary(IEnumerable<IEnumerable<IfcFace>> facesList, int entityLabel, float precision)
        {
            IXbimShapeGeometryData shapeGeometry = new XbimShapeGeometry();
            shapeGeometry.Format = (byte)XbimGeometryType.PolyhedronBinary;

            using (var ms = new MemoryStream(0x4000))
            using (var binaryWriter = new BinaryWriter(ms))
            {
                var faceLists = facesList as IList<IEnumerable<IfcFace>> ?? facesList.ToList();
                var triangulations = new List<XbimTriangulatedMesh>(faceLists.Count);
                foreach (var faceList in faceLists)
                    triangulations.Add(TriangulateFaces(faceList, entityLabel, precision));

                // Write out header
                uint verticesCount = 0;
                uint triangleCount = 0;
                uint facesCount = 0;
                var boundingBox = XbimRect3D.Empty;
                foreach (var triangulatedMesh in triangulations)
                {
                    verticesCount += triangulatedMesh.VertexCount;
                    triangleCount += triangulatedMesh.TriangleCount;
                    facesCount += (uint)triangulatedMesh.Faces.Count;
                    if (boundingBox.IsEmpty) boundingBox = triangulatedMesh.BoundingBox;
                    else boundingBox.Union(triangulatedMesh.BoundingBox);
                }

                binaryWriter.Write((byte)1); //stream format version
            // ReSharper disable once RedundantCast
                binaryWriter.Write((UInt32)verticesCount); //number of vertices
                binaryWriter.Write(triangleCount); //number of triangles

                foreach (var v in triangulations.SelectMany(t=>t.Vertices))
                {
                    binaryWriter.Write(v.X);
                    binaryWriter.Write(v.Y);
                    binaryWriter.Write(v.Z);
                }
                shapeGeometry.BoundingBox = boundingBox.ToFloatArray();
                //now write out the faces

                binaryWriter.Write(facesCount);
                uint verticesOffset = 0;
                int invalidNormal = new XbimPackedNormal(255, 255).ToUnit16();
                foreach (var triangulatedMesh in triangulations)
                {
                    foreach (var faceGroup in triangulatedMesh.Faces)
                    {
                        var numTrianglesInFace = faceGroup.Value.Count;
                        //we need to fix this
                        var planar = invalidNormal != faceGroup.Key; //we have a mesh of faces that all have the same normals at their vertices
                        if (!planar) numTrianglesInFace *= -1; //set flag to say multiple normals

                        binaryWriter.Write((Int32)numTrianglesInFace);

                        bool first = true;
                        foreach (var triangle in faceGroup.Value)
                        {
                            if (planar && first)
                            {
                                triangle[0].PackedNormal.Write(binaryWriter);
                                first = false;
                            }
                            WriteIndex(binaryWriter, (uint)triangle[0].StartVertexIndex + verticesOffset, verticesCount);
                            if (!planar)
                                triangle[0].PackedNormal.Write(binaryWriter);
                            WriteIndex(binaryWriter, (uint)triangle[0].NextEdge.StartVertexIndex + verticesOffset, verticesCount);
                            if (!planar) triangle[0].NextEdge.PackedNormal.Write(binaryWriter);
                            WriteIndex(binaryWriter, (uint)triangle[0].NextEdge.NextEdge.StartVertexIndex + verticesOffset,
                                verticesCount);
                            if (!planar) triangle[0].NextEdge.NextEdge.PackedNormal.Write(binaryWriter);
                        }
                    }
                    verticesOffset += triangulatedMesh.VertexCount;
                }
                binaryWriter.Flush();
                shapeGeometry.ShapeData = ms.ToArray();
            }
            return shapeGeometry;
        }
 internal void AddNormal(XbimPackedNormal xbimPackedNormal)
 {
    _normals.Add(xbimPackedNormal);
 }