/// <summary> /// Adds layer for baking /// </summary> /// <param name="tdata">TexturaData struct that contains texture and its params</param> public void AddTexture(AppearanceManager.TextureData tdata) { lock (textures) { textures.Add(tdata); } }
public void Bake() { bakedTexture = new AssetTexture(new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump)); // These are for head baking, they get special treatment AppearanceManager.TextureData skinTexture = new AppearanceManager.TextureData(); List <AppearanceManager.TextureData> tattooTextures = new List <AppearanceManager.TextureData>(); List <ManagedImage> alphaWearableTextures = new List <ManagedImage>(); // Base color for eye bake is white, color of layer0 for others if (bakeType == BakeType.Eyes) { InitBakedLayerColor(Color4.White); } else if (textures.Count > 0) { InitBakedLayerColor(textures[0].Color); } // Sort out the special layers we need for head baking and alpha foreach (AppearanceManager.TextureData tex in textures) { if (tex.Texture == null) { continue; } if (tex.TextureIndex == AvatarTextureIndex.HeadBodypaint || tex.TextureIndex == AvatarTextureIndex.UpperBodypaint || tex.TextureIndex == AvatarTextureIndex.LowerBodypaint) { skinTexture = tex; } if (tex.TextureIndex == AvatarTextureIndex.HeadTattoo || tex.TextureIndex == AvatarTextureIndex.UpperTattoo || tex.TextureIndex == AvatarTextureIndex.LowerTattoo) { tattooTextures.Add(tex); } if (tex.TextureIndex >= AvatarTextureIndex.LowerAlpha && tex.TextureIndex <= AvatarTextureIndex.HairAlpha) { if (tex.Texture.Image.Alpha != null) { alphaWearableTextures.Add(tex.Texture.Image.Clone()); } } } if (bakeType == BakeType.Head) { DrawLayer(LoadResourceLayer("head_color.tga"), false); AddAlpha(bakedTexture.Image, LoadResourceLayer("head_alpha.tga")); MultiplyLayerFromAlpha(bakedTexture.Image, LoadResourceLayer("head_skingrain.tga")); } if (skinTexture.Texture == null) { if (bakeType == BakeType.UpperBody) { DrawLayer(LoadResourceLayer("upperbody_color.tga"), false); } if (bakeType == BakeType.LowerBody) { DrawLayer(LoadResourceLayer("lowerbody_color.tga"), false); } } // Layer each texture on top of one other, applying alpha masks as we go for (int i = 0; i < textures.Count; i++) { // Skip if we have no texture on this layer if (textures[i].Texture == null) { continue; } // Is this Alpha wearable and does it have an alpha channel? if (textures[i].TextureIndex >= AvatarTextureIndex.LowerAlpha && textures[i].TextureIndex <= AvatarTextureIndex.HairAlpha) { continue; } // Don't draw skin and tattoo on head bake first // For head bake the skin and texture are drawn last, go figure if (bakeType == BakeType.Head && (textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || textures[i].TextureIndex == AvatarTextureIndex.HeadTattoo)) { continue; } ManagedImage texture = textures[i].Texture.Image.Clone(); //File.WriteAllBytes(bakeType + "-texture-layer-" + textures[i].TextureIndex + "-" + i + ".tga", texture.ExportTGA()); // Resize texture to the size of baked layer // FIXME: if texture is smaller than the layer, don't stretch it, tile it if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { continue; } } // Special case for hair layer for the head bake // If we don't have skin texture, we discard hair alpha // and apply hair(i==2) pattern over the texture if (skinTexture.Texture == null && bakeType == BakeType.Head && textures[i].TextureIndex == AvatarTextureIndex.Hair) { if (texture.Alpha != null) { for (int j = 0; j < texture.Alpha.Length; j++) { texture.Alpha[j] = (byte)255; } } MultiplyLayerFromAlpha(texture, LoadResourceLayer("head_hair.tga")); } bool processingSkin = true; // Aply tint and alpha masks except for skin that has a texture // on layer 0 which always overrides other skin settings if (!(textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || textures[i].TextureIndex == AvatarTextureIndex.UpperBodypaint || textures[i].TextureIndex == AvatarTextureIndex.LowerBodypaint)) { processingSkin = false; ApplyTint(texture, textures[i].Color); // For hair bake, we skip all alpha masks // and use one from the texture, for both // alpha and morph layers if (bakeType == BakeType.Hair) { if (texture.Alpha != null) { bakedTexture.Image.Bump = texture.Alpha; } else { for (int j = 0; j < bakedTexture.Image.Bump.Length; j++) { bakedTexture.Image.Bump[j] = byte.MaxValue; } } } // Apply parametrized alpha masks else if (textures[i].AlphaMasks != null && textures[i].AlphaMasks.Count > 0) { // Combined mask for the layer, fully transparent to begin with ManagedImage combinedMask = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha); int addedMasks = 0; // First add mask in normal blend mode foreach (KeyValuePair <VisualAlphaParam, float> kvp in textures[i].AlphaMasks) { if (!MaskBelongsToBake(kvp.Key.TGAFile)) { continue; } if (kvp.Key.MultiplyBlend == false && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) { ApplyAlpha(combinedMask, kvp.Key, kvp.Value); //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); addedMasks++; } } // If there were no mask in normal blend mode make aplha fully opaque if (addedMasks == 0) { for (int l = 0; l < combinedMask.Alpha.Length; l++) { combinedMask.Alpha[l] = 255; } } // Add masks in multiply blend mode foreach (KeyValuePair <VisualAlphaParam, float> kvp in textures[i].AlphaMasks) { if (!MaskBelongsToBake(kvp.Key.TGAFile)) { continue; } if (kvp.Key.MultiplyBlend == true && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) { ApplyAlpha(combinedMask, kvp.Key, kvp.Value); //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); addedMasks++; } } if (addedMasks > 0) { // Apply combined alpha mask to the cloned texture AddAlpha(texture, combinedMask); } // Is this layer used for morph mask? If it is, use its // alpha as the morth for the whole bake if (Textures[i].TextureIndex == AppearanceManager.MorphLayerForBakeType(bakeType)) { bakedTexture.Image.Bump = texture.Alpha; } //File.WriteAllBytes(bakeType + "-masked-texture-" + i + ".tga", texture.ExportTGA()); } } bool useAlpha = i == 0 && (BakeType == BakeType.Skirt || BakeType == BakeType.Hair); DrawLayer(texture, useAlpha); //File.WriteAllBytes(bakeType + "-layer-" + i + ".tga", texture.ExportTGA()); } // For head and tattoo, we add skin last if (bakeType == BakeType.Head) { if (skinTexture.Texture != null) { ManagedImage texture = skinTexture.Texture.Image.Clone(); if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { } } DrawLayer(texture, false); } foreach (AppearanceManager.TextureData tex in tattooTextures) { // Add head tattoo here (if available, order-dependant) if (tex.Texture != null) { ManagedImage texture = tex.Texture.Image.Clone(); if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { } } DrawLayer(texture, false); } } } // Apply any alpha wearable textures to make parts of the avatar disappear m_log.DebugFormat("[XBakes]: Number of alpha wearable textures: {0}", alphaWearableTextures.Count); foreach (ManagedImage img in alphaWearableTextures) { AddAlpha(bakedTexture.Image, img); } // We are done, encode asset for finalized bake bakedTexture.Encode(); //File.WriteAllBytes(bakeType + ".tga", bakedTexture.Image.ExportTGA()); }
public void Bake() { bakedTexture = new AssetTexture(new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump)); // These are for head baking, they get special treatment AppearanceManager.TextureData skinTexture = new AppearanceManager.TextureData(); List<AppearanceManager.TextureData> tattooTextures = new List<AppearanceManager.TextureData>(); List<ManagedImage> alphaWearableTextures = new List<ManagedImage>(); // Base color for eye bake is white, color of layer0 for others if (bakeType == BakeType.Eyes) { InitBakedLayerColor(Color4.White); } else if (textures.Count > 0) { InitBakedLayerColor(textures[0].Color); } // Sort out the special layers we need for head baking and alpha foreach (AppearanceManager.TextureData tex in textures) { if (tex.Texture == null) continue; if (tex.TextureIndex == AvatarTextureIndex.HeadBodypaint || tex.TextureIndex == AvatarTextureIndex.UpperBodypaint || tex.TextureIndex == AvatarTextureIndex.LowerBodypaint) skinTexture = tex; if (tex.TextureIndex == AvatarTextureIndex.HeadTattoo || tex.TextureIndex == AvatarTextureIndex.UpperTattoo || tex.TextureIndex == AvatarTextureIndex.LowerTattoo) tattooTextures.Add(tex); if (tex.TextureIndex >= AvatarTextureIndex.LowerAlpha && tex.TextureIndex <= AvatarTextureIndex.HairAlpha) { if (tex.Texture.Image.Alpha != null) alphaWearableTextures.Add(tex.Texture.Image.Clone()); } } if (bakeType == BakeType.Head) { DrawLayer(LoadResourceLayer("head_color.tga"), false); AddAlpha(bakedTexture.Image, LoadResourceLayer("head_alpha.tga")); MultiplyLayerFromAlpha(bakedTexture.Image, LoadResourceLayer("head_skingrain.tga")); } if (skinTexture.Texture == null) { if (bakeType == BakeType.UpperBody) { DrawLayer(LoadResourceLayer("upperbody_color.tga"), false); } if (bakeType == BakeType.LowerBody) { DrawLayer(LoadResourceLayer("lowerbody_color.tga"), false); } } // Layer each texture on top of one other, applying alpha masks as we go for (int i = 0; i < textures.Count; i++) { // Skip if we have no texture on this layer if (textures[i].Texture == null) continue; // Is this Alpha wearable and does it have an alpha channel? if (textures[i].TextureIndex >= AvatarTextureIndex.LowerAlpha && textures[i].TextureIndex <= AvatarTextureIndex.HairAlpha) continue; // Don't draw skin and tattoo on head bake first // For head bake the skin and texture are drawn last, go figure if (bakeType == BakeType.Head && (textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || textures[i].TextureIndex == AvatarTextureIndex.HeadTattoo)) continue; ManagedImage texture = textures[i].Texture.Image.Clone(); //File.WriteAllBytes(bakeType + "-texture-layer-" + textures[i].TextureIndex + "-" + i + ".tga", texture.ExportTGA()); // Resize texture to the size of baked layer // FIXME: if texture is smaller than the layer, don't stretch it, tile it if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { continue; } } // Special case for hair layer for the head bake // If we don't have skin texture, we discard hair alpha // and apply hair(i==2) pattern over the texture if (skinTexture.Texture == null && bakeType == BakeType.Head && textures[i].TextureIndex == AvatarTextureIndex.Hair) { if (texture.Alpha != null) { for (int j = 0; j < texture.Alpha.Length; j++) texture.Alpha[j] = (byte)255; } MultiplyLayerFromAlpha(texture, LoadResourceLayer("head_hair.tga")); } bool processingSkin = true; // Aply tint and alpha masks except for skin that has a texture // on layer 0 which always overrides other skin settings if (!(textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || textures[i].TextureIndex == AvatarTextureIndex.UpperBodypaint || textures[i].TextureIndex == AvatarTextureIndex.LowerBodypaint)) { processingSkin = false; ApplyTint(texture, textures[i].Color); // For hair bake, we skip all alpha masks // and use one from the texture, for both // alpha and morph layers if (bakeType == BakeType.Hair) { if (texture.Alpha != null) { bakedTexture.Image.Bump = texture.Alpha; } else { for (int j = 0; j < bakedTexture.Image.Bump.Length; j++) bakedTexture.Image.Bump[j] = byte.MaxValue; } } // Apply parametrized alpha masks else if (textures[i].AlphaMasks != null && textures[i].AlphaMasks.Count > 0) { // Combined mask for the layer, fully transparent to begin with ManagedImage combinedMask = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha); int addedMasks = 0; // First add mask in normal blend mode foreach (KeyValuePair<VisualAlphaParam, float> kvp in textures[i].AlphaMasks) { if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; if (kvp.Key.MultiplyBlend == false && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) { ApplyAlpha(combinedMask, kvp.Key, kvp.Value); //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); addedMasks++; } } // If there were no mask in normal blend mode make aplha fully opaque if (addedMasks == 0) for (int l = 0; l < combinedMask.Alpha.Length; l++) combinedMask.Alpha[l] = 255; // Add masks in multiply blend mode foreach (KeyValuePair<VisualAlphaParam, float> kvp in textures[i].AlphaMasks) { if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; if (kvp.Key.MultiplyBlend == true && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) { ApplyAlpha(combinedMask, kvp.Key, kvp.Value); //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); addedMasks++; } } if (addedMasks > 0) { // Apply combined alpha mask to the cloned texture AddAlpha(texture, combinedMask); } // Is this layer used for morph mask? If it is, use its // alpha as the morth for the whole bake if (Textures[i].TextureIndex == AppearanceManager.MorphLayerForBakeType(bakeType)) { bakedTexture.Image.Bump = texture.Alpha; } //File.WriteAllBytes(bakeType + "-masked-texture-" + i + ".tga", texture.ExportTGA()); } } bool useAlpha = i == 0 && (BakeType == BakeType.Skirt || BakeType == BakeType.Hair); DrawLayer(texture, useAlpha); //File.WriteAllBytes(bakeType + "-layer-" + i + ".tga", texture.ExportTGA()); } // For head and tattoo, we add skin last if (bakeType == BakeType.Head) { if (skinTexture.Texture != null) { ManagedImage texture = skinTexture.Texture.Image.Clone(); if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { } } DrawLayer(texture, false); } foreach (AppearanceManager.TextureData tex in tattooTextures) { // Add head tattoo here (if available, order-dependant) if (tex.Texture != null) { ManagedImage texture = tex.Texture.Image.Clone(); if (texture.Width != bakeWidth || texture.Height != bakeHeight) { try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } catch (Exception) { } } DrawLayer(texture, false); } } } // Apply any alpha wearable textures to make parts of the avatar disappear m_log.DebugFormat("[XBakes]: Number of alpha wearable textures: {0}", alphaWearableTextures.Count); foreach (ManagedImage img in alphaWearableTextures) AddAlpha(bakedTexture.Image, img); // We are done, encode asset for finalized bake bakedTexture.Encode(); //File.WriteAllBytes(bakeType + ".tga", bakedTexture.Image.ExportTGA()); }
private void pic_MouseClick(object sender, MouseEventArgs e) { PictureBox control = (PictureBox)sender; OpenFileDialog dialog = new OpenFileDialog(); // TODO: Setup a dialog.Filter for supported image types if (dialog.ShowDialog() == DialogResult.OK) { try { System.Drawing.Image image = System.Drawing.Image.FromFile(dialog.FileName); #region Dimensions Check if (control == picEyesBake) { // Eyes texture is 128x128 if (Width != 128 || Height != 128) { Bitmap resized = new Bitmap(128, 128, image.PixelFormat); Graphics graphics = Graphics.FromImage(resized); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.DrawImage(image, 0, 0, 128, 128); image.Dispose(); image = resized; } } else { // Other textures are 512x512 if (Width != 128 || Height != 128) { Bitmap resized = new Bitmap(512, 512, image.PixelFormat); Graphics graphics = Graphics.FromImage(resized); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.DrawImage(image, 0, 0, 512, 512); image.Dispose(); image = resized; } } #endregion Dimensions Check // Set the control image control.Image = image; } catch (Exception ex) { MessageBox.Show("Failed to load image: " + ex.Message); } } else { control.Image = null; } #region Baking Dictionary <int, float> paramValues = GetParamValues(); Dictionary <AvatarTextureIndex, AssetTexture> layers = new Dictionary <AvatarTextureIndex, AssetTexture>(); int textureCount = 0; if ((string)control.Tag == "Head") { if (picHair.Image != null) { layers.Add(AvatarTextureIndex.Hair, new AssetTexture(new ManagedImage((Bitmap)picHair.Image))); ++textureCount; } if (picHeadBodypaint.Image != null) { layers.Add(AvatarTextureIndex.HeadBodypaint, new AssetTexture(new ManagedImage((Bitmap)picHeadBodypaint.Image))); ++textureCount; } // Compute the head bake Baker baker = new Baker(BakeType.Head); foreach (KeyValuePair <AvatarTextureIndex, AssetTexture> kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picHeadBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Upper") { if (picUpperBodypaint.Image != null) { layers.Add(AvatarTextureIndex.UpperBodypaint, new AssetTexture(new ManagedImage((Bitmap)picUpperBodypaint.Image))); ++textureCount; } if (picUpperGloves.Image != null) { layers.Add(AvatarTextureIndex.UpperGloves, new AssetTexture(new ManagedImage((Bitmap)picUpperGloves.Image))); ++textureCount; } if (picUpperUndershirt.Image != null) { layers.Add(AvatarTextureIndex.UpperUndershirt, new AssetTexture(new ManagedImage((Bitmap)picUpperUndershirt.Image))); ++textureCount; } if (picUpperShirt.Image != null) { layers.Add(AvatarTextureIndex.UpperShirt, new AssetTexture(new ManagedImage((Bitmap)picUpperShirt.Image))); ++textureCount; } if (picUpperJacket.Image != null) { layers.Add(AvatarTextureIndex.UpperJacket, new AssetTexture(new ManagedImage((Bitmap)picUpperJacket.Image))); ++textureCount; } // Compute the upper body bake Baker baker = new Baker(BakeType.UpperBody); foreach (KeyValuePair <AvatarTextureIndex, AssetTexture> kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picUpperBodyBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Lower") { if (picLowerBodypaint.Image != null) { layers.Add(AvatarTextureIndex.LowerBodypaint, new AssetTexture(new ManagedImage((Bitmap)picLowerBodypaint.Image))); ++textureCount; } if (picLowerUnderpants.Image != null) { layers.Add(AvatarTextureIndex.LowerUnderpants, new AssetTexture(new ManagedImage((Bitmap)picLowerUnderpants.Image))); ++textureCount; } if (picLowerSocks.Image != null) { layers.Add(AvatarTextureIndex.LowerSocks, new AssetTexture(new ManagedImage((Bitmap)picLowerSocks.Image))); ++textureCount; } if (picLowerShoes.Image != null) { layers.Add(AvatarTextureIndex.LowerShoes, new AssetTexture(new ManagedImage((Bitmap)picLowerShoes.Image))); ++textureCount; } if (picLowerPants.Image != null) { layers.Add(AvatarTextureIndex.LowerPants, new AssetTexture(new ManagedImage((Bitmap)picLowerPants.Image))); ++textureCount; } // Compute the lower body bake Baker baker = new Baker(BakeType.LowerBody); foreach (KeyValuePair <AvatarTextureIndex, AssetTexture> kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picLowerBodyBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Bake") { // Bake image has been set manually, no need to manually calculate a bake // FIXME: } #endregion Baking }