ai-creature / index.html
Artod's picture
Upload 16 files
b396e7b
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The AI Creature</title>
<!-- Babylon.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
<script src="https://preview.babylonjs.com/ammo.js"></script>
<script src="https://preview.babylonjs.com/cannon.js"></script>
<script src="https://preview.babylonjs.com/Oimo.js"></script>
<script src="https://preview.babylonjs.com/earcut.min.js"></script>
<script src="https://preview.babylonjs.com/babylon.js"></script>
<script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
<script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
<script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
<script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
<script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
<script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
<script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
<!-- tf.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.12.0/dist/tf.min.js"></script>
<script src="agent_sac.js"></script>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
#testCanvas0 {
position:absolute;
width: 128px;
height: 128px;
right:600px;
bottom: 0;
}
#testCanvas1 {
position:absolute;
width: 128px;
height: 128px;
right:450px;
bottom: 0;
}
#testCanvas2 {
position:absolute;
width: 128px;
height: 128px;
right: 300px;
bottom: 0;
}
#testCanvas3 {
position: absolute;
width: 128px;
height: 128px;
right: 150px;
bottom: 0;
}
#testCanvas4 {
position: absolute;
width: 128px;
height: 128px;
right: 0px;
bottom: 0;
}
/* #votes {
position: absolute;
border: 1px solid black;
bottom: 200px;
right: 0px;
width: 100px;
height: 150px;
} */
.vote {
position: absolute;
width: 60px;
height: 60px;
right: 10px;
}
.vote:hover {
cursor: pointer;
}
#like {
bottom: 200px;
}
#dislike {
bottom: 120px;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<canvas id="testCanvas0"></canvas>
<canvas id="testCanvas1"></canvas>
<canvas id="testCanvas2"></canvas>
<canvas id="testCanvas3"></canvas>
<canvas id="testCanvas4"></canvas>
<!-- <div id="votes"> -->
<div class="vote" id="like">
<img src="data:image/png;base64,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" alt="like">
</div>
<div class="vote" id="dislike">
<img src="data:image/png;base64,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" alt="">
</div>
<!-- </div> -->
<script>
window.engine = null;
window.scene = null;
window.sceneToRender = null;
const agent = new AgentSac({trainable: false, verbose: false})
const canvas = document.getElementById("renderCanvas");
const createDefaultEngine = () => new BABYLON.Engine(canvas, true, {
preserveDrawingBuffer: true,
stencil: true,
disableWebGL2Support: false
})
window.vote = 0
document.getElementById("like").addEventListener("click", () => {
// if (!transitions.length) return
window.reward = 1
// transitions[transitions.length - 1].reward += reward
// globalReward += reward
// console.log('reward like: ', transitions[transitions.length - 1].reward, globalReward)
})
document.getElementById("dislike").addEventListener("click", () => {
// if (!transitions.length) return
window.reward = -1
// transitions[transitions.length - 1].reward += reward
// globalReward += reward
// console.log('reward dislike: ', transitions[transitions.length - 1].reward, globalReward)
})
window.transitions = []
window.globalReward = 0
const BINOCULAR = true
const createScene = async () => {
await agent.init()
// This creates a basic Babylon Scene object (non-mesh)
const scene = new BABYLON.Scene(engine);
scene.collisionsEnabled = true
// Environment
const hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("3d/env/environment.dds", scene);
hdrTexture.name = "envTex";
hdrTexture.gammaSpace = false;
scene.environmentTexture = hdrTexture;
const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", {size:1000.0}, scene);
const skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
skyboxMaterial.backFaceCulling = false;
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("3d/env/skybox", scene);
skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
skybox.material = skyboxMaterial;
//CAMERA
const camera = new BABYLON.ArcRotateCamera("Camera", BABYLON.Tools.ToRadians(-120), BABYLON.Tools.ToRadians(80), 65, new BABYLON.Vector3(0, -15, 0), scene);
camera.attachControl(canvas, true);
camera.lowerRadiusLimit = 10;
camera.upperRadiusLimit = 120;
camera.collisionRadius = new BABYLON.Vector3(2, 2, 2);
camera.checkCollisions = true;
//enable Physics in the scene vector = gravity
scene.enablePhysics(new BABYLON.Vector3(0, 0, 0), new BABYLON.AmmoJSPlugin(false));
const physicsEngine = scene.getPhysicsEngine()
// physicsEngine.setSubTimeStep(physicsEngine.getTimeStep()/3 * 1000)
physicsEngine.setTimeStep(1 / 60)
physicsEngine.setSubTimeStep(1)
//LIGHTS
const light1 = new BABYLON.PointLight("light1", new BABYLON.Vector3(0, 5,-6), scene);
const light2 = new BABYLON.PointLight("light2", new BABYLON.Vector3(6, 5, 3.5), scene);
const light3 = new BABYLON.DirectionalLight("light3", new BABYLON.Vector3(20, -5, 20), scene);
light1.intensity = 15;
light2.intensity = 5;
engine.displayLoadingUI();
await Promise.all([
BABYLON.SceneLoader.AppendAsync("3d/marbleTower.glb"),
BABYLON.SceneLoader.AppendAsync("https://models.babylonjs.com/Marble/marble/marble.gltf")
])
scene.getMeshByName("marble").isVisible = false
const tower = scene.getMeshByName("tower");
tower.setParent(null)
tower.checkCollisions = true;
tower.impostor = new BABYLON.PhysicsImpostor(tower, BABYLON.PhysicsImpostor.MeshImpostor, {
mass: 0,
friction: 1
}, scene);
tower.material = scene.getMaterialByName("stone")
tower.material.backFaceCulling = false
/* CREATURE */
const creature = BABYLON.MeshBuilder.CreateSphere("creature", {diameter1segments:32}, scene)
creature.parent = null
creature.setParent(null)
creature.position = new BABYLON.Vector3(0,-5,0)
creature.isPickable = false
const crMat = new BABYLON.StandardMaterial("cr_mat", scene);
crMat.alpha = 0 // for screenshots
creature.material = crMat
creature.impostor = new BABYLON.PhysicsImpostor(creature, BABYLON.PhysicsImpostor.SphereImpostor, {
mass: 1,
friction: 0,
stiffness: 0,
restitution: 0
}, scene)
BABYLON.ParticleHelper.SnippetUrl = "3d/snippet";
// Sparks
creature.sparks = await BABYLON.ParticleHelper.CreateFromSnippetAsync("UY098C-3.json", scene, false);
creature.sparks.emitter = creature;
// Core
creature.glow = await BABYLON.ParticleHelper.CreateFromSnippetAsync("EXUQ7M-5.json", scene, false);
creature.glow.emitter = creature;
/* CREATURE's CAMERA */
const crCameraLeft = new BABYLON.UniversalCamera("cr_camera_l", new BABYLON.Vector3(0, 0, 0), scene)
crCameraLeft.parent = creature
crCameraLeft.position = new BABYLON.Vector3(-0.5, 0, 0)//new BABYLON.Vector3(0, 5, -10)
crCameraLeft.fov = 2
crCameraLeft.setTarget(new BABYLON.Vector3(-1, 0, 0.6))
const crCameraRight = new BABYLON.UniversalCamera("cr_camera_r", new BABYLON.Vector3(0, 0, 0), scene)
crCameraRight.parent = creature
crCameraRight.position = new BABYLON.Vector3(0.5, 0, 0)//new BABYLON.Vector3(0, 5, -10)
crCameraRight.fov = 2
crCameraRight.setTarget(new BABYLON.Vector3(1, 0, 0.6))
const crCameraLeftPl = BABYLON.MeshBuilder.CreateSphere("crCameraLeftPl", {diameter0.1segments32}, scene);
crCameraLeftPl.parent = creature
crCameraLeftPl.position = new BABYLON.Vector3(-0.5, 0, 0)
const crCameraLeftPlclMat = new BABYLON.StandardMaterial("crCameraLeftPlclMat", scene)
crCameraLeftPlclMat.alpha = 0.3 // for screenshots
crCameraLeftPlclMat.diffuseColor = new BABYLON.Color3(0, 0, 0)
crCameraLeftPl.material = crCameraLeftPlclMat
const crCameraRightPl = BABYLON.MeshBuilder.CreateSphere("crCameraRightPl", {diameter0.1segments32}, scene);
crCameraRightPl.parent = creature
crCameraRightPl.position = new BABYLON.Vector3(0.5, 0, 0)
const crCameraRightPlclMat = new BABYLON.StandardMaterial("crCameraRightPlclMat", scene)
crCameraRightPlclMat.alpha = 0.3 // for screenshots
crCameraRightPlclMat.diffuseColor = new BABYLON.Color3(0, 0, 0)
crCameraRightPl.material = crCameraRightPlclMat
// crCameraLeft.rotation = new BABYLON.Vector3(0, -(Math.PI - 0.3), 0)
// crCameraLeft.fovMode = BABYLON.Camera.PERSPECTIVE_CAMERA;
// crCameraRight.rotation = new BABYLON.Vector3(0, +(Math.PI - 0.3), 0)
// crCameraRight.fovMode = BABYLON.Camera.FOVMODE_HORIZONTAL_FIXED;
// crCameraRight.checkCollisions = true;
// crCamera.rotation = (new BABYLON.Vector3(0.5, 0, 0))
// crCamera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
// crCamera.ellipsoidOffset = new BABYLON.Vector3(3, 3, 3);
// creature.checkCollisions = true;
// scene.collisionsEnabled = true;
// crCamera.applyGravity = true;
// crCamera.fovMode = BABYLON.Camera.PERSPECTIVE_CAMERA;
// crCamera.fovMode = BABYLON.Camera.FOVMODE_HORIZONTAL_FIXED;
// crCamera.inertia = 2
// crCamera.setTarget(new BABYLON.Vector3(2, 0, 0))
// const crCameraMesh = BABYLON.MeshBuilder.CreateSphere("cr_camera_mesh", {diameter: 1, segments: 32}, scene);
// crCameraMesh.parent = crCamera
// crCameraMesh.isVisible = 1
/* CLIENT */
const client = BABYLON.MeshBuilder.CreateSphere("client", {diameter3segments32}, scene);
client.parent = camera
client.setParent(camera)
// client.position = new BABYLON.Vector3(0, -12,0)
const clMat = new BABYLON.StandardMaterial("cl_mat", scene)
clMat.diffuseColor = new BABYLON.Color3(0, 0, 0)
client.material = clMat
engine.hideLoadingUI();
/* CAGE */
const cage = BABYLON.MeshBuilder.CreateSphere("cage", {
segements: 64,
diameter: 50
}, scene)
// const cage = BABYLON.MeshBuilder.CreateBox("cage", {
// width: 100,
// depth: 100,
// height: 40
// }, scene)
cage.parent = null
cage.setParent(null)
cage.position = new BABYLON.Vector3(0, -12,0)
cage.isPickable = true
const cageMat = new BABYLON.StandardMaterial("cage_mat", scene);
cageMat.alpha = 0.1 // for ray hit
cage.material = cageMat
cage.material.backFaceCulling = false
cage.impostor = new BABYLON.PhysicsImpostor(cage, BABYLON.PhysicsImpostor.MeshImpostor, {
mass: 0,
friction: 1
}, scene);
/* MIRROR */
/* const mirror = BABYLON.MeshBuilder.CreateBox("mirror", {
width: 10,
depth: 0.1,
height: 5
}, scene)
mirror.material = new BABYLON.StandardMaterial("mirror_mat", scene)
mirror.position = new BABYLON.Vector3(20, 0, 0)
// mirror.addRotation(0, Math.PI/2, 0)
mirror.isVisible = true
// How to use: mirror.material.diffuseTexture = new BABYLON.Texture(base64Data, scene) // timer ~1ms
*/
// const [ballRed, ballGreen, ballBlue, ballPurple, ballYellow] = ['red', 'green', 'blue', 'purple', 'yellow'].map(color => {
const ballPos = [[-10,-10,10], [10,-10,-10], [-10,-10,-10], [10,-10,10]]
// const balls = ['red', 'green', 'blue', 'purple'].map((color, i) => {
const balls = ['green', 'green', 'red', 'red'].map((color, i) => {
const ball = BABYLON.MeshBuilder.CreateSphere("ball_"+ color + i, {diameter7segments: 64}, scene)
ball.position = new BABYLON.Vector3(...ballPos[i])
ball.parent = null
ball.setParent(null)
ball.isPickable = true
ball.impostor = new BABYLON.PhysicsImpostor(ball, BABYLON.PhysicsImpostor.SphereImpostor, {
mass: 7,
friction: 1,
stiffness: 1,
restitution: 1
}, scene);
ball.material = scene.getMaterialByName(color + "Mat")
ball.checkCollisions = true
ball.material.backFaceCulling = false
return ball
})
// balls[0].position = new BABYLON.Vector3(10, 0, 0)
/* SHuffle */
// scene.onPointerDown = function(evt, pickInfo) {
// if(pickInfo.hit && pickInfo.pickedMesh.id.startsWith('cage')) {
// const getRand = () => new BABYLON.Vector3(Math.random()/10 - 0.1, Math.random()/10 - 0.1, Math.random()/10 - 0.1)
// balls.forEach(ball => ball.impostor.applyImpulse(getRand(), BABYLON.Vector3.Zero()))
// }
// }
// setInterval(()=>{
// const getRand = () => new BABYLON.Vector3(Math.random()/10 - 0.1, Math.random()/10 - 0.1, Math.random()/10 - 0.1)
// balls.forEach(ball => ball.impostor.applyImpulse(getRand(), BABYLON.Vector3.Zero()))
// }, 1000)
// ballRed.impostor.applyImpulse(new BABYLON.Vector3(0, -20, 0), BABYLON.Vector3.Zero())
// ballGr.impostor.applyImpulse(new BABYLON.Vector3(0, -20, 0), BABYLON.Vector3.Zero())
/* WORKER */
const worker = new Worker('worker.js')
let inited = false
worker.addEventListener('message', e => {
const { weights, frame } = e.data
tf.tidy(() => {
if (weights) {
inited = true
agent.actor.setWeights(weights.map(w => tf.tensor(w))) // timer ~30ms
if (Math.random() > 0.99) console.log('weights:', weights)
}
})
})
/* COLLISIONS DETECTION */
const impostors = scene.getPhysicsEngine()._impostors.filter(im => im.object.id !== creature.id)
creature.impostor.registerOnPhysicsCollide(impostors, (body1, body2) => {})
impostors.forEach(impostor => {
impostor.onCollide = e => {
if (window.onCollide) {
const collision = e.point.subtract(creature.position).normalize()
window.onCollide(collision, impostor.object.id)
}
}
})
// ;(() => {
// let coll
// creature.impostor.onCollide = e => {
// coll = e.point.subtract(creature.position).normalize()
// console.log('crea', coll)
// if (window.onCollide)
// window.onCollide(coll)
// }
// balls.forEach(ball => {
// ball.impostor.onCollide = e => {
// const collision = e.point.subtract(creature.position).normalize()
// console.log('crea ball', coll, collision)
// if (window.onCollide)
// window.onCollide(collision, ball.id)
// // if (ball.id.endsWith('_red'))
// console.log('onCollide mesh:', ball.id)
// }
// })
// })()
const base64ToImg = (base64) => new Promise((res, _) => {
const img = new Image()
img.src = base64
img.onload = () => res(img)
})
const TRANSITIONS_BUFFER_SIZE = 2
const frameEvery = 1000/30 // ~33ms ~24frames/sec
const frameStack = []
// const transitions = []
// let start = Date.now() + frameEvery
let timer = Date.now()
let busy = false
let stateId = 0
let prevLinearVelocity = BABYLON.Vector3.Zero()
window.collision = BABYLON.Vector3.Zero()
window.reward = 0
window.globalReward = 0
// let collisionMesh = null
const testLayer = agent.actor.layers[4]
const spy = tf.model({inputs: agent.actor.inputs, outputs: testLayer.output})
scene.registerAfterRender(async () => { // timer ~ 20-90ms
if (/*Date.now() < start || */busy || !inited) return
// const delta = (Date.now() - timestamp) / 1000 // sec
// timestamp = Date.now()
// start = Date.now() + frameEvery
busy = true
// const timerLbl = 'TimerLabel-' + start
/*
console.time(timerLbl)
console.timeEnd(timerLbl)
console.log('numTensors BEFORE: ' + tf.memory().numTensors)
console.log('numTensors AFTER: ' + tf.memory().numTensors)
*/
// const screenShots = []
// screenShots.push(
// BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraLeft, { // ~ 7-60ms
// height: agent._frameShape[0],
// width: agent._frameShape[1]
// })
// )
// screenShots.push(
// BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraRight, { // ~ 7-60ms
// height: agent._frameShape[0],
// width: agent._frameShape[1]
// })
// )
// const base64Data = await Promise.all(screenShots)
// frameStack.push(base64Data)
//delay
if (!frameStack.length) {
frameStack.push([
await BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraLeft, { // ~ 7-60ms
height: agent._frameShape[0],
width: agent._frameShape[1]
})
])
} else {
frameStack[0].push(
await BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraRight, { // ~ 7-60ms
height: agent._frameShape[0],
width: agent._frameShape[1]
})
)
}
if (frameStack.length >= agent._nFrames && frameStack[0].length == 2) { // ~20ms
if (frameStack.length > agent._nFrames)
throw new Error("(⊙_⊙')")
const imgs = await Promise.all(frameStack.flat().map(fr => base64ToImg(fr)))
const framesNorm = tf.tidy(() => {
const greyScaler = tf.tensor([0.299, 0.587, 0.114], [1, 1, 3])
let imgTensors = imgs.map(img => tf.browser.fromPixels(img)
//.mul(greyScaler).sum(-1, true)
)
// optic chiasma
// imgTensors = imgTensors.map(img => tf.split(img, 2, 1))
// for (let i = 0; i < imgTensors.length; i = i + 2) {
// const first = tf.concat([imgTensors[i][0], imgTensors[i+1][0]], -1)
// const second = tf.concat([imgTensors[i][1], imgTensors[i+1][1]], -1)
// imgTensors[i] = first
// imgTensors[i+1] = second
// }
// imgTensors = [
// imgTensors[0].concat(imgTensors[1], 1),
// //imgTensors[2].concat(imgTensors[3], 1)
// ]
// if (collisionMesh) {
imgTensors = imgTensors.map((t, i) => {
const canv = document.getElementById('testCanvas' + (i+3))
if (canv) {
tf.browser.toPixels(t, canv) // timer ~1ms
}
return t
.sub(255/2)
.div(255/2)
})
// }
const resL = tf.concat(imgTensors.filter((el, i) => i%2==0), -1)
const resR = tf.concat(imgTensors.filter((el, i) => i%2==1), -1)
return [resL, resR]
// return [tf.concat(imgTensors, -1)]
// let frTest = tf.unstack(res, -1)
// // frTest = [tf.concat(frTest.slice(0,3), -1), tf.concat(frTest.slice(3), -1)]
// console.log(frTest[0].arraySync()[30][0][0], frTest[3].arraySync()[30][0][0])
// console.log(tf.concat(tf.unstack(tf.concat(imgTensors, 2), -1), -1).arraySync()[30][0][0])
})
const framesBatch = framesNorm.map(fr => tf.stack([fr]))
const delta = (Date.now() - timer) / 1000 // sec
console.log('delta (s)', delta)
const linearVelocity = creature.impostor.getLinearVelocity()
const linearVelocityNorm = linearVelocity.normalize()
const acceleration = linearVelocity.subtract(prevLinearVelocity).scale(1/delta).normalize()
timer = Date.now()
prevLinearVelocity = linearVelocity
const ray = new BABYLON.Ray(creature.position, linearVelocityNorm)
const hit = scene.pickWithRay(ray)
let lidar = 0
if (hit.pickedMesh) {
lidar = Math.tanh((hit.distance - creature.impostor.getRadius())/10) // stretch tanh by 10 for precision
// console.log('Hit: ', hit.pickedMesh.name, hit.distance, lidar, linearVelocity, collision)
}
const telemetry = [
linearVelocityNorm.x,
linearVelocityNorm.y,
linearVelocityNorm.z,
acceleration.x,
acceleration.y,
acceleration.z,
window.collision.x,
window.collision.y,
window.collision.z,
lidar
]
const reward = window.reward
//collisionMesh &&
// if (collisionMesh && transitions.length) {
// tf.tidy(() => {
// let frTest = tf.unstack(tf.tensor(transitions[transitions.length - 1].state[1], [64,128, agent._nFrames]), -1)
// // frTest = [tf.stack(frTest.slice(0,3), -1), tf.stack(frTest.slice(3), -1)]
// let i = 0
// for (const fr of frTest) {
// i++
// tf.browser.toPixels(fr, document.getElementById('testCanvas' + i)) // timer ~1ms
// }
// })
// }
window.collision = BABYLON.Vector3.Zero() // reset collision point
window.reward = -0.01
window.onCollide = undefined
const telemetryBatch = tf.tensor(telemetry, [1, agent._nTelemetry])
const action = agent.sampleAction([telemetryBatch, ...framesBatch]) // timer ~5ms
// TODO: !!!!!await find the way to avoid framesNorm.array()
console.time('await')
const [framesArrL, framesArrR,[actionArr]] = await Promise.all([...(framesNorm.map(fr => fr.array())), action.array()]) // action come as a batch of size 1
console.timeEnd('await')
// DEBUG Conv encoder
tf.tidy(() => { // timer ~2.5ms
const testOutput = spy.predict([telemetryBatch, ...framesBatch], {batchSize: 1})
console.log('spy', testLayer.name, testOutput.arraySync())
return
let tiles = tf.clipByValue(tf.squeeze(testOutput), 0, 1)
tiles = tf.transpose(tiles, [2,0,1])
tiles = tf.unstack(tiles)
let res = [], line = []
for (const [i, tile] of tiles.entries()) {
line.push(tile)
if ((i+1) % 8 == 0 && i) {
res.push(tf.concat(line, 1))
line = []
}
}
const testFr = tf.concat(res)
tf.browser.toPixels(testFr, document.getElementById('testCanvas2')) // timer ~1ms
})
const
impulse = actionArr.slice(0, 3)//.map(el => el/10)//, // [0,-1, 0], //
// rotation = actionArr.slice(3, 7).map(el => el),
// color = actionArr.slice(3, 6).map(el => el)/.map(el => el) // [-1,1] => [0,2] => [0, 255]
// look = actionArr.slice(3, 6)
// console.log('tel tel: ', telemetry.map(t=> t.toFixed(3)))
// console.log('tel imp:', impulse.map(t=> t.toFixed(3)))
console.assert(actionArr.length === 3, actionArr.length)
console.assert(impulse.length === 3)
// console.assert(look.length === 3)
// console.assert(rotation.length === 4)
// console.assert(color.length === 3)
// [0,-1,0]
creature.impostor.setAngularVelocity(BABYLON.Quaternion.Zero()) // just in case, probably redundant
// creature.impostor.setLinearVelocity(BABYLON.Vector3.Zero()) // contact point zero
creature.impostor.applyImpulse(new BABYLON.Vector3(...impulse), creature.getAbsolutePosition()) // contact point zero
creature.impostor.setAngularVelocity(BABYLON.Quaternion.Zero())
// creature.glow.color2 = new BABYLON.Color4(...color)
// after applyImpulse the linear velocity is recalculated right away
const newLinearVelocity = creature.impostor.getLinearVelocity().normalize()
// creature.lookAt(new BABYLON.Vector3(0, -1, 0), 0, 0, 0, BABYLON.Space.LOCAL)
creature.lookAt(creature.position.add(newLinearVelocity))
//if (!window.rr) window.rr =
// creature.lookAt(creature.position.add(new BABYLON.Vector3(0,1,0)))
const transtion = {
id: stateId++,
state: [telemetry, framesArrL, framesArrR], // 20ms vs 50ms || size 200kb vs 1.5mb
action: actionArr,
reward
}
transitions.push(transtion)
window.onCollide = (collision, mesh) => {
window.collision = collision
window.reward += -0.05
if (mesh.startsWith('ball_')) {
console.log('reward', mesh)
window.reward = 1
if (mesh.includes('red'))
window.reward = -1
}
window.onCollide = undefined
}
if (transitions.length >= TRANSITIONS_BUFFER_SIZE) {
if (transitions.length > TRANSITIONS_BUFFER_SIZE || TRANSITIONS_BUFFER_SIZE < 2)
throw new Error("(⊙_⊙')")
const transition = transitions.shift()
// if (transition.reward > 0) {
// transition.priority = 7
// console.log('reward prio:', transition, transition.state[0])
// }
window.globalReward += transition.reward
console.log('reward', transition.reward, window.globalReward)
worker.postMessage({action: 'newTransition', transition}) // timer ~ 6ms
}
// imgTensors.forEach(t => t.dispose())
// frames.dispose()
framesNorm.map(fr => fr.dispose())
framesBatch.map(fr => fr.dispose())
telemetryBatch.dispose()
action.dispose()
// if (stateId%1 == 0)
// frameStack.forEach((base64Data, i) => {
// const img = new Image()
// img.onload = () => document.getElementById('testCanvas' + (i+2))
// .getContext('2d')
// .drawImage(img, 0, 0, 256, 128)
// img.src = base64Data
// })
frameStack.length = 0 // I will regret about this :D
}
//mirror.material.diffuseTexture = new BABYLON.Texture(base64Data, scene) // timer ~1ms
// const img = await base64ToImg(base64Data) // timer ~2-12ms
// const tensor = tf.browser.fromPixels(img) // timer ~ 1ms
// const arr = await tensor.array() // timer ~ 6-15ms
// worker.postMessage(arr) // timer ~ 6ms
// tensor.dispose()
busy = false
})
return scene
};
window.initFunction = async function() {
await Ammo();
const asyncEngineCreation = async function() {
try {
return createDefaultEngine();
} catch(e) {
console.log("the available createEngine function failed. Creating the default engine instead");
return createDefaultEngine();
}
}
window.engine = await asyncEngineCreation();
if (!engine) throw 'engine should not be null.';
window.scene = await createScene();
};
initFunction().then(() => {
sceneToRender = scene;
engine.runRenderLoop(function () {
if (sceneToRender && sceneToRender.activeCamera) {
sceneToRender.render();
}
});
});
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>