expo-three icon indicating copy to clipboard operation
expo-three copied to clipboard

LoadTextureAsync not working with standalone app

Open Trancoso opened this issue 5 years ago • 15 comments

Hi I’m realizing a project where I have to realize a 3D earth with Expo-THREE but i would like to give a texture to my sphere . However, I only achieved this on development but with a standalone app (android) the texture doesn’t download. I have a black sphere. On IOS, everything goes well in development and with Testfligt app. I do like that to render my sphere :

const terre = require("../../../../assets/images/panorama.png")    
    scene = new THREE.Scene();
    const light = new THREE.DirectionalLight(new THREE.Color(0xffffff), 1);
    light.position.set(6, 6, 8);
    scene.add(light)
    camera = new THREE.PerspectiveCamera(
      75,
      gl.drawingBufferWidth / gl.drawingBufferHeight,
      0.1,
      1000
    );
    renderer = new ExpoTHREE.Renderer({ gl });
    renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);

    const texture = await ExpoTHREE.loadTextureAsync({asset: terre})
    const geometry = new THREE.SphereBufferGeometry(GLOBE_RADIUS, 128, 128);
    const material = await new THREE.MeshLambertMaterial({
      map: texture
    });
    const sphere = new THREE.Mesh(geometry, material);
    scene.add(sphere);
    camera.position.set(6, 6, 8);

I also tried to replace my local uri by a picture online but i had the same issue as before.

I also try to realize this issue :

  • https://github.com/expo/expo-three/issues/10#issuecomment-350186269
  • https://github.com/expo/expo/issues/540#issuecomment-327309618
  • https://github.com/expo/expo/issues/2693#issuecomment-488631357

but after tried this , the result was the same for the standalone app.

My config file :

App.json : { "expo": { "name": "name", "slug": "name-app", "privacy": "public", "sdkVersion": "32.0.0", "platforms": [ "ios", "android" ], "version": "2.0.0", "orientation": "portrait", "icon": ./launcher.png", "splash": { "image": "./splash.png", "resizeMode": "contain", "backgroundColor": "#ffdc00" }, "updates": { "fallbackToCacheTimeout": 0 }, "assetBundlePatterns": [ "/" ], "android": { "package": "org..***", "versionCode": 23, "googleServicesFile": "./google-services.json", "permissions": [ "ACCESS_COARSE_LOCATION", "ACCESS_FINE_LOCATION", "CAMERA", "READ_CONTACTS" ] } } }

Mon Package.json : { "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", "eject": "expo eject" }, "dependencies": { "d3-geo": "^1.11.3", "expo": "^32.0.0", "expo-asset-utils": "^1.0.0", "expo-face-detector": "^4.0.0", "expo-graphics": "^1.0.3", "expo-three": "^3.0.0-alpha.8", "react": "16.5.0", "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz", "three": "^0.96.0" }, "devDependencies": { "babel-preset-expo": "^5.0.0" }, "private": true }

Can you tell me what can I do ? Thanks a lot !

Trancoso avatar May 30 '19 09:05 Trancoso

In prod with adb logcat i can see this error : "THREE.WebGLState exgl invalid pixel data argument for gl textImage2D()!". Could it be our problem ?

Trancoso avatar Jun 04 '19 08:06 Trancoso

Hello, I work with @Trancoso and was able to find a workaround for our problem. Using adb logcat with a production build and logging the texture returned by ExpoTHREE.loadAsync I noticed that the asset localUri started with asset://. Then using https://github.com/expo/expo/issues/2693#issuecomment-488631357 to get a file:// uri for my asset, I overwrote the texture image localUri with the uri. So far it works fine. Here is the code:

const uri =  await this.copyAssetToCacheAsync(require('../../../../assets/images/panorama.png'),'panorama.png');
  const texture = await ExpoTHREE.loadAsync(uri);
  texture.image.data.localUri = texture.image.data.uri;
  const material = new THREE.MeshBasicMaterial({
    map: texture
  });

  const geometry = new THREE.SphereBufferGeometry(GLOBE_RADIUS, 128, 128);
  const sphere = new THREE.Mesh(geometry, material);
  scene.add(sphere);

Where copyAssetToCacheAsync function is the one provided by adbl in the linked issue.

bonbertr avatar Jun 05 '19 08:06 bonbertr

Thanks @bonbertr for the work around!

After some more digging, I think the root cause could be here, the native C++ implementation of expo-gl: https://github.com/expo/expo/blob/master/packages/expo-gl-cpp/cpp/UEXGL.cpp

The loadImage() function has a special case to handle Assets passed in the form of JavaScript objects with a .localUri property. However, it only handles the file:// prefix and fails to handle the asset:// prefix (which only appears for assets loaded in standalone Android builds).

danmaas avatar Aug 21 '19 21:08 danmaas

Hey everyone.. i was having a lot of trouble with expo-three when trying to load an .obj i will post here what me and my colleges have done to work around this. Every time that i tried to load an .obj using loadAsync or loadObjAsync the require method to load static assets always resolve to undefined.

The solution was create a metro.config.js in your root project folder adding assetExtensions like obj and mtl like this:

module.exports = {
	resolver: {
		assetExts: ["db", "mp3", "ttf", "obj", "png", "jpg", "mtl"]
	}
};

and then in your app.json point to the new file

"packagerOpts": {
			"config": "metro.config.js"
}

I'm posting this in case anyone pass trough the same hell

tauanbinato avatar Feb 20 '20 18:02 tauanbinato

@bonbertr 's workaround is not working anymore after upgrading from Expo 43 to 45. End up with a black textured object. Non-textured objects render just fine. I was able to fix this by skipping the local cache thing and loading the texture from the module resource, but this doesn't obviously work on Android in production builds. Anyone else bumped into this? I've now spent two workdays trying to find the root cause to no avail. My current hypothesis is that TextureLoader cannot load from local URIs anymore.

pehagg avatar May 13 '22 05:05 pehagg

Perhaps try one of my polyfills via expo-asset:

  • https://gist.github.com/CodyJasonBennett/e5cce5854abb2324459601da5f14bb44

This is what https://github.com/pmndrs/react-three-fiber uses ATM to unify loaders with web.

CodyJasonBennett avatar May 25 '22 23:05 CodyJasonBennett

Off-topic: we ended up ditching expo-three altogether. Too frustrated with all the issues we had every time Expo was updated. Was never able to fix the texture loading issue, which was the last nail in the coffin.

pehagg avatar May 26 '22 04:05 pehagg

I know the feeling XD. Out of curiosity, are you still using React Native at all, and if so with what graphics API? Or did you go 100% native?

danmaas avatar May 26 '22 07:05 danmaas

I know the feeling XD. Out of curiosity, are you still using React Native at all, and if so with what graphics API? Or did you go 100% native?

We continue to use React Native. We just replaced the 3D content views with regular 2D views. The 3D views didn't sit well with the rest of the app's look and feel anyway.

pehagg avatar May 27 '22 11:05 pehagg

you need to change the file resolution. For example "test1.png" to "test1.xpng" or "test2.jpg " on "test2.xjpg".

const texture = await ExpoTHREE.loadTextureAsync({asset: require('../../../../assets/images/panorama.xpng')}); const material = new THREE.MeshBasicMaterial({ map: texture });

const geometry = new THREE.SphereBufferGeometry(GLOBE_RADIUS, 128, 128); const sphere = new THREE.Mesh(geometry, material); scene.add(sphere);

work for me sdk44 react-native 0.64.0

ssdenis00 avatar Sep 15 '22 05:09 ssdenis00

Has anyone seen success on this workaround recently? I'm stuck on my android build, IOS works fine.

"expo": "^46.0.0",
"expo-asset": "^8.7.0",
 "expo-file-system": "^15.1.1",
 "expo-font": "~10.2.0",
 "expo-gl": "~11.4.0",
 "expo-three": "^7.0.0",
 "react": "18.0.0",
 "react-dom": "18.0.0",
 "react-native": "0.69.6",
 "react-native-fs": "^2.20.0",
 "react-native-web": "~0.18.7",
 "three": "0.124.0",

Using the workaround @bonbertr mentioned I hit an issue during the ExpoTHREE.loadAsync call Error: Expected URL scheme 'http' or 'https' but was 'file'

I attempted downgrading three.js in hopes the loader lived there, but like @pehagg said it looks like the textureLoader is living in expo.

Any advice? I'm about three days in.

DominicBartel avatar Jan 10 '23 01:01 DominicBartel

@DominicBartel you can try this:

const asset = Asset.fromURI(localPath);
asset.localUri = localPath;
const texture = await ExpoTHREE.loadAsync(asset);

obasille avatar Jan 19 '23 17:01 obasille

I was having an issue where the model wouldnt load because "Expected URL scheme 'http' or 'https' but was 'file'". When I looked at the asset, I noticed that the localUri url scheme was file, but uri followed the http url scheme. Switching to that allowed me to load the model. There's probably a huge downside to this, but if you want the model to load on Android, there's this

phantom-factotum avatar Jan 27 '23 01:01 phantom-factotum

asset.localUri = localPath;

Doing this gave me the error: TypeError: Cannot read property 'elements' of undefined, js engine: hermes

Struggling so much with this bug

uakhundz avatar Mar 04 '23 21:03 uakhundz

in my case it's not a local path...

earonesty avatar Jul 28 '23 15:07 earonesty