Netplay, non-sync bodies can't affect sync bodies. 99% done and ask for help
Webm that shows the case: https://webmshare.com/Av4bE
I have p2.world where some bodies synchronize through internet and some not. They exist but I don't care if theirs positions synchronize between users.
The key thing is these non-sync bodies can't affect sync bodies so everything will be in sync.
I've discovered that vlambda & wlambda in bodies are responsible for solving collisions etc.
So this is what I did (a little change in addToWlambda):
p2.Equation.prototype.addToWlambda = function(deltalambda) {
var bi = this.bodyA,
bj = this.bodyB,
temp = addToWlambda_temp,
Gi = addToWlambda_Gi,
Gj = addToWlambda_Gj,
ri = addToWlambda_ri,
rj = addToWlambda_rj,
invMassi = bi.invMassSolve,
invMassj = bj.invMassSolve,
invIi = bi.invInertiaSolve,
invIj = bj.invInertiaSolve,
Mdiag = addToWlambda_Mdiag,
G = this.G;
Gi[0] = G[0];
Gi[1] = G[1];
Gj[0] = G[3];
Gj[1] = G[4];
if ( bi.sync == bj.sync || !bi.sync ) {
// Add to linear velocity
// v_lambda += inv(M) * delta_lamba * G
p2.vec2.scale(temp, Gi, invMassi*deltalambda);
p2.vec2.multiply(temp, temp, bi.massMultiplier);
p2.vec2.add( bi.vlambda, bi.vlambda, temp);
// This impulse is in the offset frame
// Also add contribution to angular
//bi.wlambda -= vec2.crossLength(temp,ri);
bi.wlambda += invIi * G[2] * deltalambda;
}
if ( bi.sync == bj.sync || !bj.sync ) {
p2.vec2.scale(temp, Gj, invMassj*deltalambda);
p2.vec2.multiply(temp, temp, bj.massMultiplier);
p2.vec2.add( bj.vlambda, bj.vlambda, temp);
//bj.wlambda -= vec2.crossLength(temp,rj);
bj.wlambda += invIj * G[5] * deltalambda;
}
}
if ( bi.sync == bj.sync || !bi.sync ) {
if both are sync or non-sync they can interact between themselves
if not, vlambda & wlambda are supplemented with new data only if coresponding body is non-sync.
ie. non-sync bodies takes all reaction/moving if non-sync and sync bodies collide
But this works only almost ideal.
Indeed sync bodies to non-sync bodies behave like static bodies - success! Now we can guess that two simulations will be still in sync - it's not. Quickly both simulations get desync.
I am missing some places where changes are needed? Thanks for help and for great library. I learn a lot studying it!
Can't you just change the collision groups of the sync and non-sync ones so they never interact?
Imagine raindrops falling at moving cube, or more realisitic case, parts of player flesh/body after death flying around. They need to collide but they can't change anything in sync bodies, because non-sync bodies can be in different places in each user.
Besides of these cases. It is really interesting to code sth like that :D
Solver by Equations supplements bodies wlambda & vlambda and lambdas supplements bodies velocities. Equations are cleared up at the begining of each internalStep. (I don't use springs or constraints yet)
I already take care of w/vlambda, so there must be some extra variable/s in p2 that keeps something that supplements bodies velocities
Webm showing the case: https://webmshare.com/Av4bE
Without non-sync bodies added to world, everything stays in sync.
Ah, you want the async bodies to be affected by the sync bodies, but not the other way around. Got it.
Yes exactly. And of course async can be affected by async too. To make it clear
sync <=> sync -- collide normal async <=> async -- collide normal sync <=> async -- sync bodies are like static to async ie. async body can't affect sync body
If you have some shots where I can dig to find it out I would really appreciate for help. Is it technically possible?
I was thinking that maybe different count of equations in Solver in each user is the reason of desync. So made it that way:
p2.GSSolver.prototype.solve = function(h, world, equations) {
if ( equations === undefined ) {
var equationsGhosts = this.equations.filter((c) => {return !c.bodyA.sync || !c.bodyB.sync;});
var equationsNormal = this.equations.filter((c) => {return c.bodyA.sync && c.bodyB.sync;});
this.solve(h, world, equationsNormal);
this.solve(h, world, equationsGhosts);
return;
}
[...]
}
The same result, only bodies behave a little different, but still normal. (I have deterministic setup in world)
Also tried minimum setup of GSSolver.solve function with change from above too. And it looks irractional. Equations that has bodyA & bodyB with sync == true are solved separately, all calculations modify only Equation variables and Body wlambda&vlambda. So everything should be fine, because I take care of wlambda&vlambda as I showed at the begining of this issue.
p2.GSSolver.prototype.solve = function(h, world, equations, ghosts) {
if ( equations === undefined ) {
if(this.equations.length){
for(var i=0; i<world.bodies.length; i++){
world.bodies[i].updateSolveMassProperties();
}
}
var equationsGhosts = this.equations.filter((c) => {return !c.bodyA.sync || !c.bodyB.sync;});
var equationsNormal = this.equations.filter((c) => {return c.bodyA.sync && c.bodyB.sync;});
this.solve(h, world, equationsNormal, false);
this.solve(h, world, equationsGhosts, true);
return;
}
this.usedIterations = 0;
// Things that does not change during iteration can be computed once
if(this.lambda.length < equations.length){
this.lambda = new p2.Utils.ARRAY_TYPE(equations.length + this.arrayStep);
this.Bs = new p2.Utils.ARRAY_TYPE(equations.length + this.arrayStep);
this.invCs = new p2.Utils.ARRAY_TYPE(equations.length + this.arrayStep);
}
setArrayZero(this.lambda);
for(var i=0; i!==equations.length; i++){
var c = equations[i];
if(c.timeStep !== h || c.needsUpdate){
c.timeStep = h;
c.update();
}
this.Bs[i] = c.computeB(c.a,c.b,h);
this.invCs[i] = c.computeInvC(c.epsilon);
}
if(equations.length !== 0){
// Iterate over all equations
for(var iter=0; iter!==this.iterations; iter++){
for(var j=0; j!==equations.length; j++){
var c = equations[j];
p2.GSSolver.iterateEquation(j,c,c.epsilon,this.Bs,this.invCs,this.lambda,this.useZeroRHS,h,iter);
}
this.usedIterations++;
}
// Add result to velocity
for(var i=0; i<world.bodies.length; i++){
world.bodies[i].addConstraintVelocity();
world.bodies[i].resetConstraintVelocity();
}
}
};
@schteppe Do you think it would require refactoring whole p2.js?
ContactEquations & FrictionEquations livetime starts and ends in world.internalStep method. So they can't keep any values between steps that may affect sync body in next step.
It looks really weird, especially that I've separated solving (sync<=>sync) and (nonsync<=>nonsync & nonsync<=>sync). The only way Equation can affect Body is by wlambda & vlambda.
(forgive me for this) It's like p2.js has not-important but still bug somewhere. I was thinking maybe equation pool doesn't clear some values when pops new element, but everything seems all right.
I even disabled friction, so only ContantEquations matter, still no luck.
I know you proposed creating KINEMATIC body for each sync body that has to collide with non-sync, but it would make serious performance drop, because sync bodies are often big majority. Still it is the way, but the last one.
You're already refactoring the core, right? :) The contacts and friction equations need to be re-added each step. If you have a better idea, it is welcome. I still think it's a better idea to use extra kinematic bodies. Other than saving time, this could help isolate any bugs that you have found in p2 related to the equations/wlambda. It would probably be a little bit slower in terms of performance, yes.
@schteppe Just overriding some few methods, not a lot :d
re-added or re-generated?
As far as I can see in code, these equations are generated/created in the middle of internalStep but in next internalStep at the begining they are utilized to pool. So that's why I posted that theirs livetime starts and end in internalStep.
But I'm not sure about it. I see it clear, but maybe I'm still wrong. If you can say yes/no, I would be 100% sure then.
If it is true, then the only variables that can accumulate some antagonist (:>) values are wlambda & vlambda. That's why I am so confused, because I already take care of them.
I am very amazed how elegant Equations thing is done. You didn't have to do separate algorithms for solving collisions and springs. It's very cool I think.
Oh, sorry, the answer is yes. After the step equations are not used any more. And wlambda+vlambda on the bodies should be reset as well. Equations are pretty cool, indeed. With a little math you can make them do contacts, friction and complex joints.
Got it. I've done everything fine. Just islandSplit turned on as default was the problem.
I was so sure I've disabled it (haha common story)
I guess I can live without islandSplit. For better performance I could code own Solver based on WebWorkers in near future.
Thanks for ur help! :) @schteppe @englercj
EDIT:
I was digging more about it and I've find out that equations that has bodyA or bodyB sync==false (aka non-sync equations) have to be just solved separately. Then islandSplit works, but case without islandSplit needs it too.
It isn't hard, you just need to override internalStep method in World class. Do some filtering of equations for dividing them into two groups, sync & nonsync equations and do the solving part separately.