bot propelMove
|
Before Width: | Height: | Size: 2.6 MiB |
@@ -1,11 +0,0 @@
|
|||||||
Model Information:
|
|
||||||
* title: Mech Drone
|
|
||||||
* source: https://sketchfab.com/3d-models/mech-drone-8d06874aac5246c59edb4adbe3606e0e
|
|
||||||
* author: Willy Decarpentrie (https://sketchfab.com/skudgee)
|
|
||||||
|
|
||||||
Model License:
|
|
||||||
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
|
||||||
* requirements: Author must be credited. Commercial use is allowed.
|
|
||||||
|
|
||||||
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
|
|
||||||
This work is based on "Mech Drone" (https://sketchfab.com/3d-models/mech-drone-8d06874aac5246c59edb4adbe3606e0e) by Willy Decarpentrie (https://sketchfab.com/skudgee) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
|
||||||
|
Before Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 6.9 MiB |
@@ -41,6 +41,14 @@ body[eicapp] {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: repeating-linear-gradient( -45deg, #000, #333 10px, #000 10px, #333 20px );
|
background: repeating-linear-gradient( -45deg, #000, #333 10px, #000 10px, #333 20px );
|
||||||
}
|
}
|
||||||
|
.helperBotCanvas{
|
||||||
|
display: block;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
[eicapp] [eicapptoolbar] {
|
[eicapp] [eicapptoolbar] {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
"path": "/Windoz",
|
"path": "/Windoz",
|
||||||
"classes": [ "WindozController", "WindozDomContent", "WindozDialogContent", "WindozMetaData", "WindozModel", "WindozPluralModel", "WindozBusModel" ],
|
"classes": [ "WindozController", "WindozDomContent", "WindozDialogContent", "WindozMetaData", "WindozModel", "WindozPluralModel", "WindozBusModel" ],
|
||||||
"dependencies" : { "WindozPluralModel": [ "WindozModel" ] }
|
"dependencies" : { "WindozPluralModel": [ "WindozModel" ] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/HelperBot",
|
||||||
|
"classes": [ "HelperBot.module" ],
|
||||||
|
"dependencies" : { }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"masterController": "WindozAppController",
|
"masterController": "WindozAppController",
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||||
|
|
||||||
|
|
||||||
|
class HelperBot{
|
||||||
|
|
||||||
|
constructor(canvasSelector){
|
||||||
|
this.renderer = null
|
||||||
|
this.canvasEl = document.querySelector(canvasSelector)
|
||||||
|
this.initScene()
|
||||||
|
window.bot=this
|
||||||
|
}
|
||||||
|
|
||||||
|
initScene(){
|
||||||
|
|
||||||
|
this.canvasEl .width = window.innerWidth
|
||||||
|
this.canvasEl .height = window.innerHeight
|
||||||
|
|
||||||
|
this.scene = new THREE.Scene()
|
||||||
|
|
||||||
|
this.camera = new THREE.PerspectiveCamera(30, this.canvasEl.clientWidth / this.canvasEl.clientHeight, 0.1, 1000)
|
||||||
|
this.camera.position.set(0, 1, 10)
|
||||||
|
this.camera.lookAt(0, 0, 0)
|
||||||
|
|
||||||
|
// Lights
|
||||||
|
const dLight1 = new THREE.DirectionalLight(0xffffff, 1.5)
|
||||||
|
dLight1.position.set(5, 5, 5)
|
||||||
|
this.scene.add(dLight1)
|
||||||
|
const dLight2 = new THREE.DirectionalLight(0xffffff, 1.5)
|
||||||
|
dLight2.position.set(-5, -5, 5)
|
||||||
|
this.scene.add(dLight2)
|
||||||
|
this.scene.add(new THREE.AmbientLight(0xffffff, 1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.renderer = new THREE.WebGLRenderer({
|
||||||
|
canvas: this.canvasEl ,
|
||||||
|
alpha: true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.renderer.setSize(this.canvasEl .width, this.canvasEl .height)
|
||||||
|
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||||
|
this.renderer.setClearColor(0x000000, 0)
|
||||||
|
|
||||||
|
|
||||||
|
this.threeClock = new THREE.Clock()
|
||||||
|
const loader = new GLTFLoader().setPath('/app/assets/3dModels/')
|
||||||
|
loader.load('mecha2.glb', async gltf => {
|
||||||
|
this.mecha = new THREE.Group()
|
||||||
|
this.scene.add(this.mecha)
|
||||||
|
|
||||||
|
this.mechaModel = gltf.scene
|
||||||
|
this.mecha.add(this.mechaModel)
|
||||||
|
|
||||||
|
this.mechaModel.scale.set(5, 5, 5)
|
||||||
|
this.mechaModel.rotation.set(0, 0, 0)
|
||||||
|
this.mechaModel.position.set(0, -1.5, 0)
|
||||||
|
this.mechaModel.updateMatrix()
|
||||||
|
this.mechaModel.rotateY(-Math.PI) // 180deg so facing user
|
||||||
|
|
||||||
|
// GPU compilation (optimization)
|
||||||
|
await this.renderer.compileAsync(this.mechaModel, this.camera, this.scene)
|
||||||
|
|
||||||
|
this.mechaModel.traverse(obj => {
|
||||||
|
if (obj.isMesh && obj.material && obj.material.color) {
|
||||||
|
const c = obj.material.color.clone()
|
||||||
|
const boost = 0.1
|
||||||
|
obj.material.emissive = c
|
||||||
|
obj.material.emissiveIntensity = boost
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (gltf.animations && gltf.animations.length > 0) {
|
||||||
|
this.mixer = new THREE.AnimationMixer(this.mechaModel)
|
||||||
|
const action = this.mixer.clipAction(gltf.animations[0])
|
||||||
|
action.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.leftArm = this.mechaModel.getObjectByName('ArmL1_01')
|
||||||
|
this.animate = true
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
this.resizeWindow()
|
||||||
|
window.addEventListener('resize', this.resizeWindow.bind(this))
|
||||||
|
this.render()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeWindow() {
|
||||||
|
const w = window.innerWidth
|
||||||
|
const h = window.innerHeight
|
||||||
|
|
||||||
|
this.canvasEl .width = w
|
||||||
|
this.canvasEl .height = h
|
||||||
|
|
||||||
|
this.renderer.setSize(w, h)
|
||||||
|
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||||
|
|
||||||
|
this.camera.aspect = w / h
|
||||||
|
this.camera.updateProjectionMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
startRendering(){
|
||||||
|
this.addControls()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
TWEEN.update()
|
||||||
|
const resized = this.resizeRendererToDisplaySize()
|
||||||
|
|
||||||
|
const delta = this.threeClock.getDelta()
|
||||||
|
if(this.mixer && this.animate) this.mixer.update(delta)
|
||||||
|
|
||||||
|
this.renderer.render(this.scene, this.camera)
|
||||||
|
requestAnimationFrame(this.render.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeRendererToDisplaySize() {
|
||||||
|
const width = this.canvasEl.clientWidth
|
||||||
|
const height = this.canvasEl.clientHeight
|
||||||
|
if (this.canvasEl.width !== width || this.canvasEl.height !== height) {
|
||||||
|
this.renderer.setSize(width, height, false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
translateBot(position, delay=500, endcb, updatecb){
|
||||||
|
if(this.movingBotTween) this.movingBotTween.end()
|
||||||
|
this.movingBotTween = new TWEEN.Tween(this.mecha.position)
|
||||||
|
.to({ x: Number(position.x),
|
||||||
|
y: Number(position.z),
|
||||||
|
z: Number(position.y),
|
||||||
|
}, delay)
|
||||||
|
.easing(TWEEN.Easing.Sinusoidal.InOut)
|
||||||
|
if(typeof(endcb)=='function') this.movingBotTween.onComplete(endcb)
|
||||||
|
if(typeof(updatecb)=='function') this.movingBotTween.onUpdate(updatecb)
|
||||||
|
this.movingBotTween.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateBot(rotation, delay=500, endcb, updatecb){
|
||||||
|
//TODO : respect existing Z
|
||||||
|
|
||||||
|
if(this.rotatingBotTween) this.rotatingBotTween.end()
|
||||||
|
this.rotatingBotTween = new TWEEN.Tween(this.mecha.rotation)
|
||||||
|
.to({ x: Number(rotation.x*Math.PI/180),
|
||||||
|
y: Number(rotation.z*Math.PI/180),
|
||||||
|
z: Number(rotation.y*Math.PI/180),
|
||||||
|
}, delay)
|
||||||
|
.easing(TWEEN.Easing.Sinusoidal.InOut)
|
||||||
|
if(typeof(endcb)=='function') this.rotatingBotTween.onComplete(endcb)
|
||||||
|
if(typeof(updatecb)=='function') this.rotatingBotTween.onUpdate(updatecb)
|
||||||
|
this.rotatingBotTween.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
propelMove(position){
|
||||||
|
//TODO : respect existing Z rotation
|
||||||
|
|
||||||
|
const moveDelay = 1000
|
||||||
|
const dx = this.mecha.position.x - position.x
|
||||||
|
const dy = this.mecha.position.z - position.y
|
||||||
|
const dz = this.mecha.position.y - position.z
|
||||||
|
const delay = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)) * moveDelay
|
||||||
|
const ax = (dy>0.5) ? -10 : ((dy<-0.5) ? 10 : 0 )
|
||||||
|
const ay = (dx>0.5) ? 10 : ((dx<-0.5) ? -10 : 0 )
|
||||||
|
|
||||||
|
console.log( this.mecha.position.x, this.mecha.position.y)
|
||||||
|
console.log(dx,dy,ax,ay)
|
||||||
|
|
||||||
|
const rotationDelay = Math.min(delay/4, 500)
|
||||||
|
this.rotateBot({x: ax, y: ay, z: 0}, rotationDelay)
|
||||||
|
|
||||||
|
let chkpt1 = true, chkpt2 = true, chkpt3 = true
|
||||||
|
const startTime = Date.now()
|
||||||
|
this.translateBot(position, delay, null, () => {
|
||||||
|
const t = (Date.now()-startTime)/delay
|
||||||
|
if(chkpt1 && (t >= 0.2)){ //Time to stop accelerating
|
||||||
|
this.rotateBot({x: 0, y: 0, z: 0}, rotationDelay)
|
||||||
|
chkpt1 = false
|
||||||
|
}
|
||||||
|
if(chkpt2 && (t >= 0.7)){ //Time to brake
|
||||||
|
this.rotateBot({x: -ax, y: -ay, z: 0}, rotationDelay)
|
||||||
|
chkpt2 = false
|
||||||
|
}
|
||||||
|
if(chkpt3 && (t >= 0.9)){ //Time to restore
|
||||||
|
this.rotateBot({x: 0, y: 0, z: 0}, rotationDelay)
|
||||||
|
chkpt3 = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registerClass('HelperBot', HelperBot)
|
||||||
@@ -89,10 +89,12 @@ export class Threetobus{
|
|||||||
if(mapping.child) id += '_'+mapping.child
|
if(mapping.child) id += '_'+mapping.child
|
||||||
if(id){
|
if(id){
|
||||||
const obj3D = this.scene.getObjectByName(id)
|
const obj3D = this.scene.getObjectByName(id)
|
||||||
|
if(obj3D){
|
||||||
this.assignFromConfig(payload, mapping, obj3D)
|
this.assignFromConfig(payload, mapping, obj3D)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assignFromConfig(payload, mapping, obj3D) {
|
assignFromConfig(payload, mapping, obj3D) {
|
||||||
const tweenProps = {
|
const tweenProps = {
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ API remove
|
|||||||
API listAgentTypes
|
API listAgentTypes
|
||||||
if unsavec changes in scene => confirm before reloading
|
if unsavec changes in scene => confirm before reloading
|
||||||
|
|
||||||
|
agent rotation (param + tween)
|
||||||
|
|
||||||
Bugs
|
Bugs
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||||
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
||||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
||||||
|
|
||||||
export class kfArena{
|
export class kfArena{
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,4 @@
|
|||||||
</menu>
|
</menu>
|
||||||
<div class="app-workspace"></div>
|
<div class="app-workspace"></div>
|
||||||
<div class="app-content-thesaurus"></div>
|
<div class="app-content-thesaurus"></div>
|
||||||
|
<canvas class="helperBotCanvas"></canvas>
|
||||||
@@ -18,6 +18,8 @@ class EICAppTemplate extends WindozDomContent {
|
|||||||
|
|
||||||
// todo : replace with something more dynamic
|
// todo : replace with something more dynamic
|
||||||
this.loadAppMenu('global/app-menu-map.json');
|
this.loadAppMenu('global/app-menu-map.json');
|
||||||
|
|
||||||
|
new app.LoadedClasses.HelperBot('.helperBotCanvas')
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalResize() { WindozController.resize(); }
|
onGlobalResize() { WindozController.resize(); }
|
||||||
@@ -117,6 +119,7 @@ class EICAppTemplate extends WindozDomContent {
|
|||||||
onBusConnected() { this.userIcon.online = true; }
|
onBusConnected() { this.userIcon.online = true; }
|
||||||
|
|
||||||
onBusClosed() { this.userIcon.online = false; }
|
onBusClosed() { this.userIcon.online = false; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.registerClass('EICAppTemplate', EICAppTemplate);
|
app.registerClass('EICAppTemplate', EICAppTemplate);
|
||||||