Skip to content

Commit

Permalink
Examples ES6 (#5555)
Browse files Browse the repository at this point in the history
* add scripts/spine/playcanvas-spine.3.8.mjs

* Add scripts/posteffects/posteffect-bloom.mjs

* examples/iframe/ to ES6
add importmap.js

* changes to examples/graphics

* changes to examples/loaders

* examples/physics changes

* examples/misc changes

* more examples changes

* add examples/app/ stuff

* add examples/animation/*

* last three files

* fix paths, remove TS mode

* examples/src/iframe: SoC + clean up / refactoring

* examples/src/iframe/index.mjs: refactor / simplify
remove ScriptLoader stuff

* Make hot-reload work with async functions

* cleanup some animation examples

* rework animation/layer-masks.mjs

* bring animation examples into shape

* prepare code for standalone html files for every example
refactor classes into own files for reusing with less dependencies

* refactor out DeviceSelector for standalone example component reusing

* pass controlsObserver as prop into DeviceSelector

* ControlLoader cleanup

* refactor standalone example

* fixes for remote server

* make import map work for both local and remote

* fix spineboy example

* enable and simplify "normal" build process

* fix thumbnail paths for web server like lighttpd

* lint: ignore es6 version of playcanvas-spine.3.8.mjs

* more build script simplification

* turn /extras/ into playcanvas-extras package so rollup accepts it as proper link (npm link --save --dev playcanvas-extras ../extras)

* fix some examples and paths for "npm run serve"

* only import what's absolutely required

* fix all physics examples

* fix all animation/camera examples

* convert user-interface examples

* convert xr examples

* convert input examples

* convert sound examples

* first batch of graphics examples conversion

* 2nd batch of graphics examples...

* use react-es6 in importmap and make paths easier to read

* fix all LOADERS examples

* UI: fix asset paths

* fix animation examples

* 3rd batch of GRAPHICS examples fixing (controls + paths)

* fix more graphics examples

* another batch of graphics fixes

* 5th batch of GRAPHICS examples fixing

* start fine-tuning of GRAPHICS examples

* wheeew GRAPHICS examples are are done

* make puppeteer work

* move jsx.mjs into examples/src/app

* don't add standalone .html's in this PR

* general example fixing

* align UI examples

* fix mouse example

* fix makeCamelCase

* fix thumbnail generation

* minify what's possible via example-data.mjs to circumvent terser() messing with functions

* remove types.d.ts, add typedef instead

* add basisPath/dracoPath

* small cleanup/fix

* fix read-dir.mjs

* add getGlslangPath() and getTwgslPath()

* fix bundle size issue in device-selector.mjs

* Fix arkit.png path

* simplify assetPath.mjs href + make it work in node

* examples/scripts/example-data.mjs: Use ES6 module import as single source of truth for examples

* thumbnail generation: modules are single source of truth for examples too

* Lack of Chrome's WebGPU support on Linux

* fix toKebabCase

* Use of enablePolyfillFunctionCall() in two specific examples only

* shorten some code

* huge simplification of options handling, removing regex to parse function arguments etc. which is just brittle

* fix some leftovers from last commit

* replace handlebars template with type-safe ES6 template

* bunch of cleanup

* examples/src/importmap.js: add tweenjs

* examples/src/assetPath.mjs: fix all pathes and simplify code

* bunch of last issues i found with examples

* ControlPanel: use jsx(...)

* add generate-standalone-files.mjs to minimize bundle size towards absolute minimum for each example

* fix asset paths

* small layer mask refactor

* actually use standalone .html's

* fix controls/app.start in animation examples

* code editor file test + type

* better types for ControlPanel

* simplify animation controls (cartesian / directional)

* decrease example-data file size drastically, examples only load what is really required...

* rename back to .tsx

* rename back to .mjs

* Guarantee that controls() has access to pc.app to render UI without being super defensive about it

* add @tweenjs/tween.js via npm registry

* immediate hot reload fixes

* fix some examples

* fix shader editing again and a bit of cleanup

* Updates from #5563

* Add changes from #5562

* fix loaders.gl example + fix old React state for controls

* make tween work in standalone version

* fix pathes in thumbnail script

* turn off debug

* Remove posteffect-bloom.mjs and playcanvas-spine.3.8.mjs

* remove <object> fallback for arkit.png

* remove obsolete rollup targets

* fix landscape / portrait mode, mostly working

* rewrite functional CodeEditor into class-based component for easier state handling

* always request files from example when displaying in different Monaco editors

* make ministats button work

* Fix orientationchange handler + make event handling nicer
Fix generate-standalone-files.mjs to generate dist/iframe dir

* oops fix issue introduced in last commit

* remove obsolete file / comment

* fix last issue from switching back to es5 version of spine lib

* examples/misc/mini-stats.mjs: fix having two mini-stats instances in case of `static MINISTATS = true;`

* cross-env for Windows compatibility

* cleanup / type improvement

* merge Martins PR

* Make sure to resize iframe app when CodeEditor fetches new information and resizes itself

* iframe example cleanup

* Fix Null renderer MiniStats issue

* npm install @playcanvas/pcui@latest

* remove obsolete code, simplify example-directory generation

* fix circular dependency of getOrientation

* drop ministats-not-supported type of message/alert

* Respect ENGINE_PATH and NODE_ENV again

* simplify and reuse MiniStats showing logic to fix double rendering in MiniStats example

* Refactor controls, remove reliance on `window` which confused rollup and caused crashes

* `npm run build:thumbnails` starts its own serve process now

* allow spaces in Examples search

* Reflect Martins AreaPicker PR

* Remove double material.depthWrite = false;

* box -> cylinder

* Update README.md

* Set `NODE_ENV=development` for `npm run develop`

* Load correct engine based on ENGINE_PATH.includes('.mjs')

* Add polyfill to load ES5 scripts with ESM engine

* Fix ready check for rendering Controls

* loadScript: simplify code by preventing special case between UMD/ESM + fixing import bug

* Fix cold start bug of: npm run build:thumbnails

* Martins PR: MiniStats render on UI layer

* rollup.config.js: check if ENGINE_PATH requires a dir or file copy watcher (mjs -> js)

* Use ../src/index.js as default for NODE_ENV=development

* rollup.config.js: implement `buildAndWatchStandaloneExamples`

* Martins Reflection Planar fix

* Fix scrollbar issue and bit of cleanup

* Also build dist/iframe/playcanvas-extras.js (UMD) so `npm run develop` has live updates

* Remove bunch of obsolete code (which was assigned to window for Controls, but we have a better way via params now)

* Sticky device type selection

* iframeUtils: cleanup + better typing + add hotReload event

* index.mjs: remove window assignments

* Move app destruction/hot-reload logic from UI into standalone examples

* Add/use removeRedundantSpaces(...) on file contents to align all GLSL examples in Monaco

* Fix active device display by making DeviceSelector a stateful component and simplify logic/exports of different parts

* remove debug version of iframeEval

* CodeEditor: fix undefined file during device change

* Prevent WebGPU when example is known to crash (with console warning)

* Fix cropped number input fields

* Update mergeState functions to fix CodeEditor file-still-loading issue

* Simplify / fix state in Example component

* 'Picked WebGPU but example is not supported on WebGPU' + reuse WEBGPU_ENABLED property via `=== false`

* Rewrite all todo JSDoc + few better types + remove two no-longer-used functions (jsxTry and appendReactToBody)

* sidebar/example-data: remove some comments / clean-up

* Change back to example file on example change

* Prevent reload while still reloading

* Fix resize of every example + simplify generated standalone files

* remove @todo, using: miniStats?.destroy();

* npm install for package-lock.json sync

* UI text TypeWriter example: add clearInterval for setInterval

* position sound example: add resize code

* detach "new Mouse" in two examples via app.on('destroy', ...)

* Animation Locomotion: add document.removeEventListener("mousedown", ...) for event cleaning

* UI Text Emojis: only one `WEBGPU_ENABLED = true`

* six examples: static WEBGPU_ENABLED = false; // with reason

* Make sure that React events into iframe can't trigger undefined-errors (destroy/show-ministats events)
Remove two <div>'s (#app and #appInner which weren't even used)
Stop exposing app into window

* bring back outer divs (I mistakenly thought they were superfluous)

* Monaco editor: show/hide minimap via Command Palette (F1) + save state in localStorage

* Don't call app.destroy() twice

* Simplify iframeEval into iframeReady, make it 100% fail-safe
  • Loading branch information
kungfooman committed Oct 17, 2023
1 parent b08d517 commit 47d1f4c
Show file tree
Hide file tree
Showing 286 changed files with 37,974 additions and 23,519 deletions.
2 changes: 2 additions & 0 deletions examples/.gitignore
@@ -0,0 +1,2 @@
# generate this file: npm run build:example:data
example-data.mjs
119 changes: 58 additions & 61 deletions examples/README.md
Expand Up @@ -20,97 +20,96 @@ npm run develop
Visit the url mentioned in your terminal to view the examples browser.

You can also run the examples browser with a specific version of the engine by running the following command:

```
ENGINE_PATH=../build/playcanvas.mjs/index.js npm run develop
```

Where `../build/playcanvas.mjs/index.js` is the path to the es6 version of the engine.

Or directly from the source:

```
ENGINE_PATH=../src/index.js npm run develop
```

To create the side bar thumbnails run the following script:
```
npm run build:thumbnails
```

Please note that the examples app requires a built version of the engine to be present in the engine repo within the `../build` folder. If you haven't already done so, run `npm install` followed by `npm run build` in the engine repo.

As the examples are written in TypeScript, you will also need to build the type definitions in the engine repo with `npm run build:types`.
As Monaco is supporting IntelliSense via type definitions files, you will also need to build the type definitions in the engine repo with `npm run build:types`.

## Creating an example

The available examples are written as classes in TypeScript under the paths `./src/examples/\<categoryName\>/\<exampleName>.tsx.
The available examples are written as classes in JavaScript under the paths `./src/examples/\<categoryName\>/\<exampleName>.mjs.
To create a new example you can copy any of the existing examples as a template and update its path.

Each example extends the `Example` parent class and can implement three methods to define its functionality:
Each example can implement two methods to define its functionality:

### `example` function
```tsx
import * as pc from 'playcanvas/build/playcanvas.js';
example(canvas: HTMLCanvasElement) {
const app = new pc.Application(canvas, {});
}
```
This is the only function that's required to run an example. The code defined in this function is executed each time the example play button is pressed. It takes the example's canvas element as its first argument and usually begins by creating a new PlayCanvas application using that canvas.

### `load` function
You can define a set of PlayCanvas assets to load into your application using this function. The function should return a set of Loader React components:
```tsx
import React from 'react';
import { AssetLoader } from '../../app/helpers/loader';
load() {
return <>
<AssetLoader name='statue' type='container' url='static/assets/models/statue.glb' />
<AssetLoader name='firstPersonCamScript' type='script' url='static/scripts/camera/first-person-camera.js' />
<>;
}
```
As assets are loaded using React, be sure to import React into any example that is loading assets.

Assets and scripts present in the `./assets` and `../scripts` directories will be available to examples under the `static/` path.
Each asset you load will be made available to the `example` function you write as the second parameter and will already be in the loaded state.
```tsx
example(canvas: HTMLCanvasElement, assets: { statue: pc.Asset, firstPersonCamScript: pc.Asset }) {
```js
import * as pc from 'playcanvas';
/**
* @typedef {import('../../options.mjs').ExampleOptions} ExampleOptions
* @param {import('../../options.mjs').ExampleOptions} options - The example options.
* @returns {Promise<pc.AppBase>} The example application.
*/
async function example({ canvas, deviceType, assetPath, scriptsPath }) {
const app = new pc.Application(canvas, {});
// this will log true
console.log(assets.statue.loaded);
}
```

Be sure to correctly define the type of the assets parameter to list each asset you're loading into the example.
This is the only function that's required to run an example. The code defined in this function is executed each time the example play button is pressed. It takes the example's canvas element from the options and usually begins by creating a new PlayCanvas `Application` or `AppBase` using that canvas.

You can load external scripts into an example using the `loadES5` function as follows:

You can also load external scripts into an example using the `ScriptLoader` React component as follows:
```tsx
import React from 'react';
import { ScriptLoader } from '../../app/helpers/loader';
load() {
return <>
<ScriptLoader name='TWEEN' url='https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js' />
<>;
```js
import { loadES5 } from '../../loadES5.mjs';
async function example({ canvas, deviceType, files, assetPath, glslangPath, twgslPath }) {
const CORE = await loadES5('https://cdn.jsdelivr.net/npm/@loaders.gl/core@2.3.6/dist/dist.min.js');
const DRACO = await loadES5('https://cdn.jsdelivr.net/npm/@loaders.gl/draco@2.3.6/dist/dist.min.js');
console.log({ CORE, DRACO })
}
```
Each script will be made available as a parameter of the example function as an esModule using the name it was given and therefore any scripts should be defined in the examples function signature as follows:
```tsx
example(canvas: HTMLCanvasElement, TWEEN: any) {
const app = new pc.Application(canvas, {});
console.log(TWEEN);
}

However, depending on external URL's is maybe not what you want as it breaks your examples once your internet connection is gone - you can simply install a NPM package and use it instead aswell:

```js
import * as TWEEN from '@tweenjs/tween.js';
```

### `controls` function

This function allows you to define a set of PCUI based interface which can be used to display stats from your example or provide users with a way of controlling the example.
```tsx
import Button from '@playcanvas/pcui/Button/component';
controls(data: any) {
return <>
<Button text='Flash' onClick={() => {
data.set('flash', !data.get('flash'));
}}/>
</>;

```js
/**
* @param {import('../../app/example.mjs').ControlOptions} options - The options.
* @returns {JSX.Element} The returned JSX Element.
*/
function controls({ observer, ReactPCUI, React, jsx, fragment }) {
const { Button } = ReactPCUI;
return fragment(
jsx(Button, {
text: 'Flash',
onClick: () => {
observer.set('flash', !observer.get('flash'));
}
})
);
}
```

The controls function takes a [pcui observer](https://playcanvas.github.io/pcui/data-binding/using-observers/) as its parameter and returns a set of PCUI components. Check this [link](https://playcanvas.github.io/pcui/examples/todo/) for an example of how to create and use PCUI.

The data observer used in the controls function will be made available as the third parameter in the example function:
```tsx
example(canvas: HTMLCanvasElement, assets: {}, data: any) {
The data observer used in the `controls` function will be made available as a property in the example function:

```js
example({ canvas, assets, data }) {
const app = new pc.Application(canvas, {});
console.log(data.get('flash'));
}
Expand Down Expand Up @@ -140,14 +139,12 @@ npm run build
npm run serve
```

3) **Generate thumbnails** by first ensuring the examples browser is running on [http://localhost:5000]() then running the following command. This step will create the thumbnails directory for the browser and may take a while depending on the number of new examples or if this is first time it has been run locally.
3) **Generate thumbnails** This step will create the thumbnails directory for the browser and may take a while depending on the number of new examples or if this is first time it has been run locally.
```
npm run build:thumbnails
```
If you are serving the examples browser from a different port, you can specify the port using the `PORT` environment variable:
```
PORT=5001 npm run build:thumbnails
```

This command spawns its own `serve` instance on port 12321, so you don't need to care about that.

4) Copy the contents of the `./dist` directory to the root of the [playcanvas.github.io](https://github.com/playcanvas/playcanvas.github.io) repository. Be sure not to wipe the contents of the `pcui` subdirectory in that repository.

Expand Down
17 changes: 17 additions & 0 deletions examples/app.html
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>PlayCanvas Examples</title>
<meta name="description" content="">
<meta name="keywords" content="PlayCanvas">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="icon" type="image/png" href="./playcanvas-logo.png" />
<link rel="stylesheet" href="./src/static/styles.css">
</head>
<body>
<div id='app'></div>
<script src="./src/importmap.js"></script>
<script type="module" src="./src/app/index.mjs"></script>
</body>
</html>
4 changes: 3 additions & 1 deletion examples/tsconfig.json → examples/jsconfig.json
@@ -1,7 +1,9 @@
{
"compilerOptions": {
"checkJs": true,
"outDir": "dist",
"noImplicitAny": true,
"strictNullChecks": true,
"module": "ES2015",
"target": "es5",
"allowJs": true,
Expand All @@ -14,6 +16,6 @@
"esModuleInterop" : true,
"moduleResolution" : "node"
},
"include": ["src"],
"include": ["src", "scripts"],
"exclude": ["node_modules", "src/lib"]
}

0 comments on commit 47d1f4c

Please sign in to comment.