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
|
@@ -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,39 @@
|
|
|
1
|
+
import { Application, Entity } from '@playcanvas/react';
|
|
2
|
+
import { Camera, GSplat, Script } from '@playcanvas/react/components';
|
|
3
|
+
import { useSplat } from '@playcanvas/react/hooks';
|
|
4
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
5
|
+
|
|
6
|
+
// Hosted so the template stays small. Capture or edit your own splat at https://superspl.at, then
|
|
7
|
+
// drop the .sog or .ply in public/ and point this at it instead
|
|
8
|
+
const SPLAT_URL = 'https://developer.playcanvas.com/assets/toy-cat.sog';
|
|
9
|
+
|
|
10
|
+
function Splat() {
|
|
11
|
+
const { asset } = useSplat(SPLAT_URL);
|
|
12
|
+
|
|
13
|
+
// Don't render until the splat is loaded
|
|
14
|
+
if (!asset) return null;
|
|
15
|
+
|
|
16
|
+
// Splats are captured in their own space, so this one needs offsetting and flipping upright
|
|
17
|
+
return (
|
|
18
|
+
<Entity name="toy cat" position={[0, -0.7, 0]} rotation={[0, 0, 180]}>
|
|
19
|
+
<GSplat asset={asset} />
|
|
20
|
+
</Entity>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Antialiasing is disabled - splat rendering is fragment bound, so MSAA costs a lot and adds little
|
|
25
|
+
function App() {
|
|
26
|
+
return (
|
|
27
|
+
<Application graphicsDeviceOptions={{ antialias: false }}>
|
|
28
|
+
{/* Create a camera entity with orbit controls */}
|
|
29
|
+
<Entity name="camera" position={[0, 0, 2.5]}>
|
|
30
|
+
<Camera />
|
|
31
|
+
<Script script={CameraControls} />
|
|
32
|
+
</Entity>
|
|
33
|
+
|
|
34
|
+
<Splat />
|
|
35
|
+
</Application>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default App;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# PlayCanvas Web Components + TypeScript Starter
|
|
2
|
+
|
|
3
|
+
A Vite-powered `@playcanvas/web-components` project with TypeScript, ESLint and Prettier. The scene is
|
|
4
|
+
declared as HTML custom elements in `index.html`.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
Node.js 22.23.2 or later.
|
|
9
|
+
|
|
10
|
+
## Getting started
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm create playcanvas@latest playcanvas-project -- --template web-components
|
|
14
|
+
cd playcanvas-project
|
|
15
|
+
npm install
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Open <http://localhost:5173>. Edit `index.html` to change the scene.
|
|
20
|
+
|
|
21
|
+
## Scripts
|
|
22
|
+
|
|
23
|
+
| Command | Description |
|
|
24
|
+
| ------------------- | --------------------------------- |
|
|
25
|
+
| `npm run dev` | Start the Vite development server |
|
|
26
|
+
| `npm run build` | Build for production |
|
|
27
|
+
| `npm run start` | Preview the production build |
|
|
28
|
+
| `npm run lint` | Run ESLint |
|
|
29
|
+
| `npm run fmt` | Check formatting |
|
|
30
|
+
| `npm run typecheck` | Run TypeScript checks |
|
|
31
|
+
|
|
32
|
+
Run `npm run build` to generate a deployable static site in `dist/`.
|
|
33
|
+
|
|
34
|
+
## How it fits together
|
|
35
|
+
|
|
36
|
+
`index.html` holds the scene: `<pc-app>` wraps a `<pc-scene>` of `<pc-entity>` elements, each with
|
|
37
|
+
components such as `<pc-camera>`, `<pc-light>` and `<pc-render>` attached. Entities are real DOM elements,
|
|
38
|
+
so `document.querySelector` reaches them and `setAttribute` changes them.
|
|
39
|
+
|
|
40
|
+
`src/main.ts` covers the two things HTML alone cannot do:
|
|
41
|
+
|
|
42
|
+
- **Attaching engine scripts.** `<pc-script name="...">` resolves scripts that `<pc-asset>` fetched at
|
|
43
|
+
runtime. Scripts imported through the bundler — like `CameraControls` and `Grid` here — are attached
|
|
44
|
+
through the entity instead, reached via `whenReady()` and the element's `.entity` property.
|
|
45
|
+
- **Responding to pointer input.** `<pc-app>` picks whatever is under the pointer and dispatches an
|
|
46
|
+
ordinary `PointerEvent` on the `<pc-entity>` it hit, so `addEventListener('pointerup', ...)` works on a
|
|
47
|
+
3D entity exactly as it would on a `<button>`. Events are only generated for entities that have a
|
|
48
|
+
listener.
|
|
49
|
+
|
|
50
|
+
## Editor support
|
|
51
|
+
|
|
52
|
+
The package ships a Custom Elements Manifest, which gives tag and attribute completion in `index.html`.
|
|
53
|
+
JetBrains IDEs pick it up automatically. For VS Code, add to `.vscode/settings.json`:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"html.customData": ["./node_modules/@playcanvas/web-components/dist/vscode.html-custom-data.json"]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Further reading
|
|
62
|
+
|
|
63
|
+
- [PlayCanvas Web Components manual](https://developer.playcanvas.com/user-manual/web-components/)
|
|
64
|
+
- [Web Components examples](https://playcanvas.github.io/web-components/examples)
|
|
65
|
+
- [Vite documentation](https://vite.dev/)
|
|
66
|
+
- [TypeScript documentation](https://www.typescriptlang.org/docs/)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
dist
|
|
12
|
+
dist-ssr
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Editor directories and files
|
|
16
|
+
.vscode/*
|
|
17
|
+
!.vscode/extensions.json
|
|
18
|
+
.idea
|
|
19
|
+
.DS_Store
|
|
20
|
+
*.suo
|
|
21
|
+
*.ntvs*
|
|
22
|
+
*.njsproj
|
|
23
|
+
*.sln
|
|
24
|
+
*.sw?
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "playcanvas-project",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "A PlayCanvas Web Components 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/web-components": "^0.10.1",
|
|
19
|
+
"playcanvas": "^2.21.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@playcanvas/eslint-config": "3.0.0-beta.8",
|
|
23
|
+
"eslint": "^9.39.5",
|
|
24
|
+
"prettier": "^3.9.6",
|
|
25
|
+
"typescript": "~6.0.3",
|
|
26
|
+
"vite": "^8.2.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22.23.2"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@playcanvas/eslint-config/prettier';
|
|
Binary file
|
|
@@ -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,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"erasableSyntaxOnly": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"noUncheckedSideEffectImports": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
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 + Web Components</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<pc-app>
|
|
11
|
+
<!-- Materials are declared once and referenced by id -->
|
|
12
|
+
<pc-material id="sphere-material" diffuse="lightgrey"></pc-material>
|
|
13
|
+
|
|
14
|
+
<pc-scene>
|
|
15
|
+
<!-- Camera. Orbit controls are attached in src/main.ts -->
|
|
16
|
+
<pc-entity name="camera" position="4 1 4">
|
|
17
|
+
<pc-camera clear-color="#171717"></pc-camera>
|
|
18
|
+
</pc-entity>
|
|
19
|
+
|
|
20
|
+
<!-- Lighting comes from the environment map, which is applied in src/main.ts -->
|
|
21
|
+
|
|
22
|
+
<!-- Background grid. The grid script is attached in src/main.ts -->
|
|
23
|
+
<pc-entity name="grid" scale="1000 1000 1000"></pc-entity>
|
|
24
|
+
|
|
25
|
+
<!-- The sphere. pc-app dispatches pointer events onto whatever the pointer hits -->
|
|
26
|
+
<pc-entity name="sphere" position="0 0.5 0">
|
|
27
|
+
<pc-render type="sphere" material="sphere-material"></pc-render>
|
|
28
|
+
</pc-entity>
|
|
29
|
+
</pc-scene>
|
|
30
|
+
</pc-app>
|
|
31
|
+
|
|
32
|
+
<div class="absolute overlay">
|
|
33
|
+
<div class="grow">
|
|
34
|
+
<header>
|
|
35
|
+
<h1>PlayCanvas + Web Components</h1>
|
|
36
|
+
<a href="https://developer.playcanvas.com/user-manual/web-components/" target="_blank">
|
|
37
|
+
<img src="/src/assets/playcanvas.png" class="playcanvas-logo logo" alt="PlayCanvas logo" />
|
|
38
|
+
</a>
|
|
39
|
+
<a href="https://www.typescriptlang.org/" target="_blank">
|
|
40
|
+
<img src="/src/assets/typescript.svg" class="logo typescript" alt="TypeScript logo" />
|
|
41
|
+
</a>
|
|
42
|
+
</header>
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
<span id="counter" class="pill">Click Count: 0</span>
|
|
46
|
+
<p>Edit <code>index.html</code> and save to reload the scene</p>
|
|
47
|
+
</div>
|
|
48
|
+
<p class="read-the-docs">Click on the PlayCanvas and TypeScript logos to learn more</p>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<script type="module" src="/src/main.ts"></script>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { EntityElement } from '@playcanvas/web-components';
|
|
2
|
+
import { whenReady } from '@playcanvas/web-components';
|
|
3
|
+
import type { Texture } from 'playcanvas';
|
|
4
|
+
import { Asset, TEXTURETYPE_RGBP } from 'playcanvas';
|
|
5
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
6
|
+
import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
|
|
7
|
+
|
|
8
|
+
import './style.css';
|
|
9
|
+
|
|
10
|
+
const HOVER_COLOR = 'orange';
|
|
11
|
+
const DEFAULT_COLOR = 'lightgrey';
|
|
12
|
+
|
|
13
|
+
// The camera is awaited as <pc-camera> rather than the <pc-entity> holding it - an entity becomes
|
|
14
|
+
// ready before its component children do, and CameraControls needs the camera component to exist
|
|
15
|
+
const [appElement, cameraComponent, grid, sphere] = await Promise.all([
|
|
16
|
+
whenReady('pc-app'),
|
|
17
|
+
whenReady('pc-camera'),
|
|
18
|
+
whenReady<EntityElement>('pc-entity[name="grid"]'),
|
|
19
|
+
whenReady<EntityElement>('pc-entity[name="sphere"]')
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
// This is a pre-generated environment atlas rather than an equirectangular image, so it is applied
|
|
23
|
+
// straight to the scene instead of through <pc-sky>
|
|
24
|
+
const app = appElement.app!;
|
|
25
|
+
const envAtlas = new Asset(
|
|
26
|
+
'env-atlas',
|
|
27
|
+
'texture',
|
|
28
|
+
{ url: '/environment-map.png' },
|
|
29
|
+
{
|
|
30
|
+
type: TEXTURETYPE_RGBP,
|
|
31
|
+
mipmaps: false
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
app.assets.add(envAtlas);
|
|
36
|
+
await new Promise<void>((resolve) => {
|
|
37
|
+
envAtlas.ready(() => resolve());
|
|
38
|
+
app.assets.load(envAtlas);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Light the scene from the environment, with the skybox itself left hidden
|
|
42
|
+
app.scene.envAtlas = envAtlas.resource as Texture;
|
|
43
|
+
const skyboxLayer = app.scene.layers.getLayerByName('Skybox');
|
|
44
|
+
if (skyboxLayer) {
|
|
45
|
+
skyboxLayer.enabled = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Engine scripts are resolved by the bundler, so they are attached through the entity rather than
|
|
49
|
+
// with <pc-script name="...">, which only resolves scripts fetched at runtime by <pc-asset>
|
|
50
|
+
const camera = cameraComponent.closestEntity;
|
|
51
|
+
camera?.entity?.addComponent('script');
|
|
52
|
+
camera?.entity?.script?.create(CameraControls);
|
|
53
|
+
grid.entity?.addComponent('script');
|
|
54
|
+
grid.entity?.script?.create(Grid);
|
|
55
|
+
|
|
56
|
+
const material = document.querySelector('pc-material')!;
|
|
57
|
+
const counterElement = document.getElementById('counter')!;
|
|
58
|
+
|
|
59
|
+
let count = 0;
|
|
60
|
+
|
|
61
|
+
// pc-app picks whatever is under the pointer and dispatches a regular DOM PointerEvent on it, so
|
|
62
|
+
// the sphere is wired up like any other element
|
|
63
|
+
sphere.addEventListener('pointerenter', () => {
|
|
64
|
+
material.setAttribute('diffuse', HOVER_COLOR);
|
|
65
|
+
document.body.style.cursor = 'pointer';
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
sphere.addEventListener('pointerleave', () => {
|
|
69
|
+
material.setAttribute('diffuse', DEFAULT_COLOR);
|
|
70
|
+
document.body.style.cursor = 'default';
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
sphere.addEventListener('pointerup', () => {
|
|
74
|
+
count++;
|
|
75
|
+
counterElement.textContent = `Click Count: ${count}`;
|
|
76
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
header {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: row;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 2rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
a {
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
color: #646cff;
|
|
26
|
+
pointer-events: auto;
|
|
27
|
+
text-decoration: inherit;
|
|
28
|
+
}
|
|
29
|
+
a:hover {
|
|
30
|
+
color: #535bf2;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
body {
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
padding: 0;
|
|
37
|
+
margin: 0;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
h1 {
|
|
42
|
+
font-size: 2.4em;
|
|
43
|
+
line-height: 1.1;
|
|
44
|
+
opacity: 0.85;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.logo {
|
|
48
|
+
height: 2em;
|
|
49
|
+
padding: 0.5em;
|
|
50
|
+
will-change: filter;
|
|
51
|
+
transition: filter 300ms;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.playcanvas-logo {
|
|
55
|
+
border-radius: 100%;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.logo:hover {
|
|
59
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.logo.typescript:hover {
|
|
63
|
+
filter: drop-shadow(0 0 2em #3178c6aa);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pc-app {
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.read-the-docs {
|
|
71
|
+
color: #888;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@media (prefers-color-scheme: light) {
|
|
75
|
+
:root {
|
|
76
|
+
color: #213547;
|
|
77
|
+
background-color: #ffffff;
|
|
78
|
+
}
|
|
79
|
+
a:hover {
|
|
80
|
+
color: #747bff;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.overlay {
|
|
85
|
+
margin: 0 auto;
|
|
86
|
+
text-align: center;
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: stretch;
|
|
91
|
+
width: 100vw;
|
|
92
|
+
height: 100vh;
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.overlay > .grow {
|
|
97
|
+
flex-grow: 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.absolute {
|
|
101
|
+
position: absolute;
|
|
102
|
+
top: 0;
|
|
103
|
+
left: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.pill {
|
|
107
|
+
background-color: #000;
|
|
108
|
+
color: #fff;
|
|
109
|
+
padding: 0.5rem 1rem;
|
|
110
|
+
border-radius: 0.5rem;
|
|
111
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 + Web Components</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<pc-app>
|
|
11
|
+
<!-- Swap this for your own model - anything you drop in public/ is served from the site root -->
|
|
12
|
+
<pc-asset id="model" src="/playcanvas-cube.glb"></pc-asset>
|
|
13
|
+
|
|
14
|
+
<pc-scene>
|
|
15
|
+
<!-- Camera. Orbit controls are attached in src/main.ts. The sky is HDR, so it needs
|
|
16
|
+
tone mapping to bring it back into displayable range - without it the whole
|
|
17
|
+
image blows out to white -->
|
|
18
|
+
<pc-entity name="camera" position="0 0 3">
|
|
19
|
+
<pc-camera tonemap="aces2"></pc-camera>
|
|
20
|
+
</pc-entity>
|
|
21
|
+
|
|
22
|
+
<!-- The procedural sky is both the background and the image-based lighting, so no
|
|
23
|
+
light or environment map asset is needed. The script is attached in src/main.ts -->
|
|
24
|
+
<pc-entity name="sky"></pc-entity>
|
|
25
|
+
|
|
26
|
+
<pc-entity name="model">
|
|
27
|
+
<pc-model asset="model"></pc-model>
|
|
28
|
+
</pc-entity>
|
|
29
|
+
</pc-scene>
|
|
30
|
+
</pc-app>
|
|
31
|
+
|
|
32
|
+
<script type="module" src="/src/main.ts"></script>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EntityElement } from '@playcanvas/web-components';
|
|
2
|
+
import { whenReady } from '@playcanvas/web-components';
|
|
3
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
4
|
+
import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
|
|
5
|
+
|
|
6
|
+
import './style.css';
|
|
7
|
+
|
|
8
|
+
// The camera is awaited as <pc-camera> rather than the <pc-entity> holding it - an entity becomes
|
|
9
|
+
// ready before its component children do, and CameraControls needs the camera component to exist
|
|
10
|
+
const [cameraComponent, sky] = await Promise.all([
|
|
11
|
+
whenReady('pc-camera'),
|
|
12
|
+
whenReady<EntityElement>('pc-entity[name="sky"]')
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
// Engine scripts are resolved by the bundler, so they are attached through the entity rather than
|
|
16
|
+
// with <pc-script name="...">, which only resolves scripts fetched at runtime by <pc-asset>
|
|
17
|
+
const camera = cameraComponent.closestEntity;
|
|
18
|
+
camera?.entity?.addComponent('script');
|
|
19
|
+
camera?.entity?.script?.create(CameraControls);
|
|
20
|
+
|
|
21
|
+
sky.entity?.addComponent('script');
|
|
22
|
+
|
|
23
|
+
// The sky's default luminance is scene-scale bright, so turn it down to keep the model out of clipping
|
|
24
|
+
sky.entity?.script?.create(ProceduralSky, {
|
|
25
|
+
properties: { luminance: 0.2 }
|
|
26
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
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 + Web Components</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<pc-app>
|
|
11
|
+
<pc-scene>
|
|
12
|
+
<pc-entity name="camera" position="0 0 3">
|
|
13
|
+
<pc-camera clear-color="#8099e6"></pc-camera>
|
|
14
|
+
</pc-entity>
|
|
15
|
+
|
|
16
|
+
<pc-entity name="light" rotation="45 0 0">
|
|
17
|
+
<pc-light></pc-light>
|
|
18
|
+
</pc-entity>
|
|
19
|
+
|
|
20
|
+
<!-- The rotate script is attached in src/main.ts -->
|
|
21
|
+
<pc-entity name="cube">
|
|
22
|
+
<pc-render type="box"></pc-render>
|
|
23
|
+
</pc-entity>
|
|
24
|
+
</pc-scene>
|
|
25
|
+
</pc-app>
|
|
26
|
+
|
|
27
|
+
<script type="module" src="/src/main.ts"></script>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EntityElement } from '@playcanvas/web-components';
|
|
2
|
+
import { whenReady } from '@playcanvas/web-components';
|
|
3
|
+
import { Script } from 'playcanvas';
|
|
4
|
+
|
|
5
|
+
import './style.css';
|
|
6
|
+
|
|
7
|
+
class Rotate extends Script {
|
|
8
|
+
static scriptName = 'rotate';
|
|
9
|
+
|
|
10
|
+
update(dt: number) {
|
|
11
|
+
this.entity.rotate(10 * dt, 20 * dt, 30 * dt);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Engine scripts are resolved by the bundler, so they are attached through the entity rather than
|
|
16
|
+
// with <pc-script name="...">, which only resolves scripts fetched at runtime by <pc-asset>
|
|
17
|
+
const cube = await whenReady<EntityElement>('pc-entity[name="cube"]');
|
|
18
|
+
|
|
19
|
+
cube.entity?.addComponent('script');
|
|
20
|
+
cube.entity?.script?.create(Rotate);
|