From ea08e3802c460fcd5d1dceb2579547656c10fd8e Mon Sep 17 00:00:00 2001 From: "nola.donato@samsung.com" Date: Tue, 22 Nov 2016 19:47:36 -0800 Subject: [PATCH] Fixed multi-threaded asset loader problem GVRAssetLoader was adding a listener per thread to GVRContext for asset events. GearVRF event manager only permits one listener per target object for a particular event class. I modified the asset loader to not rely on the event manager for propagating internal asset load events. GVRAssetLoader no longer adds listeners for its internal event management, making direct calls whenever possible. To do asset loading from multiple threads you have to add a user handler to the loadModel function. This API was already there, I just made it avoid the event manager. I also gave embedded textures their own cache. I tried using the ResourceCache used for texture loading but this would not reliably cache stuff - it sometimes reported something as not cached when it actually was. Not sure what is wrong with this - we probably should look closely at texture caching to see if it actually still works. --- .../main/java/org/gearvrf/GVRAssetLoader.java | 301 ++++++++---------- .../java/org/gearvrf/GVREventReceiver.java | 4 +- .../java/org/gearvrf/GVRJassimpAdapter.java | 10 +- .../main/java/org/gearvrf/x3d/X3Dobject.java | 2 +- 4 files changed, 146 insertions(+), 171 deletions(-) diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRAssetLoader.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRAssetLoader.java index 7a2b3f592..a2404f92d 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRAssetLoader.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRAssetLoader.java @@ -25,6 +25,8 @@ import java.io.OutputStream; import java.net.URL; import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; @@ -83,6 +85,9 @@ public final class GVRAssetLoader { /** * Loads textures and listens for texture load events. * Raises the "onAssetLoaded" event after all textures have been loaded. + * This listener is NOT attached to the event manager. It is explicitly + * called by GVRAssetLoader to get around the restriction that GVRContext + * can only have a single listener for asset events. */ public static class AssetRequest implements IAssetEvents { @@ -109,7 +114,6 @@ public AssetRequest(GVRContext context, String filePath) mFileName = filePath; mUserHandler = null; mErrors = ""; - mContext.getEventReceiver().addListener(this); mVolume = new GVRResourceVolume(mContext, filePath); Log.d(TAG, "ASSET: loading %s ...", mFileName); } @@ -131,7 +135,6 @@ public AssetRequest(GVRSceneObject model, String filePath, GVRScene scene, boole mModel = null; mErrors = ""; mReplaceScene = replaceScene; - mContext.getEventReceiver().addListener(this); mVolume = new GVRResourceVolume(mContext, filePath); Log.d(TAG, "ASSET: loading %s ...", mFileName); } @@ -142,7 +145,8 @@ public AssetRequest(GVRSceneObject model, String filePath, GVRScene scene, boole * @param filePath path to file * @param userHandler user event handler to get asset events. */ - public AssetRequest(GVRSceneObject model, String filePath, IAssetEvents userHandler) { + public AssetRequest(GVRSceneObject model, String filePath, IAssetEvents userHandler) + { mScene = null; mContext = model.getGVRContext();; mNumTextures = 0; @@ -150,12 +154,7 @@ public AssetRequest(GVRSceneObject model, String filePath, IAssetEvents userHand mUserHandler = userHandler; mModel = null; mErrors = ""; - mContext.getEventReceiver().addListener(this); mVolume = new GVRResourceVolume(mContext, filePath); - if (userHandler != null) - { - mContext.getEventReceiver().addListener(userHandler); - } Log.d(TAG, "ASSET: loading %s ...", mFileName); } @@ -242,33 +241,40 @@ public Future loadFutureTexture(TextureRequest request) * @param aitex Assimp texture containing the pixel data * @return GVRTexture made from embedded texture */ - public GVRTexture loadEmbeddedTexture(TextureRequest request, AiTexture aitex, GVRTextureParameters texParams) throws IOException + public GVRTexture loadEmbeddedTexture(final TextureRequest request, final AiTexture aitex, final GVRTextureParameters texParams) throws IOException { - Bitmap bmap = null; GVRAndroidResource resource = new GVRAndroidResource(request.TextureFile); - GVRTexture texture = mContext.getAssetLoader().findTexture(resource); + GVRBitmapTexture bmapTex; - if (texture != null) - { - return texture; - } - synchronized (mNumTextures) - { - ++mNumTextures; - Log.d(TAG, "ASSET: loadEmbeddedTexture %s %d", request.TextureFile, mNumTextures); - } - if (aitex.getHeight() == 0) - { - ByteArrayInputStream input = new ByteArrayInputStream(aitex.getByteData()); - bmap = BitmapFactory.decodeStream(input); - } - else + Log.d(TAG, "ASSET: loadEmbeddedTexture %s %d", request.TextureFile, mNumTextures); + Map texCache = GVRAssetLoader.getEmbeddedTextureCache(); + synchronized (texCache) { - bmap = Bitmap.createBitmap(aitex.getWidth(), aitex.getHeight(), Bitmap.Config.ARGB_8888); - bmap.setPixels(aitex.getIntData(), 0, aitex.getWidth(), 0, 0, aitex.getWidth(), aitex.getHeight()); + GVRTexture tex = texCache.get(request.TextureFile); + if (tex != null) + { + Log.d(TAG, "ASSET: loadEmbeddedTexture found %s", resource.getResourceFilename()); + return tex; + } + synchronized (mNumTextures) + { + ++mNumTextures; + } + Bitmap bmap; + if (aitex.getHeight() == 0) + { + ByteArrayInputStream input = new ByteArrayInputStream(aitex.getByteData()); + bmap = BitmapFactory.decodeStream(input); + } + else + { + bmap = Bitmap.createBitmap(aitex.getWidth(), aitex.getHeight(), Bitmap.Config.ARGB_8888); + bmap.setPixels(aitex.getIntData(), 0, aitex.getWidth(), 0, 0, aitex.getWidth(), aitex.getHeight()); + } + bmapTex = new GVRBitmapTexture(mContext, bmap, texParams); + Log.d(TAG, "ASSET: loadEmbeddedTexture saved %s", resource.getResourceFilename()); + texCache.put(request.TextureFile, bmapTex); } - GVRBitmapTexture bmapTex = new GVRBitmapTexture(mContext, bmap, texParams); - mContext.getAssetLoader().cacheTexture(resource, bmapTex); request.loaded(bmapTex, resource); return bmapTex; } @@ -282,6 +288,13 @@ public GVRTexture loadEmbeddedTexture(TextureRequest request, AiTexture aitex, G public void onModelLoaded(GVRContext context, GVRSceneObject model, String modelFile) { mModel = model; Log.d(TAG, "ASSET: successfully loaded model %s %d", modelFile, mNumTextures); + if (mUserHandler != null) + { + mUserHandler.onModelLoaded(context, model, modelFile); + } + mContext.getEventManager().sendEvent(mContext, + IAssetEvents.class, + "onModelLoaded", new Object[]{mContext, model, modelFile}); if (mNumTextures == 0) { generateLoadEvent(); @@ -300,6 +313,12 @@ public void onModelLoaded(GVRContext context, GVRSceneObject model, String model */ public void onTextureLoaded(GVRContext context, GVRTexture texture, String texFile) { + if (mUserHandler != null) + { + mUserHandler.onTextureLoaded(context, texture, texFile); + } + mContext.getEventManager().sendEvent(mContext, IAssetEvents.class, + "onTextureLoaded", new Object[] { mContext, texture, texFile }); synchronized (mNumTextures) { Log.e(TAG, "ASSET: successfully loaded texture %s %d", texFile, mNumTextures); @@ -330,7 +349,14 @@ public void onTextureLoaded(GVRContext context, GVRTexture texture, String texFi public void onModelError(GVRContext context, String error, String modelFile) { Log.e(TAG, "ASSET: ERROR: model %s did not load %s", modelFile, error); - mErrors += "Model " + modelFile + " did not load " + error + "\n"; + if (mUserHandler != null) + { + mUserHandler.onModelError(context, error, modelFile); + } + mContext.getEventManager().sendEvent(mContext, + IAssetEvents.class, + "onModelError", new Object[] { mContext, error, modelFile }); + mErrors += error + "\n"; mModel = null; mNumTextures = 0; generateLoadEvent(); @@ -344,7 +370,13 @@ public void onModelError(GVRContext context, String error, String modelFile) */ public void onTextureError(GVRContext context, String error, String texFile) { - mErrors += "Texture " + texFile + " did not load " + error + "\n"; + mErrors += error + "\n"; + if (mUserHandler != null) + { + mUserHandler.onTextureError(context, error, texFile); + } + mContext.getEventManager().sendEvent(mContext, IAssetEvents.class, + "onTextureError", new Object[] { mContext, error, texFile }); synchronized (mNumTextures) { if (mNumTextures >= 1) @@ -375,7 +407,12 @@ public void onTextureError(GVRContext context, String error, String texFile) @Override public void onAssetLoaded(GVRContext context, GVRSceneObject model, String modelFile, String errors) { - mContext.getEventReceiver().removeListener(this); + if (mUserHandler != null) + { + mUserHandler.onAssetLoaded(context, model, modelFile, errors); + } + mContext.getEventManager().sendEvent(mContext, IAssetEvents.class, + "onAssetLoaded", new Object[] { mContext, mModel, mFileName, errors }); } private void generateLoadEvent() @@ -410,12 +447,7 @@ private void generateLoadEvent() mScene.addSceneObject(mModel); } } - mContext.getEventManager().sendEvent(mContext, IAssetEvents.class, - "onAssetLoaded", new Object[] { mContext, mModel, mFileName, errors }); - if (mUserHandler != null) - { - mContext.getEventReceiver().removeListener(mUserHandler); - } + onAssetLoaded(mContext, mModel, mFileName, errors); } } @@ -425,21 +457,21 @@ private void generateLoadEvent() public static class TextureRequest implements TextureCallback { public final String TextureFile; - protected final GVRContext mContext; protected GVRTextureParameters mTexParams; + protected AssetRequest mAssetRequest; private boolean loadFinished; - public TextureRequest(GVRContext context, String texFile, final GVRTextureParameters texParams) + public TextureRequest(AssetRequest assetRequest, String texFile, final GVRTextureParameters texParams) { - mContext = context; + mAssetRequest = assetRequest; TextureFile = texFile; mTexParams = texParams; loadFinished = false; } - public TextureRequest(GVRContext context, String texFile) + public TextureRequest(AssetRequest assetRequest, String texFile) { - mContext = context; + mAssetRequest = assetRequest; TextureFile = texFile; mTexParams = GVRAssetLoader.DEFAULT_TEXTURE_PARAMETERS; loadFinished = false; @@ -447,7 +479,7 @@ public TextureRequest(GVRContext context, String texFile) public void loaded(final GVRTexture texture, GVRAndroidResource resource) { - mContext.runOnGlThread(new Runnable() + mAssetRequest.getContext().runOnGlThread(new Runnable() { public void run() { @@ -456,9 +488,7 @@ public void run() }); if (!loadFinished) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onTextureLoaded", new Object[] { mContext, texture, TextureFile }); + mAssetRequest.onTextureLoaded(mAssetRequest.getContext(), texture, TextureFile); } loadFinished = true; } @@ -468,11 +498,9 @@ public void failed(Throwable t, GVRAndroidResource androidResource) { if (!loadFinished) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onTextureError", new Object[]{mContext, t.getMessage(), TextureFile}); + mAssetRequest.onTextureError(mAssetRequest.getContext(), t.getMessage(), TextureFile); loadFinished = true; - loaded(getDefaultTexture(mContext), null); + loaded(getDefaultTexture(mAssetRequest.getContext()), null); } } @@ -491,17 +519,17 @@ public static class MaterialTextureRequest extends TextureRequest public final GVRMaterial Material; public final String TextureName; - public MaterialTextureRequest(GVRContext context, String texFile) + public MaterialTextureRequest(AssetRequest assetRequest, String texFile) { - super(context, texFile); + super(assetRequest, texFile); Material = null; TextureName = null; } - public MaterialTextureRequest(GVRContext context, String texFile, GVRMaterial material, String textureName, + public MaterialTextureRequest(AssetRequest assetRequest, String texFile, GVRMaterial material, String textureName, final GVRTextureParameters texParams) { - super(context, texFile, texParams); + super(assetRequest, texFile, texParams); Material = material; TextureName = textureName; if (Material != null) @@ -522,6 +550,8 @@ public void loaded(GVRTexture texture, GVRAndroidResource ignored) protected GVRContext mContext; protected static ResourceCache mTextureCache = new ResourceCache(); + protected static HashMap mEmbeddedCache = new HashMap(); + protected static GVRTexture mDefaultTexture = null; /** @@ -535,6 +565,7 @@ public void loaded(GVRTexture texture, GVRAndroidResource ignored) @Override public void run() { mTextureCache = new ResourceCache(); + mEmbeddedCache = new HashMap(); mDefaultTexture = null; } }); @@ -554,28 +585,22 @@ public GVRAssetLoader(GVRContext context) } /** - * Determine if a given texture is cached - * @param resource GVRAndroidResource describing the texture - * @return GVRTexture if texture has been cached, otherwise null - */ - GVRTexture findTexture(GVRAndroidResource resource) { return mTextureCache.get(resource); } - - /** - * Internal function to put a texture into the texture cache. - * This function is not for public consumption - it is used internally - * for maintaining the texture cache. - * - * @param resource GVRAndroidResource describing the texture - * @param texture GVRTexture to add to the cache + * Get the embedded texture cache. + * This is an internal routine used during asset loading for processing + * embedded textures. + * @return embedded texture cache */ - void cacheTexture(GVRAndroidResource resource, GVRTexture texture) { mTextureCache.put(resource, texture); } + static Map getEmbeddedTextureCache() + { + return mEmbeddedCache; + } private static GVRTexture getDefaultTexture(GVRContext ctx) { if (mDefaultTexture == null) { GVRAndroidResource r = new GVRAndroidResource(ctx, R.drawable.white_texture); - mDefaultTexture = ctx.loadTexture(r); + mDefaultTexture = ctx.getAssetLoader().loadTexture(r); } return mDefaultTexture; } @@ -614,25 +639,31 @@ private static GVRTexture getDefaultTexture(GVRContext ctx) public GVRTexture loadTexture(GVRAndroidResource resource, GVRTextureParameters textureParameters) { - GVRTexture texture = mTextureCache.get(resource); - if (texture != null) + GVRBitmapTexture bmapTex = null; + synchronized (mTextureCache) { - return texture; + GVRTexture texture = mTextureCache.get(resource); + if (texture != null) + { + return texture; + } + bmapTex = new GVRBitmapTexture(mContext, (Bitmap) null, textureParameters); + mTextureCache.put(resource, bmapTex); } try { Bitmap bitmap = GVRAsynchronousResourceLoader.decodeStream(resource.getStream(), false); resource.closeStream(); - texture = bitmap == null ? null : new GVRBitmapTexture(mContext, bitmap, textureParameters); - if (texture != null) + if (bitmap != null) { - mTextureCache.put(resource, texture); + bmapTex.update(bitmap); } } - catch (IOException ex) { + catch (IOException ex) + { return null; } - return texture; + return bmapTex; } public GVRTexture loadTexture(GVRAndroidResource resource) @@ -1169,33 +1200,26 @@ private GVRSceneObject loadJassimpModel(AssetRequest request, GVRSceneObject mod catch (IOException ex) { assimpScene = null; - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, ex.getMessage(), filePath }); + request.onModelError(mContext, ex.getMessage(), filePath); throw ex; - } + } - if (assimpScene == null) { + if (assimpScene == null) + { String errmsg = "Cannot load model from path " + filePath; - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, errmsg, filePath }); + request.onModelError(mContext, errmsg, filePath); throw new IOException(errmsg); } try { jassimpAdapter.processScene(request, model, assimpScene, volume); - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelLoaded", new Object[]{mContext, model, filePath}); + request.onModelLoaded(mContext, model, filePath); return model; } catch (IOException ex) { assimpScene = null; - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, ex.getMessage(), filePath }); + request.onModelError(mContext, ex.getMessage(), filePath); throw ex; } } @@ -1203,7 +1227,8 @@ private GVRSceneObject loadJassimpModel(AssetRequest request, GVRSceneObject mod GVRSceneObject loadX3DModel(GVRAssetLoader.AssetRequest assetRequest, GVRSceneObject root, EnumSet settings, - boolean cacheEnabled, GVRScene scene) throws IOException { + boolean cacheEnabled, GVRScene scene) throws IOException + { GVRResourceVolume volume = assetRequest.getVolume(); InputStream inputStream = null; String fileName = assetRequest.getBaseName(); @@ -1211,32 +1236,33 @@ GVRSceneObject loadX3DModel(GVRAssetLoader.AssetRequest assetRequest, root.setName(fileName); org.gearvrf.x3d.X3Dobject x3dObject = new org.gearvrf.x3d.X3Dobject(assetRequest, root); - try { - ShaderSettings shaderSettings = new ShaderSettings(new GVRMaterial(mContext)); - if (!X3Dobject.UNIVERSAL_LIGHTS) { + try + { + ShaderSettings shaderSettings = new ShaderSettings(new GVRMaterial(mContext)); + if (!X3Dobject.UNIVERSAL_LIGHTS) + { X3DparseLights x3dParseLights = new X3DparseLights(mContext, root); inputStream = resource.getStream(); - if (inputStream == null) { - throw new FileNotFoundException(fileName + " not found"); + if (inputStream == null) + { + throw new FileNotFoundException(fileName + " not found"); } Log.d(TAG, "Parse: " + fileName); x3dParseLights.Parse(inputStream, shaderSettings); inputStream.close(); - } - inputStream = resource.getStream(); - if (inputStream == null) { + } + inputStream = resource.getStream(); + if (inputStream == null) + { throw new FileNotFoundException(fileName + " not found"); - } - x3dObject.Parse(inputStream, shaderSettings); - inputStream.close(); - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelLoaded", new Object[] { mContext, root, fileName }); + } + x3dObject.Parse(inputStream, shaderSettings); + inputStream.close(); + assetRequest.onModelLoaded(mContext, root, fileName); } - catch (Exception ex) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, ex.getMessage(), fileName }); + catch (Exception ex) + { + assetRequest.onModelError(mContext, ex.getMessage(), fileName); throw ex; } return root; @@ -1301,57 +1327,6 @@ public static File downloadFile(Context context, String urlString) { return new File(outputFilename); } - GVRModelSceneObject loadX3DModel(GVRAssetLoader.AssetRequest assetRequest, - EnumSet settings, boolean cacheEnabled) - throws IOException { - GVRModelSceneObject root = new GVRModelSceneObject(mContext); - GVRResourceVolume volume = assetRequest.getVolume(); - InputStream inputStream = null; - String fileName = assetRequest.getBaseName(); - GVRAndroidResource resource = volume.openResource(fileName); - - org.gearvrf.x3d.X3Dobject x3dObject = new org.gearvrf.x3d.X3Dobject(assetRequest, root); - try { - ShaderSettings shaderSettings = new ShaderSettings(new GVRMaterial(mContext)); - if (!X3Dobject.UNIVERSAL_LIGHTS) { - X3DparseLights x3dParseLights = new X3DparseLights(mContext, root); - inputStream = resource.getStream(); - if (inputStream == null) { - throw new FileNotFoundException(fileName + " not found"); - } - Log.d(TAG, "Parse: " + fileName); - x3dParseLights.Parse(inputStream, shaderSettings); - inputStream.close(); - } - inputStream = resource.getStream(); - if (inputStream == null) { - throw new FileNotFoundException(fileName + " not found"); - } - x3dObject.Parse(inputStream, shaderSettings); - inputStream.close(); - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelLoaded", new Object[] { mContext, (GVRSceneObject) root, fileName }); - } - catch (FileNotFoundException e) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, e.getMessage(), fileName }); - } - catch (IOException e1) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, e1.getMessage(), fileName }); - } - catch (Exception e2) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, e2.getMessage(), fileName }); - e2.printStackTrace(); - } - return root; - } - /** * State-less, should be fine having one instance */ diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVREventReceiver.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVREventReceiver.java index 44c7d1f41..fd0bf527c 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVREventReceiver.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVREventReceiver.java @@ -37,7 +37,9 @@ * this {@link GVREventReceiver} object. * * After implementing the above pattern, {@link GVREventManager#sendEvent(Object, Class, String, Object...)} - * can be used to deliver events to the class. + * can be used to deliver events to the class. Note that a target object can only have + * one listener for each event class. Subsequent listeners for the same target and class + * are ignored. */ public class GVREventReceiver { protected IEventReceiver mOwner; diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java index 9726d6f53..9eacc5eda 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/GVRJassimpAdapter.java @@ -578,21 +578,19 @@ private void loadTextures(GVRAssetLoader.AssetRequest assetRequest, AiMaterial m int texIndex = parseInt(texFileName.substring(1)); tex = mScene.getTextures().get(texIndex); - GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest.getContext(), mFileName + texFileName, meshMaterial, textureKey, texParams); + GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest, mFileName + texFileName, meshMaterial, textureKey, texParams); assetRequest.loadEmbeddedTexture(texRequest, tex, texParams); } catch (NumberFormatException | IndexOutOfBoundsException ex) { - mContext.getEventManager().sendEvent(mContext, - IAssetEvents.class, - "onModelError", new Object[] { mContext, ex.getMessage(), assetRequest.getFileName() }); + assetRequest.onModelError(mContext, ex.getMessage(), mFileName); } - GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest.getContext(), mFileName + texFileName, meshMaterial, textureKey, texParams); + GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest, mFileName + texFileName, meshMaterial, textureKey, texParams); assetRequest.loadEmbeddedTexture(texRequest, tex, texParams); } else { - GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest.getContext(), texFileName, meshMaterial, textureKey, texParams); + GVRAssetLoader.TextureRequest texRequest = new GVRAssetLoader.MaterialTextureRequest(assetRequest, texFileName, meshMaterial, textureKey, texParams); assetRequest.loadTexture(texRequest); } } diff --git a/GVRf/Framework/framework/src/main/java/org/gearvrf/x3d/X3Dobject.java b/GVRf/Framework/framework/src/main/java/org/gearvrf/x3d/X3Dobject.java index fdcda8dbd..f5a3de908 100644 --- a/GVRf/Framework/framework/src/main/java/org/gearvrf/x3d/X3Dobject.java +++ b/GVRf/Framework/framework/src/main/java/org/gearvrf/x3d/X3Dobject.java @@ -1279,7 +1279,7 @@ else if (qName.equalsIgnoreCase("ImageTexture")) } final String defValue = attributes.getValue("DEF"); - GVRAssetLoader.TextureRequest request = new GVRAssetLoader.TextureRequest(gvrContext, + GVRAssetLoader.TextureRequest request = new GVRAssetLoader.TextureRequest(assetRequest, filename, gvrTextureParameters); Future texture = assetRequest .loadFutureTexture(request);