public static AbstractSubBaker CreateSubBaker(this Wearable wearable) { VisualParamsMapper.CompleteParams(wearable.Params); switch (wearable.Type) { case WearableType.Eyes: return(new EyeSubBaker(wearable)); case WearableType.Skin: return(new SkinSubBaker(wearable)); case WearableType.Hair: return(new HairSubBaker(wearable)); case WearableType.Shape: return(new ShapeSubBaker(wearable)); case WearableType.Tattoo: return(new TattooSubBaker(wearable)); case WearableType.Alpha: return(new AlphaMaskSubBaker(wearable)); case WearableType.Shirt: return(new ShirtSubBaker(wearable)); case WearableType.Jacket: return(new JacketSubBaker(wearable)); case WearableType.Socks: return(new SocksSubBaker(wearable)); case WearableType.Pants: return(new PantsSubBaker(wearable)); case WearableType.Underpants: return(new UnderpantsSubBaker(wearable)); case WearableType.Undershirt: return(new UndershirtSubBaker(wearable)); case WearableType.Shoes: return(new ShoesSubBaker(wearable)); case WearableType.Skirt: return(new SkirtSubBaker(wearable)); case WearableType.Gloves: return(new GlovesSubBaker(wearable)); case WearableType.Universal: return(new UniversalSubBaker(wearable)); default: return(null); /* intentionally returning null here */ } }
public BakeOutput Process(BakeCache cache, AssetServiceInterface assetSource, Action <string> logOutput = null) { var output = new BakeOutput(); if (cache.IsBaked) { throw new AlreadyBakedException(); } m_AssetService = assetSource; output.VisualParams = VisualParamsMapper.CreateVisualParams(cache.Wearables, ref output.AvatarHeight); var Tgt = new Targets(); var SourceBakers = new Dictionary <WearableType, List <AbstractSubBaker> >(); foreach (WearableType t in Enum.GetValues(typeof(WearableType))) { SourceBakers.Add(t, new List <AbstractSubBaker>()); } foreach (AbstractSubBaker subbaker in cache.SubBakers) { #if DEBUG logOutput?.Invoke(string.Format("Using subbaker {0}: baked={1}", subbaker.Type.ToString(), subbaker.IsBaked.ToString())); #endif SourceBakers[subbaker.Type].Add(subbaker); } #if DEBUG int subbakercount = 0; foreach (List <AbstractSubBaker> entrylist in SourceBakers.Values) { subbakercount += entrylist.Count; } logOutput?.Invoke(string.Format("Using {0} subbakers", subbakercount)); #endif try { foreach (BakeTarget idx in BakeIndices) { int dimensions = idx == BakeTarget.Eyes ? 128 : 512; Bitmap bmp; if (idx == BakeTarget.Skirt && SourceBakers[WearableType.Skirt].Count == 0) { continue; } bmp = new Bitmap(dimensions, dimensions, PixelFormat.Format32bppArgb); Tgt.Images.Add(idx, bmp); Graphics gfx = Graphics.FromImage(bmp); if (idx == BakeTarget.Hair) { gfx.CompositingMode = CompositingMode.SourceCopy; using (var b = new SolidBrush(Color.FromArgb(0, Color.White))) { gfx.FillRectangle(b, new Rectangle(0, 0, dimensions, dimensions)); } } gfx.CompositingMode = CompositingMode.SourceOver; Tgt.Graphics.Add(idx, gfx); } logOutput?.Invoke("Processing R,G,B and bump parts"); DrawSubBakers(Tgt, SourceBakers[WearableType.Skin], SkinIndices); DrawSubBakers(Tgt, SourceBakers[WearableType.Universal].OrderBy(item => item.Ordinal), UniversalIndices); DrawSubBakers(Tgt, SourceBakers[WearableType.Tattoo].OrderBy(item => item.Ordinal), SkinIndices); DrawSubBakers(Tgt, SourceBakers[WearableType.Hair], new BakeTarget[] { BakeTarget.Hair }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Hair], new BakeTarget[] { BakeTarget.Hair }); DrawSubBakers(Tgt, SourceBakers[WearableType.Eyes], new BakeTarget[] { BakeTarget.Eyes }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Eyes], new BakeTarget[] { BakeTarget.Eyes }); DrawSubBakers(Tgt, SourceBakers[WearableType.Underpants].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.LowerBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Underpants], new BakeTarget[] { BakeTarget.LowerBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Undershirt].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.UpperBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Undershirt], new BakeTarget[] { BakeTarget.UpperBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Socks].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.LowerBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Socks], new BakeTarget[] { BakeTarget.LowerBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Shoes].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.LowerBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Shoes], new BakeTarget[] { BakeTarget.LowerBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Pants].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.LowerBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Pants], new BakeTarget[] { BakeTarget.LowerBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Shirt].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.UpperBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Shirt], new BakeTarget[] { BakeTarget.UpperBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Jacket].OrderBy(item => item.Ordinal), ClothingIndices); DrawBumpMaps(Tgt, SourceBakers[WearableType.Jacket], ClothingIndices); DrawSubBakers(Tgt, SourceBakers[WearableType.Gloves].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.UpperBody }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Gloves], new BakeTarget[] { BakeTarget.UpperBody }); DrawSubBakers(Tgt, SourceBakers[WearableType.Skirt].OrderBy(item => item.Ordinal), new BakeTarget[] { BakeTarget.Skirt }); DrawBumpMaps(Tgt, SourceBakers[WearableType.Skirt], new BakeTarget[] { BakeTarget.Skirt }); /* for alpha masks we have to get rid of the Graphics */ foreach (Graphics gfx in Tgt.Graphics.Values) { gfx.Dispose(); } Tgt.Graphics.Clear(); logOutput?.Invoke("Processing alpha mask"); /* clean out alpha channel. the ones we used before are not necessary anymore */ foreach (KeyValuePair <BakeTarget, Bitmap> kvp in Tgt.Images) { if (kvp.Key == BakeTarget.Hair) { /* skip hair */ continue; } int byteSize = kvp.Value.Width * kvp.Value.Height * 4; BitmapData lockBits = kvp.Value.LockBits(Tgt.Rectangles[kvp.Key], ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); var rawdata = new byte[byteSize]; Marshal.Copy(lockBits.Scan0, rawdata, 0, byteSize); for (int bytePos = byteSize; bytePos-- != 0; bytePos -= 3) { rawdata[bytePos] = 255; } Marshal.Copy(rawdata, 0, lockBits.Scan0, byteSize); kvp.Value.UnlockBits(lockBits); } if (SourceBakers[WearableType.Alpha].Count != 0) { var AlphaMaskBakes = new Dictionary <BakeTarget, byte[]>(); foreach (AbstractSubBaker baker in SourceBakers[WearableType.Alpha]) { foreach (BakeTarget tgt in BakeIndices) { Image srcimg; Bitmap tgtimg; byte[] dstAlphaMask; byte[] srcAlphaMask; int dimensions = tgt == BakeTarget.Eyes ? 128 : 512; int imagebytes = dimensions * dimensions * 4; srcimg = baker.BakeAlphaMaskOutput(this, tgt); if (srcimg == null) { continue; } if (!AlphaMaskBakes.TryGetValue(tgt, out dstAlphaMask)) { if (!Tgt.Images.TryGetValue(tgt, out tgtimg)) { continue; } dstAlphaMask = GetRawData(tgtimg); AlphaMaskBakes[tgt] = dstAlphaMask; } using (var srcbmp = new Bitmap(srcimg)) { srcAlphaMask = GetRawData(srcbmp); } for (int idx = imagebytes; idx-- != 0; idx -= 3) { dstAlphaMask[idx] = Math.Min(dstAlphaMask[idx], srcAlphaMask[idx]); } } } foreach (KeyValuePair <BakeTarget, byte[]> kvp in AlphaMaskBakes) { UpdateRawData(Tgt.Images[kvp.Key], kvp.Value); } } logOutput?.Invoke("Compressing bakes"); byte[] finalbump; output.HairBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.EncodeWithBump(Tgt.Images[BakeTarget.Hair], true, Tgt.Bumps[BakeTarget.Hair]), Name = "Bake Texture Hair" }; output.HeadBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.EncodeWithBump(Tgt.Images[BakeTarget.Head], true, Tgt.Bumps[BakeTarget.Head]), Name = "Bake Texture Head" }; output.UpperBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.EncodeWithBump(Tgt.Images[BakeTarget.UpperBody], true, Tgt.Bumps[BakeTarget.UpperBody]), Name = "Bake Texture Upperbody" }; output.LowerBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.EncodeWithBump(Tgt.Images[BakeTarget.LowerBody], true, Tgt.Bumps[BakeTarget.LowerBody]), Name = "Bake Texture Lowerbody" }; output.EyeBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.EncodeWithBump(Tgt.Images[BakeTarget.Eyes], true, Tgt.Bumps[BakeTarget.Eyes]), Name = "Bake Texture Eyes" }; Bitmap finalSkirt; if (Tgt.Images.TryGetValue(BakeTarget.Skirt, out finalSkirt)) { output.SkirtBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = Tgt.Bumps.TryGetValue(BakeTarget.Skirt, out finalbump) ? J2cEncoder.EncodeWithBump(finalSkirt, true, finalbump) : J2cEncoder.Encode(finalSkirt, true), Name = "Bake Texture Skirt" }; } Bitmap finalLeftArm; if (Tgt.Images.TryGetValue(BakeTarget.LeftArm, out finalLeftArm)) { output.LeftArmBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.Encode(finalLeftArm, true), Name = "Bake Texture LeftArm" }; } Bitmap finalLeftLeg; if (Tgt.Images.TryGetValue(BakeTarget.LeftLeg, out finalLeftLeg)) { output.LeftLegBake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.Encode(finalLeftLeg, true), Name = "Bake Texture LeftLeg" }; } Bitmap finalAux1; if (Tgt.Images.TryGetValue(BakeTarget.Aux1, out finalAux1)) { output.Aux1Bake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.Encode(finalAux1, true), Name = "Bake Texture Aux1" }; } Bitmap finalAux2; if (Tgt.Images.TryGetValue(BakeTarget.Aux1, out finalAux2)) { output.Aux2Bake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.Encode(finalAux2, true), Name = "Bake Texture Aux2" }; } Bitmap finalAux3; if (Tgt.Images.TryGetValue(BakeTarget.Aux1, out finalAux3)) { output.Aux3Bake = new AssetData { ID = UUID.RandomFixedFirst(0xffffffff), Type = AssetType.Texture, Temporary = true, Data = J2cEncoder.Encode(finalAux3, true), Name = "Bake Texture Aux3" }; } } finally { foreach (Graphics gfx in Tgt.Graphics.Values) { gfx.Dispose(); } foreach (Bitmap bmp in Tgt.Images.Values) { bmp.Dispose(); } } return(output); }