Open kerorojason opened 5 years ago
I found the same issue and seems to be related with the method getUpsideValue(), for some reason sometimes the face detected by this method is wrong and because of that the shift done in the faces are wrong too. Showing an unexpected value in the dice.
I don't know if maybe this method is called some earlier been finished the simulation done, computing the getUpsideValue() on a still in movement die.
I was doing some debugging inside getUpsideValue() and I found that the quaternion applied in the method to get the upside value sometimes differs between the simulation and the animation. I uploaded some screenshots and a video from my debugging enviroment. In red you can find the simulation estimation and in pink the final result after the animation it's finished, as I said sometimes fits perfectly ... sometimes doesn't :(
So, maybe is something related with the way the simulation is done or something inside CannonJS.
https://user-images.githubusercontent.com/2479373/116387376-0bc56000-a81b-11eb-97bd-63f20076e04e.mp4
Hey guys :) Thanks for looking into the problem! Unfortunately I currently don't have any time for the project, so I would be very happy if you find out the cause..
I noticed it back in the days as I made this package, but only sometimes.. It could be, that it has something to do with finding out, when the die finished rolling. If I remember correctly, I have a threshold of frames. If the die doesn't move too much in this threshold, the system takes it as "rolling finished" and switches the sides. Now it could be, that it was just on the edge, but eventually rolled more after this "slowness". And this of course mixes up the sides...
In first place I thought that could be something related with not finding correctly when the die was not rolling. But I think that is something more related with the inner working of CannonJS rather than the die is moving a little bit more. Seems that sometimes the trajectory is completely different.
Ok, I was modifying a little bit the library to be sure that this bug was not related with the method to known when the die stops rolling. And I'm 100% that it's something related with the way how CannonJS resolves the collisions.
I don't know why, because seems to be using a fixed-step simulation, and all the rolls would have to be the same, as long as they are using the same parameters to roll the dice (position, quaternion, velocity, angularVelocity...) but sometimes this think fails . :(
Now it shows both dice rolls, the first one used to discover the upside value and the second one is the simulation showed. In most of the cases uses to fits, but when appears a discrepancy between them, is easy to see that the dices follow different paths, so it was solved in a different way.
You can find wrong dice rolls in the video attached below:
https://user-images.githubusercontent.com/2479373/116519868-463e0400-a8d2-11eb-8911-a21a397bed81.mp4
Ok, I think I found the bug. It is related to the fact that the body is reused for both the simulation and the final animation. Internally, the body class has some attributes (inertia, torque, force ...) that must always be the same if you expect to have two throws being identical. Now it was accumulating an error internally in the body and because of that the differences.
I looked in the CannonJS documentation to see if there was any method in the Body class to restore this class , but found nothing. So I created a method that does this task and is called before the second roll of the dice.
And need to be called also in the test rolling because the dice are being reused also.
resetBody() {
this.object.body.vlambda = new CANNON.Vec3();
//this.object.body.collisionResponse = true;
this.object.body.position = new CANNON.Vec3();
this.object.body.previousPosition = new CANNON.Vec3();
this.object.body.initPosition = new CANNON.Vec3();
this.object.body.velocity = new CANNON.Vec3();
this.object.body.initVelocity = new CANNON.Vec3();
this.object.body.force = new CANNON.Vec3();
//this.object.body.sleepState = 0;
//this.object.body.timeLastSleepy = 0;
//this.object.body._wakeUpAfterNarrowphase = false;
this.object.body.torque = new CANNON.Vec3();
this.object.body.quaternion = new CANNON.Quaternion();
this.object.body.initQuaternion = new CANNON.Quaternion();
this.object.body.angularVelocity = new CANNON.Vec3();
this.object.body.initAngularVelocity = new CANNON.Vec3();
this.object.body.interpolatedPosition = new CANNON.Vec3();
this.object.body.interpolatedQuaternion = new CANNON.Quaternion();
this.object.body.inertia = new CANNON.Vec3();
this.object.body.invInertia = new CANNON.Vec3();
this.object.body.invInertiaWorld = new CANNON.Mat3();
//this.object.body.invMassSolve = 0;
this.object.body.invInertiaSolve = new CANNON.Vec3();
this.object.body.invInertiaWorldSolve = new CANNON.Mat3();
//this.object.body.aabb = new CANNON.AABB();
//this.object.body.aabbNeedsUpdate = true;
this.object.body.wlambda = new CANNON.Vec3();
this.object.body.updateMassProperties();
}
The line which are under a comment are the ones that I found that have no interest in being reset. There are probably more, but now it's working.
I am going to prepare a pull request together with the BufferGeometry update, but I would need the documentation to be updated to know the process to test the module with typescript since I have no experience with that part and I don't know how to test it.
Please try it out, I just merged @eipporko 's PR.
[Update] I changed the stableCount threshold in dice.js and the problem with DiceD6 is solved. However, it still occurs with DiceD8 DiceD10 DiceD12 DiceD20😫
In my app, I sometimes got wrong face when the rolling animation ended. I have changed some parameter (gravity, time-step and size). However, the faces show incorrectly even though I set back to the same parameter as the example code.
Another possible reason I can think of is the animation function. In order to reduce the FPS, I set the threshold to 1/30 so my updatePhysics() is called less frequently. Could this cause the problem?