Save image fetched from server in unity 3d - unity3d

I get an image from my server and I set it as a texture to a Gameobject. But what I also want to do is to save it in my project as a png. Any idea how am I supposed to do that ? I am new to unity.
Here is how I fetch my image and set it as a texture:
WWW wB = new WWW ("http://xxx.xxxx.xxxx");
yield return wB;
cube.GetComponent<Renderer>().material.mainTexture = wB.texture;
I would like to save wb.Texture as a png.

void SaveTextureToFile(Texture2D texture, string fileName)
{
var bytes=texture.EncodeToPNG();
var file = new File.Open(Application.dataPath + "/"+fileName, FileMode.Create);
var binary= new BinaryWriter(file);
binary.Write(bytes);
file.Close();
}
SaveTextureToFile(wB.texture, "abc.png");
Note that Application.dataPath can be replaced with Application.persistentDataPath.

WWW wB = new WWW ("http://xxx.xxxx.xxxx");
yield return wB;
byte[] bytes = wb.Texture.EncodeToPNG();
Now u could save bytes into a png file in your desired path.

Related

Multiple Asset Bundle Download Cache Problem

I'm using UnityWebRequestAssetBundle.GetAssetBundle(url, bundleData.version, bundleData.crc); system and I can successfully download and use bundleAssets online. But when I want to download multiple bundle assets and save them to use offline for later I have a problem.
I have 2 files for example, 'A' and 'B'.
Case 1: When I download A and go offline I can load A anytime even I close the app. But when I want to download B and come back to A can't load A again because it deletes cache for some reason and tries to download it again.
Case 2: When I download A and B together and go offline, if I load B it loads. But if I try A it can't load and needs internet connection. After that when I try to load B again I loose package so it needs internet connection again.
So basicly I want to download multiple asset bundles and I want to use them whenever I want. How can I solve this problem? Thanks.
Code example:
using UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url, bundleData.version, bundleData.crc);
yield return uwr.SendWebRequest();
bundle = DownloadHandlerAssetBundle.GetContent(uwr);
Debug.Log(bundle);
if (bundle==null)
{
Debug.Log("COULDN'T CONNECT TO URL");
callback(null);
}
else
{
Debug.Log("FOUND AT SEARCH!");
// Get downloaded asset bundle
bundle = DownloadHandlerAssetBundle.GetContent(uwr);
Sprite sprite = isDiff ? bundle.LoadAsset<Sprite>(levelText + "Diff") : bundle.LoadAsset<Sprite>(levelText);
callback(sprite);
}
I solved my issue by saving images to persistentFolder.
I write image to disk first by Texture2D.EncodeToJPG();
private void writeImageOnDisk(Sprite sprite, string fileName)
{
Texture2D texture = DeCompress(sprite.texture);
byte[] textureBytes = texture.EncodeToJPG();
File.WriteAllBytes(Application.persistentDataPath + "/" + fileName+".jpg", textureBytes);
Debug.Log("File Written On Disk!");
}
To EncodeToJPG image must be Read/Write enabled before having AssetBundles and you should decompress them first, I found solution from this topic.
private Texture2D DeCompress(Texture2D source)
{
RenderTexture renderTex = RenderTexture.GetTemporary(
source.width,
source.height,
0,
RenderTextureFormat.Default,
RenderTextureReadWrite.Linear);
Graphics.Blit(source, renderTex);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = renderTex;
Texture2D readableText = new Texture2D(source.width, source.height);
readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
readableText.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(renderTex);
return readableText;
}
And finally you can load image with this code:
private Sprite loadImageFromDisk(string fileName)
{
string dataPath = Application.persistentDataPath + "/" + fileName + ".jpg";
if (!File.Exists(dataPath))
{
Debug.LogError("File Does Not Exist!");
return null;
}
byte[] textureBytes = File.ReadAllBytes(dataPath);
Texture2D loadedTexture = new Texture2D(2048,2048,TextureFormat.ARGB32, false);
loadedTexture.LoadImage(textureBytes);
Sprite spr = Sprite.Create(loadedTexture, new Rect(0f, 0f, loadedTexture.width, loadedTexture.height), new Vector2(.5f,.5f), 2048); // 2048 is pixel per unit if you have a custom pixelPerUnit value you should set it here. Or you will see only some part of pixels of image.
spr.name = fileName;
return spr;
}
I hope this will work for you guys. This is how I store my images after downloading assetbundles and how I call them in game.

Why is the file image size of the texture different from the size when loaded in Unity?

When I used 5.01KB of texture 'Texture2D.LoadTexture()' I expected about 8KB.
but this memory alloc is 2.8MB
why texture alloc memory is more size?
Sample Code
private Texture2D[] m_SaveTextures = null;
public void Start()
{
string path = "../Directory/";
m_SaveTextures = new Texture2D[]
{
GetTexture(path + "Red.png"),
GetTexture(path + "Green.png"),
GetTexture(path + "Blue.png"),
GetTexture(path + "Black.png")
};
}
private Texture2D GetTexture(string path)
{
if (!File.Exists(path)) return null;
var texture = new Texture2D(1, 1, TextureFormat.ARGB32, false)
{
name = Path.GetFileNameWithoutExtension(path)
};
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
Example image..
Unity Profiler check image
Because png is a compressed image file, but the texture you are loading it into is not. TextureFormat.ARGB32 is not a compressed format, so the memory cost is 32 bits per pixel.
You can lower the memory footprint by using one of the compressed formats, like DXT5, if your target platform supports it.

Load binary data from the resources folder [duplicate]

I have am developing a HoloLens project that needs to reference .txt files. I have the files stored in Unity's 'Resources' folder and have them working perfectly fine (when run via Unity):
string basePath = Application.dataPath;
string metadataPath = String.Format(#"\Resources\...\metadata.txt", list);
// If metadata exists, set title and introduction strings.
if (File.Exists(basePath + metadataPath))
{
using (StreamReader sr = new StreamReader(new FileStream(basePath + metadataPath, FileMode.Open)))
{
...
}
}
However, when building the program for HoloLens deployment, I am able to run the code but it doesn't work. None of the resources show up and when examining the HoloLens Visual Studio solution (created by selecting build in Unity), I don't even see a resources or assets folder. I am wondering if I am doing something wrong or if there was a special way to deal with such resources.
Also with image and sound files...
foreach (string str in im)
{
spriteList.Add(Resources.Load<Sprite>(str));
}
The string 'str' is valid; it works absolutely fine with Unity. However, again, it's not loading anything when running through the HoloLens.
You can't read the Resources directory with the StreamReader or the File class. You must use Resources.Load.
1.The path is relative to any Resources folder inside the Assets folder of your project.
2.Do not include the file extension names such as .txt, .png, .mp3 in the path parameter.
3.Use forward slashes instead of back slashes when you have another folder inside the Resources folder. backslashes won't work.
Text files:
TextAsset txtAsset = (TextAsset)Resources.Load("textfile", typeof(TextAsset));
string tileFile = txtAsset.text;
Supported TextAsset formats:
txt .html .htm .xml .bytes .json .csv .yaml .fnt
Sound files:
AudioClip audio = Resources.Load("soundFile", typeof(AudioClip)) as AudioClip;
Image files:
Texture2D texture = Resources.Load("textureFile", typeof(Texture2D)) as Texture2D;
Sprites - Single:
Image with Texture Type set to Sprite (2D and UI) and
Image with Sprite Mode set to Single.
Sprite sprite = Resources.Load("spriteFile", typeof(Sprite)) as Sprite;
Sprites - Multiple:
Image with Texture Type set to Sprite (2D and UI) and
Image with Sprite Mode set to Multiple.
Sprite[] sprite = Resources.LoadAll<Sprite>("spriteFile") as Sprite[];
Video files (Unity >= 5.6):
VideoClip video = Resources.Load("videoFile", typeof(VideoClip)) as VideoClip;
GameObject Prefab:
GameObject prefab = Resources.Load("shipPrefab", typeof(GameObject)) as GameObject;
3D Mesh (such as FBX files)
Mesh model = Resources.Load("yourModelFileName", typeof(Mesh)) as Mesh;
3D Mesh (from GameObject Prefab)
MeshFilter modelFromGameObject = Resources.Load("yourGameObject", typeof(MeshFilter)) as MeshFilter;
Mesh loadedMesh = modelFromGameObject.sharedMesh; //Or design.mesh
3D Model (as GameObject)
GameObject loadedObj = Resources.Load("yourGameObject") as GameObject;
//MeshFilter meshFilter = loadedObj.GetComponent<MeshFilter>();
//Mesh loadedMesh = meshFilter.sharedMesh;
GameObject object1 = Instantiate(loadedObj) as GameObject;
Accessing files in a sub-folder:
For example, if you have a shoot.mp3 file which is in a sub-folder called "Sound" that is placed in the Resources folder, you use the forward slash:
AudioClip audio = Resources.Load("Sound/shoot", typeof(AudioClip)) as AudioClip;
Asynchronous Loading:
IEnumerator loadFromResourcesFolder()
{
//Request data to be loaded
ResourceRequest loadAsync = Resources.LoadAsync("shipPrefab", typeof(GameObject));
//Wait till we are done loading
while (!loadAsync.isDone)
{
Debug.Log("Load Progress: " + loadAsync.progress);
yield return null;
}
//Get the loaded data
GameObject prefab = loadAsync.asset as GameObject;
}
To use: StartCoroutine(loadFromResourcesFolder());
If you want to load text file from resource please do not specify file extension
just simply follow the below given code
private void Start()
{
TextAsset t = Resources.Load<TextAsset>("textFileName");
List<string> lines = new List<string>(t.text.Split('\n'));
foreach (string line in lines)
{
Debug.Log(line);
}
}
debug.Log can print all the data that are available in text file
You can try to store your text files in streamingsasset folder. It works fine in my project

Imaged loaded via new WWW doesn't display in a build, but it does in the player

I'm loading a .jpeg file in at runtime from disk, it works absolutely fine in the player but when I build the project the texture being loaded in via WWW doesn't display - no errors. It suggests a format issue, but like I said it renders as expected in the player:
Unit doc: https://docs.unity3d.com/ScriptReference/WWW.LoadImageIntoTexture.html
private IEnumerator SetMaterialImage(string path)
{
Texture2D tex;
tex = new Texture2D(4, 4, TextureFormat.RGB24, false, true);
WWW www = new WWW(path);
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("ERROR REDNERING IMAGE --- " + www.error);
}
while (!www.isDone) yield return null;
if (www.isDone)
{
Debug.Log("WWW,ISDONE = TRUE");
Shader shader = Shader.Find("Standard");
www.LoadImageIntoTexture(tex);
GetComponent<Renderer>().material.mainTexture = tex;
GetComponent<Renderer>().material.shader = shader;
}
}
Edit: please don't suggest the Resources folder - this particular application must load from disk
I was unable to locate any Unity documentation that explains why this scenario differed between Player and Build, however I was able to confirm that:
GetComponent<Renderer>().material.mainTexture = tex;
Renderer>().material.shader = shader;
Should have been declared in the opposite order:
Renderer>().material.shader = shader;
GetComponent<Renderer>().material.mainTexture = tex;
Edit: Credit #Programmer 'The reason that code work in the Editor is because the Editor remembers which Shader or Texture is assigned to a material. So, when you change the shader, it uses the old texture'

Unity texture from disk has low resolution

I am trying to load a texture(and create a sprite from it eventually) from disk but sprite renders as low resolution image.
What I am doing:
-> Download the image from url. Once the image is downloaded, I save the texture as png to disk so that next time it doesn't requires a download.
WWW www = new WWW(url);
yield return www;
if (www.isDone)
{
if (string.IsNullOrEmpty(www.error))
{
Sprite img = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));
reward.RewardSprite = img;
byte[] bytes = www.texture.EncodeToPNG();
FileManager.SaveRewardImage(reward.rewardId, bytes);
}
else
{
Debug.Log(www.error);
}
}
-> Load from disk
string path = string.Format("Cache\\Venue\\{0}", nameWithoutExtension);
return Resources.Load<Texture2D>(path);
The first time when the texture loads from url, its resolution seems fine(because its the original one). When it loads from cache, it attenuates to a lower one.
Can someone tell me what am I missing, or even if there is way around it?
Thanks in advance.
You can overload your texture in your sprite creation in the same way that you do with the rectangle and specify in TextureFormat the format you need:
Sprite img = Sprite.Create(
new Texture2D (www.texture.width, www.texture.height, TextureFormat format, bool mipmap),
new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));