[SOLVED] Loading scene json from remote location

You’re right, sorry. If I add the following code to handle non-file assets, it does show metalness/shininess on the model but no texture:

if (element.file) {
  // ... Handle texture and model
} else { // added this 
  // if not, we're a material or something so PC can handle it.
  const asset = new pc.Asset(
    element.name,
    element.type,
    element.file,
    element.data,
  );

  asset.id = parseInt(element.id, 10);
  // asset.preload = element.preload ? element.preload : false;
  // tags
  asset.tags.add(element.tags);
  // i18n
  if (element.i18n) {
    for (const locale in element.i18n) {
      asset.addLocalizedAssetId(locale, element.i18n[locale]);
    }
  }
  pc.app.assets.add(asset);
}

image

Or am I still overlooking something? :grimacing:

The bindings depend on events firing from the assets registry so the image Base64 loader also needs to fire events from the registry. This is how I would do it:

    if (element.type === 'texture') {
        const textureAsset = await new Promise((resolve, reject) => {
          // create asset
          const asset = new pc.Asset(
            element.name,
            'texture',
            {
              url: '',
            },
            element.data,
          );
          asset.id = parseInt(element.id, 10);

          const tex = new pc.Texture(app.graphicsDevice, {
            mipmaps: false,
            flipY: true,
          });

          tex.minFilter = pc.FILTER_LINEAR;
          tex.magFilter = pc.FILTER_LINEAR;
          tex.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
          tex.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

          if (!diffuseFromBase64) {
            // eslint-disable-next-line no-use-before-define
            const blob = bytesToB64Blob(new Uint8Array(file.data));
            const url = window.URL.createObjectURL(blob);
            asset.file.url = url;
            app.assets.add(asset);
            app.assets.load(asset);
          }

          asset.ready((ass) => {
            resolve(ass);
          });
        });
      }

Full code;

/* eslint-disable no-loop-func */
/* eslint-disable no-await-in-loop */
/* eslint-disable guard-for-in */
/* eslint-disable global-require */
// Assets
import './style.css';

// Modules
import $ from 'jquery';
import axios from 'axios';

// Playcanvas
import * as pc from 'playcanvas';

// Start Playcanvas app
$(document.body).append('<canvas id="app"></canvas>');
const appCanvas = $('#app')[0];
const app = new pc.Application(appCanvas, {
  mouse: new pc.Mouse(appCanvas),
  keyboard: new pc.Keyboard(window),
});

// fill the available space at full resolution
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

// ensure canvas is resized when window changes size
window.addEventListener('resize', () => {
  app.resizeCanvas();
});

// create camera entity
const camera = new pc.Entity('camera');
camera.addComponent('camera', {
  clearColor: new pc.Color(1, 1, 1),
});
app.root.addChild(camera);
camera.setPosition(0, 0, 10);

// create directional light entity
const light = new pc.Entity('light');
light.addComponent('light');
app.root.addChild(light);
light.setEulerAngles(45, 0, 0);

// Load the build
async function loadScene(diffuseFromBase64) {
  const buildData = (await axios.get(`${window.origin}/playcanvas_build/config.json`)).data;
  const sceneData = (await axios.get(`${window.origin}/playcanvas_build/972413.json`)).data;
  console.log('buildData:', buildData, '\nsceneData:', sceneData);

  // Load all the assets
  for (const key in buildData.assets) {
    const element = buildData.assets[key];

    if (element.file) {
      let responseType = 'arraybuffer';
      if (element.file.filename.split('.')[1] === 'json') responseType = 'text';
      const file = await axios.get(`${window.origin}/playcanvas_build/${element.file.url}`, {
        responseType,
      });

      if (element.type === 'texture') {
        const textureAsset = await new Promise((resolve, reject) => {
          // create asset
          const asset = new pc.Asset(
            element.name,
            'texture',
            {
              url: '',
            },
            element.data,
          );
          asset.id = parseInt(element.id, 10);

          const tex = new pc.Texture(app.graphicsDevice, {
            mipmaps: false,
            flipY: true,
          });

          tex.minFilter = pc.FILTER_LINEAR;
          tex.magFilter = pc.FILTER_LINEAR;
          tex.addressU = pc.ADDRESS_CLAMP_TO_EDGE;
          tex.addressV = pc.ADDRESS_CLAMP_TO_EDGE;

          if (!diffuseFromBase64) {
            // eslint-disable-next-line no-use-before-define
            const blob = bytesToB64Blob(new Uint8Array(file.data));
            const url = window.URL.createObjectURL(blob);
            asset.file.url = url;
            app.assets.add(asset);
            app.assets.load(asset);
          }

          asset.ready((ass) => {
            resolve(ass);
          });
        });
      } else {
        // Turn those assets in a blob reference
        const blob = new Blob([JSON.stringify(file.data)]);
        const url = URL.createObjectURL(blob);
        const assetPromise = new Promise((resolve) => {
          // eslint-disable-next-line no-shadow
          const file = {
            url,
            filename: element.name,
          };
          const newAsset = new pc.Asset(element.name, element.type, file);
          newAsset.data = element.data;
          newAsset.id = parseInt(element.id, 10);
          // tags
          newAsset.tags.add(element.tags);
          // i18n
          if (element.i18n) {
            for (const locale in element.i18n) {
              newAsset.addLocalizedAssetId(locale, element.i18n[locale]);
            }
          }
          newAsset.once('load', (modelAsset) => {
            resolve(modelAsset);
            URL.revokeObjectURL(url);
          });
          pc.app.assets.add(newAsset);
          pc.app.assets.load(newAsset);
        });
        const asset = await assetPromise;
      }
    } else { // if not, we're a material or something so PC can handle it.
      const asset = new pc.Asset(
        element.name,
        element.type,
        element.file,
        element.data,
      );

      asset.id = parseInt(element.id, 10);
      // asset.preload = element.preload ? element.preload : false;
      // tags
      asset.tags.add(element.tags);
      // i18n
      if (element.i18n) {
        for (const locale in element.i18n) {
          asset.addLocalizedAssetId(locale, element.i18n[locale]);
        }
      }
      pc.app.assets.add(asset);
      pc.app.assets.load(asset);
    }
  }

  const sceneBlobData = JSON.stringify(sceneData);
  const blob = new Blob([sceneBlobData], {
    type: 'application/json',
  });
  const url = URL.createObjectURL(blob);
  pc.app.scenes.add('CustomBuild', url);
  const scene = pc.app.scenes.find('CustomBuild');
  pc.app.scenes.loadSceneHierarchy(
    scene.url, (err, root) => {
      if (err) return console.error(err);
      console.log('loaded scene');
      window.pc = pc;
    },
  );
}

const USE_PRESET_IMG_WITH_BASE64 = false;

loadScene(USE_PRESET_IMG_WITH_BASE64).then(() => {
  app.start();
});

function bytesToB64Blob(bytes) {
  let binary = '';
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }

  const ab = new ArrayBuffer(binary.length);
  const ia = new Uint8Array(ab);

  for (let i = 0; i < binary.length; i++) {
    ia[i] = binary.charCodeAt(i);
  }
  return new Blob([ab], { type: 'image/jpeg' });
}

Thank you very much! I didn’t know it would allow loading from blob urls either, great to know!