Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/assets/scripts/gravity.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Script } from 'playcanvas';

export class Gravity extends Script {
update(dt) {
const { x, y, z } = this.entity.getPosition();
this.entity.rigidbody.applyForce(-x, -y, -z);
}
}
18 changes: 18 additions & 0 deletions examples/assets/scripts/physical-pointer.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Script } from 'playcanvas';

/**
* @import { CameraComponent } from 'playcanvas';
*/

export class PhysicalPointer extends Script {
initialize() {
const canvas = this.app.graphicsDevice.canvas;
canvas.addEventListener('pointermove', (event) => {
/** @type {CameraComponent} */
const camera = this.app.root.findComponent('camera');
const { z } = camera.entity.getPosition();
const { x, y } = camera.screenToWorld(event.clientX, event.clientY, z);
this.entity.setPosition(x, y, 0);
});
}
}
1 change: 1 addition & 0 deletions examples/js/example-list.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const examples = [
{ name: 'Gaussian Splatting', path: 'splat.html' },
{ name: 'GLB Loader', path: 'glb.html' },
{ name: 'Physics', path: 'physics.html' },
{ name: 'Physics Cluster', path: 'physics-cluster.html' },
{ name: 'Positional Sound', path: 'positional-sound.html' },
{ name: 'Shoe Configurator', path: 'shoe-configurator.html' },
{ name: 'Solar System', path: 'solar-system.html' },
Expand Down
84 changes: 84 additions & 0 deletions examples/physics-cluster.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>PlayCanvas Web Components - Physics Cluster</title>
<script type="importmap">
{
"imports": {
"playcanvas": "../node_modules/playcanvas/build/playcanvas.mjs"
}
}
</script>
<script type="module" src="../dist/pwc.mjs"></script>
<link rel="stylesheet" href="css/example.css">
</head>
<body>
<pc-app>
<!-- Modules -->
<pc-module name="Ammo" glue="modules/ammo/ammo.wasm.js" wasm="modules/ammo/ammo.wasm.wasm" fallback="modules/ammo/ammo.js"></pc-module>
<!-- Assets -->
<pc-asset src="assets/skies/octagon-lamps-photo-studio-2k.hdr" id="studio"></pc-asset>
<pc-asset src="assets/scripts/gravity.mjs"></pc-asset>
<pc-asset src="assets/scripts/physical-pointer.mjs"></pc-asset>
<pc-material id="hotpink" diffuse="hotpink"></pc-material>
<pc-material id="mediumseagreen" diffuse="mediumseagreen"></pc-material>
<!-- Scene -->
<pc-scene gravity="0 0 0">
<!-- Sky -->
<pc-sky asset="studio" type="none" lighting></pc-sky>
<!-- Camera -->
<pc-entity name="camera" position="0 0 5">
<pc-camera clear-color="#cccccc" tonemap="neutral"></pc-camera>
</pc-entity>
<!-- Physical Sphere Template -->
<template id="sphere-template">
<pc-entity>
<pc-render type="sphere" material="mediumseagreen"></pc-render>
<pc-collision type="sphere"></pc-collision>
<pc-rigidbody type="dynamic" angular-damping="0.9" linear-damping="0.9" restitution="0.9"></pc-rigidbody>
<pc-scripts>
<pc-script name="gravity"></pc-script>
</pc-scripts>
</pc-entity>
</template>
<!-- Physical Pointer -->
<pc-entity name="pointer" position="0 0 0">
<pc-entity scale="0.5 0.5 0.5">
<pc-render type="sphere"></pc-render>
</pc-entity>
<pc-light type="omni" cast-shadows></pc-light>
<pc-collision type="sphere" radius="0.25"></pc-collision>
<pc-rigidbody type="kinematic" restitution="0.9"></pc-rigidbody>
<pc-scripts>
<pc-script name="physicalPointer"></pc-script>
</pc-scripts>
</pc-entity>
</pc-scene>
</pc-app>
<script>
document.addEventListener('DOMContentLoaded', () => {
const scene = document.querySelector('pc-scene');
const sphereTemplate = document.getElementById('sphere-template');

// Clone and append 40 spheres
for (let i = 0; i < 40; i++) {
// Clone the sphere template
const clone = document.importNode(sphereTemplate.content, true);
const sphereEntity = clone.querySelector('pc-entity');

// Set a random start position
const x = (Math.random() - 0.5) * 10;
const y = (Math.random() - 0.5) * 10;
const z = (Math.random() - 0.5) * 10;
sphereEntity.setAttribute('position', `${x} ${y} ${z}`);

// Append the clone to the scene
scene.appendChild(clone);
}
});
</script>
<script type="module" src="js/example.mjs"></script>
</body>
</html>
39 changes: 35 additions & 4 deletions src/scene.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Color, Scene } from 'playcanvas';
import { Color, Scene, Vec3 } from 'playcanvas';

import { AppElement } from './app';
import { AsyncElement } from './async-element';
import { parseColor } from './utils';
import { parseColor, parseVec3 } from './utils';

/**
* The SceneElement interface provides properties and methods for manipulating
Expand Down Expand Up @@ -35,6 +36,11 @@ class SceneElement extends AsyncElement {
*/
private _fogEnd = 1000;

/**
* The gravity of the scene.
*/
private _gravity = new Vec3(0, -9.81, 0);

/**
* The PlayCanvas scene instance.
*/
Expand All @@ -56,7 +62,9 @@ class SceneElement extends AsyncElement {
this.scene.fog.density = this._fogDensity;
this.scene.fog.start = this._fogStart;
this.scene.fog.end = this._fogEnd;
// ... set other properties on the scene as well

const appElement = this.parentElement as AppElement;
appElement.app!.systems.rigidbody!.gravity.copy(this._gravity);
}
}

Expand Down Expand Up @@ -155,8 +163,28 @@ class SceneElement extends AsyncElement {
return this._fogEnd;
}

/**
* Sets the gravity of the scene.
* @param value - The gravity.
*/
set gravity(value: Vec3) {
this._gravity = value;
if (this.scene) {
const appElement = this.parentElement as AppElement;
appElement.app!.systems.rigidbody!.gravity.copy(value);
}
}

/**
* Gets the gravity of the scene.
* @returns The gravity.
*/
get gravity() {
return this._gravity;
}

static get observedAttributes() {
return ['fog', 'fog-color', 'fog-density', 'fog-start', 'fog-end'];
return ['fog', 'fog-color', 'fog-density', 'fog-start', 'fog-end', 'gravity'];
}

attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
Expand All @@ -176,6 +204,9 @@ class SceneElement extends AsyncElement {
case 'fog-end':
this.fogEnd = parseFloat(newValue);
break;
case 'gravity':
this.gravity = parseVec3(newValue);
break;
// ... handle other attributes as well
}
}
Expand Down