| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|
|
| <title>The AI Creature</title> |
|
|
| |
| <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> |
|
|
| |
| <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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| .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 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,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAwQSURBVGhD7VpbiCVHGa4+p/tc5syZnd2d3Z2Q4AUvyUPAEIwY1uTB+CA+CAoSULyAshqMgnhBEREkD4qCD4q6PnjBEFBBFkREfDL4kCho8MUbG93LZCdz2Zk5Z861z+n2+/6q6tPdp89lMqeHiH7MP1Vd3VX1f/VX1f9X91H/a3BM+rLH7lOiqxeTgpE2pHP2vSpEOhMLJwzFqETRCBWrQlYgpyCnIWdieZbXITUIn6tASkZcCNsYGFmDsB6f433eY18tyO8gT4D0X5FOxbEJg2AZyZ2Qe4zcDXmVKaOCJEQlPXTmsEfp1KYGw8BkDHjPwb9CsaCcoqsKrqccr6ycAq4htoHA95XfOlRDv/8iCj8G0ldYfxLifc4NkOToPwB5N+RNkNdBVqFgpQBK1EeEClMvK3jIprbnwVCpbl+nrOPBbi4EPHENcuWqckqYAGWIy7EdRzgcquYLN9Sg172KywdAek/fGYfpdjbMGqLF3gZ5DPIw9HFdKEYFXdeBkmAbtYiMzcvqii8x5PHX80PV7oIc6pcx+UsYxlF9oIAblmxpWV9PwKDbUY2N62g3/AAI/8QUjyHe/BhAkgTfCHkNhFZ8EPJmWKFYgoIeSBZowoicaS6eT0AT5b8gCFWzFYhVazAc0zEUMAIkWwZZEneyHtIIg0A1bl5Tw37vOyD8cVM8hswWQHQF8n5k/wK1fwX5LvKfgVz0XFVcXiqocqkg60uUoLZMozwsYa8TgnJzPwgLsm4rGLhMshGyBi4LGEn96NTWxm6CKMs+D/kBptgrlpdUeaWGpWj69TCH9SIlKRIw5Jjasvh1XGKkHcxjB4363H8ngtOBs0KmxVQMsXlxAwOelYIJGBs+EH4XkierJbVUweaviTpqv8VpCD9Sd/XCZVW5aVPCpNF1GlTeZJHptH3V6fiydsvoi3uBrcm+AlwFhTKkpEJM75B3QT6EcLDYD1MawG+3KDdR9SKmNBZzNhKagSx33yfR8aN1OBLRW6ziqAbWG5U4tVJCEW+wnLV0x6PUgtcmSwhRy9YSD1Wvq0kHmN+yq6NZLG+sSXlw1IwhRn2YaqNr8jIIUA5Cn/wVyNdBWp5II64SCZ9H8jQse/cSPYCQ1YQP2/AbeHx5mWbXZbwepUhscwniKYimVFIuQHSo2q2e6vcGUo3bwsgtQWzTLga6YjYvj27dQpOma+ru76leY5+KfgJyGaTtsEVAkwkwgqmxI725jIQ+0aWTjNYixD4jqSmP358obAebFlRr7HfUoD9QWELq1BKiFHDhYNNNsbuIONc82naKCEBQMJIi7mEnLZVV7fy6Kp9aRS31VcjrIWNAU2PAgBqlqBx7Q1rEsLvYouNliTxTS5ZplghRCqYkumk1GQYHqg6iVeOasicHzUy1dN1pqJ5ZQzdFhqzv0yVJZNdm49IBNdAESLYgu8qoTA+KFVMeE9lkJG/u2/ZwTesOEV6RKK04FWjG/Iul2aC13TLXo3qLFKSQ3ZUQtsppKZIwGtNKpwjYMrEA7xVVH/FiY/u2OtjaVYd7+6rbwtT1wdI+j8XJZYzcEZG5FyXAaQ7w4DKGCf2NE45IUiJLxfKpstJSTa1cuKBqZ3g4clTnoKGa2zuqsbOreu2OHAaKrqs6iKO5K0+F2eBkw5NNbzLCYIhoC40q9XcpSAHaZSDDwqMyQ1wIamsmB2N0n6cct1JVy+fOqfr6OjaWEgL8vmrd3leNrR1MvRJWcBEuDwda6MjIK5sPCukT6asifxWH3qmDga9aW5sML2+j8Cl9L4nEgoBbugvJM0u18p0VbpWWhCEgj8t1LJWyeEowb7IxBFiz7d0d1UeQIMDzHnZXB4vY73bFl7KbIuoyFZdkROJqnJZCnpgYhNDvYiMIhgNxbZJHpBXqAfkh5HG4Je6KCZDFOKQXEppDxNKxlNaO8kkpeB5cxwXozXM+AKv4PRBFKhETiiSmgfC42EOkSMu3cKJqtQeqhanQ2dtTnds7qnewL9FVMBhgUmFTrVZxoKqLewJ4DrjETBpsPkJk4XoVFmaoBUUTyscGQqxoJXYtMKm9JKKpyqk3kJMNLUPwMYyF+OL4ji1VTD1JYF1HTk91VKjIIKXBwevsbKnuwR6j9Edg5af1HY1Y83GgoTi5LJE5l1q/kZj6QsWILcN9Wrq8ssqOBIzZeURMuyepaarpsFMHGvbgkQWWV9fOw6swQleXYMREq6kupA/Ti1Wewut0WVqyFciGg4joNMbLlWo8OMyuntJhCkjaw/QGLkJGIwuka8Jc2DPiJKJ8liUpMzXNhF13rD1XC0fspgCXB3BBC3MLaJwBS1TWr8knyBs5DtAeNxi6oZl+mIieQWaO5wNffDHfbR0yYzFB6wxylGgDO+JwTwCDD+re7OhdeTpI1Mj4ISgBvt/qHwpPblj0yRGgfQbEmiSVIs7HF0SWsBsPYwqeI+h++AZkosVt4MEKcWAQ6IeHsGq/1YSv32IQ8i/c+Wb6XJzQHjvaK5E8Uzt9er1cx9SPrGrX7+LIEr3GASKjW+ZqBHajd2Wd8lp6pg44F4cFzgzE4iAeBR10cWE4hFvCkIV8IX8JZP/IanGghQQYArWksvRCMUQpCwb9sQX2MDk58TxMf8yDGcFw0x8WVD/AgYTSDxCs9CVeJmH9FoRcEXmFwQFyj6LaQ1lkiQQLWJjXV4qe986V9TuUgzUWrdvko8cHpuHhi7ew1hqqhsCLR22JslII2beHAzPfdJSQMsS0g4+UOQYb/daham9vchB+gaL3gHBq3muQSQQz3z839P295vaWGnAUQza5YLIAZ9EAYSWDDr7dyCJL6EAD/hpuhgaQoANGEDEzj/lyfUUtrV1glbdD3sBMFhKECZDmseqDg273RvPWhmpuXEf8uiOvQRcJbjA83dipOxkkxVnGB6cPvFerkTymgQQcmRgjTID0L5Hch+nxGKxwpbO7s39w/XnVvHVTPlwtAtxoOK3TH9HGEN87pvPFo9rywDkpyEAmYQKkb0O+hyw/mN0L5T4Fsn9obm4MGzf/LWuPm8ZLhVupqGK5rLD/zPDBk3zUOHj4Nzpdk4IMzBizJLCpYedQ74Dwy8R9XnWpUDl9FnsKi48O7rSHmxuIinryyYUx9dhaLvIGX89SMFvjFk8glLN2d2+XU5C79HO6PIkjEbYAcS4oEv8ymrgfG4ZTPXvOxq9HAjcvzhgc3n0o43JPktezEPHFzICoHAu5U3OXjqxu3knDqj4iK5yTA7imr+HGF0E4c2q8JMIWxuIfhXwJZFdr5+84urWh8AG/+vW6P8PVnyH8HHsvZA3KgT6UlB3ZrE+9RlkN/+h7kUFoFgwH8MHqp5BPg+zEjeZYhC1AnApehjy4tHbeqazyxd28AOEbQvjHUPRDJhbge2W+jHi1SflFhD+R4A7MExDpIhCVQIkHBArj5qtoA5H5ZCyEMGGs/QRM8Xh19YzLKT4fEIDA/SFw+D2UfcgU5oaFEbYA8e8j+Qimt1Ne4ff02eA7Ksg+sneBtHnDlw8muqVj4JOQH/HEMuz3dMkM8FUuZgbfTEyMkBaFhROGhbi2voDA4vn2ztZcvpr+WHZjpR6WghyRh4VJmj8h+qzfboWDztir4THI1z9PXq8+giVxdN92BORC2OC3kGc7e7vGh0yHt8QNWHZ7nslzQ26EYeUmksuDbifgQWEWvBoiKe1+7mcmL+RpYYJW3uRZdRaKXonvuKjPW40vzgV5E96EXEVQoa+mATEyDxQAfxeW2zrOlTCmNc9Bf+OUlhBwBsw3J/5Os85MHsjbwsS1EIfeedxTQX8IY7SyzkweOAnC2wzyRWZAfLHj4DwoPzHOBSdB+JCzOZzr80KE/841bDD1fUYc/LiNxU4ftq1LFo+TIDwfMA16jQZz/4Dwd8+54GVDmJ9I/FaT1v22icdzwUkQxoGci3jyGu41G/I+CvgN5OfM5IWTIPwCfTBfuieAMv4IhWfh9vZmEPj9X6P0w7Bu4mvfopFbCGeBMJG/BP2TUyjc41Vrqohoil8MeFb2cZICab6S4UuDb4Asf/6bK3InTIA0D/f8VQ1/i/1aCPvlTsz3UN+C/BNk53tbcEycCGELc9bluy8upUOQXOz3m/8jDaX+Aw4H+bd097oGAAAAAElFTkSuQmCC" alt=""> |
| </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", () => { |
| |
| |
| window.reward = 1 |
| |
| |
| |
| }) |
| |
| document.getElementById("dislike").addEventListener("click", () => { |
| |
| |
| window.reward = -1 |
| |
| |
| |
| }) |
| |
| window.transitions = [] |
| window.globalReward = 0 |
| const BINOCULAR = true |
| |
| const createScene = async () => { |
| await agent.init() |
| |
| |
| |
| |
| const scene = new BABYLON.Scene(engine); |
| scene.collisionsEnabled = true |
| |
| |
| 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; |
| |
| |
| 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; |
| |
| |
| scene.enablePhysics(new BABYLON.Vector3(0, 0, 0), new BABYLON.AmmoJSPlugin(false)); |
| |
| const physicsEngine = scene.getPhysicsEngine() |
| |
| physicsEngine.setTimeStep(1 / 60) |
| physicsEngine.setSubTimeStep(1) |
| |
| |
| 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 |
| |
| |
| |
| const creature = BABYLON.MeshBuilder.CreateSphere("creature", {diameter: 1, segments: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 |
| 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"; |
| |
| creature.sparks = await BABYLON.ParticleHelper.CreateFromSnippetAsync("UY098C-3.json", scene, false); |
| creature.sparks.emitter = creature; |
| |
| creature.glow = await BABYLON.ParticleHelper.CreateFromSnippetAsync("EXUQ7M-5.json", scene, false); |
| creature.glow.emitter = creature; |
| |
| |
| 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) |
| 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) |
| crCameraRight.fov = 2 |
| crCameraRight.setTarget(new BABYLON.Vector3(1, 0, 0.6)) |
| |
| |
| |
| const crCameraLeftPl = BABYLON.MeshBuilder.CreateSphere("crCameraLeftPl", {diameter: 0.1, segments: 32}, scene); |
| crCameraLeftPl.parent = creature |
| crCameraLeftPl.position = new BABYLON.Vector3(-0.5, 0, 0) |
| const crCameraLeftPlclMat = new BABYLON.StandardMaterial("crCameraLeftPlclMat", scene) |
| crCameraLeftPlclMat.alpha = 0.3 |
| crCameraLeftPlclMat.diffuseColor = new BABYLON.Color3(0, 0, 0) |
| crCameraLeftPl.material = crCameraLeftPlclMat |
| |
| const crCameraRightPl = BABYLON.MeshBuilder.CreateSphere("crCameraRightPl", {diameter: 0.1, segments: 32}, scene); |
| crCameraRightPl.parent = creature |
| crCameraRightPl.position = new BABYLON.Vector3(0.5, 0, 0) |
| const crCameraRightPlclMat = new BABYLON.StandardMaterial("crCameraRightPlclMat", scene) |
| crCameraRightPlclMat.alpha = 0.3 |
| crCameraRightPlclMat.diffuseColor = new BABYLON.Color3(0, 0, 0) |
| crCameraRightPl.material = crCameraRightPlclMat |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const client = BABYLON.MeshBuilder.CreateSphere("client", {diameter: 3, segments: 32}, scene); |
| client.parent = camera |
| client.setParent(camera) |
| |
| |
| const clMat = new BABYLON.StandardMaterial("cl_mat", scene) |
| clMat.diffuseColor = new BABYLON.Color3(0, 0, 0) |
| client.material = clMat |
| |
| engine.hideLoadingUI(); |
| |
| |
| const cage = BABYLON.MeshBuilder.CreateSphere("cage", { |
| segements: 64, |
| diameter: 50 |
| }, 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 |
| cage.material = cageMat |
| cage.material.backFaceCulling = false |
| |
| cage.impostor = new BABYLON.PhysicsImpostor(cage, BABYLON.PhysicsImpostor.MeshImpostor, { |
| mass: 0, |
| friction: 1 |
| }, scene); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const ballPos = [[-10,-10,10], [10,-10,-10], [-10,-10,-10], [10,-10,10]] |
| |
| const balls = ['green', 'green', 'red', 'red'].map((color, i) => { |
| const ball = BABYLON.MeshBuilder.CreateSphere("ball_"+ color + i, {diameter: 7, segments: 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 |
| }) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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))) |
| if (Math.random() > 0.99) console.log('weights:', weights) |
| } |
| |
| }) |
| }) |
| |
| |
| 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) |
| } |
| } |
| }) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 |
| const frameStack = [] |
| |
| |
| |
| 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 |
| |
| |
| const testLayer = agent.actor.layers[4] |
| const spy = tf.model({inputs: agent.actor.inputs, outputs: testLayer.output}) |
| |
| scene.registerAfterRender(async () => { |
| if (busy || !inited) return |
| |
| |
| |
| |
| busy = true |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (!frameStack.length) { |
| frameStack.push([ |
| await BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraLeft, { |
| height: agent._frameShape[0], |
| width: agent._frameShape[1] |
| }) |
| ]) |
| } else { |
| frameStack[0].push( |
| await BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(engine, crCameraRight, { |
| height: agent._frameShape[0], |
| width: agent._frameShape[1] |
| }) |
| ) |
| } |
| |
| |
| |
| |
| if (frameStack.length >= agent._nFrames && frameStack[0].length == 2) { |
| 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) |
| |
| ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| imgTensors = imgTensors.map((t, i) => { |
| const canv = document.getElementById('testCanvas' + (i+3)) |
| if (canv) { |
| tf.browser.toPixels(t, canv) |
| } |
| 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] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| }) |
| const framesBatch = framesNorm.map(fr => tf.stack([fr])) |
| |
| const delta = (Date.now() - timer) / 1000 |
| 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) |
| |
| } |
| |
| 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 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| window.collision = BABYLON.Vector3.Zero() |
| window.reward = -0.01 |
| window.onCollide = undefined |
| const telemetryBatch = tf.tensor(telemetry, [1, agent._nTelemetry]) |
| const action = agent.sampleAction([telemetryBatch, ...framesBatch]) |
| |
| |
| |
| console.time('await') |
| const [framesArrL, framesArrR,[actionArr]] = await Promise.all([...(framesNorm.map(fr => fr.array())), action.array()]) |
| console.timeEnd('await') |
| |
| tf.tidy(() => { |
| 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')) |
| }) |
| |
| const |
| impulse = actionArr.slice(0, 3) |
| |
| |
| |
| |
| |
| |
| |
| console.assert(actionArr.length === 3, actionArr.length) |
| console.assert(impulse.length === 3) |
| |
| |
| |
| |
| |
| creature.impostor.setAngularVelocity(BABYLON.Quaternion.Zero()) |
| |
| creature.impostor.applyImpulse(new BABYLON.Vector3(...impulse), creature.getAbsolutePosition()) |
| creature.impostor.setAngularVelocity(BABYLON.Quaternion.Zero()) |
| |
| |
| |
| const newLinearVelocity = creature.impostor.getLinearVelocity().normalize() |
| |
| creature.lookAt(creature.position.add(newLinearVelocity)) |
| |
| |
| |
| const transtion = { |
| id: stateId++, |
| state: [telemetry, framesArrL, framesArrR], |
| 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() |
| |
| |
| |
| |
| |
| window.globalReward += transition.reward |
| console.log('reward', transition.reward, window.globalReward) |
| |
| |
| worker.postMessage({action: 'newTransition', transition}) |
| |
| } |
| |
| |
| |
| framesNorm.map(fr => fr.dispose()) |
| framesBatch.map(fr => fr.dispose()) |
| telemetryBatch.dispose() |
| action.dispose() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| frameStack.length = 0 |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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> |
|
|