|
1 | | -<!DOCTYPE html> |
| 1 | +<!doctype html> |
2 | 2 | <html lang="en"> |
3 | 3 | <head> |
4 | 4 | <meta charset="UTF-8" /> |
|
10 | 10 | <title>Compare Physics Body Shapes</title> |
11 | 11 | <link rel="stylesheet" href="/css/examples.css?ver=1.0.0" /> |
12 | 12 | <script src="/js/examples.js?ver=1.1.1"></script> |
13 | | - <script src="/lib/phaser.min.js?ver=3.52.0"></script> |
14 | | - <script src="/lib/enable3d/enable3d.phaserExtension.0.25.4.min.js"></script> |
15 | 13 | </head> |
16 | 14 |
|
17 | 15 | <body> |
18 | 16 | <div id="info-text">From left to right:<br />box, compound, hull, hacd, convexMesh, concaveMesh</div> |
19 | | - <script> |
20 | | - const { enable3d, Scene3D, Canvas, ExtendedObject3D } = ENABLE3D |
21 | | - |
22 | | - class MainScene extends Scene3D { |
23 | | - constructor() { |
24 | | - super({ key: 'MainScene' }) |
| 17 | + <script type="importmap"> |
| 18 | + { |
| 19 | + "imports": { |
| 20 | + "three": "/lib/threejs/r171/three.module.min.js", |
| 21 | + "orbit-controls": "/lib/threejs/r171/OrbitControls.module.min.js", |
| 22 | + "enable3d": "/lib/enable3d/enable3d.ammoPhysics.0.26.0_dev0.module.min.js", |
| 23 | + "gltf-loader": "/lib/threejs/r171/GLTFLoader.module.min.js" |
25 | 24 | } |
| 25 | + } |
| 26 | + </script> |
| 27 | + <script type="module"> |
| 28 | + import * as THREE from 'three' |
| 29 | + import { AmmoPhysics, PhysicsLoader } from 'enable3d' |
| 30 | + import { OrbitControls } from 'orbit-controls' |
| 31 | + import { GLTFLoader } from 'gltf-loader' |
| 32 | + |
| 33 | + const MainScene = () => { |
| 34 | + const scene = new THREE.Scene() |
| 35 | + scene.background = new THREE.Color(0xf0f0f0) |
| 36 | + |
| 37 | + const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000) |
| 38 | + camera.position.set(4, 4, 8) |
| 39 | + camera.lookAt(0, 1, 0) |
| 40 | + |
| 41 | + const renderer = new THREE.WebGLRenderer() |
| 42 | + renderer.setSize(window.innerWidth, window.innerHeight) |
| 43 | + document.body.appendChild(renderer.domElement) |
| 44 | + |
| 45 | + const controls = new OrbitControls(camera, renderer.domElement) |
| 46 | + controls.target.set(0, 1, 0) |
| 47 | + controls.update() |
| 48 | + |
| 49 | + scene.add(new THREE.HemisphereLight(0xffffff, 0x000000, 1)) |
| 50 | + scene.add(new THREE.AmbientLight(0xffffff)) |
| 51 | + const light = new THREE.DirectionalLight(0xffffff, 1) |
| 52 | + light.position.set(50, 200, 100) |
| 53 | + light.position.multiplyScalar(1.3) |
| 54 | + |
| 55 | + // initialize physics |
| 56 | + const physics = new AmmoPhysics(scene) |
| 57 | + physics.debug.enable(true) |
| 58 | + |
| 59 | + // add a ground |
| 60 | + physics.add.ground({ width: 20, height: 20 }) |
| 61 | + |
| 62 | + // add suzanne (the monkey's name is suzanne) |
| 63 | + new GLTFLoader().load('/assets/glb/suzanne.glb', function (gltf) { |
| 64 | + // If you can, always use simple shapes like BOX, SPHERE, CONE etc. |
| 65 | + // The second most efficient shape is a COMPOUND, which merges multiple simple shapes. |
| 66 | + // Prefer HULL over CONVEX MESH. |
| 67 | + // HACD is the most expensive but also the most accurate. |
| 68 | + // If you need a concave shape, for a static or kinematic body, use CONCAVE MESH. |
| 69 | + |
| 70 | + // (mesh and convex are aliases for convexMesh) |
| 71 | + // (concave is an alias for concaveMesh) |
| 72 | + // (heightMap uses concaveMesh by default) |
| 73 | + // (extrude uses hacd by default) |
| 74 | + |
| 75 | + const suzanne = gltf.scene.children[0] |
| 76 | + |
| 77 | + const shapes = ['box', 'compound', 'hull', 'hacd', 'convexMesh', 'concaveMesh'] |
| 78 | + |
| 79 | + const material = new THREE.MeshStandardMaterial({ color: 0xc4c4c4, transparent: true, opacity: 0.7 }) |
| 80 | + const boxShape = { shape: 'box', width: 2, height: 1.5, depth: 1.25 } |
| 81 | + |
| 82 | + // compound multiple simple shape together |
| 83 | + const compoundShape = { |
| 84 | + compound: [ |
| 85 | + // nose |
| 86 | + { shape: 'box', width: 0.5, height: 1, depth: 0.4, y: -0.5, z: 0.5 }, |
| 87 | + // ears |
| 88 | + { shape: 'box', width: 2.4, height: 0.6, depth: 0.4, z: -0.4, y: 0.2 }, |
| 89 | + // head back |
| 90 | + { shape: 'sphere', radius: 0.65, z: -0.25, y: 0.35 }, |
| 91 | + // head front |
| 92 | + { shape: 'box', width: 1.5, height: 0.8, depth: 1, y: 0.2, z: 0.2 } |
| 93 | + ] |
| 94 | + } |
| 95 | + |
| 96 | + suzanne.traverse(child => { |
| 97 | + if (child.isMesh && child.material.isMaterial) { |
| 98 | + child.material = material |
| 99 | + } |
| 100 | + }) |
26 | 101 |
|
27 | | - init() { |
28 | | - this.accessThirdDimension() |
29 | | - } |
| 102 | + shapes.forEach((shape, i) => { |
| 103 | + const object = new THREE.Object3D() |
30 | 104 |
|
31 | | - async create() { |
32 | | - this.third.warpSpeed() |
33 | | - |
34 | | - this.third.physics.debug.enable() |
35 | | - |
36 | | - // add suzanne (the monkey's name is suzanne) |
37 | | - this.third.load.gltf('/assets/glb/suzanne.glb').then(gltf => { |
38 | | - // If you can, always use simple shapes like BOX, SPHERE, CONE etc. |
39 | | - // The second most efficient shape is a COMPOUND, which merges multiple simple shapes. |
40 | | - // Prefer HULL over CONVEX MESH. |
41 | | - // HACD is the most expensive but also the most accurate. |
42 | | - // If you need a concave shape, for a static or kinematic body, use CONCAVE MESH. |
43 | | - |
44 | | - // (mesh and convex are aliases for convexMesh) |
45 | | - // (concave is an alias for concaveMesh) |
46 | | - // (heightMap uses concaveMesh by default) |
47 | | - // (extrude uses hacd by default) |
48 | | - |
49 | | - const suzanne = gltf.scene.children[0] |
50 | | - |
51 | | - const shapes = ['box', 'compound', 'hull', 'hacd', 'convexMesh', 'concaveMesh'] |
52 | | - |
53 | | - const material = this.third.add.material({ standard: { color: 0xc4c4c4, transparent: true, opacity: 0.5 } }) |
54 | | - const boxShape = { shape: 'box', width: 2, height: 1.5, depth: 1.25 } |
55 | | - |
56 | | - // compound multiple simple shape together |
57 | | - const compoundShape = { |
58 | | - compound: [ |
59 | | - // nose |
60 | | - { shape: 'box', width: 0.5, height: 1, depth: 0.4, y: -0.5, z: 0.5 }, |
61 | | - // ears |
62 | | - { shape: 'box', width: 2.4, height: 0.6, depth: 0.4, z: -0.4, y: 0.2 }, |
63 | | - // head back |
64 | | - { shape: 'sphere', radius: 0.65, z: -0.25, y: 0.35 }, |
65 | | - // head front |
66 | | - { shape: 'box', width: 1.5, height: 0.8, depth: 1, y: 0.2, z: 0.2 } |
67 | | - ] |
68 | | - } |
| 105 | + object.add(suzanne.clone()) |
| 106 | + object.position.set(i * 3 - 7.5, 1.2, 0) |
69 | 107 |
|
70 | | - suzanne.traverse(child => { |
71 | | - if (child.isMesh && child.material.isMaterial) { |
72 | | - child.material = material |
73 | | - } |
74 | | - }) |
| 108 | + // we se addChildren to false since we do not want |
| 109 | + // to create a body from suzanne's child meshes |
| 110 | + // (it would create a box 1x1x1 since no matching shape would be found) |
| 111 | + let options = { addChildren: false, shape } |
75 | 112 |
|
76 | | - shapes.forEach((shape, i) => { |
77 | | - const object = new ExtendedObject3D() |
| 113 | + if (shape === 'box') options = { ...options, ...boxShape } |
| 114 | + else if (shape === 'compound') options = { ...options, ...compoundShape } |
78 | 115 |
|
79 | | - object.add(suzanne.clone()) |
80 | | - object.position.set(i * 3 - 7.5, 1.2, 0) |
| 116 | + scene.add(object) |
| 117 | + physics.add.existing(object, options) |
| 118 | + }) |
| 119 | + }) |
81 | 120 |
|
82 | | - // we se addChildren to false since we do not want |
83 | | - // to create a body from suzanne's child meshes |
84 | | - // (it would create a box 1x1x1 since no matching shape would be found) |
85 | | - let options = { addChildren: false, shape } |
| 121 | + const clock = new THREE.Clock() |
86 | 122 |
|
87 | | - if (shape === 'box') options = { ...options, ...boxShape } |
88 | | - else if (shape === 'compound') options = { ...options, ...compoundShape } |
| 123 | + const animate = () => { |
| 124 | + // update physics |
| 125 | + physics.update(clock.getDelta() * 1000) |
| 126 | + // update the physics debugger |
| 127 | + physics.updateDebugger() |
89 | 128 |
|
90 | | - this.third.add.existing(object) |
91 | | - this.third.physics.add.existing(object, options) |
92 | | - }) |
93 | | - }) |
| 129 | + renderer.render(scene, camera) |
| 130 | + requestAnimationFrame(animate) |
94 | 131 | } |
| 132 | + requestAnimationFrame(animate) |
95 | 133 | } |
| 134 | + PhysicsLoader('/lib/ammo/moz', () => MainScene()) |
96 | 135 |
|
97 | | - const config = { |
98 | | - type: Phaser.WEBGL, |
99 | | - transparent: true, |
100 | | - scale: { |
101 | | - mode: Phaser.Scale.FIT, |
102 | | - autoCenter: Phaser.Scale.CENTER_BOTH, |
103 | | - width: window.innerWidth * Math.max(1, window.devicePixelRatio / 2), |
104 | | - height: window.innerHeight * Math.max(1, window.devicePixelRatio / 2) |
105 | | - }, |
106 | | - scene: [MainScene], |
107 | | - ...Canvas() |
108 | | - } |
109 | | - |
110 | | - window.addEventListener('load', () => { |
111 | | - enable3d(() => new Phaser.Game(config)).withPhysics('/lib/ammo/moz') |
112 | | - }) |
| 136 | + console.log(`three.js version "${THREE.REVISION}"`) |
113 | 137 | </script> |
114 | 138 | </body> |
115 | 139 | </html> |
0 commit comments