public SnapshotModel(string sFilename)
        {
            int nverts   = 0;
            int nfaces   = 0;
            int nvertsel = 0;
            //int nedgesel = 0;
            //int nfacesel = 0;
            int nmods = 0;

            Vec3f[] coords;

            int[]      selected;
            Modifier[] modifiers;

            using (Stream s = new FileStream(sFilename, FileMode.Open))
            {
                string plyline = FileIOFunctions.ReadTextString(s);
                if (plyline != "ply")
                {
                    throw new ArgumentException("TVModel.LoadObjectFromPLY: Specified file is not .ply file");
                }

                bool header = true;
                while (header)
                {
                    string cmd = FileIOFunctions.ReadTextString(s);
                    switch (cmd)
                    {
                    case "format":
                    case "comment":
                    case "property":
                        while (s.ReadByte() != 10)
                        {
                            ;                            // ignore the rest of the line
                        }
                        break;

                    case "element":
                        string variable = FileIOFunctions.ReadTextString(s);
                        int    val      = FileIOFunctions.ReadTextInteger(s);
                        switch (variable)
                        {
                        case "vertex": nverts = val; break;

                        case "face": nfaces = val; break;

                        case "selected":
                            nvertsel = val;
                            FileIOFunctions.ReadTextInteger(s);         // nedgesel =
                            FileIOFunctions.ReadTextInteger(s);         // nfacesel =
                            break;

                        case "modifiers": nmods = val; break;

                        default: throw new Exception("TVModel.LoadObjectFromPLY: Unhandled element type " + variable);
                        }
                        break;

                    case "end_header": header = false; break;

                    default: throw new Exception("TVModel.LoadObjectFromPLY: Unhandled command type " + cmd);
                    }
                }

                coords    = new Vec3f[nverts];
                selected  = new int[nvertsel];
                modifiers = new Modifier[nmods];
                groups    = new List <GroupInfo>[] {
                    new List <GroupInfo>(nverts), new List <GroupInfo>(nfaces), new List <GroupInfo>(nfaces), new List <GroupInfo>(nfaces)
                };
                groupselected = new List <bool>[] {
                    new List <bool>(nverts), new List <bool>(nverts), new List <bool>(nverts), new List <bool>(nverts)
                };
                facenormals = new List <Vec3f>[] { new List <Vec3f>(), new List <Vec3f>() };

                // read vert locations and visibility
                for (int i = 0; i < nverts; i++)
                {
                    coords[i] = new Vec3f(FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s));
                    bool v = (FileIOFunctions.ReadTextInteger(s) == 1);
                    groups[0].Add(new GroupInfo(new int[] { i }, v));
                }

                // read inds of edges and faces
                for (int i = 0; i < nfaces; i++)
                {
                    int   count = FileIOFunctions.ReadTextInteger(s);
                    int[] inds  = new int[count];
                    for (int j = 0; j < count; j++)
                    {
                        inds[j] = FileIOFunctions.ReadTextInteger(s);
                    }
                    bool v    = (FileIOFunctions.ReadTextInteger(s) == 1);
                    bool gsel = (FileIOFunctions.ReadTextInteger(s) == 1);
                    groups[count - 1].Add(new GroupInfo(inds, v));
                    groupselected[count - 1].Add(gsel);

                    if (count == 3 || count == 4)
                    {
                        Vec3f v0 = Vec3f.Normalize(coords[inds[0]] - coords[inds[1]]);
                        Vec3f v1 = Vec3f.Normalize(coords[inds[2]] - coords[inds[1]]);
                        Vec3f vn = v1 ^ v0;
                        if (vn.LengthSqr > 0.00001f)
                        {
                            facenormals[count - 3].Add(Vec3f.Normalize(vn));
                        }
                        else
                        {
                            facenormals[count - 3].Add(new Vec3f());
                        }
                    }
                }

                groups[1].TrimExcess();
                groups[2].TrimExcess();
                groups[3].TrimExcess();
                groupselected[1].TrimExcess();
                groupselected[2].TrimExcess();
                groupselected[3].TrimExcess();

                // read inds of selected verts
                for (int i = 0; i < nvertsel; i++)
                {
                    selected[i] = FileIOFunctions.ReadTextInteger(s);
                }

                // read modifiers
                for (int i = 0; i < nmods; i++)
                {
                    string modifier = FileIOFunctions.ReadTextString(s);
                    switch (modifier)
                    {
                    case "mirror":
                        modifiers[i] = new ModifierMirror()
                        {
                            name           = FileIOFunctions.ReadTextQuotedString(s),
                            usex           = (FileIOFunctions.ReadTextInteger(s) == 1),
                            usey           = (FileIOFunctions.ReadTextInteger(s) == 1),
                            usez           = (FileIOFunctions.ReadTextInteger(s) == 1),
                            mergethreshold = FileIOFunctions.ReadTextFloat(s)
                        };
                        break;

                    /*case "subdiv":
                     *  modifiers[i] = new ModifierSubDiv() {
                     *      name = FileIOFunctions.ReadTextQuotedString(s),
                     *      levels = FileIOFunctions.ReadTextInteger(s)
                     *  };
                     *  break;
                     * case "boolop":
                     *  modifiers[i] = new ModifierBoolOp
                     *      (
                     *       FileIOFunctions.ReadTextQuotedString(s),
                     *       ModifierBoolOp.StringToBoolOp( FileIOFunctions.ReadTextString(s) ),
                     *       FileIOFunctions.ReadTextString(s)
                     *       );
                     *  break;
                     * case "solidify":
                     *  modifiers[i] = new ModifierSolidify
                     *      (
                     *       FileIOFunctions.ReadTextString(s),
                     *       FileIOFunctions.ReadTextFloat(s),
                     *       FileIOFunctions.ReadTextFloat(s),
                     *       FileIOFunctions.ReadTextFloat(s),
                     *       FileIOFunctions.ReadTextFloat(s),
                     *       FileIOFunctions.ReadTextFloat(s),
                     *       ( FileIOFunctions.ReadTextInteger(s) == 1 ),
                     *       ( FileIOFunctions.ReadTextInteger(s) == 1 ),
                     *       ( FileIOFunctions.ReadTextInteger(s) == 1 )
                     *       );
                     *  break;*/
                    default: throw new Exception("TVModel.LoadObjectFromPLY: Unknown modifier " + modifier);
                    }
                }
            }

            this.verts     = coords;
            this.selinds   = selected;
            this.modifiers = modifiers;

            this.nverts = nverts;
            FillIsVertSelected();
            CalcVertexNormals();
        }
        public SnapshotScene(string sPLYFilename, int timeindex, string command, string opts, SnapshotScene prev, bool nochange, bool cmdobjlist)
        {
            this.file      = MiscFileIO.GetFileNameOnly(sPLYFilename);
            this.timeindex = timeindex;
            this.command   = command;
            this.opts      = opts;
            this.prevscene = prev;

            cselected = 0;
            cedited   = 0;

            string[] objnames;
            bool[]   objvisibles;
            bool[]   objselecteds;
            bool[]   objactives;
            bool[]   objedits;
            string[] objplyfilenames;

            using (Stream s = new FileStream(sPLYFilename, FileMode.Open))
            {
                string plyline = FileIOFunctions.ReadTextString(s);
                if (plyline != "ply")
                {
                    throw new ArgumentException("SnapshotScene: Specified file is not .ply file");
                }

                ncameras = 0;
                nmodels  = 0;
                bool header = true;
                while (header)
                {
                    string cmd = FileIOFunctions.ReadTextString(s);
                    switch (cmd)
                    {
                    case "format":
                    case "property":
                        while (s.ReadByte() != 10)
                        {
                            ;                            // ignore the rest of the line
                        }
                        break;

                    case "comment":
                        string str = FileIOFunctions.ReadTextLine(s);
                        if (str.StartsWith("Created"))
                        {
                            switch (str.Split(new char[] { ' ' })[2])
                            {
                            case "Blender": ApplicationType = ApplicationTypes.BLENDER; break;
                            }
                        }
                        break;

                    case "element":
                        string variable = FileIOFunctions.ReadTextString(s);
                        int    val      = FileIOFunctions.ReadTextInteger(s);
                        switch (variable)
                        {
                        case "views": ncameras = val; break;

                        case "objects": nmodels = val; break;

                        default: throw new Exception("SnapshotScene: Unhandled element type " + variable);
                        }
                        break;

                    case "end_header": header = false; break;

                    default: throw new Exception("SnapshotScene: Unhandled command type " + cmd);
                    }
                }

                if (ApplicationType == ApplicationTypes.UNKNOWN)
                {
                    throw new Exception("SnapshotScene: PLY was created by an unknown application");
                }

                cameras = new CameraProperties[ncameras];
                for (int i = 0; i < ncameras; i++)
                {
                    // read viewing info
                    Vec3f  loc = new Vec3f(FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s));
                    float  w   = FileIOFunctions.ReadTextFloat(s);
                    float  x   = FileIOFunctions.ReadTextFloat(s);
                    float  y   = FileIOFunctions.ReadTextFloat(s);
                    float  z   = FileIOFunctions.ReadTextFloat(s);
                    Quatf  qua = (new Quatf(w, x, y, z)).Normalize();
                    float  dis = FileIOFunctions.ReadTextFloat(s);
                    String per = FileIOFunctions.ReadTextString(s);
                    cameras[i] = new CameraProperties(loc, qua, dis, (per == "ORTHO"));
                }

                int istart = 0, iend = 0, iinc = 0;
                switch (ApplicationType)
                {
                case ApplicationTypes.BLENDER:              // blender writes list of objects "backwards"; new objects are at beginning of list!
                    istart = nmodels - 1;
                    iend   = 0;
                    iinc   = -1;
                    break;

                default: throw new Exception("SnapshotScene: Unimplemented ApplicationType");
                }

                objnames        = new string[nmodels];
                objvisibles     = new bool[nmodels];
                objselecteds    = new bool[nmodels];
                objactives      = new bool[nmodels];
                objedits        = new bool[nmodels];
                objplyfilenames = new string[nmodels];

                for (int i = istart; i != iend + iinc; i += iinc)
                {
                    objnames[i]        = FileIOFunctions.ReadTextQuotedString(s);
                    objvisibles[i]     = (FileIOFunctions.ReadTextInteger(s) == 1);
                    objselecteds[i]    = (FileIOFunctions.ReadTextInteger(s) == 1);
                    objactives[i]      = (FileIOFunctions.ReadTextInteger(s) == 1);
                    objedits[i]        = (FileIOFunctions.ReadTextInteger(s) == 1);
                    objplyfilenames[i] = FileIOFunctions.ReadTextString(s);

                    if (objselecteds[i])
                    {
                        cselected++;
                    }
                    if (objedits[i])
                    {
                        cedited++;
                    }
                }
            }

            if (cedited > 1)
            {
                throw new Exception("more than one object being edited");
            }

            bool loadall = (prev == null || cmdobjlist);                // need to load every object?

            models       = new SnapshotModel[nmodels];
            modelscached = new SnapshotModel[nmodels];
            SnapshotModel[] pmodels = null;
            if (prev != null)
            {
                pmodels = prev.Models;
            }
            for (int i = 0; i < nmodels; i++)
            {
                bool prevsel = !cmdobjlist && (pmodels != null && pmodels[i] != null && pmodels[i].objselected);
                if (loadall || (objselecteds[i] && !nochange) || objselecteds[i] != prevsel || pmodels == null || pmodels[i] == null)
                {
                    models[i]             = new SnapshotModel(objplyfilenames[i]);
                    models[i].objind      = i;
                    models[i].objname     = objnames[i];
                    models[i].objvisible  = objvisibles[i];
                    models[i].objselected = objselecteds[i];
                    models[i].objactive   = objactives[i];
                    models[i].objedit     = objedits[i];
                    modelscached[i]       = models[i];
                }
                else
                {
                    models[i]       = null;
                    modelscached[i] = pmodels[i];
                }
            }
        }