physics
This commit is contained in:
Generated
+8
@@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
"cannon-es": "^0.20.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-config-vuetify": "^1.0.0",
|
"eslint-config-vuetify": "^1.0.0",
|
||||||
@@ -2431,6 +2432,13 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cannon-es": {
|
||||||
|
"version": "0.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cannon-es/-/cannon-es-0.20.0.tgz",
|
||||||
|
"integrity": "sha512-eZhWTZIkFOnMAJOgfXJa9+b3kVlvG+FX4mdkpePev/w/rP5V8NRquGyEozcjPfEoXUlb+p7d9SUcmDSn14prOA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/chai": {
|
"node_modules/chai": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
"cannon-es": "^0.20.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-config-vuetify": "^1.0.0",
|
"eslint-config-vuetify": "^1.0.0",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GameEngine } from '@/lib/gameEngine.js';
|
import { GameEngine } from '@/lib/GameEngine.js';
|
||||||
let gameEngine = null;
|
let gameEngine = null;
|
||||||
|
|
||||||
export default{
|
export default{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { GameEngine } from '@/lib/gameEngine';
|
import { GameEngine } from '@/lib/GameEngine';
|
||||||
|
|
||||||
let gameEngine = null;
|
let gameEngine = null;
|
||||||
|
|
||||||
@@ -150,6 +150,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
async loadEnvironment(scene, target){
|
async loadEnvironment(scene, target){
|
||||||
//await gameEngine.loadPanorama(`/asset/default/43.webp`);
|
//await gameEngine.loadPanorama(`/asset/default/43.webp`);
|
||||||
|
gameEngine.activeObjects.clear();
|
||||||
await this.expandScenarioData(scene);
|
await this.expandScenarioData(scene);
|
||||||
target.objects = target.objects || {};
|
target.objects = target.objects || {};
|
||||||
let l = target.objects;
|
let l = target.objects;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<v-btn-toggle variant="tonal" v-model="store.prefs.xr.depthSense" class="ma-3" density="comfortable" color="green-darken-2">
|
<v-btn-toggle variant="tonal" v-model="store.prefs.xr.depthSense" class="ma-3" density="comfortable" color="green-darken-2">
|
||||||
<v-btn :value="true" icon="mdi-cube-outline"></v-btn>
|
<v-btn :value="true" icon="mdi-cube-outline"></v-btn>
|
||||||
</v-btn-toggle>
|
</v-btn-toggle>
|
||||||
|
<v-btn icon="mdi-walk" @click="control"></v-btn>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
<div class="container my-3 position-relative game-designer-canvas">
|
<div class="container my-3 position-relative game-designer-canvas">
|
||||||
<div ref="target" @click="targetClick" @pointerdown="targetPointerDown"></div>
|
<div ref="target" @click="targetClick" @pointerdown="targetPointerDown"></div>
|
||||||
@@ -50,7 +51,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { GameEngine } from '@/lib/gameEngine';
|
import { GameEngine } from '@/lib/GameEngine';
|
||||||
|
import { Hero } from '@/lib/Hero';
|
||||||
import { useAppStore } from '@/stores/app';
|
import { useAppStore } from '@/stores/app';
|
||||||
|
|
||||||
const store = useAppStore();
|
const store = useAppStore();
|
||||||
@@ -152,6 +154,7 @@ export default {
|
|||||||
//await gameEngine.loadPanorama(`/asset/default/43.webp`);
|
//await gameEngine.loadPanorama(`/asset/default/43.webp`);
|
||||||
await this.expandScenarioData(scene);
|
await this.expandScenarioData(scene);
|
||||||
//gameEngine.activeObjects.scale.set(0.033, 0.033, 0.033)
|
//gameEngine.activeObjects.scale.set(0.033, 0.033, 0.033)
|
||||||
|
gameEngine.activeObjects.clear();
|
||||||
target.objects = target.objects || {};
|
target.objects = target.objects || {};
|
||||||
let l = target.objects;
|
let l = target.objects;
|
||||||
if (this.scene.data.$environment.type == 'panorama2d'){
|
if (this.scene.data.$environment.type == 'panorama2d'){
|
||||||
@@ -165,6 +168,10 @@ export default {
|
|||||||
let gltf = await gameEngine.load(`/asset/default/${i.data.$go.asset.name}`);
|
let gltf = await gameEngine.load(`/asset/default/${i.data.$go.asset.name}`);
|
||||||
this.setObjectAttributes(l, i.data, gltf, 10);
|
this.setObjectAttributes(l, i.data, gltf, 10);
|
||||||
gameEngine.activeObjects.add(gltf.scene);
|
gameEngine.activeObjects.add(gltf.scene);
|
||||||
|
if (i.data.$go.type == 'player3d'){
|
||||||
|
let hero = new Hero(gltf, i.data.$go);
|
||||||
|
hero.init(gameEngine);
|
||||||
|
}
|
||||||
//console.log(JSON.stringify(l));
|
//console.log(JSON.stringify(l));
|
||||||
//window.gameEngine = gameEngine;
|
//window.gameEngine = gameEngine;
|
||||||
//console.log(new gameEngine.$.Euler({"isEuler":true,"_x":0,"_y":0,"_z":0,"_order":"XYZ"}));
|
//console.log(new gameEngine.$.Euler({"isEuler":true,"_x":0,"_y":0,"_z":0,"_order":"XYZ"}));
|
||||||
@@ -221,6 +228,10 @@ export default {
|
|||||||
gameEngine.resize(r.clientWidth, r.clientHeight);
|
gameEngine.resize(r.clientWidth, r.clientHeight);
|
||||||
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
control(){
|
||||||
|
gameEngine.hero.lockControls();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { MeshBasicMaterial, TextureLoader, LinearFilter,
|
||||||
|
sRGBEncoding,
|
||||||
|
Mesh,
|
||||||
|
OrthographicCamera,
|
||||||
|
PlaneGeometry,
|
||||||
|
RGBAFormat,
|
||||||
|
Scene } from 'three';
|
||||||
|
|
||||||
|
import { Text } from 'troika-three-text';
|
||||||
|
|
||||||
|
class DashBoard {
|
||||||
|
constructor(renderer, width, height) {
|
||||||
|
|
||||||
|
var _camera = new OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 0, 1);
|
||||||
|
|
||||||
|
var _scene = new Scene();
|
||||||
|
|
||||||
|
var _params = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat, stencilBuffer: true };
|
||||||
|
this.points = 0;
|
||||||
|
|
||||||
|
var _texture = new TextureLoader().load('./assets/maze/x.png');
|
||||||
|
_texture.encoding = sRGBEncoding;
|
||||||
|
|
||||||
|
var _material = new MeshBasicMaterial({
|
||||||
|
map: _texture,
|
||||||
|
alphaTest: .5
|
||||||
|
});
|
||||||
|
|
||||||
|
// _mesh = new Mesh( new PlaneGeometry( width * 0.015, width * 0.015 ), _material );
|
||||||
|
var _text = new Text();
|
||||||
|
_text.font = './assets/fonts/MonomakhUnicode.otf';
|
||||||
|
_text.text = 'Точки: 0';
|
||||||
|
_text.anchorX = 'right';
|
||||||
|
_text.anchorY = 'top';
|
||||||
|
_text.fontSize = width * 0.015;
|
||||||
|
_text.position.set(width * .48, height * .47, 0);
|
||||||
|
_text.color = 0xffffff;
|
||||||
|
_text.outlineColor = 0x222222;
|
||||||
|
_text.outlineWidth = '5%';
|
||||||
|
_text.outlineBlur = '5%';
|
||||||
|
//_scene.add( _mesh );
|
||||||
|
_scene.add(_text);
|
||||||
|
_text.sync();
|
||||||
|
|
||||||
|
this.render = function (scene, camera) {
|
||||||
|
renderer.render(_scene, _camera);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setSize = function (width, height) {
|
||||||
|
_camera.left = width / -2;
|
||||||
|
_camera.right = width / 2;
|
||||||
|
_camera.top = height / 2;
|
||||||
|
_camera.bottom = height / -2;
|
||||||
|
_camera.updateProjectionMatrix();
|
||||||
|
|
||||||
|
_text.position.set(width * .48, height * .47, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addPoints = function (points) {
|
||||||
|
this.onpoints && this.onpoints(this.points + points, this.points);
|
||||||
|
this.points += points;
|
||||||
|
_text.text = 'точки: ' + this.points;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.dispose = function () {
|
||||||
|
if (_mesh) _mesh.geometry.dispose();
|
||||||
|
if (_material) _material.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { DashBoard };
|
||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
import { AnimationMixer } from 'three';
|
||||||
|
import { PointerControls } from './PointerControls';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import * as CANNON from 'cannon-es';
|
||||||
|
|
||||||
|
class Hero{
|
||||||
|
|
||||||
|
constructor(object, data){
|
||||||
|
this.object = object;
|
||||||
|
this.data = data;
|
||||||
|
this.model = object.scene
|
||||||
|
}
|
||||||
|
|
||||||
|
init(gameEngine){
|
||||||
|
this.gameEngine = gameEngine;
|
||||||
|
this.mixer = new AnimationMixer(this.model);
|
||||||
|
gameEngine.mixers.push( this.mixer );
|
||||||
|
this.actionWalk = this.mixer.clipAction( this.object.animations.find(a=>a.name=='walk') );
|
||||||
|
this.actionIdle = this.mixer.clipAction( this.object.animations.find(a=>a.name=='idle') );
|
||||||
|
this.actionIdle.play();
|
||||||
|
this.activeAction = this.actionIdle;
|
||||||
|
this.pointerControls = new PointerControls(gameEngine.cameraPivot, this.model, gameEngine.renderer.domElement);
|
||||||
|
gameEngine.hero = this;
|
||||||
|
|
||||||
|
// Character Collider
|
||||||
|
const characterCollider = new THREE.Object3D()
|
||||||
|
characterCollider.position.y = 3
|
||||||
|
gameEngine.activeObjects.add(characterCollider)
|
||||||
|
const colliderShape = new CANNON.Sphere(0.5)
|
||||||
|
const colliderBody = new CANNON.Body({ mass: 1, material: gameEngine.phy.slipperyMaterial })
|
||||||
|
colliderBody.addShape(colliderShape, new CANNON.Vec3(0, 0.5, 0))
|
||||||
|
colliderBody.addShape(colliderShape, new CANNON.Vec3(0, -0.5, 0))
|
||||||
|
colliderBody.position.set(
|
||||||
|
characterCollider.position.x,
|
||||||
|
characterCollider.position.y,
|
||||||
|
characterCollider.position.z
|
||||||
|
)
|
||||||
|
colliderBody.linearDamping = 0.95
|
||||||
|
colliderBody.angularFactor.set(0, 1, 0) // prevents rotation X,Z axis
|
||||||
|
gameEngine.phy.world.addBody(colliderBody)
|
||||||
|
|
||||||
|
this.characterCollider = characterCollider;
|
||||||
|
this.colliderBody = colliderBody;
|
||||||
|
|
||||||
|
this.clock = new THREE.Clock()
|
||||||
|
this.delta = 0
|
||||||
|
this.inputVelocity = new THREE.Vector3()
|
||||||
|
this.velocity = new CANNON.Vec3()
|
||||||
|
this.euler = new THREE.Euler()
|
||||||
|
this.quat = new THREE.Quaternion()
|
||||||
|
this.v = new THREE.Vector3()
|
||||||
|
this.targetQuaternion = new THREE.Quaternion()
|
||||||
|
this.distance = 0
|
||||||
|
this.canJump = true;
|
||||||
|
|
||||||
|
this.contactNormal = new CANNON.Vec3()
|
||||||
|
this.upAxis = new CANNON.Vec3(0, 1, 0)
|
||||||
|
|
||||||
|
colliderBody.addEventListener('collide', function (e) {
|
||||||
|
const contact = e.contact
|
||||||
|
if (contact.bi.id == this.colliderBody.id) {
|
||||||
|
contact.ni.negate(this.contactNormal)
|
||||||
|
} else {
|
||||||
|
this.contactNormal.copy(contact.ni)
|
||||||
|
}
|
||||||
|
if (this.contactNormal.dot(this.upAxis) > 0.5) {
|
||||||
|
if (!this.canJump) {
|
||||||
|
setAction(animationActions[1], true)
|
||||||
|
}
|
||||||
|
this.canJump = true
|
||||||
|
}
|
||||||
|
}.bind(this))
|
||||||
|
|
||||||
|
|
||||||
|
this.ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lockControls(){
|
||||||
|
this.pointerControls.controls.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
setAction(toAction, loop){
|
||||||
|
if (toAction != this.activeAction) {
|
||||||
|
let lastAction = this.activeAction;
|
||||||
|
this.activeAction = toAction
|
||||||
|
lastAction.fadeOut(0.1)
|
||||||
|
this.activeAction.reset()
|
||||||
|
this.activeAction.fadeIn(0.1)
|
||||||
|
this.activeAction.play()
|
||||||
|
if (!loop) {
|
||||||
|
this.activeAction.clampWhenFinished = true
|
||||||
|
this.activeAction.loop = THREE.LoopOnce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(){
|
||||||
|
let { inputVelocity, velocity, euler, quat, v, targetQuaternion, clock } = this;
|
||||||
|
let pc = this.pointerControls;
|
||||||
|
pc.update();
|
||||||
|
if (this.ready) {
|
||||||
|
if (this.canJump) {
|
||||||
|
//walking
|
||||||
|
this.mixer.update(this.distance / 100)
|
||||||
|
} else {
|
||||||
|
//were in the air
|
||||||
|
this.mixer.update(this.delta)
|
||||||
|
}
|
||||||
|
const p = this.characterCollider.position
|
||||||
|
p.y -= 1
|
||||||
|
this.model.position.y = this.characterCollider.position.y
|
||||||
|
this.distance = this.model.position.distanceTo(p)
|
||||||
|
|
||||||
|
const rotationMatrix = new THREE.Matrix4()
|
||||||
|
rotationMatrix.lookAt(p, this.model.position, this.model.up)
|
||||||
|
targetQuaternion.setFromRotationMatrix(rotationMatrix)
|
||||||
|
|
||||||
|
if (!this.model.quaternion.equals(targetQuaternion)) {
|
||||||
|
this.model.quaternion.rotateTowards(targetQuaternion, this.delta * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canJump) {
|
||||||
|
inputVelocity.set(0, 0, 0)
|
||||||
|
|
||||||
|
if (pc.moveForward) {
|
||||||
|
inputVelocity.z = -1
|
||||||
|
}
|
||||||
|
if (pc.moveBackward) {
|
||||||
|
inputVelocity.z = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc.moveLeft) {
|
||||||
|
inputVelocity.x = -1
|
||||||
|
}
|
||||||
|
if (pc.moveRight) {
|
||||||
|
inputVelocity.x = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
inputVelocity.setLength(this.delta * 100)
|
||||||
|
|
||||||
|
// apply camera rotation to inputVelocity
|
||||||
|
euler.y = this.gameEngine.cameraYaw.rotation.y;
|
||||||
|
//euler.y = this.gameEngine.camera.rotation.y;
|
||||||
|
euler.order = 'XYZ'
|
||||||
|
quat.setFromEuler(euler)
|
||||||
|
inputVelocity.applyQuaternion(quat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inputVelocity.x || inputVelocity.z){
|
||||||
|
this.setAction(this.actionWalk, true)
|
||||||
|
}else{
|
||||||
|
this.setAction(this.actionIdle, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.position.lerp(this.characterCollider.position, 1)
|
||||||
|
}
|
||||||
|
velocity.set(inputVelocity.x, inputVelocity.y, inputVelocity.z)
|
||||||
|
this.colliderBody.applyImpulse(velocity)
|
||||||
|
|
||||||
|
this.delta = Math.min(clock.getDelta(), 0.1)
|
||||||
|
this.gameEngine.phy.world.step(this.delta)
|
||||||
|
|
||||||
|
//cannonDebugRenderer.update()
|
||||||
|
|
||||||
|
this.characterCollider.position.set(this.colliderBody.position.x, this.colliderBody.position.y, this.colliderBody.position.z)
|
||||||
|
// boxes.forEach((b, i) => {
|
||||||
|
// boxMeshes[i].position.set(b.position.x, b.position.y, b.position.z)
|
||||||
|
// boxMeshes[i].quaternion.set(b.quaternion.x, b.quaternion.y, b.quaternion.z, b.quaternion.w)
|
||||||
|
// })
|
||||||
|
|
||||||
|
this.characterCollider.getWorldPosition(v)
|
||||||
|
this.gameEngine.cameraPivot.position.lerp(v, 0.1)
|
||||||
|
|
||||||
|
// if (this.gameEngine.camera.position.distanceTo(v) > 200){
|
||||||
|
// this.gameEngine.camera.position.set(v.x+10, 3, v.z + 30);
|
||||||
|
// }
|
||||||
|
//this.gameEngine.camera.lookAt(v.x, 3, v.z);
|
||||||
|
//this.gameEngine.camera.rotation.y = this.model.rotation.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Hero }
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
Clock,
|
||||||
|
Vector3
|
||||||
|
} from 'three';
|
||||||
|
|
||||||
|
import { PointerLockControls } from 'three/examples/jsm/Addons.js';
|
||||||
|
|
||||||
|
class PointerControls {
|
||||||
|
constructor(camera, hero, domElement) {
|
||||||
|
this.moveForward = false;
|
||||||
|
this.moveBackward = false;
|
||||||
|
this.moveLeft = false;
|
||||||
|
this.moveRight = false;
|
||||||
|
this.moveUp = false;
|
||||||
|
this.moveDown = false;
|
||||||
|
this.rotateLeft = false;
|
||||||
|
this.rotateRight = false;
|
||||||
|
this.canJump = false;
|
||||||
|
this.velocity = new Vector3();
|
||||||
|
this.direction = new Vector3();
|
||||||
|
this.rotationY = 0;
|
||||||
|
this.vertex = new Vector3();
|
||||||
|
this.rvelo = 0;
|
||||||
|
|
||||||
|
this.camera = camera;
|
||||||
|
this.hero = hero;
|
||||||
|
this.clock = new Clock();
|
||||||
|
this.click = false;
|
||||||
|
|
||||||
|
this.controls = new PointerLockControls(camera, domElement);
|
||||||
|
|
||||||
|
const onKeyDown = (event) => {
|
||||||
|
switch (event.code) {
|
||||||
|
case 'ArrowUp':
|
||||||
|
case 'KeyW':
|
||||||
|
this.moveForward = true;
|
||||||
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'KeyA':
|
||||||
|
this.moveLeft = true;
|
||||||
|
this.rotateLeft = true;
|
||||||
|
break;
|
||||||
|
case 'ArrowDown':
|
||||||
|
case 'KeyS':
|
||||||
|
this.moveBackward = true;
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
case 'KeyD':
|
||||||
|
this.moveRight = true;
|
||||||
|
this.rotateRight = true;
|
||||||
|
break;
|
||||||
|
case 'KeyQ':
|
||||||
|
this.rotateLeft = true;
|
||||||
|
break;
|
||||||
|
case 'KeyE':
|
||||||
|
this.rotateRight = true;
|
||||||
|
break;
|
||||||
|
case 'KeyR':
|
||||||
|
this.moveUp = true;
|
||||||
|
break;
|
||||||
|
case 'KeyF':
|
||||||
|
this.moveDown = true;
|
||||||
|
break;
|
||||||
|
case 'Space':
|
||||||
|
if (this.canJump === true) this.velocity.y += 350;
|
||||||
|
this.canJump = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyUp = (event) => {
|
||||||
|
switch (event.code) {
|
||||||
|
case 'ArrowUp':
|
||||||
|
case 'KeyW':
|
||||||
|
this.moveForward = false;
|
||||||
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'KeyA':
|
||||||
|
this.moveLeft = false;
|
||||||
|
this.rotateLeft = false;
|
||||||
|
break;
|
||||||
|
case 'ArrowDown':
|
||||||
|
case 'KeyS':
|
||||||
|
this.moveBackward = false;
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
case 'KeyD':
|
||||||
|
this.moveRight = false;
|
||||||
|
this.rotateRight = false;
|
||||||
|
break;
|
||||||
|
case 'KeyQ':
|
||||||
|
this.rotateLeft = false;
|
||||||
|
break;
|
||||||
|
case 'KeyE':
|
||||||
|
this.rotateRight = false;
|
||||||
|
break;
|
||||||
|
case 'KeyR':
|
||||||
|
this.moveUp = false;
|
||||||
|
break;
|
||||||
|
case 'KeyF':
|
||||||
|
this.moveDown = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', onKeyDown);
|
||||||
|
document.addEventListener('keyup', onKeyUp);
|
||||||
|
|
||||||
|
window.addEventListener("gamepadconnected", (e) => {
|
||||||
|
this.gp = navigator.getGamepads()[e.gamepad.index];
|
||||||
|
console.log("Gamepad connected", this.gp);
|
||||||
|
});
|
||||||
|
|
||||||
|
domElement.addEventListener('click', () => {
|
||||||
|
this.controls.isLocked && this.clicked && this.clicked();
|
||||||
|
});
|
||||||
|
|
||||||
|
domElement.addEventListener('mousedown', () => {
|
||||||
|
this.controls.isLocked && this.onpointer && this.onpointer('start');
|
||||||
|
});
|
||||||
|
|
||||||
|
domElement.addEventListener('mousemove', () => {
|
||||||
|
this.controls.isLocked && this.onpointer && this.onpointer('drag');
|
||||||
|
});
|
||||||
|
|
||||||
|
domElement.addEventListener('mouseup', () => {
|
||||||
|
this.controls.isLocked && this.onpointer && this.onpointer('end');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update = () => {
|
||||||
|
// const delta = this.clock.getDelta() * 10;
|
||||||
|
// if (this.gp) {
|
||||||
|
// this.gp = navigator.getGamepads()[this.gp.index];
|
||||||
|
// this.gp.pressed = this.gp.buttons[4].pressed || this.gp.buttons[5].pressed || this.gp.buttons[6].pressed || this.gp.buttons[7].pressed || this.gp.buttons[9].pressed;
|
||||||
|
// if (!this.click && this.gp.pressed) {
|
||||||
|
// this.click = true;
|
||||||
|
// this.clicked?.();
|
||||||
|
// //console.log(this.gp.buttons.map((b,i)=>[i, b.pressed]));
|
||||||
|
// }
|
||||||
|
// if (this.click && !this.gp.pressed) this.click = false;
|
||||||
|
// }
|
||||||
|
// //this.velocity.x -= this.velocity.x * 5.0 * delta;
|
||||||
|
// this.velocity.z -= this.velocity.z * 5.0 * delta;
|
||||||
|
// this.rvelo -= this.rvelo * 5 * delta;
|
||||||
|
// // this.velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
|
||||||
|
// this.direction.z = Number(this.moveForward) - Number(this.moveBackward) - Math.floor((this.gp && this.gp.axes[1] || 0) * 100) / 100 + (this.touchControls && this.touchControls.move || 0);
|
||||||
|
// this.rotationY = Number(this.rotateLeft) - Number(this.rotateRight) - Math.floor((this.gp && this.gp.axes[0] || 0) * 100) / 110 + (this.touchControls && this.touchControls.rotate || 0);
|
||||||
|
// //this.direction.x = Number( this.moveRight ) - Number( this.moveLeft );
|
||||||
|
// //this.direction.normalize(); // this ensures consistent movements in all directions
|
||||||
|
// if (this.direction.z) this.velocity.z -= this.direction.z * 5.0 * delta;
|
||||||
|
// //if ( this.moveLeft || this.moveRight ) this.velocity.x -= this.direction.x * 5.0 * delta;
|
||||||
|
// if (this.rotationY) this.rvelo -= this.rotationY * 8 * delta;
|
||||||
|
|
||||||
|
//this.velocity.x && this.controls.moveRight( - this.velocity.x * delta );
|
||||||
|
// if (this.velocity.z) {
|
||||||
|
// this.controls.moveForward(-this.velocity.z * delta);
|
||||||
|
// this.hero.position.z += -this.velocity.z * delta;
|
||||||
|
// }
|
||||||
|
// this.controls.moveRight( this.direction.x * delta );
|
||||||
|
// this.controls.moveForward( this.direction.z * delta );
|
||||||
|
// this.hero.position.y += (Number(this.moveUp) - Number(this.moveDown)) * delta;
|
||||||
|
// this.hero.rotation.y += -this.rvelo * delta;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { PointerControls };
|
||||||
+173
-124
@@ -2,6 +2,7 @@ import * as THREE from 'three';
|
|||||||
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
|
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
|
||||||
import { DRACOLoader } from 'three/examples/jsm/Addons.js';
|
import { DRACOLoader } from 'three/examples/jsm/Addons.js';
|
||||||
import { OrbitControls } from 'three/examples/jsm/Addons.js';
|
import { OrbitControls } from 'three/examples/jsm/Addons.js';
|
||||||
|
//import { Controller as OrbitControls } from './3rd-party/phy/3TH/Controller.js';
|
||||||
import { ViewportGizmo } from "three-viewport-gizmo";
|
import { ViewportGizmo } from "three-viewport-gizmo";
|
||||||
//import { AnaglyphEffect } from './three/AnaglyphEffect';
|
//import { AnaglyphEffect } from './three/AnaglyphEffect';
|
||||||
import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js';
|
import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js';
|
||||||
@@ -12,6 +13,7 @@ import { TransformControls } from 'three/addons/controls/TransformControls.js';
|
|||||||
import { ARButton } from 'three/addons/webxr/ARButton.js';
|
import { ARButton } from 'three/addons/webxr/ARButton.js';
|
||||||
import { XRButton } from 'three/addons/webxr/XRButton.js';
|
import { XRButton } from 'three/addons/webxr/XRButton.js';
|
||||||
import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
|
import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
|
||||||
|
import * as CANNON from 'cannon-es';
|
||||||
|
|
||||||
class GameEngine {
|
class GameEngine {
|
||||||
async init(domNode, opts = {}) {
|
async init(domNode, opts = {}) {
|
||||||
@@ -27,67 +29,73 @@ class GameEngine {
|
|||||||
|
|
||||||
this.frustumSize = 50;
|
this.frustumSize = 50;
|
||||||
this.orthographicCamera = new THREE.OrthographicCamera(
|
this.orthographicCamera = new THREE.OrthographicCamera(
|
||||||
this.frustumSize * this.aspect / - 2,
|
this.frustumSize * this.aspect / - 2,
|
||||||
this.frustumSize * this.aspect / 2,
|
this.frustumSize * this.aspect / 2,
|
||||||
this.frustumSize / 2,
|
this.frustumSize / 2,
|
||||||
this.frustumSize / - 2,
|
this.frustumSize / - 2,
|
||||||
0.01, 1000);
|
0.01, 1000);
|
||||||
this.orthographicCamera.position.set( 0, 0, 100 );
|
this.orthographicCamera.position.set(0, 0, 100);
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
this.initCameraPivot()
|
||||||
|
|
||||||
// let light = new THREE.AmbientLight( 0x404040, 300 ); // soft white light
|
// let light = new THREE.AmbientLight( 0x404040, 300 ); // soft white light
|
||||||
// scene.add( this.light );
|
// scene.add( this.light );
|
||||||
|
|
||||||
const dirLight = new THREE.DirectionalLight( 0xffffff, 4 );
|
const dirLight = new THREE.DirectionalLight(0xffffff, 4);
|
||||||
dirLight.color.setHSL( 0.1, 1, 0.95 );
|
dirLight.color.setHSL(0.1, 1, 0.95);
|
||||||
dirLight.position.set( -12, 33, 37 );
|
dirLight.position.set(-12, 33, 37);
|
||||||
//dirLight.position.multiplyScalar( 20 );
|
//dirLight.position.multiplyScalar( 20 );
|
||||||
scene.add( dirLight );
|
scene.add(dirLight);
|
||||||
|
|
||||||
dirLight.castShadow = true;
|
dirLight.castShadow = true;
|
||||||
|
|
||||||
dirLight.shadow.mapSize.width = 2048;
|
dirLight.shadow.mapSize.width = 2048;
|
||||||
dirLight.shadow.mapSize.height = 2048;
|
dirLight.shadow.mapSize.height = 2048;
|
||||||
|
|
||||||
const d = 50;
|
const d = 50;
|
||||||
|
|
||||||
dirLight.shadow.camera.left = - d;
|
dirLight.shadow.camera.left = - d;
|
||||||
dirLight.shadow.camera.right = d;
|
dirLight.shadow.camera.right = d;
|
||||||
dirLight.shadow.camera.top = d;
|
dirLight.shadow.camera.top = d;
|
||||||
dirLight.shadow.camera.bottom = - d;
|
dirLight.shadow.camera.bottom = - d;
|
||||||
|
|
||||||
dirLight.shadow.camera.far = 1000;
|
dirLight.shadow.camera.far = 1000;
|
||||||
dirLight.shadow.bias = - 0.001;
|
dirLight.shadow.bias = - 0.001;
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({
|
const renderer = new THREE.WebGLRenderer({
|
||||||
antialias: true,
|
antialias: true,
|
||||||
// alpha: true,
|
// alpha: true,
|
||||||
preserveDrawingBuffer: true //this is important for screenshots capturing
|
preserveDrawingBuffer: true //this is important for screenshots capturing
|
||||||
//powerPreference: "high-performance",
|
//powerPreference: "high-performance",
|
||||||
});
|
});
|
||||||
renderer.setPixelRatio( window.devicePixelRatio );
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
// renderer.toneMapping = THREE.CineonToneMapping;
|
// renderer.toneMapping = THREE.CineonToneMapping;
|
||||||
// renderer.toneMappingExposure = 1.2;
|
// renderer.toneMappingExposure = 1.2;
|
||||||
// renderer.shadowMap.enabled = true;
|
// renderer.shadowMap.enabled = true;
|
||||||
// renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
|
// renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
|
||||||
// renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
// renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||||
// renderer.toneMappingExposure = 1;
|
// renderer.toneMappingExposure = 1;
|
||||||
renderer.shadowMap.enabled = true;
|
renderer.shadowMap.enabled = true;
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||||
renderer.setSize(this.w, this.h);
|
renderer.setSize(this.w, this.h);
|
||||||
renderer.setViewport( 0, 0, this.w, this.h );
|
renderer.setViewport(0, 0, this.w, this.h);
|
||||||
renderer.autoClear = true;
|
renderer.autoClear = true;
|
||||||
|
|
||||||
this.anaglyph = new AnaglyphEffect( renderer );
|
this.anaglyph = new AnaglyphEffect(renderer);
|
||||||
this.anaglyph.setSize( this.w, this.h );
|
this.anaglyph.setSize(this.w, this.h);
|
||||||
|
|
||||||
this.stereo = new StereoEffect(renderer);
|
this.stereo = new StereoEffect(renderer);
|
||||||
this.stereo.setSize( this.w, this.h );
|
this.stereo.setSize(this.w, this.h);
|
||||||
|
|
||||||
const controls = new OrbitControls( this.camera, renderer.domElement );
|
this.activeObjects = new THREE.Group();
|
||||||
if (opts.gizmo){
|
scene.add(this.activeObjects);
|
||||||
|
|
||||||
|
const controls = new OrbitControls(this.camera, renderer.domElement, this.activeObjects);
|
||||||
|
if (opts.gizmo) {
|
||||||
const gizmo = new ViewportGizmo(this.camera, renderer, {
|
const gizmo = new ViewportGizmo(this.camera, renderer, {
|
||||||
container:'.renderer-gizmo',
|
container: '.renderer-gizmo',
|
||||||
//type:'cube'
|
//type:'cube'
|
||||||
});
|
});
|
||||||
gizmo.attachControls(controls);
|
gizmo.attachControls(controls);
|
||||||
@@ -97,18 +105,19 @@ class GameEngine {
|
|||||||
this.orbitControls = controls;
|
this.orbitControls = controls;
|
||||||
//controls.enableZoom = true;
|
//controls.enableZoom = true;
|
||||||
//const controls = new MapControls( camera, renderer.domElement );
|
//const controls = new MapControls( camera, renderer.domElement );
|
||||||
this.transformControls = new TransformControls( this.camera, renderer.domElement );
|
this.transformControls = new TransformControls(this.camera, renderer.domElement);
|
||||||
this.transformControls.addEventListener( 'dragging-changed', function ( event ) {
|
this.transformControls.addEventListener('dragging-changed', function (event) {
|
||||||
controls.enabled = ! event.value;
|
controls.enabled = !event.value;
|
||||||
} );
|
});
|
||||||
// controls.enableDamping = true;
|
// controls.enableDamping = true;
|
||||||
// controls.screenSpacePanning = true;
|
// controls.screenSpacePanning = true;
|
||||||
|
|
||||||
this.renderType = 'ST';
|
this.renderType = 'ST';
|
||||||
|
|
||||||
function animate(time) {
|
function animate(time) {
|
||||||
let delta = clock.getDelta();
|
let delta = clock.getDelta();
|
||||||
mixer.update(delta);
|
gameEngine.hero?.update();
|
||||||
|
gameEngine.mixers.forEach(m => m.update(delta));
|
||||||
gameEngine.handleXrAction(gameEngine, delta)
|
gameEngine.handleXrAction(gameEngine, delta)
|
||||||
gameEngine.render(scene, gameEngine.camera);
|
gameEngine.render(scene, gameEngine.camera);
|
||||||
gameEngine.gizmo?.render();
|
gameEngine.gizmo?.render();
|
||||||
@@ -120,15 +129,11 @@ class GameEngine {
|
|||||||
|
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.scene = scene;
|
|
||||||
|
|
||||||
this.draco = new DRACOLoader().setDecoderPath( '/3rdparty/draco/' );
|
this.draco = new DRACOLoader().setDecoderPath('/3rdparty/draco/');
|
||||||
this.loader = new GLTFLoader();
|
this.loader = new GLTFLoader();
|
||||||
this.loader.setDRACOLoader(this.draco);
|
this.loader.setDRACOLoader(this.draco);
|
||||||
this.mixer = mixer;
|
this.mixers = [mixer];
|
||||||
|
|
||||||
this.activeObjects = new THREE.Group();
|
|
||||||
scene.add(this.activeObjects);
|
|
||||||
|
|
||||||
domNode.appendChild(renderer.domElement);
|
domNode.appendChild(renderer.domElement);
|
||||||
|
|
||||||
@@ -138,24 +143,26 @@ class GameEngine {
|
|||||||
texture.mapping = THREE.EquirectangularReflectionMapping;
|
texture.mapping = THREE.EquirectangularReflectionMapping;
|
||||||
// scene.background = bck; //new THREE.Color(0.7,0.7,0.7);
|
// scene.background = bck; //new THREE.Color(0.7,0.7,0.7);
|
||||||
scene.environment = texture;
|
scene.environment = texture;
|
||||||
scene.background = new THREE.Color(1,1,1);
|
scene.background = new THREE.Color(1, 1, 1);
|
||||||
console.log('GameEngine started')
|
console.log('GameEngine started')
|
||||||
renderer.domElement.addEventListener('wheel', (event)=>{
|
renderer.domElement.addEventListener('wheel', (event) => {
|
||||||
gameEngine.camera.zoom -= event.deltaY / 1000;
|
gameEngine.camera.zoom -= event.deltaY / 1000;
|
||||||
gameEngine.camera.zoom = Math.max(gameEngine.camera.zoom, .4);
|
gameEngine.camera.zoom = Math.max(gameEngine.camera.zoom, .4);
|
||||||
controls.rotateSpeed = 1 / gameEngine.camera.zoom;
|
controls.rotateSpeed = 1 / gameEngine.camera.zoom;
|
||||||
gameEngine.camera.updateProjectionMatrix();
|
gameEngine.camera.updateProjectionMatrix();
|
||||||
})
|
})
|
||||||
|
|
||||||
if (opts.ar){
|
this.initPhysics();
|
||||||
|
|
||||||
|
if (opts.ar) {
|
||||||
renderer.xr.enabled = true;
|
renderer.xr.enabled = true;
|
||||||
document.body.appendChild( ARButton.createButton( renderer, { } ) );
|
document.body.appendChild(ARButton.createButton(renderer, {}));
|
||||||
}
|
}
|
||||||
if (opts.xr){
|
if (opts.xr) {
|
||||||
renderer.xr.enabled = true;
|
renderer.xr.enabled = true;
|
||||||
document.body.appendChild(XRButton.createButton(renderer, opts.depthSense ?{
|
document.body.appendChild(XRButton.createButton(renderer, opts.depthSense ? {
|
||||||
'requiredFeatures': ['depth-sensing'],
|
'requiredFeatures': ['depth-sensing'],
|
||||||
'depthSensing': {
|
'depthSensing': {
|
||||||
usagePreference: ["gpu-optimized"],
|
usagePreference: ["gpu-optimized"],
|
||||||
dataFormatPreference: ["unsigned-short"],
|
dataFormatPreference: ["unsigned-short"],
|
||||||
matchDepthView: false
|
matchDepthView: false
|
||||||
@@ -165,44 +172,44 @@ class GameEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initXrControllers(){
|
initXrControllers() {
|
||||||
let c1 = this.renderer.xr.getController( 0 );
|
let c1 = this.renderer.xr.getController(0);
|
||||||
c1.addEventListener( 'select', this.onSelect.bind(this) );
|
c1.addEventListener('select', this.onSelect.bind(this));
|
||||||
c1.addEventListener( 'selectstart', this.onControllerEvent.bind(this) );
|
c1.addEventListener('selectstart', this.onControllerEvent.bind(this));
|
||||||
c1.addEventListener( 'selectend', this.onControllerEvent.bind(this) );
|
c1.addEventListener('selectend', this.onControllerEvent.bind(this));
|
||||||
c1.addEventListener( 'move', this.onControllerEvent.bind(this) );
|
c1.addEventListener('move', this.onControllerEvent.bind(this));
|
||||||
c1.userData.active = false;
|
c1.userData.active = false;
|
||||||
c1.addEventListener('connected', e=>{
|
c1.addEventListener('connected', e => {
|
||||||
c1.gamepad = e.data.gamepad;
|
c1.gamepad = e.data.gamepad;
|
||||||
this.session = this.renderer.xr.getSession();
|
this.session = this.renderer.xr.getSession();
|
||||||
this.session.addEventListener('selectstart', this.onControllerEvent.bind(this));
|
this.session.addEventListener('selectstart', this.onControllerEvent.bind(this));
|
||||||
})
|
})
|
||||||
this.scene.add( c1 );
|
this.scene.add(c1);
|
||||||
|
|
||||||
let c2 = this.renderer.xr.getController( 1 );
|
let c2 = this.renderer.xr.getController(1);
|
||||||
c2.addEventListener( 'select', this.onSelect.bind(this) );
|
c2.addEventListener('select', this.onSelect.bind(this));
|
||||||
c2.addEventListener( 'selectstart', this.onControllerEvent.bind(this) );
|
c2.addEventListener('selectstart', this.onControllerEvent.bind(this));
|
||||||
c2.addEventListener( 'selectend', this.onControllerEvent.bind(this) );
|
c2.addEventListener('selectend', this.onControllerEvent.bind(this));
|
||||||
c2.addEventListener( 'move', this.onControllerEvent.bind(this) );
|
c2.addEventListener('move', this.onControllerEvent.bind(this));
|
||||||
c2.userData.active = true;
|
c2.userData.active = true;
|
||||||
c2.addEventListener('connected', e=>{
|
c2.addEventListener('connected', e => {
|
||||||
c2.gamepad = e.data.gamepad;
|
c2.gamepad = e.data.gamepad;
|
||||||
})
|
})
|
||||||
this.scene.add( c2 );
|
this.scene.add(c2);
|
||||||
|
|
||||||
const controllerModelFactory = new XRControllerModelFactory();
|
const controllerModelFactory = new XRControllerModelFactory();
|
||||||
|
|
||||||
let controllerGrip1 = this.renderer.xr.getControllerGrip( 0 );
|
let controllerGrip1 = this.renderer.xr.getControllerGrip(0);
|
||||||
controllerGrip1.add( controllerModelFactory.createControllerModel( controllerGrip1 ) );
|
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1));
|
||||||
this.scene.add( controllerGrip1 );
|
this.scene.add(controllerGrip1);
|
||||||
|
|
||||||
let controllerGrip2 = this.renderer.xr.getControllerGrip( 1 );
|
let controllerGrip2 = this.renderer.xr.getControllerGrip(1);
|
||||||
controllerGrip2.add( controllerModelFactory.createControllerModel( controllerGrip2 ) );
|
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2));
|
||||||
this.scene.add( controllerGrip2 );
|
this.scene.add(controllerGrip2);
|
||||||
|
|
||||||
const geometry = new THREE.BufferGeometry().setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 1 ) ] );
|
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, - 1)]);
|
||||||
|
|
||||||
let line = new THREE.Line( geometry );
|
let line = new THREE.Line(geometry);
|
||||||
line.name = 'line';
|
line.name = 'line';
|
||||||
line.scale.z = 5;
|
line.scale.z = 5;
|
||||||
|
|
||||||
@@ -210,42 +217,84 @@ class GameEngine {
|
|||||||
this.xrController2 = c2
|
this.xrController2 = c2
|
||||||
}
|
}
|
||||||
|
|
||||||
handleXrAction(gameEngine, delta){
|
initCameraPivot() {
|
||||||
|
const pivot = new THREE.Object3D()
|
||||||
|
pivot.position.set(0, 1, 10)
|
||||||
|
|
||||||
|
const yaw = new THREE.Object3D()
|
||||||
|
const pitch = new THREE.Object3D()
|
||||||
|
|
||||||
|
this.scene.add(pivot)
|
||||||
|
pivot.add(yaw)
|
||||||
|
yaw.add(pitch)
|
||||||
|
pitch.add(this.perspectiveCamera);
|
||||||
|
pitch.add(this.orthographicCamera);
|
||||||
|
this.cameraPivot = pivot;
|
||||||
|
this.cameraYaw = yaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
initPhysics() {
|
||||||
|
const world = new CANNON.World()
|
||||||
|
world.gravity.set(0, -9.82, 0)
|
||||||
|
const groundMaterial = new CANNON.Material('groundMaterial')
|
||||||
|
const slipperyMaterial = new CANNON.Material('slipperyMaterial')
|
||||||
|
const slippery_ground_cm = new CANNON.ContactMaterial(
|
||||||
|
groundMaterial,
|
||||||
|
slipperyMaterial,
|
||||||
|
{
|
||||||
|
friction: 0,
|
||||||
|
restitution: 0.3,
|
||||||
|
contactEquationStiffness: 1e8,
|
||||||
|
contactEquationRelaxation: 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
world.addContactMaterial(slippery_ground_cm)
|
||||||
|
const planeShape = new CANNON.Plane()
|
||||||
|
const planeBody = new CANNON.Body({ mass: 0, material: groundMaterial })
|
||||||
|
planeBody.addShape(planeShape)
|
||||||
|
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
|
||||||
|
world.addBody(planeBody)
|
||||||
|
this.phy = {
|
||||||
|
world, slipperyMaterial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleXrAction(gameEngine, delta) {
|
||||||
//console.log(event.type);
|
//console.log(event.type);
|
||||||
if (gameEngine.xrController1?.gamepad){
|
if (gameEngine.xrController1?.gamepad) {
|
||||||
let gp = gameEngine.xrController1.gamepad;
|
let gp = gameEngine.xrController1.gamepad;
|
||||||
if (gp.axes[3] != 0){
|
if (gp.axes[3] != 0) {
|
||||||
gameEngine.scene.position.z += gp.axes[3] * delta;
|
gameEngine.scene.position.z += gp.axes[3] * delta;
|
||||||
}
|
}
|
||||||
if (gp.axes[2] != 0){
|
if (gp.axes[2] != 0) {
|
||||||
gameEngine.scene.position.x += gp.axes[2] * delta;
|
gameEngine.scene.position.x += gp.axes[2] * delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gameEngine.xrController2?.gamepad){
|
if (gameEngine.xrController2?.gamepad) {
|
||||||
let gp = gameEngine.xrController2.gamepad;
|
let gp = gameEngine.xrController2.gamepad;
|
||||||
let gp1 = gameEngine.xrController1.gamepad;
|
let gp1 = gameEngine.xrController1.gamepad;
|
||||||
// if (gp.axes[3] != 0){
|
// if (gp.axes[3] != 0){
|
||||||
// let sc = gameEngine.scene.scale.x + gp.axes[3] * delta * 0.5;
|
// let sc = gameEngine.scene.scale.x + gp.axes[3] * delta * 0.5;
|
||||||
// gameEngine.scene.scale.set(sc, sc, sc);
|
// gameEngine.scene.scale.set(sc, sc, sc);
|
||||||
// }
|
// }
|
||||||
if (gp.axes[2] != 0){
|
if (gp.axes[2] != 0) {
|
||||||
if (gp1.buttons[4]?.pressed){
|
if (gp1.buttons[4]?.pressed) {
|
||||||
gameEngine.scene.position.y += gp.axes[2] * delta;
|
gameEngine.scene.position.y += gp.axes[2] * delta;
|
||||||
}else if (gp1.buttons[5]?.pressed){
|
} else if (gp1.buttons[5]?.pressed) {
|
||||||
let sc = gameEngine.scene.scale.x * (gp.axes[2] * delta * 0.5 + 1);
|
let sc = gameEngine.scene.scale.x * (gp.axes[2] * delta * 0.5 + 1);
|
||||||
gameEngine.scene.scale.set(sc, sc, sc);
|
gameEngine.scene.scale.set(sc, sc, sc);
|
||||||
}else{
|
} else {
|
||||||
gameEngine.scene.rotation.y += gp.axes[2] * delta * 0.5;
|
gameEngine.scene.rotation.y += gp.axes[2] * delta * 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gp.buttons[4]?.pressed){
|
if (gp.buttons[4]?.pressed) {
|
||||||
// gameEngine.setCameraOrthographic();
|
// gameEngine.setCameraOrthographic();
|
||||||
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera);
|
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera);
|
||||||
let session = gameEngine.renderer.xr.getFrame().session;
|
let session = gameEngine.renderer.xr.getFrame().session;
|
||||||
console.log(session);
|
console.log(session);
|
||||||
session.pauseDepthSensing();
|
session.pauseDepthSensing();
|
||||||
}
|
}
|
||||||
if (gp.buttons[5]?.pressed){
|
if (gp.buttons[5]?.pressed) {
|
||||||
// gameEngine.setCameraOrthographic();
|
// gameEngine.setCameraOrthographic();
|
||||||
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera);
|
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera);
|
||||||
let session = gameEngine.renderer.xr.getFrame().session;
|
let session = gameEngine.renderer.xr.getFrame().session;
|
||||||
@@ -257,7 +306,7 @@ class GameEngine {
|
|||||||
|
|
||||||
onControllerEvent(event) {
|
onControllerEvent(event) {
|
||||||
//this.handleXrAction(event, this);
|
//this.handleXrAction(event, this);
|
||||||
event.type !=='move' && console.log(event)
|
event.type !== 'move' && console.log(event)
|
||||||
// const controller = event.target;
|
// const controller = event.target;
|
||||||
// if (controller.userData.active === false) return;
|
// if (controller.userData.active === false) return;
|
||||||
// this.raycaster.setFromXRController(controller);
|
// this.raycaster.setFromXRController(controller);
|
||||||
@@ -300,10 +349,10 @@ class GameEngine {
|
|||||||
|
|
||||||
$ = THREE;
|
$ = THREE;
|
||||||
|
|
||||||
async load(url, progress){
|
async load(url, progress) {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject) => {
|
||||||
this.loader.load(url, o=>{
|
this.loader.load(url, o => {
|
||||||
o.scene.traverse(object=>{
|
o.scene.traverse(object => {
|
||||||
// if (object.name == 'environment'){
|
// if (object.name == 'environment'){
|
||||||
// console.log('env recomputing')
|
// console.log('env recomputing')
|
||||||
// object.material.shading = THREE.SmoothShading;
|
// object.material.shading = THREE.SmoothShading;
|
||||||
@@ -318,9 +367,9 @@ class GameEngine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadTexture(url, progress){
|
async loadTexture(url, progress) {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject) => {
|
||||||
new THREE.TextureLoader().load(url, texture=>{
|
new THREE.TextureLoader().load(url, texture => {
|
||||||
//texture.encoding = THREE.sRGBEncoding;
|
//texture.encoding = THREE.sRGBEncoding;
|
||||||
texture.colorSpace = THREE.SRGBColorSpace;
|
texture.colorSpace = THREE.SRGBColorSpace;
|
||||||
resolve(texture)
|
resolve(texture)
|
||||||
@@ -328,54 +377,54 @@ class GameEngine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadPanorama(url){
|
async loadPanorama(url) {
|
||||||
let t = await this.loadTexture(url);
|
let t = await this.loadTexture(url);
|
||||||
t.mapping = THREE.EquirectangularReflectionMapping;
|
t.mapping = THREE.EquirectangularReflectionMapping;
|
||||||
this.scene.background = t;
|
this.scene.background = t;
|
||||||
this.scene.environment = t;
|
this.scene.environment = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
async captureScreenshot(type = 'image/webp', quality = 80){
|
async captureScreenshot(type = 'image/webp', quality = 80) {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject) => {
|
||||||
this.renderer.domElement.toBlob(resolve, type, quality)
|
this.renderer.domElement.toBlob(resolve, type, quality)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
playAnimation(object, clip, play = true){
|
playAnimation(object, clip, play = true) {
|
||||||
let action = this.mixer.clipAction(clip, object);
|
let action = this.mixers[0].clipAction(clip, object);
|
||||||
if (play) action.play();
|
if (play) action.play();
|
||||||
else action.stop();
|
else action.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(){
|
stop() {
|
||||||
this.renderer?.setAnimationLoop(null);
|
this.renderer?.setAnimationLoop(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMouseVector(mouseEvent, domElement){
|
getMouseVector(mouseEvent, domElement) {
|
||||||
//console.log(mouseEvent, domElement)
|
//console.log(mouseEvent, domElement)
|
||||||
const mouse = new THREE.Vector2();
|
const mouse = new THREE.Vector2();
|
||||||
if (this.renderType == 'VR'){
|
if (this.renderType == 'VR') {
|
||||||
let x;
|
let x;
|
||||||
if (mouseEvent.offsetX > window.innerWidth / 2){
|
if (mouseEvent.offsetX > window.innerWidth / 2) {
|
||||||
x = (mouseEvent.offsetX - window.innerWidth / 2) * 2
|
x = (mouseEvent.offsetX - window.innerWidth / 2) * 2
|
||||||
}else {
|
} else {
|
||||||
x = mouseEvent.offsetX * 2;
|
x = mouseEvent.offsetX * 2;
|
||||||
}
|
}
|
||||||
mouse.x = ( x / window.innerWidth ) * 2 - 1;
|
mouse.x = (x / window.innerWidth) * 2 - 1;
|
||||||
mouse.y = - ( mouseEvent.offsetY / window.innerHeight ) * 2 + 1;
|
mouse.y = - (mouseEvent.offsetY / window.innerHeight) * 2 + 1;
|
||||||
}else{
|
} else {
|
||||||
mouse.x = ( mouseEvent.offsetX / domElement.clientWidth ) * 2 - 1;
|
mouse.x = (mouseEvent.offsetX / domElement.clientWidth) * 2 - 1;
|
||||||
mouse.y = - ( mouseEvent.offsetY / domElement.clientHeight ) * 2 + 1;
|
mouse.y = - (mouseEvent.offsetY / domElement.clientHeight) * 2 + 1;
|
||||||
}
|
}
|
||||||
return mouse;
|
return mouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
intersect(mouseEvent, domElement, objects, recursive = false, returnInputObjects = true){
|
intersect(mouseEvent, domElement, objects, recursive = false, returnInputObjects = true) {
|
||||||
let mouse = this.getMouseVector(mouseEvent, domElement);
|
let mouse = this.getMouseVector(mouseEvent, domElement);
|
||||||
this.raycaster.setFromCamera(mouse, this.camera);
|
this.raycaster.setFromCamera(mouse, this.camera);
|
||||||
let intersects = this.raycaster.intersectObjects(objects.filter(o=>o.visible), recursive);
|
let intersects = this.raycaster.intersectObjects(objects.filter(o => o.visible), recursive);
|
||||||
if (returnInputObjects && recursive){
|
if (returnInputObjects && recursive) {
|
||||||
intersects.forEach(o=>{
|
intersects.forEach(o => {
|
||||||
while (o.object && !objects.includes(o.object)) {
|
while (o.object && !objects.includes(o.object)) {
|
||||||
o.object = o.object.parent;
|
o.object = o.object.parent;
|
||||||
}
|
}
|
||||||
@@ -384,13 +433,13 @@ class GameEngine {
|
|||||||
return intersects;
|
return intersects;
|
||||||
}
|
}
|
||||||
|
|
||||||
autoScale(object, mk = 1){
|
autoScale(object, mk = 1) {
|
||||||
let bb = new THREE.Box3().setFromObject(object);
|
let bb = new THREE.Box3().setFromObject(object);
|
||||||
let k = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
|
let k = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
|
||||||
object.scale.multiplyScalar(mk/k);
|
object.scale.multiplyScalar(mk / k);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCamera(camera){
|
setCamera(camera) {
|
||||||
//camera.updateProjectionMatrix();
|
//camera.updateProjectionMatrix();
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
//this.transformControls.camera = camera;
|
//this.transformControls.camera = camera;
|
||||||
@@ -413,11 +462,11 @@ class GameEngine {
|
|||||||
this.camera = o;
|
this.camera = o;
|
||||||
this.transformControls.camera = o;
|
this.transformControls.camera = o;
|
||||||
this.orbitControls.object = o;
|
this.orbitControls.object = o;
|
||||||
if (this.gizmo){
|
if (this.gizmo) {
|
||||||
this.gizmo.camera = o;
|
this.gizmo.camera = o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCameraPerspective() {
|
setCameraPerspective() {
|
||||||
let o = this.perspectiveCamera;
|
let o = this.perspectiveCamera;
|
||||||
// const oldY = o.position.y;
|
// const oldY = o.position.y;
|
||||||
@@ -427,12 +476,12 @@ class GameEngine {
|
|||||||
this.camera = o;
|
this.camera = o;
|
||||||
this.transformControls.camera = o;
|
this.transformControls.camera = o;
|
||||||
this.orbitControls.object = o;
|
this.orbitControls.object = o;
|
||||||
if (this.gizmo){
|
if (this.gizmo) {
|
||||||
this.gizmo.camera = o;
|
this.gizmo.camera = o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(w, h){
|
resize(w, h) {
|
||||||
this.w = w;
|
this.w = w;
|
||||||
this.h = h;
|
this.h = h;
|
||||||
this.aspect = this.w / this.h
|
this.aspect = this.w / this.h
|
||||||
@@ -443,21 +492,21 @@ class GameEngine {
|
|||||||
this.orthographicCamera.top = this.frustumSize / 2;
|
this.orthographicCamera.top = this.frustumSize / 2;
|
||||||
this.orthographicCamera.bottom = - this.frustumSize / 2;
|
this.orthographicCamera.bottom = - this.frustumSize / 2;
|
||||||
this.renderer.setSize(w, h);
|
this.renderer.setSize(w, h);
|
||||||
this.renderer.setViewport( 0, 0, this.w, this.h );
|
this.renderer.setViewport(0, 0, this.w, this.h);
|
||||||
this.anaglyph.setSize( w, h );
|
this.anaglyph.setSize(w, h);
|
||||||
this.stereo.setSize( w, h );
|
this.stereo.setSize(w, h);
|
||||||
this.gizmo?.update();
|
this.gizmo?.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(scene, camera){
|
render(scene, camera) {
|
||||||
if (this.renderType == 'VR'){
|
if (this.renderType == 'VR') {
|
||||||
this.stereo?.render( scene, camera );
|
this.stereo?.render(scene, camera);
|
||||||
}else if (this.renderType == 'AG'){
|
} else if (this.renderType == 'AG') {
|
||||||
this.anaglyph?.render( scene, camera );
|
this.anaglyph?.render(scene, camera);
|
||||||
}else{
|
} else {
|
||||||
this.renderer.render( scene, camera );
|
this.renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {GameEngine}
|
export { GameEngine }
|
||||||
Reference in New Issue
Block a user