\r
Camera Camera;\r
Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();\r
- RadegastInstance instance;\r
MeshmerizerR renderer;\r
OpenTK.Graphics.GraphicsMode GLMode = null;\r
AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);\r
OpenTK.Vector4 specularColor;\r
float drawDistance = 48f;\r
float drawDistanceSquared = 48f * 48f;\r
+ bool useDecodedImageCache = true;\r
\r
GridClient Client;\r
RadegastInstance Instance;\r
\r
this.Instance = instance;\r
this.Client = instance.Client;\r
- \r
+\r
UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];\r
cbAA.CheckedChanged += cbAA_CheckedChanged;\r
\r
if (msToSleep < 10) msToSleep = 10;\r
Thread.Sleep(msToSleep);\r
}\r
- \r
+\r
Render(false);\r
\r
glControl.SwapBuffers();\r
// Already have this one loaded\r
if (item.Data.TextureInfo.TexturePointer != 0) continue;\r
\r
- if (item.TextureData != null)\r
+ byte[] imageBytes = null;\r
+ if (item.TGAData != null)\r
+ {\r
+ imageBytes = item.TGAData;\r
+ }\r
+ else if (item.TextureData != null)\r
{\r
ManagedImage mi;\r
- Image img;\r
if (!OpenJPEG.DecodeToImage(item.TextureData, out mi)) continue;\r
\r
bool hasAlpha = false;\r
}\r
}\r
\r
- using (MemoryStream byteData = new MemoryStream(mi.ExportTGA()))\r
+ item.Data.TextureInfo.HasAlpha = hasAlpha;\r
+ item.Data.TextureInfo.FullAlpha = fullAlpha;\r
+ item.Data.TextureInfo.IsMask = isMask;\r
+\r
+ imageBytes = mi.ExportTGA();\r
+ if (useDecodedImageCache)\r
+ {\r
+ RHelp.SaveCachedImage(imageBytes, item.TeFace.TextureID, hasAlpha, fullAlpha, isMask);\r
+ }\r
+ }\r
+\r
+ if (imageBytes != null)\r
+ {\r
+ Image img;\r
+\r
+ using (MemoryStream byteData = new MemoryStream(imageBytes))\r
{\r
img = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(byteData);\r
}\r
\r
Bitmap bitmap = (Bitmap)img;\r
\r
- item.Data.TextureInfo.HasAlpha = hasAlpha;\r
- item.Data.TextureInfo.FullAlpha = fullAlpha;\r
- item.Data.TextureInfo.IsMask = isMask;\r
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);\r
- item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, hasAlpha);\r
+ item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, item.Data.TextureInfo.HasAlpha);\r
bitmap.Dispose();\r
- item.TextureData = null;\r
}\r
+\r
+ item.TextureData = null;\r
+ item.TGAData = null;\r
+ imageBytes = null;\r
}\r
context.Dispose();\r
window.Dispose();\r
else\r
{\r
TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;\r
-\r
- if (item.TextureData == null)\r
+ if (item.TextureData == null && item.TGAData == null)\r
{\r
- ThreadPool.QueueUserWorkItem(sync =>\r
+ if (useDecodedImageCache && RHelp.LoadCachedImage(item.TeFace.TextureID, out item.TGAData, out item.Data.TextureInfo.HasAlpha, out item.Data.TextureInfo.FullAlpha, out item.Data.TextureInfo.IsMask))\r
{\r
- Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) =>\r
+ PendingTextures.Enqueue(item);\r
+ }\r
+ else\r
+ {\r
+ ThreadPool.QueueUserWorkItem(sync =>\r
{\r
- if (state == TextureRequestState.Finished)\r
+ Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) =>\r
{\r
- item.TextureData = asset.AssetData;\r
- PendingTextures.Enqueue(item);\r
- }\r
+ if (state == TextureRequestState.Finished)\r
+ {\r
+ item.TextureData = asset.AssetData;\r
+ PendingTextures.Enqueue(item);\r
+ }\r
+ });\r
});\r
- });\r
+ }\r
}\r
else\r
{\r
// Need to adjust UV for spheres as they are sort of half-prim\r
if (prim.PrimData.ProfileCurve == ProfileCurve.HalfCircle)\r
{\r
- teFace = new Primitive.TextureEntryFace(teFace);\r
+ teFace = (Primitive.TextureEntryFace)teFace.Clone();\r
teFace.RepeatV *= 2;\r
teFace.OffsetV += 0.5f;\r
}\r
\r
- // Sculpt UV map seems to come out vertically flipped from the PrimMesher. Fix it\r
- if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)\r
+ // Sculpt UV vertically flipped compared to prims. Flip back\r
+ if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && prim.Sculpt.Type != SculptType.Mesh)\r
{\r
- teFace = new Primitive.TextureEntryFace(teFace);\r
+ teFace = (Primitive.TextureEntryFace)teFace.Clone();\r
teFace.RepeatV *= -1;\r
}\r
\r
try\r
{\r
gotImage.Reset();\r
- instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>\r
- {\r
- if (state == TextureRequestState.Finished)\r
+ bool hasAlpha, fullAlpha, isMask;\r
+ byte[] tgaData;\r
+ if (useDecodedImageCache && RHelp.LoadCachedImage(textureID, out tgaData, out hasAlpha, out fullAlpha, out isMask))\r
+ {\r
+ img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData));\r
+ }\r
+ else\r
+ {\r
+ instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>\r
{\r
- ManagedImage mi;\r
- OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);\r
-\r
- if (removeAlpha)\r
+ if (state == TextureRequestState.Finished)\r
{\r
- if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)\r
+ ManagedImage mi;\r
+ OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);\r
+\r
+ if (removeAlpha)\r
+ {\r
+ if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)\r
+ {\r
+ mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);\r
+ }\r
+ }\r
+ tgaData = mi.ExportTGA();\r
+ img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData));\r
+ if (useDecodedImageCache)\r
{\r
- mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);\r
+ RHelp.SaveCachedImage(tgaData, textureID, (mi.Channels & ManagedImage.ImageChannels.Alpha) != 0, false, false);\r
}\r
}\r
-\r
- img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));\r
+ gotImage.Set();\r
}\r
- gotImage.Set();\r
- }\r
- );\r
- gotImage.WaitOne(30 * 1000, false);\r
+ );\r
+ gotImage.WaitOne(30 * 1000, false);\r
+ }\r
if (img != null)\r
{\r
texture = img;\r
using System.Linq;\r
using System.Text;\r
using System.IO;\r
+using System.IO.Compression;\r
using System.Xml;\r
using System.Threading;\r
using OpenTK.Graphics.OpenGL;\r
public Primitive Prim;\r
public Primitive.TextureEntryFace TeFace;\r
public byte[] TextureData = null;\r
+ public byte[] TGAData = null;\r
}\r
\r
public enum RenderPass\r
{\r
return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
}\r
+\r
+ #region Cached image save and load\r
+ public static readonly string RAD_IMG_MAGIC = "radegast_img";\r
+\r
+ public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)\r
+ {\r
+ tgaData = null;\r
+ hasAlpha = fullAlpha = isMask = false;\r
+\r
+ try\r
+ {\r
+ string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
+ //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
+\r
+ using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))\r
+ {\r
+ byte[] header = new byte[36];\r
+ int i = 0;\r
+ f.Read(header, 0, header.Length);\r
+\r
+ // check if the file is starting with magic string\r
+ if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))\r
+ return false;\r
+ i += RAD_IMG_MAGIC.Length;\r
+\r
+ if (header[i++] != 1) // check version\r
+ return false;\r
+\r
+ hasAlpha = header[i++] == 1;\r
+ fullAlpha = header[i++] == 1;\r
+ isMask = header[i++] == 1;\r
+\r
+ int uncompressedSize = Utils.BytesToInt(header, i);\r
+ i += 4;\r
+\r
+ textureID = new UUID(header, i);\r
+ i += 16;\r
+\r
+ tgaData = new byte[uncompressedSize];\r
+ using (var compressed = new DeflateStream(f, CompressionMode.Decompress))\r
+ {\r
+ int read = 0;\r
+ while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+ catch (FileNotFoundException) { }\r
+ catch (Exception ex)\r
+ {\r
+ Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)\r
+ {\r
+ try\r
+ {\r
+ string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
+ //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
+\r
+ using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))\r
+ {\r
+ int i = 0;\r
+ // magic header\r
+ f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);\r
+ i += RAD_IMG_MAGIC.Length;\r
+\r
+ // version\r
+ f.WriteByte((byte)1);\r
+ i++;\r
+\r
+ // texture info\r
+ f.WriteByte(hasAlpha ? (byte)1 : (byte)0);\r
+ f.WriteByte(fullAlpha ? (byte)1 : (byte)0);\r
+ f.WriteByte(isMask ? (byte)1 : (byte)0);\r
+ i += 3;\r
+\r
+ // texture size\r
+ byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);\r
+ f.Write(uncompressedSize, 0, uncompressedSize.Length);\r
+ i += uncompressedSize.Length;\r
+\r
+ // texture id\r
+ byte[] id = new byte[16];\r
+ textureID.ToBytes(id, 0);\r
+ f.Write(id, 0, 16);\r
+ i += 16;\r
+\r
+ // compressed texture data\r
+ using (var compressed = new DeflateStream(f, CompressionMode.Compress))\r
+ {\r
+ compressed.Write(tgaData, 0, tgaData.Length);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));\r
+ return false;\r
+ }\r
+ }\r
+ #endregion Cached image save and load\r
}\r
\r
/// <summary>\r
return Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0]) * Math.PI / 180f), (float)(float.Parse(rotparts[1]) * Math.PI / 180f), (float)(float.Parse(rotparts[2]) * Math.PI / 180f));\r
}\r
}\r
-}\r
+}
\ No newline at end of file