public PrimDisplayData ExtractPrimMesh(SceneObjectPart part, GroupLoader.LoaderParams parms, HashSet <UUID> fullPermTextures)
        {
            Primitive prim = part.Shape.ToOmvPrimitive(part.OffsetPosition, part.RotationOffset);

            //always generate at scale 1.0 and export the true scale for each part
            prim.Scale = new Vector3(1, 1, 1);

            FacetedMesh mesh;

            try
            {
                if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
                {
                    if (prim.Sculpt.Type != SculptType.Mesh)
                    { // Regular sculptie
                        Image img = null;
                        if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
                        {
                            return(null);
                        }

                        mesh = _renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
                        img.Dispose();
                    }
                    else
                    { // Mesh
                        var meshAsset = _stratus.RequestAssetSync(prim.Sculpt.SculptTexture);
                        if (!FacetedMesh.TryDecodeFromAsset(prim, new OpenMetaverse.Assets.AssetMesh(prim.Sculpt.SculptTexture, meshAsset.Data), DetailLevel.Highest, out mesh))
                        {
                            return(null);
                        }
                    }
                }
                else
                {
                    mesh = _renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
                }
            }
            catch
            {
                return(null);
            }

            // Create a FaceData struct for each face that stores the 3D data
            // in a OpenGL friendly format
            for (int j = 0; j < mesh.Faces.Count; j++)
            {
                Face face = mesh.Faces[j];
                PrimFace.FaceData data = new PrimFace.FaceData();

                // Vertices for this face
                data.Vertices = new float[face.Vertices.Count * 3];
                data.Normals  = new float[face.Vertices.Count * 3];
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
                    data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
                    data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;

                    data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
                    data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
                    data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
                }

                // Indices for this face
                data.Indices = face.Indices.ToArray();

                // Texture transform for this face
                Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);

                //not sure where this bug is coming from, but in order for sculpt textures
                //to line up, we need to flip V here
                if (prim.Sculpt != null && prim.Sculpt.Type != SculptType.None && prim.Sculpt.Type != SculptType.Mesh)
                {
                    teFace.RepeatV *= -1.0f;
                }

                _renderer.TransformTexCoords(face.Vertices, face.Center, teFace, prim.Scale);

                // Texcoords for this face
                data.TexCoords = new float[face.Vertices.Count * 2];
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
                    data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
                }

                if (((parms.Checks & LoaderChecks.TexturesMustBeFullPerm) != 0))
                {
                    if (teFace.TextureID != UUID.Zero && !fullPermTextures.Contains(teFace.TextureID))
                    {
                        teFace.TextureID = UUID.Zero;
                    }
                }

                //store the actual texture
                data.TextureInfo = new PrimFace.TextureInfo {
                    TextureID = teFace.TextureID
                };

                // Set the UserData for this face to our FaceData struct
                face.UserData = data;
                mesh.Faces[j] = face;
            }


            var pos = part.IsRootPart() ? part.RawGroupPosition : part.OffsetPosition;

            return(new PrimDisplayData {
                Mesh = mesh, IsRootPrim = part.IsRootPart(),
                OffsetPosition = pos, OffsetRotation = part.RotationOffset,
                Scale = part.Scale,
                ShapeHash = _objectHasher.GetMeshShapeHash(part.Shape),
                MaterialHash = _objectHasher.GetMeshMaterialHash(prim),
                LinkNum = part.LinkNum
            });
        }
        public GroupDisplayData LoadFromXML(string xmlData, GroupLoader.LoaderParams parms)
        {
            SceneObjectGroup sog = SceneXmlLoader.DeserializeGroupFromXml2(xmlData);

            return(GroupDisplayDataFromSOG(UUID.Zero, parms, sog, null, string.Empty, null));
        }