create-playcanvas 0.2.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -47
- package/dist/index.mjs +49 -46
- package/package.json +54 -49
- package/templates/_shared/model-viewer/public/playcanvas-cube.glb +0 -0
- package/templates/engine/base/README.md +38 -0
- package/templates/engine/base/eslint.config.mjs +3 -0
- package/templates/engine/base/package.json +30 -0
- package/templates/engine/base/prettier.config.mjs +1 -0
- package/templates/engine/base/src/vite-env.d.ts +19 -0
- package/templates/engine/base/tsconfig.json +25 -0
- package/templates/engine/interactive-sphere/index.html +13 -0
- package/templates/engine/interactive-sphere/src/App.ts +180 -0
- package/templates/{vanilla-ts → engine/interactive-sphere}/src/main.ts +8 -8
- package/templates/engine/interactive-sphere/src/style.css +107 -0
- package/templates/engine/interactive-sphere/src/utils.ts +16 -0
- package/templates/engine/model-viewer/index.html +13 -0
- package/templates/engine/model-viewer/src/main.ts +81 -0
- package/templates/engine/model-viewer/src/style.css +8 -0
- package/templates/engine/spinning-cube/index.html +13 -0
- package/templates/engine/spinning-cube/src/main.ts +65 -0
- package/templates/engine/spinning-cube/src/style.css +8 -0
- package/templates/engine/splat-viewer/index.html +13 -0
- package/templates/engine/splat-viewer/src/main.ts +74 -0
- package/templates/engine/splat-viewer/src/style.css +8 -0
- package/templates/index.ts +47 -57
- package/templates/react/base/README.md +38 -0
- package/templates/react/base/eslint.config.mjs +14 -0
- package/templates/react/base/package.json +36 -0
- package/templates/react/base/prettier.config.mjs +1 -0
- package/templates/react/base/src/vite-env.d.ts +19 -0
- package/templates/react/base/tsconfig.json +26 -0
- package/templates/react/base/vite.config.ts +7 -0
- package/templates/react/interactive-sphere/index.html +13 -0
- package/templates/react/interactive-sphere/src/App.css +75 -0
- package/templates/react/interactive-sphere/src/App.tsx +43 -0
- package/templates/react/interactive-sphere/src/Scene.tsx +69 -0
- package/templates/react/interactive-sphere/src/index.css +51 -0
- package/templates/react/interactive-sphere/src/main.tsx +11 -0
- package/templates/react/model-viewer/index.html +13 -0
- package/templates/react/model-viewer/src/App.tsx +43 -0
- package/templates/react/model-viewer/src/index.css +9 -0
- package/templates/react/model-viewer/src/main.tsx +11 -0
- package/templates/react/spinning-cube/index.html +13 -0
- package/templates/react/spinning-cube/src/App.tsx +36 -0
- package/templates/react/spinning-cube/src/index.css +9 -0
- package/templates/react/spinning-cube/src/main.tsx +11 -0
- package/templates/react/splat-viewer/index.html +13 -0
- package/templates/react/splat-viewer/src/App.tsx +39 -0
- package/templates/react/splat-viewer/src/index.css +9 -0
- package/templates/react/splat-viewer/src/main.tsx +11 -0
- package/templates/web-components/base/README.md +66 -0
- package/templates/web-components/base/_gitignore +24 -0
- package/templates/web-components/base/eslint.config.mjs +3 -0
- package/templates/web-components/base/package.json +31 -0
- package/templates/web-components/base/prettier.config.mjs +1 -0
- package/templates/web-components/base/src/assets/playcanvas.png +0 -0
- package/templates/web-components/base/src/vite-env.d.ts +19 -0
- package/templates/web-components/base/tsconfig.json +25 -0
- package/templates/web-components/interactive-sphere/index.html +53 -0
- package/templates/web-components/interactive-sphere/src/assets/typescript.svg +1 -0
- package/templates/web-components/interactive-sphere/src/main.ts +76 -0
- package/templates/web-components/interactive-sphere/src/style.css +111 -0
- package/templates/web-components/model-viewer/index.html +34 -0
- package/templates/web-components/model-viewer/src/main.ts +26 -0
- package/templates/web-components/model-viewer/src/style.css +8 -0
- package/templates/web-components/spinning-cube/index.html +29 -0
- package/templates/web-components/spinning-cube/src/main.ts +20 -0
- package/templates/web-components/spinning-cube/src/style.css +8 -0
- package/templates/web-components/splat-viewer/index.html +31 -0
- package/templates/web-components/splat-viewer/src/main.ts +14 -0
- package/templates/web-components/splat-viewer/src/style.css +8 -0
- package/index.js +0 -3
- package/templates/react-ts/README.md +0 -82
- package/templates/react-ts/index.html +0 -13
- package/templates/react-ts/package.json +0 -29
- package/templates/react-ts/scripts/ensure-node-version.cjs +0 -12
- package/templates/react-ts/src/App.css +0 -87
- package/templates/react-ts/src/App.tsx +0 -46
- package/templates/react-ts/src/Scene.tsx +0 -70
- package/templates/react-ts/src/index.css +0 -73
- package/templates/react-ts/src/main.tsx +0 -10
- package/templates/react-ts/src/vite-env.d.ts +0 -1
- package/templates/react-ts/tsconfig.app.json +0 -27
- package/templates/react-ts/tsconfig.json +0 -7
- package/templates/react-ts/tsconfig.node.json +0 -25
- package/templates/react-ts/vite.config.ts +0 -7
- package/templates/vanilla-ts/README.md +0 -114
- package/templates/vanilla-ts/index.html +0 -13
- package/templates/vanilla-ts/package.json +0 -23
- package/templates/vanilla-ts/public/environment-map.png +0 -0
- package/templates/vanilla-ts/scripts/ensure-node-version.cjs +0 -12
- package/templates/vanilla-ts/src/App.ts +0 -182
- package/templates/vanilla-ts/src/style.css +0 -140
- package/templates/vanilla-ts/src/utils.ts +0 -16
- package/templates/vanilla-ts/src/vite-env.d.ts +0 -1
- package/templates/vanilla-ts/tsconfig.json +0 -25
- /package/templates/{react-ts → _shared/interactive-sphere}/public/environment-map.png +0 -0
- /package/templates/{react-ts → engine/base}/_gitignore +0 -0
- /package/templates/{react-ts → engine/base}/src/assets/playcanvas.png +0 -0
- /package/templates/{vanilla-ts → engine/interactive-sphere}/src/assets/typescript.svg +0 -0
- /package/templates/{vanilla-ts → react/base}/_gitignore +0 -0
- /package/templates/{vanilla-ts → react/base}/src/assets/playcanvas.png +0 -0
- /package/templates/{react-ts → react/interactive-sphere}/src/assets/react.svg +0 -0
package/templates/index.ts
CHANGED
|
@@ -1,60 +1,50 @@
|
|
|
1
|
-
import colors
|
|
2
|
-
const { blue, cyan } = colors
|
|
1
|
+
import colors from 'picocolors';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
* You can add new frameworks here.
|
|
6
|
-
*
|
|
7
|
-
* The framework name is used to create the directory name.
|
|
8
|
-
* The framework display name is used to display the framework name in the CLI.
|
|
9
|
-
* The framework description is used to display the framework description in the CLI.
|
|
10
|
-
* The framework color is used to display the framework color in the CLI.
|
|
11
|
-
* The framework variants are used to display the framework variants in the CLI.
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export const FRAMEWORKS: Framework[] = [
|
|
15
|
-
{
|
|
16
|
-
name: 'vanilla',
|
|
17
|
-
display: 'Vanilla',
|
|
18
|
-
description: 'with playcanvas engine',
|
|
19
|
-
color: yellow,
|
|
20
|
-
variants: [
|
|
21
|
-
{
|
|
22
|
-
name: 'vanilla-ts',
|
|
23
|
-
display: 'TypeScript',
|
|
24
|
-
color: blue,
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'react',
|
|
30
|
-
display: 'React',
|
|
31
|
-
description: 'with @playcanvas/react',
|
|
32
|
-
color: cyan,
|
|
33
|
-
variants: [
|
|
34
|
-
{
|
|
35
|
-
name: 'react-ts',
|
|
36
|
-
display: 'TypeScript',
|
|
37
|
-
color: blue,
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
}
|
|
41
|
-
]
|
|
3
|
+
const { cyan, magenta, yellow } = colors;
|
|
42
4
|
|
|
43
|
-
export const TEMPLATES =
|
|
5
|
+
export const TEMPLATES = [
|
|
6
|
+
{
|
|
7
|
+
name: 'engine',
|
|
8
|
+
display: 'Engine',
|
|
9
|
+
description: 'with the PlayCanvas Engine API directly',
|
|
10
|
+
color: yellow
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'react',
|
|
14
|
+
display: 'React',
|
|
15
|
+
description: 'with @playcanvas/react',
|
|
16
|
+
color: cyan
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'web-components',
|
|
20
|
+
display: 'Web Components',
|
|
21
|
+
description: 'with @playcanvas/web-components',
|
|
22
|
+
color: magenta
|
|
23
|
+
}
|
|
24
|
+
];
|
|
44
25
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
26
|
+
// Every template holds a `base` directory of shared tooling plus one directory per boilerplate,
|
|
27
|
+
// which is copied over the top. Assets a boilerplate needs in every template live once in
|
|
28
|
+
// `_shared/<boilerplate>` and are seeded in first. The first entry is the default.
|
|
29
|
+
export const BOILERPLATES = [
|
|
30
|
+
{
|
|
31
|
+
name: 'spinning-cube',
|
|
32
|
+
display: 'Spinning Cube',
|
|
33
|
+
description: 'a rotating cube and a light'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'interactive-sphere',
|
|
37
|
+
display: 'Interactive Sphere',
|
|
38
|
+
description: 'orbit controls, a grid and pointer picking'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'model-viewer',
|
|
42
|
+
display: 'Model Viewer',
|
|
43
|
+
description: 'a glTF model, orbit controls and a procedural sky'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'splat-viewer',
|
|
47
|
+
display: 'Splat Viewer',
|
|
48
|
+
description: 'a Gaussian splat and orbit controls'
|
|
49
|
+
}
|
|
50
|
+
];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# PlayCanvas React + TypeScript Starter
|
|
2
|
+
|
|
3
|
+
A Vite-powered `@playcanvas/react` project with TypeScript, hot module replacement, ESLint and Prettier.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Node.js 22.23.2 or later.
|
|
8
|
+
|
|
9
|
+
## Getting started
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm create playcanvas@latest playcanvas-project -- --template react
|
|
13
|
+
cd playcanvas-project
|
|
14
|
+
npm install
|
|
15
|
+
npm run dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Open <http://localhost:5173>. Edit the files under `src/` and save to see the scene update.
|
|
19
|
+
|
|
20
|
+
## Scripts
|
|
21
|
+
|
|
22
|
+
| Command | Description |
|
|
23
|
+
| ------------------- | --------------------------------- |
|
|
24
|
+
| `npm run dev` | Start the Vite development server |
|
|
25
|
+
| `npm run build` | Build for production |
|
|
26
|
+
| `npm run start` | Preview the production build |
|
|
27
|
+
| `npm run lint` | Run ESLint |
|
|
28
|
+
| `npm run fmt` | Check formatting |
|
|
29
|
+
| `npm run typecheck` | Run TypeScript checks |
|
|
30
|
+
|
|
31
|
+
Run `npm run build` to generate a deployable static site in `dist/`.
|
|
32
|
+
|
|
33
|
+
## Further reading
|
|
34
|
+
|
|
35
|
+
- [PlayCanvas React manual](https://developer.playcanvas.com/user-manual/react/)
|
|
36
|
+
- [React documentation](https://react.dev/)
|
|
37
|
+
- [Vite documentation](https://vite.dev/)
|
|
38
|
+
- [TypeScript documentation](https://www.typescriptlang.org/docs/)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import reactConfig from '@playcanvas/eslint-config/react';
|
|
2
|
+
import typescriptConfig from '@playcanvas/eslint-config/typescript';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
...typescriptConfig,
|
|
6
|
+
...reactConfig,
|
|
7
|
+
{
|
|
8
|
+
rules: {
|
|
9
|
+
'react/jsx-uses-react': 'off',
|
|
10
|
+
'react/prop-types': 'off',
|
|
11
|
+
'react/react-in-jsx-scope': 'off'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "playcanvas-project",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "A PlayCanvas React project",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"fmt": "prettier --check .",
|
|
11
|
+
"fmt:fix": "prettier --write .",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"lint:fix": "eslint . --fix",
|
|
14
|
+
"start": "vite preview",
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@playcanvas/react": "^0.11.5",
|
|
19
|
+
"playcanvas": "^2.21.3",
|
|
20
|
+
"react": "^19.2.8",
|
|
21
|
+
"react-dom": "^19.2.8"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@playcanvas/eslint-config": "3.0.0-beta.8",
|
|
25
|
+
"@types/react": "^19.2.18",
|
|
26
|
+
"@types/react-dom": "^19.2.4",
|
|
27
|
+
"@vitejs/plugin-react": "^6.0.5",
|
|
28
|
+
"eslint": "^9.39.5",
|
|
29
|
+
"prettier": "^3.9.6",
|
|
30
|
+
"typescript": "~6.0.3",
|
|
31
|
+
"vite": "^8.2.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.23.2"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@playcanvas/eslint-config/prettier';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
declare module 'playcanvas/scripts/esm/camera-controls.mjs' {
|
|
4
|
+
import type { Script } from 'playcanvas';
|
|
5
|
+
|
|
6
|
+
export const CameraControls: typeof Script & (new () => Script);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module 'playcanvas/scripts/esm/grid.mjs' {
|
|
10
|
+
import type { Script } from 'playcanvas';
|
|
11
|
+
|
|
12
|
+
export const Grid: typeof Script & (new () => Script);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module 'playcanvas/scripts/esm/sky/procedural-sky.mjs' {
|
|
16
|
+
import type { Script } from 'playcanvas';
|
|
17
|
+
|
|
18
|
+
export const ProceduralSky: typeof Script & (new () => Script);
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["src", "vite.config.ts"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>PlayCanvas + React + TS</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#root {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
height: 100vh;
|
|
5
|
+
width: 100vw;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
header {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: row;
|
|
11
|
+
align-items: center;
|
|
12
|
+
padding: 2rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.absolute {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.full-bleed {
|
|
22
|
+
width: 100vw;
|
|
23
|
+
height: 100vh;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.logo {
|
|
28
|
+
height: 2em;
|
|
29
|
+
padding: 0.5em;
|
|
30
|
+
will-change: filter;
|
|
31
|
+
transition: filter 300ms;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.playcanvas-logo {
|
|
35
|
+
border-radius: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.logo:hover {
|
|
39
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.logo.react:hover {
|
|
43
|
+
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.read-the-docs {
|
|
47
|
+
color: #888;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.overlay {
|
|
51
|
+
margin: 0 auto;
|
|
52
|
+
text-align: center;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: stretch;
|
|
57
|
+
width: 100vw;
|
|
58
|
+
height: 100vh;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.overlay a {
|
|
63
|
+
pointer-events: auto;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.overlay > .grow {
|
|
67
|
+
flex-grow: 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.pill {
|
|
71
|
+
background-color: #000;
|
|
72
|
+
color: #fff;
|
|
73
|
+
padding: 0.5rem 1rem;
|
|
74
|
+
border-radius: 0.5rem;
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Application } from '@playcanvas/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import playcanvasLogo from './assets/playcanvas.png';
|
|
5
|
+
import reactLogo from './assets/react.svg';
|
|
6
|
+
import './App.css';
|
|
7
|
+
import Scene from './Scene';
|
|
8
|
+
|
|
9
|
+
function App() {
|
|
10
|
+
const [count, setCount] = useState(0);
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<div className="full-bleed">
|
|
15
|
+
<Application className="playcanvas-app">
|
|
16
|
+
<Scene onClick={() => setCount((count) => count + 1)} />
|
|
17
|
+
</Application>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="absolute overlay">
|
|
20
|
+
<div className="grow">
|
|
21
|
+
<header>
|
|
22
|
+
<h1>PlayCanvas + React</h1>
|
|
23
|
+
<a href="https://developer.playcanvas.com/user-manual/react/" target="_blank" rel="noreferrer">
|
|
24
|
+
<img src={playcanvasLogo} className="playcanvas-logo logo" alt="PlayCanvas logo" />
|
|
25
|
+
</a>
|
|
26
|
+
<a href="https://react.dev" target="_blank" rel="noreferrer">
|
|
27
|
+
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
28
|
+
</a>
|
|
29
|
+
</header>
|
|
30
|
+
</div>
|
|
31
|
+
<div>
|
|
32
|
+
<span className="pill">Click Count: {count}</span>
|
|
33
|
+
<p>
|
|
34
|
+
Edit <code>src/Scene.tsx</code> and save to test HMR
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
<p className="read-the-docs">Click on the PlayCanvas and React logos to learn more</p>
|
|
38
|
+
</div>
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default App;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Entity } from '@playcanvas/react';
|
|
2
|
+
import { Camera, Environment, Render, Script } from '@playcanvas/react/components';
|
|
3
|
+
import { useEnvAtlas, useMaterial } from '@playcanvas/react/hooks';
|
|
4
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
5
|
+
import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The Scene renders a sphere with a grid and camera controls
|
|
10
|
+
*/
|
|
11
|
+
function Scene({ onClick }: SceneProps) {
|
|
12
|
+
const [hovering, setHovering] = useState(false);
|
|
13
|
+
|
|
14
|
+
// Set a material color based on the hover state
|
|
15
|
+
const diffuse = hovering ? 'orange' : 'lightgrey';
|
|
16
|
+
|
|
17
|
+
// Create a material for the sphere
|
|
18
|
+
const material = useMaterial({ diffuse });
|
|
19
|
+
|
|
20
|
+
// Load the environment map
|
|
21
|
+
const { asset: envMap } = useEnvAtlas('/environment-map.png');
|
|
22
|
+
|
|
23
|
+
// Change the mouse cursor based on the hover state
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
document.body.style.cursor = hovering ? 'pointer' : 'default';
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
document.body.style.cursor = '';
|
|
29
|
+
};
|
|
30
|
+
}, [hovering]);
|
|
31
|
+
|
|
32
|
+
// Don't render until the environment map is loaded
|
|
33
|
+
if (!envMap) return null;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
{/* Render some environment lighting using the environment map */}
|
|
38
|
+
<Environment envAtlas={envMap} showSkybox={false} />
|
|
39
|
+
|
|
40
|
+
{/* Render a background grid */}
|
|
41
|
+
<Entity scale={[1000, 1000, 1000]}>
|
|
42
|
+
<Script script={Grid} />
|
|
43
|
+
</Entity>
|
|
44
|
+
|
|
45
|
+
{/* Create a camera entity with camera controls */}
|
|
46
|
+
<Entity name="camera" position={[4, 1, 4]}>
|
|
47
|
+
<Camera clearColor="#171717" />
|
|
48
|
+
<Script script={CameraControls} />
|
|
49
|
+
</Entity>
|
|
50
|
+
|
|
51
|
+
{/* Create and position entity with pointer events */}
|
|
52
|
+
<Entity
|
|
53
|
+
position={[0, 0.5, 0]}
|
|
54
|
+
onClick={onClick}
|
|
55
|
+
onPointerOver={() => setHovering(true)}
|
|
56
|
+
onPointerOut={() => setHovering(false)}
|
|
57
|
+
>
|
|
58
|
+
{/* Render a sphere with the material */}
|
|
59
|
+
<Render type="sphere" material={material} />
|
|
60
|
+
</Entity>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type SceneProps = {
|
|
66
|
+
onClick: () => void;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default Scene;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
a {
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
color: #646cff;
|
|
19
|
+
text-decoration: inherit;
|
|
20
|
+
}
|
|
21
|
+
a:hover {
|
|
22
|
+
color: #535bf2;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
a > img {
|
|
26
|
+
vertical-align: middle;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
margin: 0;
|
|
31
|
+
display: flex;
|
|
32
|
+
place-items: center;
|
|
33
|
+
min-width: 320px;
|
|
34
|
+
min-height: 100vh;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
h1 {
|
|
38
|
+
font-size: 2.4em;
|
|
39
|
+
line-height: 1.1;
|
|
40
|
+
opacity: 0.85;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (prefers-color-scheme: light) {
|
|
44
|
+
:root {
|
|
45
|
+
color: #213547;
|
|
46
|
+
background-color: #ffffff;
|
|
47
|
+
}
|
|
48
|
+
a:hover {
|
|
49
|
+
color: #747bff;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>PlayCanvas + React</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Application, Container, Entity } from '@playcanvas/react';
|
|
2
|
+
import { Camera, Script } from '@playcanvas/react/components';
|
|
3
|
+
import { useModel } from '@playcanvas/react/hooks';
|
|
4
|
+
import { TONEMAP_ACES2 } from 'playcanvas';
|
|
5
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
6
|
+
import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
|
|
7
|
+
|
|
8
|
+
// Swap this for your own model - anything you drop in public/ is served from the site root
|
|
9
|
+
const MODEL_URL = '/playcanvas-cube.glb';
|
|
10
|
+
|
|
11
|
+
function Model() {
|
|
12
|
+
const { asset } = useModel(MODEL_URL);
|
|
13
|
+
|
|
14
|
+
// Don't render until the model is loaded
|
|
15
|
+
if (!asset) return null;
|
|
16
|
+
|
|
17
|
+
return <Container asset={asset} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<Application>
|
|
23
|
+
{/* The procedural sky is both the background and the image-based lighting, so no
|
|
24
|
+
light or environment map asset is needed. Its default luminance is scene-scale
|
|
25
|
+
bright, so turn it down to keep the model out of clipping */}
|
|
26
|
+
<Entity name="sky">
|
|
27
|
+
<Script script={ProceduralSky} luminance={0.2} />
|
|
28
|
+
</Entity>
|
|
29
|
+
|
|
30
|
+
{/* Create a camera entity with orbit controls. The sky is HDR, so it needs tone
|
|
31
|
+
mapping to bring it back into displayable range - without it the whole image blows
|
|
32
|
+
out to white */}
|
|
33
|
+
<Entity name="camera" position={[0, 0, 3]}>
|
|
34
|
+
<Camera toneMapping={TONEMAP_ACES2} />
|
|
35
|
+
<Script script={CameraControls} />
|
|
36
|
+
</Entity>
|
|
37
|
+
|
|
38
|
+
<Model />
|
|
39
|
+
</Application>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default App;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>PlayCanvas + React</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Application, Entity } from '@playcanvas/react';
|
|
2
|
+
import { Camera, Light, Render } from '@playcanvas/react/components';
|
|
3
|
+
import { useAppEvent } from '@playcanvas/react/hooks';
|
|
4
|
+
import type { Entity as PcEntity } from 'playcanvas';
|
|
5
|
+
import { useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
function Scene() {
|
|
8
|
+
const cube = useRef<PcEntity>(null);
|
|
9
|
+
|
|
10
|
+
// Rotate the cube according to the delta time since the last frame
|
|
11
|
+
useAppEvent('update', (dt: number) => cube.current?.rotate(10 * dt, 20 * dt, 30 * dt));
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
<Entity name="camera" position={[0, 0, 3]}>
|
|
16
|
+
<Camera clearColor="#8099e6" />
|
|
17
|
+
</Entity>
|
|
18
|
+
<Entity name="light" rotation={[45, 0, 0]}>
|
|
19
|
+
<Light type="directional" />
|
|
20
|
+
</Entity>
|
|
21
|
+
<Entity name="cube" ref={cube}>
|
|
22
|
+
<Render type="box" />
|
|
23
|
+
</Entity>
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function App() {
|
|
29
|
+
return (
|
|
30
|
+
<Application>
|
|
31
|
+
<Scene />
|
|
32
|
+
</Application>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default App;
|