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' });
}