Skip to content

Commit

Permalink
Merge branch 'main' into kirill/gltf-export-material
Browse files Browse the repository at this point in the history
  • Loading branch information
querielo committed Oct 18, 2023
2 parents e6a4177 + 47d1f4c commit 95485cf
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# generate this file: npm run build:example:data
example-data.mjs
119 changes: 58 additions & 61 deletions examples/README.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 95485cf

Please sign in to comment.