I was wondering if there was a way to load a simple .fbx file with just a cube into my game without using the monogame content pipeline. Here's the code I have currently, but I want to essentially just get rid of the requirement for the file to be .xnb
public static Model LoadModel(string model)
{
Model outModel = null;
// If model is already in memory, reference that instead of having to load it again
if (Loaded3DModels.ContainsKey(model))
{
Loaded3DModels.TryGetValue(model, out outModel);
}
else
{
if (File.Exists(Game.gameWindow.Content.RootDirectory + "/" + model + ".fbx"))
{
outModel = Game.gameWindow.Content.Load<Model>(model + ".fbx");
Loaded3DModels.Add(model, outModel);
}
else
{
Debug.LogError("The Model \"" + model + ".fbx\" does not exist!", true, 2);
}
}
return outModel;
}
So I managed to use AssImp to load a mesh, and then "Convert" it to something Monogame could render
public Renderer3DComponent(Mesh mesh)
{
// Set up material
Material = new BasicEffect(Game.graphics.GraphicsDevice);
Material.Alpha = 1;
Material.VertexColorEnabled = true;
Material.LightingEnabled = false;
// Tris
for (int i = mesh.VertexCount - 1; i >= 0; i--)
{
var Vert = mesh.Vertices[i];
Verts.Add(new VertexPositionColor(new Vector3(Vert.X, Vert.Y, Vert.Z), Color.White));
}
// Buffer
Buffer = new VertexBuffer(Game.graphics.GraphicsDevice, typeof(VertexPositionColor), Verts.Count, BufferUsage.WriteOnly);
Buffer.SetData(Verts.ToArray());
}
Related
I have a Unity game where the player gets one's inventory from the database after starting the game.
I got the pictures of items stored in Firebase Storage, and I can download them.
However, I don't know how to make sprites from the byte[] (fileContents) I get by downloading.
(I'd load these sprites in the player's inventory later)
I got the following C# script:
private void getPlayersInventory()
{
FirebaseDatabase.DefaultInstance.GetReference("users").Child(auth.CurrentUser.UserId)
.Child("inventory").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
print("unable to get snapshot for the inventory");
}
else if (task.IsCompleted)
{
snapshot = task.Result;
if(snapshot.HasChildren)
{
foreach(var current in snapshot.Children)
{
string currentName = current.Value.ToString();
print(currentName.ToLower() + ".png");
const long maxAllowedSize = 10 * 1024 * 1024;
storageReference.Child(currentName.ToLower() + ".png")
.GetBytesAsync(maxAllowedSize)
.ContinueWith((Task<byte[]> task_) =>
{
if (task_.IsFaulted || task_.IsCanceled)
{
Debug.Log(task_.Exception.ToString());
}
else
{
byte[] fileContents = task_.Result;
Debug.Log("Finished downloading!");
}
});
}
}
}
});
}
I believe that you're looking for ImageConversion.LoadImage
Your full code will look something like:
var tex = new Texture(1,1); // note that the size is overridden
tex.LoadImage(task_.Result);
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(tex.width/2, tex.height/2));
You can also pack textures at runtime if that's your eventual goal. Once you get through the tex.LoadImage chunk, you should be able to work out Texture2D.PackTextures.
--Patrick
I'm making a game with musical instruments and a recording device in-game. I want the user to be able to play the instruments and record them to an audioclip - and to a wav file - which they can then play back as accompaniment to themselves playing another instrument, the output again recorded, allowing them to make tracks with combined instruments. So far I have everything working except I can't figure out how to get the audioclip - I have .wav files being saved to the asset folder but I don't understand the code enough to figure out where to set the audioclip's data to the data being written.
I'm using code from Olkor here to save the recording as wav, which is working great: https://unitylist.com/p/za/Output-Audio-Recorder
My two options to save an audioclip from this seem to be a) save to an audioclip at the same time as saving to disk (I can't figure this out) or save it to disk and then load it into the game as an audioclip - using Unity Web Request which I have tried to do but I get an error either cannot access the .audioclip property of an aborted DownloadHandlerAudioClip or if I invoke the function with a delay to load the file, there is an error decoding the audio. Either way I have a saved .wav audio file in my assets folder but no audioclip.
InvalidOperationException: Cannot access the .audioClip property of an aborted DownloadHandlerAudioClip
UnityEngine.Networking.DownloadHandlerAudioClip.GetContent
(UnityEngine.Networking.UnityWebRequest www) (at
C:/buildslave/unity/build/Modules/UnityWebRequestAudio/Public/DownloadHandler
Audio.bindings.cs:49)
OutputAudioRecorder+<GetAudioClip>d__27.MoveNext () (at
Assets/Scripts/OutputAudioRecorder.cs:202)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator
enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
The script below that I'm using is attached to the audiolistener and so it's picking up all the output of audio from the game. I have looked at doing something with audioListener GetOutputData and passing it to audioClip SetData but I haven't been able to figure it out and I admit that this is beyond my current ability - I'd really appreciate some insight into how to approach this problem in any of the ways possible.
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class OutputAudioRecorder : MonoBehaviour
{
public const string DEFAULT_FILENAME = "record";
public const string FILE_EXTENSION = ".wav";
public bool IsRecording { get { return recOutput; } }
private int bufferSize;
private int numBuffers;
private int outputRate;
private int headerSize = 44; //default for uncompressed wav
private String fileName;
private bool recOutput = false;
private AudioClip newClip;
private FileStream fileStream;
private AudioClip[] audioClips;
private AudioSource[] audioSources;
public int currentSlot;
float[] tempDataSource;
void Awake()
{
outputRate = AudioSettings.outputSampleRate;
}
void Start()
{
AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
audioSources = new AudioSource[3];
audioSources[0] = GameObject.FindWithTag("RecSlot1").GetComponent<AudioSource>();
audioSources[1] = GameObject.FindWithTag("RecSlot2").GetComponent<AudioSource>();
audioSources[2] = GameObject.FindWithTag("RecSlot3").GetComponent<AudioSource>();
}
public void StartRecording(string recordFileName)
{
fileName = Path.GetFileNameWithoutExtension(recordFileName) + FILE_EXTENSION;
if (!recOutput)
{
StartWriting(fileName);
recOutput = true;
}
else
{
Debug.LogError("Recording is in progress already");
}
}
public void StopRecording()
{
recOutput = false;
WriteHeader();
UpdateClip();
}
private void StartWriting(String name)
{
fileStream = new FileStream(Application.dataPath + "/" + name, FileMode.Create);
var emptyByte = new byte();
for (int i = 0; i < headerSize; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
}
private void OnAudioFilterRead(float[] data, int channels)
{
if (recOutput)
{
ConvertAndWrite(data); //audio data is interlaced
}
}
private void ConvertAndWrite(float[] dataSource)
{
var intData = new Int16[dataSource.Length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData = new Byte[dataSource.Length * 2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
var rescaleFactor = 32767; //to convert float to Int16
for (var i = 0; i < dataSource.Length; i++)
{
intData[i] = (Int16)(dataSource[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
tempDataSource = new float[dataSource.Length];
tempDataSource = dataSource;
}
private void WriteHeader()
{
fileStream.Seek(0, SeekOrigin.Begin);
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
var subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
var audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
var numChannels = BitConverter.GetBytes(two);
fileStream.Write(numChannels, 0, 2);
var sampleRate = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate, 0, 4);
var byteRate = BitConverter.GetBytes(outputRate * 4);
fileStream.Write(byteRate, 0, 4);
UInt16 four = 4;
var blockAlign = BitConverter.GetBytes(four);
fileStream.Write(blockAlign, 0, 2);
UInt16 sixteen = 16;
var bitsPerSample = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample, 0, 2);
var dataString = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString, 0, 4);
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize);
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
}
void UpdateClip()
{
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip()
{
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + Application.dataPath + "myRecord1.wav", AudioType.WAV))
{
yield return www.Send();
if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
AudioClip newClip = DownloadHandlerAudioClip.GetContent(www);
Debug.Log(newClip.name + "name " + newClip.length);
keyboardScript.audioClip = newClip;
}
}
}
Try to load as audio clip using this:
private IENumerator LoadAudio(string url) {
WWW www;
www = new WWW("file:///" + url);
yield return www;
if (www != null && www.isDone)
{
AudioClip audioClip;
audioClip = www.GetAudioClip(true, false, AudioType.WAV);
}
}
That works for me at least.
Thought this was solution but still not working to load at runtime. (It just refers to the old copy of the clip until I reload the game. Didn't notice this at first.)
While I wasn't able to get the web request to work for me or get WWW to work either, I did get the effect I wanted
by creating a blank clip in the assets folder where the .wav saves, then dragging it in the properties window as a reference, setting the audio clip to streaming and then using the code above to write the wav, it automatically overwrites the clip with the data I recorded.
The audio has a bit of distortion on it (crackling) which I'll look into next.
This solution is I'm aware probably incredibly obvious (i guess I had to give unity something to refer to but didn't realise it) and doesn't solve the problem of loading files at runtime if they aren't predefined like mine are, but it fixed my problem at least.
I still new using UnityWebRequest to download and load asset bundle from server container. The problem is the value for the download progress always 0.
How can I get the value for download progress?
Code Below what I try to download and get the download progress.
//Method to download the assetbundle
IEnumerator DownloadAsset()
{
string url = here the URL for asset bundle;
using (var uwr = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
{
uwr.downloadHandler = new DownloadHandlerAssetBundle(url, 36, 0);
UnityWebRequestAsyncOperation operation = uwr.SendWebRequest();
yield return StartCoroutine(DownloadProgress(operation));
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr);
{
print("Get asset from bundle...");
}
//Load scene
uwr.Dispose();
print("ready to Load scene from asset...");
StartCoroutine(LoadSceneProgress("Example"));
bundle.Unload(false);
}
}
//Method for download progress
IEnumerator DownloadProgress(UnityWebRequestAsyncOperation operation)
{
while (!operation.isDone)
{
progressBar.color = Color.red;
downloadDataProgress = operation.progress * 100;
progressBar.fillAmount = downloadDataProgress / 100;
print("Download: " + downloadDataProgress);
yield return null;
}
Debug.Log("Done");
}
I expect to display download progress bar or download percentage to show the download progress on screen. but the download progress value always 0.
Instead of
yield return StartCoroutine(DownloadProgress(operation));
the proper way of yielding an IEnumerator is simply
yield return DownloadProgress(operation);
However why not simply do it directly in the same Coroutine?
I would however recommend to rather use UnityWebRequestAssetBundle.GetAssetBundle instead of configurating it from scratch yourself and some other changes:
IEnumerator DownloadAsset()
{
string url = "<here the URL for asset bundle>";
/*
* directly use UnityWebRequestAssetBundle.GetAssetBundle
* instead of "manually" configure and attach the download handler etc
*/
using (var uwr = new UnityWebRequestAssetBundle.GetAssetBundle(url, 36, 0)
{
var operation = uwr.SendWebRequest();
/*
* this should be done only once actually
*/
progressBar.color = Color.red;
while (!operation.isDone)
{
/*
* as BugFinder metnioned in the comments
* what you want to track is uwr.downloadProgress
*/
downloadDataProgress = uwr.downloadProgress * 100;
/*
* use a float division here
* I don't know what type downloadDataProgress is
* but if it is an int than you will always get
* an int division <somethingSmallerThan100>/100 = 0
*/
progressBar.fillAmount = downloadDataProgress / 100.0f;
print("Download: " + downloadDataProgress);
yield return null;
}
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr);
{
print("Get asset from bundle...");
}
/*
* You do not have to Dispose uwr since the using block does this automatically
*/
//uwr.Dispose();
//Load scene
print("ready to Load scene from asset...");
StartCoroutine(LoadSceneProgress("Example"));
bundle.Unload(false);
}
}
Note from Mayur Asodariya fromt he comments below:
It might happen that your server does not provide the download size and therefore no progress information. In this case you can follow this post to configure your server correctly.
Use following code snippet as a refference:
public IEnumerator ShowDownloadProgress(UnityWebRequest www)
{
while (!www.isDone)
{
LoadingContainer.SetActive(true);
SliderHandle.sprite = Resources.Load<Sprite>("LoadingScreenIcons");
loadingSlider.value = www.downloadProgress*100;
Debug.Log("Download Progress: " +www.downloadProgress);
NewDownloadProgressText.text = (string.Format("{0:0%}", www.downloadProgress));
yield return new WaitForSeconds(.01f);
}
loadingSlider.value = 0;
}
I'm fairly new to C# and Unity so I'm having a little issue here. I'm working on some kind of "photobooth app", which makes the gallery a big part of it. However, I managed to sort out the part of taking screenshots and show them, my problem is that I can't seem to figure out how to delete the pictures from the gallery (I mean, inside the app).
So far, I'm working with this code (just to give it some sense), but this bit erases ALL of the taken pictures, not just the one currently showing.
tring path = Application.persistentDataPath;
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] info = dir.GetFiles("*.png");
foreach (FileInfo f in info)
{
File.Delete(f.FullName);
}
I don't know if it'd help, but this is the code I'm using to take and save the screenshots:
yield return new WaitForEndOfFrame();
string timeStamp = System.DateTime.Now.ToString("dd-MM-yyyy-HH-mm-ss");
string fileName = "Screenshot" + timeStamp + ".png";
string pathToSave = fileName;
ScreenCapture.CaptureScreenshot(pathToSave);
yield return new WaitForEndOfFrame();
And, the one I'm using for showing them in the gallery:
public class ScreenShotPreview : MonoBehaviour
{
[SerializeField]
GameObject Panel;
[SerializeField]
string sceneName;
string[] files = null;
int whichScreenShotIsShown = 0;
void Start()
{
files = Directory.GetFiles(Application.persistentDataPath + "/", "*.png");
if (files.Length > 0)
{
GetPictureAndShowIt();
}
}
void GetPictureAndShowIt()
{
string pathToFile = files[whichScreenShotIsShown];
Texture2D texture = GetScreenshotImage(pathToFile);
Sprite sp = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
Panel.GetComponent<Image>().sprite = sp;
}
Texture2D GetScreenshotImage(string filePath)
{
Texture2D texture = null;
byte[] fileBytes;
if (File.Exists(filePath))
{
fileBytes = File.ReadAllBytes(filePath);
texture = new Texture2D(2, 2, TextureFormat.RGB24, false);
texture.LoadImage(fileBytes);
}
return texture;
}
public void NextPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown += 1;
if (whichScreenShotIsShown > files.Length - 1)
whichScreenShotIsShown = 0;
GetPictureAndShowIt();
}
}
public void PreviousPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown -= 1;
if (whichScreenShotIsShown < 0)
whichScreenShotIsShown = files.Length - 1;
GetPictureAndShowIt();
}
}
I hope it makes sense? Thank you in advance!
TLDR; Can't figure out how to delete current picture showing in the gallery.
Your file paths are stored in the string[] files variable. The whichScreenShotIsShown variable is the current index that determines which path is currently being displayed. These two variables are declared inside the ScreenShotPreview script.
Therefore to delete the current file, you would do something like this:
string currentFile = files[whichScreenShotIsShown];
File.Delete(currentFile );
Goal
Get a gif working in unity from a URL, I am currently using the WWW class. I am currently getting a byte[] and converting it to a System.Drawing.Image. This works in the editor but not any build
Error:
"Type Load Exception: Could not load type" System.IO.InternalBufferOverflowException from the assembly "System.Drawing.Image" at line 111
Why?
It has to do with the System.Drawing.Image.FromStream built in method, Unity for some reason doesn't like it. The other options are .FromFile and .FromHBitMap, I dont know how to use HBitMap but going back to my original plan, .FromFile is unusable to me.
Entire Code
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
using System.Collections;
public class AnimatedGifDrawerBack : MonoBehaviour
{
public string loadingGifPath;
public float speed = 1;
public Vector2 drawPosition;
public string pName;
public float width;
public float height;
public float percentage;
public GameObject positionPlaceHolderGO;
public Vector2 positionPlaceHolder;
public Text debugText;
private SpriteImageArray sia;
private string url;
private WWW www;
public bool finishedWWW = false;
public bool hasWWW = false;
public bool canOnGUI = false;
List<Texture2D> gifFrames = new List<Texture2D>();
void Start()
{
percentage = 1.3f;
positionPlaceHolderGO = GameObject.FindGameObjectWithTag("PBLPlace");
positionPlaceHolder = positionPlaceHolderGO.transform.position;
}
void Update()
{
while (hasWWW == false)
{
Debug.Log("in while loop");
if (this.GetComponent<PokemonCreatorBack>().name == "")
{
}
else
{
debugText.text = "Name Found";
url = "www.pkparaiso.com/imagenes/xy/sprites/animados-espalda/" + this.GetComponent<PokemonCreatorBack>().PokemonName.ToLower() + ".gif";
StartCoroutine(WaitForRequest(positionPlaceHolderGO, url));
hasWWW = true;
debugText.text = "hawWWW = true";
}
}
}
void OnGUI()
{
height = (float)Screen.height - 80f / percentage;
//GUI.DrawTexture (new Rect (Screen.width-width, Screen.height - height, gifFrames [0].width * percentage, gifFrames [0].height * percentage), gifFrames [(int)(Time.frameCount * speed) % gifFrames.Count]);
if (canOnGUI)
GUI.DrawTexture(new Rect(positionPlaceHolder.x, positionPlaceHolder.y, gifFrames[0].width * percentage, gifFrames[0].height * percentage), gifFrames[(int)(Time.frameCount * speed) % gifFrames.Count]);
}
IEnumerator WaitForRequest(GameObject go, string url)
{
www = new WWW(url);
yield return www;
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.texture.name);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
debugText.text = "finishedWWW = true";
finishedWWW = true;
}
public System.Drawing.Image ByteArrayToImage(byte[] byteArrayIn)
{
if (finishedWWW == false)
{
Debug.Log("Called too early");
}
if (byteArrayIn == null)
{
Debug.Log("Null byte array");
return null;
}
Debug.Log("Bytra array in length: " + byteArrayIn.GetLongLength(0));
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image returnImage = System.Drawing.Image.FromStream(ms); //MAIN SOURCE OF ERROR HERE
finishedWWW = true;
debugText.text = "System.Image Created";
return returnImage;
}
public void loadImage()
{
Debug.Log("Called Load Image BACK");
debugText.text = "Called Load Image BACK";
System.Drawing.Image gifImage = ByteArrayToImage(www.bytes);
FrameDimension dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
int frameCount = gifImage.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
gifImage.SelectActiveFrame(dimension, i);
Bitmap frame = new Bitmap(gifImage.Width, gifImage.Height);
System.Drawing.Graphics.FromImage(frame).DrawImage(gifImage, Point.Empty);
Texture2D frameTexture = new Texture2D(frame.Width, frame.Height);
for (int x = 0; x < frame.Width; x++)
for (int y = 0; y < frame.Height; y++)
{
System.Drawing.Color sourceColor = frame.GetPixel(x, y);
frameTexture.SetPixel(frame.Width - 1 + x, -y, new Color32(sourceColor.R, sourceColor.G, sourceColor.B, sourceColor.A)); // for some reason, x is flipped
}
frameTexture.Apply();
gifFrames.Add(frameTexture);
}
Debug.Log("Starting ON GUI!");
debugText.text = "Starting OnGUI";
canOnGUI = true;
}
}
Thoughts
byteArrayIn.GetLongLength(0)
returns 80,000 at most.
The last debug statement coming through is Called Image Loading BACK.
I will write my own fire streamer if necessary, and if it is necessary can someone point me in the write direction for that.
I think the main workaround is dealing with the Image.FromStream().
There are two of these in the scene.
All thoughts or solutions are welcome, I really just wish I knew how to tackle this error so that I could share it more with the Unity Community.
We faced the same problem this morning.
The app is not finding a type in the System.IO namespace required by System.Drawing.Image.
The missing type has apparently been stripped from the system.dll that is packed during the build process.
To fix this, you need to copy and replace the unity generated System.dll with the original mono System.dll.
In your build, replace projectName_Data\Managed\System.dll with the System.dll found in Unity's mono installation folder:
Editor\Data\Mono\lib\mono\2.0 (relative to the root of the Unity installation folder).
Hope it helps!