< Summary

Line coverage
99%
Covered lines: 980
Uncovered lines: 7
Coverable lines: 987
Total lines: 2402
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

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: FromStream(...)82.13%319319100%
File 1: ParseMaterialColor(...)100%1818100%
File 1: ParseMaterialMap(...)88.46%130130100%
File 2: MoveNextSkipEmpty(...)100%44100%
File 2: GetNextValue(...)100%11100%
File 2: TryFloatParse(...)100%11100%
File 2: TryIntParse(...)100%11100%
File 2: FromStream(...)100%176176100%
File 2: ParseMaterialColor(...)100%3232100%
File 2: ParseMaterialMap(...)100%11100%
File 2: ParseMaterialMap(...)96.3%10810897.2%

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 col
 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 specify a
 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 RGB
 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                            }
 866                        }
 867
 1868                        map.Offset = offset;
 1869                        break;
 870
 871                    case "-s":
 872                    {
 1873                        if (values.Length - index < 2)
 874                        {
 1875                            throw new InvalidDataException(string.Concat("A ", statement, " -s option must specify at le
 876                        }
 877
 1878                        var scale = new ObjVector3(1.0f, 1.0f, 1.0f);
 879
 1880                        scale.X = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1881                        index++;
 882
 1883                        if (values.Length - index > 2)
 884                        {
 1885                            if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out 
 886                            {
 1887                                scale.Y = v;
 1888                                index++;
 889                            }
 890                            else
 891                            {
 1892                                map.Scale = scale;
 1893                                break;
 894                            }
 895
 1896                            if (values.Length - index > 2)
 897                            {
 1898                                if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, 
 899                                {
 1900                                    scale.Z = v;
 1901                                    index++;
 902                                }
 903                            }
 904                        }
 905
 1906                        map.Scale = scale;
 1907                        break;
 908                    }
 909
 910                    case "-t":
 911                    {
 1912                        if (values.Length - index < 2)
 913                        {
 1914                            throw new InvalidDataException(string.Concat("A ", statement, " -t option must specify at le
 915                        }
 916
 1917                        var turbulence = new ObjVector3();
 918
 1919                        turbulence.X = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 1920                        index++;
 921
 1922                        if (values.Length - index > 2)
 923                        {
 1924                            if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out 
 925                            {
 1926                                turbulence.Y = v;
 1927                                index++;
 928                            }
 929                            else
 930                            {
 1931                                map.Turbulence = turbulence;
 1932                                break;
 933                            }
 934
 1935                            if (values.Length - index > 2)
 936                            {
 1937                                if (float.TryParse(values[index + 1], NumberStyles.Float, CultureInfo.InvariantCulture, 
 938                                {
 1939                                    turbulence.Z = v;
 1940                                    index++;
 941                                }
 942                            }
 943                        }
 944
 1945                        map.Turbulence = turbulence;
 1946                        break;
 947                    }
 948
 949                    case "-texres":
 1950                        if (values.Length - index < 2)
 951                        {
 1952                            throw new InvalidDataException(string.Concat("A ", statement, " -texres option must specify 
 953                        }
 954
 1955                        map.TextureResolution = int.Parse(values[index + 1], CultureInfo.InvariantCulture);
 956
 1957                        index++;
 1958                        break;
 959
 960                    default:
 961                    {
 1962                        if (settings.KeepWhitespacesOfMapFileReferences)
 963                        {
 1964                            var charsRead = 0;
 1965                            for (var i = 1; i < index; i++)
 966                            {
 1967                                charsRead += values[i].Length;
 968                            }
 969
 1970                            map.FileName = currentLine.Remove(0, statement.Length + charsRead + index);
 971                        }
 972                        else
 973                        {
 1974                            string filename = string.Join(" ", values, index, values.Length - index);
 975
 1976                            map.FileName = filename;
 977                        }
 978
 1979                        index = values.Length;
 980
 981                        break;
 982                    }
 983                }
 984            }
 985
 1986            return map;
 987        }
 988    }
 989}
 990
 991#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        {
 226            while (values.MoveNext())
 27            {
 228                if (values.Current.Start.Value != values.Current.End.Value)
 29                {
 230                    return;
 31                }
 32            }
 233        }
 34
 35        private static ReadOnlySpan<char> GetNextValue(ref ReadOnlySpan<char> currentLine, ref SpanSplitEnumerator value
 36        {
 237            MoveNextSkipEmpty(ref values);
 238            return currentLine[values.Current];
 39        }
 40
 41        private static bool TryFloatParse(ReadOnlySpan<char> s, out float value)
 42        {
 243            return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
 44        }
 45
 46        private static bool TryIntParse(ReadOnlySpan<char> s, out int value)
 47        {
 248            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        {
 254            if (stream == null)
 55            {
 256                throw new ArgumentNullException(nameof(stream));
 57            }
 58
 259            var mtl = new ObjMaterialFile();
 260            var lineReader = new LineReader9();
 61
 262            ObjMaterial? currentMaterial = null;
 63
 264            int valueBufferSize = 16;
 265            Span<char> valueBuffer = stackalloc char[valueBufferSize];
 66
 267            foreach (var currentLineString in lineReader.Read9(stream))
 68            {
 269                ReadOnlySpan<char> currentLine = currentLineString.Buffer.AsSpan()[..currentLineString.Length];
 70
 271                int valuesCount = 0;
 72
 273                foreach (Range range in currentLine.SplitAny(LineReader9.LineSeparators))
 74                {
 275                    if (currentLine[range].Length == 0)
 76                    {
 77                        continue;
 78                    }
 79
 280                    valuesCount++;
 81                }
 82
 283                SpanSplitEnumerator values = currentLine.SplitAny(LineReader9.LineSeparators);
 84
 285                MoveNextSkipEmpty(ref values);
 86
 287                if (values.Current.End.Value - values.Current.Start.Value > valueBufferSize)
 88                {
 89                    continue;
 90                }
 91
 292                ReadOnlySpan<char> value0 = currentLine[values.Current];
 293                int value0Length = value0.ToLowerInvariant(valueBuffer);
 94
 95                //if (value0Length == -1)
 96                //{
 97                //    throw new InvalidDataException("the buffer is too small");
 98                //}
 99
 2100                switch (valueBuffer[..value0Length])
 101                {
 102                    case "newmtl":
 103                        {
 2104                            if (valuesCount < 2)
 105                            {
 2106                                throw new InvalidDataException("A newmtl statement must specify a name.");
 107                            }
 108
 2109                            var sb = new StringBuilder();
 110
 2111                            sb.Append(GetNextValue(ref currentLine, ref values));
 112
 2113                            for (int i = 2; i < valuesCount; i++)
 114                            {
 2115                                sb.Append(' ');
 2116                                sb.Append(GetNextValue(ref currentLine, ref values));
 117                            }
 118
 2119                            currentMaterial = new ObjMaterial
 2120                            {
 2121                                Name = sb.ToString()
 2122                            };
 123
 2124                            mtl.Materials.Add(currentMaterial);
 125
 2126                            break;
 127                        }
 128
 129                    case "ka":
 2130                        if (currentMaterial == null)
 131                        {
 2132                            throw new InvalidDataException("The material name is not specified.");
 133                        }
 134
 2135                        currentMaterial.AmbientColor = ParseMaterialColor("Ka", ref currentLine, ref values, valuesCount
 2136                        break;
 137
 138                    case "kd":
 2139                        if (currentMaterial == null)
 140                        {
 2141                            throw new InvalidDataException("The material name is not specified.");
 142                        }
 143
 2144                        currentMaterial.DiffuseColor = ParseMaterialColor("Kd", ref currentLine, ref values, valuesCount
 2145                        break;
 146
 147                    case "ke":
 2148                        if (currentMaterial == null)
 149                        {
 2150                            throw new InvalidDataException("The material name is not specified.");
 151                        }
 152
 2153                        currentMaterial.EmissiveColor = ParseMaterialColor("Ke", ref currentLine, ref values, valuesCoun
 2154                        break;
 155
 156                    case "ks":
 2157                        if (currentMaterial == null)
 158                        {
 2159                            throw new InvalidDataException("The material name is not specified.");
 160                        }
 161
 2162                        currentMaterial.SpecularColor = ParseMaterialColor("Ks", ref currentLine, ref values, valuesCoun
 2163                        break;
 164
 165                    case "tf":
 2166                        if (currentMaterial == null)
 167                        {
 2168                            throw new InvalidDataException("The material name is not specified.");
 169                        }
 170
 2171                        currentMaterial.TransmissionColor = ParseMaterialColor("Tf", ref currentLine, ref values, values
 2172                        break;
 173
 174                    case "illum":
 175                    {
 2176                        if (currentMaterial == null)
 177                        {
 2178                            throw new InvalidDataException("The material name is not specified.");
 179                        }
 180
 2181                        if (valuesCount < 2)
 182                        {
 2183                            throw new InvalidDataException("An illum statement must specify an illumination model.");
 184                        }
 185
 2186                        if (valuesCount != 2)
 187                        {
 2188                            throw new InvalidDataException("An illum statement has too many values.");
 189                        }
 190
 2191                        if (TryIntParse(GetNextValue(ref currentLine, ref values), out var value))
 192                        {
 2193                            currentMaterial.IlluminationModel = value;
 194                        }
 195
 2196                        break;
 197                    }
 198                    case "d":
 199                        {
 2200                            if (currentMaterial == null)
 201                            {
 2202                                throw new InvalidDataException("The material name is not specified.");
 203                            }
 204
 2205                            if (valuesCount < 2)
 206                            {
 2207                                throw new InvalidDataException("A d statement must specify a factor.");
 208                            }
 209
 2210                            var value1 = GetNextValue(ref currentLine, ref values);
 211
 2212                            if (value1.Equals("-halo", StringComparison.OrdinalIgnoreCase))
 213                            {
 2214                                if (valuesCount < 3)
 215                                {
 2216                                    throw new InvalidDataException("A d statement must specify a factor.");
 217                                }
 218
 2219                                if (valuesCount != 3)
 220                                {
 2221                                    throw new InvalidDataException("A d statement has too many values.");
 222                                }
 223
 2224                                currentMaterial.IsHaloDissolve = true;
 2225                                if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 226                                {
 2227                                    currentMaterial.DissolveFactor = value;
 228                                }
 229                            }
 230                            else
 231                            {
 2232                                if (valuesCount != 2)
 233                                {
 2234                                    throw new InvalidDataException("A d statement has too many values.");
 235                                }
 236
 2237                                if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 238                                {
 2239                                    currentMaterial.DissolveFactor = value;
 240                                }
 241                            }
 242
 2243                            break;
 244                        }
 245
 246                    case "ns":
 247                    {
 2248                        if (currentMaterial == null)
 249                        {
 2250                            throw new InvalidDataException("The material name is not specified.");
 251                        }
 252
 2253                        if (valuesCount < 2)
 254                        {
 2255                            throw new InvalidDataException("A Ns statement must specify a specular exponent.");
 256                        }
 257
 2258                        if (valuesCount != 2)
 259                        {
 2260                            throw new InvalidDataException("A Ns statement has too many values.");
 261                        }
 262
 2263                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 264                        {
 2265                            currentMaterial.SpecularExponent = value;
 266                        }
 2267                        break;
 268                    }
 269                    case "sharpness":
 270                    {
 2271                        if (currentMaterial == null)
 272                        {
 2273                            throw new InvalidDataException("The material name is not specified.");
 274                        }
 275
 2276                        if (valuesCount < 2)
 277                        {
 2278                            throw new InvalidDataException("A sharpness statement must specify a sharpness value.");
 279                        }
 280
 2281                        if (valuesCount != 2)
 282                        {
 2283                            throw new InvalidDataException("A sharpness statement has too many values.");
 284                        }
 2285                        if (TryIntParse(GetNextValue(ref currentLine, ref values), out var value))
 286                        {
 2287                            currentMaterial.Sharpness = value;
 288                        }
 2289                        break;
 290                    }
 291                    case "ni":
 292                    {
 2293                        if (currentMaterial == null)
 294                        {
 2295                            throw new InvalidDataException("The material name is not specified.");
 296                        }
 297
 2298                        if (valuesCount < 2)
 299                        {
 2300                            throw new InvalidDataException("A Ni statement must specify an optical density.");
 301                        }
 302
 2303                        if (valuesCount != 2)
 304                        {
 2305                            throw new InvalidDataException("A Ni statement has too many values.");
 306                        }
 307
 2308                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 309                        {
 2310                            currentMaterial.OpticalDensity = value;
 311                        }
 312
 2313                        break;
 314                    }
 315                    case "map_aat":
 316                        {
 2317                            if (currentMaterial == null)
 318                            {
 2319                                throw new InvalidDataException("The material name is not specified.");
 320                            }
 321
 2322                            if (valuesCount < 2)
 323                            {
 2324                                throw new InvalidDataException("A map_aat statement must specify a value.");
 325                            }
 326
 2327                            if (valuesCount != 2)
 328                            {
 2329                                throw new InvalidDataException("A map_aat statement has too many values.");
 330                            }
 331
 2332                            var value1 = GetNextValue(ref currentLine, ref values);
 333
 2334                            if (value1.Equals("on", StringComparison.OrdinalIgnoreCase))
 335                            {
 2336                                currentMaterial.IsAntiAliasingEnabled = true;
 337                            }
 2338                            else if (value1.Equals("off", StringComparison.OrdinalIgnoreCase))
 339                            {
 2340                                currentMaterial.IsAntiAliasingEnabled = false;
 341                            }
 342                            else
 343                            {
 2344                                throw new InvalidDataException("A map_aat statement must specify on or off.");
 345                            }
 346
 347                            break;
 348                        }
 349
 350                    case "map_ka":
 2351                        if (currentMaterial == null)
 352                        {
 2353                            throw new InvalidDataException("The material name is not specified.");
 354                        }
 355
 2356                        currentMaterial.AmbientMap = ParseMaterialMap("map_Ka", ref currentLine, ref values, valuesCount
 2357                        break;
 358
 359                    case "map_kd":
 2360                        if (currentMaterial == null)
 361                        {
 2362                            throw new InvalidDataException("The material name is not specified.");
 363                        }
 364
 2365                        currentMaterial.DiffuseMap = ParseMaterialMap("map_Kd", ref currentLine, ref values, valuesCount
 2366                        break;
 367
 368                    case "map_ke":
 2369                        if (currentMaterial == null)
 370                        {
 2371                            throw new InvalidDataException("The material name is not specified.");
 372                        }
 373
 2374                        currentMaterial.EmissiveMap = ParseMaterialMap("map_Ke", ref currentLine, ref values, valuesCoun
 2375                        break;
 376
 377                    case "map_ks":
 2378                        if (currentMaterial == null)
 379                        {
 2380                            throw new InvalidDataException("The material name is not specified.");
 381                        }
 382
 2383                        currentMaterial.SpecularMap = ParseMaterialMap("map_Ks", ref currentLine, ref values, valuesCoun
 2384                        break;
 385
 386                    case "map_ns":
 2387                        if (currentMaterial == null)
 388                        {
 2389                            throw new InvalidDataException("The material name is not specified.");
 390                        }
 391
 2392                        currentMaterial.SpecularExponentMap = ParseMaterialMap("map_Ns", ref currentLine, ref values, va
 2393                        break;
 394
 395                    case "map_d":
 396                    case "map_tr":
 2397                        if (currentMaterial == null)
 398                        {
 2399                            throw new InvalidDataException("The material name is not specified.");
 400                        }
 401
 2402                        currentMaterial.DissolveMap = ParseMaterialMap("map_d", ref currentLine, ref values, valuesCount
 2403                        break;
 404
 405                    case "decal":
 406                    case "map_decal":
 2407                        if (currentMaterial == null)
 408                        {
 2409                            throw new InvalidDataException("The material name is not specified.");
 410                        }
 411
 2412                        currentMaterial.DecalMap = ParseMaterialMap("decal", ref currentLine, ref values, valuesCount, s
 2413                        break;
 414
 415                    case "disp":
 416                    case "map_disp":
 2417                        if (currentMaterial == null)
 418                        {
 2419                            throw new InvalidDataException("The material name is not specified.");
 420                        }
 421
 2422                        currentMaterial.DispMap = ParseMaterialMap("disp", ref currentLine, ref values, valuesCount, set
 2423                        break;
 424
 425                    case "bump":
 426                    case "map_bump":
 2427                        if (currentMaterial == null)
 428                        {
 2429                            throw new InvalidDataException("The material name is not specified.");
 430                        }
 431
 2432                        currentMaterial.BumpMap = ParseMaterialMap("bump", ref currentLine, ref values, valuesCount, set
 2433                        break;
 434
 435                    case "refl":
 436                    case "map_refl":
 2437                        if (currentMaterial == null)
 438                        {
 2439                            throw new InvalidDataException("The material name is not specified.");
 440                        }
 441
 2442                        if (valuesCount < 4)
 443                        {
 2444                            throw new InvalidDataException("A refl statement must specify a type and a file name.");
 445                        }
 446
 2447                        ObjMaterialMap materialMap = ParseMaterialMap("refl", ref currentLine, ref values, valuesCount, 
 448
 449                        switch (materialMapType)
 450                        {
 451                            case MaterialMapType.Sphere:
 2452                                currentMaterial.ReflectionMap.Sphere = materialMap;
 2453                                break;
 454
 455                            case MaterialMapType.CubeTop:
 2456                                currentMaterial.ReflectionMap.CubeTop = materialMap;
 2457                                break;
 458
 459                            case MaterialMapType.CubeBottom:
 2460                                currentMaterial.ReflectionMap.CubeBottom = materialMap;
 2461                                break;
 462
 463                            case MaterialMapType.CubeFront:
 2464                                currentMaterial.ReflectionMap.CubeFront = materialMap;
 2465                                break;
 466
 467                            case MaterialMapType.CubeBack:
 2468                                currentMaterial.ReflectionMap.CubeBack = materialMap;
 2469                                break;
 470
 471                            case MaterialMapType.CubeLeft:
 2472                                currentMaterial.ReflectionMap.CubeLeft = materialMap;
 2473                                break;
 474
 475                            case MaterialMapType.CubeRight:
 2476                                currentMaterial.ReflectionMap.CubeRight = materialMap;
 2477                                break;
 478                        }
 479
 480                        break;
 481                    case "pr":
 482                    {
 2483                        if (currentMaterial == null)
 484                        {
 2485                            throw new InvalidDataException("The material name is not specified.");
 486                        }
 487
 2488                        if (valuesCount < 2)
 489                        {
 2490                            throw new InvalidDataException("A Pr statement must specify an optical density.");
 491                        }
 492
 2493                        if (valuesCount != 2)
 494                        {
 2495                            throw new InvalidDataException("A Pr statement has too many values.");
 496                        }
 497
 2498                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 499                        {
 2500                            currentMaterial.Roughness = value;
 501                        }
 502
 2503                        break;
 504                    }
 505                    case "map_pr":
 2506                        if (currentMaterial == null)
 507                        {
 2508                            throw new InvalidDataException("The material name is not specified.");
 509                        }
 510
 2511                        currentMaterial.RoughnessMap = ParseMaterialMap("map_Pr", ref currentLine, ref values, valuesCou
 2512                        break;
 513                    case "pm":
 514                    {
 2515                        if (currentMaterial == null)
 516                        {
 2517                            throw new InvalidDataException("The material name is not specified.");
 518                        }
 519
 2520                        if (valuesCount < 2)
 521                        {
 2522                            throw new InvalidDataException("A Pm statement must specify an optical density.");
 523                        }
 524
 2525                        if (valuesCount != 2)
 526                        {
 2527                            throw new InvalidDataException("A Pm statement has too many values.");
 528                        }
 529
 2530                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 531                        {
 2532                            currentMaterial.Metallic = value;
 533                        }
 2534                        break;
 535                    }
 536                    case "map_pm":
 2537                        if (currentMaterial == null)
 538                        {
 2539                            throw new InvalidDataException("The material name is not specified.");
 540                        }
 541
 2542                        currentMaterial.MetallicMap = ParseMaterialMap("map_Pm", ref currentLine, ref values, valuesCoun
 2543                        break;
 544                    case "ps":
 545                    {
 2546                        if (currentMaterial == null)
 547                        {
 2548                            throw new InvalidDataException("The material name is not specified.");
 549                        }
 550
 2551                        if (valuesCount < 2)
 552                        {
 2553                            throw new InvalidDataException("A Ps statement must specify an optical density.");
 554                        }
 555
 2556                        if (valuesCount != 2)
 557                        {
 2558                            throw new InvalidDataException("A Ps statement has too many values.");
 559                        }
 560
 2561                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 562                        {
 2563                            currentMaterial.Sheen = value;
 564                        }
 2565                        break;
 566                    }
 567                    case "map_ps":
 2568                        if (currentMaterial == null)
 569                        {
 2570                            throw new InvalidDataException("The material name is not specified.");
 571                        }
 572
 2573                        currentMaterial.SheenMap = ParseMaterialMap("map_Ps", ref currentLine, ref values, valuesCount, 
 2574                        break;
 575                    case "pc":
 576                    {
 2577                        if (currentMaterial == null)
 578                        {
 2579                            throw new InvalidDataException("The material name is not specified.");
 580                        }
 581
 2582                        if (valuesCount < 2)
 583                        {
 2584                            throw new InvalidDataException("A Pc statement must specify an optical density.");
 585                        }
 586
 2587                        if (valuesCount != 2)
 588                        {
 2589                            throw new InvalidDataException("A Pc statement has too many values.");
 590                        }
 591
 2592                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 593                        {
 2594                            currentMaterial.ClearCoatThickness = value;
 595                        }
 2596                        break;
 597                    }
 598                    case "pcr":
 599                    {
 2600                        if (currentMaterial == null)
 601                        {
 2602                            throw new InvalidDataException("The material name is not specified.");
 603                        }
 604
 2605                        if (valuesCount < 2)
 606                        {
 2607                            throw new InvalidDataException("A Pcr statement must specify an optical density.");
 608                        }
 609
 2610                        if (valuesCount != 2)
 611                        {
 2612                            throw new InvalidDataException("A Pcr statement has too many values.");
 613                        }
 614
 2615                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 616                        {
 2617                            currentMaterial.ClearCoatRoughness = value;
 618                        }
 2619                        break;
 620                    }
 621                    case "aniso":
 622                    {
 2623                        if (currentMaterial == null)
 624                        {
 2625                            throw new InvalidDataException("The material name is not specified.");
 626                        }
 627
 2628                        if (valuesCount < 2)
 629                        {
 2630                            throw new InvalidDataException("A aniso statement must specify an optical density.");
 631                        }
 632
 2633                        if (valuesCount != 2)
 634                        {
 2635                            throw new InvalidDataException("A aniso statement has too many values.");
 636                        }
 637
 2638                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 639                        {
 2640                            currentMaterial.Anisotropy = value;
 641                        }
 642
 2643                        break;
 644                    }
 645                    case "anisor":
 646                    {
 2647                        if (currentMaterial == null)
 648                        {
 2649                            throw new InvalidDataException("The material name is not specified.");
 650                        }
 651
 2652                        if (valuesCount < 2)
 653                        {
 2654                            throw new InvalidDataException("A anisor statement must specify an optical density.");
 655                        }
 656
 2657                        if (valuesCount != 2)
 658                        {
 2659                            throw new InvalidDataException("A anisor statement has too many values.");
 660                        }
 661
 2662                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 663                        {
 2664                            currentMaterial.AnisotropyRotation = value;
 665                        }
 2666                        break;
 667                    }
 668                    case "norm":
 2669                        if (currentMaterial == null)
 670                        {
 2671                            throw new InvalidDataException("The material name is not specified.");
 672                        }
 673
 2674                        currentMaterial.Norm = ParseMaterialMap("norm", ref currentLine, ref values, valuesCount, settin
 675                        break;
 676                }
 677            }
 678
 2679            mtl.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 680
 2681            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        {
 2687            if (valuesCount < 2)
 688            {
 2689                throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a color."));
 690            }
 691
 2692            var color = new ObjMaterialColor();
 693
 2694            ReadOnlySpan<char> value1 = GetNextValue(ref currentLine, ref values);
 2695            Span<char> valueBuffer = stackalloc char[value1.Length];
 2696            int value1Length = value1.ToLowerInvariant(valueBuffer);
 697
 698            //if (value1Length == -1)
 699            //{
 700            //    throw new InvalidDataException("the buffer is too small");
 701            //}
 702
 2703            int index = 1;
 704
 2705            switch (valueBuffer[..value1Length])
 706            {
 707                case "spectral":
 708                {
 2709                    index++;
 710
 2711                    if (valuesCount - index < 1)
 712                    {
 2713                        throw new InvalidDataException(string.Concat("A ", statement,
 2714                            " spectral statement must specify a file name."));
 715                    }
 716
 2717                    color.SpectralFileName = GetNextValue(ref currentLine, ref values).ToString();
 2718                    index++;
 719
 2720                    if (valuesCount > index)
 721                    {
 2722                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 723                        {
 2724                            color.SpectralFactor = value;
 725                        }
 726
 2727                        index++;
 728                    }
 729
 2730                    break;
 731                }
 732                case "xyz":
 733                {
 2734                    index++;
 735
 2736                    if (valuesCount - index < 1)
 737                    {
 2738                        throw new InvalidDataException(string.Concat("A ", statement,
 2739                            " xyz statement must specify a color."));
 740                    }
 741
 2742                    color.UseXYZColorSpace = true;
 743
 2744                    var xyz = new ObjVector3();
 2745                    if (TryFloatParse(GetNextValue(ref currentLine, ref values), out var value))
 746                    {
 2747                        xyz.X = value;
 748                    }
 749
 2750                    index++;
 751
 2752                    if (valuesCount > index)
 753                    {
 2754                        if (valuesCount - index < 2)
 755                        {
 2756                            throw new InvalidDataException(string.Concat("A ", statement,
 2757                                " xyz statement must specify a XYZ color."));
 758                        }
 759
 2760                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 761                        {
 2762                            xyz.Y = value;
 763                        }
 764
 2765                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 766                        {
 2767                            xyz.Z = value;
 768                        }
 769
 2770                        index += 2;
 771                    }
 772                    else
 773                    {
 2774                        xyz.Y = xyz.X;
 2775                        xyz.Z = xyz.X;
 776                    }
 777
 2778                    color.Color = xyz;
 2779                    break;
 780                }
 781                default:
 782                {
 2783                    var rgb = new ObjVector3();
 2784                    if (TryFloatParse(value1, out var value))
 785                    {
 2786                        rgb.X = value;
 787                    }
 788
 2789                    index++;
 790
 2791                    if (valuesCount > index)
 792                    {
 2793                        if (valuesCount - index < 2)
 794                        {
 2795                            throw new InvalidDataException(string.Concat("A ", statement,
 2796                                " statement must specify a RGB color."));
 797                        }
 798
 2799                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 800                        {
 2801                            rgb.Y = value;
 802                        }
 803
 2804                        if (TryFloatParse(GetNextValue(ref currentLine, ref values), out value))
 805                        {
 2806                            rgb.Z = value;
 807                        }
 808
 2809                        index += 2;
 810                    }
 811                    else
 812                    {
 2813                        rgb.Y = rgb.X;
 2814                        rgb.Z = rgb.X;
 815                    }
 816
 2817                    color.Color = rgb;
 818                    break;
 819                }
 820            }
 821
 2822            if (index != valuesCount)
 823            {
 2824                throw new InvalidDataException(string.Concat("A ", statement, " statement has too many values."));
 825            }
 826
 2827            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        {
 2845            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        {
 2851            materialMapType = MaterialMapType.None;
 852
 2853            var map = new ObjMaterialMap();
 2854            var charsRead = 0;
 855
 2856            int valueBufferSize = 16;
 2857            Span<char> valueBuffer = stackalloc char[valueBufferSize];
 858
 2859            for (int index = 0; index < valuesCount;)
 860            {
 2861                index++;
 862
 2863                if (valuesCount - index < 1)
 864                {
 2865                    throw new InvalidDataException(string.Concat("A ", statement, " statement must specify a filename.")
 866                }
 867
 2868                ReadOnlySpan<char> value1 = GetNextValue(ref currentLine, ref values);
 2869                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
 2873                if (value1Length == -1)
 874                {
 2875                    if (index == 1)
 876                    {
 2877                        if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 878                        {
 0879                            throw new InvalidDataException("A refl statement must specify a type.");
 880                        }
 881                    }
 2882                    if (settings.KeepWhitespacesOfMapFileReferences)
 883                    {
 2884                        map.FileName = new string(currentLine[(statement.Length + charsRead + index)..]);
 885                    }
 886                    else
 887                    {
 2888                        var sb = new StringBuilder();
 889
 2890                        sb.Append(value1);
 891
 2892                        for (int i = index + 1; i < valuesCount; i++)
 893                        {
 2894                            sb.Append(' ');
 2895                            sb.Append(GetNextValue(ref currentLine, ref values));
 896                        }
 897
 2898                        string filename = sb.ToString();
 899
 2900                        map.FileName = filename;
 901                    }
 902
 2903                    return map;
 904                }
 905
 2906                charsRead += value1Length;
 907
 2908                if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 909                {
 2910                    if (index == 1)
 911                    {
 2912                        if (!MemoryExtensions.Equals(valueBuffer[..value1Length], "-type", StringComparison.OrdinalIgnor
 913                        {
 2914                            throw new InvalidDataException("A refl statement must specify a type.");
 915                        }
 916                    }
 917                }
 918
 2919                switch (valueBuffer[..value1Length])
 920                {
 921                    case "-type":
 2922                        if (valuesCount - index < 2)
 923                        {
 2924                            throw new InvalidDataException(string.Concat("A ", statement, " -type option must specify a 
 925                        }
 926
 2927                        if (statement.Equals("refl", StringComparison.OrdinalIgnoreCase))
 928                        {
 2929                            ReadOnlySpan<char> value2 = GetNextValue(ref currentLine, ref values);
 2930                            int value2Length = value2.ToLowerInvariant(valueBuffer);
 2931                            charsRead += value2Length;
 932
 933                            //if (value2Length == -1)
 934                            //{
 935                            //    throw new InvalidDataException("the buffer is too small");
 936                            //}
 937
 2938                            switch (valueBuffer[..value2Length])
 939                            {
 940                                case "sphere":
 2941                                    materialMapType = MaterialMapType.Sphere;
 2942                                    break;
 943
 944                                case "cube_top":
 2945                                    materialMapType = MaterialMapType.CubeTop;
 2946                                    break;
 947
 948                                case "cube_bottom":
 2949                                    materialMapType = MaterialMapType.CubeBottom;
 2950                                    break;
 951
 952                                case "cube_front":
 2953                                    materialMapType = MaterialMapType.CubeFront;
 2954                                    break;
 955
 956                                case "cube_back":
 2957                                    materialMapType = MaterialMapType.CubeBack;
 2958                                    break;
 959
 960                                case "cube_left":
 2961                                    materialMapType = MaterialMapType.CubeLeft;
 2962                                    break;
 963
 964                                case "cube_right":
 2965                                    materialMapType = MaterialMapType.CubeRight;
 2966                                    break;
 967                            }
 968                        }
 969                        else
 970                        {
 2971                            GetNextValue(ref currentLine, ref values);
 972                        }
 973
 2974                        index++;
 2975                        break;
 976
 977                    case "-blenu":
 978                        {
 2979                            if (valuesCount - index < 2)
 980                            {
 2981                                throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must speci
 982                            }
 983
 2984                            var value2 = GetNextValue(ref currentLine, ref values);
 2985                            charsRead += value2.Length;
 986
 2987                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 988                            {
 2989                                map.IsHorizontalBlendingEnabled = true;
 990                            }
 2991                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 992                            {
 2993                                map.IsHorizontalBlendingEnabled = false;
 994                            }
 995                            else
 996                            {
 2997                                throw new InvalidDataException(string.Concat("A ", statement, " -blenu option must speci
 998                            }
 999
 21000                            index++;
 21001                            break;
 1002                        }
 1003
 1004                    case "-blenv":
 1005                        {
 21006                            if (valuesCount - index < 2)
 1007                            {
 21008                                throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must speci
 1009                            }
 1010
 21011                            var value2 = GetNextValue(ref currentLine, ref values);
 21012                            charsRead += value2.Length;
 1013
 21014                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1015                            {
 21016                                map.IsVerticalBlendingEnabled = true;
 1017                            }
 21018                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1019                            {
 21020                                map.IsVerticalBlendingEnabled = false;
 1021                            }
 1022                            else
 1023                            {
 21024                                throw new InvalidDataException(string.Concat("A ", statement, " -blenv option must speci
 1025                            }
 1026
 21027                            index++;
 21028                            break;
 1029                        }
 1030
 1031                    case "-bm":
 1032                    {
 21033                        if (valuesCount - index < 2)
 1034                        {
 21035                            throw new InvalidDataException(string.Concat("A ", statement, " -bm option must specify a va
 1036                        }
 1037
 21038                        var value2 = GetNextValue(ref currentLine, ref values);
 21039                        charsRead += value2.Length;
 1040
 21041                        if (TryFloatParse(value2, out var value))
 1042                        {
 21043                            map.BumpMultiplier = value;
 1044                        }
 1045
 21046                        index++;
 21047                        break;
 1048                    }
 1049                    case "-boost":
 1050                    {
 21051                        if (valuesCount - index < 2)
 1052                        {
 21053                            throw new InvalidDataException(string.Concat("A ", statement,
 21054                                " -boost option must specify a value."));
 1055                        }
 1056
 21057                        var value2 = GetNextValue(ref currentLine, ref values);
 21058                        charsRead += value2.Length;
 1059
 21060                        if (TryFloatParse(value2, out var value))
 1061                        {
 21062                            map.Boost = value;
 1063                        }
 1064
 21065                        index++;
 21066                        break;
 1067                    }
 1068                    case "-cc":
 1069                        {
 21070                            if (valuesCount - index < 2)
 1071                            {
 21072                                throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify 
 1073                            }
 1074
 21075                            var value2 = GetNextValue(ref currentLine, ref values);
 21076                            charsRead += value2.Length;
 1077
 21078                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1079                            {
 21080                                map.IsColorCorrectionEnabled = true;
 1081                            }
 21082                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1083                            {
 21084                                map.IsColorCorrectionEnabled = false;
 1085                            }
 1086                            else
 1087                            {
 21088                                throw new InvalidDataException(string.Concat("A ", statement, " -cc option must specify 
 1089                            }
 1090
 21091                            index++;
 21092                            break;
 1093                        }
 1094
 1095                    case "-clamp":
 1096                        {
 21097                            if (valuesCount - index < 2)
 1098                            {
 21099                                throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must speci
 1100                            }
 1101
 21102                            var value2 = GetNextValue(ref currentLine, ref values);
 21103                            charsRead += value2.Length;
 1104
 21105                            if (value2.Equals("on", StringComparison.OrdinalIgnoreCase))
 1106                            {
 21107                                map.IsClampingEnabled = true;
 1108                            }
 21109                            else if (value2.Equals("off", StringComparison.OrdinalIgnoreCase))
 1110                            {
 21111                                map.IsClampingEnabled = false;
 1112                            }
 1113                            else
 1114                            {
 21115                                throw new InvalidDataException(string.Concat("A ", statement, " -clamp option must speci
 1116                            }
 1117
 21118                            index++;
 21119                            break;
 1120                        }
 1121
 1122                    case "-imfchan":
 1123                        {
 21124                            if (valuesCount - index < 2)
 1125                            {
 21126                                throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must spe
 1127                            }
 1128
 21129                            ReadOnlySpan<char> value2 = GetNextValue(ref currentLine, ref values);
 21130                            int value2Length = value2.ToLowerInvariant(valueBuffer);
 21131                            charsRead += value2Length;
 1132
 1133                            //if (value2Length == -1)
 1134                            //{
 1135                            //    throw new InvalidDataException("the buffer is too small");
 1136                            //}
 1137
 21138                            switch (valueBuffer[..value2Length])
 1139                            {
 1140                                case "r":
 21141                                    map.ScalarChannel = ObjMapChannel.Red;
 21142                                    break;
 1143
 1144                                case "g":
 21145                                    map.ScalarChannel = ObjMapChannel.Green;
 21146                                    break;
 1147
 1148                                case "b":
 21149                                    map.ScalarChannel = ObjMapChannel.Blue;
 21150                                    break;
 1151
 1152                                case "m":
 21153                                    map.ScalarChannel = ObjMapChannel.Matte;
 21154                                    break;
 1155
 1156                                case "l":
 21157                                    map.ScalarChannel = ObjMapChannel.Luminance;
 21158                                    break;
 1159
 1160                                case "z":
 21161                                    map.ScalarChannel = ObjMapChannel.Depth;
 21162                                    break;
 1163
 1164                                default:
 21165                                    throw new InvalidDataException(string.Concat("A ", statement, " -imfchan option must
 1166                            }
 1167
 21168                            index++;
 21169                            break;
 1170                        }
 1171
 1172                    case "-mm":
 1173                    {
 21174                        if (valuesCount - index < 3)
 1175                        {
 21176                            throw new InvalidDataException(string.Concat("A ", statement, " -mm option must specify a ba
 1177                        }
 1178
 21179                        var value2 = GetNextValue(ref currentLine, ref values);
 21180                        charsRead += value2.Length;
 1181
 21182                        if (TryFloatParse(value2, out var value))
 1183                        {
 21184                            map.ModifierBase = value;
 1185                        }
 21186                        value2 = GetNextValue(ref currentLine, ref values);
 21187                        charsRead += value2.Length;
 1188
 21189                        if (TryFloatParse(value2, out value))
 1190                        {
 21191                            map.ModifierGain = value;
 1192                        }
 1193
 21194                        index += 2;
 21195                        break;
 1196                    }
 1197
 1198
 1199                    case "-o":
 1200                    {
 21201                        if (valuesCount - index < 2)
 1202                        {
 21203                            throw new InvalidDataException(string.Concat("A ", statement,
 21204                                " -o option must specify at least 2 values."));
 1205                        }
 1206
 21207                        var offset = new ObjVector3();
 21208                        var value2 = GetNextValue(ref currentLine, ref values);
 1209
 21210                        if (TryFloatParse(value2, out var value))
 1211                        {
 21212                            offset.X = value;
 21213                            index++;
 21214                            charsRead += value2.Length;
 1215                        }
 1216                        else
 1217                        {
 01218                            map.Offset = offset;
 01219                            break;
 1220                        }
 1221
 21222                        if (valuesCount - index > 2)
 1223                        {
 21224                            value2 = GetNextValue(ref currentLine, ref values);
 21225                            if (TryFloatParse(value2, out value))
 1226                            {
 21227                                offset.Y = value;
 21228                                index++;
 21229                                charsRead += value2.Length;
 1230                            }
 1231                            else
 1232                            {
 21233                                map.Offset = offset;
 21234                                break;
 1235                            }
 1236
 21237                            if (valuesCount - index > 2)
 1238                            {
 21239                                value2 = GetNextValue(ref currentLine, ref values);
 21240                                if (TryFloatParse(value2, out value))
 1241                                {
 21242                                    offset.Z = value;
 21243                                    index++;
 21244                                    charsRead += value2.Length;
 1245                                }
 1246                            }
 1247                        }
 1248
 21249                        map.Offset = offset;
 21250                        break;
 1251                    }
 1252                    case "-s":
 1253                    {
 21254                        if (valuesCount - index < 2)
 1255                        {
 21256                            throw new InvalidDataException(string.Concat("A ", statement,
 21257                                " -s option must specify at least 2 values."));
 1258                        }
 1259
 21260                        var scale = new ObjVector3(1.0f, 1.0f, 1.0f);
 1261
 21262                        var value2 = GetNextValue(ref currentLine, ref values);
 1263
 21264                        if (TryFloatParse(value2, out var value))
 1265                        {
 21266                            scale.X = value;
 21267                            index++;
 21268                            charsRead += value2.Length;
 1269                        }
 1270                        else
 1271                        {
 01272                            map.Scale = scale;
 01273                            break;
 1274                        }
 1275
 21276                        if (valuesCount - index > 2)
 1277                        {
 21278                            value2 = GetNextValue(ref currentLine, ref values);
 21279                            if (TryFloatParse(value2, out value))
 1280                            {
 21281                                scale.Y = value;
 21282                                index++;
 21283                                charsRead += value2.Length;
 1284                            }
 1285                            else
 1286                            {
 21287                                map.Scale = scale;
 21288                                break;
 1289                            }
 1290
 21291                            if (valuesCount - index > 2)
 1292                            {
 21293                                value2 = GetNextValue(ref currentLine, ref values);
 21294                                if (TryFloatParse(value2, out value))
 1295                                {
 21296                                    scale.Z = value;
 21297                                    index++;
 21298                                    charsRead += value2.Length;
 1299                                }
 1300                            }
 1301                        }
 1302
 21303                        map.Scale = scale;
 21304                        break;
 1305                    }
 1306                    case "-t":
 1307                    {
 21308                        if (valuesCount - index < 2)
 1309                        {
 21310                            throw new InvalidDataException(string.Concat("A ", statement,
 21311                                " -t option must specify at least 2 values."));
 1312                        }
 1313
 21314                        var turbulence = new ObjVector3();
 1315
 21316                        var value2 = GetNextValue(ref currentLine, ref values);
 1317
 21318                        if (TryFloatParse(value2, out var value))
 1319                        {
 21320                            turbulence.X = value;
 21321                            index++;
 21322                            charsRead += value2.Length;
 1323                        }
 1324                        else
 1325                        {
 01326                            map.Turbulence = turbulence;
 01327                            break;
 1328                        }
 1329
 21330                        if (valuesCount - index > 2)
 1331                        {
 21332                            value2 = GetNextValue(ref currentLine, ref values);
 21333                            if (TryFloatParse(value2, out value))
 1334                            {
 21335                                turbulence.Y = value;
 21336                                index++;
 21337                                charsRead += value2.Length;
 1338                            }
 1339                            else
 1340                            {
 21341                                map.Turbulence = turbulence;
 21342                                break;
 1343                            }
 1344
 21345                            if (valuesCount - index > 2)
 1346                            {
 21347                                value2 = GetNextValue(ref currentLine, ref values);
 21348                                if (TryFloatParse(value2, out value))
 1349                                {
 21350                                    turbulence.Z = value;
 21351                                    index++;
 21352                                    charsRead += value2.Length;
 1353                                }
 1354                            }
 1355                        }
 1356
 21357                        map.Turbulence = turbulence;
 21358                        break;
 1359                    }
 1360                    case "-texres":
 1361                    {
 21362                        if (valuesCount - index < 2)
 1363                        {
 21364                            throw new InvalidDataException(string.Concat("A ", statement,
 21365                                " -texres option must specify a value."));
 1366                        }
 21367                        var value2 = GetNextValue(ref currentLine, ref values);
 21368                        charsRead += value2.Length;
 21369                        if (TryIntParse(value2, out var value))
 1370                        {
 21371                            map.TextureResolution = value;
 1372                        }
 1373
 21374                        index++;
 21375                        break;
 1376                    }
 1377                    default:
 1378                        {
 21379                            if (settings.KeepWhitespacesOfMapFileReferences)
 1380                            {
 21381                                map.FileName = new string(currentLine[(statement.Length + index + charsRead - value1.Len
 1382                            }
 1383                            else
 1384                            {
 21385                                var sb = new StringBuilder();
 1386
 21387                                sb.Append(value1);
 1388
 21389                                for (int i = index + 1; i < valuesCount; i++)
 1390                                {
 21391                                    sb.Append(' ');
 21392                                    sb.Append(GetNextValue(ref currentLine, ref values));
 1393                                }
 1394
 21395                                string filename = sb.ToString();
 1396
 21397                                map.FileName = filename;
 1398                            }
 21399                            index = valuesCount;
 1400
 1401                            break;
 1402                        }
 1403                }
 1404            }
 1405
 21406            return map;
 1407        }
 1408    }
 1409}
 1410
 1411#endif