Importing raw .OBJ string data with ImportMesh

Hey guys and gals,

I’ve been stuck on this issue for a couple of days now and it’s about time for some reinforcements if possible.

I’ve got a backend Spring Boot project that returns a String of raw .OBJ data as a blob to an angular front-end project. I console log the resulting string and I can see the .OBJ data, I’m testing on a hefty file.

This is the code:

    this.assetsService.getAsset(assetId)
      .subscribe(async (blob) => {
        const raw = await blob.text();
        console.log(raw); // prints .OBJ file
        BAB.SceneLoader.ImportMesh('', '', 'data:' + raw, this.scene,
          (newAssetMeshes, _, __, animationGroup) => {
            console.log(newAssetMeshes);
          },
          (val) => {
            this.snackBar.open(
              'Adding ' + assetId + ' to the scene. ' + (val.loaded / val.total).toFixed(2),
              null,
              { duration: 1500 }
            );
          },
          (err, msg) => {
            this.snackBar.open('Error ' + assetId + ' could not be added.');
            console.error(msg.slice(msg.length - 500));
            // console.error(err);
          });
      });

Basically I get the classic error:

importMesh of undefined from undefined version: undefined, exporter version: undefinedimportMesh has failed JSON parse

Any thoughts on this?

These are my imports in the file:

import { ElementRef, EventEmitter, Injectable, NgZone } from '@angular/core';
import { AssetService } from './asset.service';
import { AssetModel } from './model/asset.model';
import * as BAB from '@babylonjs/core';
import '@babylonjs/loaders';
import {AssetDownloadModel} from './model/asset-download.model';
import {MatSnackBar} from '@angular/material/snack-bar';
import {AssetInfoModel} from './model/asset-info.model';

Thank y’all for the help!

Not sure if this is the only way, but you can make a “file” from the blob and import it that way:
https://playground.babylonjs.com/#V98D08

2 Likes

Very elegant :slight_smile:

You could also convert the raw data to a base64 string and use data URLs to load it (we support data URLs everywhere), but the File solution is so much cleaner :slight_smile:

2 Likes

I’ve just tried this and I’m getting an error shown here:

Here’s the new code:

    this.assetsService.getAssetQuixelEntirety(assetId)
      .subscribe((blob: Blob) => {
        const file = new File([blob], 'newFile.obj');
        console.log(file);
        BAB.SceneLoader.ImportMesh('', '', file, this.scene,
          (newAssetMeshes, _, __, animationGroup) => {
            console.log(newAssetMeshes);
          },
          (val) => {
            this.snackBar.open(
              'Adding ' + assetId + ' to the scene. ' + (val.loaded / val.total).toFixed(2),
              null,
              { duration: 1500 }
            );
          },
          (err, msg) => {
            this.snackBar.open('Error ' + assetId + ' could not be added.');
            console.error(msg.slice(msg.length - 500));
            // console.error(err);
          });
      });

Same with this solution, I’m getting this error:

Code is as follows:

    this.assetsService.getAssetQuixelEntirety(assetId)
      .subscribe(async (blob: Blob) => {
        const raw = await blob.text();
        const encoded = btoa(raw);
        console.log(encoded);
        const url = URL.createObjectURL(encoded);
        BAB.SceneLoader.ImportMesh('', url, '', this.scene,
          (newAssetMeshes, _, __, animationGroup) => {
            console.log(newAssetMeshes);
          },
          (val) => {
            this.snackBar.open(
              'Adding ' + assetId + ' to the scene. ' + (val.loaded / val.total).toFixed(2),
              null,
              { duration: 1500 }
            );
          },
          (err, msg) => {
            this.snackBar.open('Error ' + assetId + ' could not be added.');
            console.error(msg.slice(msg.length - 500));
            // console.error(err);
          });
      });

Can you create a repro in the playground?

Sure I would love to but do you have a open resource link for importing a .OBJ file directly? Cause I’m currently using an API and it requires authentication.

You can try this technique Using External Assets In the Playground | Babylon.js Documentation

Are you referring to the Embedded Asset technique or one from an open link? Cause ideally I would have to import the file contents (in this case the contents of a .OBJ file) directly into the scene.

you can copy it in the playground, just take a tiny obj in this case

Would you be open to a zoom call so that I can show you quickly? Cause there’s a lot going on and I feel as though I can’t reproduce the exact environment, I have a Spring Boot backend which is retrieving from Quixel’s API. I’m sending the raw wavefront file data from the backend to an Angular frontend as a Blob using RxJs.

An example payload can be retrieved here: Chair Wavefront Data

What version of babylonjs are you using? I can replicate your error on my pg by switching to version 4.2, whereas under 5 it works with your obj too.

1 Like

I’m using version 4.2, which errors are you replicating?

On Babylon.js Playground if you switch to version 4.2 (right hand side) then you get the 404 error you posted in your first reply.

The one with the File([Blob]) technique? Does this mean I should try updating BabylonJs?

1 Like

Yes, and yes I think updating (if possible) should resolve the issue.

Alternatively, the method here works in 4.2 as well.

1 Like

image

Using the same methodology as the link you just posted, I get this error.

Code:

    fetch(environment.backend + 'getQuixelSceneAsset?quixelAssetId=' + assetDetails)
      .then((blob) => {
        const url = URL.createObjectURL(blob);
        BAB.SceneLoader.ImportMesh('', '', url, this.scene,
          (newAssetMeshes, _, __, animationGroup) => {
            console.log('success');
          }, null, null, '.obj');

Is this method supposed to work on a localhost server as well?

It shouldn’t make a difference. Does that fetch return a blob directly? Might need to call blob() on the returned value (as in the fetch in the pg).

If I understand you correctly the createObjectURL needs a polyfill in node - it’s meant for the browser. I think the fetch promise resolves to a response object that has a .blob() method and that you need to base64 encode that and then use that data as per here:

edit: have you tried loading with:
SceneLoader.ImportMesh(undefined, environment.backend, 'getQuixelSceneAsset?quixelAssetId=' + assetDetails, scene, ...., 'obj');