I am trying to dynamically create an image database using arcores new image tracking feature.
Currently I have a server serving me image locations which I download to the persistent data path of my device. I use these images to then create new database entries like below:
Public Variables:
public AugmentedImageDatabase newBD;
public AugmentedImageDatabaseEntry newEntry;
Here I do regex matching to get the images from the datapath and convert them to texture2D's in order to populate the AugmentedImageDatabaseEntry values.
Regex r1 = new Regex(#"https?://s3-([^.]+).amazonaws.com/([^/]+)/([^/]+)/(.*)");
// Match the input for file name
Match match = r1.Match(input);
if (match.Success)
{
string v = match.Groups[4].Value;
RegexMatch = v;
Texture2D laodedTexture = LoadTextureToFile(v);
laodedTexture.EncodeToPNG();
AugmentedImageDatabaseEntry newEntry = new AugmentedImageDatabaseEntry(v, laodedTexture, Application.persistentDataPath + "/" + v);
newEntry.Name = v;
newEntry.Texture = laodedTexture;
newEntry.TextureGUID = Application.persistentDataPath + "/" + v;
Debug.Log(newEntry.Name);
Debug.Log(newEntry.Texture);
Debug.Log(newEntry.TextureGUID);
newBD.Add(newEntry);
}
To get this to work on android I had to modify the source of ARCore's unity implementation a little so that the database.Add() function would work outside of the editor.
All of this seems to work seamlessly as I don't get any errors yet.
Once I change scenes to the ARCore scene I instantiate an ARCore Camera and create a new sessionconfig which holds a reference to the database populated above.
Here is that code:
public class NewConfigSetup : MonoBehaviour {
public GameObject downloadManager;
public GameObject arcoreDevice;
// Use this for initialization
void Start () {
downloadManager = GameObject.Find("DownlaodManager");
TestModelGenerator generator = downloadManager.GetComponent<TestModelGenerator>();
GoogleARCore.ARCoreSessionConfig newconfig = new GoogleARCore.ARCoreSessionConfig();
GoogleARCore.ARCoreSessionConfig config = ScriptableObject.CreateInstance<GoogleARCore.ARCoreSessionConfig>();
config.AugmentedImageDatabase = generator.newBD;
Debug.Log("transfered db size --------------- " + config.AugmentedImageDatabase.Count);
arcoreDevice.GetComponent<GoogleARCore.ARCoreSession>().SessionConfig = config;
Instantiate(arcoreDevice,new Vector3(0,0,0), Quaternion.identity);
}
}
When I run in the editor, I dont get errors untill I view the database in the editor, thats when I get this error:
ERROR: flag '--input_image_path' is missing its argument; flag
description: Path of image to be evaluated. Currently only supports
*.png, *.jpg and *.jpeg.
When I debug and look in the memory of the AugmentedImageDatabase. Everything seems to be there and working fine. Also once I build for android I get no errors whatsoever, as well as when I use 'adb logcat -s Unity' in the command line, no exceptions are thrown.
Could this be a limitation with the new ARCore feature? Are the AugmentedImageDatabases not allowing for dynamic creation on android? If so than why are there built in functions for creating them?
I understand the features are brand new and there is not much documentation anywhere so any help would be greatly appreciated.
I posted an Issue on ARCore's Github page, and got a response that the feature you're talking about isn't yet exposed in the Unity API :
https://github.com/google-ar/arcore-unity-sdk/issues/256
Related
I have a Xamarin Forms app where I've included a font file called Roboto-Regular.ttf in the Assets folder of the Android project. Its Build Action is set to AndroidAsset.
Using the SixLabors.Fonts NuGet package, I'm trying to load this font to use it for watermarking.
However, trying to install the font using the asset stream, an exception is thrown saying:
System.NotSupportedException: Specified method is not supported.
var fonts = new FontCollection();
FontFamily fontFamily;
using (var fontStream = Assets.Open("Roboto-Regular.ttf"))
{
fontFamily = fonts.Install(fontStream); // Fails with "method not supported"
}
return fontFamily;
Any ideas what might be causing this, or if there is a better way to load fonts for use with the SixLabors.ImageSharp package?
Edit: I tried the suggestion below by SushiHangover, but it yields the same result:
There is are two Assets.Open methods and one provides an accessMode (C# Access enum flag set):
using (var fontStream = Assets.Open("Roboto-Regular.ttf", Android.Content.Res.Access.Random))
{
fontFamily = fonts.Install(fontStream);
}
re: https://developer.android.com/reference/android/content/res/AssetManager.html#open(java.lang.String,%20int)
public enum Access
{
[IntDefinition (null, JniField = "android/content/res/AssetManager.ACCESS_BUFFER")]
Buffer = 3,
[IntDefinition (null, JniField = "android/content/res/AssetManager.ACCESS_RANDOM")]
Random = 1,
[IntDefinition (null, JniField = "android/content/res/AssetManager.ACCESS_STREAMING")]
Streaming,
[IntDefinition (null, JniField = "android/content/res/AssetManager.ACCESS_UNKNOWN")]
Unknown = 0
}
Seems the underlying Stream did not have Length or Position properties (which explains the exception), so for now I resorted to converting to a seekable MemoryStream instead:
using (var assetStreamReader = new StreamReader(Assets.Open("Roboto-Regular.ttf"))
{
using (var ms = new MemoryStream())
{
assetStreamReader.BaseStream.CopyTo(ms);
ms.Position = 0;
var fontFamily = new FontCollection().Install(ms);
}
}
Looking at the FontReader implementation, the error now makes even more sense: https://github.com/SixLabors/Fonts/blob/master/src/SixLabors.Fonts/FontReader.cs
However, I'm not sure why Assets doesn't return a seekable stream?
So in my app i pass a game object, called datacontroller through out my three scenes. The persistent scene is an empty scene, the menuscreen scene and then the game scene. My application works perfectly on my computer and in editor mode but when i download the apk to my android tablet it no longer works! iv'e read this may have to do with my code for my object but i dont think i written anything that only works in the editor.
enter code here
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
using System.IO; // The System.IO namespace contains functions related to loading and saving
files
public class DataController : MonoBehaviour
{
private RoundData[] allRoundData;
private PlayerProgress playerProgress;
private string gameDataFileName = "data.json";
void Start()
{
DontDestroyOnLoad(gameObject);
LoadGameData();
LoadPlayerProgress();
SceneManager.LoadScene("MenuScreen");
}
public RoundData GetCurrentRoundData()
{
// If we wanted to return different rounds, we could do that here
// We could store an int representing the current round index in PlayerProgress
return allRoundData[0];
}
public void SubmitNewPlayerScore(int newScore)
{
// If newScore is greater than playerProgress.highestScore, update playerProgress with the new value and call SavePlayerProgress()
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
public int GetHighestPlayerScore()
{
return playerProgress.highestScore;
}
private void LoadGameData()
{
// Path.Combine combines strings into a file path
// Application.StreamingAssets points to Assets/StreamingAssets in the Editor, and the StreamingAssets folder in a build
string filePath = Path.Combine(Application.streamingAssetsPath, gameDataFileName);
if (File.Exists(filePath))
{
// Read the json from the file into a string
string dataAsJson = File.ReadAllText(filePath);
// Pass the json to JsonUtility, and tell it to create a GameData object from it
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
// Retrieve the allRoundData property of loadedData
allRoundData = loadedData.allRoundData;
}
else
{
Debug.LogError("Cannot load game data!");
}
}
// This function could be extended easily to handle any additional data we wanted to store in our PlayerProgress object
private void LoadPlayerProgress()
{
// Create a new PlayerProgress object
playerProgress = new PlayerProgress();
// If PlayerPrefs contains a key called "highestScore", set the value of playerProgress.highestScore using the value associated with that key
if (PlayerPrefs.HasKey("highestScore"))
{
playerProgress.highestScore = PlayerPrefs.GetInt("highestScore");
}
}
// This function could be extended easily to handle any additional data we wanted to store in our PlayerProgress object
private void SavePlayerProgress()
{
// Save the value playerProgress.highestScore to PlayerPrefs, with a key of "highestScore"
PlayerPrefs.SetInt("highestScore", playerProgress.highestScore);
}
}
I am starting to go through tutorials of unity myself so I am not an expert. :)
But what I would try is the first thing. using System.IO; not sure if this will work to get files on android because android has a different file structure. So I would first remove it and sort of hard code the file path or comment out the code using System.IO classes then recompile apk in unity and check if it works. I also saw this post : http://answers.unity3d.com/questions/1023391/systemio-dont-work-on-android.html
If that did not work I would comment functionality out and compile apk and check if its working if its not comment more code out until you find the line or the code that causes it to error on android. This method takes long to troubleshoot or get your code causing the problem but for me this has worked before.
I am guessing as it is working on you pc its a class or something its referencing that's not available in android.
Please share your findings if you figure out what part of the code does it. As I would also want to know to prevent me from doing it. :)
Avoid the use of System.IO on Android and in general on Unity.
Use "Resources" instead, just following this steps:
Create a folder called "Resources"
Move json file on it and rename in .txt
Use this code to get the string:
var file = Resources.Load("filename_here") as TextAsset;
Debug.Log(file.text)
I am trying to build an augmented reality app and in that app i want some prefabs to load dynamically. So that with some 3d model prefabs on unity store i created myasset.unity3d . I am using WWW.LoadFromCacheOrDownload to download that unity3d file and using it.
When i am debugging in my laptop the file is downloading and i am using it as i want but when i add build in and try it on android phone download is not happening.
The code i am using to download is
IEnumerator DownloadAndCache() {
while (!Caching.ready)
yield return null;
string bundleURL = "http://s3-ap-southeast-1.amazonaws.com/cf-export/sofa.unity3d";
www = WWW.LoadFromCacheOrDownload (bundleURL, 1);
yield return www;
Debug.Log (www.assetBundle);
AssetBundle bundle = www.assetBundle;
}
So please help me .
i also tried other way of doing it but still i did not succeed
IEnumerator Start () {
WWW www = WWW.LoadFromCacheOrDownload (BundleURL, 1);
yield return www;
AssetBundle bundle = www.assetBundle;
AssetBundleRequest request = bundle.LoadAssetAsync (AssetName, typeof(GameObject));
yield return request;
GameObject obj = request.asset as GameObject;
Instantiate (obj);
bundle.Unload(false);
www.Dispose();
}
The above two methods are working in when testing in unity IDE but if i build using android and install it in android device its not working. I gave internet access to app while building
I printed the error using a text in mobile and please check the screen shot attached below
the first error text is www (variable for downloading) and second is www.error ( actual error). i did not get what that error meant. So please help me
the issue is asset bundle version is not sync with app version. So while making asset bundle i added small code which made my code working. To make Asset bundle my code is
using UnityEditor;
public class CreateAssetBundles
{
[MenuItem ("Assets/Build AssetBundles")]
static void BuildAllAssetBundles ()
{
BuildPipeline.BuildAssetBundles ("Assets/AssetBundles", BuildAssetBundleOptions.None, BuildTarget.Android);
}
}
To use AssetBuundle for Mobile platform:
While creating AssetBundle it must be platform specific. For Android use following Script to create AssetBundle. put it inside "Editor"
using UnityEngine;
using System.Collections;
using UnityEditor;
public class ExportAssetBundles : Editor {
[MenuItem("Assets/Build AssetBundle")]
static void ExportResource()
{
string path = "Assets/AssetBundle/myAssetBundle.unity3d";
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path,
BuildAssetBundleOptions.CollectDependencies
| BuildAssetBundleOptions.CompleteAssets,BuildTarget.Android);
}
}
Then Create folder "AssetBundle" inside Asset
When you create Assetbundle the AssetBundle.unity3d file will store inside this folder
Now you need to use Created Assetbundle at runtime. For this,
Write a script AssetBundleAugmenter
public class AssetBundleAugmenter : MonoBehaviour {
public string AssetName;
public int Version;
private GameObject mBundleInstance = null;
private bool mAttached = false;
void Start() {
StartCoroutine(DownloadAndCache());
}
// Update is called once per frame
IEnumerator DownloadAndCache() {
while(!Caching.ready)
yield return null;
//you can use remote URL like: www.arprabhu.com/assetBundle OR Local URL
// example URL of file on PC filesystem (Windows)
// string bundleURL = "file:///D:/Unity/AssetBundles/MyAssetBundle.unity3d";
// example URL of file on Android device SD-card
string bundleURL = System.IO.File.ReadAllText("/mnt/sdcard/filepath.txt");
Debug.Log ("Asset Loaded");
using (WWW www = WWW .LoadFromCacheOrDownload(bundleURL, Version)) {
yield return www;
if (www .error != null)
throw new UnityException("WWW Download had an error: " + www .error);
AssetBundle bundle = www .assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.LoadAsset(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
}
}}
Attach this Script to Empty GameObject in your Scene
Congrats!, you are ready to go.
Try checking your androidManifest.xml for the permission to connect to external servers.
Then, if everything seems alright and still doesn't work, my advice is to download the Device Console plugin for the Unity Editor, which is a really nice way to access the internal Android Debugger.
https://www.assetstore.unity3d.com/en/#!/content/44935
Or if you're truly brave: use logcat within the terminal...
I use saveToFile method to save screenshot, and I check CCLOG, it shows /data/data/com.xxx.xxx/files/xxx.png
However, when I use Java to get this file, it says "No such file or directory"....
What can I do?
I had the same problem. Apparently, cocos2dx never creates the writable path directory so the screenshot doesn't get saved.
My solution was to, instead of calling CCRenderTexture::saveToFile, copy its implementation and manually save the file to a different path:
std::string MyClass::takeScreenshot(CCScene* scene) {
cocos2d::CCSize screenSize = cocos2d::CCDirector::sharedDirector()->getWinSize();
cocos2d::CCRenderTexture* tex = cocos2d::CCRenderTexture::create(screenSize.width, screenSize.height);
tex->setPosition(ccp(screenSize.width/2, screenSize.height/2));
//use a different dir than cocos default writable path
std::string filename = "/sdcard/Android/data/screenshot.png";
tex->begin();
scene->getParent()->visit();
cocos2d::CCImage* img = tex->newCCImage(true);
img->saveToFile(filename.c_str(), true);
CC_SAFE_DELETE(img);
tex->end();
return filename;
}
I also had the same problem. When I try renderTexture->saveToFile method, I can't submit path to java. No matter, I used getWritablePath() or set the path directly "/sdcard/Android/data/com.xxx.xxx/snapshot.jpg".
I try also to solve this problem on the advice of Facundo Olano. But CCImage compose only solid black image.
Finally I combine this two methods. First, I make an image by renderTexture->saveToFile:
void MyScene::pressFaceBook()
{
...
RenderTexture* renderTexture = RenderTexture::create(sharedImage->getBoundingBox().size.width, sharedImage->getBoundingBox().size.height, Texture2D::PixelFormat::RGBA8888);
renderTexture->begin();
sharedImage->visit();
renderTexture->end();
renderTexture->saveToFile("snapshot.jpg", Image::Format::JPG);
const std::string fullpath = FileUtils::getInstance()->getWritablePath() + "snapshot.jpg";
sharedImage->setVisible(false);
this->runAction(Sequence::create(DelayTime::create(2.0f),
CallFunc::create(std::bind(&MyScene::shareFaceBook, this, fullpath)),
NULL));
}
And then I use initWithImageFile with the transferred file name:
void MyScene::shareFaceBook(const std::string& outputFile)
{
const std::string joutputFile = "/sdcard/Android/data/com.xxx.xxx/snapshot.jpg";
cocos2d::Image* img = new Image;
img->initWithImageFile(fullpath);
img->saveToFile(joutputFile);
CC_SAFE_DELETE(img);
singleton->shareFaceBook(joutputFile);
}
A delay of 2 seconds is needed to capture the screenshot.
Of course, instead com.xxx.xxx you must substitute your app name.
I am currently developing an Android application using Flex 4.5.1 and I am having an issue when trying to pass data that I have stored in a SharedObject array to my Web Service for a Database query. the code below shows how I am storing the data in the SharedObject:
var so:SharedObject = SharedObject.getLocal("app");
public var prefsArray:ArrayCollection = new ArrayCollection(so.data.prefs);
protected function prefs_btn_click(event:MouseEvent):void
{
prefsArray.source.push(getFrsByIDResult.lastResult.id);
so.data.prefs = [prefsArray];
var flushStatus:String = so.flush();
if (flushStatus != null) {
switch(flushStatus) {
case SharedObjectFlushStatus.PENDING:
so.addEventListener(NetStatusEvent.NET_STATUS,
onFlushStatus);
break;
case SharedObjectFlushStatus.FLUSHED:
trace("success");
break;
}
}
}
protected function onFlushStatus(event:NetStatusEvent):void
{
trace(event.info.code);
}
I have tested the SharedObject to see if the information is being entered into it correctly and all seems fine. Now I have used the code below in order to retrieve the data from the SharedObject and try and send it to the PHP web Service to run the DB query.
var so:SharedObject = SharedObject.getLocal("app");
var arrCol:ArrayCollection = new ArrayCollection(so.data.prefs);
var str:String = new String(arrCol.toString());
protected function list_creationCompleteHandler(event:FlexEvent):void
{
getPrefsByprefIdsResult.token = prefsService.getPrefsByPrefIds(so.data.prefs);
}
I have tested the Webservice in Flex and have it configured to recieve an Array of Ints (int[]) and it works when i run a test operation on it with two dummy values. However when I try to use the code above to pass the Web Service the Shared Object data I get this error:
TypeError: Error #1034: Type Coercion failed: cannot convert []#97e97e1 to mx.collections.ArrayCollection.
at views::**************/list_creationCompleteHandler()[C:\Users\Jack\Adobe Flash Builder 4.5\****************\src\views\*******************.mxml:25]
at views::*********************/__list_creationComplete()[C:\Users\Jack\Adobe Flash Builder 4.5\****************\src\views\***************.mxml:94]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:13128]
at mx.core::UIComponent/set initialized()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:1818]
at mx.managers::LayoutManager/validateClient()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1090]
at mx.core::UIComponent/validateNow()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:8067]
at spark.components::ViewNavigator/commitNavigatorAction()[E:\dev\4.5.1\frameworks\projects\mobilecomponents\src\spark\components\ViewNavigator.as:1878]
at spark.components::ViewNavigator/commitProperties()[E:\dev\4.5.1\frameworks\projects\mobilecomponents\src\spark\components\ViewNavigator.as:1236]
at mx.core::UIComponent/validateProperties()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:8209]
at mx.managers::LayoutManager/validateProperties()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:597]
at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:783]
at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1180]
I have replaced certain filenames and locations with *'s to protect the work i am doing, but can someone please help me with this issues as I believe it has to be something simple???
Thanks
ok so let me explain in more detail. This is being designed for an Android app like I said, but image what I am trying to do is to store Bookmarks persistently using the Local Shared Object.
The first chunck of code you see above is designed to create the LSO attribute for the bookmark i want to create and imagine that there can be more than one bookmark set at different times like in a web browser. The only way i could find to do this was to store these items/details in an array which I retrieve and then update before saving back to the LSO and saving.
The second piece of code related to imagine a "Bookmarks Page" with a list of all the content that I have bookmarked. Now what I wanted to happen was thta I would be able to call up the LSO attribute which held the id's of the bookmarks and then load up thier details in a list format.
I have managed to create the LSO and store the bookmark deatils in and allow them to be updated and entries added. Also I have made sure that the PHP code that I have pulls back all the database objects relating to the array of id's and this has been tested using flex. The only thing that I cant seem to do is to pass the id's to the PHP web service file. The code in the Web Service file is below if that helps:
public function getPrefsByPrefIds($PrefIds) {
$stmt = mysqli_prepare($this->connection, "SELECT * FROM $this->tablename WHERE $this->tablename.id IN(" .implode(",", $PrefIds). ")");
$this->throwExceptionOnError();
mysqli_stmt_execute($stmt);
$this->throwExceptionOnError();
$rows = array();
mysqli_stmt_bind_result($stmt, $row->id, $row->name, $row->desc);
while (mysqli_stmt_fetch($stmt)) {
$rows[] = $row;
$row = new stdClass();
mysqli_stmt_bind_result($stmt, $row->id, $row->name, $row->desc);
}
mysqli_stmt_free_result($stmt);
mysqli_close($this->connection);
return $rows;
}
Yes I had already tried that but thanks. I have made some more progress on my own as I have been experimenting with the different types of objects that can be stored in SharedObjects. I have managed to get the solution part working with this code:
This code is designed to capture the boomark info and store it in an arrayCollection before transferring it to a bytesArray and saving
var so:SharedObject = SharedObject.getLocal("app");
public var prefArray:ArrayCollection = new ArrayCollection(so.data.prefs);
protected function prefs_btn_click(event:MouseEvent):void
{
prefArray.source.push(getCompaniesByIDResult.lastResult.id);
so.data.prefs = [prefArray];
var bytes:ByteArray = new ByteArray();
bytes.writeObject(prefArray);
so.data.ac = bytes;
var flushStatus:String = so.flush();
if (flushStatus != null) {
switch(flushStatus) {
case SharedObjectFlushStatus.PENDING:
so.addEventListener(NetStatusEvent.NET_STATUS,
onFlushStatus);
break;
case SharedObjectFlushStatus.FLUSHED:
trace("success");
break;
}
}
}
protected function onFlushStatus(event:NetStatusEvent):void
{
trace(event.info.code);
}
This next code is the designed to retrieve that information from the SahredObjects bytesArray and put it back into an Array Collection
var so:SharedObject = SharedObject.getLocal("app");
var ba:ByteArray = so.data.ac as ByteArray;
var ac:ArrayCollection;
protected function list_creationCompleteHandler(event:FlexEvent):void
{
ba.position = 0;
ac = ba.readObject() as ArrayCollection;
getPrefsByPrefIdsResult.token = prefsService.getPrefsByPrefIds(ac);
}
however as I have said this works in a small way only as if I store only one Bookmark (id) for an item and then go to the bookmarks list the details for that bookark are successfully retrieved, however if I save more than one Bookmark(2 or more id's) the page will not load the details, i do not get an error but I believe it is hanging because it is looking for say id's "1,2" instead of "1" and "2" but i dont know why this is or how to resolve this. I appreciate the advice I have been given but am finding it hard there is no one who can help me with this issue and I am having to do various experiemnts with the code. Can someone please help me with this I would really appreciate it :-) Thanks