Raggar

Members
  • Content count

    308
  • Joined

  • Last visited

1 Follower

About Raggar

  • Rank
    Advanced Member

Recent Profile Visitors

2,252 profile views
  1. If you simply null it out, it will automatically use the .rotation property instead.
  2. .babylon Model Library

    I've never had an issue with importing models from clara.io. I believe they use the blender exporter. Although, when you import a model from clara.io using the .babylon extension, the actual mesh is at index 1, as either the exporter or their own software creates an empty mesh at index 0, and makes this the parent of the actual geometry mesh. This shouldn't be the issue, though, as scaling, rotating positioning etc. still effects the child.
  3. @BitOfGold That's cool. CannonJS starts acting weird after a few hundred dynamic objects, unfortunately. I was planning on making a game with thousands of dynamic bodies at some point. And I do mean thousands at the same time, not spread out too much. Rendering will have to be faked with instances and sprites, but I don't think the web is ready for physics like that On the client, I'm running my physics simulation in a worker as well to squeeze out some extra juice. I have a hard time wrapping my head around typed arrays, so for the time being, I'm just using posting JSON objects instead of transferables. All my static objects are merged into one compound body, as this gives some extra performance in CannonJS. For the time being, it's too late to switch physics engine. Maybe in the future. We'll see how this tech evolves. I went back to websockets, as for some reason they seem to perform better than WebRTC using simple-peer on the client and either electron-webrtc or node-webrtc on the server. But the way I set up my comms, I can easily switch after I've done some testing and benchmarking.
  4. For the fun of it, I tried recreating a multiplayer version. This is mostly copy/paste from various other projects of mine, so it's messsaah. It is using native CannonJS, and you can't really push other players as their angularvelocity is zeroed when keys aren't pressed. Easy to fix, though. It is FAR from perfect, but it has the basics. Authoritative server, input prediction, server reconciliation, entity interpolation and a fixed timestep input loop in case of lags. Pretty much anything could be optimized. Loops, meshes(instances) etc. But in case you need some ideas for how you Could handle networking, you can take a look at it. I have a 130ms ping or round trip latency, and it seems to run decently, although collisions do seem a bit off at times. Networking is hard Btw. If you press "Run" in the playground, the socket connection isn't dropped, so you'll have multiple balls to control, with only the most recent being applied reconciliation and prediction, so it should represent how you see other players. http://playground.babylonjs.com/#RM9TBC#1 It does seem to have some crashes that don't happen in the non-pg version, but you'll get the idea. var CANNON = require('cannon'), express = require('express'), WebSocket = require('ws'), gameloop = require('node-gameloop'), http = require('http'); var server = http.createServer(function(request, response) { }); server.listen(7777, function() { process.on('warning', e => console.warn(e.stack)); //console.log(id); }); var players = [], boxBodies = []; var world; var playerIDs = 1, bodyIDs = 1; var bodiesToRemoveFromWorld = []; var wss = new WebSocket.Server({ port: 8088 }); wss.on('connection', function connection(ws, req) { ws.isAlive = true; ws.on('pong', function(){ ws.isAlive = true}); ws.id = playerIDs++; ws.on('message', function incoming(message) { translate(message, ws); }); ws.on('error', function close(e) { console.log('error', e); }); ws.on('close', function close() { console.log('disconnected'); if(!ws.id){ console.log("no ID"); return; } var player = findPlayerByID(ws.id); if(player){ console.log("Player found"); broadcastToAllSockets({m:17, p:ws.id}); removePlayer(player); } }); }); const interval = setInterval(() => { wss.clients.forEach((ws) => { if (ws.isAlive === false) { var player = findPlayerByID(ws.id); if(player){ console.log("Player found"); broadcastToAllSockets({m:17, p:ws.id}); removePlayer(player); } return ws.terminate() }; ws.isAlive = false; //ws.pingStart = Date.now(); ws.ping('', false, true); }); }, 2000); function removePlayer(player){ if(!player){ console.log("Not found"); return; } bodiesToRemoveFromWorld.push(player.body); var index = players.indexOf(player); if(index > -1){ players.splice(index, 1); } } function translate(message, socket){ var decoded = JSON.parse(message); if(decoded.m === 0){ requestGameState(socket); //var player = new Player(); //player.id = socket.id = playerIDs++; //emitIfOpen(socket, {m:1, p:player.id}); } else if(decoded.m === 2){ console.log("Requesting players"); // var player = new Player(); //player.id = socket.id; //emitIfOpen(socket, {m:1, p:player.id}); //broadcastToAllSockets({m:1, p:player.id}); } else if(decoded.m === 4){ var player = new Player(); player.id = socket.id; player.socket = socket; player.hasSpawned = true; broadcastToAllSockets({m:1, p:player.id}); emitIfOpen(socket, {m:1, p:player.id}); players.push(player); } else if(decoded.m === 10){ //console.log("m"); applyInput(socket.id, decoded.p); } } function applyInput(id, input){ var player = findPlayerByID(id); if(!player){ console.log("nope"); return; } if(input.w){ player.body.angularVelocity.x = 10; //player.body.position.x += 0.1; } else if(input.s) { // player.body.position.x -= 0.1; player.body.angularVelocity.x = -10; } else { player.body.angularVelocity.x = 0; } if(input.a){ player.body.angularVelocity.z = 10; // player.body.position.z -= 0.1; } else if(input.d) { player.body.angularVelocity.z = -10; // player.body.position.z += 0.1; } else { player.body.angularVelocity.z = 0; } player.lastProcessedInput = input.seq; } function requestGameState(socket){ var states = []; for(var i=0;i<players.length;i++){ var player = players[i]; if(player){ states.push([player.id, player.body.position.x, player.body.position.y, player.body.position.z]); } } emitIfOpen(socket, {m:4, p:states, i:socket.id}); } function emitIfOpen(socket, packet){ if (socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify(packet)); } } var Player = function(){ this.mass = 5; this.radius = 0.5; this.pings = []; this.ping = 0; this.lastProcessedInput = 0; this.alive = false; this.hasSpawned = false; this.shape = new CANNON.Sphere(this.radius); this.body = new CANNON.Body({mass: 1}); this.body.fixedRotation = true; this.body.updateMassProperties(); this.body.addShape(this.shape, new CANNON.Vec3(0,0,0)); world.add(this.body); return this; } function findPlayerByID(id){ for (var i=0;i<players.length;i++){ if(players[i].id === id){ // console.log("Player Found"); return players[i]; } } } var createWorld = function(){ world = new CANNON.World(); world.quatNormalizeSkip = 0; world.quatNormalizeFast = false; world.defaultContactMaterial.contactEquationStiffness = 1e128; world.defaultContactMaterial.contactEquationRelaxation = 4; // world.gravity.set(0, -9.82, 0); world.solver.iterations = 20; world.solver.tolerance = 0.0; world.gravity.set(0,-30,0); world.broadphase = new CANNON.NaiveBroadphase(); var groundShape = new CANNON.Plane(); var groundBody = new CANNON.Body({ mass: 0, shape: groundShape}); //, collisionFilterGroup: 2, collisionFilterMask: 1 groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); //world.add(groundBody); var boxShape01 = new CANNON.Box(new CANNON.Vec3(10,0.2,3)); var boxShape02 = new CANNON.Box(new CANNON.Vec3(10,10,0.2)); // x: -0.08715574274765817, y: -0, z: -0, w: 0.9961946980917455 var boxBody = new CANNON.Body({ mass: 0}); boxBody.addShape(boxShape01, new CANNON.Vec3(0,-1,0), new CANNON.Quaternion(-0.08715574274765817,0,0,0.9961946980917455)); boxBody.addShape(boxShape02, new CANNON.Vec3(0,9,7), new CANNON.Quaternion(0.19509032201612825,0,0,0.9807852804032304)); world.add(boxBody); for(var i=0;i<3;i++){ var body = {cType:0, id:bodyIDs++}; var boxShape = new CANNON.Box(new CANNON.Vec3(2*0.5,1*0.5,2*0.5)); var boxBody = new CANNON.Body({ mass: 50}); boxBody.addShape(boxShape); body.body = boxBody; world.add(boxBody); boxBodies.push(body); } for(var i=0;i<3;i++){ var body = {cType:1, id:bodyIDs++}; var sphereShape = new CANNON.Sphere(1); var sphereBody = new CANNON.Body({ mass: 50}); sphereBody.addShape(sphereShape); body.body = sphereBody; world.add(sphereBody); boxBodies.push(body); } return world; } var mainLoop = gameloop.setGameLoop(function(delta) { if(world){ for(var i=0,length=bodiesToRemoveFromWorld.length;i<length;i++){ if(bodiesToRemoveFromWorld[i]){ world.remove(bodiesToRemoveFromWorld[i]); } bodiesToRemoveFromWorld.splice(i,1); } for(var i=0;i<boxBodies.length;i++){ var body = boxBodies[i]; if(body){ body.body.velocity.z = -3; } } //console.log("stp"); world.step(1.0/60); } }, 1000 / 60); var networkLoop2 = gameloop.setGameLoop(function(delta) { sendStates(); for(var i=0;i<boxBodies.length;i++){ var body = boxBodies[i]; if(body){ if(body.body.position.y < -10){ body.body.velocity.set(0,0,0); body.body.angularVelocity.set(Math.random()*5,Math.random()*5,Math.random()*5); body.body.position.set(Math.random()*20-10,50,15); } } } for(var i=0;i<players.length;i++){ var player = players[i]; if(player){ if(player.body.position.y < -10){ player.body.velocity.set(0,0,0); player.body.position.set(Math.random()*20-10,20,0); } } } }, 1000 / 10); function sendStates(){ var states = []; var bodies = []; for(var i=0;i<players.length;i++){ var player = players[i]; if(player && player.hasSpawned){ states.push([player.id,parseFloat(player.body.position.x.toFixed(5)),parseFloat(player.body.position.y.toFixed(5)),parseFloat(player.body.position.z.toFixed(5)), player.lastProcessedInput]); } } for(var i=0;i<boxBodies.length;i++){ var body = boxBodies[i]; if(body){ bodies.push([body.id, body.cType, parseFloat(body.body.position.x.toFixed(5)),parseFloat(body.body.position.y.toFixed(5)),parseFloat(body.body.position.z.toFixed(5)), parseFloat(body.body.quaternion.x.toFixed(5)),parseFloat(body.body.quaternion.y.toFixed(5)),parseFloat(body.body.quaternion.z.toFixed(5)),parseFloat(body.body.quaternion.w.toFixed(5))]); } } broadcastToAllSockets({m:6, p:states, b:bodies}); } function broadcastToAllSockets(packet){ for(var i=0,length=players.length;i<length;i++){ var player = players[i]; if(player){ emitIfOpen(player.socket, packet); } } } world = createWorld();
  5. Character creation

    You could scale bones as well, as done in the following thread: http://playground.babylonjs.com/#94EHM5#1
  6. It seems to be because the impostor is linked to the sphere, without being in the physicsImpostor property. You can do this: https://www.babylonjs-playground.com/indexstable#JS6FR6#2 Or this: https://www.babylonjs-playground.com/indexstable#JS6FR6#3 But yeah. This is a bug.
  7. Babylon.js: A double array to 3d

    Did a little test. Without merging I get about 30-34 FPS. http://www.babylonjs-playground.com/#VQ84FP#1 With merging I'm at 60. http://www.babylonjs-playground.com/#VQ84FP If you use different materials, you'll have to merge the ones sharing the same material: I'm not sure how you would get rid of hidden faces. Maybe use planes instead?
  8. Babylon.js: A double array to 3d

    I'm sure it's because of the draw calls. If I import ~600 box models I hit around ~20 FPS, but if I merge those boxes I'm back at 60 FPS. You can merge the boxes after creation, or use instances, but this depends on the use-case.
  9. Just a quick little demo of something I thought would be fun to create. No smoothing groups or textures and dirty animations. PoC PoC PoC. All separate weapon parts are weighted to bones, so they can be positioned, scaled and rotated individually, to create endless weapons(Only 4 in this example), all from a single mesh. I've chosen to do it in 3Ds Max, although it can be done in code as well. http://playground.babylonjs.com/#ZIH5Y7
  10. This is sooo weird. I've had this issue for some time now, but starting a new 3Ds Max project somehow fixed it ? :/ https://www.babylonjs-playground.com/#070GWA#3
  11. This has been bugging me for some time now. I'm trying to recreate an imported mesh(box) using the MeshBuilder and the boundingInfo of the imported model. The issue happens when I start scaling the model(In 3Ds Max). Look at the following example: https://www.babylonjs-playground.com/#070GWA The meshes line up perfectly using the extendSize and centerWorld. But as soon as I start scaling the models, the following happens: https://www.babylonjs-playground.com/#070GWA#1 I tried a few things without any success: https://www.babylonjs-playground.com/#070GWA#2 Can this be done without any matrix magic?
  12. I was wondering whether or not it's possible to get the world/3D positions of different textures using the TerrainMaterial. Let's use the TerrainMaterial PG as an example: https://www.babylonjs-playground.com/#E6OZX#7 Now, let's say I want to place meshes wherever the rock texture is used on the ground mesh, programmatically and automatically. I don't seem to find any relevant references or functions in the object, and can't think of a proper way without loading the image separately and somehow map those colors to the ground positions.
  13. hoverboard setup

    What a failed attempt: http://playground.babylonjs.com/#G2R4DU Would break your leg in an instance http://playground.babylonjs.com/#G2R4DU#1
  14. skeleton animation for AI

    Well, of course. Don't stay up for too long, Mr. Raggar! I just thought using the actual property would be very useful. Then you could call the function to change position, rotation etc. without having additional check in place in the function itself.
  15. skeleton animation for AI

    http://playground.babylonjs.com/#C3KABR#2 Hmm. Any idea why the following doesn't work? http://playground.babylonjs.com/#C3KABR#3