< Summary

Line coverage
99%
Covered lines: 992
Uncovered lines: 7
Coverable lines: 999
Total lines: 2430
Line coverage: 99.2%
Branch coverage
90%
Covered branches: 711
Total branches: 787
Branch coverage: 90.3%
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\ObjMaterialFileReader.cs

#LineLine coverage
 1// <copyright file="ObjMaterialFileReader.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 ObjMaterialFileReader
 16    {
 17        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 18        public static ObjMaterialFile FromStream(Stream? stream, ObjMaterialFileReaderSettings settings)
 19        {
 120            if (stream == null)
 21            {
 122                throw new ArgumentNullException(nameof(stream));
 23            }
 24
 125            var mtl = new ObjMaterialFile();
 126            var lineReader = new LineReader();
 27
 128            ObjMaterial? currentMaterial = null;
 29
 130            foreach (string currentLine in lineReader.Read(stream))
 31            {
 132                string[] values = currentLine.Split(LineReader.LineSeparators, StringSplitOptions.RemoveEmptyEntries);
 33
 134                switch (values[0].ToLowerInvariant())
 35                {
 36                    case "newmtl":
 137                        if (values.Length < 2)
 38                        {
 139                            throw new InvalidDataException("A newmtl statement must specify a name.");
 40                        }
 41
 142                        currentMaterial = new ObjMaterial
 143                        {
 144                            Name = string.Join(" ", values, 1, values.Length - 1)
 145                        };
 46
 147                        mtl.Materials.Add(currentMaterial);
 48
 149                        break;
 50
 51                    case "ka":
 152                        if (currentMaterial == null)
 53                        {
 154                            throw new InvalidDataException("The material name is not specified.");
 55                        }
 56
 157                        currentMaterial.AmbientColor = ObjMaterialFileReader.ParseMaterialColor("Ka", values);
 158                        break;
 59
 60                    case "kd":
 161                        if (currentMaterial == null)
 62                        {
 163                            throw new InvalidDataException("The material name is not specified.");
 64                        }
 65
 166                        currentMaterial.DiffuseColor = ObjMaterialFileReader.ParseMaterialColor("Kd", values);
 167                        break;
 68
 69                    case "ke":
 170                        if (currentMaterial == null)
 71                        {
 172                            throw new InvalidDataException("The material name is not specified.");
 73                        }
 74
 175                        currentMaterial.EmissiveColor = ObjMaterialFileReader.ParseMaterialColor("Ke", values);
 176                        break;
 77
 78                    case "ks":
 179                        if (currentMaterial == null)
 80                        {
 181                            throw new InvalidDataException("The material name is not specified.");
 82                        }
 83
 184                        currentMaterial.SpecularColor = ObjMaterialFileReader.ParseMaterialColor("Ks", values);
 185                        break;
 86
 87                    case "tf":
 188                        if (currentMaterial == null)
 89                        {
 190                            throw new InvalidDataException("The material name is not specified.");
 91                        }
 92
 193                        currentMaterial.TransmissionColor = ObjMaterialFileReader.ParseMaterialColor("Tf", values);
 194                        break;
 95
 96                    case "illum":
 197                        if (currentMaterial == null)
 98                        {
 199                            throw new InvalidDataException("The material name is not specified.");
 100                        }
 101
 1102                        if (values.Length < 2)
 103                        {
 1104                            throw new InvalidDataException("An illum statement must specify an illumination model.");
 105                        }
 106
 1107                        if (values.Length != 2)
 108                        {
 1109                            throw new InvalidDataException("An illum statement has too many values.");
 110                        }
 111
 1112                        currentMaterial.IlluminationModel = int.Parse(values[1], CultureInfo.InvariantCulture);
 1113                        break;
 114
 115                    case "d":
 1116                        if (currentMaterial == null)
 117                        {
 1118                            throw new InvalidDataException("The material name is not specified.");
 119                        }
 120
 1121                        if (values.Length < 2)
 122                        {
 1123                            throw new InvalidDataException("A d statement must specify a factor.");
 124                        }
 125
 1126                        if (string.Equals(values[1], "-halo", StringComparison.OrdinalIgnoreCase))
 127                        {
 1128                            if (values.Length < 3)
 129                            {
 1130                                throw new InvalidDataException("A d statement must specify a factor.");
 131                            }
 132
 1133                            if (values.Length != 3)
 134                            {
 1135                                throw new InvalidDataException("A d statement has too many values.");
 136                            }
 137
 1138                            currentMaterial.IsHaloDissolve = true;
 1139                            currentMaterial.DissolveFactor = float.Parse(values[2], CultureInfo.InvariantCulture);
 140                        }
 141                        else
 142                        {
 1143                            if (values.Length != 2)
 144                            {
 1145                                throw new InvalidDataException("A d statement has too many values.");
 146                            }
 147
 1148                            currentMaterial.DissolveFactor = float.Parse(values[1], CultureInfo.InvariantCulture);
 149                        }
 150
 1151                        break;
 152
 153                    case "ns":
 1154                        if (currentMaterial == null)
 155                        {
 1156                            throw new InvalidDataException("The material name is not specified.");
 157                        }
 158
 1159                        if (values.Length < 2)
 160                        {
 1161                            throw new InvalidDataException("A Ns statement must specify a specular exponent.");
 162                        }
 163
 1164                        if (values.Length != 2)
 165                        {
 1166                            throw new InvalidDataException("A Ns statement has too many values.");
 167                        }
 168
 1169                        currentMaterial.SpecularExponent = float.Parse(values[1], CultureInfo.InvariantCulture);
 1170                        break;
 171
 172                    case "sharpness":
 1173                        if (currentMaterial == null)
 174                        {
 1175                            throw new InvalidDataException("The material name is not specified.");
 176                        }
 177
 1178                        if (values.Length < 2)
 179                        {
 1180                            throw new InvalidDataException("A sharpness statement must specify a sharpness value.");
 181                        }
 182
 1183                        if (values.Length != 2)
 184                        {
 1185                            throw new InvalidDataException("A sharpness statement has too many values.");
 186                        }
 187
 1188                        currentMaterial.Sharpness = int.Parse(values[1], CultureInfo.InvariantCulture);
 1189                        break;
 190
 191                    case "ni":
 1192                        if (currentMaterial == null)
 193                        {
 1194                            throw new InvalidDataException("The material name is not specified.");
 195                        }
 196
 1197                        if (values.Length < 2)
 198                        {
 1199                            throw new InvalidDataException("A Ni statement must specify an optical density.");
 200                        }
 201
 1202                        if (values.Length != 2)
 203                        {
 1204                            throw new InvalidDataException("A Ni statement has too many values.");
 205                        }
 206
 1207                        currentMaterial.OpticalDensity = float.Parse(values[1], CultureInfo.InvariantCulture);
 1208                        break;
 209
 210                    case "map_aat":
 1211                        if (currentMaterial == null)
 212                        {
 1213                            throw new InvalidDataException("The material name is not specified.");
 214                        }
 215
 1216                        if (values.Length < 2)
 217                        {
 1218                            throw new InvalidDataException("A map_aat statement must specify a value.");
 219                        }
 220
 1221                        if (values.Length != 2)
 222                        {
 1223                            throw new InvalidDataException("A map_aat statement has too many values.");
 224                        }
 225
 1226                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 227                        {
 1228                            currentMaterial.IsAntiAliasingEnabled = true;
 229                        }
 1230                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 231                        {
 1232                            currentMaterial.IsAntiAliasingEnabled = false;
 233                        }
 234                        else
 235                        {
 1236                            throw new InvalidDataException("A map_aat statement must specify on or off.");
 237                        }
 238
 239                        break;
 240
 241                    case "map_ka":
 1242                        if (currentMaterial == null)
 243                        {
 1244                            throw new InvalidDataException("The material name is not specified.");
 245                        }
 246
 1247                        currentMaterial.AmbientMap = ObjMaterialFileReader.ParseMaterialMap("map_Ka", values, currentLin
 1248                        break;
 249
 250                    case "map_kd":
 1251                        if (currentMaterial == null)
 252                        {
 1253                            throw new InvalidDataException("The material name is not specified.");
 254                        }
 255
 1256                        currentMaterial.DiffuseMap = ObjMaterialFileReader.ParseMaterialMap("map_Kd", values, currentLin
 1257                        break;
 258
 259                    case "map_ke":
 1260                        if (currentMaterial == null)
 261                        {
 1262                            throw new InvalidDataException("The material name is not specified.");
 263                        }
 264
 1265                        currentMaterial.EmissiveMap = ObjMaterialFileReader.ParseMaterialMap("map_Ke", values, currentLi
 1266                        break;
 267
 268                    case "map_ks":
 1269                        if (currentMaterial == null)
 270                        {
 1271                            throw new InvalidDataException("The material name is not specified.");
 272                        }
 273
 1274                        currentMaterial.SpecularMap = ObjMaterialFileReader.ParseMaterialMap("map_Ks", values, currentLi
 1275                        break;
 276
 277                    case "map_ns":
 1278                        if (currentMaterial == null)
 279                        {
 1280                            throw new InvalidDataException("The material name is not specified.");
 281                        }
 282
 1283                        currentMaterial.SpecularExponentMap = ObjMaterialFileReader.ParseMaterialMap("map_Ns", values, c
 1284                        break;
 285
 286                    case "map_d":
 287                    case "map_tr":
 1288                        if (currentMaterial == null)
 289                        {
 1290                            throw new InvalidDataException("The material name is not specified.");
 291                        }
 292
 1293                        currentMaterial.DissolveMap = ObjMaterialFileReader.ParseMaterialMap("map_d", values, currentLin
 1294                        break;
 295
 296                    case "decal":
 297                    case "map_decal":
 1298                        if (currentMaterial == null)
 299                        {
 1300                            throw new InvalidDataException("The material name is not specified.");
 301                        }
 302
 1303                        currentMaterial.DecalMap = ObjMaterialFileReader.ParseMaterialMap("decal", values, currentLine, 
 1304                        break;
 305
 306                    case "disp":
 307                    case "map_disp":
 1308                        if (currentMaterial == null)
 309                        {
 1310                            throw new InvalidDataException("The material name is not specified.");
 311                        }
 312
 1313                        currentMaterial.DispMap = ObjMaterialFileReader.ParseMaterialMap("disp", values, currentLine, se
 1314                        break;
 315
 316                    case "bump":
 317                    case "map_bump":
 1318                        if (currentMaterial == null)
 319                        {
 1320                            throw new InvalidDataException("The material name is not specified.");
 321                        }
 322
 1323                        currentMaterial.BumpMap = ObjMaterialFileReader.ParseMaterialMap("bump", values, currentLine, se
 1324                        break;
 325
 326                    case "refl":
 327                    case "map_refl":
 1328                        if (currentMaterial == null)
 329                        {
 1330                            throw new InvalidDataException("The material name is not specified.");
 331                        }
 332
 1333                        if (values.Length < 4)
 334                        {
 1335                            throw new InvalidDataException("A refl statement must specify a type and a file name.");
 336                        }
 337
 1338                        if (!string.Equals(values[1], "-type", StringComparison.OrdinalIgnoreCase))
 339                        {
 1340                            throw new InvalidDataException("A refl statement must specify a type.");
 341                        }
 342
 1343                        switch (values[2].ToLowerInvariant())
 344                        {
 345                            case "sphere":
 1346                                currentMaterial.ReflectionMap.Sphere = ObjMaterialFileReader.ParseMaterialMap("refl", va
 1347                                break;
 348
 349                            case "cube_top":
 1350                                currentMaterial.ReflectionMap.CubeTop = ObjMaterialFileReader.ParseMaterialMap("refl", v
 1351                                break;
 352
 353                            case "cube_bottom":
 1354                                currentMaterial.ReflectionMap.CubeBottom = ObjMaterialFileReader.ParseMaterialMap("refl"
 1355                                break;
 356
 357                            case "cube_front":
 1358                                currentMaterial.ReflectionMap.CubeFront = ObjMaterialFileReader.ParseMaterialMap("refl",
 1359                                break;
 360
 361                            case "cube_back":
 1362                                currentMaterial.ReflectionMap.CubeBack = ObjMaterialFileReader.ParseMaterialMap("refl", 
 1363                                break;
 364
 365                            case "cube_left":
 1366                                currentMaterial.ReflectionMap.CubeLeft = ObjMaterialFileReader.ParseMaterialMap("refl", 
 1367                                break;
 368
 369                            case "cube_right":
 1370                                currentMaterial.ReflectionMap.CubeRight = ObjMaterialFileReader.ParseMaterialMap("refl",
 1371                                break;
 372                        }
 373
 374                        break;
 375                    case "pr":
 1376                        if (currentMaterial == null)
 377                        {
 1378                            throw new InvalidDataException("The material name is not specified.");
 379                        }
 380
 1381                        if (values.Length < 2)
 382                        {
 1383                            throw new InvalidDataException("A Pr statement must specify an optical density.");
 384                        }
 385
 1386                        if (values.Length != 2)
 387                        {
 1388                            throw new InvalidDataException("A Pr statement has too many values.");
 389                        }
 390
 1391                        currentMaterial.Roughness = float.Parse(values[1], CultureInfo.InvariantCulture);
 1392                        break;
 393                    case "map_pr":
 1394                        if (currentMaterial == null)
 395                        {
 1396                            throw new InvalidDataException("The material name is not specified.");
 397                        }
 398
 1399                        currentMaterial.RoughnessMap = ObjMaterialFileReader.ParseMaterialMap("map_Pr", values, currentL
 1400                        break;
 401                    case "pm":
 1402                        if (currentMaterial == null)
 403                        {
 1404                            throw new InvalidDataException("The material name is not specified.");
 405                        }
 406
 1407                        if (values.Length < 2)
 408                        {
 1409                            throw new InvalidDataException("A Pm statement must specify an optical density.");
 410                        }
 411
 1412                        if (values.Length != 2)
 413                        {
 1414                            throw new InvalidDataException("A Pm statement has too many values.");
 415                        }
 416
 1417                        currentMaterial.Metallic = float.Parse(values[1], CultureInfo.InvariantCulture);
 1418                        break;
 419                    case "map_pm":
 1420                        if (currentMaterial == null)
 421                        {
 1422                            throw new InvalidDataException("The material name is not specified.");
 423                        }
 424
 1425                        currentMaterial.MetallicMap = ObjMaterialFileReader.ParseMaterialMap("map_Pm", values, currentLi
 1426                        break;
 427                    case "ps":
 1428                        if (currentMaterial == null)
 429                        {
 1430                            throw new InvalidDataException("The material name is not specified.");
 431                        }
 432
 1433                        if (values.Length < 2)
 434                        {
 1435                            throw new InvalidDataException("A Ps statement must specify an optical density.");
 436                        }
 437
 1438                        if (values.Length != 2)
 439                        {
 1440                            throw new InvalidDataException("A Ps statement has too many values.");
 441                        }
 442
 1443                        currentMaterial.Sheen = float.Parse(values[1], CultureInfo.InvariantCulture);
 1444                        break;
 445                    case "map_ps":
 1446                        if (currentMaterial == null)
 447                        {
 1448                            throw new InvalidDataException("The material name is not specified.");
 449                        }
 450
 1451                        currentMaterial.SheenMap = ObjMaterialFileReader.ParseMaterialMap("map_Ps", values, currentLine,
 1452                        break;
 453                    case "pc":
 1454                        if (currentMaterial == null)
 455                        {
 1456                            throw new InvalidDataException("The material name is not specified.");
 457                        }
 458
 1459                        if (values.Length < 2)
 460                        {
 1461                            throw new InvalidDataException("A Pc statement must specify an optical density.");
 462                        }
 463
 1464                        if (values.Length != 2)
 465                        {
 1466                            throw new InvalidDataException("A Pc statement has too many values.");
 467                        }
 468
 1469                        currentMaterial.ClearCoatThickness = float.Parse(values[1], CultureInfo.InvariantCulture);
 1470                        break;
 471                    case "pcr":
 1472                        if (currentMaterial == null)
 473                        {
 1474                            throw new InvalidDataException("The material name is not specified.");
 475                        }
 476
 1477                        if (values.Length < 2)
 478                        {
 1479                            throw new InvalidDataException("A Pcr statement must specify an optical density.");
 480                        }
 481
 1482                        if (values.Length != 2)
 483                        {
 1484                            throw new InvalidDataException("A Pcr statement has too many values.");
 485                        }
 486
 1487                        currentMaterial.ClearCoatRoughness = float.Parse(values[1], CultureInfo.InvariantCulture);
 1488                        break;
 489                    case "aniso":
 1490                        if (currentMaterial == null)
 491                        {
 1492                            throw new InvalidDataException("The material name is not specified.");
 493                        }
 494
 1495                        if (values.Length < 2)
 496                        {
 1497                            throw new InvalidDataException("A aniso statement must specify an optical density.");
 498                        }
 499
 1500                        if (values.Length != 2)
 501                        {
 1502                            throw new InvalidDataException("A aniso statement has too many values.");
 503                        }
 504
 1505                        currentMaterial.Anisotropy = float.Parse(values[1], CultureInfo.InvariantCulture);
 1506                        break;
 507                    case "anisor":
 1508                        if (currentMaterial == null)
 509                        {
 1510                            throw new InvalidDataException("The material name is not specified.");
 511                        }
 512
 1513                        if (values.Length < 2)
 514                        {
 1515                            throw new InvalidDataException("A anisor statement must specify an optical density.");
 516                        }
 517
 1518                        if (values.Length != 2)
 519                        {
 1520                            throw new InvalidDataException("A anisor statement has too many values.");
 521                        }
 522
 1523                        currentMaterial.AnisotropyRotation = float.Parse(values[1], CultureInfo.InvariantCulture);
 1524                        break;
 525                    case "norm":
 1526                        if (currentMaterial == null)
 527                        {
 1528                            throw new InvalidDataException("The material name is not specified.");
 529                        }
 530
 1531                        currentMaterial.Norm = ObjMaterialFileReader.ParseMaterialMap("norm", values, currentLine, setti
 532                        break;
 533                }
 534            }
 535
 1536            mtl.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 537
 1538            return mtl;
 539        }
 540
 541        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 542        private static ObjMaterialColor ParseMaterialColor(string statement, string[] values)
 543        {
 1544            if (values.Length < 2)
 545            {
 1546                throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a color."));
 547            }
 548
 1549            var color = new ObjMaterialColor();
 550
 1551            int index = 1;
 552
 1553            switch (values[1].ToLowerInvariant())
 554            {
 555                case "spectral":
 1556                    index++;
 557
 1558                    if (values.Length - index < 1)
 559                    {
 1560                        throw new InvalidDataException(string.Concat("A ", statement, " spectral statement must specify 
 561                    }
 562
 1563                    color.SpectralFileName = values[index];
 1564                    index++;
 565
 1566                    if (values.Length > index)
 567                    {
 1568                        color.SpectralFactor = float.Parse(values[index], CultureInfo.InvariantCulture);
 1569                        index++;
 570                    }
 571
 1572                    break;
 573
 574                case "xyz":
 575                    {
 1576                        index++;
 577
 1578                        if (values.Length - index < 1)
 579                        {
 1580                            throw new InvalidDataException(string.Concat("A ", statement, " xyz statement must specify a
 581                        }
 582
 1583                        color.UseXYZColorSpace = true;
 584
 1585                        var xyz = new ObjVector3();
 586
 1587                        xyz.X = float.Parse(values[index], CultureInfo.InvariantCulture);
 1588                        index++;
 589
 1590                        if (values.Length > index)
 591                        {
 1592                            if (values.Length - index < 2)
 593                            {
 1594                                throw new InvalidDataException(string.Concat("A ", statement, " xyz statement must speci
 595                            }
 596
 1597                            xyz.Y = float.Parse(values[index], CultureInfo.InvariantCulture);
 1598                            xyz.Z = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1599                            index += 2;
 600                        }
 601                        else
 602                        {
 1603                            xyz.Y = xyz.X;
 1604                            xyz.Z = xyz.X;
 605                        }
 606
 1607                        color.Color = xyz;
 1608                        break;
 609                    }
 610
 611                default:
 612                    {
 1613                        var rgb = new ObjVector3();
 614
 1615                        rgb.X = float.Parse(values[index], CultureInfo.InvariantCulture);
 1616                        index++;
 617
 1618                        if (values.Length > index)
 619                        {
 1620                            if (values.Length - index < 2)
 621                            {
 1622                                throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a
 623                            }
 624
 1625                            rgb.Y = float.Parse(values[index], CultureInfo.InvariantCulture);
 1626                            rgb.Z = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1627                            index += 2;
 628                        }
 629                        else
 630                        {
 1631                            rgb.Y = rgb.X;
 1632                            rgb.Z = rgb.X;
 633                        }
 634
 1635                        color.Color = rgb;
 636                        break;
 637                    }
 638            }
 639
 1640            if (index != values.Length)
 641            {
 1642                throw new InvalidDataException(string.Concat("A ", statement, " statement has too many values."));
 643            }
 644
 1645            return color;
 646        }
 647
 648        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 649        private static ObjMaterialMap ParseMaterialMap(string statement, string[] values, string currentLine, ObjMateria
 650        {
 1651            var map = new ObjMaterialMap();
 652
 1653            for (int index = 0; index < values.Length;)
 654            {
 1655                index++;
 656
 1657                if (values.Length - index < 1)
 658                {
 1659                    throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a filename.")
 660                }
 661
 1662                switch (values[index].ToLowerInvariant())
 663                {
 664                    case "-type":
 1665                        if (values.Length - index < 2)
 666                        {
 1667                            throw new InvalidDataException(string.Concat("A ", statement, " -type option must specify a 
 668                        }
 669
 1670                        index++;
 1671                        break;
 672
 673                    case "-blenu":
 1674                        if (values.Length - index < 2)
 675                        {
 1676                            throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must specify a
 677                        }
 678
 1679                        if (string.Equals(values[index + 1], "on", StringComparison.OrdinalIgnoreCase))
 680                        {
 1681                            map.IsHorizontalBlendingEnabled = true;
 682                        }
 1683                        else if (string.Equals(values[index + 1], "off", StringComparison.OrdinalIgnoreCase))
 684                        {
 1685                            map.IsHorizontalBlendingEnabled = false;
 686                        }
 687                        else
 688                        {
 1689                            throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must specify o
 690                        }
 691
 1692                        index++;
 1693                        break;
 694
 695                    case "-blenv":
 1696                        if (values.Length - index < 2)
 697                        {
 1698                            throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must specify a
 699                        }
 700
 1701                        if (string.Equals(values[index + 1], "on", StringComparison.OrdinalIgnoreCase))
 702                        {
 1703                            map.IsVerticalBlendingEnabled = true;
 704                        }
 1705                        else if (string.Equals(values[index + 1], "off", StringComparison.OrdinalIgnoreCase))
 706                        {
 1707                            map.IsVerticalBlendingEnabled = false;
 708                        }
 709                        else
 710                        {
 1711                            throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must specify o
 712                        }
 713
 1714                        index++;
 1715                        break;
 716
 717                    case "-bm":
 1718                        if (values.Length - index < 2)
 719                        {
 1720                            throw new InvalidDataException(string.Concat("A ", statement, " -bm option must specify a va
 721                        }
 722
 1723                        map.BumpMultiplier = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 724
 1725                        index++;
 1726                        break;
 727
 728                    case "-boost":
 1729                        if (values.Length - index < 2)
 730                        {
 1731                            throw new InvalidDataException(string.Concat("A ", statement, " -boost option must specify a
 732                        }
 733
 1734                        map.Boost = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 735
 1736                        index++;
 1737                        break;
 738
 739                    case "-cc":
 1740                        if (values.Length - index < 2)
 741                        {
 1742                            throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify a va
 743                        }
 744
 1745                        if (string.Equals(values[index + 1], "on", StringComparison.OrdinalIgnoreCase))
 746                        {
 1747                            map.IsColorCorrectionEnabled = true;
 748                        }
 1749                        else if (string.Equals(values[index + 1], "off", StringComparison.OrdinalIgnoreCase))
 750                        {
 1751                            map.IsColorCorrectionEnabled = false;
 752                        }
 753                        else
 754                        {
 1755                            throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify on o
 756                        }
 757
 1758                        index++;
 1759                        break;
 760
 761                    case "-clamp":
 1762                        if (values.Length - index < 2)
 763                        {
 1764                            throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must specify a
 765                        }
 766
 1767                        if (string.Equals(values[index + 1], "on", StringComparison.OrdinalIgnoreCase))
 768                        {
 1769                            map.IsClampingEnabled = true;
 770                        }
 1771                        else if (string.Equals(values[index + 1], "off", StringComparison.OrdinalIgnoreCase))
 772                        {
 1773                            map.IsClampingEnabled = false;
 774                        }
 775                        else
 776                        {
 1777                            throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must specify o
 778                        }
 779
 1780                        index++;
 1781                        break;
 782
 783                    case "-imfchan":
 1784                        if (values.Length - index < 2)
 785                        {
 1786                            throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must specify
 787                        }
 788
 1789                        switch (values[index + 1].ToLowerInvariant())
 790                        {
 791                            case "r":
 1792                                map.ScalarChannel = ObjMapChannel.Red;
 1793                                break;
 794
 795                            case "g":
 1796                                map.ScalarChannel = ObjMapChannel.Green;
 1797                                break;
 798
 799                            case "b":
 1800                                map.ScalarChannel = ObjMapChannel.Blue;
 1801                                break;
 802
 803                            case "m":
 1804                                map.ScalarChannel = ObjMapChannel.Matte;
 1805                                break;
 806
 807                            case "l":
 1808                                map.ScalarChannel = ObjMapChannel.Luminance;
 1809                                break;
 810
 811                            case "z":
 1812                                map.ScalarChannel = ObjMapChannel.Depth;
 1813                                break;
 814
 815                            default:
 1816                                throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must spe
 817                        }
 818
 1819                        index++;
 1820                        break;
 821
 822                    case "-mm":
 1823                        if (values.Length - index < 3)
 824                        {
 1825                            throw new InvalidDataException(string.Concat("A ", statement, " -mm option must specify a ba
 826                        }
 827
 1828                        map.ModifierBase = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1829                        map.ModifierGain = float.Parse(values[index + 2], CultureInfo.InvariantCulture);
 830
 1831                        index += 2;
 1832                        break;
 833
 834                    case "-o":
 1835                        if (values.Length - index < 2)
 836                        {
 1837                            throw new InvalidDataException(string.Concat("A ", statement, " -o option must specify at le
 838                        }
 839
 1840                        var offset = new ObjVector3();
 841
 1842                        offset.X = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1843                        index++;
 844
 1845                        if (values.Length - index > 2)
 846                        {
 1847                            if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out 
 848                            {
 1849                                offset.Y = v;
 1850                                index++;
 851                            }
 852                            else
 853                            {
 1854                                map.Offset = offset;
 1855                                break;
 856                            }
 857
 1858                            if (values.Length - index > 2)
 859                            {
 1860                                if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, 
 861                                {
 1862                                    offset.Z = v;
 1863                                    index++;
 864                                }
 865                                else
 866                                {
 1867                                    map.Offset = offset;
 1868                                    break;
 869                                }
 870                            }
 871                        }
 872
 1873                        map.Offset = offset;
 1874                        break;
 875
 876                    case "-s":
 877                        {
 1878                            if (values.Length - index < 2)
 879                            {
 1880                                throw new InvalidDataException(string.Concat("A ", statement, " -s option must specify a
 881                            }
 882
 1883                            var scale = new ObjVector3(1.0f, 1.0f, 1.0f);
 884
 1885                            scale.X = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1886                            index++;
 887
 1888                            if (values.Length - index > 2)
 889                            {
 1890                                if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, 
 891                                {
 1892                                    scale.Y = v;
 1893                                    index++;
 894                                }
 895                                else
 896                                {
 1897                                    map.Scale = scale;
 1898                                    break;
 899                                }
 900
 1901                                if (values.Length - index > 2)
 902                                {
 1903                                    if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCultu
 904                                    {
 1905                                        scale.Z = v;
 1906                                        index++;
 907                                    }
 908                                    else
 909                                    {
 1910                                        map.Scale = scale;
 1911                                        break;
 912                                    }
 913                                }
 914                            }
 915
 1916                            map.Scale = scale;
 1917                            break;
 918                        }
 919
 920                    case "-t":
 921                        {
 1922                            if (values.Length - index < 2)
 923                            {
 1924                                throw new InvalidDataException(string.Concat("A ", statement, " -t option must specify a
 925                            }
 926
 1927                            var turbulence = new ObjVector3();
 928
 1929                            turbulence.X = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1930                            index++;
 931
 1932                            if (values.Length - index > 2)
 933                            {
 1934                                if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, 
 935                                {
 1936                                    turbulence.Y = v;
 1937                                    index++;
 938                                }
 939                                else
 940                                {
 1941                                    map.Turbulence = turbulence;
 1942                                    break;
 943                                }
 944
 1945                                if (values.Length - index > 2)
 946                                {
 1947                                    if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCultu
 948                                    {
 1949                                        turbulence.Z = v;
 1950                                        index++;
 951                                    }
 952                                    else
 953                                    {
 1954                                        map.Turbulence = turbulence;
 1955                                        break;
 956                                    }
 957                                }
 958                            }
 959
 1960                            map.Turbulence = turbulence;
 1961                            break;
 962                        }
 963
 964                    case "-texres":
 1965                        if (values.Length - index < 2)
 966                        {
 1967                            throw new InvalidDataException(string.Concat("A ", statement, " -texres option must specify 
 968                        }
 969
 1970                        map.TextureResolution = int.Parse(values[index + 1], CultureInfo.InvariantCulture);
 971
 1972                        index++;
 1973                        break;
 974
 975                    default:
 976                        {
 1977                            if (settings.KeepWhitespacesOfMapFileReferences)
 978                            {
 1979                                var charsRead = 0;
 1980                                for (var i = 1; i < index; i++)
 981                                {
 1982                                    charsRead += values[i].Length;
 983                                }
 1984                                map.FileName = currentLine.Remove(0, statement.Length + charsRead + index);
 985                            }
 986                            else
 987                            {
 1988                                string filename = string.Join(" ", values, index, values.Length - index);
 989
 1990                                map.FileName = filename;
 991                            }
 1992                            index = values.Length;
 993
 994                            break;
 995                        }
 996                }
 997            }
 998
 1999            return map;
 1000        }
 1001    }
 1002}
 1003
 1004#endif

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

#LineLine coverage
 1// <copyright file="ObjMaterialFileReader9.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 ObjMaterialFileReader
 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            }
 333        }
 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 bool TryFloatParse(ReadOnlySpan<char> s, out float value)
 42        {
 343            return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
 44        }
 45
 46        private static bool TryIntParse(ReadOnlySpan<char> s, out int value)
 47        {
 348            return int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
 49        }
 50
 51        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 52        public static ObjMaterialFile FromStream(Stream? stream, ObjMaterialFileReaderSettings settings)
 53        {
 354            if (stream == null)
 55            {
 356                throw new ArgumentNullException(nameof(stream));
 57            }
 58
 359            var mtl = new ObjMaterialFile();
 360            var lineReader = new LineReader9();
 61
 362            ObjMaterial? currentMaterial = null;
 63
 364            int valueBufferSize = 16;
 365            Span<char> valueBuffer = stackalloc char[valueBufferSize];
 66
 367            foreach (var currentLineString in lineReader.Read9(stream))
 68            {
 369                ReadOnlySpan<char> currentLine = currentLineString.Buffer.AsSpan()[..currentLineString.Length];
 70
 371                int valuesCount = 0;
 72
 373                foreach (Range range in currentLine.SplitAny(LineReader9.LineSeparators))
 74                {
 375                    if (currentLine[range].Length == 0)
 76                    {
 77                        continue;
 78                    }
 79
 380                    valuesCount++;
 81                }
 82
 383                SpanSplitEnumerator values = currentLine.SplitAny(LineReader9.LineSeparators);
 84
 385                MoveNextSkipEmpty(ref values);
 86
 387                if (values.Current.End.Value - values.Current.Start.Value > valueBufferSize)
 88                {
 89                    continue;
 90                }
 91
 392                ReadOnlySpan<char> value0 = currentLine[values.Current];
 393                int value0Length = value0.ToLowerInvariant(valueBuffer);
 94
 95                //if (value0Length == -1)
 96                //{
 97                //    throw new InvalidDataException("the buffer is too small");
 98                //}
 99
 3100                switch (valueBuffer[..value0Length])
 101                {
 102                    case "newmtl":
 103                        {
 3104                            if (valuesCount < 2)
 105                            {
 3106                                throw new InvalidDataException("A newmtl statement must specify a name.");
 107                            }
 108
 3109                            var sb = new StringBuilder();
 110
 3111                            sb.Append(GetNextValue(ref currentLine, ref values));
 112
 3113                            for (int i = 2; i < valuesCount; i++)
 114                            {
 3115                                sb.Append(' ');
 3116                                sb.Append(GetNextValue(ref currentLine, ref values));
 117                            }
 118
 3119                            currentMaterial = new ObjMaterial
 3120                            {
 3121                                Name = sb.ToString()
 3122                            };
 123
 3124                            mtl.Materials.Add(currentMaterial);
 125
 3126                            break;
 127                        }
 128
 129                    case "ka":
 3130                        if (currentMaterial == null)
 131                        {
 3132                            throw new InvalidDataException("The material name is not specified.");
 133                        }
 134
 3135                        currentMaterial.AmbientColor = ParseMaterialColor("Ka", ref currentLine, ref values, valuesCount
 3136                        break;
 137
 138                    case "kd":
 3139                        if (currentMaterial == null)
 140                        {
 3141                            throw new InvalidDataException("The material name is not specified.");
 142                        }
 143
 3144                        currentMaterial.DiffuseColor = ParseMaterialColor("Kd", ref currentLine, ref values, valuesCount
 3145                        break;
 146
 147                    case "ke":
 3148                        if (currentMaterial == null)
 149                        {
 3150                            throw new InvalidDataException("The material name is not specified.");
 151                        }
 152
 3153                        currentMaterial.EmissiveColor = ParseMaterialColor("Ke", ref currentLine, ref values, valuesCoun
 3154                        break;
 155
 156                    case "ks":
 3157                        if (currentMaterial == null)
 158                        {
 3159                            throw new InvalidDataException("The material name is not specified.");
 160                        }
 161
 3162                        currentMaterial.SpecularColor = ParseMaterialColor("Ks", ref currentLine, ref values, valuesCoun
 3163                        break;
 164
 165                    case "tf":
 3166                        if (currentMaterial == null)
 167                        {
 3168                            throw new InvalidDataException("The material name is not specified.");
 169                        }
 170
 3171                        currentMaterial.TransmissionColor = ParseMaterialColor("Tf", ref currentLine, ref values, values
 3172                        break;
 173
 174                    case "illum":
 175                    {
 3176                        if (currentMaterial == null)
 177                        {
 3178                            throw new InvalidDataException("The material name is not specified.");
 179                        }
 180
 3181                        if (valuesCount < 2)
 182                        {
 3183                            throw new InvalidDataException("An illum statement must specify an illumination model.");
 184                        }
 185
 3186                        if (valuesCount != 2)
 187                        {
 3188                            throw new InvalidDataException("An illum statement has too many values.");
 189                        }
 190
 3191                        if (TryIntParse(GetNextValue(ref currentLine, ref values), out var value))
 192                        {
 3193                            currentMaterial.IlluminationModel = value;
 194                        }
 195
 3196                        break;
 197                    }
 198                    case "d":
 199                        {
 3200                            if (currentMaterial == null)
 201                            {
 3202                                throw new InvalidDataException("The material name is not specified.");
 203                            }
 204
 3205                            if (valuesCount < 2)
 206                            {
 3207                                throw new InvalidDataException("A d statement must specify a factor.");
 208                            }
 209
 3210                            var value1 = GetNextValue(ref currentLine, ref values);
 211
 3212                            if (value1.Equals("-halo", StringComparison.OrdinalIgnoreCase))
 213                            {
 3214                                if (valuesCount < 3)
 215                                {
 3216                                    throw new InvalidDataException("A d statement must specify a factor.");
 217                                }
 218
 3219                                if (valuesCount != 3)
 220                                {
 3221                                    throw new InvalidDataException("A d statement has too many values.");
 222                                }
 223
 3224                                currentMaterial.IsHaloDissolve = true;
 3225                                if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 226                                {
 3227                                    currentMaterial.DissolveFactor = value;
 228                                }
 229                            }
 230                            else
 231                            {
 3232                                if (valuesCount != 2)
 233                                {
 3234                                    throw new InvalidDataException("A d statement has too many values.");
 235                                }
 236
 3237                                if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 238                                {
 3239                                    currentMaterial.DissolveFactor = value;
 240                                }
 241                            }
 242
 3243                            break;
 244                        }
 245
 246                    case "ns":
 247                    {
 3248                        if (currentMaterial == null)
 249                        {
 3250                            throw new InvalidDataException("The material name is not specified.");
 251                        }
 252
 3253                        if (valuesCount < 2)
 254                        {
 3255                            throw new InvalidDataException("A Ns statement must specify a specular exponent.");
 256                        }
 257
 3258                        if (valuesCount != 2)
 259                        {
 3260                            throw new InvalidDataException("A Ns statement has too many values.");
 261                        }
 262
 3263                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 264                        {
 3265                            currentMaterial.SpecularExponent = value;
 266                        }
 3267                        break;
 268                    }
 269                    case "sharpness":
 270                    {
 3271                        if (currentMaterial == null)
 272                        {
 3273                            throw new InvalidDataException("The material name is not specified.");
 274                        }
 275
 3276                        if (valuesCount < 2)
 277                        {
 3278                            throw new InvalidDataException("A sharpness statement must specify a sharpness value.");
 279                        }
 280
 3281                        if (valuesCount != 2)
 282                        {
 3283                            throw new InvalidDataException("A sharpness statement has too many values.");
 284                        }
 3285                        if (TryIntParse(GetNextValue(ref currentLine, ref values), out var value))
 286                        {
 3287                            currentMaterial.Sharpness = value;
 288                        }
 3289                        break;
 290                    }
 291                    case "ni":
 292                    {
 3293                        if (currentMaterial == null)
 294                        {
 3295                            throw new InvalidDataException("The material name is not specified.");
 296                        }
 297
 3298                        if (valuesCount < 2)
 299                        {
 3300                            throw new InvalidDataException("A Ni statement must specify an optical density.");
 301                        }
 302
 3303                        if (valuesCount != 2)
 304                        {
 3305                            throw new InvalidDataException("A Ni statement has too many values.");
 306                        }
 307
 3308                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 309                        {
 3310                            currentMaterial.OpticalDensity = value;
 311                        }
 312
 3313                        break;
 314                    }
 315                    case "map_aat":
 316                        {
 3317                            if (currentMaterial == null)
 318                            {
 3319                                throw new InvalidDataException("The material name is not specified.");
 320                            }
 321
 3322                            if (valuesCount < 2)
 323                            {
 3324                                throw new InvalidDataException("A map_aat statement must specify a value.");
 325                            }
 326
 3327                            if (valuesCount != 2)
 328                            {
 3329                                throw new InvalidDataException("A map_aat statement has too many values.");
 330                            }
 331
 3332                            var value1 = GetNextValue(ref currentLine, ref values);
 333
 3334                            if (value1.Equals("on", StringComparison.OrdinalIgnoreCase))
 335                            {
 3336                                currentMaterial.IsAntiAliasingEnabled = true;
 337                            }
 3338                            else if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 339                            {
 3340                                currentMaterial.IsAntiAliasingEnabled = false;
 341                            }
 342                            else
 343                            {
 3344                                throw new InvalidDataException("A map_aat statement must specify on or off.");
 345                            }
 346
 347                            break;
 348                        }
 349
 350                    case "map_ka":
 3351                        if (currentMaterial == null)
 352                        {
 3353                            throw new InvalidDataException("The material name is not specified.");
 354                        }
 355
 3356                        currentMaterial.AmbientMap = ParseMaterialMap("map_Ka", ref currentLine, ref values, valuesCount
 3357                        break;
 358
 359                    case "map_kd":
 3360                        if (currentMaterial == null)
 361                        {
 3362                            throw new InvalidDataException("The material name is not specified.");
 363                        }
 364
 3365                        currentMaterial.DiffuseMap = ParseMaterialMap("map_Kd", ref currentLine, ref values, valuesCount
 3366                        break;
 367
 368                    case "map_ke":
 3369                        if (currentMaterial == null)
 370                        {
 3371                            throw new InvalidDataException("The material name is not specified.");
 372                        }
 373
 3374                        currentMaterial.EmissiveMap = ParseMaterialMap("map_Ke", ref currentLine, ref values, valuesCoun
 3375                        break;
 376
 377                    case "map_ks":
 3378                        if (currentMaterial == null)
 379                        {
 3380                            throw new InvalidDataException("The material name is not specified.");
 381                        }
 382
 3383                        currentMaterial.SpecularMap = ParseMaterialMap("map_Ks", ref currentLine, ref values, valuesCoun
 3384                        break;
 385
 386                    case "map_ns":
 3387                        if (currentMaterial == null)
 388                        {
 3389                            throw new InvalidDataException("The material name is not specified.");
 390                        }
 391
 3392                        currentMaterial.SpecularExponentMap = ParseMaterialMap("map_Ns", ref currentLine, ref values, va
 3393                        break;
 394
 395                    case "map_d":
 396                    case "map_tr":
 3397                        if (currentMaterial == null)
 398                        {
 3399                            throw new InvalidDataException("The material name is not specified.");
 400                        }
 401
 3402                        currentMaterial.DissolveMap = ParseMaterialMap("map_d", ref currentLine, ref values, valuesCount
 3403                        break;
 404
 405                    case "decal":
 406                    case "map_decal":
 3407                        if (currentMaterial == null)
 408                        {
 3409                            throw new InvalidDataException("The material name is not specified.");
 410                        }
 411
 3412                        currentMaterial.DecalMap = ParseMaterialMap("decal", ref currentLine, ref values, valuesCount, s
 3413                        break;
 414
 415                    case "disp":
 416                    case "map_disp":
 3417                        if (currentMaterial == null)
 418                        {
 3419                            throw new InvalidDataException("The material name is not specified.");
 420                        }
 421
 3422                        currentMaterial.DispMap = ParseMaterialMap("disp", ref currentLine, ref values, valuesCount, set
 3423                        break;
 424
 425                    case "bump":
 426                    case "map_bump":
 3427                        if (currentMaterial == null)
 428                        {
 3429                            throw new InvalidDataException("The material name is not specified.");
 430                        }
 431
 3432                        currentMaterial.BumpMap = ParseMaterialMap("bump", ref currentLine, ref values, valuesCount, set
 3433                        break;
 434
 435                    case "refl":
 436                    case "map_refl":
 3437                        if (currentMaterial == null)
 438                        {
 3439                            throw new InvalidDataException("The material name is not specified.");
 440                        }
 441
 3442                        if (valuesCount < 4)
 443                        {
 3444                            throw new InvalidDataException("A refl statement must specify a type and a file name.");
 445                        }
 446
 3447                        ObjMaterialMap materialMap = ParseMaterialMap("refl", ref currentLine, ref values, valuesCount, 
 448
 449                        switch (materialMapType)
 450                        {
 451                            case MaterialMapType.Sphere:
 3452                                currentMaterial.ReflectionMap.Sphere = materialMap;
 3453                                break;
 454
 455                            case MaterialMapType.CubeTop:
 3456                                currentMaterial.ReflectionMap.CubeTop = materialMap;
 3457                                break;
 458
 459                            case MaterialMapType.CubeBottom:
 3460                                currentMaterial.ReflectionMap.CubeBottom = materialMap;
 3461                                break;
 462
 463                            case MaterialMapType.CubeFront:
 3464                                currentMaterial.ReflectionMap.CubeFront = materialMap;
 3465                                break;
 466
 467                            case MaterialMapType.CubeBack:
 3468                                currentMaterial.ReflectionMap.CubeBack = materialMap;
 3469                                break;
 470
 471                            case MaterialMapType.CubeLeft:
 3472                                currentMaterial.ReflectionMap.CubeLeft = materialMap;
 3473                                break;
 474
 475                            case MaterialMapType.CubeRight:
 3476                                currentMaterial.ReflectionMap.CubeRight = materialMap;
 3477                                break;
 478                        }
 479
 480                        break;
 481                    case "pr":
 482                    {
 3483                        if (currentMaterial == null)
 484                        {
 3485                            throw new InvalidDataException("The material name is not specified.");
 486                        }
 487
 3488                        if (valuesCount < 2)
 489                        {
 3490                            throw new InvalidDataException("A Pr statement must specify an optical density.");
 491                        }
 492
 3493                        if (valuesCount != 2)
 494                        {
 3495                            throw new InvalidDataException("A Pr statement has too many values.");
 496                        }
 497
 3498                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 499                        {
 3500                            currentMaterial.Roughness = value;
 501                        }
 502
 3503                        break;
 504                    }
 505                    case "map_pr":
 3506                        if (currentMaterial == null)
 507                        {
 3508                            throw new InvalidDataException("The material name is not specified.");
 509                        }
 510
 3511                        currentMaterial.RoughnessMap = ParseMaterialMap("map_Pr", ref currentLine, ref values, valuesCou
 3512                        break;
 513                    case "pm":
 514                    {
 3515                        if (currentMaterial == null)
 516                        {
 3517                            throw new InvalidDataException("The material name is not specified.");
 518                        }
 519
 3520                        if (valuesCount < 2)
 521                        {
 3522                            throw new InvalidDataException("A Pm statement must specify an optical density.");
 523                        }
 524
 3525                        if (valuesCount != 2)
 526                        {
 3527                            throw new InvalidDataException("A Pm statement has too many values.");
 528                        }
 529
 3530                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 531                        {
 3532                            currentMaterial.Metallic = value;
 533                        }
 3534                        break;
 535                    }
 536                    case "map_pm":
 3537                        if (currentMaterial == null)
 538                        {
 3539                            throw new InvalidDataException("The material name is not specified.");
 540                        }
 541
 3542                        currentMaterial.MetallicMap = ParseMaterialMap("map_Pm", ref currentLine, ref values, valuesCoun
 3543                        break;
 544                    case "ps":
 545                    {
 3546                        if (currentMaterial == null)
 547                        {
 3548                            throw new InvalidDataException("The material name is not specified.");
 549                        }
 550
 3551                        if (valuesCount < 2)
 552                        {
 3553                            throw new InvalidDataException("A Ps statement must specify an optical density.");
 554                        }
 555
 3556                        if (valuesCount != 2)
 557                        {
 3558                            throw new InvalidDataException("A Ps statement has too many values.");
 559                        }
 560
 3561                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 562                        {
 3563                            currentMaterial.Sheen = value;
 564                        }
 3565                        break;
 566                    }
 567                    case "map_ps":
 3568                        if (currentMaterial == null)
 569                        {
 3570                            throw new InvalidDataException("The material name is not specified.");
 571                        }
 572
 3573                        currentMaterial.SheenMap = ParseMaterialMap("map_Ps", ref currentLine, ref values, valuesCount, 
 3574                        break;
 575                    case "pc":
 576                    {
 3577                        if (currentMaterial == null)
 578                        {
 3579                            throw new InvalidDataException("The material name is not specified.");
 580                        }
 581
 3582                        if (valuesCount < 2)
 583                        {
 3584                            throw new InvalidDataException("A Pc statement must specify an optical density.");
 585                        }
 586
 3587                        if (valuesCount != 2)
 588                        {
 3589                            throw new InvalidDataException("A Pc statement has too many values.");
 590                        }
 591
 3592                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 593                        {
 3594                            currentMaterial.ClearCoatThickness = value;
 595                        }
 3596                        break;
 597                    }
 598                    case "pcr":
 599                    {
 3600                        if (currentMaterial == null)
 601                        {
 3602                            throw new InvalidDataException("The material name is not specified.");
 603                        }
 604
 3605                        if (valuesCount < 2)
 606                        {
 3607                            throw new InvalidDataException("A Pcr statement must specify an optical density.");
 608                        }
 609
 3610                        if (valuesCount != 2)
 611                        {
 3612                            throw new InvalidDataException("A Pcr statement has too many values.");
 613                        }
 614
 3615                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 616                        {
 3617                            currentMaterial.ClearCoatRoughness = value;
 618                        }
 3619                        break;
 620                    }
 621                    case "aniso":
 622                    {
 3623                        if (currentMaterial == null)
 624                        {
 3625                            throw new InvalidDataException("The material name is not specified.");
 626                        }
 627
 3628                        if (valuesCount < 2)
 629                        {
 3630                            throw new InvalidDataException("A aniso statement must specify an optical density.");
 631                        }
 632
 3633                        if (valuesCount != 2)
 634                        {
 3635                            throw new InvalidDataException("A aniso statement has too many values.");
 636                        }
 637
 3638                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 639                        {
 3640                            currentMaterial.Anisotropy = value;
 641                        }
 642
 3643                        break;
 644                    }
 645                    case "anisor":
 646                    {
 3647                        if (currentMaterial == null)
 648                        {
 3649                            throw new InvalidDataException("The material name is not specified.");
 650                        }
 651
 3652                        if (valuesCount < 2)
 653                        {
 3654                            throw new InvalidDataException("A anisor statement must specify an optical density.");
 655                        }
 656
 3657                        if (valuesCount != 2)
 658                        {
 3659                            throw new InvalidDataException("A anisor statement has too many values.");
 660                        }
 661
 3662                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 663                        {
 3664                            currentMaterial.AnisotropyRotation = value;
 665                        }
 3666                        break;
 667                    }
 668                    case "norm":
 3669                        if (currentMaterial == null)
 670                        {
 3671                            throw new InvalidDataException("The material name is not specified.");
 672                        }
 673
 3674                        currentMaterial.Norm = ParseMaterialMap("norm", ref currentLine, ref values, valuesCount, settin
 675                        break;
 676                }
 677            }
 678
 3679            mtl.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 680
 3681            return mtl;
 682        }
 683
 684        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 685        private static ObjMaterialColor ParseMaterialColor(ReadOnlySpan<char> statement, ref ReadOnlySpan<char> currentL
 686        {
 3687            if (valuesCount < 2)
 688            {
 3689                throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a color."));
 690            }
 691
 3692            var color = new ObjMaterialColor();
 693
 3694            ReadOnlySpan<char> value1 = GetNextValue(ref currentLine, ref values);
 3695            Span<char> valueBuffer = stackalloc char[value1.Length];
 3696            int value1Length = value1.ToLowerInvariant(valueBuffer);
 697
 698            //if (value1Length == -1)
 699            //{
 700            //    throw new InvalidDataException("the buffer is too small");
 701            //}
 702
 3703            int index = 1;
 704
 3705            switch (valueBuffer[..value1Length])
 706            {
 707                case "spectral":
 708                {
 3709                    index++;
 710
 3711                    if (valuesCount - index < 1)
 712                    {
 3713                        throw new InvalidDataException(string.Concat("A ", statement,
 3714                            " spectral statement must specify a file name."));
 715                    }
 716
 3717                    color.SpectralFileName = GetNextValue(ref currentLine, ref values).ToString();
 3718                    index++;
 719
 3720                    if (valuesCount > index)
 721                    {
 3722                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 723                        {
 3724                            color.SpectralFactor = value;
 725                        }
 726
 3727                        index++;
 728                    }
 729
 3730                    break;
 731                }
 732                case "xyz":
 733                {
 3734                    index++;
 735
 3736                    if (valuesCount - index < 1)
 737                    {
 3738                        throw new InvalidDataException(string.Concat("A ", statement,
 3739                            " xyz statement must specify a color."));
 740                    }
 741
 3742                    color.UseXYZColorSpace = true;
 743
 3744                    var xyz = new ObjVector3();
 3745                    if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 746                    {
 3747                        xyz.X = value;
 748                    }
 749
 3750                    index++;
 751
 3752                    if (valuesCount > index)
 753                    {
 3754                        if (valuesCount - index < 2)
 755                        {
 3756                            throw new InvalidDataException(string.Concat("A ", statement,
 3757                                " xyz statement must specify a XYZ color."));
 758                        }
 759
 3760                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 761                        {
 3762                            xyz.Y = value;
 763                        }
 764
 3765                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 766                        {
 3767                            xyz.Z = value;
 768                        }
 769
 3770                        index += 2;
 771                    }
 772                    else
 773                    {
 3774                        xyz.Y = xyz.X;
 3775                        xyz.Z = xyz.X;
 776                    }
 777
 3778                    color.Color = xyz;
 3779                    break;
 780                }
 781                default:
 782                {
 3783                    var rgb = new ObjVector3();
 3784                    if (TryFloatParse(value1, out var value))
 785                    {
 3786                        rgb.X = value;
 787                    }
 788
 3789                    index++;
 790
 3791                    if (valuesCount > index)
 792                    {
 3793                        if (valuesCount - index < 2)
 794                        {
 3795                            throw new InvalidDataException(string.Concat("A ", statement,
 3796                                " statement must specify a RGB color."));
 797                        }
 798
 3799                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 800                        {
 3801                            rgb.Y = value;
 802                        }
 803
 3804                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 805                        {
 3806                            rgb.Z = value;
 807                        }
 808
 3809                        index += 2;
 810                    }
 811                    else
 812                    {
 3813                        rgb.Y = rgb.X;
 3814                        rgb.Z = rgb.X;
 815                    }
 816
 3817                    color.Color = rgb;
 818                    break;
 819                }
 820            }
 821
 3822            if (index != valuesCount)
 823            {
 3824                throw new InvalidDataException(string.Concat("A ", statement, " statement has too many values."));
 825            }
 826
 3827            return color;
 828        }
 829
 830        private enum MaterialMapType
 831        {
 832            None,
 833            Sphere,
 834            CubeTop,
 835            CubeBottom,
 836            CubeFront,
 837            CubeBack,
 838            CubeLeft,
 839            CubeRight
 840        }
 841
 842        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 843        private static ObjMaterialMap ParseMaterialMap(ReadOnlySpan<char> statement, ref ReadOnlySpan<char> currentLine,
 844        {
 3845            return ParseMaterialMap(statement, ref currentLine, ref values, valuesCount, settings, out _);
 846        }
 847
 848        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 849        private static ObjMaterialMap ParseMaterialMap(ReadOnlySpan<char> statement, ref ReadOnlySpan<char> currentLine,
 850        {
 3851            materialMapType = MaterialMapType.None;
 852
 3853            var map = new ObjMaterialMap();
 3854            var charsRead = 0;
 855
 3856            int valueBufferSize = 16;
 3857            Span<char> valueBuffer = stackalloc char[valueBufferSize];
 858
 3859            for (int index = 0; index < valuesCount;)
 860            {
 3861                index++;
 862
 3863                if (valuesCount - index < 1)
 864                {
 3865                    throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a filename.")
 866                }
 867
 3868                ReadOnlySpan<char> value1 = GetNextValue(ref currentLine, ref values);
 3869                int value1Length = value1.ToLowerInvariant(valueBuffer);
 870
 871                // Value1Length is -1 when the buffer is too small.
 872                // This should only happen if the value is a file name and not an option
 3873                if (value1Length == -1)
 874                {
 3875                    if (index == 1)
 876                    {
 3877                        if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 878                        {
 0879                            throw new InvalidDataException("A refl statement must specify a type.");
 880                        }
 881                    }
 3882                    if (settings.KeepWhitespacesOfMapFileReferences)
 883                    {
 3884                        map.FileName = new string(currentLine[(statement.Length + charsRead + 1)..]);
 885                    }
 886                    else
 887                    {
 3888                        var sb = new StringBuilder();
 889
 3890                        sb.Append(value1);
 891
 3892                        for (int i = index + 1; i < valuesCount; i++)
 893                        {
 3894                            sb.Append(' ');
 3895                            sb.Append(GetNextValue(ref currentLine, ref values));
 896                        }
 897
 3898                        string filename = sb.ToString();
 899
 3900                        map.FileName = filename;
 901                    }
 902
 3903                    return map;
 904                }
 905
 3906                charsRead += value1Length;
 907
 3908                if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 909                {
 3910                    if (index == 1)
 911                    {
 3912                        if (!MemoryExtensions.Equals(valueBuffer[..value1Length], "-type", StringComparison.OrdinalIgnor
 913                        {
 3914                            throw new InvalidDataException("A refl statement must specify a type.");
 915                        }
 916                    }
 917                }
 918
 3919                switch (valueBuffer[..value1Length])
 920                {
 921                    case "-type":
 3922                        if (valuesCount - index < 2)
 923                        {
 3924                            throw new InvalidDataException(string.Concat("A ", statement, " -type option must specify a 
 925                        }
 926
 3927                        if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 928                        {
 3929                            ReadOnlySpan<char> value2 = GetNextValue(ref currentLine, ref values);
 3930                            int value2Length = value2.ToLowerInvariant(valueBuffer);
 3931                            charsRead += value2Length;
 932
 933                            //if (value2Length == -1)
 934                            //{
 935                            //    throw new InvalidDataException("the buffer is too small");
 936                            //}
 937
 3938                            switch (valueBuffer[..value2Length])
 939                            {
 940                                case "sphere":
 3941                                    materialMapType = MaterialMapType.Sphere;
 3942                                    break;
 943
 944                                case "cube_top":
 3945                                    materialMapType = MaterialMapType.CubeTop;
 3946                                    break;
 947
 948                                case "cube_bottom":
 3949                                    materialMapType = MaterialMapType.CubeBottom;
 3950                                    break;
 951
 952                                case "cube_front":
 3953                                    materialMapType = MaterialMapType.CubeFront;
 3954                                    break;
 955
 956                                case "cube_back":
 3957                                    materialMapType = MaterialMapType.CubeBack;
 3958                                    break;
 959
 960                                case "cube_left":
 3961                                    materialMapType = MaterialMapType.CubeLeft;
 3962                                    break;
 963
 964                                case "cube_right":
 3965                                    materialMapType = MaterialMapType.CubeRight;
 3966                                    break;
 967                            }
 968                        }
 969                        else
 970                        {
 3971                            GetNextValue(ref currentLine, ref values);
 972                        }
 973
 3974                        index++;
 3975                        break;
 976
 977                    case "-blenu":
 978                        {
 3979                            if (valuesCount - index < 2)
 980                            {
 3981                                throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must speci
 982                            }
 983
 3984                            var value2 = GetNextValue(ref currentLine, ref values);
 3985                            charsRead += value2.Length;
 986
 3987                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 988                            {
 3989                                map.IsHorizontalBlendingEnabled = true;
 990                            }
 3991                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 992                            {
 3993                                map.IsHorizontalBlendingEnabled = false;
 994                            }
 995                            else
 996                            {
 3997                                throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must speci
 998                            }
 999
 31000                            index++;
 31001                            break;
 1002                        }
 1003
 1004                    case "-blenv":
 1005                        {
 31006                            if (valuesCount - index < 2)
 1007                            {
 31008                                throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must speci
 1009                            }
 1010
 31011                            var value2 = GetNextValue(ref currentLine, ref values);
 31012                            charsRead += value2.Length;
 1013
 31014                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1015                            {
 31016                                map.IsVerticalBlendingEnabled = true;
 1017                            }
 31018                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1019                            {
 31020                                map.IsVerticalBlendingEnabled = false;
 1021                            }
 1022                            else
 1023                            {
 31024                                throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must speci
 1025                            }
 1026
 31027                            index++;
 31028                            break;
 1029                        }
 1030
 1031                    case "-bm":
 1032                    {
 31033                        if (valuesCount - index < 2)
 1034                        {
 31035                            throw new InvalidDataException(string.Concat("A ", statement, " -bm option must specify a va
 1036                        }
 1037
 31038                        var value2 = GetNextValue(ref currentLine, ref values);
 31039                        charsRead += value2.Length;
 1040
 31041                        if (TryFloatParse(value2, out var value))
 1042                        {
 31043                            map.BumpMultiplier = value;
 1044                        }
 1045
 31046                        index++;
 31047                        break;
 1048                    }
 1049                    case "-boost":
 1050                    {
 31051                        if (valuesCount - index < 2)
 1052                        {
 31053                            throw new InvalidDataException(string.Concat("A ", statement,
 31054                                " -boost option must specify a value."));
 1055                        }
 1056
 31057                        var value2 = GetNextValue(ref currentLine, ref values);
 31058                        charsRead += value2.Length;
 1059
 31060                        if (TryFloatParse(value2, out var value))
 1061                        {
 31062                            map.Boost = value;
 1063                        }
 1064
 31065                        index++;
 31066                        break;
 1067                    }
 1068                    case "-cc":
 1069                        {
 31070                            if (valuesCount - index < 2)
 1071                            {
 31072                                throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify 
 1073                            }
 1074
 31075                            var value2 = GetNextValue(ref currentLine, ref values);
 31076                            charsRead += value2.Length;
 1077
 31078                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1079                            {
 31080                                map.IsColorCorrectionEnabled = true;
 1081                            }
 31082                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1083                            {
 31084                                map.IsColorCorrectionEnabled = false;
 1085                            }
 1086                            else
 1087                            {
 31088                                throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify 
 1089                            }
 1090
 31091                            index++;
 31092                            break;
 1093                        }
 1094
 1095                    case "-clamp":
 1096                        {
 31097                            if (valuesCount - index < 2)
 1098                            {
 31099                                throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must speci
 1100                            }
 1101
 31102                            var value2 = GetNextValue(ref currentLine, ref values);
 31103                            charsRead += value2.Length;
 1104
 31105                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1106                            {
 31107                                map.IsClampingEnabled = true;
 1108                            }
 31109                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1110                            {
 31111                                map.IsClampingEnabled = false;
 1112                            }
 1113                            else
 1114                            {
 31115                                throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must speci
 1116                            }
 1117
 31118                            index++;
 31119                            break;
 1120                        }
 1121
 1122                    case "-imfchan":
 1123                        {
 31124                            if (valuesCount - index < 2)
 1125                            {
 31126                                throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must spe
 1127                            }
 1128
 31129                            ReadOnlySpan<char> value2 = GetNextValue(ref currentLine, ref values);
 31130                            int value2Length = value2.ToLowerInvariant(valueBuffer);
 31131                            charsRead += value2Length;
 1132
 1133                            //if (value2Length == -1)
 1134                            //{
 1135                            //    throw new InvalidDataException("the buffer is too small");
 1136                            //}
 1137
 31138                            switch (valueBuffer[..value2Length])
 1139                            {
 1140                                case "r":
 31141                                    map.ScalarChannel = ObjMapChannel.Red;
 31142                                    break;
 1143
 1144                                case "g":
 31145                                    map.ScalarChannel = ObjMapChannel.Green;
 31146                                    break;
 1147
 1148                                case "b":
 31149                                    map.ScalarChannel = ObjMapChannel.Blue;
 31150                                    break;
 1151
 1152                                case "m":
 31153                                    map.ScalarChannel = ObjMapChannel.Matte;
 31154                                    break;
 1155
 1156                                case "l":
 31157                                    map.ScalarChannel = ObjMapChannel.Luminance;
 31158                                    break;
 1159
 1160                                case "z":
 31161                                    map.ScalarChannel = ObjMapChannel.Depth;
 31162                                    break;
 1163
 1164                                default:
 31165                                    throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must
 1166                            }
 1167
 31168                            index++;
 31169                            break;
 1170                        }
 1171
 1172                    case "-mm":
 1173                    {
 31174                        if (valuesCount - index < 3)
 1175                        {
 31176                            throw new InvalidDataException(string.Concat("A ", statement, " -mm option must specify a ba
 1177                        }
 1178
 31179                        var value2 = GetNextValue(ref currentLine, ref values);
 31180                        charsRead += value2.Length;
 1181
 31182                        if (TryFloatParse(value2, out var value))
 1183                        {
 31184                            map.ModifierBase = value;
 1185                        }
 31186                        value2 = GetNextValue(ref currentLine, ref values);
 31187                        charsRead += value2.Length;
 1188
 31189                        if (TryFloatParse(value2, out value))
 1190                        {
 31191                            map.ModifierGain = value;
 1192                        }
 1193
 31194                        index += 2;
 31195                        break;
 1196                    }
 1197
 1198
 1199                    case "-o":
 1200                    {
 31201                        if (valuesCount - index < 2)
 1202                        {
 31203                            throw new InvalidDataException(string.Concat("A ", statement,
 31204                                " -o option must specify at least 2 values."));
 1205                        }
 1206
 31207                        var offset = new ObjVector3();
 31208                        var value2 = GetNextValue(ref currentLine, ref values);
 1209
 31210                        if (TryFloatParse(value2, out var value))
 1211                        {
 31212                            offset.X = value;
 31213                            index++;
 31214                            charsRead += value2.Length;
 1215                        }
 1216                        else
 1217                        {
 01218                            map.Offset = offset;
 01219                            break;
 1220                        }
 1221
 31222                        if (valuesCount - index > 2)
 1223                        {
 31224                            value2 = GetNextValue(ref currentLine, ref values);
 31225                            if (TryFloatParse(value2, out value))
 1226                            {
 31227                                offset.Y = value;
 31228                                index++;
 31229                                charsRead += value2.Length;
 1230                            }
 1231                            else
 1232                            {
 31233                                map.Offset = offset;
 31234                                break;
 1235                            }
 1236
 31237                            if (valuesCount - index > 2)
 1238                            {
 31239                                value2 = GetNextValue(ref currentLine, ref values);
 31240                                if (TryFloatParse(value2, out value))
 1241                                {
 31242                                    offset.Z = value;
 31243                                    index++;
 31244                                    charsRead += value2.Length;
 1245                                }
 1246                                else
 1247                                {
 31248                                    map.Offset = offset;
 31249                                    break;
 1250                                }
 1251                            }
 1252                        }
 1253
 31254                        map.Offset = offset;
 31255                        break;
 1256                    }
 1257                    case "-s":
 1258                    {
 31259                        if (valuesCount - index < 2)
 1260                        {
 31261                            throw new InvalidDataException(string.Concat("A ", statement,
 31262                                " -s option must specify at least 2 values."));
 1263                        }
 1264
 31265                        var scale = new ObjVector3(1.0f, 1.0f, 1.0f);
 1266
 31267                        var value2 = GetNextValue(ref currentLine, ref values);
 1268
 31269                        if (TryFloatParse(value2, out var value))
 1270                        {
 31271                            scale.X = value;
 31272                            index++;
 31273                            charsRead += value2.Length;
 1274                        }
 1275                        else
 1276                        {
 01277                            map.Scale = scale;
 01278                            break;
 1279                        }
 1280
 31281                        if (valuesCount - index > 2)
 1282                        {
 31283                            value2 = GetNextValue(ref currentLine, ref values);
 31284                            if (TryFloatParse(value2, out value))
 1285                            {
 31286                                scale.Y = value;
 31287                                index++;
 31288                                charsRead += value2.Length;
 1289                            }
 1290                            else
 1291                            {
 31292                                map.Scale = scale;
 31293                                break;
 1294                            }
 1295
 31296                            if (valuesCount - index > 2)
 1297                            {
 31298                                value2 = GetNextValue(ref currentLine, ref values);
 31299                                if (TryFloatParse(value2, out value))
 1300                                {
 31301                                    scale.Z = value;
 31302                                    index++;
 31303                                    charsRead += value2.Length;
 1304                                }
 1305                                else
 1306                                {
 31307                                    map.Scale = scale;
 31308                                    break;
 1309                                }
 1310                            }
 1311                        }
 1312
 31313                        map.Scale = scale;
 31314                        break;
 1315                    }
 1316                    case "-t":
 1317                    {
 31318                        if (valuesCount - index < 2)
 1319                        {
 31320                            throw new InvalidDataException(string.Concat("A ", statement,
 31321                                " -t option must specify at least 2 values."));
 1322                        }
 1323
 31324                        var turbulence = new ObjVector3();
 1325
 31326                        var value2 = GetNextValue(ref currentLine, ref values);
 1327
 31328                        if (TryFloatParse(value2, out var value))
 1329                        {
 31330                            turbulence.X = value;
 31331                            index++;
 31332                            charsRead += value2.Length;
 1333                        }
 1334                        else
 1335                        {
 01336                            map.Turbulence = turbulence;
 01337                            break;
 1338                        }
 1339
 31340                        if (valuesCount - index > 2)
 1341                        {
 31342                            value2 = GetNextValue(ref currentLine, ref values);
 31343                            if (TryFloatParse(value2, out value))
 1344                            {
 31345                                turbulence.Y = value;
 31346                                index++;
 31347                                charsRead += value2.Length;
 1348                            }
 1349                            else
 1350                            {
 31351                                map.Turbulence = turbulence;
 31352                                break;
 1353                            }
 1354
 31355                            if (valuesCount - index > 2)
 1356                            {
 31357                                value2 = GetNextValue(ref currentLine, ref values);
 31358                                if (TryFloatParse(value2, out value))
 1359                                {
 31360                                    turbulence.Z = value;
 31361                                    index++;
 31362                                    charsRead += value2.Length;
 1363                                }
 1364                                else
 1365                                {
 31366                                    map.Turbulence = turbulence;
 31367                                    break;
 1368                                }
 1369                            }
 1370                        }
 1371
 31372                        map.Turbulence = turbulence;
 31373                        break;
 1374                    }
 1375                    case "-texres":
 1376                    {
 31377                        if (valuesCount - index < 2)
 1378                        {
 31379                            throw new InvalidDataException(string.Concat("A ", statement,
 31380                                " -texres option must specify a value."));
 1381                        }
 31382                        var value2 = GetNextValue(ref currentLine, ref values);
 31383                        charsRead += value2.Length;
 31384                        if (TryIntParse(value2, out var value))
 1385                        {
 31386                            map.TextureResolution = value;
 1387                        }
 1388
 31389                        index++;
 31390                        break;
 1391                    }
 1392                    default:
 1393                        {
 31394                            if (settings.KeepWhitespacesOfMapFileReferences)
 1395                            {
 31396                                map.FileName = new string(currentLine[(statement.Length + index + charsRead - value1.Len
 1397                            }
 1398                            else
 1399                            {
 31400                                var sb = new StringBuilder();
 1401
 31402                                sb.Append(value1);
 1403
 31404                                for (int i = index + 1; i < valuesCount; i++)
 1405                                {
 31406                                    sb.Append(' ');
 31407                                    sb.Append(GetNextValue(ref currentLine, ref values));
 1408                                }
 1409
 31410                                string filename = sb.ToString();
 1411
 31412                                map.FileName = filename;
 1413                            }
 31414                            index = valuesCount;
 1415
 1416                            break;
 1417                        }
 1418                }
 1419            }
 1420
 31421            return map;
 1422        }
 1423    }
 1424}
 1425
 1426#endif

Methods/Properties

FromStream(System.IO.Stream, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings)
ParseMaterialColor(string, string[])
ParseMaterialMap(string, string[], string, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings)
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>)
TryFloatParse(System.ReadOnlySpan<char>, out float)
TryIntParse(System.ReadOnlySpan<char>, out int)
FromStream(System.IO.Stream, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings)
ParseMaterialColor(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int)
ParseMaterialColor(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int)
ParseMaterialMap(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings)
ParseMaterialMap(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings)
ParseMaterialMap(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref System.MemoryExtensions.SpanSplitEnumerator<char>, int, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings, out JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReader.MaterialMapType)
ParseMaterialMap(System.ReadOnlySpan<char>, ref System.ReadOnlySpan<char>, ref Polyfills.Polyfill.SpanSplitEnumerator<char>, int, JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReaderSettings, out JeremyAnsel.Media.WavefrontObj.ObjMaterialFileReader.MaterialMapType)