< Summary

Line coverage
99%
Covered lines: 1031
Uncovered lines: 1
Coverable lines: 1032
Total lines: 2627
Line coverage: 99.9%
Branch coverage
95%
Covered branches: 795
Total branches: 836
Branch coverage: 95%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\projects\jeremyansel-media-wavefrontobj\JeremyAnsel.Media.WavefrontObj\JeremyAnsel.Media.WavefrontObj\ObjFileReader.cs

#LineLine coverage
 1// <copyright file="ObjFileReader.cs" company="Jérémy Ansel">
 2// Copyright (c) 2017, 2019 Jérémy Ansel
 3// </copyright>
 4// <license>
 5// Licensed under the MIT license. See LICENSE.txt
 6// </license>
 7
 8#if !NET6_0_OR_GREATER
 9
 10using System.Diagnostics.CodeAnalysis;
 11using System.Globalization;
 12
 13namespace JeremyAnsel.Media.WavefrontObj
 14{
 15    internal static class ObjFileReader
 16    {
 17        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 18        public static ObjFile FromStream(Stream? stream, ObjFileReaderSettings settings)
 19        {
 120            if (stream == null)
 21            {
 122                throw new ArgumentNullException(nameof(stream));
 23            }
 24
 125            var obj = new ObjFile();
 126            var context = new ObjFileReaderContext(obj, settings);
 127            var lineReader = new LineReader();
 28
 129            foreach (string currentLine in lineReader.Read(stream))
 30            {
 131                string[] values = currentLine.Split(LineReader.LineSeparators, StringSplitOptions.RemoveEmptyEntries);
 32
 133                switch (values[0].ToLowerInvariant())
 34                {
 35                    case "v":
 36                        {
 137                            if (values.Length < 4)
 38                            {
 139                                throw new InvalidDataException("A v statement must specify at least 3 values.");
 40                            }
 41
 142                            float x = float.Parse(values[1], CultureInfo.InvariantCulture);
 143                            float y = float.Parse(values[2], CultureInfo.InvariantCulture);
 144                            float z = float.Parse(values[3], CultureInfo.InvariantCulture);
 145                            float w = 1.0f;
 146                            bool hasColor = false;
 147                            float r = 0.0f;
 148                            float g = 0.0f;
 149                            float b = 0.0f;
 150                            float a = 1.0f;
 51
 152                            if (values.Length == 4 || values.Length == 5)
 53                            {
 154                                if (values.Length == 5)
 55                                {
 156                                    w = float.Parse(values[4], CultureInfo.InvariantCulture);
 57                                }
 58                            }
 159                            else if (values.Length == 7 || values.Length == 8)
 60                            {
 161                                hasColor = true;
 162                                r = float.Parse(values[4], CultureInfo.InvariantCulture);
 163                                g = float.Parse(values[5], CultureInfo.InvariantCulture);
 164                                b = float.Parse(values[6], CultureInfo.InvariantCulture);
 65
 166                                if (values.Length == 8)
 67                                {
 168                                    a = float.Parse(values[7], CultureInfo.InvariantCulture);
 69                                }
 70                            }
 71                            else
 72                            {
 173                                throw new InvalidDataException("A v statement has too many values.");
 74                            }
 75
 176                            var v = new ObjVertex();
 177                            v.Position = new ObjVector4(x, y, z, w);
 78
 179                            if (hasColor)
 80                            {
 181                                v.Color = new ObjVector4(r, g, b, a);
 82                            }
 83
 184                            obj.Vertices.Add(v);
 185                            break;
 86                        }
 87
 88                    case "vp":
 89                        {
 190                            if (values.Length < 2)
 91                            {
 192                                throw new InvalidDataException("A vp statement must specify at least 1 value.");
 93                            }
 94
 195                            var v = new ObjVector3();
 196                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 97
 198                            if (values.Length == 2)
 99                            {
 1100                                v.Y = 0.0f;
 1101                                v.Z = 1.0f;
 102                            }
 1103                            else if (values.Length == 3)
 104                            {
 1105                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 1106                                v.Z = 1.0f;
 107                            }
 1108                            else if (values.Length == 4)
 109                            {
 1110                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 1111                                v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 112                            }
 113                            else
 114                            {
 1115                                throw new InvalidDataException("A vp statement has too many values.");
 116                            }
 117
 1118                            obj.ParameterSpaceVertices.Add(v);
 1119                            break;
 120                        }
 121
 122                    case "vn":
 123                        {
 1124                            if (values.Length < 4)
 125                            {
 1126                                throw new InvalidDataException("A vn statement must specify 3 values.");
 127                            }
 128
 1129                            if (values.Length != 4)
 130                            {
 1131                                throw new InvalidDataException("A vn statement has too many values.");
 132                            }
 133
 1134                            var v = new ObjVector3();
 1135                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 1136                            v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 1137                            v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 138
 1139                            obj.VertexNormals.Add(v);
 1140                            break;
 141                        }
 142
 143                    case "vt":
 144                        {
 1145                            if (values.Length < 2)
 146                            {
 1147                                throw new InvalidDataException("A vt statement must specify at least 1 value.");
 148                            }
 149
 1150                            var v = new ObjVector3();
 1151                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 152
 1153                            if (values.Length == 2)
 154                            {
 1155                                v.Y = 0.0f;
 1156                                v.Z = 0.0f;
 157                            }
 1158                            else if (values.Length == 3)
 159                            {
 1160                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 1161                                v.Z = 0.0f;
 162                            }
 1163                            else if (values.Length == 4)
 164                            {
 1165                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 1166                                v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 167                            }
 168                            else
 169                            {
 1170                                throw new InvalidDataException("A vt statement has too many values.");
 171                            }
 172
 1173                            obj.TextureVertices.Add(v);
 1174                            break;
 175                        }
 176
 177                    case "cstype":
 1178                        ObjFileReader.ParseFreeFormType(context, values);
 1179                        break;
 180
 181                    case "deg":
 1182                        if (values.Length < 2)
 183                        {
 1184                            throw new InvalidDataException("A deg statement must specify at least 1 value.");
 185                        }
 186
 1187                        if (values.Length == 2)
 188                        {
 1189                            context.DegreeU = int.Parse(values[1], CultureInfo.InvariantCulture);
 1190                            context.DegreeV = 0;
 191                        }
 1192                        else if (values.Length == 3)
 193                        {
 1194                            context.DegreeU = int.Parse(values[1], CultureInfo.InvariantCulture);
 1195                            context.DegreeV = int.Parse(values[2], CultureInfo.InvariantCulture);
 196                        }
 197                        else
 198                        {
 1199                            throw new InvalidDataException("A deg statement has too many values.");
 200                        }
 201
 202                        break;
 203
 204                    case "bmat":
 205                        {
 1206                            if (values.Length < 2)
 207                            {
 1208                                throw new InvalidDataException("A bmat statement must specify a direction.");
 209                            }
 210
 211                            int d;
 212
 1213                            if (string.Equals(values[1], "u", StringComparison.OrdinalIgnoreCase))
 214                            {
 1215                                d = 1;
 216                            }
 1217                            else if (string.Equals(values[1], "v", StringComparison.OrdinalIgnoreCase))
 218                            {
 1219                                d = 2;
 220                            }
 221                            else
 222                            {
 1223                                throw new InvalidDataException("A bmat statement has an unknown direction.");
 224                            }
 225
 1226                            int count = (context.DegreeU + 1) * (context.DegreeV + 1);
 227
 1228                            if (values.Length != count + 2)
 229                            {
 1230                                throw new InvalidDataException("A bmat statement has too many or too few values.");
 231                            }
 232
 1233                            var matrix = new float[count];
 234
 1235                            for (int i = 0; i < count; i++)
 236                            {
 1237                                matrix[i] = float.Parse(values[2 + i], CultureInfo.InvariantCulture);
 238                            }
 239
 240                            switch (d)
 241                            {
 242                                case 1:
 1243                                    context.BasicMatrixU = matrix;
 1244                                    break;
 245
 246                                case 2:
 1247                                    context.BasicMatrixV = matrix;
 1248                                    break;
 249                            }
 250
 251                            break;
 252                        }
 253
 254                    case "step":
 1255                        if (values.Length < 2)
 256                        {
 1257                            throw new InvalidDataException("A step statement must specify at least 1 value.");
 258                        }
 259
 1260                        if (values.Length == 2)
 261                        {
 1262                            context.StepU = float.Parse(values[1], CultureInfo.InvariantCulture);
 1263                            context.StepV = 1.0f;
 264                        }
 1265                        else if (values.Length == 3)
 266                        {
 1267                            context.StepU = float.Parse(values[1], CultureInfo.InvariantCulture);
 1268                            context.StepV = float.Parse(values[2], CultureInfo.InvariantCulture);
 269                        }
 270                        else
 271                        {
 1272                            throw new InvalidDataException("A step statement has too many values.");
 273                        }
 274
 275                        break;
 276
 277                    case "p":
 278                        {
 1279                            if (values.Length < 2)
 280                            {
 1281                                throw new InvalidDataException("A p statement must specify at least 1 value.");
 282                            }
 283
 1284                            var point = new ObjPoint();
 285
 1286                            for (int i = 1; i < values.Length; i++)
 287                            {
 1288                                point.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 289                            }
 290
 1291                            context.ApplyAttributesToElement(point);
 1292                            context.ApplyAttributesToPolygonalElement(point);
 293
 1294                            obj.Points.Add(point);
 295
 1296                            foreach (var group in context.GetCurrentGroups())
 297                            {
 1298                                group.Points.Add(point);
 299                            }
 300
 301                            break;
 302                        }
 303
 304                    case "l":
 305                        {
 1306                            if (values.Length < 3)
 307                            {
 1308                                throw new InvalidDataException("A l statement must specify at least 2 values.");
 309                            }
 310
 1311                            var line = new ObjLine();
 312
 1313                            for (int i = 1; i < values.Length; i++)
 314                            {
 1315                                line.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 316                            }
 317
 1318                            context.ApplyAttributesToElement(line);
 1319                            context.ApplyAttributesToPolygonalElement(line);
 320
 1321                            obj.Lines.Add(line);
 322
 1323                            foreach (var group in context.GetCurrentGroups())
 324                            {
 1325                                group.Lines.Add(line);
 326                            }
 327
 328                            break;
 329                        }
 330
 331                    case "f":
 332                    case "fo":
 333                        {
 1334                            if (values.Length < 4)
 335                            {
 1336                                throw new InvalidDataException("A f statement must specify at least 3 values.");
 337                            }
 338
 1339                            var face = new ObjFace();
 340
 1341                            for (int i = 1; i < values.Length; i++)
 342                            {
 1343                                face.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 344                            }
 345
 1346                            context.ApplyAttributesToElement(face);
 1347                            context.ApplyAttributesToPolygonalElement(face);
 348
 1349                            obj.Faces.Add(face);
 350
 1351                            foreach (var group in context.GetCurrentGroups())
 352                            {
 1353                                group.Faces.Add(face);
 354                            }
 355
 356                            break;
 357                        }
 358
 359                    case "curv":
 360                        {
 1361                            if (values.Length < 5)
 362                            {
 1363                                throw new InvalidDataException("A curv statement must specify at least 4 values.");
 364                            }
 365
 1366                            var curve = new ObjCurve();
 367
 1368                            curve.StartParameter = float.Parse(values[1], CultureInfo.InvariantCulture);
 1369                            curve.EndParameter = float.Parse(values[2], CultureInfo.InvariantCulture);
 370
 1371                            for (int i = 3; i < values.Length; i++)
 372                            {
 1373                                int v = int.Parse(values[i], CultureInfo.InvariantCulture);
 374
 1375                                if (v == 0)
 376                                {
 1377                                    throw new InvalidDataException("A curv statement contains an invalid vertex index.")
 378                                }
 379
 1380                                if (v < 0)
 381                                {
 1382                                    v = obj.Vertices.Count + v + 1;
 383                                }
 384
 1385                                if (v <= 0 || v > obj.Vertices.Count)
 386                                {
 1387                                    throw new IndexOutOfRangeException();
 388                                }
 389
 1390                                curve.Vertices.Add(v);
 391                            }
 392
 1393                            context.ApplyAttributesToElement(curve);
 1394                            context.ApplyAttributesToFreeFormElement(curve);
 1395                            context.CurrentFreeFormElement = curve;
 396
 1397                            obj.Curves.Add(curve);
 398
 1399                            foreach (var group in context.GetCurrentGroups())
 400                            {
 1401                                group.Curves.Add(curve);
 402                            }
 403
 404                            break;
 405                        }
 406
 407                    case "curv2":
 408                        {
 1409                            if (values.Length < 3)
 410                            {
 1411                                throw new InvalidDataException("A curv2 statement must specify at least 2 values.");
 412                            }
 413
 1414                            var curve = new ObjCurve2D();
 415
 1416                            for (int i = 1; i < values.Length; i++)
 417                            {
 1418                                int vp = int.Parse(values[i], CultureInfo.InvariantCulture);
 419
 1420                                if (vp == 0)
 421                                {
 1422                                    throw new InvalidDataException("A curv2 statement contains an invalid parameter spac
 423                                }
 424
 1425                                if (vp < 0)
 426                                {
 1427                                    vp = obj.ParameterSpaceVertices.Count + vp + 1;
 428                                }
 429
 1430                                if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 431                                {
 1432                                    throw new IndexOutOfRangeException();
 433                                }
 434
 1435                                curve.ParameterSpaceVertices.Add(vp);
 436                            }
 437
 1438                            context.ApplyAttributesToElement(curve);
 1439                            context.ApplyAttributesToFreeFormElement(curve);
 1440                            context.CurrentFreeFormElement = curve;
 441
 1442                            obj.Curves2D.Add(curve);
 443
 1444                            foreach (var group in context.GetCurrentGroups())
 445                            {
 1446                                group.Curves2D.Add(curve);
 447                            }
 448
 449                            break;
 450                        }
 451
 452                    case "surf":
 453                        {
 1454                            if (values.Length < 6)
 455                            {
 1456                                throw new InvalidDataException("A surf statement must specify at least 5 values.");
 457                            }
 458
 1459                            var surface = new ObjSurface();
 460
 1461                            surface.StartParameterU = float.Parse(values[1], CultureInfo.InvariantCulture);
 1462                            surface.EndParameterU = float.Parse(values[2], CultureInfo.InvariantCulture);
 1463                            surface.StartParameterV = float.Parse(values[3], CultureInfo.InvariantCulture);
 1464                            surface.EndParameterV = float.Parse(values[4], CultureInfo.InvariantCulture);
 465
 1466                            for (int i = 5; i < values.Length; i++)
 467                            {
 1468                                surface.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 469                            }
 470
 1471                            context.ApplyAttributesToElement(surface);
 1472                            context.ApplyAttributesToFreeFormElement(surface);
 1473                            context.CurrentFreeFormElement = surface;
 474
 1475                            obj.Surfaces.Add(surface);
 476
 1477                            foreach (var group in context.GetCurrentGroups())
 478                            {
 1479                                group.Surfaces.Add(surface);
 480                            }
 481
 482                            break;
 483                        }
 484
 485                    case "parm":
 1486                        if (context.CurrentFreeFormElement == null)
 487                        {
 488                            break;
 489                        }
 490
 1491                        if (values.Length < 4)
 492                        {
 1493                            throw new InvalidDataException("A parm statement must specify at least 3 values.");
 494                        }
 495
 496                        List<float> parameters;
 497
 1498                        if (string.Equals(values[1], "u", StringComparison.OrdinalIgnoreCase))
 499                        {
 1500                            parameters = context.CurrentFreeFormElement.ParametersU;
 501                        }
 1502                        else if (string.Equals(values[1], "v", StringComparison.OrdinalIgnoreCase))
 503                        {
 1504                            parameters = context.CurrentFreeFormElement.ParametersV;
 505                        }
 506                        else
 507                        {
 1508                            throw new InvalidDataException("A parm statement has an unknown direction.");
 509                        }
 510
 1511                        for (int i = 2; i < values.Length; i++)
 512                        {
 1513                            parameters.Add(float.Parse(values[i], CultureInfo.InvariantCulture));
 514                        }
 515
 1516                        break;
 517
 518                    case "trim":
 1519                        if (context.CurrentFreeFormElement == null)
 520                        {
 521                            break;
 522                        }
 523
 1524                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.OuterTrimmingCurves, obj, values);
 1525                        break;
 526
 527                    case "hole":
 1528                        if (context.CurrentFreeFormElement == null)
 529                        {
 530                            break;
 531                        }
 532
 1533                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.InnerTrimmingCurves, obj, values);
 1534                        break;
 535
 536                    case "scrv":
 1537                        if (context.CurrentFreeFormElement == null)
 538                        {
 539                            break;
 540                        }
 541
 1542                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.SequenceCurves, obj, values);
 1543                        break;
 544
 545                    case "sp":
 1546                        if (context.CurrentFreeFormElement == null)
 547                        {
 548                            break;
 549                        }
 550
 1551                        if (values.Length < 2)
 552                        {
 1553                            throw new InvalidDataException("A sp statement must specify at least 1 value.");
 554                        }
 555
 1556                        for (int i = 1; i < values.Length; i++)
 557                        {
 1558                            int vp = int.Parse(values[i], CultureInfo.InvariantCulture);
 559
 1560                            if (vp == 0)
 561                            {
 1562                                throw new InvalidDataException("A sp statement contains an invalid parameter space verte
 563                            }
 564
 1565                            if (vp < 0)
 566                            {
 1567                                vp = obj.ParameterSpaceVertices.Count + vp + 1;
 568                            }
 569
 1570                            if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 571                            {
 1572                                throw new IndexOutOfRangeException();
 573                            }
 574
 1575                            context.CurrentFreeFormElement.SpecialPoints.Add(vp);
 576                        }
 577
 1578                        break;
 579
 580                    case "end":
 1581                        context.CurrentFreeFormElement = null;
 1582                        break;
 583
 584                    case "con":
 1585                        ObjFileReader.ParseSurfaceConnection(obj, values);
 1586                        break;
 587
 588                    case "g":
 1589                        ObjFileReader.ParseGroupName(values, context);
 1590                        break;
 591
 592                    case "s":
 1593                        if (values.Length < 2)
 594                        {
 1595                            throw new InvalidDataException("A s statement must specify a value.");
 596                        }
 597
 1598                        if (values.Length != 2)
 599                        {
 1600                            throw new InvalidDataException("A s statement has too many values.");
 601                        }
 602
 1603                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 604                        {
 1605                            context.SmoothingGroupNumber = 0;
 606                        }
 607                        else
 608                        {
 1609                            context.SmoothingGroupNumber = long.Parse(values[1], CultureInfo.InvariantCulture);
 610                        }
 611
 1612                        break;
 613
 614                    case "mg":
 1615                        if (values.Length < 2)
 616                        {
 1617                            throw new InvalidDataException("A mg statement must specify a value.");
 618                        }
 619
 1620                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 621                        {
 1622                            context.MergingGroupNumber = 0;
 623                        }
 624                        else
 625                        {
 1626                            context.MergingGroupNumber = int.Parse(values[1], CultureInfo.InvariantCulture);
 627                        }
 628
 1629                        if (context.MergingGroupNumber == 0)
 630                        {
 1631                            if (values.Length > 3)
 632                            {
 1633                                throw new InvalidDataException("A mg statement has too many values.");
 634                            }
 635                        }
 636                        else
 637                        {
 1638                            if (values.Length != 3)
 639                            {
 1640                                throw new InvalidDataException("A mg statement has too many or too few values.");
 641                            }
 642
 1643                            float res = float.Parse(values[2], CultureInfo.InvariantCulture);
 644
 1645                            obj.MergingGroupResolutions[context.MergingGroupNumber] = res;
 646                        }
 647
 1648                        break;
 649
 650                    case "o":
 1651                        if (settings.HandleObjectNamesAsGroup)
 652                        {
 1653                            ParseGroupName(values, context);
 1654                            break;
 655                        }
 1656                        if (values.Length == 1)
 657                        {
 1658                            context.ObjectName = null;
 1659                            break;
 660                        }
 1661                        if (values.Length != 2)
 662                        {
 1663                            throw new InvalidDataException("A o statement has too many values.");
 664                        }
 665
 1666                        context.ObjectName = values[1];
 1667                        break;
 668
 669                    case "bevel":
 1670                        if (values.Length < 2)
 671                        {
 1672                            throw new InvalidDataException("A bevel statement must specify a name.");
 673                        }
 674
 1675                        if (values.Length != 2)
 676                        {
 1677                            throw new InvalidDataException("A bevel statement has too many values.");
 678                        }
 679
 1680                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 681                        {
 1682                            context.IsBevelInterpolationEnabled = true;
 683                        }
 1684                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 685                        {
 1686                            context.IsBevelInterpolationEnabled = false;
 687                        }
 688                        else
 689                        {
 1690                            throw new InvalidDataException("A bevel statement must specify on or off.");
 691                        }
 692
 693                        break;
 694
 695                    case "c_interp":
 1696                        if (values.Length < 2)
 697                        {
 1698                            throw new InvalidDataException("A c_interp statement must specify a name.");
 699                        }
 700
 1701                        if (values.Length != 2)
 702                        {
 1703                            throw new InvalidDataException("A c_interp statement has too many values.");
 704                        }
 705
 1706                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 707                        {
 1708                            context.IsColorInterpolationEnabled = true;
 709                        }
 1710                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 711                        {
 1712                            context.IsColorInterpolationEnabled = false;
 713                        }
 714                        else
 715                        {
 1716                            throw new InvalidDataException("A c_interp statement must specify on or off.");
 717                        }
 718
 719                        break;
 720
 721                    case "d_interp":
 1722                        if (values.Length < 2)
 723                        {
 1724                            throw new InvalidDataException("A d_interp statement must specify a name.");
 725                        }
 726
 1727                        if (values.Length != 2)
 728                        {
 1729                            throw new InvalidDataException("A d_interp statement has too many values.");
 730                        }
 731
 1732                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 733                        {
 1734                            context.IsDissolveInterpolationEnabled = true;
 735                        }
 1736                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 737                        {
 1738                            context.IsDissolveInterpolationEnabled = false;
 739                        }
 740                        else
 741                        {
 1742                            throw new InvalidDataException("A d_interp statement must specify on or off.");
 743                        }
 744
 745                        break;
 746
 747                    case "lod":
 1748                        if (values.Length < 2)
 749                        {
 1750                            throw new InvalidDataException("A lod statement must specify a value.");
 751                        }
 752
 1753                        if (values.Length != 2)
 754                        {
 1755                            throw new InvalidDataException("A lod statement has too many values.");
 756                        }
 757
 1758                        context.LevelOfDetail = int.Parse(values[1], CultureInfo.InvariantCulture);
 1759                        break;
 760
 761                    case "maplib":
 1762                        if (values.Length < 2)
 763                        {
 1764                            throw new InvalidDataException("A maplib statement must specify a file name.");
 765                        }
 766
 1767                        for (int i = 1; i < values.Length; i++)
 768                        {
 1769                            obj.MapLibraries.Add(values[i]);
 770                        }
 771
 1772                        break;
 773
 774                    case "mtllib":
 1775                        if (values.Length < 2)
 776                        {
 1777                            throw new InvalidDataException("A mtllib statement must specify a file name.");
 778                        }
 779
 1780                        if (settings.KeepWhitespacesOfMtlLibReferences)
 781                        {
 1782                            obj.MaterialLibraries.Add(currentLine.Remove(0, 7).Trim());
 783                        }
 784                        else
 785                        {
 1786                            obj.MaterialLibraries.Add(string.Join(" ", values, 1, values.Length - 1));
 787                        }
 788
 1789                        break;
 790
 791                    case "usemap":
 1792                        if (values.Length < 2)
 793                        {
 1794                            throw new InvalidDataException("A usemap statement must specify a value.");
 795                        }
 796
 1797                        if (values.Length != 2)
 798                        {
 1799                            throw new InvalidDataException("A usemap statement has too many values.");
 800                        }
 801
 1802                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 803                        {
 1804                            context.MapName = null;
 805                        }
 806                        else
 807                        {
 1808                            context.MapName = values[1];
 809                        }
 810
 1811                        break;
 812
 813                    case "usemtl":
 1814                        if (values.Length < 2)
 815                        {
 1816                            throw new InvalidDataException("A usemtl statement must specify a value.");
 817                        }
 818
 1819                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 820                        {
 1821                            if (values.Length != 2)
 822                            {
 1823                                throw new InvalidDataException("A usemtl statement has too many values.");
 824                            }
 825
 1826                            context.MaterialName = null;
 827                        }
 828                        else
 829                        {
 1830                            context.MaterialName = string.Join(" ", values, 1, values.Length - 1);
 831                        }
 832
 1833                        break;
 834
 835                    case "shadow_obj":
 1836                        if (values.Length < 2)
 837                        {
 1838                            throw new InvalidDataException("A shadow_obj statement must specify a file name.");
 839                        }
 840
 1841                        obj.ShadowObjectFileName = string.Join(" ", values, 1, values.Length - 1);
 1842                        break;
 843
 844                    case "trace_obj":
 1845                        if (values.Length < 2)
 846                        {
 1847                            throw new InvalidDataException("A trace_obj statement must specify a file name.");
 848                        }
 849
 1850                        obj.TraceObjectFileName = string.Join(" ", values, 1, values.Length - 1);
 1851                        break;
 852
 853                    case "ctech":
 1854                        context.CurveApproximationTechnique = ObjFileReader.ParseApproximationTechnique(values);
 1855                        break;
 856
 857                    case "stech":
 1858                        context.SurfaceApproximationTechnique = ObjFileReader.ParseApproximationTechnique(values);
 1859                        break;
 860
 861                    case "bsp":
 862                    case "bzp":
 863                    case "cdc":
 864                    case "cdp":
 865                    case "res":
 1866                        throw new NotImplementedException(string.Concat(values[0], " statement have been replaced by fre
 867                }
 868            }
 869
 1870            obj.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 871
 1872            return obj;
 873        }
 874
 875        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 876        private static ObjTriplet ParseTriplet(ObjFile obj, string value)
 877        {
 1878            var values = value.Split('/');
 879
 1880            if (values.Length > 3)
 881            {
 1882                throw new InvalidDataException("A triplet has too many values.");
 883            }
 884
 1885            int v = !string.IsNullOrEmpty(values[0]) ? int.Parse(values[0], CultureInfo.InvariantCulture) : 0;
 886
 1887            if (v == 0)
 888            {
 1889                throw new InvalidDataException("A triplet must specify a vertex index.");
 890            }
 891
 1892            if (v < 0)
 893            {
 1894                v = obj.Vertices.Count + v + 1;
 895            }
 896
 1897            if (v <= 0 || v > obj.Vertices.Count)
 898            {
 1899                throw new IndexOutOfRangeException();
 900            }
 901
 1902            int vt = values.Length > 1 && !string.IsNullOrEmpty(values[1]) ? int.Parse(values[1], CultureInfo.InvariantC
 903
 1904            if (vt != 0)
 905            {
 1906                if (vt < 0)
 907                {
 1908                    vt = obj.TextureVertices.Count + vt + 1;
 909                }
 910
 1911                if (vt <= 0 || vt > obj.TextureVertices.Count)
 912                {
 1913                    throw new IndexOutOfRangeException();
 914                }
 915            }
 916
 1917            int vn = values.Length > 2 && !string.IsNullOrEmpty(values[2]) ? int.Parse(values[2], CultureInfo.InvariantC
 918
 1919            if (vn != 0)
 920            {
 1921                if (vn < 0)
 922                {
 1923                    vn = obj.VertexNormals.Count + vn + 1;
 924                }
 925
 1926                if (vn <= 0 || vn > obj.VertexNormals.Count)
 927                {
 1928                    throw new IndexOutOfRangeException();
 929                }
 930            }
 931
 1932            return new ObjTriplet(v, vt, vn);
 933        }
 934
 935        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 936        private static ObjCurveIndex ParseCurveIndex(ObjFile obj, string[] values, int index)
 937        {
 1938            float start = float.Parse(values[index], CultureInfo.InvariantCulture);
 1939            float end = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1940            int curve2D = int.Parse(values[index + 2], CultureInfo.InvariantCulture);
 941
 1942            if (curve2D == 0)
 943            {
 1944                throw new InvalidDataException("A curve index must specify an index.");
 945            }
 946
 1947            if (curve2D < 0)
 948            {
 1949                curve2D = obj.Curves2D.Count + curve2D + 1;
 950            }
 951
 1952            if (curve2D <= 0 || curve2D > obj.Curves2D.Count)
 953            {
 1954                throw new IndexOutOfRangeException();
 955            }
 956
 1957            return new ObjCurveIndex(start, end, curve2D);
 958        }
 959
 960        private static void ParseCurveIndex(List<ObjCurveIndex> curves, ObjFile obj, string[] values)
 961        {
 1962            if (values.Length < 4)
 963            {
 1964                throw new InvalidDataException(string.Concat("A ", values[0], " statement must specify at least 3 value.
 965            }
 966
 1967            if ((values.Length - 1) % 3 != 0)
 968            {
 1969                throw new InvalidDataException(string.Concat("A ", values[0], " statement has too many values."));
 970            }
 971
 1972            for (int i = 1; i < values.Length; i += 3)
 973            {
 1974                curves.Add(ObjFileReader.ParseCurveIndex(obj, values, i));
 975            }
 1976        }
 977
 978        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 979        private static void ParseFreeFormType(ObjFileReaderContext context, string[] values)
 980        {
 1981            if (values.Length < 2)
 982            {
 1983                throw new InvalidDataException("A cstype statement must specify a value.");
 984            }
 985
 986            string type;
 987
 1988            if (values.Length == 2)
 989            {
 1990                context.IsRationalForm = false;
 1991                type = values[1];
 992            }
 1993            else if (values.Length == 3 && string.Equals(values[1], "rat", StringComparison.OrdinalIgnoreCase))
 994            {
 1995                context.IsRationalForm = true;
 1996                type = values[2];
 997            }
 998            else
 999            {
 11000                throw new InvalidDataException("A cstype statement has too many values.");
 1001            }
 1002
 11003            switch (type.ToLowerInvariant())
 1004            {
 1005                case "bmatrix":
 11006                    context.FreeFormType = ObjFreeFormType.BasisMatrix;
 11007                    break;
 1008
 1009                case "bezier":
 11010                    context.FreeFormType = ObjFreeFormType.Bezier;
 11011                    break;
 1012
 1013                case "bspline":
 11014                    context.FreeFormType = ObjFreeFormType.BSpline;
 11015                    break;
 1016
 1017                case "cardinal":
 11018                    context.FreeFormType = ObjFreeFormType.Cardinal;
 11019                    break;
 1020
 1021                case "taylor":
 11022                    context.FreeFormType = ObjFreeFormType.Taylor;
 11023                    break;
 1024
 1025                default:
 11026                    throw new InvalidDataException("A cstype statement has an unknown type.");
 1027            }
 1028        }
 1029
 1030        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 1031        private static void ParseSurfaceConnection(ObjFile obj, string[] values)
 1032        {
 11033            if (values.Length < 9)
 1034            {
 11035                throw new InvalidDataException("A con statement must specify 8 values.");
 1036            }
 1037
 11038            if (values.Length != 9)
 1039            {
 11040                throw new InvalidDataException("A con statement has too many values.");
 1041            }
 1042
 11043            int surface1 = int.Parse(values[1], CultureInfo.InvariantCulture);
 1044
 11045            if (surface1 == 0)
 1046            {
 11047                throw new InvalidDataException("A con statement must specify a surface index.");
 1048            }
 1049
 11050            if (surface1 < 0)
 1051            {
 11052                surface1 = obj.Surfaces.Count + surface1 + 1;
 1053            }
 1054
 11055            if (surface1 <= 0 || surface1 > obj.Surfaces.Count)
 1056            {
 11057                throw new IndexOutOfRangeException();
 1058            }
 1059
 11060            var curve1 = ObjFileReader.ParseCurveIndex(obj, values, 2);
 1061
 11062            int surface2 = int.Parse(values[5], CultureInfo.InvariantCulture);
 1063
 11064            if (surface2 == 0)
 1065            {
 11066                throw new InvalidDataException("A con statement must specify a surface index.");
 1067            }
 1068
 11069            if (surface2 < 0)
 1070            {
 11071                surface2 = obj.Surfaces.Count + surface2 + 1;
 1072            }
 1073
 11074            if (surface2 <= 0 || surface2 > obj.Surfaces.Count)
 1075            {
 11076                throw new IndexOutOfRangeException();
 1077            }
 1078
 11079            var curve2 = ObjFileReader.ParseCurveIndex(obj, values, 6);
 1080
 11081            var connection = new ObjSurfaceConnection
 11082            {
 11083                Surface1 = surface1,
 11084                Curve2D1 = curve1,
 11085                Surface2 = surface2,
 11086                Curve2D2 = curve2
 11087            };
 1088
 11089            obj.SurfaceConnections.Add(connection);
 11090        }
 1091
 1092        private static void ParseGroupName(string[] values, ObjFileReaderContext context)
 1093        {
 11094            context.GroupNames.Clear();
 1095
 11096            if (context.Settings.OnlyOneGroupNamePerLine)
 1097            {
 11098                var name = string.Join(" ", values, 1, values.Length - 1);
 11099                if (!string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
 1100                {
 11101                    context.GroupNames.Add(name);
 1102                }
 1103            }
 1104            else
 1105            {
 11106                for (int i = 1; i < values.Length; i++)
 1107                {
 11108                    var name = values[i];
 1109
 11110                    if (!string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
 1111                    {
 11112                        context.GroupNames.Add(name);
 1113                    }
 1114                }
 1115            }
 1116
 11117            context.GetCurrentGroups();
 11118        }
 1119
 1120        private static ObjApproximationTechnique ParseApproximationTechnique(string[] values)
 1121        {
 1122            ObjApproximationTechnique technique;
 1123
 11124            if (values.Length < 2)
 1125            {
 11126                throw new InvalidDataException(string.Concat("A ", values[0], " statement must specify a technique."));
 1127            }
 1128
 11129            switch (values[1].ToLowerInvariant())
 1130            {
 1131                case "cparm":
 1132                    {
 11133                        if (values.Length < 3)
 1134                        {
 11135                            throw new InvalidDataException(string.Concat("A ", values[0], " cparm statement must specify
 1136                        }
 1137
 11138                        if (values.Length != 3)
 1139                        {
 11140                            throw new InvalidDataException(string.Concat("A ", values[0], " cparm statement has too many
 1141                        }
 1142
 11143                        float res = float.Parse(values[2], CultureInfo.InvariantCulture);
 11144                        technique = new ObjConstantParametricSubdivisionTechnique(res);
 11145                        break;
 1146                    }
 1147
 1148                case "cparma":
 1149                    {
 11150                        if (values.Length < 4)
 1151                        {
 11152                            throw new InvalidDataException(string.Concat("A ", values[0], " cparma statement must specif
 1153                        }
 1154
 11155                        if (values.Length != 4)
 1156                        {
 11157                            throw new InvalidDataException(string.Concat("A ", values[0], " cparma statement has too man
 1158                        }
 1159
 11160                        float resU = float.Parse(values[2], CultureInfo.InvariantCulture);
 11161                        float resV = float.Parse(values[3], CultureInfo.InvariantCulture);
 11162                        technique = new ObjConstantParametricSubdivisionTechnique(resU, resV);
 11163                        break;
 1164                    }
 1165
 1166                case "cparmb":
 1167                    {
 11168                        if (values.Length < 3)
 1169                        {
 11170                            throw new InvalidDataException(string.Concat("A ", values[0], " cparmb statement must specif
 1171                        }
 1172
 11173                        if (values.Length != 3)
 1174                        {
 11175                            throw new InvalidDataException(string.Concat("A ", values[0], " cparmb statement has too man
 1176                        }
 1177
 11178                        float resU = float.Parse(values[2], CultureInfo.InvariantCulture);
 11179                        technique = new ObjConstantParametricSubdivisionTechnique(resU);
 11180                        break;
 1181                    }
 1182
 1183                case "cspace":
 1184                    {
 11185                        if (values.Length < 3)
 1186                        {
 11187                            throw new InvalidDataException(string.Concat("A ", values[0], " cspace statement must specif
 1188                        }
 1189
 11190                        if (values.Length != 3)
 1191                        {
 11192                            throw new InvalidDataException(string.Concat("A ", values[0], " cspace statement has too man
 1193                        }
 1194
 11195                        float length = float.Parse(values[2], CultureInfo.InvariantCulture);
 11196                        technique = new ObjConstantSpatialSubdivisionTechnique(length);
 11197                        break;
 1198                    }
 1199
 1200                case "curv":
 1201                    {
 11202                        if (values.Length < 4)
 1203                        {
 11204                            throw new InvalidDataException(string.Concat("A ", values[0], " curv statement must specify 
 1205                        }
 1206
 11207                        if (values.Length != 4)
 1208                        {
 11209                            throw new InvalidDataException(string.Concat("A ", values[0], " curv statement has too many 
 1210                        }
 1211
 11212                        float distance = float.Parse(values[2], CultureInfo.InvariantCulture);
 11213                        float angle = float.Parse(values[3], CultureInfo.InvariantCulture);
 11214                        technique = new ObjCurvatureDependentSubdivisionTechnique(distance, angle);
 11215                        break;
 1216                    }
 1217
 1218                default:
 11219                    throw new InvalidDataException(string.Concat("A ", values[0], " statement contains an unknown techni
 1220            }
 1221
 11222            return technique;
 1223        }
 1224    }
 1225}
 1226
 1227#endif

C:\projects\jeremyansel-media-wavefrontobj\JeremyAnsel.Media.WavefrontObj\JeremyAnsel.Media.WavefrontObj\ObjFileReader9.cs

#LineLine coverage
 1// <copyright file="ObjFileReader9.cs" company="Jérémy Ansel">
 2// Copyright (c) 2017, 2019 Jérémy Ansel
 3// </copyright>
 4// <license>
 5// Licensed under the MIT license. See LICENSE.txt
 6// </license>
 7
 8#if NET6_0_OR_GREATER
 9
 10using System.Diagnostics.CodeAnalysis;
 11using System.Globalization;
 12using System.Text;
 13
 14#if NET9_0_OR_GREATER
 15using SpanSplitEnumerator = System.MemoryExtensions.SpanSplitEnumerator<char>;
 16#else
 17using SpanSplitEnumerator = Polyfills.Polyfill.SpanSplitEnumerator<char>;
 18#endif
 19
 20namespace JeremyAnsel.Media.WavefrontObj
 21{
 22    internal static class ObjFileReader
 23    {
 24        private static void MoveNextSkipEmpty(ref SpanSplitEnumerator values)
 25        {
 326            while (values.MoveNext())
 27            {
 328                if (values.Current.Start.Value != values.Current.End.Value)
 29                {
 330                    return;
 31                }
 32            }
 033        }
 34
 35        private static ReadOnlySpan<char> GetNextValue(ref ReadOnlySpan<char> currentLine, ref SpanSplitEnumerator value
 36        {
 337            MoveNextSkipEmpty(ref values);
 338            return currentLine[values.Current];
 39        }
 40
 41        private static float FloatParse(ReadOnlySpan<char> s)
 42        {
 343            return float.Parse(s, NumberStyles.Float, CultureInfo.InvariantCulture);
 44        }
 45
 46        private static int IntParse(ReadOnlySpan<char> s)
 47        {
 348            return int.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
 49        }
 50
 51        private static long LongParse(ReadOnlySpan<char> s)
 52        {
 353            return long.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
 54        }
 55
 56        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 57        public static ObjFile FromStream(Stream? stream, ObjFileReaderSettings settings)
 58        {
 359            if (stream == null)
 60            {
 361                throw new ArgumentNullException(nameof(stream));
 62            }
 63
 364            var obj = new ObjFile();
 365            var context = new ObjFileReaderContext(obj, settings);
 366            var lineReader = new LineReader9();
 67
 368            int valueBufferSize = 16;
 369            Span<char> valueBuffer = stackalloc char[valueBufferSize];
 70
 371            foreach (var currentLineString in lineReader.Read9(stream))
 72            {
 373                ReadOnlySpan<char> currentLine = currentLineString.Buffer.AsSpan()[..currentLineString.Length];
 74
 375                int valuesCount = 0;
 76
 377                foreach (Range range in currentLine.SplitAny(LineReader9.LineSeparators))
 78                {
 379                    if (currentLine[range].Length == 0)
 80                    {
 81                        continue;
 82                    }
 83
 384                    valuesCount++;
 85                }
 86
 387                SpanSplitEnumerator values = currentLine.SplitAny(LineReader9.LineSeparators);
 88
 389                MoveNextSkipEmpty(ref values);
 90
 391                if (values.Current.End.Value - values.Current.Start.Value > valueBufferSize)
 92                {
 93                    continue;
 94                }
 95
 396                ReadOnlySpan<char> value0 = currentLine[values.Current];
 397                int value0Length = value0.ToLowerInvariant(valueBuffer);
 98
 99                //if (value0Length == -1)
 100                //{
 101                //    throw new InvalidDataException("the buffer is too small");
 102                //}
 103
 3104                switch (valueBuffer[..value0Length])
 105                {
 106                    case "v":
 107                        {
 3108                            if (valuesCount < 4)
 109                            {
 3110                                throw new InvalidDataException("A v statement must specify at least 3 values.");
 111                            }
 112
 3113                            float x = FloatParse(GetNextValue(ref currentLine, ref values));
 3114                            float y = FloatParse(GetNextValue(ref currentLine, ref values));
 3115                            float z = FloatParse(GetNextValue(ref currentLine, ref values));
 3116                            float w = 1.0f;
 3117                            bool hasColor = false;
 3118                            float r = 0.0f;
 3119                            float g = 0.0f;
 3120                            float b = 0.0f;
 3121                            float a = 1.0f;
 122
 3123                            if (valuesCount == 4 || valuesCount == 5)
 124                            {
 3125                                if (valuesCount == 5)
 126                                {
 3127                                    w = FloatParse(GetNextValue(ref currentLine, ref values));
 128                                }
 129                            }
 3130                            else if (valuesCount == 7 || valuesCount == 8)
 131                            {
 3132                                hasColor = true;
 3133                                r = FloatParse(GetNextValue(ref currentLine, ref values));
 3134                                g = FloatParse(GetNextValue(ref currentLine, ref values));
 3135                                b = FloatParse(GetNextValue(ref currentLine, ref values));
 136
 3137                                if (valuesCount == 8)
 138                                {
 3139                                    a = FloatParse(GetNextValue(ref currentLine, ref values));
 140                                }
 141                            }
 142                            else
 143                            {
 3144                                throw new InvalidDataException("A v statement has too many values.");
 145                            }
 146
 3147                            var v = new ObjVertex();
 3148                            v.Position = new ObjVector4(x, y, z, w);
 149
 3150                            if (hasColor)
 151                            {
 3152                                v.Color = new ObjVector4(r, g, b, a);
 153                            }
 154
 3155                            obj.Vertices.Add(v);
 3156                            break;
 157                        }
 158
 159                    case "vp":
 160                        {
 3161                            if (valuesCount < 2)
 162                            {
 3163                                throw new InvalidDataException("A vp statement must specify at least 1 value.");
 164                            }
 165
 3166                            var v = new ObjVector3();
 3167                            v.X = FloatParse(GetNextValue(ref currentLine, ref values));
 168
 3169                            if (valuesCount == 2)
 170                            {
 3171                                v.Y = 0.0f;
 3172                                v.Z = 1.0f;
 173                            }
 3174                            else if (valuesCount == 3)
 175                            {
 3176                                v.Y = FloatParse(GetNextValue(ref currentLine, ref values));
 3177                                v.Z = 1.0f;
 178                            }
 3179                            else if (valuesCount == 4)
 180                            {
 3181                                v.Y = FloatParse(GetNextValue(ref currentLine, ref values));
 3182                                v.Z = FloatParse(GetNextValue(ref currentLine, ref values));
 183                            }
 184                            else
 185                            {
 3186                                throw new InvalidDataException("A vp statement has too many values.");
 187                            }
 188
 3189                            obj.ParameterSpaceVertices.Add(v);
 3190                            break;
 191                        }
 192
 193                    case "vn":
 194                        {
 3195                            if (valuesCount < 4)
 196                            {
 3197                                throw new InvalidDataException("A vn statement must specify 3 values.");
 198                            }
 199
 3200                            if (valuesCount != 4)
 201                            {
 3202                                throw new InvalidDataException("A vn statement has too many values.");
 203                            }
 204
 3205                            var v = new ObjVector3();
 3206                            v.X = FloatParse(GetNextValue(ref currentLine, ref values));
 3207                            v.Y = FloatParse(GetNextValue(ref currentLine, ref values));
 3208                            v.Z = FloatParse(GetNextValue(ref currentLine, ref values));
 209
 3210                            obj.VertexNormals.Add(v);
 3211                            break;
 212                        }
 213
 214                    case "vt":
 215                        {
 3216                            if (valuesCount < 2)
 217                            {
 3218                                throw new InvalidDataException("A vt statement must specify at least 1 value.");
 219                            }
 220
 3221                            var v = new ObjVector3();
 3222                            v.X = FloatParse(GetNextValue(ref currentLine, ref values));
 223
 3224                            if (valuesCount == 2)
 225                            {
 3226                                v.Y = 0.0f;
 3227                                v.Z = 0.0f;
 228                            }
 3229                            else if (valuesCount == 3)
 230                            {
 3231                                v.Y = FloatParse(GetNextValue(ref currentLine, ref values));
 3232                                v.Z = 0.0f;
 233                            }
 3234                            else if (valuesCount == 4)
 235                            {
 3236                                v.Y = FloatParse(GetNextValue(ref currentLine, ref values));
 3237                                v.Z = FloatParse(GetNextValue(ref currentLine, ref values));
 238                            }
 239                            else
 240                            {
 3241                                throw new InvalidDataException("A vt statement has too many values.");
 242                            }
 243
 3244                            obj.TextureVertices.Add(v);
 3245                            break;
 246                        }
 247
 248                    case "cstype":
 3249                        ParseFreeFormType(context, ref currentLine, ref values, valuesCount);
 3250                        break;
 251
 252                    case "deg":
 3253                        if (valuesCount < 2)
 254                        {
 3255                            throw new InvalidDataException("A deg statement must specify at least 1 value.");
 256                        }
 257
 3258                        if (valuesCount == 2)
 259                        {
 3260                            context.DegreeU = IntParse(GetNextValue(ref currentLine, ref values));
 3261                            context.DegreeV = 0;
 262                        }
 3263                        else if (valuesCount == 3)
 264                        {
 3265                            context.DegreeU = IntParse(GetNextValue(ref currentLine, ref values));
 3266                            context.DegreeV = IntParse(GetNextValue(ref currentLine, ref values));
 267                        }
 268                        else
 269                        {
 3270                            throw new InvalidDataException("A deg statement has too many values.");
 271                        }
 272
 273                        break;
 274
 275                    case "bmat":
 276                        {
 3277                            if (valuesCount < 2)
 278                            {
 3279                                throw new InvalidDataException("A bmat statement must specify a direction.");
 280                            }
 281
 282                            int d;
 3283                            var value1 = GetNextValue(ref currentLine, ref values);
 284
 3285                            if (value1.Equals("u", StringComparison.OrdinalIgnoreCase))
 286                            {
 3287                                d = 1;
 288                            }
 3289                            else if (value1.Equals("v", StringComparison.OrdinalIgnoreCase))
 290                            {
 3291                                d = 2;
 292                            }
 293                            else
 294                            {
 3295                                throw new InvalidDataException("A bmat statement has an unknown direction.");
 296                            }
 297
 3298                            int count = (context.DegreeU + 1) * (context.DegreeV + 1);
 299
 3300                            if (valuesCount != count + 2)
 301                            {
 3302                                throw new InvalidDataException("A bmat statement has too many or too few values.");
 303                            }
 304
 3305                            var matrix = new float[count];
 306
 3307                            for (int i = 0; i < count; i++)
 308                            {
 3309                                matrix[i] = FloatParse(GetNextValue(ref currentLine, ref values));
 310                            }
 311
 312                            switch (d)
 313                            {
 314                                case 1:
 3315                                    context.BasicMatrixU = matrix;
 3316                                    break;
 317
 318                                case 2:
 3319                                    context.BasicMatrixV = matrix;
 3320                                    break;
 321                            }
 322
 323                            break;
 324                        }
 325
 326                    case "step":
 3327                        if (valuesCount < 2)
 328                        {
 3329                            throw new InvalidDataException("A step statement must specify at least 1 value.");
 330                        }
 331
 3332                        if (valuesCount == 2)
 333                        {
 3334                            context.StepU = FloatParse(GetNextValue(ref currentLine, ref values));
 3335                            context.StepV = 1.0f;
 336                        }
 3337                        else if (valuesCount == 3)
 338                        {
 3339                            context.StepU = FloatParse(GetNextValue(ref currentLine, ref values));
 3340                            context.StepV = FloatParse(GetNextValue(ref currentLine, ref values));
 341                        }
 342                        else
 343                        {
 3344                            throw new InvalidDataException("A step statement has too many values.");
 345                        }
 346
 347                        break;
 348
 349                    case "p":
 350                        {
 3351                            if (valuesCount < 2)
 352                            {
 3353                                throw new InvalidDataException("A p statement must specify at least 1 value.");
 354                            }
 355
 3356                            var point = new ObjPoint();
 357
 3358                            for (int i = 1; i < valuesCount; i++)
 359                            {
 3360                                point.Vertices.Add(ParseTriplet(obj, GetNextValue(ref currentLine, ref values)));
 361                            }
 362
 3363                            context.ApplyAttributesToElement(point);
 3364                            context.ApplyAttributesToPolygonalElement(point);
 365
 3366                            obj.Points.Add(point);
 367
 3368                            foreach (var group in context.GetCurrentGroups())
 369                            {
 3370                                group.Points.Add(point);
 371                            }
 372
 373                            break;
 374                        }
 375
 376                    case "l":
 377                        {
 3378                            if (valuesCount < 3)
 379                            {
 3380                                throw new InvalidDataException("A l statement must specify at least 2 values.");
 381                            }
 382
 3383                            var line = new ObjLine();
 384
 3385                            for (int i = 1; i < valuesCount; i++)
 386                            {
 3387                                line.Vertices.Add(ParseTriplet(obj, GetNextValue(ref currentLine, ref values)));
 388                            }
 389
 3390                            context.ApplyAttributesToElement(line);
 3391                            context.ApplyAttributesToPolygonalElement(line);
 392
 3393                            obj.Lines.Add(line);
 394
 3395                            foreach (var group in context.GetCurrentGroups())
 396                            {
 3397                                group.Lines.Add(line);
 398                            }
 399
 400                            break;
 401                        }
 402
 403                    case "f":
 404                    case "fo":
 405                        {
 3406                            if (valuesCount < 4)
 407                            {
 3408                                throw new InvalidDataException("A f statement must specify at least 3 values.");
 409                            }
 410
 3411                            var face = new ObjFace();
 412
 3413                            for (int i = 1; i < valuesCount; i++)
 414                            {
 3415                                face.Vertices.Add(ParseTriplet(obj, GetNextValue(ref currentLine, ref values)));
 416                            }
 417
 3418                            context.ApplyAttributesToElement(face);
 3419                            context.ApplyAttributesToPolygonalElement(face);
 420
 3421                            obj.Faces.Add(face);
 422
 3423                            foreach (var group in context.GetCurrentGroups())
 424                            {
 3425                                group.Faces.Add(face);
 426                            }
 427
 428                            break;
 429                        }
 430
 431                    case "curv":
 432                        {
 3433                            if (valuesCount < 5)
 434                            {
 3435                                throw new InvalidDataException("A curv statement must specify at least 4 values.");
 436                            }
 437
 3438                            var curve = new ObjCurve();
 439
 3440                            curve.StartParameter = FloatParse(GetNextValue(ref currentLine, ref values));
 3441                            curve.EndParameter = FloatParse(GetNextValue(ref currentLine, ref values));
 442
 3443                            for (int i = 3; i < valuesCount; i++)
 444                            {
 3445                                int v = IntParse(GetNextValue(ref currentLine, ref values));
 446
 3447                                if (v == 0)
 448                                {
 3449                                    throw new InvalidDataException("A curv statement contains an invalid vertex index.")
 450                                }
 451
 3452                                if (v < 0)
 453                                {
 3454                                    v = obj.Vertices.Count + v + 1;
 455                                }
 456
 3457                                if (v <= 0 || v > obj.Vertices.Count)
 458                                {
 3459                                    throw new IndexOutOfRangeException();
 460                                }
 461
 3462                                curve.Vertices.Add(v);
 463                            }
 464
 3465                            context.ApplyAttributesToElement(curve);
 3466                            context.ApplyAttributesToFreeFormElement(curve);
 3467                            context.CurrentFreeFormElement = curve;
 468
 3469                            obj.Curves.Add(curve);
 470
 3471                            foreach (var group in context.GetCurrentGroups())
 472                            {
 3473                                group.Curves.Add(curve);
 474                            }
 475
 476                            break;
 477                        }
 478
 479                    case "curv2":
 480                        {
 3481                            if (valuesCount < 3)
 482                            {
 3483                                throw new InvalidDataException("A curv2 statement must specify at least 2 values.");
 484                            }
 485
 3486                            var curve = new ObjCurve2D();
 487
 3488                            for (int i = 1; i < valuesCount; i++)
 489                            {
 3490                                int vp = IntParse(GetNextValue(ref currentLine, ref values));
 491
 3492                                if (vp == 0)
 493                                {
 3494                                    throw new InvalidDataException("A curv2 statement contains an invalid parameter spac
 495                                }
 496
 3497                                if (vp < 0)
 498                                {
 3499                                    vp = obj.ParameterSpaceVertices.Count + vp + 1;
 500                                }
 501
 3502                                if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 503                                {
 3504                                    throw new IndexOutOfRangeException();
 505                                }
 506
 3507                                curve.ParameterSpaceVertices.Add(vp);
 508                            }
 509
 3510                            context.ApplyAttributesToElement(curve);
 3511                            context.ApplyAttributesToFreeFormElement(curve);
 3512                            context.CurrentFreeFormElement = curve;
 513
 3514                            obj.Curves2D.Add(curve);
 515
 3516                            foreach (var group in context.GetCurrentGroups())
 517                            {
 3518                                group.Curves2D.Add(curve);
 519                            }
 520
 521                            break;
 522                        }
 523
 524                    case "surf":
 525                        {
 3526                            if (valuesCount < 6)
 527                            {
 3528                                throw new InvalidDataException("A surf statement must specify at least 5 values.");
 529                            }
 530
 3531                            var surface = new ObjSurface();
 532
 3533                            surface.StartParameterU = FloatParse(GetNextValue(ref currentLine, ref values));
 3534                            surface.EndParameterU = FloatParse(GetNextValue(ref currentLine, ref values));
 3535                            surface.StartParameterV = FloatParse(GetNextValue(ref currentLine, ref values));
 3536                            surface.EndParameterV = FloatParse(GetNextValue(ref currentLine, ref values));
 537
 3538                            for (int i = 5; i < valuesCount; i++)
 539                            {
 3540                                surface.Vertices.Add(ParseTriplet(obj, GetNextValue(ref currentLine, ref values)));
 541                            }
 542
 3543                            context.ApplyAttributesToElement(surface);
 3544                            context.ApplyAttributesToFreeFormElement(surface);
 3545                            context.CurrentFreeFormElement = surface;
 546
 3547                            obj.Surfaces.Add(surface);
 548
 3549                            foreach (var group in context.GetCurrentGroups())
 550                            {
 3551                                group.Surfaces.Add(surface);
 552                            }
 553
 554                            break;
 555                        }
 556
 557                    case "parm":
 558                        {
 3559                            if (context.CurrentFreeFormElement == null)
 560                            {
 561                                break;
 562                            }
 563
 3564                            if (valuesCount < 4)
 565                            {
 3566                                throw new InvalidDataException("A parm statement must specify at least 3 values.");
 567                            }
 568
 569                            List<float> parameters;
 3570                            var value1 = GetNextValue(ref currentLine, ref values);
 571
 3572                            if (value1.Equals("u", StringComparison.OrdinalIgnoreCase))
 573                            {
 3574                                parameters = context.CurrentFreeFormElement.ParametersU;
 575                            }
 3576                            else if (value1.Equals("v", StringComparison.OrdinalIgnoreCase))
 577                            {
 3578                                parameters = context.CurrentFreeFormElement.ParametersV;
 579                            }
 580                            else
 581                            {
 3582                                throw new InvalidDataException("A parm statement has an unknown direction.");
 583                            }
 584
 3585                            for (int i = 2; i < valuesCount; i++)
 586                            {
 3587                                parameters.Add(FloatParse(GetNextValue(ref currentLine, ref values)));
 588                            }
 589
 3590                            break;
 591                        }
 592
 593                    case "trim":
 3594                        if (context.CurrentFreeFormElement == null)
 595                        {
 596                            break;
 597                        }
 598
 3599                        ParseCurveIndex(context.CurrentFreeFormElement.OuterTrimmingCurves, obj, value0, ref currentLine
 3600                        break;
 601
 602                    case "hole":
 3603                        if (context.CurrentFreeFormElement == null)
 604                        {
 605                            break;
 606                        }
 607
 3608                        ParseCurveIndex(context.CurrentFreeFormElement.InnerTrimmingCurves, obj, value0, ref currentLine
 3609                        break;
 610
 611                    case "scrv":
 3612                        if (context.CurrentFreeFormElement == null)
 613                        {
 614                            break;
 615                        }
 616
 3617                        ParseCurveIndex(context.CurrentFreeFormElement.SequenceCurves, obj, value0, ref currentLine, ref
 3618                        break;
 619
 620                    case "sp":
 3621                        if (context.CurrentFreeFormElement == null)
 622                        {
 623                            break;
 624                        }
 625
 3626                        if (valuesCount < 2)
 627                        {
 3628                            throw new InvalidDataException("A sp statement must specify at least 1 value.");
 629                        }
 630
 3631                        for (int i = 1; i < valuesCount; i++)
 632                        {
 3633                            int vp = IntParse(GetNextValue(ref currentLine, ref values));
 634
 3635                            if (vp == 0)
 636                            {
 3637                                throw new InvalidDataException("A sp statement contains an invalid parameter space verte
 638                            }
 639
 3640                            if (vp < 0)
 641                            {
 3642                                vp = obj.ParameterSpaceVertices.Count + vp + 1;
 643                            }
 644
 3645                            if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 646                            {
 3647                                throw new IndexOutOfRangeException();
 648                            }
 649
 3650                            context.CurrentFreeFormElement.SpecialPoints.Add(vp);
 651                        }
 652
 3653                        break;
 654
 655                    case "end":
 3656                        context.CurrentFreeFormElement = null;
 3657                        break;
 658
 659                    case "con":
 3660                        ParseSurfaceConnection(obj, ref currentLine, ref values, valuesCount);
 3661                        break;
 662
 663                    case "g":
 3664                        ParseGroupName(context, ref currentLine, ref values, valuesCount);
 3665                        break;
 666
 667                    case "s":
 668                        {
 3669                            if (valuesCount < 2)
 670                            {
 3671                                throw new InvalidDataException("A s statement must specify a value.");
 672                            }
 673
 3674                            if (valuesCount != 2)
 675                            {
 3676                                throw new InvalidDataException("A s statement has too many values.");
 677                            }
 678
 3679                            var value1 = GetNextValue(ref currentLine, ref values);
 680
 3681                            if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 682                            {
 3683                                context.SmoothingGroupNumber = 0;
 684                            }
 685                            else
 686                            {
 3687                                context.SmoothingGroupNumber = LongParse(value1);
 688                            }
 689
 3690                            break;
 691                        }
 692
 693                    case "mg":
 694                        {
 3695                            if (valuesCount < 2)
 696                            {
 3697                                throw new InvalidDataException("A mg statement must specify a value.");
 698                            }
 699
 3700                            var value1 = GetNextValue(ref currentLine, ref values);
 701
 3702                            if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 703                            {
 3704                                context.MergingGroupNumber = 0;
 705                            }
 706                            else
 707                            {
 3708                                context.MergingGroupNumber = IntParse(value1);
 709                            }
 710
 3711                            if (context.MergingGroupNumber == 0)
 712                            {
 3713                                if (valuesCount > 3)
 714                                {
 3715                                    throw new InvalidDataException("A mg statement has too many values.");
 716                                }
 717                            }
 718                            else
 719                            {
 3720                                if (valuesCount != 3)
 721                                {
 3722                                    throw new InvalidDataException("A mg statement has too many or too few values.");
 723                                }
 724
 3725                                float res = FloatParse(GetNextValue(ref currentLine, ref values));
 726
 3727                                obj.MergingGroupResolutions[context.MergingGroupNumber] = res;
 728                            }
 729
 3730                            break;
 731                        }
 732
 733                    case "o":
 3734                        if (settings.HandleObjectNamesAsGroup)
 735                        {
 3736                            ParseGroupName(context, ref currentLine, ref values, valuesCount);
 3737                            break;
 738                        }
 739
 3740                        if (valuesCount == 1)
 741                        {
 3742                            context.ObjectName = null;
 3743                            break;
 744                        }
 745
 3746                        if (valuesCount != 2)
 747                        {
 3748                            throw new InvalidDataException("A o statement has too many values.");
 749                        }
 750
 3751                        context.ObjectName = GetNextValue(ref currentLine, ref values).ToString();
 3752                        break;
 753
 754                    case "bevel":
 755                        {
 3756                            if (valuesCount < 2)
 757                            {
 3758                                throw new InvalidDataException("A bevel statement must specify a name.");
 759                            }
 760
 3761                            if (valuesCount != 2)
 762                            {
 3763                                throw new InvalidDataException("A bevel statement has too many values.");
 764                            }
 765
 3766                            var value1 = GetNextValue(ref currentLine, ref values);
 767
 3768                            if (value1.Equals("on", StringComparison.OrdinalIgnoreCase))
 769                            {
 3770                                context.IsBevelInterpolationEnabled = true;
 771                            }
 3772                            else if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 773                            {
 3774                                context.IsBevelInterpolationEnabled = false;
 775                            }
 776                            else
 777                            {
 3778                                throw new InvalidDataException("A bevel statement must specify on or off.");
 779                            }
 780
 781                            break;
 782                        }
 783
 784                    case "c_interp":
 785                        {
 3786                            if (valuesCount < 2)
 787                            {
 3788                                throw new InvalidDataException("A c_interp statement must specify a name.");
 789                            }
 790
 3791                            if (valuesCount != 2)
 792                            {
 3793                                throw new InvalidDataException("A c_interp statement has too many values.");
 794                            }
 795
 3796                            var value1 = GetNextValue(ref currentLine, ref values);
 797
 3798                            if (value1.Equals("on", StringComparison.OrdinalIgnoreCase))
 799                            {
 3800                                context.IsColorInterpolationEnabled = true;
 801                            }
 3802                            else if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 803                            {
 3804                                context.IsColorInterpolationEnabled = false;
 805                            }
 806                            else
 807                            {
 3808                                throw new InvalidDataException("A c_interp statement must specify on or off.");
 809                            }
 810
 811                            break;
 812                        }
 813
 814                    case "d_interp":
 815                        {
 3816                            if (valuesCount < 2)
 817                            {
 3818                                throw new InvalidDataException("A d_interp statement must specify a name.");
 819                            }
 820
 3821                            if (valuesCount != 2)
 822                            {
 3823                                throw new InvalidDataException("A d_interp statement has too many values.");
 824                            }
 825
 3826                            var value1 = GetNextValue(ref currentLine, ref values);
 827
 3828                            if (value1.Equals("on", StringComparison.OrdinalIgnoreCase))
 829                            {
 3830                                context.IsDissolveInterpolationEnabled = true;
 831                            }
 3832                            else if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 833                            {
 3834                                context.IsDissolveInterpolationEnabled = false;
 835                            }
 836                            else
 837                            {
 3838                                throw new InvalidDataException("A d_interp statement must specify on or off.");
 839                            }
 840
 841                            break;
 842                        }
 843
 844                    case "lod":
 3845                        if (valuesCount < 2)
 846                        {
 3847                            throw new InvalidDataException("A lod statement must specify a value.");
 848                        }
 849
 3850                        if (valuesCount != 2)
 851                        {
 3852                            throw new InvalidDataException("A lod statement has too many values.");
 853                        }
 854
 3855                        context.LevelOfDetail = IntParse(GetNextValue(ref currentLine, ref values));
 3856                        break;
 857
 858                    case "maplib":
 3859                        if (valuesCount < 2)
 860                        {
 3861                            throw new InvalidDataException("A maplib statement must specify a file name.");
 862                        }
 863
 3864                        for (int i = 1; i < valuesCount; i++)
 865                        {
 3866                            obj.MapLibraries.Add(GetNextValue(ref currentLine, ref values).ToString());
 867                        }
 868
 3869                        break;
 870
 871                    case "mtllib":
 872                        {
 3873                            if (valuesCount < 2)
 874                            {
 3875                                throw new InvalidDataException("A mtllib statement must specify a file name.");
 876                            }
 877
 3878                            if (settings.KeepWhitespacesOfMtlLibReferences)
 879                            {
 3880                                obj.MaterialLibraries.Add(new string(currentLine[7..]).Trim());
 881                            }
 882                            else
 883                            {
 3884                                var sb = new StringBuilder();
 885
 3886                                sb.Append(GetNextValue(ref currentLine, ref values));
 887
 3888                                for (int i = 2; i < valuesCount; i++)
 889                                {
 3890                                    sb.Append(' ');
 3891                                    sb.Append(GetNextValue(ref currentLine, ref values));
 892                                }
 893
 3894                                obj.MaterialLibraries.Add(sb.ToString());
 895                            }
 896
 3897                            break;
 898                        }
 899
 900                    case "usemap":
 901                        {
 3902                            if (valuesCount < 2)
 903                            {
 3904                                throw new InvalidDataException("A usemap statement must specify a value.");
 905                            }
 906
 3907                            if (valuesCount != 2)
 908                            {
 3909                                throw new InvalidDataException("A usemap statement has too many values.");
 910                            }
 911
 3912                            var value1 = GetNextValue(ref currentLine, ref values);
 913
 3914                            if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 915                            {
 3916                                context.MapName = null;
 917                            }
 918                            else
 919                            {
 3920                                context.MapName = value1.ToString();
 921                            }
 922
 3923                            break;
 924                        }
 925
 926                    case "usemtl":
 927                        {
 3928                            if (valuesCount < 2)
 929                            {
 3930                                throw new InvalidDataException("A usemtl statement must specify a value.");
 931                            }
 932
 3933                            var value1 = GetNextValue(ref currentLine, ref values);
 934
 3935                            if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 936                            {
 3937                                if (valuesCount != 2)
 938                                {
 3939                                    throw new InvalidDataException("A usemtl statement has too many values.");
 940                                }
 941
 3942                                context.MaterialName = null;
 943                            }
 944                            else
 945                            {
 3946                                var sb = new StringBuilder();
 947
 3948                                sb.Append(value1);
 949
 3950                                for (int i = 2; i < valuesCount; i++)
 951                                {
 3952                                    sb.Append(' ');
 3953                                    sb.Append(GetNextValue(ref currentLine, ref values));
 954                                }
 955
 3956                                context.MaterialName = sb.ToString();
 957                            }
 958
 3959                            break;
 960                        }
 961
 962                    case "shadow_obj":
 963                        {
 3964                            if (valuesCount < 2)
 965                            {
 3966                                throw new InvalidDataException("A shadow_obj statement must specify a file name.");
 967                            }
 968
 3969                            var sb = new StringBuilder();
 970
 3971                            sb.Append(GetNextValue(ref currentLine, ref values));
 972
 3973                            for (int i = 2; i < valuesCount; i++)
 974                            {
 3975                                sb.Append(' ');
 3976                                sb.Append(GetNextValue(ref currentLine, ref values));
 977                            }
 978
 3979                            obj.ShadowObjectFileName = sb.ToString();
 3980                            break;
 981                        }
 982
 983                    case "trace_obj":
 984                        {
 3985                            if (valuesCount < 2)
 986                            {
 3987                                throw new InvalidDataException("A trace_obj statement must specify a file name.");
 988                            }
 989
 3990                            var sb = new StringBuilder();
 991
 3992                            sb.Append(GetNextValue(ref currentLine, ref values));
 993
 3994                            for (int i = 2; i < valuesCount; i++)
 995                            {
 3996                                sb.Append(' ');
 3997                                sb.Append(GetNextValue(ref currentLine, ref values));
 998                            }
 999
 31000                            obj.TraceObjectFileName = sb.ToString();
 31001                            break;
 1002                        }
 1003
 1004                    case "ctech":
 31005                        context.CurveApproximationTechnique = ParseApproximationTechnique(value0, ref currentLine, ref v
 31006                        break;
 1007
 1008                    case "stech":
 31009                        context.SurfaceApproximationTechnique = ParseApproximationTechnique(value0, ref currentLine, ref
 31010                        break;
 1011
 1012                    case "bsp":
 1013                    case "bzp":
 1014                    case "cdc":
 1015                    case "cdp":
 1016                    case "res":
 31017                        throw new NotImplementedException(string.Concat(value0.ToString(), " statement have been replace
 1018                }
 1019            }
 1020
 31021            obj.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 1022
 31023            return obj;
 1024        }
 1025
 1026        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 1027        private static ObjTriplet ParseTriplet(ObjFile obj, ReadOnlySpan<char> value)
 1028        {
 31029            int valuesCount = 0;
 1030
 31031            foreach (var _ in value.Split('/'))
 1032            {
 31033                valuesCount++;
 1034            }
 1035
 31036            var values = value.Split('/');
 1037
 31038            if (valuesCount > 3)
 1039            {
 31040                throw new InvalidDataException("A triplet has too many values.");
 1041            }
 1042
 31043            values.MoveNext();
 31044            int v = value[values.Current].Length != 0 ? IntParse(value[values.Current]) : 0;
 1045
 31046            if (v == 0)
 1047            {
 31048                throw new InvalidDataException("A triplet must specify a vertex index.");
 1049            }
 1050
 31051            if (v < 0)
 1052            {
 31053                v = obj.Vertices.Count + v + 1;
 1054            }
 1055
 31056            if (v <= 0 || v > obj.Vertices.Count)
 1057            {
 31058                throw new IndexOutOfRangeException();
 1059            }
 1060
 31061            values.MoveNext();
 31062            int vt = valuesCount > 1 && value[values.Current].Length != 0 ? IntParse(value[values.Current]) : 0;
 1063
 31064            if (vt != 0)
 1065            {
 31066                if (vt < 0)
 1067                {
 31068                    vt = obj.TextureVertices.Count + vt + 1;
 1069                }
 1070
 31071                if (vt <= 0 || vt > obj.TextureVertices.Count)
 1072                {
 31073                    throw new IndexOutOfRangeException();
 1074                }
 1075            }
 1076
 31077            values.MoveNext();
 31078            int vn = valuesCount > 2 && value[values.Current].Length != 0 ? IntParse(value[values.Current]) : 0;
 1079
 31080            if (vn != 0)
 1081            {
 31082                if (vn < 0)
 1083                {
 31084                    vn = obj.VertexNormals.Count + vn + 1;
 1085                }
 1086
 31087                if (vn <= 0 || vn > obj.VertexNormals.Count)
 1088                {
 31089                    throw new IndexOutOfRangeException();
 1090                }
 1091            }
 1092
 31093            return new ObjTriplet(v, vt, vn);
 1094        }
 1095
 1096        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 1097        private static ObjCurveIndex ParseCurveIndex(ObjFile obj, ref ReadOnlySpan<char> currentLine, ref SpanSplitEnume
 1098        {
 31099            float start = FloatParse(GetNextValue(ref currentLine, ref values));
 31100            float end = FloatParse(GetNextValue(ref currentLine, ref values));
 31101            int curve2D = IntParse(GetNextValue(ref currentLine, ref values));
 1102
 31103            if (curve2D == 0)
 1104            {
 31105                throw new InvalidDataException("A curve index must specify an index.");
 1106            }
 1107
 31108            if (curve2D < 0)
 1109            {
 31110                curve2D = obj.Curves2D.Count + curve2D + 1;
 1111            }
 1112
 31113            if (curve2D <= 0 || curve2D > obj.Curves2D.Count)
 1114            {
 31115                throw new IndexOutOfRangeException();
 1116            }
 1117
 31118            return new ObjCurveIndex(start, end, curve2D);
 1119        }
 1120
 1121        private static void ParseCurveIndex(List<ObjCurveIndex> curves, ObjFile obj, ReadOnlySpan<char> value0, ref Read
 1122        {
 31123            if (valuesCount < 4)
 1124            {
 31125                throw new InvalidDataException(string.Concat("A ", value0.ToString(), " statement must specify at least 
 1126            }
 1127
 31128            if ((valuesCount - 1) % 3 != 0)
 1129            {
 31130                throw new InvalidDataException(string.Concat("A ", value0.ToString(), " statement has too many values.")
 1131            }
 1132
 31133            for (int i = 1; i < valuesCount; i += 3)
 1134            {
 31135                curves.Add(ParseCurveIndex(obj, ref currentLine, ref values));
 1136            }
 31137        }
 1138
 1139        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 1140        private static void ParseFreeFormType(ObjFileReaderContext context, ref ReadOnlySpan<char> currentLine, ref Span
 1141        {
 31142            if (valuesCount < 2)
 1143            {
 31144                throw new InvalidDataException("A cstype statement must specify a value.");
 1145            }
 1146
 1147            string type;
 1148
 31149            if (valuesCount == 2)
 1150            {
 31151                context.IsRationalForm = false;
 31152                type = GetNextValue(ref currentLine, ref values).ToString();
 1153            }
 31154            else if (valuesCount == 3 && GetNextValue(ref currentLine, ref values).Equals("rat", StringComparison.Ordina
 1155            {
 31156                context.IsRationalForm = true;
 31157                type = GetNextValue(ref currentLine, ref values).ToString();
 1158            }
 1159            else
 1160            {
 31161                throw new InvalidDataException("A cstype statement has too many values.");
 1162            }
 1163
 31164            switch (type.ToLowerInvariant())
 1165            {
 1166                case "bmatrix":
 31167                    context.FreeFormType = ObjFreeFormType.BasisMatrix;
 31168                    break;
 1169
 1170                case "bezier":
 31171                    context.FreeFormType = ObjFreeFormType.Bezier;
 31172                    break;
 1173
 1174                case "bspline":
 31175                    context.FreeFormType = ObjFreeFormType.BSpline;
 31176                    break;
 1177
 1178                case "cardinal":
 31179                    context.FreeFormType = ObjFreeFormType.Cardinal;
 31180                    break;
 1181
 1182                case "taylor":
 31183                    context.FreeFormType = ObjFreeFormType.Taylor;
 31184                    break;
 1185
 1186                default:
 31187                    throw new InvalidDataException("A cstype statement has an unknown type.");
 1188            }
 1189        }
 1190
 1191        private static void ParseSurfaceConnection(ObjFile obj, ref ReadOnlySpan<char> currentLine, ref SpanSplitEnumera
 1192        {
 31193            if (valuesCount < 9)
 1194            {
 31195                throw new InvalidDataException("A con statement must specify 8 values.");
 1196            }
 1197
 31198            if (valuesCount != 9)
 1199            {
 31200                throw new InvalidDataException("A con statement has too many values.");
 1201            }
 1202
 31203            int surface1 = IntParse(GetNextValue(ref currentLine, ref values));
 1204
 31205            if (surface1 == 0)
 1206            {
 31207                throw new InvalidDataException("A con statement must specify a surface index.");
 1208            }
 1209
 31210            if (surface1 < 0)
 1211            {
 31212                surface1 = obj.Surfaces.Count + surface1 + 1;
 1213            }
 1214
 31215            if (surface1 <= 0 || surface1 > obj.Surfaces.Count)
 1216            {
 31217                throw new IndexOutOfRangeException();
 1218            }
 1219
 31220            var curve1 = ParseCurveIndex(obj, ref currentLine, ref values);
 1221
 31222            int surface2 = IntParse(GetNextValue(ref currentLine, ref values));
 1223
 31224            if (surface2 == 0)
 1225            {
 31226                throw new InvalidDataException("A con statement must specify a surface index.");
 1227            }
 1228
 31229            if (surface2 < 0)
 1230            {
 31231                surface2 = obj.Surfaces.Count + surface2 + 1;
 1232            }
 1233
 31234            if (surface2 <= 0 || surface2 > obj.Surfaces.Count)
 1235            {
 31236                throw new IndexOutOfRangeException();
 1237            }
 1238
 31239            var curve2 = ParseCurveIndex(obj, ref currentLine, ref values);
 1240
 31241            var connection = new ObjSurfaceConnection
 31242            {
 31243                Surface1 = surface1,
 31244                Curve2D1 = curve1,
 31245                Surface2 = surface2,
 31246                Curve2D2 = curve2
 31247            };
 1248
 31249            obj.SurfaceConnections.Add(connection);
 31250        }
 1251
 1252        private static void ParseGroupName(ObjFileReaderContext context, ref ReadOnlySpan<char> currentLine, ref SpanSpl
 1253        {
 31254            context.GroupNames.Clear();
 1255
 31256            if (context.Settings.OnlyOneGroupNamePerLine)
 1257            {
 31258                var sb = new StringBuilder();
 1259
 31260                sb.Append(GetNextValue(ref currentLine, ref values));
 1261
 31262                for (int i = 2; i < valuesCount; i++)
 1263                {
 31264                    sb.Append(' ');
 31265                    sb.Append(GetNextValue(ref currentLine, ref values));
 1266                }
 1267
 1268
 31269                var name = sb.ToString();
 31270                if (!string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
 1271                {
 31272                    context.GroupNames.Add(name);
 1273                }
 1274            }
 1275            else
 1276            {
 31277                for (int i = 1; i < valuesCount; i++)
 1278                {
 31279                    var name = GetNextValue(ref currentLine, ref values).ToString();
 1280
 31281                    if (!string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
 1282                    {
 31283                        context.GroupNames.Add(name);
 1284                    }
 1285                }
 1286            }
 1287
 31288            context.GetCurrentGroups();
 31289        }
 1290
 1291        private static ObjApproximationTechnique ParseApproximationTechnique(ReadOnlySpan<char> value0, ref ReadOnlySpan
 1292        {
 1293            ObjApproximationTechnique technique;
 1294
 31295            if (valuesCount < 2)
 1296            {
 31297                throw new InvalidDataException(string.Concat("A ", value0.ToString(), " statement must specify a techniq
 1298            }
 1299
 31300            string value1 = GetNextValue(ref currentLine, ref values).ToString().ToLowerInvariant();
 1301
 1302            switch (value1)
 1303            {
 1304                case "cparm":
 1305                    {
 31306                        if (valuesCount < 3)
 1307                        {
 31308                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparm statement must
 1309                        }
 1310
 31311                        if (valuesCount != 3)
 1312                        {
 31313                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparm statement has 
 1314                        }
 1315
 31316                        float res = FloatParse(GetNextValue(ref currentLine, ref values));
 31317                        technique = new ObjConstantParametricSubdivisionTechnique(res);
 31318                        break;
 1319                    }
 1320
 1321                case "cparma":
 1322                    {
 31323                        if (valuesCount < 4)
 1324                        {
 31325                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparma statement mus
 1326                        }
 1327
 31328                        if (valuesCount != 4)
 1329                        {
 31330                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparma statement has
 1331                        }
 1332
 31333                        float resU = FloatParse(GetNextValue(ref currentLine, ref values));
 31334                        float resV = FloatParse(GetNextValue(ref currentLine, ref values));
 31335                        technique = new ObjConstantParametricSubdivisionTechnique(resU, resV);
 31336                        break;
 1337                    }
 1338
 1339                case "cparmb":
 1340                    {
 31341                        if (valuesCount < 3)
 1342                        {
 31343                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparmb statement mus
 1344                        }
 1345
 31346                        if (valuesCount != 3)
 1347                        {
 31348                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cparmb statement has
 1349                        }
 1350
 31351                        float resU = FloatParse(GetNextValue(ref currentLine, ref values));
 31352                        technique = new ObjConstantParametricSubdivisionTechnique(resU);
 31353                        break;
 1354                    }
 1355
 1356                case "cspace":
 1357                    {
 31358                        if (valuesCount < 3)
 1359                        {
 31360                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cspace statement mus
 1361                        }
 1362
 31363                        if (valuesCount != 3)
 1364                        {
 31365                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " cspace statement has
 1366                        }
 1367
 31368                        float length = FloatParse(GetNextValue(ref currentLine, ref values));
 31369                        technique = new ObjConstantSpatialSubdivisionTechnique(length);
 31370                        break;
 1371                    }
 1372
 1373                case "curv":
 1374                    {
 31375                        if (valuesCount < 4)
 1376                        {
 31377                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " curv statement must 
 1378                        }
 1379
 31380                        if (valuesCount != 4)
 1381                        {
 31382                            throw new InvalidDataException(string.Concat("A ", value0.ToString(), " curv statement has t
 1383                        }
 1384
 31385                        float distance = FloatParse(GetNextValue(ref currentLine, ref values));
 31386                        float angle = FloatParse(GetNextValue(ref currentLine, ref values));
 31387                        technique = new ObjCurvatureDependentSubdivisionTechnique(distance, angle);
 31388                        break;
 1389                    }
 1390
 1391                default:
 31392                    throw new InvalidDataException(string.Concat("A ", value0.ToString(), " statement contains an unknow
 1393            }
 1394
 31395            return technique;
 1396        }
 1397    }
 1398}
 1399
 1400#endif

Methods/Properties

FromStream(System.IO.Stream, JeremyAnsel.Media.WavefrontObj.ObjFileReaderSettings)
ParseTriplet(JeremyAnsel.Media.WavefrontObj.ObjFile, string)
ParseCurveIndex(JeremyAnsel.Media.WavefrontObj.ObjFile, string[], int)
ParseCurveIndex(System.Collections.Generic.List<JeremyAnsel.Media.WavefrontObj.ObjCurveIndex>, JeremyAnsel.Media.WavefrontObj.ObjFile, string[])
ParseFreeFormType(JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext, string[])
ParseSurfaceConnection(JeremyAnsel.Media.WavefrontObj.ObjFile, string[])
ParseGroupName(string[], JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext)
ParseApproximationTechnique(string[])
MoveNextSkipEmpty(ref System.MemoryExtensions.SpanSplitEnumerator<char>)
MoveNextSkipEmpty(ref Polyfills.Polyfill.SpanSplitEnumerator<char>)
GetNextValue(ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>)
GetNextValue(ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>)
FloatParse(System.ReadOnlySpan<char>)
IntParse(System.ReadOnlySpan<char>)
LongParse(System.ReadOnlySpan<char>)
FromStream(System.IO.Stream, JeremyAnsel.Media.WavefrontObj.ObjFileReaderSettings)
ParseTriplet(JeremyAnsel.Media.WavefrontObj.ObjFile, System.ReadOnlySpan<char>)
ParseCurveIndex(JeremyAnsel.Media.WavefrontObj.ObjFile, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>)
ParseCurveIndex(JeremyAnsel.Media.WavefrontObj.ObjFile, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>)
ParseCurveIndex(System.Collections.Generic.List<JeremyAnsel.Media.WavefrontObj.ObjCurveIndex>, JeremyAnsel.Media.WavefrontObj.ObjFile, System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseCurveIndex(System.Collections.Generic.List<JeremyAnsel.Media.WavefrontObj.ObjCurveIndex>, JeremyAnsel.Media.WavefrontObj.ObjFile, System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)
ParseFreeFormType(JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseFreeFormType(JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)
ParseSurfaceConnection(JeremyAnsel.Media.WavefrontObj.ObjFile, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseSurfaceConnection(JeremyAnsel.Media.WavefrontObj.ObjFile, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)
ParseGroupName(JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseGroupName(JeremyAnsel.Media.WavefrontObj.ObjFileReaderContext, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)
ParseApproximationTechnique(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseApproximationTechnique(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)