create-playcanvas 0.5.0 → 0.6.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 +20 -27
- package/dist/index.mjs +44 -42
- package/package.json +1 -1
- package/templates/_features/physics/assets/public/ammo/LICENSE +21 -0
- package/templates/_features/physics/assets/public/ammo/SOURCE.md +16 -0
- package/templates/_features/physics/assets/public/ammo/ammo.js +973 -0
- package/templates/_features/physics/assets/public/ammo/ammo.wasm.js +937 -0
- package/templates/_features/physics/assets/public/ammo/ammo.wasm.wasm +0 -0
- package/templates/_features/physics/react/package.json +5 -0
- package/templates/_features/physics/react/src/sync-ammo.d.ts +4 -0
- package/templates/_shared/ar-placement/README.md +7 -0
- package/templates/_shared/ar-placement/src/ar-placement.ts +123 -0
- package/templates/_shared/first-person-controller/README.md +7 -0
- package/templates/_shared/first-person-controller/src/controller.ts +18 -0
- package/templates/_shared/first-person-controller/src/scripts.d.ts +5 -0
- package/templates/_shared/interactive-sphere/README.md +7 -0
- package/templates/_shared/model-viewer/README.md +7 -0
- package/templates/_shared/physics-playground/README.md +7 -0
- package/templates/_shared/product-configurator/ASSET_LICENSES.md +3 -0
- package/templates/_shared/product-configurator/README.md +7 -0
- package/templates/_shared/product-configurator/src/product.css +14 -0
- package/templates/_shared/scene-editor/README.md +7 -0
- package/templates/_shared/scene-editor/src/scene-editor.ts +57 -0
- package/templates/_shared/spinning-cube/README.md +7 -0
- package/templates/_shared/splat-viewer/README.md +7 -0
- package/templates/_shared/sprite-game/README.md +7 -0
- package/templates/_shared/sprite-game/src/sprite-game.ts +197 -0
- package/templates/_shared/third-person-controller/README.md +7 -0
- package/templates/_shared/third-person-controller/src/controller.ts +53 -0
- package/templates/_shared/third-person-controller/src/scripts.d.ts +5 -0
- package/templates/_shared/vr-starter/README.md +7 -0
- package/templates/_shared/vr-starter/src/vr.ts +40 -0
- package/templates/_shared/vr-starter/src/xr.d.ts +11 -0
- package/templates/engine/ar-placement/src/main.ts +77 -0
- package/templates/engine/base/.prettierignore +1 -0
- package/templates/engine/base/README.md +1 -1
- package/templates/engine/base/_npmrc +1 -0
- package/templates/engine/base/eslint.config.mjs +1 -1
- package/templates/engine/base/index.html +13 -0
- package/templates/engine/base/package.json +2 -2
- package/templates/engine/base/src/starter.css +104 -0
- package/templates/engine/first-person-controller/src/main.ts +139 -0
- package/templates/engine/interactive-sphere/src/App.ts +9 -7
- package/templates/engine/interactive-sphere/src/main.ts +2 -29
- package/templates/engine/interactive-sphere/src/style.css +18 -97
- package/templates/engine/model-viewer/src/main.ts +3 -2
- package/templates/engine/physics-playground/src/main.ts +132 -0
- package/templates/engine/product-configurator/src/main.ts +156 -0
- package/templates/engine/scene-editor/src/main.ts +90 -0
- package/templates/engine/splat-viewer/src/main.ts +4 -3
- package/templates/engine/sprite-game/src/main.ts +72 -0
- package/templates/engine/third-person-controller/src/main.ts +216 -0
- package/templates/engine/vr-starter/src/main.ts +101 -0
- package/templates/index.ts +66 -5
- package/templates/react/ar-placement/src/App.tsx +58 -0
- package/templates/react/base/.prettierignore +1 -0
- package/templates/react/base/README.md +1 -1
- package/templates/react/base/_npmrc +1 -0
- package/templates/react/base/eslint.config.mjs +1 -1
- package/templates/react/base/index.html +13 -0
- package/templates/react/base/package.json +2 -2
- package/templates/react/base/src/main.tsx +10 -0
- package/templates/react/base/src/starter.css +104 -0
- package/templates/react/first-person-controller/src/App.tsx +115 -0
- package/templates/react/interactive-sphere/src/App.css +18 -68
- package/templates/react/interactive-sphere/src/App.tsx +1 -22
- package/templates/react/interactive-sphere/src/Scene.tsx +7 -6
- package/templates/react/interactive-sphere/src/index.css +1 -48
- package/templates/react/model-viewer/src/App.tsx +2 -2
- package/templates/react/physics-playground/src/App.tsx +85 -0
- package/templates/react/product-configurator/src/App.tsx +157 -0
- package/templates/react/scene-editor/src/App.tsx +84 -0
- package/templates/react/splat-viewer/src/App.tsx +3 -3
- package/templates/react/sprite-game/src/App.tsx +38 -0
- package/templates/react/third-person-controller/src/App.tsx +193 -0
- package/templates/react/vr-starter/src/App.tsx +99 -0
- package/templates/web-components/ar-placement/index.html +44 -0
- package/templates/web-components/ar-placement/src/main.ts +13 -0
- package/templates/web-components/base/.prettierignore +1 -0
- package/templates/web-components/base/README.md +1 -1
- package/templates/web-components/base/_npmrc +1 -0
- package/templates/web-components/base/eslint.config.mjs +1 -1
- package/templates/web-components/base/package.json +3 -3
- package/templates/web-components/base/src/starter.css +104 -0
- package/templates/web-components/first-person-controller/index.html +168 -0
- package/templates/web-components/first-person-controller/src/main.ts +15 -0
- package/templates/web-components/interactive-sphere/index.html +5 -22
- package/templates/web-components/interactive-sphere/src/main.ts +5 -4
- package/templates/web-components/interactive-sphere/src/style.css +18 -76
- package/templates/web-components/model-viewer/index.html +1 -1
- package/templates/web-components/model-viewer/src/main.ts +2 -1
- package/templates/web-components/physics-playground/index.html +51 -0
- package/templates/web-components/physics-playground/src/main.ts +32 -0
- package/templates/web-components/product-configurator/index.html +51 -0
- package/templates/web-components/product-configurator/src/main.ts +93 -0
- package/templates/web-components/scene-editor/index.html +51 -0
- package/templates/web-components/scene-editor/src/main.ts +26 -0
- package/templates/web-components/spinning-cube/src/main.ts +1 -0
- package/templates/web-components/splat-viewer/index.html +2 -2
- package/templates/web-components/splat-viewer/src/main.ts +2 -1
- package/templates/web-components/sprite-game/index.html +34 -0
- package/templates/web-components/sprite-game/src/main.ts +9 -0
- package/templates/web-components/third-person-controller/index.html +183 -0
- package/templates/web-components/third-person-controller/src/main.ts +16 -0
- package/templates/web-components/vr-starter/index.html +80 -0
- package/templates/web-components/vr-starter/src/main.ts +12 -0
- package/templates/engine/interactive-sphere/src/assets/typescript.svg +0 -1
- package/templates/react/interactive-sphere/src/assets/react.svg +0 -1
- package/templates/web-components/interactive-sphere/src/assets/typescript.svg +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppBase,
|
|
3
|
+
AppOptions,
|
|
4
|
+
CameraComponentSystem,
|
|
5
|
+
CollisionComponentSystem,
|
|
6
|
+
Color,
|
|
7
|
+
Entity,
|
|
8
|
+
FILLMODE_FILL_WINDOW,
|
|
9
|
+
LightComponentSystem,
|
|
10
|
+
RenderComponentSystem,
|
|
11
|
+
RESOLUTION_AUTO,
|
|
12
|
+
RigidBodyComponentSystem,
|
|
13
|
+
ScriptComponentSystem,
|
|
14
|
+
StandardMaterial,
|
|
15
|
+
Vec3,
|
|
16
|
+
WasmModule,
|
|
17
|
+
createGraphicsDevice
|
|
18
|
+
} from 'playcanvas';
|
|
19
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
20
|
+
|
|
21
|
+
import './starter.css';
|
|
22
|
+
|
|
23
|
+
const COLORS = [new Color(0.16, 0.68, 0.9), new Color(0.95, 0.34, 0.22), new Color(0.98, 0.68, 0.18)];
|
|
24
|
+
const TYPES = ['box', 'sphere', 'capsule', 'cylinder'] as const;
|
|
25
|
+
const SCALES: [number, number, number][] = [
|
|
26
|
+
[0.9, 0.9, 0.9],
|
|
27
|
+
[0.9, 0.9, 0.9],
|
|
28
|
+
[0.7, 1.1, 0.7],
|
|
29
|
+
[0.8, 1.1, 0.8]
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
WasmModule.setConfig('Ammo', {
|
|
33
|
+
glueUrl: '/ammo/ammo.wasm.js',
|
|
34
|
+
wasmUrl: '/ammo/ammo.wasm.wasm',
|
|
35
|
+
fallbackUrl: '/ammo/ammo.js'
|
|
36
|
+
});
|
|
37
|
+
await new Promise<void>((resolve) => {
|
|
38
|
+
WasmModule.getInstance('Ammo', () => resolve());
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
document.body.insertAdjacentHTML(
|
|
42
|
+
'beforeend',
|
|
43
|
+
'<div class="hud"><section class="panel"><h1>Physics Playground</h1><p>Rigid bodies, collisions and runtime spawning.</p><div class="controls"><button id="spawn">Spawn object</button><button id="reset">Reset</button></div></section></div>'
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
47
|
+
const device = await createGraphicsDevice(canvas);
|
|
48
|
+
const options = new AppOptions();
|
|
49
|
+
options.graphicsDevice = device;
|
|
50
|
+
options.componentSystems = [
|
|
51
|
+
RenderComponentSystem,
|
|
52
|
+
CameraComponentSystem,
|
|
53
|
+
LightComponentSystem,
|
|
54
|
+
ScriptComponentSystem,
|
|
55
|
+
CollisionComponentSystem,
|
|
56
|
+
RigidBodyComponentSystem
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const app = new AppBase(canvas);
|
|
60
|
+
app.init(options);
|
|
61
|
+
app.start();
|
|
62
|
+
app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
63
|
+
app.setCanvasResolution(RESOLUTION_AUTO);
|
|
64
|
+
app.systems.rigidbody!.gravity.set(0, -9.81, 0);
|
|
65
|
+
|
|
66
|
+
const materials = COLORS.map((color) => {
|
|
67
|
+
const material = new StandardMaterial();
|
|
68
|
+
material.diffuse = color;
|
|
69
|
+
material.gloss = 0.45;
|
|
70
|
+
material.update();
|
|
71
|
+
return material;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const floorMaterial = new StandardMaterial();
|
|
75
|
+
floorMaterial.diffuse = new Color(0.18, 0.2, 0.24);
|
|
76
|
+
floorMaterial.gloss = 0.2;
|
|
77
|
+
floorMaterial.update();
|
|
78
|
+
|
|
79
|
+
const floor = new Entity('floor');
|
|
80
|
+
floor.setLocalScale(10, 0.2, 10);
|
|
81
|
+
floor.setPosition(0, -0.1, 0);
|
|
82
|
+
floor.addComponent('render', { type: 'box', material: floorMaterial });
|
|
83
|
+
floor.addComponent('collision', { type: 'box', halfExtents: new Vec3(5, 0.1, 5) });
|
|
84
|
+
floor.addComponent('rigidbody', { type: 'static' });
|
|
85
|
+
app.root.addChild(floor);
|
|
86
|
+
|
|
87
|
+
const bodies: Entity[] = [];
|
|
88
|
+
const spawn = () => {
|
|
89
|
+
const i = bodies.length;
|
|
90
|
+
const type = TYPES[i % TYPES.length];
|
|
91
|
+
const entity = new Entity(type);
|
|
92
|
+
entity.setPosition(((i % 4) - 1.5) * 1.1, 0.7 + Math.floor(i / 4) * 1.1, (Math.floor(i / 4) - 1) * 1.4);
|
|
93
|
+
entity.setEulerAngles(i * 13, i * 29, 0);
|
|
94
|
+
entity.setLocalScale(...SCALES[i % SCALES.length]);
|
|
95
|
+
entity.addComponent('render', { type, material: materials[i % materials.length] });
|
|
96
|
+
entity.addComponent('collision', {
|
|
97
|
+
type,
|
|
98
|
+
halfExtents: new Vec3(0.5, 0.5, 0.5),
|
|
99
|
+
radius: 0.5
|
|
100
|
+
});
|
|
101
|
+
entity.addComponent('rigidbody', { type: 'dynamic', mass: 1, restitution: 0.35 });
|
|
102
|
+
app.root.addChild(entity);
|
|
103
|
+
bodies.push(entity);
|
|
104
|
+
};
|
|
105
|
+
const reset = () => {
|
|
106
|
+
bodies.splice(0).forEach((entity) => entity.destroy());
|
|
107
|
+
for (let i = 0; i < 12; i++) spawn();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const camera = new Entity('camera');
|
|
111
|
+
camera.setPosition(10, 8, 10);
|
|
112
|
+
camera.lookAt(0, 2, 0);
|
|
113
|
+
camera.addComponent('camera', { clearColor: new Color(0.05, 0.07, 0.11) });
|
|
114
|
+
camera.addComponent('script');
|
|
115
|
+
camera.script!.create(CameraControls, { properties: { sceneSize: 10 } });
|
|
116
|
+
app.root.addChild(camera);
|
|
117
|
+
|
|
118
|
+
const light = new Entity('light');
|
|
119
|
+
light.addComponent('light', {
|
|
120
|
+
type: 'directional',
|
|
121
|
+
intensity: 2.5,
|
|
122
|
+
castShadows: true,
|
|
123
|
+
shadowBias: 0.2,
|
|
124
|
+
normalOffsetBias: 0.05
|
|
125
|
+
});
|
|
126
|
+
light.setEulerAngles(45, 35, 0);
|
|
127
|
+
app.root.addChild(light);
|
|
128
|
+
|
|
129
|
+
document.getElementById('spawn')!.onclick = spawn;
|
|
130
|
+
document.getElementById('reset')!.onclick = reset;
|
|
131
|
+
reset();
|
|
132
|
+
window.addEventListener('resize', () => app.resizeCanvas());
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { ContainerResource, RenderComponent } from 'playcanvas';
|
|
2
|
+
import {
|
|
3
|
+
AppBase,
|
|
4
|
+
AppOptions,
|
|
5
|
+
Asset,
|
|
6
|
+
AssetListLoader,
|
|
7
|
+
CameraComponentSystem,
|
|
8
|
+
Color,
|
|
9
|
+
ContainerHandler,
|
|
10
|
+
CylinderGeometry,
|
|
11
|
+
Entity,
|
|
12
|
+
FILLMODE_FILL_WINDOW,
|
|
13
|
+
LightComponentSystem,
|
|
14
|
+
Mesh,
|
|
15
|
+
MeshInstance,
|
|
16
|
+
RenderComponentSystem,
|
|
17
|
+
RESOLUTION_AUTO,
|
|
18
|
+
ScriptComponentSystem,
|
|
19
|
+
StandardMaterial,
|
|
20
|
+
TextureHandler,
|
|
21
|
+
TONEMAP_ACES2,
|
|
22
|
+
Vec2,
|
|
23
|
+
Vec3,
|
|
24
|
+
createGraphicsDevice
|
|
25
|
+
} from 'playcanvas';
|
|
26
|
+
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
|
|
27
|
+
import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
|
|
28
|
+
|
|
29
|
+
import './product.css';
|
|
30
|
+
import './starter.css';
|
|
31
|
+
|
|
32
|
+
const PRODUCT_URL = 'https://developer.playcanvas.com/assets/lambo.glb';
|
|
33
|
+
const PAINTS = [
|
|
34
|
+
{ name: 'Crimson', color: '#d6293e', shade: '#9b1731' },
|
|
35
|
+
{ name: 'Arctic', color: '#d8e4e9', shade: '#8fa8b5' },
|
|
36
|
+
{ name: 'Volt', color: '#9acb34', shade: '#3c742d' }
|
|
37
|
+
];
|
|
38
|
+
const CAMERA_FOCUS = new Vec3(0, 0.8, 0);
|
|
39
|
+
const CAMERA_PITCH = new Vec2(-85, -4);
|
|
40
|
+
|
|
41
|
+
document.body.insertAdjacentHTML(
|
|
42
|
+
'beforeend',
|
|
43
|
+
`<div class="hud"><section class="panel"><h1>Product Configurator</h1><p>Choose a paint finish, then drag to orbit.</p><div class="controls variants" id="paints">${PAINTS.map(({ name, color }, i) => `<button aria-pressed="${i === 0}" data-color="${color}"><span class="finish" style="--color:${color}"></span>${name}</button>`).join('')}</div></section></div>`
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
47
|
+
const device = await createGraphicsDevice(canvas);
|
|
48
|
+
device.maxPixelRatio = Math.min(device.maxPixelRatio, 2);
|
|
49
|
+
|
|
50
|
+
const options = new AppOptions();
|
|
51
|
+
options.graphicsDevice = device;
|
|
52
|
+
options.componentSystems = [RenderComponentSystem, CameraComponentSystem, LightComponentSystem, ScriptComponentSystem];
|
|
53
|
+
options.resourceHandlers = [TextureHandler, ContainerHandler];
|
|
54
|
+
|
|
55
|
+
const app = new AppBase(canvas);
|
|
56
|
+
app.init(options);
|
|
57
|
+
app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
58
|
+
app.setCanvasResolution(RESOLUTION_AUTO);
|
|
59
|
+
|
|
60
|
+
const product = new Asset('car', 'container', { url: PRODUCT_URL });
|
|
61
|
+
await new Promise<void>((resolve) => new AssetListLoader([product], app.assets).load(() => resolve()));
|
|
62
|
+
|
|
63
|
+
app.start();
|
|
64
|
+
|
|
65
|
+
const car = (product.resource as ContainerResource).instantiateRenderEntity();
|
|
66
|
+
car.setLocalEulerAngles(-90, -25, 0);
|
|
67
|
+
app.root.addChild(car);
|
|
68
|
+
app.root.syncHierarchy();
|
|
69
|
+
|
|
70
|
+
const meshes = (car.findComponents('render') as RenderComponent[]).flatMap((render) => render.meshInstances);
|
|
71
|
+
const bounds = meshes[0].aabb.clone();
|
|
72
|
+
meshes.slice(1).forEach((mesh) => bounds.add(mesh.aabb));
|
|
73
|
+
const scale = 5 / Math.max(bounds.halfExtents.x * 2, bounds.halfExtents.z * 2);
|
|
74
|
+
car.setLocalScale(scale, scale, scale);
|
|
75
|
+
app.root.syncHierarchy();
|
|
76
|
+
bounds.copy(meshes[0].aabb);
|
|
77
|
+
meshes.slice(1).forEach((mesh) => bounds.add(mesh.aabb));
|
|
78
|
+
car.setPosition(-bounds.center.x, 0.03 - bounds.getMin().y, -bounds.center.z);
|
|
79
|
+
|
|
80
|
+
const body = meshes.filter((mesh) => mesh.material.name === 'material');
|
|
81
|
+
const panels = meshes.filter((mesh) => mesh.material.name === 'material_1');
|
|
82
|
+
const paint = body[0].material.clone() as StandardMaterial;
|
|
83
|
+
const shade = panels[0].material.clone() as StandardMaterial;
|
|
84
|
+
body.forEach((mesh) => (mesh.material = paint));
|
|
85
|
+
panels.forEach((mesh) => (mesh.material = shade));
|
|
86
|
+
paint.diffuse.fromString(PAINTS[0].color);
|
|
87
|
+
shade.diffuse.fromString(PAINTS[0].shade);
|
|
88
|
+
paint.update();
|
|
89
|
+
shade.update();
|
|
90
|
+
|
|
91
|
+
const sky = new Entity('sky');
|
|
92
|
+
sky.addComponent('script');
|
|
93
|
+
sky.script!.create(ProceduralSky, { properties: { luminance: 0.18 } });
|
|
94
|
+
app.root.addChild(sky);
|
|
95
|
+
|
|
96
|
+
const camera = new Entity('camera');
|
|
97
|
+
camera.setPosition(5.5, 3.2, 6.5);
|
|
98
|
+
camera.lookAt(CAMERA_FOCUS);
|
|
99
|
+
camera.addComponent('camera', { fov: 40, toneMapping: TONEMAP_ACES2, renderSceneColorMap: true });
|
|
100
|
+
camera.addComponent('script');
|
|
101
|
+
camera.script!.create(CameraControls, {
|
|
102
|
+
properties: {
|
|
103
|
+
sceneSize: 6.2,
|
|
104
|
+
focusPoint: CAMERA_FOCUS,
|
|
105
|
+
pitchRange: CAMERA_PITCH,
|
|
106
|
+
enableFly: false,
|
|
107
|
+
enablePan: false
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
app.root.addChild(camera);
|
|
111
|
+
|
|
112
|
+
const ground = new Entity('studio-ground');
|
|
113
|
+
const podium = new StandardMaterial();
|
|
114
|
+
podium.diffuse = new Color(0.3, 0.32, 0.34);
|
|
115
|
+
podium.metalness = 0.15;
|
|
116
|
+
podium.gloss = 0.65;
|
|
117
|
+
podium.update();
|
|
118
|
+
|
|
119
|
+
ground.setPosition(0, -0.08, 0);
|
|
120
|
+
ground.addComponent('render', {
|
|
121
|
+
meshInstances: [
|
|
122
|
+
new MeshInstance(
|
|
123
|
+
Mesh.fromGeometry(
|
|
124
|
+
device,
|
|
125
|
+
new CylinderGeometry({ radius: 4, height: 0.16, heightSegments: 1, capSegments: 128 })
|
|
126
|
+
),
|
|
127
|
+
podium
|
|
128
|
+
)
|
|
129
|
+
],
|
|
130
|
+
castShadows: false,
|
|
131
|
+
receiveShadows: true
|
|
132
|
+
});
|
|
133
|
+
app.root.addChild(ground);
|
|
134
|
+
|
|
135
|
+
const light = new Entity('light');
|
|
136
|
+
light.addComponent('light', {
|
|
137
|
+
type: 'directional',
|
|
138
|
+
intensity: 2.5,
|
|
139
|
+
castShadows: true,
|
|
140
|
+
shadowBias: 0.2,
|
|
141
|
+
normalOffsetBias: 0.05
|
|
142
|
+
});
|
|
143
|
+
light.setEulerAngles(45, -145, 0);
|
|
144
|
+
app.root.addChild(light);
|
|
145
|
+
|
|
146
|
+
document.querySelectorAll<HTMLButtonElement>('#paints button').forEach((button, i, buttons) => {
|
|
147
|
+
button.onclick = () => {
|
|
148
|
+
paint.diffuse.fromString(PAINTS[i].color);
|
|
149
|
+
shade.diffuse.fromString(PAINTS[i].shade);
|
|
150
|
+
paint.update();
|
|
151
|
+
shade.update();
|
|
152
|
+
buttons.forEach((item, j) => (item.ariaPressed = String(i === j)));
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
window.addEventListener('resize', () => app.resizeCanvas());
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppBase,
|
|
3
|
+
AppOptions,
|
|
4
|
+
CameraComponentSystem,
|
|
5
|
+
Color,
|
|
6
|
+
Entity,
|
|
7
|
+
FILLMODE_FILL_WINDOW,
|
|
8
|
+
LightComponentSystem,
|
|
9
|
+
RenderComponentSystem,
|
|
10
|
+
RESOLUTION_AUTO,
|
|
11
|
+
ScriptComponentSystem,
|
|
12
|
+
StandardMaterial,
|
|
13
|
+
createGraphicsDevice
|
|
14
|
+
} from 'playcanvas';
|
|
15
|
+
import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
|
|
16
|
+
|
|
17
|
+
import { SceneEditor } from './scene-editor';
|
|
18
|
+
import './starter.css';
|
|
19
|
+
|
|
20
|
+
document.body.insertAdjacentHTML(
|
|
21
|
+
'beforeend',
|
|
22
|
+
'<div class="hud"><section class="panel"><h1>Scene Editor</h1><p>Select an object, then translate, rotate or scale it.</p><div class="controls"><button data-target="0" aria-pressed="true">Cube</button><button data-target="1">Sphere</button></div><div class="controls"><button data-mode="translate" aria-pressed="true">Move</button><button data-mode="rotate">Rotate</button><button data-mode="scale">Scale</button></div></section></div>'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
26
|
+
const device = await createGraphicsDevice(canvas);
|
|
27
|
+
const options = new AppOptions();
|
|
28
|
+
options.graphicsDevice = device;
|
|
29
|
+
options.componentSystems = [RenderComponentSystem, CameraComponentSystem, LightComponentSystem, ScriptComponentSystem];
|
|
30
|
+
|
|
31
|
+
const app = new AppBase(canvas);
|
|
32
|
+
app.init(options);
|
|
33
|
+
app.start();
|
|
34
|
+
app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
35
|
+
app.setCanvasResolution(RESOLUTION_AUTO);
|
|
36
|
+
|
|
37
|
+
app.scene.ambientLight = new Color(0.25, 0.28, 0.34);
|
|
38
|
+
|
|
39
|
+
const coral = new StandardMaterial();
|
|
40
|
+
coral.diffuse = new Color(1, 0.32, 0.27);
|
|
41
|
+
coral.update();
|
|
42
|
+
const cyan = new StandardMaterial();
|
|
43
|
+
cyan.diffuse = new Color(0.14, 0.68, 0.88);
|
|
44
|
+
cyan.update();
|
|
45
|
+
|
|
46
|
+
const cube = new Entity('cube');
|
|
47
|
+
cube.addComponent('render', { type: 'box', material: coral });
|
|
48
|
+
cube.setPosition(-1.2, 0.5, 0);
|
|
49
|
+
app.root.addChild(cube);
|
|
50
|
+
|
|
51
|
+
const sphere = new Entity('sphere');
|
|
52
|
+
sphere.addComponent('render', { type: 'sphere', material: cyan });
|
|
53
|
+
sphere.setPosition(1.2, 0.5, 0);
|
|
54
|
+
app.root.addChild(sphere);
|
|
55
|
+
|
|
56
|
+
const grid = new Entity('grid');
|
|
57
|
+
grid.addComponent('script');
|
|
58
|
+
grid.script!.create(Grid);
|
|
59
|
+
grid.setLocalScale(12, 12, 12);
|
|
60
|
+
app.root.addChild(grid);
|
|
61
|
+
|
|
62
|
+
const camera = new Entity('camera');
|
|
63
|
+
camera.addComponent('camera', { clearColor: new Color(0.04, 0.06, 0.09) });
|
|
64
|
+
camera.setPosition(4, 3.2, 5);
|
|
65
|
+
camera.lookAt(0, 0.5, 0);
|
|
66
|
+
app.root.addChild(camera);
|
|
67
|
+
|
|
68
|
+
const light = new Entity('light');
|
|
69
|
+
light.addComponent('light', {
|
|
70
|
+
type: 'directional',
|
|
71
|
+
intensity: 2.5,
|
|
72
|
+
castShadows: true,
|
|
73
|
+
shadowBias: 0.2,
|
|
74
|
+
normalOffsetBias: 0.05
|
|
75
|
+
});
|
|
76
|
+
light.setEulerAngles(45, 30, 0);
|
|
77
|
+
app.root.addChild(light);
|
|
78
|
+
|
|
79
|
+
const editor = new Entity('editor');
|
|
80
|
+
editor.addComponent('script');
|
|
81
|
+
editor.script!.create(SceneEditor, {
|
|
82
|
+
properties: {
|
|
83
|
+
camera,
|
|
84
|
+
targets: [cube, sphere],
|
|
85
|
+
layer: app.scene.layers.getLayerByName('World')
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
app.root.addChild(editor);
|
|
89
|
+
|
|
90
|
+
window.addEventListener('resize', () => app.resizeCanvas());
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
Asset,
|
|
5
5
|
AssetListLoader,
|
|
6
6
|
CameraComponentSystem,
|
|
7
|
+
Color,
|
|
7
8
|
Entity,
|
|
8
9
|
FILLMODE_FILL_WINDOW,
|
|
9
10
|
GSplatComponentSystem,
|
|
@@ -67,8 +68,8 @@ app.root.addChild(toyCat);
|
|
|
67
68
|
|
|
68
69
|
// Create camera entity with orbit controls
|
|
69
70
|
const camera = new Entity('camera');
|
|
70
|
-
camera.addComponent('camera');
|
|
71
|
-
camera.setPosition(0, 0, 2.
|
|
71
|
+
camera.addComponent('camera', { clearColor: new Color(0.56, 0.58, 0.62) });
|
|
72
|
+
camera.setPosition(0, 0, 2.8);
|
|
72
73
|
camera.addComponent('script');
|
|
73
|
-
camera.script?.create(CameraControls);
|
|
74
|
+
camera.script?.create(CameraControls, { properties: { sceneSize: 2 } });
|
|
74
75
|
app.root.addChild(camera);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppBase,
|
|
3
|
+
AppOptions,
|
|
4
|
+
CameraComponentSystem,
|
|
5
|
+
Color,
|
|
6
|
+
Entity,
|
|
7
|
+
FILLMODE_FILL_WINDOW,
|
|
8
|
+
RenderComponentSystem,
|
|
9
|
+
RESOLUTION_AUTO,
|
|
10
|
+
ScriptComponentSystem,
|
|
11
|
+
SpriteComponentSystem,
|
|
12
|
+
StandardMaterial,
|
|
13
|
+
createGraphicsDevice
|
|
14
|
+
} from 'playcanvas';
|
|
15
|
+
|
|
16
|
+
import { SpriteGame } from './sprite-game';
|
|
17
|
+
import './starter.css';
|
|
18
|
+
|
|
19
|
+
document.body.insertAdjacentHTML(
|
|
20
|
+
'beforeend',
|
|
21
|
+
'<div class="hud"><section class="panel"><h1>Sprite Game</h1><p>Use A/D or the arrow keys to move and Space to jump.</p></section></div>'
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
25
|
+
const device = await createGraphicsDevice(canvas);
|
|
26
|
+
device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
|
|
27
|
+
|
|
28
|
+
const options = new AppOptions();
|
|
29
|
+
options.graphicsDevice = device;
|
|
30
|
+
options.componentSystems = [RenderComponentSystem, CameraComponentSystem, SpriteComponentSystem, ScriptComponentSystem];
|
|
31
|
+
|
|
32
|
+
const app = new AppBase(canvas);
|
|
33
|
+
app.init(options);
|
|
34
|
+
app.start();
|
|
35
|
+
app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
36
|
+
app.setCanvasResolution(RESOLUTION_AUTO);
|
|
37
|
+
|
|
38
|
+
const camera = new Entity('camera');
|
|
39
|
+
camera.addComponent('camera', {
|
|
40
|
+
projection: 1,
|
|
41
|
+
orthoHeight: 4.4,
|
|
42
|
+
clearColor: new Color(0.36, 0.73, 0.9)
|
|
43
|
+
});
|
|
44
|
+
camera.setPosition(0, 0, 10);
|
|
45
|
+
app.root.addChild(camera);
|
|
46
|
+
|
|
47
|
+
const dirtMaterial = new StandardMaterial();
|
|
48
|
+
dirtMaterial.emissive = new Color(0.45, 0.28, 0.16);
|
|
49
|
+
dirtMaterial.update();
|
|
50
|
+
const grassMaterial = new StandardMaterial();
|
|
51
|
+
grassMaterial.emissive = new Color(0.28, 0.62, 0.34);
|
|
52
|
+
grassMaterial.update();
|
|
53
|
+
|
|
54
|
+
const dirt = new Entity('dirt');
|
|
55
|
+
dirt.addComponent('render', { type: 'box', material: dirtMaterial });
|
|
56
|
+
dirt.setLocalScale(20, 2.6, 1);
|
|
57
|
+
dirt.setPosition(0, -3.39, 0);
|
|
58
|
+
app.root.addChild(dirt);
|
|
59
|
+
|
|
60
|
+
const grass = new Entity('grass');
|
|
61
|
+
grass.addComponent('render', { type: 'box', material: grassMaterial });
|
|
62
|
+
grass.setLocalScale(20, 0.18, 1);
|
|
63
|
+
grass.setPosition(0, -2, 0);
|
|
64
|
+
app.root.addChild(grass);
|
|
65
|
+
|
|
66
|
+
const game = new Entity('game');
|
|
67
|
+
game.setPosition(0, -2.15, 0);
|
|
68
|
+
game.addComponent('script');
|
|
69
|
+
game.script!.create(SpriteGame);
|
|
70
|
+
app.root.addChild(game);
|
|
71
|
+
|
|
72
|
+
window.addEventListener('resize', () => app.resizeCanvas());
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppBase,
|
|
3
|
+
AppOptions,
|
|
4
|
+
CameraComponentSystem,
|
|
5
|
+
CollisionComponentSystem,
|
|
6
|
+
Color,
|
|
7
|
+
Entity,
|
|
8
|
+
FILLMODE_FILL_WINDOW,
|
|
9
|
+
LightComponentSystem,
|
|
10
|
+
RenderComponentSystem,
|
|
11
|
+
RESOLUTION_AUTO,
|
|
12
|
+
RigidBodyComponentSystem,
|
|
13
|
+
ScriptComponentSystem,
|
|
14
|
+
StandardMaterial,
|
|
15
|
+
Vec3,
|
|
16
|
+
WasmModule,
|
|
17
|
+
createGraphicsDevice
|
|
18
|
+
} from 'playcanvas';
|
|
19
|
+
|
|
20
|
+
import { addThirdPersonController } from './controller';
|
|
21
|
+
import './starter.css';
|
|
22
|
+
|
|
23
|
+
const BLOCK_SCALE: [number, number, number] = [2.4, 1, 2.4];
|
|
24
|
+
const BLOCK_HALF_EXTENTS: [number, number, number] = [1.2, 0.5, 1.2];
|
|
25
|
+
const BLOCKS: [number, number, number][] = [
|
|
26
|
+
[-4.5, 0.5, -3.5],
|
|
27
|
+
[4.2, 0.5, -2.8],
|
|
28
|
+
[-3.7, 0.5, 3.2],
|
|
29
|
+
[3.8, 0.5, 3.6]
|
|
30
|
+
];
|
|
31
|
+
const TREES: [number, number, number][] = [
|
|
32
|
+
[-7, -6, 1.1],
|
|
33
|
+
[6.5, -6.5, 1.25],
|
|
34
|
+
[-7.5, 5, 0.9],
|
|
35
|
+
[7, 5.5, 1.15],
|
|
36
|
+
[-2.2, -8, 0.85]
|
|
37
|
+
];
|
|
38
|
+
const PINE_LAYERS: [number, number, number][] = [
|
|
39
|
+
[1.9, 2.1, 1.8],
|
|
40
|
+
[2.65, 1.6, 1.55],
|
|
41
|
+
[3.3, 1.1, 1.4]
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
WasmModule.setConfig('Ammo', {
|
|
45
|
+
glueUrl: '/ammo/ammo.wasm.js',
|
|
46
|
+
wasmUrl: '/ammo/ammo.wasm.wasm',
|
|
47
|
+
fallbackUrl: '/ammo/ammo.js'
|
|
48
|
+
});
|
|
49
|
+
await new Promise<void>((resolve) => {
|
|
50
|
+
WasmModule.getInstance('Ammo', () => resolve());
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
document.body.insertAdjacentHTML(
|
|
54
|
+
'beforeend',
|
|
55
|
+
'<div class="hud"><section class="panel"><h1>Third-Person Controller</h1><p>Click the scene, then use WASD to move, mouse to orbit and Space to jump.</p></section></div>'
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
|
|
59
|
+
const device = await createGraphicsDevice(canvas);
|
|
60
|
+
const options = new AppOptions();
|
|
61
|
+
options.graphicsDevice = device;
|
|
62
|
+
options.componentSystems = [
|
|
63
|
+
RenderComponentSystem,
|
|
64
|
+
CameraComponentSystem,
|
|
65
|
+
LightComponentSystem,
|
|
66
|
+
ScriptComponentSystem,
|
|
67
|
+
CollisionComponentSystem,
|
|
68
|
+
RigidBodyComponentSystem
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const app = new AppBase(canvas);
|
|
72
|
+
app.init(options);
|
|
73
|
+
app.start();
|
|
74
|
+
app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
75
|
+
app.setCanvasResolution(RESOLUTION_AUTO);
|
|
76
|
+
app.scene.ambientLight = new Color(0.28, 0.32, 0.38);
|
|
77
|
+
|
|
78
|
+
const groundMaterial = new StandardMaterial();
|
|
79
|
+
groundMaterial.diffuse = new Color(0.2, 0.4, 0.18);
|
|
80
|
+
groundMaterial.update();
|
|
81
|
+
const blockMaterial = new StandardMaterial();
|
|
82
|
+
blockMaterial.diffuse = new Color(0.34, 0.35, 0.3);
|
|
83
|
+
blockMaterial.update();
|
|
84
|
+
|
|
85
|
+
const ground = new Entity('ground');
|
|
86
|
+
ground.setLocalScale(20, 0.2, 20);
|
|
87
|
+
ground.setPosition(0, -0.1, 0);
|
|
88
|
+
ground.addComponent('render', { type: 'box', material: groundMaterial });
|
|
89
|
+
ground.addComponent('collision', { type: 'box', halfExtents: new Vec3(10, 0.1, 10) });
|
|
90
|
+
ground.addComponent('rigidbody', { type: 'static' });
|
|
91
|
+
app.root.addChild(ground);
|
|
92
|
+
|
|
93
|
+
for (const [i, position] of BLOCKS.entries()) {
|
|
94
|
+
const block = new Entity(`block-${i}`);
|
|
95
|
+
block.setPosition(...position);
|
|
96
|
+
block.setLocalScale(...BLOCK_SCALE);
|
|
97
|
+
block.setEulerAngles(0, i * 25, 0);
|
|
98
|
+
block.addComponent('render', { type: 'box', material: blockMaterial });
|
|
99
|
+
block.addComponent('collision', { type: 'box', halfExtents: new Vec3(...BLOCK_HALF_EXTENTS) });
|
|
100
|
+
block.addComponent('rigidbody', { type: 'static' });
|
|
101
|
+
app.root.addChild(block);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const wood = new StandardMaterial();
|
|
105
|
+
wood.diffuse = new Color(0.34, 0.18, 0.08);
|
|
106
|
+
wood.update();
|
|
107
|
+
const leaves = new StandardMaterial();
|
|
108
|
+
leaves.diffuse = new Color(0.08, 0.3, 0.12);
|
|
109
|
+
leaves.update();
|
|
110
|
+
const tips = new StandardMaterial();
|
|
111
|
+
tips.diffuse = new Color(0.12, 0.4, 0.16);
|
|
112
|
+
tips.update();
|
|
113
|
+
const prop = (
|
|
114
|
+
name: string,
|
|
115
|
+
position: [number, number, number],
|
|
116
|
+
scale: [number, number, number],
|
|
117
|
+
material: StandardMaterial
|
|
118
|
+
) => {
|
|
119
|
+
const entity = new Entity(name);
|
|
120
|
+
entity.setPosition(...position);
|
|
121
|
+
entity.setLocalScale(...scale);
|
|
122
|
+
entity.addComponent('render', { type: 'box', material });
|
|
123
|
+
entity.addComponent('collision', {
|
|
124
|
+
type: 'box',
|
|
125
|
+
halfExtents: new Vec3(scale[0] / 2, scale[1] / 2, scale[2] / 2)
|
|
126
|
+
});
|
|
127
|
+
entity.addComponent('rigidbody', { type: 'static' });
|
|
128
|
+
app.root.addChild(entity);
|
|
129
|
+
};
|
|
130
|
+
for (const [i, [x, z, size]] of TREES.entries()) {
|
|
131
|
+
prop(`tree-${i}-trunk`, [x, size * 0.9, z], [size * 0.38, size * 1.8, size * 0.38], wood);
|
|
132
|
+
for (const [j, [y, width, height]] of PINE_LAYERS.entries()) {
|
|
133
|
+
const crown = new Entity(`tree-${i}-crown-${j}`);
|
|
134
|
+
crown.setPosition(x, size * y, z);
|
|
135
|
+
crown.setLocalScale(size * width, size * height, size * width);
|
|
136
|
+
crown.addComponent('render', { type: 'cone', material: j === 1 ? tips : leaves });
|
|
137
|
+
app.root.addChild(crown);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
prop('fallen-log', [0, 0.35, 6], [3.8, 0.7, 0.7], wood);
|
|
141
|
+
prop('stump', [-1.8, 0.4, -2.2], [0.9, 0.8, 0.9], wood);
|
|
142
|
+
|
|
143
|
+
const player = new Entity('player');
|
|
144
|
+
player.setPosition(0, 1.1, 0);
|
|
145
|
+
player.addComponent('collision', {
|
|
146
|
+
type: 'capsule',
|
|
147
|
+
radius: 0.5,
|
|
148
|
+
height: 2.4,
|
|
149
|
+
linearOffset: new Vec3(0, 0.1, 0)
|
|
150
|
+
});
|
|
151
|
+
player.addComponent('rigidbody', { type: 'dynamic', mass: 70, angularFactor: Vec3.ZERO });
|
|
152
|
+
app.root.addChild(player);
|
|
153
|
+
|
|
154
|
+
const shirt = new StandardMaterial();
|
|
155
|
+
shirt.diffuse = new Color(0.15, 0.55, 0.9);
|
|
156
|
+
shirt.update();
|
|
157
|
+
const skin = new StandardMaterial();
|
|
158
|
+
skin.diffuse = new Color(0.78, 0.53, 0.35);
|
|
159
|
+
skin.update();
|
|
160
|
+
const pants = new StandardMaterial();
|
|
161
|
+
pants.diffuse = new Color(0.18, 0.29, 0.52);
|
|
162
|
+
pants.update();
|
|
163
|
+
|
|
164
|
+
const model = new Entity('character');
|
|
165
|
+
model.setLocalPosition(0, 0.2, 0);
|
|
166
|
+
const part = (
|
|
167
|
+
parent: Entity,
|
|
168
|
+
name: string,
|
|
169
|
+
position: [number, number, number],
|
|
170
|
+
scale: [number, number, number],
|
|
171
|
+
material: StandardMaterial
|
|
172
|
+
) => {
|
|
173
|
+
const entity = new Entity(name);
|
|
174
|
+
entity.addComponent('render', { type: 'box', material });
|
|
175
|
+
entity.setLocalPosition(...position);
|
|
176
|
+
entity.setLocalScale(...scale);
|
|
177
|
+
parent.addChild(entity);
|
|
178
|
+
};
|
|
179
|
+
const limb = (
|
|
180
|
+
name: string,
|
|
181
|
+
position: [number, number, number],
|
|
182
|
+
offset: number,
|
|
183
|
+
scale: [number, number, number],
|
|
184
|
+
material: StandardMaterial
|
|
185
|
+
) => {
|
|
186
|
+
const joint = new Entity(name);
|
|
187
|
+
joint.setLocalPosition(...position);
|
|
188
|
+
model.addChild(joint);
|
|
189
|
+
part(joint, `${name}-mesh`, [0, offset, 0], scale, material);
|
|
190
|
+
};
|
|
191
|
+
part(model, 'body', [0, 0, 0], [0.56, 0.88, 0.38], shirt);
|
|
192
|
+
part(model, 'head', [0, 0.76, 0], [0.52, 0.6, 0.52], skin);
|
|
193
|
+
limb('left-arm', [-0.38, 0.44, 0], -0.44, [0.18, 0.88, 0.32], shirt);
|
|
194
|
+
limb('right-arm', [0.38, 0.44, 0], -0.44, [0.18, 0.88, 0.32], shirt);
|
|
195
|
+
limb('left-leg', [-0.15, -0.44, 0], -0.43, [0.24, 0.86, 0.34], pants);
|
|
196
|
+
limb('right-leg', [0.15, -0.44, 0], -0.43, [0.24, 0.86, 0.34], pants);
|
|
197
|
+
player.addChild(model);
|
|
198
|
+
|
|
199
|
+
const camera = new Entity('camera');
|
|
200
|
+
camera.addComponent('camera', { clearColor: new Color(0.48, 0.72, 0.9) });
|
|
201
|
+
camera.setPosition(0, 3, 6);
|
|
202
|
+
app.root.addChild(camera);
|
|
203
|
+
addThirdPersonController(player, camera, model);
|
|
204
|
+
|
|
205
|
+
const light = new Entity('light');
|
|
206
|
+
light.addComponent('light', {
|
|
207
|
+
type: 'directional',
|
|
208
|
+
intensity: 2.5,
|
|
209
|
+
castShadows: true,
|
|
210
|
+
shadowBias: 0.2,
|
|
211
|
+
normalOffsetBias: 0.05
|
|
212
|
+
});
|
|
213
|
+
light.setEulerAngles(45, 30, 0);
|
|
214
|
+
app.root.addChild(light);
|
|
215
|
+
|
|
216
|
+
window.addEventListener('resize', () => app.resizeCanvas());
|