Beispiel #1
0
        // Allow loading VirtualTextures from modded AssetMetadatas.

        public static VirtualTexture CreateTexture(ModAsset metadata)
        {
            VirtualTexture virtualTexture = (VirtualTexture)(object)new patch_VirtualTexture(metadata);

            assets.Add(virtualTexture);
            return(virtualTexture);
        }
Beispiel #2
0
        // Extra step for when this parent texture's size doesn't match with hardcoded sizes.
        private static void ScaleSubtextureCoords(MTexture parent, ref int x, ref int y, ref int width, ref int height)
        {
            VirtualTexture parentTexture = ((patch_MTexture)parent)._Texture;

            // Try to find the most up-to-date MTexture parent belonging to this texture.
            // Use it for subtexturing instead.
            MTexture atlased;

            if (AtlasExt.VTextureToMTextureMap != null && AtlasExt.VTextureToMTextureMap.TryGetValue(parentTexture.Name, out atlased))
            {
                parent = atlased;
            }

            MTextureOverride layer = ((patch_MTexture)parent).OverrideMeta;

            if (layer == null)
            {
                return;
            }

            // Works just fine... except for the _orignal_ snow flakes in GameLoader.
            float xf = parentTexture.Width / (float)parent.Width;
            float yf = parentTexture.Height / (float)parent.Height;

            x      = (int)Math.Round(x * xf);
            y      = (int)Math.Round(y * yf);
            width  = (int)Math.Round(width * xf);
            height = (int)Math.Round(height * yf);
        }
Beispiel #3
0
    private void Start()
    {
        NativeArray <VirtualTextureFormat> formats = new NativeArray <VirtualTextureFormat>(1, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

        formats[0] = new VirtualTextureFormat(VirtualTextureSize.x256, RenderTextureFormat.ARGB32, "_SampledVT");
        vt         = new VirtualTexture(5, 2, formats);
        StartCoroutine(run());
    }
Beispiel #4
0
 public MTextureOverride AddOverride(VirtualTexture texture, Vector2 drawOffset, int frameWidth, int frameHeight)
 => AddOverride(new MTextureOverride {
     Texture       = texture,
     ClipRect      = new Rectangle(0, 0, texture.Width, texture.Height),
     ForceClipRect = true,
     DrawOffset    = drawOffset,
     Width         = frameWidth,
     Height        = frameHeight,
 });
Beispiel #5
0
        // Mods can't access patch_ classes directly.
        // We thus expose any new members through extensions.

        /// <summary>
        /// If the VirtualTexture originates from a mod, get the mod asset metadata.
        /// </summary>
        public static ModAsset GetMetadata(this VirtualTexture self)
        => ((patch_VirtualTexture)(object)self).Metadata;
Beispiel #6
0
 public MTextureOverride AddOverride(VirtualTexture texture)
 => AddOverride(texture, DrawOffset, Width, Height);
Beispiel #7
0
 /// <summary>
 /// Add a new override layer, based on the given VirtualTexture and parameters.
 /// </summary>
 /// <returns>The resulting MTextureOverride.</returns>
 public static MTextureOverride AddOverride(this MTexture self, VirtualTexture texture, Vector2 drawOffset, int frameWidth, int frameHeight)
 => ((patch_MTexture)self).AddOverride(texture, drawOffset, frameWidth, frameHeight);
Beispiel #8
0
 public extern void orig_set_Texture(VirtualTexture value);
Beispiel #9
0
 public MTextureOverlay AddOverlay(VirtualTexture texture)
 => AddOverlay(texture, DrawOffset, Width, Height);
Beispiel #10
0
 /// <summary>
 /// Add a new override layer, based on the given VirtualTexture.
 /// </summary>
 /// <returns>The resulting MTextureOverride.</returns>
 public static MTextureOverride AddOverride(this MTexture self, VirtualTexture texture)
 => ((patch_MTexture)self).AddOverride(texture);
Beispiel #11
0
 /// <summary>
 /// Set a fallback texture in case the texture becomes unavailable on reload.
 /// </summary>
 public static void SetFallback(this VirtualTexture self, VirtualTexture fallback)
 => ((patch_VirtualTexture)(object)self).Fallback = fallback;
Beispiel #12
0
        public static void Ingest(this Atlas self, AssetMetadata asset)
        {
            // Crawl through all child assets.
            if (asset.AssetType == typeof(AssetTypeDirectory))
            {
                foreach (AssetMetadata child in asset.Children)
                {
                    self.Ingest(child);
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.AssetType == typeof(Texture2D))
            {
                string parentPath = self.GetDataPath();
                if (parentPath.StartsWith(Everest.Content.PathContentOrig))
                {
                    parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
                }
                parentPath = parentPath.Replace('\\', '/');

                string path = asset.PathRelative;
                if (!path.StartsWith(parentPath))
                {
                    return;
                }
                path = path.Substring(parentPath.Length + 1);

                VirtualTexture replacementV = VirtualContentExt.CreateTexture(asset);
                MTexture       replacement;
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();

                Dictionary <string, MTexture> textures = self.GetTextures();
                MTexture existing;
                if (textures.TryGetValue(path, out existing))
                {
                    // We're the currently active overlay.
                    if (existing.Texture.GetMetadata() == asset)
                    {
                        return;
                    }

                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        existing.AddOverlay(replacementV, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Keep width and height from existing instance.
                        existing.AddOverlay(replacementV, existing.DrawOffset, existing.Width, existing.Height);
                    }

                    replacement = existing;
                }
                else
                {
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        replacement = new MTexture(replacementV, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Apply width and height from replacement texture.
                        replacement = new MTexture(replacementV);
                    }
                    // TODO: What's with the AtlasPath? Seems to stem from an atlas metadata property...
                }

                self[path] = replacement;
                return;
            }
        }
Beispiel #13
0
            public static void Dump(string assetNameFull, object asset)
            {
                string assetName = assetNameFull;

                if (assetName.StartsWith(PathContentOrig))
                {
                    assetName = assetName.Substring(PathContentOrig.Length + 1);
                }
                else if (File.Exists(assetName))
                {
                    return; // Don't dump absolutely loaded files.
                }
                string pathDump = Path.Combine(PathDUMP, assetName);

                Directory.CreateDirectory(Path.GetDirectoryName(pathDump));

                if (asset is IMeta)
                {
                    if (!File.Exists(pathDump + ".meta.yaml"))
                    {
                        using (Stream stream = File.OpenWrite(pathDump + ".meta.yaml"))
                            using (StreamWriter writer = new StreamWriter(stream))
                                YamlHelper.Serializer.Serialize(writer, asset, asset.GetType());
                    }
                }
                else if (asset is Texture2D)
                {
                    Texture2D tex = (Texture2D)asset;
                    if (!File.Exists(pathDump + ".png"))
                    {
                        using (Stream stream = File.OpenWrite(pathDump + ".png"))
                            tex.SaveAsPng(stream, tex.Width, tex.Height);
                    }
                }
                else if (asset is VirtualTexture)
                {
                    VirtualTexture tex = (VirtualTexture)asset;
                    Dump(assetName, tex.Texture);
                }
                else if (asset is MTexture)
                {
                    MTexture tex = (MTexture)asset;
                    // Always copy even if !.IsSubtexture() as we need to Postdivide()
                    using (Texture2D region = tex.GetSubtextureCopy().Postdivide())
                        Dump(assetName, region);

                    if (tex.DrawOffset.X != 0 || tex.DrawOffset.Y != 0 ||
                        tex.Width != tex.ClipRect.Width || tex.Height != tex.ClipRect.Height
                        )
                    {
                        Dump(assetName, new MTextureMeta {
                            X      = (int)Math.Round(tex.DrawOffset.X),
                            Y      = (int)Math.Round(tex.DrawOffset.Y),
                            Width  = tex.Width,
                            Height = tex.Height
                        });
                    }
                }
                else if (asset is Atlas)
                {
                    Atlas atlas = (Atlas)asset;

                    /*
                     * for (int i = 0; i < atlas.Sources.Count; i++) {
                     *  VirtualTexture source = atlas.Sources[i];
                     *  string name = source.Name;
                     *
                     *  if (name.StartsWith(assetNameFull))
                     *      name = assetName + "_s_" + name.Substring(assetNameFull.Length);
                     *  else
                     *      name = Path.Combine(assetName + "_s", name);
                     *  if (name.EndsWith(".data") || name.EndsWith(".meta"))
                     *      name = name.Substring(0, name.Length - 5);
                     *
                     *  Dump(name, source);
                     * }
                     */

                    Dictionary <string, MTexture> textures = atlas.GetTextures();
                    foreach (KeyValuePair <string, MTexture> kvp in textures)
                    {
                        string   name   = kvp.Key;
                        MTexture source = kvp.Value;
                        Dump(Path.Combine(assetName, name.Replace('/', Path.DirectorySeparatorChar)), source);
                    }
                }

                // TODO: Dump more asset types if required.
            }
Beispiel #14
0
        /// <summary>
        /// Feed the given ModAsset into the atlas.
        /// </summary>
        public static void Ingest(this Atlas atlas, ModAsset asset)
        {
            if (asset == null)
            {
                return;
            }

            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                lock (asset.Children) {
                    foreach (ModAsset child in asset.Children)
                    {
                        atlas.Ingest(child);
                    }
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type != typeof(Texture2D))
            {
                return;
            }

            string parentPath = atlas.GetDataPath();

            if (parentPath.StartsWith(Everest.Content.PathContentOrig))
            {
                parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
            }
            parentPath = parentPath.Replace('\\', '/');

            string path = asset.PathVirtual;

            if (!path.StartsWith(parentPath + "/"))
            {
                return;
            }
            path = path.Substring(parentPath.Length + 1);

            Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{Path.GetFileName(atlas.GetDataPath())} + ({asset.Source?.Name ?? "???"}) {path}");

            MTexture mtex;

            Dictionary <string, MTexture> textures = atlas.GetTextures();

            if (textures.TryGetValue(path, out mtex))
            {
                mtex.SetOverride(asset);
            }
            else
            {
                VirtualTexture vtex = VirtualContentExt.CreateTexture(asset);
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();
                if (meta != null)
                {
                    // Apply width and height from meta.
                    if (meta.Width == 0)
                    {
                        meta.Width = vtex.Width;
                    }
                    if (meta.Height == 0)
                    {
                        meta.Height = vtex.Height;
                    }
                    mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                }
                else
                {
                    // Apply width and height from replacement texture.
                    mtex = new MTexture(vtex);
                }
                mtex.AtlasPath = path;
                mtex.SetAtlas(atlas);
                mtex.SetOverride(asset);
            }

            atlas.ResetCaches();
            atlas[path] = mtex;
        }
        private static void onTextureLazyLoad(On.Monocle.VirtualTexture.orig_Reload orig, VirtualTexture self)
        {
            // this is actually called on every texture load, so we need to check if this is a lazy load or not
            string name = self.Name;

            if (!preloadingTextures && lazilyLoadedTextures.Contains(name))
            {
                string currentMap = (Engine.Scene as Level)?.Session?.Area.GetSID();

                Logger.Log(LogLevel.Debug, "CollabUtils2/LazyLoadingHandler", name + " was lazily loaded by Everest! It will be associated to map " + currentMap + ".");
                newPaths.Add(name);

                if (currentMap != null)
                {
                    // add the texture to the lists associated to this map
                    if (!pathsPerMap.TryGetValue(currentMap, out HashSet <string> pathsForThisMap))
                    {
                        pathsForThisMap         = new HashSet <string>();
                        pathsPerMap[currentMap] = pathsForThisMap;
                    }
                    pathsForThisMap.Add(name);

                    if (!texturesPerMap.TryGetValue(currentMap, out HashSet <VirtualTexture> texturesForThisMap))
                    {
                        texturesForThisMap         = new HashSet <VirtualTexture>();
                        texturesPerMap[currentMap] = texturesForThisMap;
                    }
                    texturesForThisMap.Add(self);
                }
            }

            orig(self);
        }
Beispiel #16
0
        /// <summary>
        /// Feed the given ModAsset into the atlas.
        /// </summary>
        public static void Ingest(this Atlas self, ModAsset asset)
        {
            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                foreach (ModAsset child in asset.Children)
                {
                    self.Ingest(child);
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type == typeof(Texture2D))
            {
                Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{self.GetDataPath()} + {asset.PathVirtual}");

                string parentPath = self.GetDataPath();
                if (parentPath.StartsWith(Everest.Content.PathContentOrig))
                {
                    parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
                }
                parentPath = parentPath.Replace('\\', '/');

                bool   lq   = false;
                string path = asset.PathVirtual;

                if (path.StartsWith(parentPath + "LQ/"))
                {
                    lq   = true;
                    path = path.Substring(parentPath.Length + 3);
                }
                else if (path.StartsWith(parentPath + "/"))
                {
                    path = path.Substring(parentPath.Length + 1);
                }
                else
                {
                    return;
                }

                VirtualTexture vtex = VirtualContentExt.CreateTexture(asset);
                MTexture       mtex;
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();
                if (meta != null)
                {
                    if (meta.Width == 0)
                    {
                        meta.Width = vtex.Width;
                    }
                    if (meta.Height == 0)
                    {
                        meta.Height = vtex.Height;
                    }
                }

                Dictionary <string, MTexture> textures = self.GetTextures();
                MTexture existing;
                if (textures.TryGetValue(path, out existing))
                {
                    if (lq && !CoreModule.Settings.LQAtlas)
                    {
                        return;
                    }

                    if (existing.Texture.GetMetadata() == asset)
                    {
                        return; // We're the currently active overlay.
                    }
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        existing.AddOverride(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Keep width and height from existing instance.
                        existing.AddOverride(vtex, existing.DrawOffset, existing.Width, existing.Height);
                    }

                    mtex = existing;
                }
                else
                {
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Apply width and height from replacement texture.
                        mtex = new MTexture(vtex);
                    }
                    mtex.SetAtlasPath(path);
                }

                VTextureToMTextureMap[vtex.Name] = mtex;
                self[path] = mtex;
                if (!self.Sources.Contains(vtex))
                {
                    self.Sources.Add(vtex);
                }
                return;
            }
        }
Beispiel #17
0
        public void Ingest(ModAsset asset)
        {
            if (asset == null)
            {
                return;
            }

            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                lock (asset.Children) {
                    foreach (ModAsset child in asset.Children)
                    {
                        Ingest(child);
                    }
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type != typeof(Texture2D))
            {
                return;
            }

            string path = asset.PathVirtual;

            if (!path.StartsWith(RelativeDataPath))
            {
                return;
            }
            path = path.Substring(RelativeDataPath.Length);

            if (textures.TryGetValue(path, out MTexture mtex))
            {
                Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{Path.GetFileName(DataPath)} + ({asset.Source?.Name ?? "???"}) {path}");
                mtex.SetOverride(asset);
                this[path] = mtex;
                return;
            }

            VirtualTexture vtex = VirtualContentExt.CreateTexture(asset);
            MTextureMeta   meta = asset.GetMeta <MTextureMeta>();

            if (meta != null)
            {
                // Apply width and height from meta.
                if (meta.Width == 0)
                {
                    meta.Width = vtex.Width;
                }
                if (meta.Height == 0)
                {
                    meta.Height = vtex.Height;
                }
                mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
            }
            else
            {
                // Apply width and height from replacement texture.
                mtex = new MTexture(vtex);
            }
            mtex.AtlasPath = path;
            mtex.SetAtlas(this);
            mtex.SetOverride(asset);
            this[path] = mtex;
        }
Beispiel #18
0
        public static Language LoadLanguage(string filename)
        {
            Language language = orig_LoadLanguage(filename);

            if (language.Id.Equals("english", StringComparison.InvariantCultureIgnoreCase))
            {
                FallbackLanguage = language;
            }

            string path = filename;

            if (path.StartsWith(Everest.Content.PathContentOrig))
            {
                path = path.Substring(Everest.Content.PathContentOrig.Length + 1);
            }
            path = path.Replace('\\', '/');
            if (path.EndsWith(".txt"))
            {
                path = path.Substring(0, path.Length - 4);
            }

            IEnumerable <ModAsset> metas = Everest.Content.Mods.Select(mod => {
                ModAsset asset;
                if (mod.Map.TryGetValue(path, out asset))
                {
                    return(asset);
                }
                return(null);
            }).Where(asset => asset != null);

            if (!metas.Any())
            {
                return(language);
            }

            foreach (ModAsset meta in metas)
            {
                string line;

                string        currentName = "";
                StringBuilder builder     = new StringBuilder();
                string        prev        = "";
                using (StreamReader reader = new StreamReader(meta.Stream))
                    while (reader.Peek() != -1)
                    {
                        line = reader.ReadLine().Trim('\r', '\n').Trim();

                        // The following is the original parser decompiled and formatted to our best understanding.

                        // ???
                        bool startsWithVariable = false;
                        foreach (string variable in LanguageDataVariables)
                        {
                            if (!string.IsNullOrEmpty(variable) && line.StartsWith(variable, StringComparison.InvariantCultureIgnoreCase))
                            {
                                startsWithVariable = true;
                                break;
                            }
                        }
                        if (!startsWithVariable)
                        {
                            line = Regex.Replace(line, @"\[unknown\]", @"", RegexOptions.IgnoreCase);
                            line = Regex.Replace(line, @"\[left\]", @"{left}", RegexOptions.IgnoreCase);
                            line = Regex.Replace(line, @"\[right\]", @"{right}", RegexOptions.IgnoreCase);
                            line = Regex.Replace(line, @"\[(?<content>[^\[\\]*(?:\\.[^\]\\]*)*)\]", @"{portrait ${content}}");
                        }

                        if (line.Length <= 0)
                        {
                            continue;
                        }
                        if (line[0] == '#')
                        {
                            continue;
                        }

                        line = line.Replace("\\#", "#");
                        bool isVariable = variable.IsMatch(line);
                        if (!isVariable)
                        {
                            if (builder.Length > 0)
                            {
                                string built = builder.ToString();
                                if (!built.EndsWith("{break}") && !built.EndsWith("{n}") && command.Replace(prev, "").Length > 0)
                                {
                                    builder.Append("{break}");
                                }
                            }
                            builder.Append(line);
                            goto Next;
                        }

                        if (!string.IsNullOrEmpty(currentName) && !language.Dialog.ContainsKey(currentName))
                        {
                            language.Dialog.Add(currentName, builder.ToString());
                        }

                        string[] splitByEqual = line.Split('=');
                        string   name         = splitByEqual[0].Trim();
                        string   value        = (splitByEqual.Length > 1) ? splitByEqual[1].Trim() : "";

                        if (name.Equals("language", StringComparison.OrdinalIgnoreCase))
                        {
                            string[] splitByComma = value.Split(',');
                            if (!Dialog.Languages.TryGetValue(splitByComma[0], out language))
                            {
                                language          = new Language();
                                language.FontFace = null;
                                language.Id       = splitByComma[0];
                                Dialog.Languages.Add(language.Id, language);
                            }
                            if (splitByComma.Length > 1)
                            {
                                language.Label = splitByComma[1];
                            }
                            goto Next;
                        }

                        if (name.Equals("icon", StringComparison.OrdinalIgnoreCase))
                        {
                            VirtualTexture texture = VirtualContent.CreateTexture(Path.Combine("Dialog", value));
                            language.Icon = new MTexture(texture);
                            goto Next;
                        }

                        if (name.Equals("order", StringComparison.OrdinalIgnoreCase))
                        {
                            language.Order = int.Parse(value);
                            goto Next;
                        }

                        if (name.Equals("font", StringComparison.OrdinalIgnoreCase))
                        {
                            string[] splitByComma = value.Split(',');
                            language.FontFace     = splitByComma[0];
                            language.FontFaceSize = float.Parse(splitByComma[1], CultureInfo.InvariantCulture);
                            goto Next;
                        }

                        if (name.Equals("SPLIT_REGEX", StringComparison.OrdinalIgnoreCase))
                        {
                            language.SplitRegex = value;
                            goto Next;
                        }

                        if (name.Equals("commas", StringComparison.OrdinalIgnoreCase))
                        {
                            language.CommaCharacters = value;
                            goto Next;
                        }

                        if (name.Equals("periods", StringComparison.OrdinalIgnoreCase))
                        {
                            language.PeriodCharacters = value;
                            goto Next;
                        }

                        currentName = name;
                        builder.Clear();
                        builder.Append(value);

Next:
                        prev = line;
                    }

                // The game originally also checks if the key already exists and uses Add(...).
                if (!string.IsNullOrEmpty(currentName) /* && !language.Dialog.ContainsKey(currentName)*/)
                {
                    // language.Dialog.Add(currentName, builder.ToString());
                    language.Dialog[currentName] = builder.ToString();
                }
            }

            return(language);
        }
Beispiel #19
0
        /// <summary>
        /// Load the custom mountain models for mods.
        /// </summary>
        public static void LoadModData()
        {
            if (!ModsDataLoaded)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                lock (Everest.Content.Map) {
                    foreach (KeyValuePair <string, ModAsset> kvp in Everest.Content.Map)
                    {
                        MapMeta meta;
                        // Check if the meta for this asset exists and if it has a MountainModelDirectory specified
                        if (kvp.Value != null && (meta = kvp.Value.GetMeta <MapMeta>()) != null && meta.Mountain != null && !string.IsNullOrEmpty(meta.Mountain.MountainModelDirectory))
                        {
                            // Create the mountain resources for this map if they don't exist already
                            if (!MountainMappings.TryGetValue(kvp.Key, out MountainResources resources))
                            {
                                resources = new MountainResources();
                                MountainMappings.Add(kvp.Key, resources);
                            }

                            if (resolveModel(meta, "mountain", out ModAsset mountain, out string mountainPath))
                            {
                                resources.MountainTerrain = loadModelFile(mountain, mountainPath);
                            }

                            if (resolveModel(meta, "buildings", out ModAsset buildings, out string buildingsPath))
                            {
                                resources.MountainBuildings = loadModelFile(buildings, buildingsPath);
                            }

                            if (resolveModel(meta, "mountain_wall", out ModAsset coreWall, out string coreWallPath))
                            {
                                resources.MountainCoreWall = loadModelFile(coreWall, coreWallPath);
                            }

                            if (resolveModel(meta, "bird", out ModAsset bird, out string birdPath))
                            {
                                resources.MountainBird = loadModelFile(bird, birdPath);
                            }

                            if (resolveModel(meta, "moon", out ModAsset moon, out string moonPath))
                            {
                                resources.MountainMoon = loadModelFile(moon, moonPath);
                            }

                            resources.MountainExtraModels.Clear();
                            resources.MountainExtraModelTextures.Clear();

                            while (resolveModel(meta, "extra" + resources.MountainExtraModels.Count, out ModAsset extra, out string extraPath))
                            {
                                // load the extra model.
                                int extraIndex = resources.MountainExtraModels.Count;
                                resources.MountainExtraModels.Add(loadModelFile(extra, extraPath));

                                // load the textures associated with the extra model.
                                VirtualTexture[] textures = new VirtualTexture[4];
                                for (int i = 0; i < 4; i++)
                                {
                                    if (MTN.Mountain.Has(Path.Combine(meta.Mountain.MountainTextureDirectory, "extra" + extraIndex + "_" + i).Replace('\\', '/')))
                                    {
                                        textures[i] = MTN.Mountain[Path.Combine(meta.Mountain.MountainTextureDirectory, "extra" + extraIndex + "_" + i).Replace('\\', '/')].Texture;
                                    }
                                }
                                resources.MountainExtraModelTextures.Add(textures);
                            }
                        }
                    }
                }
                Console.WriteLine(" - MODDED MTN DATA LOAD: " + stopwatch.ElapsedMilliseconds + "ms");
            }

            ModsDataLoaded = true;
        }