public UUID SaveBitmap(Bitmap data, bool lossless, bool temporary) { AssetBase asset = new AssetBase(UUID.Random(), "MRMDynamicImage", AssetType.Texture, m_scene.RegionInfo.RegionID) { Data = OpenJPEG.EncodeFromImage(data, lossless), Description = "MRM Image", Flags = (temporary) ? AssetFlags.Temperary : 0 }; asset.FillHash(); asset.ID = m_scene.AssetService.Store(asset); return asset.ID; }
/// <summary> /// Update what the avatar is wearing using an item from their inventory. /// </summary> /// <param name = "client"></param> /// <param name = "e"></param> public void AvatarIsWearing(IClientAPI client, AvatarWearingArgs e) { IScenePresence sp = m_scene.GetScenePresence(client.AgentId); if (sp == null) { MainConsole.Instance.WarnFormat("[AvatarFactory]: AvatarIsWearing unable to find presence for {0}", client.AgentId); return; } MainConsole.Instance.DebugFormat("[AvatarFactory]: AvatarIsWearing called for {0}", client.AgentId); // operate on a copy of the appearance so we don't have to lock anything IAvatarAppearanceModule appearance = sp.RequestModuleInterface<IAvatarAppearanceModule>(); AvatarAppearance avatAppearance = new AvatarAppearance(appearance.Appearance, false); IOpenRegionSettingsModule module = m_scene.RequestModuleInterface<IOpenRegionSettingsModule>(); bool NeedsRebake = false; if (module != null && module.EnableTeenMode) { foreach (AvatarWearingArgs.Wearable wear in e.NowWearing) { if (wear.Type == 10 & wear.ItemID == UUID.Zero && module.DefaultUnderpants != UUID.Zero) { NeedsRebake = true; wear.ItemID = module.DefaultUnderpants; InventoryItemBase item = new InventoryItemBase(UUID.Random()) { InvType = (int) InventoryType.Wearable, AssetType = (int) AssetType.Clothing, Name = "Default Underpants", Folder = m_scene.InventoryService.GetFolderForType(client.AgentId, InventoryType. Wearable, AssetType. Clothing).ID, Owner = client.AgentId, CurrentPermissions = 0, CreatorId = UUID.Zero.ToString(), AssetID = module.DefaultUnderpants }; //Locked client.SendInventoryItemCreateUpdate(item, 0); } else if (wear.Type == 10 & wear.ItemID == UUID.Zero) { NeedsRebake = true; InventoryItemBase item = new InventoryItemBase(UUID.Random()) { InvType = (int) InventoryType.Wearable, AssetType = (int) AssetType.Clothing, Name = "Default Underpants", Folder = m_scene.InventoryService.GetFolderForType(client.AgentId, InventoryType. Wearable, AssetType. Clothing).ID, Owner = client.AgentId, CurrentPermissions = 0 }; //Locked if (m_underPantsUUID == UUID.Zero) { m_underPantsUUID = UUID.Random(); AssetBase asset = new AssetBase(m_underPantsUUID, "Default Underpants", AssetType.Clothing, UUID.Zero) {Data = Utils.StringToBytes(m_defaultUnderPants)}; asset.FillHash(); asset.ID = m_scene.AssetService.Store(asset); m_underPantsUUID = asset.ID; } item.CreatorId = UUID.Zero.ToString(); item.AssetID = m_underPantsUUID; m_scene.InventoryService.AddItem(item); client.SendInventoryItemCreateUpdate(item, 0); wear.ItemID = item.ID; } if (wear.Type == 11 && wear.ItemID == UUID.Zero && module.DefaultUndershirt != UUID.Zero) { NeedsRebake = true; wear.ItemID = module.DefaultUndershirt; InventoryItemBase item = new InventoryItemBase(UUID.Random()) { InvType = (int) InventoryType.Wearable, AssetType = (int) AssetType.Clothing, Name = "Default Undershirt", Folder = m_scene.InventoryService.GetFolderForType(client.AgentId, InventoryType. Wearable, AssetType. Clothing).ID, Owner = client.AgentId, CurrentPermissions = 0, CreatorId = UUID.Zero.ToString(), AssetID = module.DefaultUndershirt }; //Locked client.SendInventoryItemCreateUpdate(item, 0); } else if (wear.Type == 11 & wear.ItemID == UUID.Zero) { NeedsRebake = true; InventoryItemBase item = new InventoryItemBase(UUID.Random()) { InvType = (int) InventoryType.Wearable, AssetType = (int) AssetType.Clothing, Name = "Default Undershirt", Folder = m_scene.InventoryService.GetFolderForType(client.AgentId, InventoryType. Wearable, AssetType. Clothing).ID, Owner = client.AgentId, CurrentPermissions = 0 }; //Locked if (m_underShirtUUID == UUID.Zero) { m_underShirtUUID = UUID.Random(); AssetBase asset = new AssetBase(m_underShirtUUID, "Default Undershirt", AssetType.Clothing, UUID.Zero) {Data = Utils.StringToBytes(m_defaultUnderShirt)}; asset.FillHash(); asset.ID = m_scene.AssetService.Store(asset); m_underShirtUUID = asset.ID; } item.CreatorId = UUID.Zero.ToString(); item.AssetID = m_underShirtUUID; m_scene.InventoryService.AddItem(item); client.SendInventoryItemCreateUpdate(item, 0); wear.ItemID = item.ID; } } } #if (!ISWIN) foreach (AvatarWearingArgs.Wearable wear in e.NowWearing) { if (wear.Type < AvatarWearable.MAX_WEARABLES) { /*if (incomingLinks.ContainsKey (wear.ItemID)) { wear.ItemID = incomingLinks[wear.ItemID]; }*/ avatAppearance.Wearables[wear.Type].Add(wear.ItemID, UUID.Zero); } } #else foreach (AvatarWearingArgs.Wearable wear in e.NowWearing.Where(wear => wear.Type < AvatarWearable.MAX_WEARABLES)) { avatAppearance.Wearables[wear.Type].Add(wear.ItemID, UUID.Zero); } #endif avatAppearance.GetAssetsFrom(appearance.Appearance); // This could take awhile since it needs to pull inventory SetAppearanceAssets(sp.UUID, e.NowWearing, appearance.Appearance, ref avatAppearance); // could get fancier with the locks here, but in the spirit of "last write wins" // this should work correctly, also, we don't need to send the appearance here // since the "iswearing" will trigger a new set of visual param and baked texture changes // when those complete, the new appearance will be sent appearance.Appearance = avatAppearance; if (NeedsRebake) { //Tell the client about the new things it is wearing sp.ControllingClient.SendWearables(appearance.Appearance.Wearables, appearance.Appearance.Serial); //Then forcefully tell it to rebake #if (!ISWIN) foreach (Primitive.TextureEntryFace t in appearance.Appearance.Texture.FaceTextures) { Primitive.TextureEntryFace face = (t); if (face != null) { sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID); } } #else foreach (Primitive.TextureEntryFace face in appearance.Appearance.Texture.FaceTextures.Select(t => (t)).Where(face => face != null)) { sp.ControllingClient.SendRebakeAvatarTextures(face.TextureID); } #endif } QueueAppearanceSave(sp.UUID); //Send the wearables HERE so that the client knows what it is wearing //sp.ControllingClient.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial); //Do not save or send the appearance! The client loops back and sends a bunch of SetAppearance // (handled above) and that takes care of it }
// This needs ThreatLevel high. It is an excellent griefer tool, // In a loop, it can cause asset bloat and DOS levels of asset // writes. // public void osMakeNotecard(string notecardName, LSL_List contents) { if (!ScriptProtection.CheckThreatLevel(ThreatLevel.High, "osMakeNotecard", m_host, "OSSL", m_itemID)) return; // Create new asset AssetBase asset = new AssetBase(UUID.Random(), notecardName, AssetType.Notecard, m_host.OwnerID) { Description = "Script Generated Notecard" }; string notecardData = String.Empty; for (int i = 0; i < contents.Length; i++) { notecardData += contents.GetLSLStringItem(i) + "\n"; } int textLength = notecardData.Length; notecardData = "Linden text version 2\n{\nLLEmbeddedItems version 1\n{\ncount 0\n}\nText length " + textLength.ToString(CultureInfo.InvariantCulture) + "\n" + notecardData + "}\n"; asset.Data = Util.UTF8.GetBytes(notecardData); asset.FillHash(); asset.ID = World.AssetService.Store(asset); // Create Task Entry TaskInventoryItem taskItem = new TaskInventoryItem(); taskItem.ResetIDs(m_host.UUID); taskItem.ParentID = m_host.UUID; taskItem.CreationDate = (uint)Util.UnixTimeSinceEpoch(); taskItem.Name = asset.Name; taskItem.Description = asset.Description; taskItem.Type = (int)AssetType.Notecard; taskItem.InvType = (int)InventoryType.Notecard; taskItem.OwnerID = m_host.OwnerID; taskItem.CreatorID = m_host.OwnerID; taskItem.BasePermissions = (uint)PermissionMask.All; taskItem.CurrentPermissions = (uint)PermissionMask.All; taskItem.EveryonePermissions = 0; taskItem.NextPermissions = (uint)PermissionMask.All; taskItem.GroupID = m_host.GroupID; taskItem.GroupPermissions = 0; taskItem.Flags = 0; taskItem.SalePrice = 0; taskItem.SaleType = 0; taskItem.PermsGranter = UUID.Zero; taskItem.PermsMask = 0; taskItem.AssetID = asset.ID; m_host.Inventory.AddInventoryItem(taskItem, false); }
/// <summary> /// This method is called if a given model avatar name can not be found. If the external /// file has already been loaded once, then control returns immediately. If not, then it /// looks for a default appearance file. This file contains XML definitions of zero or more named /// avatars, each avatar can specify zero or more "outfits". Each outfit is a collection /// of items that together, define a particular ensemble for the avatar. Each avatar should /// indicate which outfit is the default, and this outfit will be automatically worn. The /// other outfits are provided to allow "real" avatars a way to easily change their outfits. /// </summary> private bool CreateDefaultAvatars() { // Only load once if (m_defaultAvatarsLoaded) { return false; } MainConsole.Instance.DebugFormat("[RADMIN] Creating default avatar entries"); m_defaultAvatarsLoaded = true; // Load processing starts here... try { string defaultAppearanceFileName = null; //m_config may be null if RemoteAdmin configuration secition is missing or disabled in Aurora.ini if (m_config != null) { defaultAppearanceFileName = m_config.GetString("default_appearance", "default_appearance.xml"); } if (File.Exists(defaultAppearanceFileName)) { XmlDocument doc = new XmlDocument(); string name = "*unknown*"; string email = "anon@anon"; uint regionXLocation = 1000; uint regionYLocation = 1000; string password = UUID.Random().ToString(); // No requirement to sign-in. UUID ID = UUID.Zero; XmlNode perms = null; IScene scene = manager.CurrentOrFirstScene; IInventoryService inventoryService = scene.InventoryService; IAssetService assetService = scene.AssetService; doc.LoadXml(File.ReadAllText(defaultAppearanceFileName)); // Load up any included assets. Duplicates will be ignored XmlNodeList assets = doc.GetElementsByTagName("RequiredAsset"); foreach (XmlNode assetNode in assets) { AssetBase asset = new AssetBase(UUID.Random(), GetStringAttribute(assetNode, "name", ""), (AssetType) SByte.Parse(GetStringAttribute(assetNode, "type", "")), UUID.Zero) { Description = GetStringAttribute(assetNode, "desc", ""), Data = Convert.FromBase64String(assetNode.InnerText), Flags = ((Boolean.Parse(GetStringAttribute(assetNode, "local", ""))) ? AssetFlags.Local : AssetFlags.Normal) | ((Boolean.Parse(GetStringAttribute(assetNode, "temporary", ""))) ? AssetFlags.Temperary : AssetFlags.Normal) }; asset.FillHash(); asset.ID = assetService.Store(asset); } XmlNodeList avatars = doc.GetElementsByTagName("Avatar"); // The document may contain multiple avatars foreach (XmlElement avatar in avatars) { MainConsole.Instance.DebugFormat("[RADMIN] Loading appearance for {0}, gender = {1}", GetStringAttribute(avatar,"name","?"), GetStringAttribute(avatar,"gender","?")); // Create the user identified by the avatar entry bool include = false; try { // Only the name value is mandatory name = GetStringAttribute(avatar,"name",name); email = GetStringAttribute(avatar,"email",email); regionXLocation = GetUnsignedAttribute(avatar,"regx",regionXLocation); regionYLocation = GetUnsignedAttribute(avatar,"regy",regionYLocation); password = GetStringAttribute(avatar,"password",password); string[] names = name.Split(); UUID scopeID = scene.RegionInfo.ScopeID; UserAccount account = scene.UserAccountService.GetUserAccount(scopeID, names[0], names[1]); if (null == account) { account = CreateUser(scopeID, names[0], names[1], password, email); if (null == account) { MainConsole.Instance.ErrorFormat("[RADMIN] Avatar {0} {1} was not created", names[0], names[1]); return false; } } // Set home position GridRegion home = scene.GridService.GetRegionByPosition(scopeID, (int)(regionXLocation * Constants.RegionSize), (int)(regionYLocation * Constants.RegionSize)); if (null == home) { MainConsole.Instance.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", names[0], names[1]); } else { IAgentInfoService agentInfoService = scene.RequestModuleInterface<IAgentInfoService>(); agentInfoService.SetHomePosition(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); MainConsole.Instance.DebugFormat("[RADMIN]: Set home region {0} for updated user account {1} {2}", home.RegionID, names[0], names[1]); } ID = account.PrincipalID; MainConsole.Instance.DebugFormat("[RADMIN] User {0}[{1}] created or retrieved", name, ID); include = true; } catch (Exception e) { MainConsole.Instance.DebugFormat("[RADMIN] Error creating user {0} : {1}", name, e.Message); include = false; } // OK, User has been created OK, now we can install the inventory. // First retrieve the current inventory (the user may already exist) // Note that althought he inventory is retrieved, the hierarchy has // not been interpreted at all. if (include) { // Setup for appearance processing AvatarData avatarData = scene.AvatarService.GetAvatar(ID); AvatarAppearance avatarAppearance = avatarData != null ? avatarData.ToAvatarAppearance(ID) : new AvatarAppearance(); AvatarWearable[] wearables = avatarAppearance.Wearables; for (int i=0; i<wearables.Length; i++) { wearables[i] = new AvatarWearable(); } try { // MainConsole.Instance.DebugFormat("[RADMIN] {0} folders, {1} items in inventory", // uic.folders.Count, uic.items.Count); InventoryFolderBase clothingFolder = inventoryService.GetFolderForType (ID, InventoryType.Wearable, AssetType.Clothing); // This should *never* be the case if (clothingFolder == null || clothingFolder.Type != (short)AssetType.Clothing) { clothingFolder = new InventoryFolderBase { ID = UUID.Random(), Name = "Clothing", Owner = ID, Type = (short) AssetType.Clothing, ParentID = inventoryService.GetRootFolder(ID).ID, Version = 1 }; inventoryService.AddFolder(clothingFolder); // store base record MainConsole.Instance.ErrorFormat("[RADMIN] Created clothing folder for {0}/{1}", name, ID); } // OK, now we have an inventory for the user, read in the outfits from the // default appearance XMl file. XmlNodeList outfits = avatar.GetElementsByTagName("Ensemble"); foreach (XmlElement outfit in outfits) { MainConsole.Instance.DebugFormat("[RADMIN] Loading outfit {0} for {1}", GetStringAttribute(outfit,"name","?"), GetStringAttribute(avatar,"name","?")); string outfitName = GetStringAttribute(outfit,"name",""); bool select = (GetStringAttribute(outfit,"default","no") == "yes"); // If the folder already exists, re-use it. The defaults may // change over time. Augment only. List<InventoryFolderBase> folders = inventoryService.GetFolderContent(ID, clothingFolder.ID).Folders; #if (!ISWIN) InventoryFolderBase extraFolder = null; foreach (InventoryFolderBase folder in folders) { if (folder.Name == outfitName) { extraFolder = folder; break; } } #else InventoryFolderBase extraFolder = folders.FirstOrDefault(folder => folder.Name == outfitName); #endif // Otherwise, we must create the folder. if (extraFolder == null) { MainConsole.Instance.DebugFormat("[RADMIN] Creating outfit folder {0} for {1}", outfitName, name); extraFolder = new InventoryFolderBase { ID = UUID.Random(), Name = outfitName, Owner = ID, Type = (short) AssetType.Clothing, Version = 1, ParentID = clothingFolder.ID }; inventoryService.AddFolder(extraFolder); MainConsole.Instance.DebugFormat("[RADMIN] Adding outfile folder {0} to folder {1}", extraFolder.ID, clothingFolder.ID); } // Now get the pieces that make up the outfit XmlNodeList items = outfit.GetElementsByTagName("Item"); foreach (XmlElement item in items) { UUID assetid = UUID.Zero; XmlNodeList children = item.ChildNodes; foreach (XmlNode child in children) { switch (child.Name) { case "Permissions" : MainConsole.Instance.DebugFormat("[RADMIN] Permissions specified"); perms = child; break; case "Asset" : assetid = new UUID(child.InnerText); break; } } InventoryItemBase inventoryItem = null; // Check if asset is in inventory already List<InventoryItemBase> inventoryItems = inventoryService.GetFolderContent(ID, extraFolder.ID).Items; #if (!ISWIN) inventoryItem = null; foreach (InventoryItemBase listItem in inventoryItems) { if (listItem.AssetID == assetid) { inventoryItem = listItem; break; } } #else inventoryItem = inventoryItems.FirstOrDefault(listItem => listItem.AssetID == assetid); #endif // Create inventory item if (inventoryItem == null) { inventoryItem = new InventoryItemBase(UUID.Random(), ID) { Name = GetStringAttribute(item, "name", ""), Description = GetStringAttribute(item, "desc", ""), InvType = GetIntegerAttribute(item, "invtype", -1), CreatorId = GetStringAttribute(item, "creatorid", ""), CreatorIdAsUuid = (UUID) GetStringAttribute(item, "creatoruuid", ""), NextPermissions = GetUnsignedAttribute(perms, "next", 0x7fffffff), CurrentPermissions = GetUnsignedAttribute(perms, "current", 0x7fffffff), BasePermissions = GetUnsignedAttribute(perms, "base", 0x7fffffff), EveryOnePermissions = GetUnsignedAttribute(perms, "everyone", 0x7fffffff), GroupPermissions = GetUnsignedAttribute(perms, "group", 0x7fffffff), AssetType = GetIntegerAttribute(item, "assettype", -1), AssetID = assetid, GroupID = (UUID) GetStringAttribute(item, "groupid", ""), GroupOwned = (GetStringAttribute(item, "groupowned", "false") == "true"), SalePrice = GetIntegerAttribute(item, "saleprice", 0), SaleType = (byte) GetIntegerAttribute(item, "saletype", 0), Flags = GetUnsignedAttribute(item, "flags", 0), CreationDate = GetIntegerAttribute(item, "creationdate", Util.UnixTimeSinceEpoch()), Folder = extraFolder.ID }; // associated asset // Parent folder ILLClientInventory inventoryModule = manager.CurrentOrFirstScene.RequestModuleInterface<ILLClientInventory>(); if (inventoryModule != null) inventoryModule.AddInventoryItem(inventoryItem); MainConsole.Instance.DebugFormat("[RADMIN] Added item {0} to folder {1}", inventoryItem.ID, extraFolder.ID); } // Attach item, if attachpoint is specified int attachpoint = GetIntegerAttribute(item,"attachpoint",0); if (attachpoint != 0) { avatarAppearance.SetAttachment(attachpoint, inventoryItem.ID, inventoryItem.AssetID); MainConsole.Instance.DebugFormat("[RADMIN] Attached {0}", inventoryItem.ID); } // Record whether or not the item is to be initially worn try { if (select && (GetStringAttribute(item, "wear", "false") == "true")) { avatarAppearance.Wearables[inventoryItem.Flags].Wear(inventoryItem.ID, inventoryItem.AssetID); } } catch (Exception e) { MainConsole.Instance.WarnFormat("[RADMIN] Error wearing item {0} : {1}", inventoryItem.ID, e.Message); } } // foreach item in outfit MainConsole.Instance.DebugFormat("[RADMIN] Outfit {0} load completed", outfitName); } // foreach outfit MainConsole.Instance.DebugFormat("[RADMIN] Inventory update complete for {0}", name); AvatarData avatarData2 = new AvatarData(avatarAppearance); scene.AvatarService.SetAvatar(ID, avatarData2); } catch (Exception e) { MainConsole.Instance.WarnFormat("[RADMIN] Inventory processing incomplete for user {0} : {1}", name, e.Message); } } // End of include } MainConsole.Instance.DebugFormat("[RADMIN] Default avatar loading complete"); } else { MainConsole.Instance.DebugFormat("[RADMIN] No default avatar information available"); return false; } } catch (Exception e) { MainConsole.Instance.WarnFormat("[RADMIN] Exception whilst loading default avatars ; {0}", e.Message); return false; } return true; }
/// <summary> /// Builds a composited terrain texture given the region texture /// and heightmap settings /// </summary> /// <param name = "heightmap">Terrain heightmap</param> /// <param name = "regionInfo">Region information including terrain texture parameters</param> /// <returns>A composited 256x256 RGB texture ready for rendering</returns> /// <remarks> /// Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting /// </remarks> public static Bitmap Splat(ITerrainChannel heightmap, UUID[] textureIDs, float[] startHeights, float[] heightRanges, Vector3d regionPosition, IAssetService assetService, bool textureTerrain) { Debug.Assert(textureIDs.Length == 4); Debug.Assert(startHeights.Length == 4); Debug.Assert(heightRanges.Length == 4); Bitmap[] detailTexture = new Bitmap[4]; if (textureTerrain) { // Swap empty terrain textureIDs with default IDs for (int i = 0; i < textureIDs.Length; i++) { if (textureIDs[i] == UUID.Zero) textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; } #region Texture Fetching if (assetService != null) { for (int i = 0; i < 4; i++) { UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); AssetBase asset = assetService.Get(cacheID.ToString()); if ((asset != null) && (asset.Data != null) && (asset.Data.Length != 0)) { try { using (MemoryStream stream = new MemoryStream(asset.Data)) detailTexture[i] = (Bitmap) Image.FromStream(stream); } catch (Exception ex) { MainConsole.Instance.Warn("Failed to decode cached terrain texture " + cacheID + " (textureID: " + textureIDs[i] + "): " + ex.Message); } } if (detailTexture[i] == null) { // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG asset = assetService.Get(textureIDs[i].ToString()); if (asset != null) { try { detailTexture[i] = (Bitmap) J2kImage.FromBytes(asset.Data); } catch (Exception ex) { MainConsole.Instance.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message); } } if (detailTexture[i] != null) { Bitmap bitmap = detailTexture[i]; // Make sure this texture is the correct size, otherwise resize if (bitmap.Width != 256 || bitmap.Height != 256) bitmap = ImageUtils.ResizeImage(bitmap, 256, 256); // Save the decoded and resized texture to the cache byte[] data; using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); data = stream.ToArray(); } // Cache a PNG copy of this terrain texture AssetBase newAsset = new AssetBase { Data = data, Description = "PNG", Flags = AssetFlags.Collectable | AssetFlags.Temporary | AssetFlags.Local, ID = cacheID, Name = String.Empty, TypeString = "image/png" }; newAsset.FillHash(); newAsset.ID = assetService.Store(newAsset); } } } } #endregion Texture Fetching } // Fill in any missing textures with a solid color for (int i = 0; i < 4; i++) { if (detailTexture[i] == null) { // Create a solid color texture for this layer detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb); using (Graphics gfx = Graphics.FromImage(detailTexture[i])) { using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i])) gfx.FillRectangle(brush, 0, 0, 256, 256); } } else if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256) { detailTexture[i] = ResizeBitmap(detailTexture[i], 256, 256); } } #region Layer Map float diff = (float)heightmap.Height / (float)Constants.RegionSize; float[] layermap = new float[Constants.RegionSize*Constants.RegionSize]; for (float y = 0; y < heightmap.Height; y += diff) { for (float x = 0; x < heightmap.Height; x += diff) { float newX = x / diff; float newY = y / diff; float height = heightmap[(int)newX, (int)newY]; float pctX = newX/255f; float pctY = newY/255f; // Use bilinear interpolation between the four corners of start height and // height range to select the current values at this position float startHeight = ImageUtils.Bilinear( startHeights[0], startHeights[2], startHeights[1], startHeights[3], pctX, pctY); startHeight = Utils.Clamp(startHeight, 0f, 255f); float heightRange = ImageUtils.Bilinear( heightRanges[0], heightRanges[2], heightRanges[1], heightRanges[3], pctX, pctY); heightRange = Utils.Clamp(heightRange, 0f, 255f); // Generate two frequencies of perlin noise based on our global position // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting Vector3 vec = new Vector3 ( ((float) regionPosition.X + newX)*0.20319f, ((float) regionPosition.Y + newY)*0.20319f, height*0.25f ); float lowFreq = Perlin.noise2(vec.X*0.222222f, vec.Y*0.222222f)*6.5f; float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f)*2.25f; float noise = (lowFreq + highFreq)*2f; // Combine the current height, generated noise, start height, and height range parameters, then scale all of it float layer = ((height + noise - startHeight)/heightRange)*4f; if (Single.IsNaN(layer)) layer = 0f; layermap[(int)(newY * Constants.RegionSize + newX)] = Utils.Clamp(layer, 0f, 3f); } } #endregion Layer Map #region Texture Compositing Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb); BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); unsafe { // Get handles to all of the texture data arrays BitmapData[] datas = new[] { detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat), detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat), detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat), detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat) }; int[] comps = new[] { (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3 }; for (int y = 0; y < Constants.RegionSize; y++) { for (int x = 0; x < Constants.RegionSize; x++) { float layer = layermap[y*Constants.RegionSize + x]; // Select two textures int l0 = (int) Math.Floor(layer); int l1 = Math.Min(l0 + 1, 3); byte* ptrA = (byte*) datas[l0].Scan0 + y*datas[l0].Stride + x*comps[l0]; byte* ptrB = (byte*) datas[l1].Scan0 + y*datas[l1].Stride + x*comps[l1]; byte* ptrO = (byte*) outputData.Scan0 + y*outputData.Stride + x*3; float aB = *(ptrA + 0); float aG = *(ptrA + 1); float aR = *(ptrA + 2); float bB = *(ptrB + 0); float bG = *(ptrB + 1); float bR = *(ptrB + 2); float layerDiff = layer - l0; // Interpolate between the two selected textures *(ptrO + 0) = (byte) Math.Floor(aB + layerDiff*(bB - aB)); *(ptrO + 1) = (byte) Math.Floor(aG + layerDiff*(bG - aG)); *(ptrO + 2) = (byte) Math.Floor(aR + layerDiff*(bR - aR)); } } for (int i = 0; i < 4; i++) { detailTexture[i].UnlockBits(datas[i]); detailTexture[i].Dispose(); } } layermap = null; output.UnlockBits(outputData); // We generated the texture upside down, so flip it output.RotateFlip(RotateFlipType.RotateNoneFlipY); #endregion Texture Compositing return output; }