|
@@ -0,0 +1,299 @@
|
|
|
+import * as THREE from 'https://cdn.skypack.dev/[email protected]';
|
|
|
+
|
|
|
+import {OrbitControls} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls.js';
|
|
|
+
|
|
|
+const DEFAULT_MASS = 10;
|
|
|
+
|
|
|
+
|
|
|
+class RigidBody {
|
|
|
+ constructor() {
|
|
|
+ }
|
|
|
+
|
|
|
+ setRestitution(val) {
|
|
|
+ this.body_.setRestitution(val);
|
|
|
+ }
|
|
|
+
|
|
|
+ setFriction(val) {
|
|
|
+ this.body_.setFriction(val);
|
|
|
+ }
|
|
|
+
|
|
|
+ setRollingFriction(val) {
|
|
|
+ this.body_.setRollingFriction(val);
|
|
|
+ }
|
|
|
+
|
|
|
+ createBox(mass, pos, quat, size) {
|
|
|
+ this.transform_ = new Ammo.btTransform();
|
|
|
+ this.transform_.setIdentity();
|
|
|
+ this.transform_.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
|
|
|
+ this.transform_.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
|
|
|
+ this.motionState_ = new Ammo.btDefaultMotionState(this.transform_);
|
|
|
+
|
|
|
+ const btSize = new Ammo.btVector3(size.x * 0.5, size.y * 0.5, size.z * 0.5);
|
|
|
+ this.shape_ = new Ammo.btBoxShape(btSize);
|
|
|
+ this.shape_.setMargin(0.05);
|
|
|
+
|
|
|
+ this.inertia_ = new Ammo.btVector3(0, 0, 0);
|
|
|
+ if (mass > 0) {
|
|
|
+ this.shape_.calculateLocalInertia(mass, this.inertia_);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.info_ = new Ammo.btRigidBodyConstructionInfo(
|
|
|
+ mass, this.motionState_, this.shape_, this.inertia_);
|
|
|
+ this.body_ = new Ammo.btRigidBody(this.info_);
|
|
|
+
|
|
|
+ Ammo.destroy(btSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ createSphere(mass, pos, size) {
|
|
|
+ this.transform_ = new Ammo.btTransform();
|
|
|
+ this.transform_.setIdentity();
|
|
|
+ this.transform_.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
|
|
|
+ this.transform_.setRotation(new Ammo.btQuaternion(0, 0, 0, 1));
|
|
|
+ this.motionState_ = new Ammo.btDefaultMotionState(this.transform_);
|
|
|
+
|
|
|
+ this.shape_ = new Ammo.btSphereShape(size);
|
|
|
+ this.shape_.setMargin(0.05);
|
|
|
+
|
|
|
+ this.inertia_ = new Ammo.btVector3(0, 0, 0);
|
|
|
+ if(mass > 0) {
|
|
|
+ this.shape_.calculateLocalInertia(mass, this.inertia_);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.info_ = new Ammo.btRigidBodyConstructionInfo(mass, this.motionState_, this.shape_, this.inertia_);
|
|
|
+ this.body_ = new Ammo.btRigidBody(this.info_);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class BasicWorldDemo {
|
|
|
+ constructor() {
|
|
|
+ }
|
|
|
+
|
|
|
+ initialize() {
|
|
|
+ this.collisionConfiguration_ = new Ammo.btDefaultCollisionConfiguration();
|
|
|
+ this.dispatcher_ = new Ammo.btCollisionDispatcher(this.collisionConfiguration_);
|
|
|
+ this.broadphase_ = new Ammo.btDbvtBroadphase();
|
|
|
+ this.solver_ = new Ammo.btSequentialImpulseConstraintSolver();
|
|
|
+ this.physicsWorld_ = new Ammo.btDiscreteDynamicsWorld(
|
|
|
+ this.dispatcher_, this.broadphase_, this.solver_, this.collisionConfiguration_);
|
|
|
+ this.physicsWorld_.setGravity(new Ammo.btVector3(0, -100, 0));
|
|
|
+
|
|
|
+ this.threejs_ = new THREE.WebGLRenderer({
|
|
|
+ antialias: true,
|
|
|
+ });
|
|
|
+ this.threejs_.shadowMap.enabled = true;
|
|
|
+ this.threejs_.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
|
+ this.threejs_.setPixelRatio(window.devicePixelRatio);
|
|
|
+ this.threejs_.setSize(window.innerWidth, window.innerHeight);
|
|
|
+
|
|
|
+ document.body.appendChild(this.threejs_.domElement);
|
|
|
+
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ this.onWindowResize_();
|
|
|
+ }, false);
|
|
|
+
|
|
|
+ const fov = 60;
|
|
|
+ const aspect = 1920 / 1080;
|
|
|
+ const near = 1.0;
|
|
|
+ const far = 1000.0;
|
|
|
+ this.camera_ = new THREE.PerspectiveCamera(fov, aspect, near, far);
|
|
|
+ this.camera_.position.set(75, 20, 0);
|
|
|
+
|
|
|
+ this.scene_ = new THREE.Scene();
|
|
|
+
|
|
|
+ let light = new THREE.DirectionalLight(0xFFFFFF, 1.0);
|
|
|
+ light.position.set(20, 100, 10);
|
|
|
+ light.target.position.set(0, 0, 0);
|
|
|
+ light.castShadow = true;
|
|
|
+ light.shadow.bias = -0.001;
|
|
|
+ light.shadow.mapSize.width = 2048;
|
|
|
+ light.shadow.mapSize.height = 2048;
|
|
|
+ light.shadow.camera.near = 0.1;
|
|
|
+ light.shadow.camera.far = 500.0;
|
|
|
+ light.shadow.camera.near = 0.5;
|
|
|
+ light.shadow.camera.far = 500.0;
|
|
|
+ light.shadow.camera.left = 100;
|
|
|
+ light.shadow.camera.right = -100;
|
|
|
+ light.shadow.camera.top = 100;
|
|
|
+ light.shadow.camera.bottom = -100;
|
|
|
+ this.scene_.add(light);
|
|
|
+
|
|
|
+ light = new THREE.AmbientLight(0x101010);
|
|
|
+ this.scene_.add(light);
|
|
|
+
|
|
|
+ const controls = new OrbitControls(
|
|
|
+ this.camera_, this.threejs_.domElement);
|
|
|
+ controls.target.set(0, 20, 0);
|
|
|
+ controls.update();
|
|
|
+
|
|
|
+ const loader = new THREE.CubeTextureLoader();
|
|
|
+ const texture = loader.load([
|
|
|
+ './resources/posx.jpg',
|
|
|
+ './resources/negx.jpg',
|
|
|
+ './resources/posy.jpg',
|
|
|
+ './resources/negy.jpg',
|
|
|
+ './resources/posz.jpg',
|
|
|
+ './resources/negz.jpg',
|
|
|
+ ]);
|
|
|
+ this.scene_.background = texture;
|
|
|
+
|
|
|
+ const ground = new THREE.Mesh(
|
|
|
+ new THREE.BoxGeometry(100, 1, 100),
|
|
|
+ new THREE.MeshStandardMaterial({color: 0x404040}));
|
|
|
+ ground.castShadow = false;
|
|
|
+ ground.receiveShadow = true;
|
|
|
+ this.scene_.add(ground);
|
|
|
+
|
|
|
+ const rbGround = new RigidBody();
|
|
|
+ rbGround.createBox(0, ground.position, ground.quaternion, new THREE.Vector3(100, 1, 100));
|
|
|
+ rbGround.setRestitution(0.99);
|
|
|
+ this.physicsWorld_.addRigidBody(rbGround.body_);
|
|
|
+
|
|
|
+ this.rigidBodies_ = [];
|
|
|
+
|
|
|
+ // const box = new THREE.Mesh(
|
|
|
+ // new THREE.BoxGeometry(4, 4, 4),
|
|
|
+ // new THREE.MeshStandardMaterial({color: 0x808080}));
|
|
|
+ // box.position.set(0, 40, 0);
|
|
|
+ // box.castShadow = true;
|
|
|
+ // box.receiveShadow = true;
|
|
|
+ // this.scene_.add(box);
|
|
|
+
|
|
|
+ // const rbBox = new RigidBody();
|
|
|
+ // rbBox.createBox(1, box.position, box.quaternion, new THREE.Vector3(4, 4, 4));
|
|
|
+ // rbBox.setRestitution(0.25);
|
|
|
+ // rbBox.setFriction(1);
|
|
|
+ // rbBox.setRollingFriction(5);
|
|
|
+ // this.physicsWorld_.addRigidBody(rbBox.body_);
|
|
|
+
|
|
|
+ // this.rigidBodies_.push({mesh: box, rigidBody: rbBox});
|
|
|
+
|
|
|
+ let isSphere = true;
|
|
|
+ for (let x = -4; x < 4; ++x) {
|
|
|
+ for (let y = -4; y < 4; ++y) {
|
|
|
+ if (isSphere) {
|
|
|
+ const box = new THREE.Mesh(
|
|
|
+ new THREE.SphereGeometry(4),
|
|
|
+ new THREE.MeshStandardMaterial({color: 0x808080}));
|
|
|
+ box.position.set(x * 10, Math.random() * 20 + 40, y * 10);
|
|
|
+ box.castShadow = true;
|
|
|
+ box.receiveShadow = true;
|
|
|
+ this.scene_.add(box);
|
|
|
+
|
|
|
+ const rbBox = new RigidBody();
|
|
|
+ rbBox.createSphere(1, box.position, 4);
|
|
|
+ rbBox.setRestitution(0.5);
|
|
|
+ rbBox.setFriction(1);
|
|
|
+ rbBox.setRollingFriction(1);
|
|
|
+ this.physicsWorld_.addRigidBody(rbBox.body_);
|
|
|
+
|
|
|
+ this.rigidBodies_.push({mesh: box, rigidBody: rbBox});
|
|
|
+ } else {
|
|
|
+ const box = new THREE.Mesh(
|
|
|
+ new THREE.BoxGeometry(4, 4, 4),
|
|
|
+ new THREE.MeshStandardMaterial({color: 0x808080}));
|
|
|
+ box.position.set(x * 10, Math.random() * 5 + 40, y * 10);
|
|
|
+ box.castShadow = true;
|
|
|
+ box.receiveShadow = true;
|
|
|
+ this.scene_.add(box);
|
|
|
+
|
|
|
+ const rbBox = new RigidBody();
|
|
|
+ rbBox.createBox(1, box.position, box.quaternion, new THREE.Vector3(4, 4, 4));
|
|
|
+ rbBox.setRestitution(0.25);
|
|
|
+ rbBox.setFriction(1);
|
|
|
+ rbBox.setRollingFriction(5);
|
|
|
+ this.physicsWorld_.addRigidBody(rbBox.body_);
|
|
|
+
|
|
|
+ this.rigidBodies_.push({mesh: box, rigidBody: rbBox});
|
|
|
+ }
|
|
|
+ isSphere = !isSphere;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.tmpTransform_ = new Ammo.btTransform();
|
|
|
+
|
|
|
+ this.countdown_ = 1.0;
|
|
|
+ this.count_ = 0;
|
|
|
+ this.previousRAF_ = null;
|
|
|
+ this.raf_();
|
|
|
+ }
|
|
|
+
|
|
|
+ onWindowResize_() {
|
|
|
+ this.camera_.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ this.camera_.updateProjectionMatrix();
|
|
|
+ this.threejs_.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ raf_() {
|
|
|
+ requestAnimationFrame((t) => {
|
|
|
+ if (this.previousRAF_ === null) {
|
|
|
+ this.previousRAF_ = t;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.step_(t - this.previousRAF_);
|
|
|
+ this.threejs_.render(this.scene_, this.camera_);
|
|
|
+ this.raf_();
|
|
|
+ this.previousRAF_ = t;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ spawn_() {
|
|
|
+ const scale = Math.random() * 4 + 4;
|
|
|
+ const box = new THREE.Mesh(
|
|
|
+ new THREE.BoxGeometry(scale, scale, scale),
|
|
|
+ new THREE.MeshStandardMaterial({
|
|
|
+ color: 0x808080,
|
|
|
+ }));
|
|
|
+ box.position.set(Math.random() * 2 - 1, 200.0, Math.random() * 2 - 1);
|
|
|
+ box.quaternion.set(0, 0, 0, 1);
|
|
|
+ box.castShadow = true;
|
|
|
+ box.receiveShadow = true;
|
|
|
+
|
|
|
+ const rb = new RigidBody();
|
|
|
+ rb.createBox(DEFAULT_MASS, box.position, box.quaternion, new THREE.Vector3(scale, scale, scale), null);
|
|
|
+ rb.setRestitution(0.125);
|
|
|
+ rb.setFriction(1);
|
|
|
+ rb.setRollingFriction(5);
|
|
|
+
|
|
|
+ this.physicsWorld_.addRigidBody(rb.body_);
|
|
|
+
|
|
|
+ this.rigidBodies_.push({mesh: box, rigidBody: rb});
|
|
|
+
|
|
|
+ this.scene_.add(box);
|
|
|
+ }
|
|
|
+
|
|
|
+ step_(timeElapsed) {
|
|
|
+ const timeElapsedS = timeElapsed * 0.001;
|
|
|
+
|
|
|
+ this.countdown_ -= timeElapsedS;
|
|
|
+ if (this.countdown_ < 0 && this.count_ < 10) {
|
|
|
+ this.countdown_ = 0.25;
|
|
|
+ this.count_ += 1;
|
|
|
+ this.spawn_();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.physicsWorld_.stepSimulation(timeElapsedS, 10);
|
|
|
+
|
|
|
+ for (let i = 0; i < this.rigidBodies_.length; ++i) {
|
|
|
+ this.rigidBodies_[i].rigidBody.motionState_.getWorldTransform(this.tmpTransform_);
|
|
|
+ const pos = this.tmpTransform_.getOrigin();
|
|
|
+ const quat = this.tmpTransform_.getRotation();
|
|
|
+ const pos3 = new THREE.Vector3(pos.x(), pos.y(), pos.z());
|
|
|
+ const quat3 = new THREE.Quaternion(quat.x(), quat.y(), quat.z(), quat.w());
|
|
|
+
|
|
|
+ this.rigidBodies_[i].mesh.position.copy(pos3);
|
|
|
+ this.rigidBodies_[i].mesh.quaternion.copy(quat3);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+let APP_ = null;
|
|
|
+
|
|
|
+window.addEventListener('DOMContentLoaded', async () => {
|
|
|
+ Ammo().then((lib) => {
|
|
|
+ Ammo = lib;
|
|
|
+ APP_ = new BasicWorldDemo();
|
|
|
+ APP_.initialize();
|
|
|
+ });
|
|
|
+});
|