Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi app fix #6645

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/iframe/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export function updateDeviceType(config) {
return;
}

if (config.WEBGL_DISABLED && config.WEBGPU_DISABLED) {
console.warn('Both WebGL and WebGPU are disabled. Using NullGraphicsDevice instead.');
deviceType = 'null';
return;
}
if (config.WEBGPU_DISABLED) {
console.warn('WebGPU is disabled. Using WebGL2 instead.');
deviceType = 'webgl2';
Expand Down
198 changes: 104 additions & 94 deletions examples/src/examples/misc/multi-app.example.mjs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
// @config NO_MINISTATS
// @config NO_DEVICE_SELECTOR
// @config WEBGPU_DISABLED
// @config WEBGL_DISABLED
import * as pc from 'playcanvas';
import { data } from 'examples/observer';
import { rootPath } from 'examples/utils';

// Use custom createGraphicsDevice function to not automatically include fall backs
/**
* @param {HTMLCanvasElement} canvas - The canvas element.
* @param {string} deviceType - The device type.
* @returns {Promise<pc.AppBase>} The example application.
* @returns {Promise<pc.GraphicsDevice>} The graphics device.
*/
const createApp = async function (deviceType) {
const assets = {
font: new pc.Asset('font', 'font', { url: rootPath + '/static/assets/fonts/courier.json' })
};

const canvas = document.createElement('canvas');
canvas.id = `app-${Math.random().toString(36).substring(7)}`; // generate a random id
document.getElementById('appInner')?.appendChild(canvas);

// Don't use createGraphicsDevice as it automatically falls back, which we don't want
async function createGraphicsDevice(canvas, deviceType) {
let device;
if (deviceType === 'webgpu') {
device = new pc.WebgpuGraphicsDevice(canvas, {});
Expand All @@ -28,98 +22,110 @@ const createApp = async function (deviceType) {
} else {
device = new pc.NullGraphicsDevice(canvas, {});
}
return device;
}

/**
* @param {string} deviceType - The device type.
* @returns {Promise<pc.AppBase>} The example application.
*/
async function createApp(deviceType) {
const assets = {
font: new pc.Asset('font', 'font', { url: rootPath + '/static/assets/fonts/courier.json' })
};

const canvas = document.createElement('canvas');
canvas.id = `app-${Math.random().toString(36).substring(7)}`; // generate a random id
document.getElementById('appInner')?.appendChild(canvas);

const device = await createGraphicsDevice(canvas, deviceType);

const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;

createOptions.componentSystems = [
pc.RenderComponentSystem,
pc.CameraComponentSystem,
pc.LightComponentSystem,
pc.ScreenComponentSystem,
pc.ElementComponentSystem
];

createOptions.resourceHandlers = [
// @ts-ignore
pc.TextureHandler,
// @ts-ignore
pc.FontHandler
];

const app = new pc.AppBase(canvas);
app.init(createOptions);
app.start();

const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
assetListLoader.load(() => {
app.setCanvasFillMode(pc.FILLMODE_NONE);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

// Ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener('resize', resize);
app.on('destroy', () => {
window.removeEventListener('resize', resize);
});

// create box entity
const box = new pc.Entity('cube');
box.addComponent('render', {
type: 'box'
});
app.root.addChild(box);

// create camera entity
const clearValue = 0.3 + Math.random() * 0.3;
const camera = new pc.Entity('camera');
camera.addComponent('camera', {
clearColor: new pc.Color(clearValue, clearValue, clearValue)
});
app.root.addChild(camera);
camera.setPosition(0, -0.4, 3);

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

// Create a 2D screen
const screen = new pc.Entity();
screen.addComponent('screen', {
referenceResolution: new pc.Vec2(1280, 720),
scaleBlend: 0.5,
scaleMode: pc.SCALEMODE_BLEND,
screenSpace: true
});
app.root.addChild(screen);

// device type as text
const text = app.graphicsDevice.isWebGL2 ? 'WebGL 2' : 'WebGPU';

// Text with outline to identify the platform
const textOutline = new pc.Entity();
textOutline.setLocalPosition(0, -100, 0);
textOutline.addComponent('element', {
pivot: new pc.Vec2(0.5, 0.5),
anchor: new pc.Vec4(0.5, -0.2, 0.5, 0.5),
fontAsset: assets.font.id,
fontSize: 130,
text: text,
color: new pc.Color(1, 0.9, 0.9),
outlineColor: new pc.Color(0, 0, 0),
outlineThickness: 1,
type: pc.ELEMENTTYPE_TEXT
});
screen.addChild(textOutline);

// rotate the box according to the delta time since the last frame
app.on('update', (/** @type {number} */ dt) => box.rotate(10 * dt, 20 * dt, 30 * dt));
app.setCanvasFillMode(pc.FILLMODE_NONE);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

// Ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener('resize', resize);
app.on('destroy', () => {
window.removeEventListener('resize', resize);
});

await new Promise((resolve) => {
new pc.AssetListLoader(Object.values(assets), app.assets).load(resolve);
});

// create box entity
const box = new pc.Entity('cube', app);
box.addComponent('render', {
type: 'box'
});
app.root.addChild(box);

// create camera entity
const clearValue = 0.3 + Math.random() * 0.3;
const camera = new pc.Entity('camera', app);
camera.addComponent('camera', {
clearColor: new pc.Color(clearValue, clearValue, clearValue)
});
app.root.addChild(camera);
camera.setPosition(0, -0.4, 3);

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

// Create a 2D screen
const screen = new pc.Entity('screen', app);
screen.addComponent('screen', {
referenceResolution: new pc.Vec2(1280, 720),
scaleBlend: 0.5,
scaleMode: pc.SCALEMODE_BLEND,
screenSpace: true
});
app.root.addChild(screen);

// Text with outline to identify the platform
const text = new pc.Entity('text', app);
text.setLocalPosition(0, -100, 0);
text.addComponent('element', {
pivot: new pc.Vec2(0.5, 0.5),
anchor: new pc.Vec4(0.5, -0.2, 0.5, 0.5),
fontAsset: assets.font.id,
fontSize: 130,
text: app.graphicsDevice.isWebGL2 ? 'WebGL 2' : 'WebGPU',
color: new pc.Color(0.9, 0.9, 0.9),
outlineColor: new pc.Color(0, 0, 0),
outlineThickness: 1,
type: pc.ELEMENTTYPE_TEXT
});
screen.addChild(text);

// rotate the box according to the delta time since the last frame
app.on('update', (/** @type {number} */ dt) => box.rotate(10 * dt, 20 * dt, 30 * dt));

app.start();

return app;
};
}

/**
* @type {Record<string, pc.AppBase[]>}
Expand All @@ -136,20 +142,24 @@ if (existingCanvas) {
existingCanvas.remove();
}

/**
* @param {string} deviceType - The device type.
*/
async function addApp(deviceType) {
try {
const app = await createApp(deviceType);
apps[deviceType].push(app);
data.set(deviceType, apps[deviceType].length);
} catch (e) {
console.error(e);
}
}

// Add event listers for adding and removing apps
for (const deviceType in apps) {
data.set(deviceType, 0);

data.on(`add:${deviceType}`, () => {
createApp(deviceType)
.then((app) => {
apps[deviceType].push(app);
data.set(deviceType, apps[deviceType].length);
})
.catch((err) => {
console.error(err);
});
});
data.on(`add:${deviceType}`, () => addApp(deviceType));

data.on(`remove:${deviceType}`, () => {
const app = apps[deviceType].pop();
Expand Down Expand Up @@ -181,7 +191,7 @@ const destroy = () => {
};

// Start with a webgl2 and webgpu app
data.emit('add:webgl2');
data.emit('add:webgpu');
await addApp('webgl2');
await addApp('webgpu');

export { destroy };